Safe API Framework 0.1.0 build 384e6ae
Layered API framework for safety-related applications (ERTMS RBC reference targeting CENELEC EN 50128 SIL 4)
Loading...
Searching...
No Matches
rte_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(rte_dual_negotiator_t *negotiator, const rte_dual_state_frame_t *frame)
25{
26 rte_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)rte_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(rte_dual_negotiator_t *negotiator)
38{
39 rte_dual_state_t old_own = negotiator->own_state;
40 rte_dual_state_t old_peer = negotiator->peer_state;
41 bool peer_contact_recent = false;
42
43 if (negotiator->have_last_peer_seen)
44 {
45 rte_timestamp_ms_t now_ms = 0U;
46
47 (void)rte_timer_now(&now_ms);
48 if (now_ms >= negotiator->last_peer_seen_ms)
49 {
50 rte_timestamp_ms_t elapsed = now_ms - negotiator->last_peer_seen_ms;
51
52 peer_contact_recent = (elapsed < (rte_timestamp_ms_t)negotiator->peer_lost_timeout_ms);
53 }
54 /* else: clock moved backwards - no monotonic guarantee assumed
55 * (see rte_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 = RTE_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 = RTE_DUAL_STATE_UNKNOWN;
69 if (negotiator->own_state != RTE_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 = RTE_DUAL_STATE_UNKNOWN;
75 }
76 }
77 else if ((negotiator->own_state == RTE_DUAL_STATE_IDLE) || (negotiator->own_state == RTE_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 rte_dual_negotiator_execute()'s own doc. */
88 negotiator->own_state = RTE_DUAL_STATE_ONLINE;
89 negotiator->peer_state =
90 negotiator->own_channel_degraded ? RTE_DUAL_STATE_COLDSTANDBY : RTE_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 ? RTE_DUAL_STATE_COLDSTANDBY
98 negotiator->peer_state = RTE_DUAL_STATE_ONLINE;
99 }
100 }
101 else if (negotiator->own_state == RTE_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 ? RTE_DUAL_STATE_COLDSTANDBY : RTE_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 ? RTE_DUAL_STATE_COLDSTANDBY : RTE_DUAL_STATE_HOTSTANDBY;
116 negotiator->peer_state = RTE_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 rte_dual_negotiator_config_t *config)
129{
130 rte_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)rte_timer_now(&now_ms);
145 negotiator->own_startup_timestamp_ms =
146 (config->resume_own_startup_timestamp_ms != 0U) ? config->resume_own_startup_timestamp_ms : now_ms;
147 negotiator->own_channel_degraded = false;
148
149 negotiator->have_peer_startup_timestamp = false;
150 negotiator->peer_startup_timestamp_ms = 0U;
151 negotiator->peer_channel_degraded = false;
152
153 negotiator->own_state = config->resume_own_state; /* RTE_DUAL_STATE_IDLE (0) unless a resume was requested */
154 negotiator->peer_state = RTE_DUAL_STATE_IDLE; /* always fresh - see this field's own config doc */
155
156 negotiator->have_last_peer_seen = false;
157 negotiator->last_peer_seen_ms = 0U;
158
159 return RTE_STATUS_OK;
160}
161
163{
165 rte_status_t status;
166
167 if (negotiator == NULL)
168 {
170 }
171
172 negotiator->own_channel_degraded =
174
175 (void)rte_dual_channel_send_state_frame(negotiator->channel, negotiator->own_state,
176 negotiator->own_channel_degraded,
177 negotiator->own_startup_timestamp_ms);
178
179 status = rte_dual_channel_receive_state_frame(negotiator->channel, receive_timeout_ms, &frame);
180 if (status == RTE_STATUS_OK)
181 {
182 negotiator_process_peer_frame(negotiator, &frame);
183 }
184 /* Drain any additional already-buffered frames non-blockingly, so a
185 * burst arriving in one round doesn't leave stale ones unread past
186 * this call. */
187 while (rte_dual_channel_receive_state_frame(negotiator->channel, 0U, &frame) == RTE_STATUS_OK)
188 {
189 negotiator_process_peer_frame(negotiator, &frame);
190 }
191
192 negotiator_update_states(negotiator);
193
194 return RTE_STATUS_OK;
195}
196
198{
199 if (negotiator == NULL)
200 {
201 return RTE_DUAL_STATE_IDLE;
202 }
203 return negotiator->own_state;
204}
205
207{
208 if (negotiator == NULL)
209 {
210 return RTE_DUAL_STATE_IDLE;
211 }
212 return negotiator->peer_state;
213}
214
216{
217 if (negotiator == NULL)
218 {
219 return 0U;
220 }
221 return negotiator->own_startup_timestamp_ms;
222}
223
225{
226 if (negotiator == NULL)
227 {
228 return 0U;
229 }
230 return negotiator->peer_startup_timestamp_ms;
231}
232
234{
235 if (negotiator == NULL)
236 {
237 return 0U;
238 }
239 return negotiator->own_id;
240}
241
243{
244 if (negotiator == NULL)
245 {
246 return 0U;
247 }
248 return negotiator->peer_id;
249}
rte_status_t rte_dual_negotiator_init(rte_dual_negotiator_t *negotiator, const rte_dual_negotiator_config_t *config)
Initializes a rte_dual_negotiator_t. Captures this instance's own startup timestamp once (rte_timer_n...
rte_status_t rte_dual_channel_receive_state_frame(rte_dual_channel_t *channel, rte_duration_ms_t timeout_ms, rte_dual_state_frame_t *out_frame)
Returns the most recently staged inbound STATE frame, actively polling the configured links if none w...
uint32_t rte_dual_negotiator_get_own_id(const rte_dual_negotiator_t *negotiator)
Returns this instance's own tie-break identifier (rte_dual_negotiator_config_t::own_id at init time)....
uint32_t rte_dual_negotiator_get_peer_id(const rte_dual_negotiator_t *negotiator)
Returns the expected peer's tie-break identifier (rte_dual_negotiator_config_t::peer_id at init time)...
rte_dual_state_t rte_dual_negotiator_get_peer_state(const rte_dual_negotiator_t *negotiator)
Returns this instance's last-known view of the peer's rte_dual_state_t.
rte_status_t rte_dual_negotiator_execute(rte_dual_negotiator_t *negotiator, rte_duration_ms_t receive_timeout_ms)
Drives one round of state negotiation: refreshes own_channel_degraded from the attached channel's cur...
rte_dual_state_t
State of one instance in a dual (two-redundant-instance) relationship, as decided by rte_dual_negotia...
uint64_t rte_dual_negotiator_get_own_startup_timestamp_ms(const rte_dual_negotiator_t *negotiator)
Returns this instance's own startup timestamp (captured at rte_dual_negotiator_init() time,...
rte_dual_channel_status_t rte_dual_channel_get_status(const rte_dual_channel_t *channel)
Returns the aggregate connection status across every configured redundant link, as of the most recent...
rte_dual_state_t rte_dual_negotiator_get_own_state(const rte_dual_negotiator_t *negotiator)
Returns this instance's own current rte_dual_state_t.
uint64_t rte_dual_negotiator_get_peer_startup_timestamp_ms(const rte_dual_negotiator_t *negotiator)
Returns this instance's last-known view of the peer's own startup timestamp (0/unset until the first ...
rte_status_t rte_dual_channel_send_state_frame(rte_dual_channel_t *channel, rte_dual_state_t state, bool channel_degraded, uint64_t timestamp_ms)
Sends a rte_dual_state_frame_t (rte_dual_frames.h) on every configured redundant link - fire-and-forg...
@ RTE_DUAL_CHANNEL_STATUS_FULL
@ RTE_DUAL_STATE_HOTSTANDBY
@ RTE_DUAL_STATE_UNKNOWN
@ RTE_DUAL_STATE_ONLINE
@ RTE_DUAL_STATE_IDLE
@ RTE_DUAL_STATE_COLDSTANDBY
rte_status_t
Common result/status codes.
Definition rte_status.h:27
@ RTE_STATUS_OK
Definition rte_status.h:28
@ RTE_STATUS_INVALID_PARAM
Definition rte_status.h:29
rte_status_t rte_timer_now(rte_timestamp_ms_t *out_now_ms)
Returns the current monotonic time base used by all timers.
Definition rte_timer.c:139
uint32_t rte_duration_ms_t
Definition rte_types.h:27
uint64_t rte_timestamp_ms_t
Definition rte_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 rte_dual_state_t for both this instance and its peer,...
OS Abstraction Layer - Timer service.
Configuration for rte_dual_negotiator_init().
rte_dual_negotiator_state_change_callback_t state_change_callback
One rte_dual_negotiator_t instance's state. Caller-owned storage; every field is private - reach it o...
RTE_DUAL_FRAME_KIND_STATE payload - rte_dual_negotiator_t's own periodic beacon.