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_proc_ma.c
Go to the documentation of this file.
1
5
6#include "ab_gp_proc_ma.h"
8#include "../state_machines/session/ab_gp_session_fsm.h" /* ab_gp_session_fsm_can_handle() -
9 * a pure legality query, not an
10 * event dispatch (see line ~205). */
11#include "rte/oal/memory/rte_mem_util.h"
12#include "rte/utils/cast/rte_cast.h"
13
31static bool distance_from_lrbg_to_route_end(const ab_site_ga_route_layout_t *route, uint32_t nid_lrbg,
32 uint32_t *out_dist_m)
33{
34 const ab_site_balise_t *balise = ab_site_get_balise(nid_lrbg);
35 const ab_site_signal_t *end_sig = ab_site_get_signal(route->end_signal);
36
37 if ((balise == NULL) || (end_sig == NULL) || (out_dist_m == NULL))
38 {
39 return false;
40 }
41
42 if (end_sig->direction == AB_SITE_DIR_REVERSE)
43 {
44 *out_dist_m = (balise->chainage_m > end_sig->chainage_m) ? (balise->chainage_m - end_sig->chainage_m) : 0U;
45 }
46 else
47 {
48 *out_dist_m = (end_sig->chainage_m > balise->chainage_m) ? (end_sig->chainage_m - balise->chainage_m) : 0U;
49 }
50 return true;
51}
52
53uint32_t ab_gp_proc_ma_remaining_length_m(const ab_site_ga_route_layout_t *route, uint32_t nid_lrbg, uint32_t d_lrbg_m)
54{
55 uint32_t remaining_m = 0U;
56
57 /* @p d_lrbg_m (distance since the train's own last-reported balise)
58 * is deliberately NOT part of this computation, on direct
59 * instruction: MA length is measured from the train's last KNOWN
60 * balise - a discrete, site-data-backed reference point - not from a
61 * continuously self-reported odometer offset past it. Kept as a
62 * parameter (unused) rather than dropped from the signature - both
63 * call sites below already resolve it from ctx->position/session
64 * state and passing it costs nothing; a future revision may want it
65 * back for a tighter bound. */
66 (void)d_lrbg_m;
67
68 if (route == NULL)
69 {
70 return 0U;
71 }
72
73 if (!distance_from_lrbg_to_route_end(route, nid_lrbg, &remaining_m))
74 {
75 return route->length_m; /* cannot resolve position within this route - see this function's own doc */
76 }
77
78 /* Not clamped to route->length_m: a balise before the start signal legitimately has more than the authored
79 * route length to the end signal (see distance_from_lrbg_to_route_end()). */
80 return remaining_m;
81}
82
84 const ab_site_ga_route_layout_t *route,
85 uint32_t ma_seq)
86{
87 uint8_t i;
88 const ab_site_segment_t *seg;
89 const ab_site_balise_t *balise;
90 uint32_t cum_dist = 0U;
91 uint32_t remaining_m;
92
93 if (ctx == NULL || route == NULL)
94 {
95 return false;
96 }
97
98 /* Checked FIRST, before any of the packet-15/21/27/5 computation below
99 * mutates ctx->ma - a rejected grant must leave ctx untouched, not
100 * half-populate it then discover the event has no outcome. MA_GRANTED
101 * is handled from ESTABLISHED/DATA_VALIDATED/STANDBY/FS
102 * (ab_gp_session_fsm.c's own table, FS itself included since
103 * ab_gp_proc_ma_compute() can legitimately run again while already FS).
104 * ESTABLISHED is included deliberately, not left off "for strictness":
105 * this codebase's own real connect path (ab_gp_proc_dispatch.c's
106 * handle_msg_155()) drives straight to ESTABLISHED and never validates
107 * train data at all (see that function's own doc) - requiring
108 * DATA_VALIDATED here would make every real MA grant on that path
109 * unreachable. Still a real tightening versus the old unconditional
110 * assignment: IDLE/INITIATED/SHUNTING/EMERGENCY_STOP/TERMINATING can no
111 * longer jump a train straight to Full Supervision. A query only
112 * (can_handle, not handle_event) - the actual transition is applied
113 * below, once this function's own work has succeeded, not here. */
115 {
116 return false;
117 }
118
119 remaining_m = ab_gp_proc_ma_remaining_length_m(route, ctx->position.nid_lrbg, ctx->position.d_lrbg_m);
120
121 /* remaining_m is the distance to the end signal (the Supervised
122 * Location). The granted End/Limit of Authority sits one overlap
123 * margin short of it - see RTE_EXAMPLE_MA_OVERLAP_M's own doc.
124 * Subtracted here once; ab_gp_proc_ma_extend() then just adds each
125 * further route's own length, so the single margin carries to
126 * whatever the new last end signal is. */
127 if (remaining_m > RTE_EXAMPLE_MA_OVERLAP_M)
128 {
129 remaining_m -= RTE_EXAMPLE_MA_OVERLAP_M;
130 }
131 else
132 {
133 remaining_m = 0U;
134 }
135
136 rte_mem_set(&ctx->ma, 0, sizeof(ctx->ma));
137
138 ctx->ma.has_active_ma = true;
139 ctx->ma.ma_seq = ma_seq;
140 ctx->ma.ma_length_m = remaining_m;
141 ctx->ma.v_loa_kmh = 0U;
142 ctx->ma.t_loa_s = 60U;
143 ctx->ma.start_signal = route->start_signal;
144 ctx->ma.end_signal = route->end_signal;
145 ctx->ma.route_count = 1U;
146 ctx->ma.route_ids[0] = route->id;
147
148 /* 1. Packet 15 (Movement Authority) */
149 ctx->ma.p15.ma_seq = ma_seq;
150 ctx->ma.p15.ma_length = remaining_m;
151 ctx->ma.p15.v_loa = 0U;
152 ctx->ma.p15.t_loa_s = 60U;
153 ctx->ma.p15.has_section_timer = false;
154
155 /* 2. Packet 21 (Gradient Profile along constituent segments) */
156 ctx->ma.p21.segment_count = 0U;
157 cum_dist = 0U;
158 for (i = 0U; (i < route->segment_count) && (i < (uint8_t)RTE_EXAMPLE_MAX_SEGMENTS_PER_ROUTE) && (i < (uint8_t)RBC_MAX_GRADIENT_SEGMENTS); i++)
159 {
160 seg = ab_site_get_segment(route->segment_ids[i]);
161 if (seg != NULL)
162 {
163 int8_t g_a = 0;
164 ctx->ma.p21.segments[ctx->ma.p21.segment_count].d_gradient = cum_dist;
165 (void)rte_cast_i32_to_i8((int32_t)seg->gradient_permil, &g_a);
166 ctx->ma.p21.segments[ctx->ma.p21.segment_count].g_a = g_a;
167 ctx->ma.p21.segment_count++;
168 (void)rte_cast_checked_add_u32(cum_dist, seg->length_m, &cum_dist);
169 }
170 }
171
172 /* 3. Packet 27 (Static Speed Profile along constituent segments) */
173 ctx->ma.p27.segment_count = 0U;
174 cum_dist = 0U;
175 for (i = 0U; (i < route->segment_count) && (i < (uint8_t)RTE_EXAMPLE_MAX_SEGMENTS_PER_ROUTE) && (i < (uint8_t)RBC_MAX_SPEED_SEGMENTS); i++)
176 {
177 seg = ab_site_get_segment(route->segment_ids[i]);
178 if (seg != NULL)
179 {
180 uint8_t v_static = 0U;
181 ctx->ma.p27.segments[ctx->ma.p27.segment_count].d_static = cum_dist;
182 (void)rte_cast_u32_to_u8(seg->v_max_kmh / 5U, &v_static);
183 ctx->ma.p27.segments[ctx->ma.p27.segment_count].v_static = v_static;
184 ctx->ma.p27.segment_count++;
185 (void)rte_cast_checked_add_u32(cum_dist, seg->length_m, &cum_dist);
186 }
187 }
188
189 /* 4. Packet 5 (Balise Linking Information) */
190 ctx->ma.p5.link_count = 0U;
191 for (i = 0U; (i < route->balise_count) && (i < (uint8_t)RTE_EXAMPLE_MAX_BALISES_PER_ROUTE) && (i < (uint8_t)RBC_MAX_LINKING_ITEMS); i++)
192 {
193 balise = ab_site_get_balise(route->balise_nid_lrbgs[i]);
194 if (balise != NULL)
195 {
196 uint16_t nid_bg = 0U;
197 (void)rte_cast_u32_to_u16(balise->nid_lrbg & 0x3FFFU, &nid_bg);
198 ctx->ma.p5.links[ctx->ma.p5.link_count].nid_bg = nid_bg;
199 ctx->ma.p5.links[ctx->ma.p5.link_count].d_link = balise->pos_m;
201 ctx->ma.p5.link_count++;
202 }
203 }
204
205 /* Advance train operational state to Full Supervision. Already
206 * confirmed handleable above (can_handle, no side effect) before any
207 * of this function's own work started - this call can therefore only
208 * fail on ctx == NULL, already excluded at function entry. */
210
211 return true;
212}
213
215 const ab_site_ga_route_layout_t *additional_route)
216{
217 if (ctx == NULL || additional_route == NULL || !ctx->ma.has_active_ma)
218 {
219 return false;
220 }
221
223 {
224 return false;
225 }
226
227 /* The extended MA is measured from the same LRBG as the first grant: distance(LRBG -> the new last end signal)
228 * minus one overlap, not the old length plus the added route's authored length (which drifts whenever the
229 * authored lengthM differs from the chainage). It replaces the old value: that one was relative to an earlier
230 * LRBG, and keeping it (a max) would overshoot the new end signal once the train has moved on. */
231 {
232 uint32_t total_m = ab_gp_proc_ma_remaining_length_m(additional_route, ctx->position.nid_lrbg,
233 ctx->position.d_lrbg_m);
234 total_m = (total_m > RTE_EXAMPLE_MA_OVERLAP_M) ? (total_m - RTE_EXAMPLE_MA_OVERLAP_M) : 0U;
235 ctx->ma.ma_length_m = total_m;
236 }
237 ctx->ma.ma_seq++;
238 ctx->ma.end_signal = additional_route->end_signal;
239 ctx->ma.route_ids[ctx->ma.route_count] = additional_route->id;
240 ctx->ma.route_count++;
241
242 ctx->ma.p15.ma_seq = ctx->ma.ma_seq;
243 ctx->ma.p15.ma_length = ctx->ma.ma_length_m;
244
245 return true;
246}
247
249 uint32_t new_ma_length_m)
250{
251 if (ctx == NULL || !ctx->ma.has_active_ma)
252 {
253 return false;
254 }
255
256 if (new_ma_length_m < ctx->ma.ma_length_m)
257 {
258 ctx->ma.ma_length_m = new_ma_length_m;
259 ctx->ma.p15.ma_length = new_ma_length_m;
260 return true;
261 }
262
263 return false;
264}
265
267{
268 if (ctx == NULL || !ctx->ma.has_active_ma)
269 {
270 return false;
271 }
272
273 return (ma_seq == ctx->ma.ma_seq);
274}
275
277{
278 if (ctx == NULL)
279 {
280 return;
281 }
282
283 ctx->ma.has_active_ma = false;
284 ctx->ma.ma_length_m = 0U;
285 ctx->ma.route_count = 0U;
286 /* No outer guard needed - the table only defines an outcome for
287 * MA_REVOKED from FS, so it is a harmless no-op from anywhere else
288 * (e.g. an already-STANDBY train with no MA to begin with). */
290}
bool ab_gp_dispatch(ab_gp_ertms_train_context_t *ctx, ab_gp_event_t event)
Raises event to every state machine subscribed to it, in subscription-table order,...
Synchronous, statically-wired event dispatch for GP's per-entity procedure state machines (state_mach...
#define RBC_MAX_GRADIENT_SEGMENTS
#define RBC_MAX_LINKING_ITEMS
#define RBC_MAX_SPEED_SEGMENTS
@ AB_GP_SESSION_EVENT_MA_REVOKED
Definition ab_gp_event.h:59
@ AB_GP_SESSION_EVENT_MA_GRANTED
Definition ab_gp_event.h:58
bool ab_gp_proc_ma_shorten(ab_gp_ertms_train_context_t *ctx, uint32_t new_ma_length_m)
Shortens an existing Movement Authority (Subset-026 §5.5.4 / Msg 9).
bool ab_gp_proc_ma_extend(ab_gp_ertms_train_context_t *ctx, const ab_site_ga_route_layout_t *additional_route)
Extends an existing Movement Authority with an additional connected route.
bool ab_gp_proc_ma_compute(ab_gp_ertms_train_context_t *ctx, const ab_site_ga_route_layout_t *route, uint32_t ma_seq)
Computes and grants a new Movement Authority based on site track layout.
void ab_gp_proc_ma_revoke(ab_gp_ertms_train_context_t *ctx)
Revokes active Movement Authority (e.g. during emergency or termination).
bool ab_gp_proc_ma_handle_ack(ab_gp_ertms_train_context_t *ctx, uint32_t ma_seq)
Handles Movement Authority Acknowledgement (Msg 146).
uint32_t ab_gp_proc_ma_remaining_length_m(const ab_site_ga_route_layout_t *route, uint32_t nid_lrbg, uint32_t d_lrbg_m)
Computes the Movement Authority length actually REMAINING from a train's own last-KNOWN balise to rou...
static bool distance_from_lrbg_to_route_end(const ab_site_ga_route_layout_t *route, uint32_t nid_lrbg, uint32_t *out_dist_m)
Distance, in meters, from nid_lrbg's own balise to route's END signal, measured along the route's dir...
ERTMS Subset-026 §5.5 Movement Authority (MA) Management Procedure.
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.
Event-driven legal-transition table for ab_gp_ertms_session_state_t (Subset-026 Chapter 5's various p...
#define RTE_EXAMPLE_MAX_ROUTES_PER_TRAIN
#define RTE_EXAMPLE_MA_OVERLAP_M
@ RBC_Q_DIR_NOMINAL
rbc_packet_21_gradient_profile_t p21
uint16_t route_ids[RTE_EXAMPLE_MAX_ROUTES_PER_TRAIN]
rbc_packet_5_linking_t p5
rbc_packet_27_ssp_t p27
Complete ERTMS state and procedure context for one train slot.
ab_gp_ertms_session_state_t session_state
ab_gp_ertms_position_record_t position
ab_gp_ertms_ma_record_t ma
rbc_gradient_segment_t segments[RBC_MAX_GRADIENT_SEGMENTS]
rbc_speed_segment_t segments[RBC_MAX_SPEED_SEGMENTS]
rbc_linking_item_t links[RBC_MAX_LINKING_ITEMS]