|
Safe API Framework
Layered API framework for safety-related applications (ERTMS RBC reference targeting CENELEC EN 50128 SIL 4)
|
Which of two redundant instances is active, and how well-backed is the standby one (ADR-020). More...
Files | |
| file | src/redundancy/dual/sapi_dual_channel.c |
| "DualChannel" layer of ADR-020 - see sapi_dual_channel.h. | |
| file | src/redundancy/dual/sapi_dual_msgchannel.c |
| "Channel" layer of ADR-020 - see sapi_dual_msgchannel.h. | |
| file | src/redundancy/dual/sapi_dual_negotiator.c |
| Dual state negotiator of ADR-020 - see sapi_dual_negotiator.h. | |
| file | src/redundancy/dual/sapi_dual_types.c |
| Diagnostics-only string rendering for the sapi_dual module's shared enums (ADR-020). | |
Data Structures | |
| struct | sapi_dual_channel_t |
| One sapi_dual_channel_t instance's state. Caller-owned storage; every field is private - reach it only through the functions below. More... | |
| struct | sapi_dual_channel_config_t |
| Configuration for sapi_dual_channel_init(). More... | |
| struct | sapi_dual_frame_header_t |
| Common 4-byte header prefixing every Layer-2 frame; kept a fixed 4 bytes (not just 1) so the fields that follow in sapi_dual_ack_frame_t/sapi_dual_state_frame_t stay naturally aligned. More... | |
| struct | sapi_dual_ack_frame_t |
| SAPI_DUAL_FRAME_KIND_ACK payload. More... | |
| struct | sapi_dual_state_frame_t |
| SAPI_DUAL_FRAME_KIND_STATE payload - sapi_dual_negotiator_t's own periodic beacon. More... | |
| struct | sapi_dual_heartbeat_frame_t |
| SAPI_DUAL_FRAME_KIND_HEARTBEAT payload - connection maintenance heartbeat. More... | |
| struct | sapi_dual_msgchannel_t |
| One sapi_dual_msgchannel_t instance's state. Caller-owned storage (REQ-DUAL-MSGCHANNEL-001); opaque in practice, exposed here (not via SAFEAPI_DECLARE_STORAGE) only because its size is already small and fixed - callers must still treat every field as private and only reach it through the functions below. More... | |
| struct | sapi_dual_msgchannel_config_t |
| Configuration for sapi_dual_msgchannel_init(). More... | |
| struct | sapi_dual_negotiator_t |
| One sapi_dual_negotiator_t instance's state. Caller-owned storage; every field is private - reach it only through the functions below. More... | |
| struct | sapi_dual_negotiator_config_t |
| Configuration for sapi_dual_negotiator_init(). More... | |
Macros | |
| #define | SAPI_DUAL_CHANNEL_MAX_LINKS 4U |
| Maximum number of redundant links one sapi_dual_channel_t may be configured with. Fixed, not dynamic (REQ-OAL-COMMON-010). | |
| #define | SAPI_DUAL_CHANNEL_MAX_PAYLOAD (SAPI_DUAL_MSGCHANNEL_MAX_PAYLOAD - 4U) |
| Max application payload bytes usable via sapi_dual_channel_send()/_receive() - Layer-1's own SAPI_DUAL_MSGCHANNEL_MAX_PAYLOAD minus this layer's 4-byte sapi_dual_frame_header_t. | |
| #define | SAPI_DUAL_MSGCHANNEL_MAX_PAYLOAD 248U |
| Max payload bytes usable via sapi_dual_msgchannel_send()/ _receive() - bounded by sapi_vital_message_t's own fixed 248-byte payload field (sapi_checksum.h). | |
Typedefs | |
| typedef void(*) | sapi_dual_channel_status_callback_t(sapi_dual_channel_status_t new_status, sapi_dual_channel_status_t old_status, void *user_ctx) |
| Optional callback invoked whenever sapi_dual_channel_get_status()'s value changes (ADR-020 section 2's "indicate to the user via
callback if registered" requirement). Called synchronously from inside sapi_dual_channel_send() (the only place aggregate status is recomputed) - keep this fast; it is on the same call path as the send it was triggered by. | |
| typedef void(*) | sapi_dual_negotiator_state_change_callback_t(sapi_dual_state_t new_own_state, sapi_dual_state_t old_own_state, sapi_dual_state_t new_peer_state, sapi_dual_state_t old_peer_state, void *user_ctx) |
| Optional callback invoked whenever either the own or the peer sapi_dual_state_t changes, from inside sapi_dual_negotiator_execute(). | |
Enumerations | |
| enum | sapi_dual_frame_kind_t { SAPI_DUAL_FRAME_KIND_DATA = 0 , SAPI_DUAL_FRAME_KIND_ACK = 1 , SAPI_DUAL_FRAME_KIND_STATE = 2 , SAPI_DUAL_FRAME_KIND_HEARTBEAT = 3 } |
| Which kind of Layer-2 frame a given Layer-1 payload holds. Stored as the first byte of every sapi_dual_channel_t frame so DATA, ACK, and STATE traffic sharing the same redundant links can never be misinterpreted as each other. More... | |
| enum | sapi_dual_state_t { SAPI_DUAL_STATE_IDLE = 0 , SAPI_DUAL_STATE_UNKNOWN = 1 , SAPI_DUAL_STATE_ONLINE = 2 , SAPI_DUAL_STATE_HOTSTANDBY = 3 , SAPI_DUAL_STATE_COLDSTANDBY = 4 } |
| State of one instance in a dual (two-redundant-instance) relationship, as decided by sapi_dual_negotiator_t (ADR-020 section 3). Both this instance's own state and its last-known view of the peer's state use this same enum - see sapi_dual_negotiator_get_own_state()/_get_peer_state(). More... | |
| enum | sapi_dual_channel_status_t { SAPI_DUAL_CHANNEL_STATUS_DOWN = 0 , SAPI_DUAL_CHANNEL_STATUS_DEGRADED = 1 , SAPI_DUAL_CHANNEL_STATUS_FULL = 2 } |
| Aggregate connection status of a sapi_dual_channel_t across all of its configured redundant links (ADR-020 section 2). Independent of sapi_dual_state_t: losing some (not all) of the redundant links degrades this status without by itself forcing any sapi_dual_state_t transition - only total loss (DOWN) is a negotiator-relevant liveness event. More... | |
Functions | |
| 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_init()) and starts sapi_dual_channel_get_status() at SAPI_DUAL_CHANNEL_STATUS_DOWN (no traffic has been sent/received yet). | |
| 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 state (ADR-020 section 2: the real payload traffic itself is the liveness check). For each link, waits up to config->ack_timeout_ms for that link's own ACK (matched by sequence number, not just "an ACK arrived") before moving on; a DATA or STATE frame received from the peer while waiting is still auto-ACKed/staged as a side effect, not dropped. | |
| 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-ACked on arrival - see sapi_dual_channel_send()'s own doc) if none was already staged from a previous sapi_dual_channel_send() call's own incidental polling. | |
| sapi_status_t | sapi_dual_channel_send_heartbeat (sapi_dual_channel_t *channel, uint32_t *out_ack_count) |
| Sends a sapi_dual_state_frame_t (sapi_dual_frames.h) on every configured redundant link - fire-and-forget, no ACK wait (unlike sapi_dual_channel_send()'s DATA frames), matching the periodic-beacon nature of state negotiation. Intended to be called by a sapi_dual_negotiator_t, not directly by application code - see ADR-020 section 3. | |
| sapi_status_t | sapi_dual_channel_send_state_frame (sapi_dual_channel_t *channel, sapi_dual_state_t state, bool channel_degraded, uint64_t timestamp_ms) |
| sapi_status_t | sapi_dual_channel_receive_state_frame (sapi_dual_channel_t *channel, sapi_duration_ms_t timeout_ms, sapi_dual_state_frame_t *out_frame) |
| Returns the most recently staged inbound STATE frame, actively polling the configured links if none was already staged. Intended to be called by a sapi_dual_negotiator_t - see ADR-020 section 3. | |
| 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_dual_channel_send() call (ADR-020 section 2). SAPI_DUAL_CHANNEL_STATUS_DOWN before the first send. | |
| bool | sapi_dual_channel_is_link_up (const sapi_dual_channel_t *channel, uint32_t link_index) |
| Returns whether one specific configured link is currently considered up, as of the most recent sapi_dual_channel_send() call - for diagnostics/logging (e.g. which specific redundant path is the one that's down), not a safety-decision input on its own (see sapi_dual_channel_get_status() for the aggregate). | |
| sapi_status_t | sapi_dual_msgchannel_init (sapi_dual_msgchannel_t *channel, const sapi_dual_msgchannel_config_t *config) |
| Initializes a sapi_dual_msgchannel_t: starts both sequence counters at 0. | |
| sapi_status_t | sapi_dual_msgchannel_send (sapi_dual_msgchannel_t *channel, const uint8_t *payload, uint8_t payload_size, sapi_duration_ms_t timeout_ms, uint32_t *out_sequence) |
| Wraps payload in a sapi_vital_message_t (this channel's own sender_id and next sequence_number) and sends it over config->link, blocking at most timeout_ms. | |
| sapi_status_t | sapi_dual_msgchannel_receive (sapi_dual_msgchannel_t *channel, uint8_t *out_payload, uint8_t payload_max_size, sapi_duration_ms_t timeout_ms, uint8_t *out_payload_size, uint32_t *out_sequence) |
| Receives one frame over config->link, blocking at most timeout_ms, verifies its CRC-64 and sequence continuity (sapi_checksum_vital_message_verify() against channel->expected_sequence) and its sender_id against config->expected_peer_id, and extracts the payload. | |
| sapi_status_t | sapi_dual_msgchannel_reset_sequence (sapi_dual_msgchannel_t *channel) |
| Resets both sequence counters to 0. Intended to be called by the caller (typically sapi_dual_channel_t) exactly once, immediately after config->link has been freshly (re)established with the peer - mirrors safeAPIRBC2oo2's own precedent (channel_ab.c's cycle_resync_requested handling after a peer link reconnects) for why a fresh link needs a fresh, mutually agreed starting sequence rather than fighting over whatever counters were left over from before the disconnect. | |
| sapi_status_t | sapi_dual_negotiator_init (sapi_dual_negotiator_t *negotiator, const sapi_dual_negotiator_config_t *config) |
| Initializes a sapi_dual_negotiator_t. Captures this instance's own startup timestamp once (sapi_timer_now(), degrading to 0 if no timer backend is registered - same best-effort posture as sapi_log_write_event()'s Timestamp field) for use on every beacon this negotiator ever sends - see sapi_dual_state_frame_t's own doc on why this must stay fixed. Both own and peer state start at SAPI_DUAL_STATE_IDLE. | |
| sapi_status_t | sapi_dual_negotiator_execute (sapi_dual_negotiator_t *negotiator, sapi_duration_ms_t receive_timeout_ms) |
| Drives one round of state negotiation: refreshes own_channel_degraded from the attached channel's current sapi_dual_channel_get_status(), sends this instance's own beacon, drains and processes every currently-pending inbound STATE frame, then recomputes both own and peer sapi_dual_state_t: | |
| sapi_dual_state_t | sapi_dual_negotiator_get_own_state (const sapi_dual_negotiator_t *negotiator) |
| Returns this instance's own current sapi_dual_state_t. | |
| sapi_dual_state_t | sapi_dual_negotiator_get_peer_state (const sapi_dual_negotiator_t *negotiator) |
| Returns this instance's last-known view of the peer's sapi_dual_state_t. | |
| const char * | sapi_dual_state_to_string (sapi_dual_state_t state) |
| Returns a short, static, human-readable string for a sapi_dual_state_t. Intended for diagnostics/logging only (e.g. as the Info= field of a sapi_log_write_event() call); never on a safety-decision path. | |
| const char * | sapi_dual_channel_status_to_string (sapi_dual_channel_status_t status) |
| Returns a short, static, human-readable string for a sapi_dual_channel_status_t. Same diagnostics-only contract as sapi_dual_state_to_string(). | |
Which of two redundant instances is active, and how well-backed is the standby one (ADR-020).
| #define SAPI_DUAL_CHANNEL_MAX_LINKS 4U |
Maximum number of redundant links one sapi_dual_channel_t may be configured with. Fixed, not dynamic (REQ-OAL-COMMON-010).
Definition at line 66 of file sapi_dual_channel.h.
| #define SAPI_DUAL_CHANNEL_MAX_PAYLOAD (SAPI_DUAL_MSGCHANNEL_MAX_PAYLOAD - 4U) |
Max application payload bytes usable via sapi_dual_channel_send()/_receive() - Layer-1's own SAPI_DUAL_MSGCHANNEL_MAX_PAYLOAD minus this layer's 4-byte sapi_dual_frame_header_t.
Definition at line 72 of file sapi_dual_channel.h.
| #define SAPI_DUAL_MSGCHANNEL_MAX_PAYLOAD 248U |
Max payload bytes usable via sapi_dual_msgchannel_send()/ _receive() - bounded by sapi_vital_message_t's own fixed 248-byte payload field (sapi_checksum.h).
Definition at line 58 of file sapi_dual_msgchannel.h.
| typedef void(*) sapi_dual_channel_status_callback_t(sapi_dual_channel_status_t new_status, sapi_dual_channel_status_t old_status, void *user_ctx) |
Optional callback invoked whenever sapi_dual_channel_get_status()'s value changes (ADR-020 section 2's "indicate to the user via callback if registered" requirement). Called synchronously from inside sapi_dual_channel_send() (the only place aggregate status is recomputed) - keep this fast; it is on the same call path as the send it was triggered by.
| new_status | Status just transitioned to. |
| old_status | Status just transitioned from. |
| user_ctx | Caller-supplied context from sapi_dual_channel_config_t. |
Definition at line 85 of file sapi_dual_channel.h.
| typedef void(*) sapi_dual_negotiator_state_change_callback_t(sapi_dual_state_t new_own_state, sapi_dual_state_t old_own_state, sapi_dual_state_t new_peer_state, sapi_dual_state_t old_peer_state, void *user_ctx) |
Optional callback invoked whenever either the own or the peer sapi_dual_state_t changes, from inside sapi_dual_negotiator_execute().
| new_own_state | Own state just transitioned to. |
| old_own_state | Own state just transitioned from. |
| new_peer_state | Peer state just transitioned to. |
| old_peer_state | Peer state just transitioned from. |
| user_ctx | Caller-supplied context from sapi_dual_negotiator_config_t. |
Definition at line 71 of file sapi_dual_negotiator.h.
Which kind of Layer-2 frame a given Layer-1 payload holds. Stored as the first byte of every sapi_dual_channel_t frame so DATA, ACK, and STATE traffic sharing the same redundant links can never be misinterpreted as each other.
Explicit numeric values are fixed and part of the wire format: do not renumber existing entries, only append.
| Enumerator | |
|---|---|
| SAPI_DUAL_FRAME_KIND_DATA | Application payload (sapi_dual_channel_send()/_receive()); expects a SAPI_DUAL_FRAME_KIND_ACK frame back. |
| SAPI_DUAL_FRAME_KIND_ACK | Acknowledges one DATA frame's sequence_number (acked_sequence below) - checked, not just "some ACK arrived", against the specific send it corresponds to (ADR-020 section 1). |
| SAPI_DUAL_FRAME_KIND_STATE | sapi_dual_negotiator_t's own state beacon - fire-and-forget, periodic, no ACK expected (unlike DATA). |
| SAPI_DUAL_FRAME_KIND_HEARTBEAT | Liveness heartbeat frame - sent periodically to maintain connection, expects a SAPI_DUAL_FRAME_KIND_ACK frame back. |
Definition at line 41 of file sapi_dual_frames.h.
| enum sapi_dual_state_t |
State of one instance in a dual (two-redundant-instance) relationship, as decided by sapi_dual_negotiator_t (ADR-020 section 3). Both this instance's own state and its last-known view of the peer's state use this same enum - see sapi_dual_negotiator_get_own_state()/_get_peer_state().
Explicit numeric values are fixed and part of the ABI (this value travels on the wire inside sapi_dual_state_frame_t::state): do not renumber existing entries, only append.
Definition at line 37 of file sapi_dual_types.h.
Aggregate connection status of a sapi_dual_channel_t across all of its configured redundant links (ADR-020 section 2). Independent of sapi_dual_state_t: losing some (not all) of the redundant links degrades this status without by itself forcing any sapi_dual_state_t transition - only total loss (DOWN) is a negotiator-relevant liveness event.
Definition at line 65 of file sapi_dual_types.h.
| 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_init()) and starts sapi_dual_channel_get_status() at SAPI_DUAL_CHANNEL_STATUS_DOWN (no traffic has been sent/received yet).
| channel | Caller-owned storage to initialize. Must not be NULL. |
| config | Configuration. Must not be NULL; config->link_count must be in [1, SAPI_DUAL_CHANNEL_MAX_LINKS]; every config->links[0..link_count-1] must not be NULL. |
Definition at line 183 of file sapi_dual_channel.c.
| 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 state (ADR-020 section 2: the real payload traffic itself is the liveness check). For each link, waits up to config->ack_timeout_ms for that link's own ACK (matched by sequence number, not just "an ACK arrived") before moving on; a DATA or STATE frame received from the peer while waiting is still auto-ACKed/staged as a side effect, not dropped.
After every configured link has been tried, recomputes sapi_dual_channel_get_status() and invokes config->status_callback if it changed (ADR-020 section 2).
| channel | Initialized channel. Must not be NULL. |
| payload | Payload to send. May be NULL only if payload_size is 0. |
| payload_size | Payload size in bytes; must be <= SAPI_DUAL_CHANNEL_MAX_PAYLOAD. |
| out_ack_link_count | Optional; if not NULL, receives how many of the configured links ACKed this send. May be NULL. |
Definition at line 232 of file sapi_dual_channel.c.
| 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-ACked on arrival - see sapi_dual_channel_send()'s own doc) if none was already staged from a previous sapi_dual_channel_send() call's own incidental polling.
| channel | Initialized channel. Must not be NULL. |
| out_payload | Destination buffer. Must not be NULL. |
| max_size | Usable size of out_payload; must be > 0. |
| timeout_ms | Maximum total time to actively poll the configured links if nothing was already staged (split evenly across links); 0 = check only what is already staged. |
| out_size | Receives the actual payload size. Must not be NULL. |
Definition at line 409 of file sapi_dual_channel.c.
| sapi_status_t sapi_dual_channel_send_heartbeat | ( | sapi_dual_channel_t * | channel, |
| uint32_t * | out_ack_count ) |
Sends a sapi_dual_state_frame_t (sapi_dual_frames.h) on every configured redundant link - fire-and-forget, no ACK wait (unlike sapi_dual_channel_send()'s DATA frames), matching the periodic-beacon nature of state negotiation. Intended to be called by a sapi_dual_negotiator_t, not directly by application code - see ADR-020 section 3.
| channel | Initialized channel. Must not be NULL. |
| state | This instance's own sapi_dual_state_t to advertise. |
| channel_degraded | This instance's own current sapi_dual_channel_get_status() != FULL, as a single bit (so the peer can tell HOTSTANDBY from COLDSTANDBY for itself). |
| timestamp_ms | Caller-supplied value for the frame's own timestamp_ms field. This function does not source it from sapi_timer_now() itself: a sapi_dual_negotiator_t needs the same fixed value on every beacon it ever sends (captured once at its own init) for its startup tie-break to remain stable call to call - see sapi_dual_state_frame_t's own doc. |
Transmits a connection maintenance heartbeat frame across every configured link and waits up to ack_timeout_ms for an ACK on each.
| channel | Initialized channel. Must not be NULL. |
| out_ack_count | Optional; receives the number of links that ACKed. |
Definition at line 474 of file sapi_dual_channel.c.
| sapi_status_t sapi_dual_channel_send_state_frame | ( | sapi_dual_channel_t * | channel, |
| sapi_dual_state_t | state, | ||
| bool | channel_degraded, | ||
| uint64_t | timestamp_ms ) |
Definition at line 594 of file sapi_dual_channel.c.
| sapi_status_t sapi_dual_channel_receive_state_frame | ( | sapi_dual_channel_t * | channel, |
| sapi_duration_ms_t | timeout_ms, | ||
| sapi_dual_state_frame_t * | out_frame ) |
Returns the most recently staged inbound STATE frame, actively polling the configured links if none was already staged. Intended to be called by a sapi_dual_negotiator_t - see ADR-020 section 3.
| channel | Initialized channel. Must not be NULL. |
| timeout_ms | Maximum total time to actively poll if nothing was already staged (split evenly across links); 0 = check only what is already staged. |
| out_frame | Receives the frame. Must not be NULL. |
Definition at line 628 of file sapi_dual_channel.c.
| 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_dual_channel_send() call (ADR-020 section 2). SAPI_DUAL_CHANNEL_STATUS_DOWN before the first send.
| channel | Channel to query. May be NULL (returns DOWN, defensive default). |
Definition at line 662 of file sapi_dual_channel.c.
| bool sapi_dual_channel_is_link_up | ( | const sapi_dual_channel_t * | channel, |
| uint32_t | link_index ) |
Returns whether one specific configured link is currently considered up, as of the most recent sapi_dual_channel_send() call - for diagnostics/logging (e.g. which specific redundant path is the one that's down), not a safety-decision input on its own (see sapi_dual_channel_get_status() for the aggregate).
| channel | Channel to query. May be NULL (returns false). |
| link_index | Index into the configured links, 0..link_count-1. |
Definition at line 671 of file sapi_dual_channel.c.
| sapi_status_t sapi_dual_msgchannel_init | ( | sapi_dual_msgchannel_t * | channel, |
| const sapi_dual_msgchannel_config_t * | config ) |
Initializes a sapi_dual_msgchannel_t: starts both sequence counters at 0.
| channel | Caller-owned storage to initialize. Must not be NULL. |
| config | Configuration. Must not be NULL; config->link must not be NULL. |
Local makros Local types declarations Local variables declarations Global variables declarations Local function declarations Global functions
Definition at line 20 of file sapi_dual_msgchannel.c.
| sapi_status_t sapi_dual_msgchannel_send | ( | sapi_dual_msgchannel_t * | channel, |
| const uint8_t * | payload, | ||
| uint8_t | payload_size, | ||
| sapi_duration_ms_t | timeout_ms, | ||
| uint32_t * | out_sequence ) |
Wraps payload in a sapi_vital_message_t (this channel's own sender_id and next sequence_number) and sends it over config->link, blocking at most timeout_ms.
| channel | Initialized channel. Must not be NULL. |
| payload | Payload to send. May be NULL only if payload_size is 0. |
| payload_size | Payload size in bytes; must be <= SAPI_DUAL_MSGCHANNEL_MAX_PAYLOAD. |
| timeout_ms | Maximum time to wait for the underlying sapi_netlink_send() to complete (REQ-DUAL-MSGCHANNEL-002). |
| out_sequence | Optional; if not NULL, receives the sequence_number this frame was sent with (the caller needs this to correlate a later ACK - see sapi_dual_channel.h). May be NULL. |
Definition at line 37 of file sapi_dual_msgchannel.c.
| sapi_status_t sapi_dual_msgchannel_receive | ( | sapi_dual_msgchannel_t * | channel, |
| uint8_t * | out_payload, | ||
| uint8_t | payload_max_size, | ||
| sapi_duration_ms_t | timeout_ms, | ||
| uint8_t * | out_payload_size, | ||
| uint32_t * | out_sequence ) |
Receives one frame over config->link, blocking at most timeout_ms, verifies its CRC-64 and sequence continuity (sapi_checksum_vital_message_verify() against channel->expected_sequence) and its sender_id against config->expected_peer_id, and extracts the payload.
| channel | Initialized channel. Must not be NULL. |
| out_payload | Destination buffer. Must not be NULL. |
| payload_max_size | Usable size of out_payload; must be > 0. |
| timeout_ms | Maximum time to wait for a frame to arrive. |
| out_payload_size | Receives the actual payload size. Must not be NULL. |
| out_sequence | Optional; receives the frame's own sequence_number (e.g. so a DATA frame's sequence can be echoed back in an ACK - see sapi_dual_channel.h). May be NULL. |
Definition at line 80 of file sapi_dual_msgchannel.c.
| sapi_status_t sapi_dual_msgchannel_reset_sequence | ( | sapi_dual_msgchannel_t * | channel | ) |
Resets both sequence counters to 0. Intended to be called by the caller (typically sapi_dual_channel_t) exactly once, immediately after config->link has been freshly (re)established with the peer - mirrors safeAPIRBC2oo2's own precedent (channel_ab.c's cycle_resync_requested handling after a peer link reconnects) for why a fresh link needs a fresh, mutually agreed starting sequence rather than fighting over whatever counters were left over from before the disconnect.
| channel | Channel to reset. Must not be NULL. |
Definition at line 135 of file sapi_dual_msgchannel.c.
| sapi_status_t sapi_dual_negotiator_init | ( | sapi_dual_negotiator_t * | negotiator, |
| const sapi_dual_negotiator_config_t * | config ) |
Initializes a sapi_dual_negotiator_t. Captures this instance's own startup timestamp once (sapi_timer_now(), degrading to 0 if no timer backend is registered - same best-effort posture as sapi_log_write_event()'s Timestamp field) for use on every beacon this negotiator ever sends - see sapi_dual_state_frame_t's own doc on why this must stay fixed. Both own and peer state start at SAPI_DUAL_STATE_IDLE.
| negotiator | Caller-owned storage to initialize. Must not be NULL. |
| config | Configuration. Must not be NULL; config->channel must not be NULL. |
Definition at line 127 of file sapi_dual_negotiator.c.
| sapi_status_t sapi_dual_negotiator_execute | ( | sapi_dual_negotiator_t * | negotiator, |
| sapi_duration_ms_t | receive_timeout_ms ) |
Drives one round of state negotiation: refreshes own_channel_degraded from the attached channel's current sapi_dual_channel_get_status(), sends this instance's own beacon, drains and processes every currently-pending inbound STATE frame, then recomputes both own and peer sapi_dual_state_t:
Invokes config->state_change_callback if either state changed.
| negotiator | Initialized negotiator. Must not be NULL. |
| receive_timeout_ms | Maximum time to wait for at least one inbound STATE frame if none is already pending on the attached channel (passed through to sapi_dual_channel_receive_state_frame()); 0 = do not block, only process what is already pending. |
Definition at line 161 of file sapi_dual_negotiator.c.
| sapi_dual_state_t sapi_dual_negotiator_get_own_state | ( | const sapi_dual_negotiator_t * | negotiator | ) |
Returns this instance's own current sapi_dual_state_t.
| negotiator | Negotiator to query. May be NULL (returns SAPI_DUAL_STATE_IDLE, defensive default). |
Definition at line 196 of file sapi_dual_negotiator.c.
| sapi_dual_state_t sapi_dual_negotiator_get_peer_state | ( | const sapi_dual_negotiator_t * | negotiator | ) |
Returns this instance's last-known view of the peer's sapi_dual_state_t.
| negotiator | Negotiator to query. May be NULL (returns SAPI_DUAL_STATE_IDLE, defensive default). |
Definition at line 205 of file sapi_dual_negotiator.c.
| const char * sapi_dual_state_to_string | ( | sapi_dual_state_t | state | ) |
Returns a short, static, human-readable string for a sapi_dual_state_t. Intended for diagnostics/logging only (e.g. as the Info= field of a sapi_log_write_event() call); never on a safety-decision path.
| state | Value to render; an unrecognized value (defensive only - not reachable through the public enum) renders as "UNKNOWN_STATE". |
Local makros Local types declarations Local variables declarations Global variables declarations Local function declarations Global functions
Definition at line 21 of file sapi_dual_types.c.
| const char * sapi_dual_channel_status_to_string | ( | sapi_dual_channel_status_t | status | ) |
Returns a short, static, human-readable string for a sapi_dual_channel_status_t. Same diagnostics-only contract as sapi_dual_state_to_string().
| status | Value to render; an unrecognized value renders as "UNKNOWN_STATUS". |
Definition at line 50 of file sapi_dual_types.c.