Safe API Framework
Layered API framework for safety-related applications (ERTMS RBC reference targeting CENELEC EN 50128 SIL 4)
Loading...
Searching...
No Matches
sapi_dual_negotiator.c
Go to the documentation of this file.
1
7
9
15static bool negotiator_decide_online(uint32_t own_id, uint64_t own_ts, uint32_t peer_id, uint64_t peer_ts)
16{
17 if (own_ts != peer_ts)
18 {
19 return own_ts < peer_ts;
20 }
21 return own_id < peer_id;
22}
23
24static void negotiator_process_peer_frame(sapi_dual_negotiator_t *negotiator, const sapi_dual_state_frame_t *frame)
25{
26 sapi_timestamp_ms_t now_ms = 0U;
27
28 negotiator->peer_channel_degraded = (frame->channel_degraded != 0U);
29 negotiator->peer_startup_timestamp_ms = frame->timestamp_ms;
30 negotiator->have_peer_startup_timestamp = true;
31
32 (void)sapi_timer_now(&now_ms);
33 negotiator->last_peer_seen_ms = now_ms;
34 negotiator->have_last_peer_seen = true;
35}
36
37static void negotiator_update_states(sapi_dual_negotiator_t *negotiator)
38{
39 sapi_dual_state_t old_own = negotiator->own_state;
40 sapi_dual_state_t old_peer = negotiator->peer_state;
41 bool peer_contact_recent = false;
42
43 if (negotiator->have_last_peer_seen)
44 {
45 sapi_timestamp_ms_t now_ms = 0U;
46
47 (void)sapi_timer_now(&now_ms);
48 if (now_ms >= negotiator->last_peer_seen_ms)
49 {
50 sapi_timestamp_ms_t elapsed = now_ms - negotiator->last_peer_seen_ms;
51
52 peer_contact_recent = (elapsed < (sapi_timestamp_ms_t)negotiator->peer_lost_timeout_ms);
53 }
54 /* else: clock moved backwards - no monotonic guarantee assumed
55 * (see sapi_clocksync.h's own file-level note) - stay
56 * conservative (peer_contact_recent already false) rather than
57 * trust a negative interval. */
58 }
59
60 if (!negotiator->have_last_peer_seen)
61 {
62 /* Never yet heard from the peer at all. */
63 negotiator->peer_state = SAPI_DUAL_STATE_IDLE;
64 /* own_state stays whatever it already was (IDLE, normally). */
65 }
66 else if (!peer_contact_recent)
67 {
68 negotiator->peer_state = SAPI_DUAL_STATE_UNKNOWN;
69 if (negotiator->own_state != SAPI_DUAL_STATE_ONLINE)
70 {
71 /* IDLE, HOTSTANDBY, or COLDSTANDBY all degrade to UNKNOWN on
72 * lost peer contact - see this file's header doc for why
73 * ONLINE is the one exception. */
74 negotiator->own_state = SAPI_DUAL_STATE_UNKNOWN;
75 }
76 }
77 else if ((negotiator->own_state == SAPI_DUAL_STATE_IDLE) || (negotiator->own_state == SAPI_DUAL_STATE_UNKNOWN))
78 {
79 bool decided_online = negotiator_decide_online(negotiator->own_id, negotiator->own_startup_timestamp_ms,
80 negotiator->peer_id, negotiator->peer_startup_timestamp_ms);
81
82 if (decided_online)
83 {
84 /* We are ONLINE, they are STANDBY - their HOT/COLD depends
85 * on OUR OWN degradation (they are backing US up), not on
86 * whatever they themselves last reported about their own
87 * channel - see sapi_dual_negotiator_execute()'s own doc. */
88 negotiator->own_state = SAPI_DUAL_STATE_ONLINE;
89 negotiator->peer_state =
90 negotiator->own_channel_degraded ? SAPI_DUAL_STATE_COLDSTANDBY : SAPI_DUAL_STATE_HOTSTANDBY;
91 }
92 else
93 {
94 /* We are STANDBY, they are ONLINE - OUR OWN HOT/COLD depends
95 * on THEIR degradation (we are backing THEM up). */
96 negotiator->own_state = negotiator->peer_channel_degraded ? SAPI_DUAL_STATE_COLDSTANDBY
98 negotiator->peer_state = SAPI_DUAL_STATE_ONLINE;
99 }
100 }
101 else if (negotiator->own_state == SAPI_DUAL_STATE_ONLINE)
102 {
103 /* Same "their HOT/COLD depends on OUR degradation" rule as the
104 * initial decision above, refreshed each round. */
105 negotiator->peer_state =
106 negotiator->own_channel_degraded ? SAPI_DUAL_STATE_COLDSTANDBY : SAPI_DUAL_STATE_HOTSTANDBY;
107 /* own_state stays ONLINE. */
108 }
109 else
110 {
111 /* own_state is already HOTSTANDBY or COLDSTANDBY: refine from
112 * the peer's latest channel_degraded bit; peer_state stays
113 * ONLINE (already decided). */
114 negotiator->own_state =
115 negotiator->peer_channel_degraded ? SAPI_DUAL_STATE_COLDSTANDBY : SAPI_DUAL_STATE_HOTSTANDBY;
116 negotiator->peer_state = SAPI_DUAL_STATE_ONLINE;
117 }
118
119 if (((negotiator->own_state != old_own) || (negotiator->peer_state != old_peer))
120 && (negotiator->state_change_callback != NULL))
121 {
122 negotiator->state_change_callback(negotiator->own_state, old_own, negotiator->peer_state, old_peer,
123 negotiator->state_change_callback_ctx);
124 }
125}
126
128 const sapi_dual_negotiator_config_t *config)
129{
130 sapi_timestamp_ms_t now_ms = 0U;
131
132 if ((negotiator == NULL) || (config == NULL) || (config->channel == NULL))
133 {
135 }
136
137 negotiator->channel = config->channel;
138 negotiator->own_id = config->own_id;
139 negotiator->peer_id = config->peer_id;
140 negotiator->peer_lost_timeout_ms = config->peer_lost_timeout_ms;
141 negotiator->state_change_callback = config->state_change_callback;
142 negotiator->state_change_callback_ctx = config->state_change_callback_ctx;
143
144 (void)sapi_timer_now(&now_ms);
145 negotiator->own_startup_timestamp_ms = now_ms;
146 negotiator->own_channel_degraded = false;
147
148 negotiator->have_peer_startup_timestamp = false;
149 negotiator->peer_startup_timestamp_ms = 0U;
150 negotiator->peer_channel_degraded = false;
151
152 negotiator->own_state = SAPI_DUAL_STATE_IDLE;
153 negotiator->peer_state = SAPI_DUAL_STATE_IDLE;
154
155 negotiator->have_last_peer_seen = false;
156 negotiator->last_peer_seen_ms = 0U;
157
158 return SAPI_STATUS_OK;
159}
160
162{
164 sapi_status_t status;
165
166 if (negotiator == NULL)
167 {
169 }
170
171 negotiator->own_channel_degraded =
173
174 (void)sapi_dual_channel_send_state_frame(negotiator->channel, negotiator->own_state,
175 negotiator->own_channel_degraded,
176 negotiator->own_startup_timestamp_ms);
177
178 status = sapi_dual_channel_receive_state_frame(negotiator->channel, receive_timeout_ms, &frame);
179 if (status == SAPI_STATUS_OK)
180 {
181 negotiator_process_peer_frame(negotiator, &frame);
182 }
183 /* Drain any additional already-buffered frames non-blockingly, so a
184 * burst arriving in one round doesn't leave stale ones unread past
185 * this call. */
186 while (sapi_dual_channel_receive_state_frame(negotiator->channel, 0U, &frame) == SAPI_STATUS_OK)
187 {
188 negotiator_process_peer_frame(negotiator, &frame);
189 }
190
191 negotiator_update_states(negotiator);
192
193 return SAPI_STATUS_OK;
194}
195
197{
198 if (negotiator == NULL)
199 {
201 }
202 return negotiator->own_state;
203}
204
206{
207 if (negotiator == NULL)
208 {
210 }
211 return negotiator->peer_state;
212}
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_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 w...
sapi_dual_state_t
State of one instance in a dual (two-redundant-instance) relationship, as decided by sapi_dual_negoti...
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_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 cur...
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...
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.
@ SAPI_DUAL_STATE_IDLE
@ SAPI_DUAL_STATE_ONLINE
@ SAPI_DUAL_STATE_COLDSTANDBY
@ SAPI_DUAL_STATE_HOTSTANDBY
@ SAPI_DUAL_STATE_UNKNOWN
@ SAPI_DUAL_CHANNEL_STATUS_FULL
sapi_status_t
Common result/status codes.
Definition sapi_status.h:27
@ SAPI_STATUS_INVALID_PARAM
Definition sapi_status.h:29
@ SAPI_STATUS_OK
Definition sapi_status.h:28
sapi_status_t sapi_timer_now(sapi_timestamp_ms_t *out_now_ms)
Returns the current monotonic time base used by all timers.
Definition sapi_timer.c:130
uint32_t sapi_duration_ms_t
Definition sapi_types.h:27
uint64_t sapi_timestamp_ms_t
Definition sapi_types.h:30
static bool negotiator_decide_online(uint32_t own_id, uint64_t own_ts, uint32_t peer_id, uint64_t peer_ts)
Older-timestamp-wins tie-break, own_id/peer_id as the deterministic fallback on an exact tie - same r...
Dual state negotiator of ADR-020: decides sapi_dual_state_t for both this instance and its peer,...
OS Abstraction Layer - Timer service.
Configuration for sapi_dual_negotiator_init().
sapi_dual_negotiator_state_change_callback_t state_change_callback
One sapi_dual_negotiator_t instance's state. Caller-owned storage; every field is private - reach it ...
SAPI_DUAL_FRAME_KIND_STATE payload - sapi_dual_negotiator_t's own periodic beacon.