20 for (i = 0U; i < count; i++)
22 if (channel->link_handles[i] != NULL)
25 channel->link_handles[i] = NULL;
42 memset(&cfg, 0,
sizeof(cfg));
89 memset(&dual_cfg, 0,
sizeof(dual_cfg));
92 dual_cfg.
links[i] = channel->link_handles[i];
105 channel->link_count = 0U;
166 memset(&voter_cfg, 0,
sizeof(voter_cfg));
179 channel->link_count = 0U;
187 memset(&chan_cfg, 0,
sizeof(chan_cfg));
200 channel->link_count = 0U;
212 if ((channel == NULL) || (config == NULL))
216 memset(channel, 0,
sizeof(*channel));
217 channel->type = config->
type;
219 switch (config->
type)
222 status = safechannel_open_dual(channel, &config->as.dual);
225 status = safechannel_open_vital(channel, &config->as.vital);
238 if ((channel == NULL) || (!channel->is_open))
245 if (payload_size > (
size_t)UINT8_MAX)
251 return sapi_voter_send(&channel->impl.vital.voter, payload, payload_size);
257 if ((channel == NULL) || (!channel->is_open) || (out_size == NULL))
264 uint8_t received_size = 0U;
267 if (max_size > (
size_t)UINT8_MAX)
269 max_size = (size_t)UINT8_MAX;
273 *out_size = (size_t)received_size;
279 return sapi_voter_receive(&channel->impl.vital.voter, out_payload, max_size, &result, out_size);
285 if ((channel == NULL) || (!channel->is_open))
304 uint32_t healthy_count = 0U;
307 &healthy_count, NULL);
308 if (healthy_count == 0U)
312 if (healthy_count < channel->link_count)
326 if (!channel->is_open)
335 for (i = 0U; i < channel->link_count; i++)
342 channel->link_count = 0U;
343 channel->is_open =
false;
sapi_status_t sapi_dual_channel_send(sapi_dual_channel_t *channel, const uint8_t *payload, uint8_t payload_size, uint32_t *out_ack_link_count)
Sends payload as a DATA frame on every configured redundant link - never gated by any negotiated stat...
sapi_dual_channel_status_t sapi_dual_channel_get_status(const sapi_dual_channel_t *channel)
Returns the aggregate connection status across every configured redundant link, as of the most recent...
sapi_status_t sapi_dual_channel_receive(sapi_dual_channel_t *channel, uint8_t *out_payload, uint8_t max_size, sapi_duration_ms_t timeout_ms, uint8_t *out_size)
Returns the most recently staged inbound DATA frame, actively polling the configured links (each auto...
sapi_status_t sapi_dual_channel_init(sapi_dual_channel_t *channel, const sapi_dual_channel_config_t *config)
Initializes a sapi_dual_channel_t: initializes every configured redundant link (sapi_dual_msgchannel_...
@ SAPI_DUAL_CHANNEL_STATUS_FULL
@ SAPI_DUAL_CHANNEL_STATUS_DOWN
@ SAPI_DUAL_CHANNEL_STATUS_DEGRADED
sapi_status_t sapi_netlink_close(sapi_netlink_handle_t handle)
Closes a link.
sapi_status_t sapi_netlink_send(sapi_netlink_handle_t handle, const void *message, size_t message_size, sapi_duration_ms_t timeout_ms)
Sends one fixed-size message, blocking at most timeout_ms.
struct sapi_netlink_impl_s * sapi_netlink_handle_t
Opaque handle to an established link, returned by sapi_netlink_open().
sapi_status_t sapi_netlink_receive(sapi_netlink_handle_t handle, void *out_message, size_t buffer_size, sapi_duration_ms_t timeout_ms)
Receives one fixed-size message, blocking at most timeout_ms.
sapi_status_t sapi_netlink_open(sapi_netlink_storage_t *storage, const sapi_netlink_config_t *config, sapi_netlink_handle_t *out_handle)
Establishes a point-to-point link: binds+accepts (LISTEN) or dials (CONNECT), per config->role.
@ SAPI_NETLINK_ROLE_CONNECT
#define SAPI_SAFECHANNEL_MAX_LINKS
Maximum number of redundant endpoints one sapi_safechannel_t may be configured with,...
sapi_status_t sapi_safechannel_send(sapi_safechannel_t *channel, const uint8_t *payload, size_t payload_size)
Sends payload on the underlying channel - broadcast-with-ACK- wait for DUAL_REDUNDANT (sapi_dual_chan...
sapi_safechannel_link_status_t sapi_safechannel_get_status(const sapi_safechannel_t *channel)
Aggregate status across every configured endpoint - DOWN before the first send/receive.
sapi_status_t sapi_safechannel_open(sapi_safechannel_t *channel, const sapi_safechannel_config_t *config)
Opens a channel: opens every configured endpoint via the registered sapi_netlink backend,...
sapi_status_t sapi_safechannel_receive(sapi_safechannel_t *channel, uint8_t *out_payload, size_t max_size, sapi_duration_ms_t timeout_ms, size_t *out_size)
Returns the most recent inbound payload, actively polling the underlying channel if nothing was alrea...
sapi_safechannel_link_status_t
Aggregate link status, uniform across both wrapped types.
sapi_status_t sapi_safechannel_close(sapi_safechannel_t *channel)
Closes every endpoint this instance opened and releases the wrapped channel. Safe to call on an alrea...
@ SAPI_SAFECHANNEL_TYPE_VITAL_VOTED
@ SAPI_SAFECHANNEL_TYPE_DUAL_REDUNDANT
@ SAPI_SAFECHANNEL_LINK_DEGRADED
@ SAPI_SAFECHANNEL_LINK_DOWN
@ SAPI_SAFECHANNEL_LINK_FULL
sapi_status_t
Common result/status codes.
@ SAPI_STATUS_INVALID_PARAM
uint32_t sapi_duration_ms_t
sapi_status_t sapi_channel_destroy(sapi_channel_t *handle)
Destroys a vital channel instance.
sapi_status_t sapi_channel_init(sapi_channel_storage_t *storage, const sapi_channel_config_t *config)
Initializes one vital channel over a caller-supplied transport.
sapi_status_t sapi_voter_destroy(sapi_voter_t *voter)
Destroys a voter instance.
sapi_voting_result_t
Outcome of one sapi_voter_receive() call.
sapi_status_t sapi_voter_register_channel(sapi_voter_t *voter, sapi_channel_t *channel)
Registers one already-initialized channel with this voter.
sapi_status_t sapi_voter_send(sapi_voter_t *voter, const void *data, size_t data_size)
Broadcasts data to every registered, healthy channel.
sapi_status_t sapi_voter_init(sapi_voter_storage_t *storage, const sapi_voter_config_t *config)
Initializes a voter with zero registered channels.
sapi_status_t sapi_voter_receive(sapi_voter_t *voter, void *data, size_t data_size, sapi_voting_result_t *result, size_t *bytes_received)
Receives from every registered, healthy channel and votes.
sapi_status_t sapi_voter_get_aggregated_health(const sapi_voter_t *voter, uint32_t *healthy_count, uint32_t *total_disagreements)
Aggregated health across every registered channel.
Checksum and CRC utilities for data integrity in redundant systems.
static sapi_status_t safechannel_open_one_endpoint(sapi_netlink_storage_t *storage, const sapi_safechannel_endpoint_t *endpoint, size_t message_size, sapi_duration_ms_t connect_timeout_ms, sapi_netlink_handle_t *out_handle)
Opens one endpoint, retrying sapi_netlink_open() with a short fixed delay until connect_timeout_ms el...
static sapi_status_t safechannel_vital_backend_recv(void *channel_handle, void *data, size_t data_size, uint32_t timeout_ms)
sapi_channel_config_t::recv bridge - see safechannel_vital_backend_send().
static void safechannel_close_links(sapi_safechannel_t *channel, uint32_t count)
Closes every already-opened link handles[0..count-1] and resets the caller's bookkeeping - used both ...
static sapi_status_t safechannel_vital_backend_send(void *channel_handle, const void *data, size_t data_size)
sapi_channel_config_t::send bridge: casts the opaque channel handle back to the sapi_netlink_handle_t...
Unified, transport-hiding channel factory (ADR-022).
Configuration for sapi_channel_init().
sapi_channel_send_fn send
sapi_channel_recv_fn recv
Configuration for sapi_dual_channel_init().
sapi_duration_ms_t ack_timeout_ms
uint32_t expected_peer_id
sapi_dual_channel_status_callback_t status_callback
sapi_netlink_handle_t links[SAPI_DUAL_CHANNEL_MAX_LINKS]
void * status_callback_ctx
Configuration for sapi_netlink_open().
sapi_duration_ms_t connect_timeout_ms
Configuration for sapi_safechannel_open().
sapi_safechannel_type_t type
Configuration specific to SAPI_SAFECHANNEL_TYPE_DUAL_REDUNDANT.
sapi_duration_ms_t connect_timeout_ms
sapi_safechannel_endpoint_t endpoints[SAPI_SAFECHANNEL_MAX_LINKS]
void * status_callback_ctx
uint32_t expected_peer_id
sapi_dual_channel_status_callback_t status_callback
sapi_duration_ms_t ack_timeout_ms
One remote endpoint to open internally - replaces a caller- supplied sapi_netlink_handle_t (ADR-022 s...
Caller-owned storage for one sapi_safechannel_t instance. Every field is private - reach it only thro...
sapi_netlink_storage_t links[SAPI_SAFECHANNEL_MAX_LINKS]
Configuration specific to SAPI_SAFECHANNEL_TYPE_VITAL_VOTED.
sapi_duration_ms_t connect_timeout_ms
sapi_duration_ms_t channel_timeout_ms
void(*) on_disagreement(void *context, sapi_voting_result_t result)
sapi_safechannel_endpoint_t endpoints[SAPI_SAFECHANNEL_MAX_LINKS]
sapi_voting_strategy_t voting_strategy
Vital channel message with integrated CRC-64.
Configuration for sapi_voter_init().
bool trigger_safestate_on_disagreement
sapi_voting_strategy_t voting_strategy
void(*) on_disagreement(void *context, sapi_voting_result_t result)
uint32_t channel_timeout_ms
void * disagreement_context