RTE RBC 2oo2 Reference Application 0.1.0 build 0006685
SIL 4 2oo2 Dual-Site Fault-Tolerant ERTMS Radio Block Centre Reference Architecture
Loading...
Searching...
No Matches
ab_gp_session_fsm.c
Go to the documentation of this file.
1
5
6#include "ab_gp_session_fsm.h"
7
8#include "rte/oal/log/rte_log.h"
9#include "rte/utils/string/rte_string.h"
10
11#define AB_GP_SESSION_FSM_STATE_COUNT 9U
12#define AB_GP_SESSION_FSM_NONE (-1)
13
29 /* AB_GP_ERTMS_SESSION_IDLE - MA_GRANTED and SHUNTING_AUTHORIZED are ALSO
30 * handled directly from here (real, found-live correction, same class
31 * as ESTABLISHED's own below): ctx->session_state is driven forward
32 * (IDLE -> INITIATED -> ESTABLISHED) ONLY by a real ERTMS-envelope
33 * Msg 155 (ab_gp_proc_dispatch.c's handle_msg_155() - confirmed the
34 * ONLY call site of ab_gp_proc_session_handle_initiation()/
35 * _established() anywhere in this codebase). The flat/simplified wire
36 * scheme's own connect handshake (RBC_MSG_P0, ab_gp_train.c) and its
37 * own position reports (ab_gp_proc_position_update(), which resets
38 * contact_timer_ms but never touches session_state) never call either
39 * function - so a train connected purely via the flat scheme (the
40 * flow safeAPIRBC2oo2TestEnv's own il_grants_a_movement_authority.robot
41 * and most of ma_tests/ actually exercise: Connect Train -> P0, then
42 * a bare flat position report, then an IL-side ROUTE_ADD triggers the
43 * grant with NO Msg 155 ever sent) has session_state sitting at IDLE
44 * for its entire lifetime. Requiring ESTABLISHED-or-later here (this
45 * table's own SECOND version, corrected once) would have made every
46 * MA grant on that flow permanently unreachable too. This IS a real,
47 * documented gap (two not-fully-integrated connection-tracking
48 * mechanisms - ab_gp_db_train_is_connected() vs this FSM's own
49 * session_state), flagged for a follow-up that wires the flat
50 * scheme's own P0/connect path into this FSM for real, not silently
51 * routed around forever. */
52 {
53 AB_GP_ERTMS_SESSION_INITIATED, /* SESSION_INITIATION_REQUESTED */
54 AB_GP_SESSION_FSM_NONE, /* SESSION_ESTABLISHED_CONFIRMED */
55 AB_GP_SESSION_FSM_NONE, /* TRAIN_DATA_VALIDATED */
56 AB_GP_ERTMS_SESSION_FS, /* MA_GRANTED */
57 AB_GP_SESSION_FSM_NONE, /* MA_REVOKED */
58 AB_GP_ERTMS_SESSION_SHUNTING, /* SHUNTING_AUTHORIZED */
59 AB_GP_SESSION_FSM_NONE, /* SHUNTING_DEAUTHORIZED */
60 AB_GP_ERTMS_SESSION_EMERGENCY_STOP, /* EMERGENCY_STOP_ISSUED - legal from every state, see header doc */
61 AB_GP_SESSION_FSM_NONE, /* EMERGENCY_STOP_REVOKED_TO_FS */
62 AB_GP_SESSION_FSM_NONE, /* EMERGENCY_STOP_REVOKED_TO_STANDBY */
63 AB_GP_SESSION_FSM_NONE, /* EMERGENCY_STOP_REVOKED_TO_ESTABLISHED */
64 AB_GP_ERTMS_SESSION_IDLE /* SESSION_TERMINATED - legal from every state incl. self, see header doc */
65 },
66 /* AB_GP_ERTMS_SESSION_INITIATED */
67 {
69 AB_GP_ERTMS_SESSION_ESTABLISHED, /* SESSION_ESTABLISHED_CONFIRMED */
70 AB_GP_ERTMS_SESSION_DATA_VALIDATED, /* TRAIN_DATA_VALIDATED - can arrive before negotiation completes */
80 },
81 /* AB_GP_ERTMS_SESSION_ESTABLISHED - real, found-live correction: MA_GRANTED
82 * and SHUNTING_AUTHORIZED are ALSO handled directly from here, not only
83 * from DATA_VALIDATED/STANDBY. This codebase's own real connect path
84 * (ab_gp_proc_dispatch.c's handle_msg_155(), the wired Msg 155 handler)
85 * documents driving straight IDLE -> INITIATED -> ESTABLISHED and
86 * deliberately never validating train data ("this project's own
87 * simplified flow has no separate train-side confirmation round trip
88 * before Full Supervision work can begin... drive straight to
89 * ESTABLISHED") - train_data.valid is ONLY ever set by the separate,
90 * real-ERTMS-envelope Msg 129 path (ab_gp_proc_dispatch.c's
91 * handle_msg_129()), which the simplified flow never sends. Requiring
92 * DATA_VALIDATED here (this table's own first version) would have made
93 * every MA grant on the simplified flow - the one the real robot MA
94 * test suite (il_grants_a_movement_authority.robot and its siblings)
95 * actually exercises - permanently unreachable. Caught by tracing the
96 * real dispatch code before writing new robot coverage for this
97 * table's own earlier tightening, not by running the suite itself. */
98 {
100 AB_GP_SESSION_FSM_NONE, /* re-confirmation while already ESTABLISHED: not a transition -
101 * caller (ab_gp_proc_session_handle_established()) treats
102 * "no outcome" as "already active, just refresh". */
104 AB_GP_ERTMS_SESSION_FS, /* MA_GRANTED */
106 AB_GP_ERTMS_SESSION_SHUNTING, /* SHUNTING_AUTHORIZED */
113 },
114 /* AB_GP_ERTMS_SESSION_DATA_VALIDATED */
115 {
119 AB_GP_ERTMS_SESSION_FS, /* MA_GRANTED - first grant */
121 AB_GP_ERTMS_SESSION_SHUNTING, /* SHUNTING_AUTHORIZED - before any MA was ever granted */
128 },
129 /* AB_GP_ERTMS_SESSION_STANDBY (no active MA, session otherwise fine -
130 * reached via MA_REVOKED or SHUNTING_DEAUTHORIZED) */
131 {
135 AB_GP_ERTMS_SESSION_FS, /* MA_GRANTED - (re)grant */
144 },
145 /* AB_GP_ERTMS_SESSION_FS (active MA) */
146 {
150 AB_GP_ERTMS_SESSION_FS, /* MA_GRANTED - self, re-granting/recomputing while already FS */
151 AB_GP_ERTMS_SESSION_STANDBY, /* MA_REVOKED - full revocation */
159 },
160 /* AB_GP_ERTMS_SESSION_SHUNTING */
161 {
168 AB_GP_ERTMS_SESSION_STANDBY, /* SHUNTING_DEAUTHORIZED */
174 },
175 /* AB_GP_ERTMS_SESSION_EMERGENCY_STOP */
176 {
184 AB_GP_ERTMS_SESSION_EMERGENCY_STOP, /* EMERGENCY_STOP_ISSUED - self, re-issuing while already active */
185 AB_GP_ERTMS_SESSION_FS, /* EMERGENCY_STOP_REVOKED_TO_FS */
186 AB_GP_ERTMS_SESSION_STANDBY, /* EMERGENCY_STOP_REVOKED_TO_STANDBY */
187 AB_GP_ERTMS_SESSION_ESTABLISHED, /* EMERGENCY_STOP_REVOKED_TO_ESTABLISHED */
189 },
190 /* AB_GP_ERTMS_SESSION_TERMINATING - never actually SET by any procedure
191 * today (ab_gp_proc_session_handle_termination() goes straight to
192 * IDLE) - only ever a legal SOURCE for re-initiation. Kept here,
193 * unreachable in practice today, for whenever a real intermediate
194 * TERMINATING step is added. */
195 {
208 }
209};
210
213{
214 int8_t result;
215
216 if (((uint32_t)from >= AB_GP_SESSION_FSM_STATE_COUNT) || ((uint32_t)event >= (uint32_t)AB_GP_SESSION_EVENT_COUNT))
217 {
218 return false;
219 }
220 result = s_table[(uint32_t)from][(uint32_t)event];
221 if (result == AB_GP_SESSION_FSM_NONE)
222 {
223 return false;
224 }
225 if (out_to != NULL)
226 {
227 *out_to = (ab_gp_ertms_session_state_t)result;
228 }
229 return true;
230}
231
237{
238 switch (state)
239 {
240 case AB_GP_ERTMS_SESSION_IDLE: return "IDLE";
241 case AB_GP_ERTMS_SESSION_INITIATED: return "INITIATED";
242 case AB_GP_ERTMS_SESSION_ESTABLISHED: return "ESTABLISHED";
243 case AB_GP_ERTMS_SESSION_DATA_VALIDATED: return "DATA_VALIDATED";
244 case AB_GP_ERTMS_SESSION_STANDBY: return "STANDBY";
245 case AB_GP_ERTMS_SESSION_FS: return "FS";
246 case AB_GP_ERTMS_SESSION_SHUNTING: return "SHUNTING";
247 case AB_GP_ERTMS_SESSION_EMERGENCY_STOP: return "EMERGENCY_STOP";
248 case AB_GP_ERTMS_SESSION_TERMINATING: return "TERMINATING";
249 default: return "UNKNOWN";
250 }
251}
252
253static const char *event_name(ab_gp_session_event_t event)
254{
255 switch (event)
256 {
257 case AB_GP_SESSION_EVENT_SESSION_INITIATION_REQUESTED: return "SESSION_INITIATION_REQUESTED";
258 case AB_GP_SESSION_EVENT_SESSION_ESTABLISHED_CONFIRMED: return "SESSION_ESTABLISHED_CONFIRMED";
259 case AB_GP_SESSION_EVENT_TRAIN_DATA_VALIDATED: return "TRAIN_DATA_VALIDATED";
260 case AB_GP_SESSION_EVENT_MA_GRANTED: return "MA_GRANTED";
261 case AB_GP_SESSION_EVENT_MA_REVOKED: return "MA_REVOKED";
262 case AB_GP_SESSION_EVENT_SHUNTING_AUTHORIZED: return "SHUNTING_AUTHORIZED";
263 case AB_GP_SESSION_EVENT_SHUNTING_DEAUTHORIZED: return "SHUNTING_DEAUTHORIZED";
264 case AB_GP_SESSION_EVENT_EMERGENCY_STOP_ISSUED: return "EMERGENCY_STOP_ISSUED";
265 case AB_GP_SESSION_EVENT_EMERGENCY_STOP_REVOKED_TO_FS: return "EMERGENCY_STOP_REVOKED_TO_FS";
266 case AB_GP_SESSION_EVENT_EMERGENCY_STOP_REVOKED_TO_STANDBY: return "EMERGENCY_STOP_REVOKED_TO_STANDBY";
267 case AB_GP_SESSION_EVENT_EMERGENCY_STOP_REVOKED_TO_ESTABLISHED: return "EMERGENCY_STOP_REVOKED_TO_ESTABLISHED";
268 case AB_GP_SESSION_EVENT_SESSION_TERMINATED: return "SESSION_TERMINATED";
269 default: return "UNKNOWN_EVENT";
270 }
271}
272
281static void log_event(const ab_gp_ertms_train_context_t *ctx, ab_gp_session_event_t event, bool handled,
283{
284 char storage[128];
285 rte_string_t line;
286 const char *out_cstr = NULL;
287
288 if (rte_string_init(&line, storage, sizeof(storage)) != RTE_STATUS_OK)
289 {
290 return;
291 }
292 (void)rte_string_copy(&line, "train=");
293 (void)rte_string_append_u32(&line, (uint32_t)ctx->train_id);
294 (void)rte_string_concat(&line, " event=");
295 (void)rte_string_concat(&line, event_name(event));
296 (void)rte_string_concat(&line, " state=");
297 (void)rte_string_concat(&line, state_name(ctx->session_state));
298 if (handled)
299 {
300 (void)rte_string_concat(&line, " -> ");
301 (void)rte_string_concat(&line, state_name(to));
302 }
303 else
304 {
305 (void)rte_string_concat(&line, " NOT HANDLED");
306 }
307
308 if (rte_string_c_str(&line, &out_cstr) == RTE_STATUS_OK)
309 {
310 rte_log_write(RTE_LOG_LEVEL_DEBUG, "SESSION_FSM", out_cstr);
311 }
312}
313
315{
317 bool handled;
318
319 if (ctx == NULL)
320 {
321 return false;
322 }
323
324 handled = ab_gp_session_fsm_can_handle(ctx->session_state, event, &to);
325 log_event(ctx, event, handled, to);
326
327 if (!handled)
328 {
329 return false;
330 }
331 ctx->session_state = to;
332 return true;
333}
@ AB_GP_SESSION_EVENT_EMERGENCY_STOP_ISSUED
Definition ab_gp_event.h:62
@ AB_GP_SESSION_EVENT_SESSION_TERMINATED
Definition ab_gp_event.h:66
@ AB_GP_SESSION_EVENT_SESSION_INITIATION_REQUESTED
Definition ab_gp_event.h:55
@ AB_GP_SESSION_EVENT_EMERGENCY_STOP_REVOKED_TO_ESTABLISHED
Definition ab_gp_event.h:65
@ AB_GP_SESSION_EVENT_EMERGENCY_STOP_REVOKED_TO_STANDBY
Definition ab_gp_event.h:64
@ AB_GP_SESSION_EVENT_SHUNTING_AUTHORIZED
Definition ab_gp_event.h:60
@ AB_GP_SESSION_EVENT_MA_REVOKED
Definition ab_gp_event.h:59
@ AB_GP_SESSION_EVENT_SESSION_ESTABLISHED_CONFIRMED
Definition ab_gp_event.h:56
@ AB_GP_SESSION_EVENT_MA_GRANTED
Definition ab_gp_event.h:58
@ AB_GP_SESSION_EVENT_EMERGENCY_STOP_REVOKED_TO_FS
Definition ab_gp_event.h:63
@ AB_GP_SESSION_EVENT_COUNT
Definition ab_gp_event.h:68
@ AB_GP_SESSION_EVENT_TRAIN_DATA_VALIDATED
Definition ab_gp_event.h:57
@ AB_GP_SESSION_EVENT_SHUNTING_DEAUTHORIZED
Definition ab_gp_event.h:61
bool ab_gp_session_fsm_handle_event(ab_gp_ertms_train_context_t *ctx, ab_gp_session_event_t event)
Applies event to ctx->session_state.
static const int8_t s_table[AB_GP_SESSION_FSM_STATE_COUNT][AB_GP_SESSION_EVENT_COUNT]
s_table[from][event] is the resulting state, or AB_GP_SESSION_FSM_NONE if this table defines no outco...
static void log_event(const ab_gp_ertms_train_context_t *ctx, ab_gp_session_event_t event, bool handled, ab_gp_ertms_session_state_t to)
#define AB_GP_SESSION_FSM_NONE
bool ab_gp_session_fsm_can_handle(ab_gp_ertms_session_state_t from, ab_gp_session_event_t event, ab_gp_ertms_session_state_t *out_to)
Read-only lookup, no side effect: what event does from a given state produce, if anything.
#define AB_GP_SESSION_FSM_STATE_COUNT
static const char * event_name(ab_gp_session_event_t event)
static const char * state_name(ab_gp_ertms_session_state_t state)
Event-driven legal-transition table for ab_gp_ertms_session_state_t (Subset-026 Chapter 5's various p...
ab_gp_event_t ab_gp_session_event_t
The events this state machine reacts to are the leading (session-lifecycle) block of the shared ab_gp...
ab_gp_ertms_session_state_t
ERTMS On-Board / RBC Session Lifecycle States.
@ AB_GP_ERTMS_SESSION_ESTABLISHED
@ AB_GP_ERTMS_SESSION_FS
@ AB_GP_ERTMS_SESSION_SHUNTING
@ AB_GP_ERTMS_SESSION_INITIATED
@ AB_GP_ERTMS_SESSION_TERMINATING
@ AB_GP_ERTMS_SESSION_DATA_VALIDATED
@ AB_GP_ERTMS_SESSION_IDLE
@ AB_GP_ERTMS_SESSION_STANDBY
@ AB_GP_ERTMS_SESSION_EMERGENCY_STOP
Complete ERTMS state and procedure context for one train slot.
ab_gp_ertms_session_state_t session_state