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_train.c
Go to the documentation of this file.
1
7#include "ab_gp_train.h"
8
9#include <stdio.h>
10
11#include "rte/utils/buffer/rte_buffer.h"
12#include "rte/utils/cast/rte_cast.h"
13#include "rte/utils/lifecycle/rte_lifecycle.h"
14#include "rte/oal/log/rte_log.h"
15#include "rte/oal/memory/rte_mem_util.h"
16#include "rte/utils/notify/rte_notify.h"
17#include "ab_siteGP.h"
18
19#include "../com/ab_gp_channel_payload.h" /* ab_gp_channel_payload_register_collector() - see ab_gp_train_init() below */
22
23/* GA notification registry - module-private storage, same "static
24 * singleton" convention as ab_gp_channel.c's own g_ab_ctx. See ab_gp_train.h's
25 * own "GA notification" doc for why this file fires it (it owns the
26 * mutations) and rte_notify.h's own doc (safeAPIFreamwork, ADR-030) for
27 * the storage shape/why init/register/dispatch are hand-written. ONE list
28 * (was two) - interface-simplification pass, see ab_gp_train.h's own doc. */
31
35static bool train_id_in_range(uint8_t train_id);
36
37static ab_gp_train_context_list_t g_train_context_subscribers;
38
40{
41 rte_status_t lifecycle_status;
42
43 if (fn == NULL)
44 {
45 return RTE_STATUS_INVALID_PARAM;
46 }
47 lifecycle_status = rte_lifecycle_check_setup_allowed();
48 if (lifecycle_status != RTE_STATUS_OK)
49 {
50 return lifecycle_status;
51 }
53 {
54 return RTE_STATUS_RESOURCE_EXHAUSTED;
55 }
57 g_train_context_subscribers.slots[g_train_context_subscribers.count].context = (void *)context; /* see
58 AB/GP/ma_logic/ab_gp_logic.c's own identical cast for why this is safe */
60 return RTE_STATUS_OK;
61}
62
73static bool build_train_context(const ab_gp_train_session_t sessions[], const ab_gp_db_t *db, uint8_t train_id,
74 ab_gp_train_context_t *out_ctx)
75{
76 uint32_t i;
77 const ab_gp_train_session_t *s;
78 const ab_gp_db_train_t *t;
79
80 if (!train_id_in_range(train_id))
81 {
82 return false;
83 }
84 s = &sessions[train_id - 1U];
85 t = &db->trains[train_id - 1U];
86
87 out_ctx->train_id = train_id;
88 out_ctx->in_use = s->in_use;
89 out_ctx->cycle = s->cycle;
90 out_ctx->d_lrbg = s->d_lrbg;
91 out_ctx->nid_lrbg = t->last_nid_lrbg;
92 out_ctx->v_train = s->v_train;
93 out_ctx->m_mode = s->m_mode;
94 out_ctx->granted_length = s->granted_length;
95 out_ctx->ma_seq = s->ma_seq;
96 out_ctx->ma_acked = s->ma_acked;
97 out_ctx->has_ma = t->has_ma;
98 out_ctx->ma_pending_send = s->ma_pending_send;
102 uint32_t count = (t->route_count > (uint8_t)RTE_EXAMPLE_MAX_ROUTES_PER_TRAIN)
104 : (uint32_t)t->route_count;
105 out_ctx->route_count = (uint8_t)count;
106 for (i = 0U; i < count; i++)
107 {
108 out_ctx->route_ids[i] = t->route_ids[i];
109 }
110 for (; i < (uint32_t)RTE_EXAMPLE_MAX_ROUTES_PER_TRAIN; i++)
111 {
112 out_ctx->route_ids[i] = 0U;
113 }
114 return true;
115}
116
122static void notify_train_context(const ab_gp_train_session_t sessions[], const ab_gp_db_t *db, uint8_t train_id,
123 ab_gp_train_update_type_t update_type)
124{
126 uint32_t i;
127
128 rte_mem_set(&ctx, 0, sizeof(ctx));
129 if (!build_train_context(sessions, db, train_id, &ctx))
130 {
131 return;
132 }
133 for (i = 0U; i < g_train_context_subscribers.count; i++)
134 {
135 if (g_train_context_subscribers.slots[i].fn != NULL)
136 {
137 g_train_context_subscribers.slots[i].fn(g_train_context_subscribers.slots[i].context, &ctx,
138 update_type);
139 }
140 }
141}
142
144 uint8_t train_id, ab_gp_train_update_type_t update_type)
145{
146 notify_train_context(sessions, db, train_id, update_type);
147}
148
149bool ab_gp_get_train_context(const ab_gp_train_session_t sessions[], const ab_gp_db_t *db, uint8_t train_id,
150 ab_gp_train_context_t *out_ctx)
151{
152 return build_train_context(sessions, db, train_id, out_ctx);
153}
154
155/* --- Train-domain wire codec ------------------------------------------
156 * Own copy of the fixed-offset RBC envelope layout (REQ-RBC-001 revised)
157 * the old shared rbc_wire.c used to own for every kind - see
158 * ab_gp_train.h's own module doc for why this is now duplicated per domain
159 * rather than shared. Byte-for-byte identical to ab_ga_il.c's/ab_ga_ctc.c's
160 * own copies (same fixed offsets, same non-aliasing "always write every
161 * field, zero if not meaningful" convention) - MUST stay that way, and
162 * must stay byte-for-byte identical to SimCore's Python mirror
163 * (rbc_wire.py) too, since ROUTE_ADD/CTC_* frames never pass through
164 * this file at all but still share the same wire layout on other
165 * channels. Only handles the kinds that ever flow over Train's own
166 * dedicated relay channel (P0/M136/M24_M15/M3/M146) - a kind this file
167 * does not recognize is rejected at decode, same as a genuinely
168 * corrupted frame (REQ-RBC-002). */
169
170#define TRAIN_WIRE_HEADER_SIZE 12U
171#define TRAIN_WIRE_CONTENT_SIZE_BASE 24U
172#define TRAIN_WIRE_CONTENT_SIZE_M146 (TRAIN_WIRE_CONTENT_SIZE_BASE + 4U)
173#define TRAIN_WIRE_CONTENT_SIZE_M136 (TRAIN_WIRE_CONTENT_SIZE_BASE + 4U + 40U)
174
179{
180 uint8_t result;
181
182 switch (kind)
183 {
184 case RBC_MSG_M136:
185 result = 136U;
186 break;
187 case RBC_MSG_M3:
188 result = 3U;
189 break;
190 case RBC_MSG_M146:
191 result = 146U;
192 break;
193 case RBC_MSG_P0:
194 case RBC_MSG_M24_M15:
195 default:
196 result = 0U;
197 break;
198 }
199 return result;
200}
201
205{
206 uint16_t result;
207
208 switch (kind)
209 {
210 case RBC_MSG_M146:
211 result = (uint16_t)TRAIN_WIRE_CONTENT_SIZE_M146;
212 break;
213 case RBC_MSG_M136:
214 result = (uint16_t)TRAIN_WIRE_CONTENT_SIZE_M136;
215 break;
216 case RBC_MSG_P0:
217 case RBC_MSG_M24_M15:
218 case RBC_MSG_M3:
219 default:
220 result = (uint16_t)TRAIN_WIRE_CONTENT_SIZE_BASE;
221 break;
222 }
223 return result;
224}
225
230static void train_wire_encode(uint8_t out[RBC_ENVELOPE_WIRE_SIZE], const rbc_envelope_t *env)
231{
232 rte_buffer_t buf;
233 uint16_t content_size = train_wire_content_size_for_kind(env->kind);
234 uint16_t l_message = (uint16_t)(TRAIN_WIRE_HEADER_SIZE + content_size);
235
236 rte_mem_set(out, 0, RBC_ENVELOPE_WIRE_SIZE);
237 (void)rte_buffer_init(&buf, out, RBC_ENVELOPE_WIRE_SIZE);
238 out[0] = (uint8_t)env->kind;
239 out[1] = env->train_id;
241 out[3] = 0U; /* reserved */
242 (void)rte_buffer_set_length(&buf, 4U);
243 (void)rte_buffer_write_u16_le(&buf, l_message); /* offset 4-5 */
244 (void)rte_buffer_write_u16_le(&buf, 0U); /* offset 6-7, reserved */
245 (void)rte_buffer_write_u32_le(&buf, env->t_train); /* offset 8-11 */
246 (void)rte_buffer_write_u32_le(&buf, env->cycle);
247 (void)rte_buffer_write_u32_le(&buf, env->d_lrbg);
248 (void)rte_buffer_write_u32_le(&buf, env->msg_type);
249 (void)rte_buffer_write_u32_le(&buf, env->route_len); /* not Train's own field - always 0 here, see this
250 * file's own header for why the offset stays */
251 (void)rte_buffer_write_u32_le(&buf, env->ma_seq);
252 (void)rte_buffer_write_u32_le(&buf, env->ma_length);
253 (void)rte_buffer_write_u32_le(&buf, env->t_train_ack);
254 (void)rte_buffer_write_u32_le(&buf, env->nid_lrbg);
255 (void)rte_buffer_write_u32_le(&buf, env->q_dirlrbg);
256 (void)rte_buffer_write_u32_le(&buf, env->q_dlrbg);
257 (void)rte_buffer_write_u32_le(&buf, env->l_doubtover);
258 (void)rte_buffer_write_u32_le(&buf, env->l_doubtunder);
259 (void)rte_buffer_write_u32_le(&buf, env->q_length);
260 (void)rte_buffer_write_u32_le(&buf, env->v_train);
261 (void)rte_buffer_write_u32_le(&buf, env->q_dirtrain);
262 (void)rte_buffer_write_u32_le(&buf, env->m_mode);
263 (void)rte_buffer_write_u32_le(&buf, env->m_level);
264}
265
277static bool train_wire_decode(const uint8_t in[RBC_ENVELOPE_WIRE_SIZE], rbc_envelope_t *out_env)
278{
279 rte_buffer_t buf;
280 rbc_envelope_t decoded;
281 uint16_t l_message;
282 uint16_t expected_l_message;
283
284 rte_mem_set(&decoded, 0, sizeof(decoded));
285 decoded.kind = (rbc_msg_kind_t)in[0];
286
287 if (rte_buffer_init(&buf, (uint8_t *)in, RBC_ENVELOPE_WIRE_SIZE) != RTE_STATUS_OK)
288 {
289 return false;
290 }
291 (void)rte_buffer_set_length(&buf, RBC_ENVELOPE_WIRE_SIZE);
292
293 if (rte_buffer_read_u16_le(&buf, 4U, &l_message) != RTE_STATUS_OK)
294 {
295 return false;
296 }
297 expected_l_message = (uint16_t)(TRAIN_WIRE_HEADER_SIZE + train_wire_content_size_for_kind(decoded.kind));
298 if (l_message != expected_l_message)
299 {
300 return false;
301 }
302 decoded.l_message = l_message;
303 decoded.nid_message = in[2];
304 decoded.train_id = in[1];
305
306 if ((rte_buffer_read_u32_le(&buf, 8U, &decoded.t_train) != RTE_STATUS_OK)
307 || (rte_buffer_read_u32_le(&buf, 12U, &decoded.cycle) != RTE_STATUS_OK)
308 || (rte_buffer_read_u32_le(&buf, 16U, &decoded.d_lrbg) != RTE_STATUS_OK)
309 || (rte_buffer_read_u32_le(&buf, 20U, &decoded.msg_type) != RTE_STATUS_OK)
310 || (rte_buffer_read_u32_le(&buf, 24U, &decoded.route_len) != RTE_STATUS_OK)
311 || (rte_buffer_read_u32_le(&buf, 28U, &decoded.ma_seq) != RTE_STATUS_OK)
312 || (rte_buffer_read_u32_le(&buf, 32U, &decoded.ma_length) != RTE_STATUS_OK))
313 {
314 return false;
315 }
316 if ((rte_buffer_read_u32_le(&buf, 36U, &decoded.t_train_ack) != RTE_STATUS_OK)
317 || (rte_buffer_read_u32_le(&buf, 40U, &decoded.nid_lrbg) != RTE_STATUS_OK)
318 || (rte_buffer_read_u32_le(&buf, 44U, &decoded.q_dirlrbg) != RTE_STATUS_OK)
319 || (rte_buffer_read_u32_le(&buf, 48U, &decoded.q_dlrbg) != RTE_STATUS_OK)
320 || (rte_buffer_read_u32_le(&buf, 52U, &decoded.l_doubtover) != RTE_STATUS_OK)
321 || (rte_buffer_read_u32_le(&buf, 56U, &decoded.l_doubtunder) != RTE_STATUS_OK)
322 || (rte_buffer_read_u32_le(&buf, 60U, &decoded.q_length) != RTE_STATUS_OK)
323 || (rte_buffer_read_u32_le(&buf, 64U, &decoded.v_train) != RTE_STATUS_OK)
324 || (rte_buffer_read_u32_le(&buf, 68U, &decoded.q_dirtrain) != RTE_STATUS_OK)
325 || (rte_buffer_read_u32_le(&buf, 72U, &decoded.m_mode) != RTE_STATUS_OK)
326 || (rte_buffer_read_u32_le(&buf, 76U, &decoded.m_level) != RTE_STATUS_OK))
327 {
328 return false;
329 }
330
331 *out_env = decoded;
332 return true;
333}
334/* --- end Train-domain wire codec --------------------------------------- */
335
345static bool train_id_in_range(uint8_t train_id)
346{
347 if (train_id == 0U)
348 {
349 return false;
350 }
351 return rte_cast_bounds_check((size_t)(train_id - 1U), (size_t)RTE_EXAMPLE_MAX_TRAINS) == RTE_STATUS_OK;
352}
353
358static bool nid_engine_connected_elsewhere(const ab_gp_db_t *db, uint8_t nid_engine, uint8_t exclude_train_id)
359{
360 uint8_t i;
361
362 for (i = 0U; i < (uint8_t)RTE_EXAMPLE_MAX_TRAINS; i++)
363 {
364 uint8_t this_train_id = (uint8_t)(i + 1U);
365
366 if ((this_train_id != exclude_train_id) && db->trains[i].connected && (db->trains[i].nid_engine == nid_engine))
367 {
368 return true;
369 }
370 }
371 return false;
372}
373
374bool ab_gp_train_add(ab_gp_db_t *db, uint8_t train_id, uint8_t nid_engine)
375{
376 ab_gp_db_train_t *slot;
377
378 if (!train_id_in_range(train_id))
379 {
380 return false;
381 }
382 slot = &db->trains[train_id - 1U];
383 if (slot->connected)
384 {
385 return false; /* already connected - ab_gp_train_remove() first to replace it */
386 }
387 if (nid_engine_connected_elsewhere(db, nid_engine, 0U))
388 {
389 return false; /* same physical train already connected in a different slot */
390 }
391
392 rte_mem_set(slot, 0, sizeof(*slot));
393 slot->nid_engine = nid_engine;
394 slot->connected = true;
395 slot->has_ma = false;
396 /* No notify_train_context() call here - this function has no
397 * sessions[] in scope (db-level connection state only, see this
398 * file's own module doc on why route_ids[]/session state are kept
399 * separate). Every real caller (ab_gp_train_handle_connect(), itself
400 * only reached from apply_envelope()'s P0/M136 cases) already fires
401 * AB_TRAIN_REGISTERED right after calling this, where both sessions[]
402 * and db are in scope. */
403 return true;
404}
405
406bool ab_gp_train_remove(ab_gp_db_t *db, uint8_t train_id)
407{
408 ab_gp_db_train_t *slot;
409
410 if (!train_id_in_range(train_id))
411 {
412 return false;
413 }
414 slot = &db->trains[train_id - 1U];
415 if (!slot->connected)
416 {
417 return false;
418 }
419 if (slot->route_count > 0U)
420 {
421 return false; /* still has routes allocated - ab_ga_il_route_unassign_train() each one first */
422 }
423
424 rte_mem_set(slot, 0, sizeof(*slot));
425 return true;
426}
427
429{
430 if (!train_id_in_range(train_id))
431 {
432 return NULL;
433 }
434 if (!db->trains[train_id - 1U].connected)
435 {
436 return NULL;
437 }
438 return &db->trains[train_id - 1U];
439}
440
441bool ab_gp_train_find_by_nid_engine(const ab_gp_db_t *db, uint8_t nid_engine, uint8_t *out_train_id)
442{
443 uint8_t i;
444
445 for (i = 0U; i < (uint8_t)RTE_EXAMPLE_MAX_TRAINS; i++)
446 {
447 if (db->trains[i].connected && (db->trains[i].nid_engine == nid_engine))
448 {
449 if (out_train_id != NULL)
450 {
451 *out_train_id = (uint8_t)(i + 1U);
452 }
453 return true;
454 }
455 }
456 return false;
457}
458
459bool ab_gp_train_is_connected(const ab_gp_db_t *db, uint8_t train_id)
460{
461 if (!train_id_in_range(train_id))
462 {
463 return false;
464 }
465 return db->trains[train_id - 1U].connected;
466}
467
468bool ab_gp_train_get_nid_engine(const ab_gp_db_t *db, uint8_t train_id, uint8_t *out_nid_engine)
469{
470 if (!ab_gp_train_is_connected(db, train_id))
471 {
472 return false;
473 }
474 *out_nid_engine = db->trains[train_id - 1U].nid_engine;
475 return true;
476}
477
478bool ab_gp_train_get_route_count(const ab_gp_db_t *db, uint8_t train_id, uint8_t *out_route_count)
479{
480 if (!ab_gp_train_is_connected(db, train_id))
481 {
482 return false;
483 }
484 *out_route_count = db->trains[train_id - 1U].route_count;
485 return true;
486}
487
488bool ab_gp_train_set_nid_engine(ab_gp_db_t *db, uint8_t train_id, uint8_t nid_engine)
489{
490 if (!ab_gp_train_is_connected(db, train_id))
491 {
492 return false;
493 }
494 if (nid_engine_connected_elsewhere(db, nid_engine, train_id))
495 {
496 return false;
497 }
498 db->trains[train_id - 1U].nid_engine = nid_engine;
499 return true;
500}
501
502bool ab_gp_train_set_has_ma(ab_gp_db_t *db, uint8_t train_id, bool has_ma)
503{
504 if (!ab_gp_train_is_connected(db, train_id))
505 {
506 return false;
507 }
508 db->trains[train_id - 1U].has_ma = has_ma;
509 /* No notify_train_context() call here (this function has no
510 * sessions[] in scope - db-level state only) - a route-grant/release
511 * is a multi-step sequence (granted_length/ma_seq/has_ma all change
512 * together, see AB/GA/il/ab_ga_il.c's own handle_route_add()/
513 * handle_route_release()), so the caller explicitly fires
514 * AB_TRAIN_MA_CHANGED once, after every field is updated, via
515 * ab_gp_train_notify_context_changed() (ga_interface.h) - firing
516 * from each individual setter here would either fire with a
517 * half-updated context or fire multiple times for one logical
518 * change. */
519 return true;
520}
521
522bool ab_gp_train_get_last_position(const ab_gp_db_t *db, uint8_t train_id, uint32_t *out_nid_lrbg)
523{
524 if (!ab_gp_train_is_connected(db, train_id))
525 {
526 return false;
527 }
528 *out_nid_lrbg = db->trains[train_id - 1U].last_nid_lrbg;
529 return true;
530}
531
532bool ab_gp_train_set_last_position(ab_gp_db_t *db, uint8_t train_id, uint32_t nid_lrbg)
533{
534 if (!ab_gp_train_is_connected(db, train_id))
535 {
536 return false;
537 }
538 db->trains[train_id - 1U].last_nid_lrbg = nid_lrbg;
539 return true;
540}
541
542bool ab_gp_train_handle_connect(ab_gp_db_t *db, uint8_t train_id)
543{
544 return ab_gp_train_add(db, train_id, train_id);
545}
546
547bool ab_gp_train_handle_position_report(ab_gp_db_t *db, uint8_t train_id, uint32_t nid_lrbg)
548{
549 if (!ab_gp_train_is_connected(db, train_id))
550 {
551 (void)ab_gp_train_handle_connect(db, train_id);
552 }
553 if (nid_lrbg == 0U)
554 {
555 return true; /* nothing new to record - not an error */
556 }
557 return ab_gp_train_set_last_position(db, train_id, nid_lrbg);
558}
559
560void ab_gp_train_build_position_answer(uint8_t out[RBC_ENVELOPE_WIRE_SIZE], uint8_t train_id, uint32_t cycle,
561 uint32_t d_lrbg, uint32_t msg_type)
562{
563 rbc_envelope_t env;
564
565 rte_mem_set(&env, 0, sizeof(env));
566 env.kind = RBC_MSG_M24_M15;
567 env.train_id = train_id;
568 env.cycle = cycle;
569 env.d_lrbg = d_lrbg;
570 env.msg_type = msg_type;
571 train_wire_encode(out, &env);
572}
573
574void ab_gp_train_build_movement_authority(uint8_t out[RBC_ENVELOPE_WIRE_SIZE], uint8_t train_id, uint32_t ma_seq,
575 uint32_t ma_length)
576{
577 rbc_envelope_t env;
578
579 rte_mem_set(&env, 0, sizeof(env));
580 env.kind = RBC_MSG_M3;
581 env.train_id = train_id;
582 env.ma_seq = ma_seq;
583 env.ma_length = ma_length;
584 train_wire_encode(out, &env);
585}
586
589 const rbc_envelope_t *env, uint32_t cycle, rte_example_site_t site,
590 const char *role_tag)
591{
592 if (!session->in_use)
593 {
594 rte_mem_set(session, 0, sizeof(*session));
595 session->in_use = true;
596 session->train_id = env->train_id;
597 /* Idempotent: ab_gp_train_add() itself is safe to call on an
598 * already-connected slot - see its own doc. */
599 (void)ab_gp_train_handle_connect(db, env->train_id);
601 RTE_EXAMPLE_LOG("[%s] cycle %u: [ONLINE] train %u connected (P0)\n", role_tag, cycle,
602 (unsigned int)env->train_id);
603 rte_log_write_event(RTE_LOG_LEVEL_INFO, site_config_name(site), cycle, "Train", role_tag, "P0",
604 "connected", NULL);
605 }
606 else
607 {
608 /* DEBUG instrumentation (left in place, gated by
609 * RTE_RBC_LOG_LEVEL=DEBUG) - a redundant P0 for an
610 * already-connected train is a deliberate no-op (this
611 * branch), NOT a session reset - see the sibling branch
612 * above for the actual reset path. Logged so a
613 * "did a reconnect wipe my MA" investigation can rule
614 * this specific mechanism in or out from the log alone,
615 * rather than re-deriving it by reading this file again. */
616 rte_log_write_event(RTE_LOG_LEVEL_DEBUG, site_config_name(site), cycle, "Train", role_tag, "P0",
617 "redundant P0 for already-connected train - no-op, session untouched", NULL);
618 }
619}
620
626 const rbc_envelope_t *env, uint32_t cycle, rte_example_site_t site,
627 const char *role_tag, uint8_t idx, uint32_t cyc_msg_type_due[],
628 bool cyc_position_reply_due[])
629{
630 uint32_t local_msg_type;
631
632 if (!session->in_use)
633 {
634 /* Tolerate M136 arriving before/without a P0 - simple
635 * first pass, lenient rather than dropping real position
636 * data over a missed handshake. */
637 rte_mem_set(session, 0, sizeof(*session));
638 session->in_use = true;
639 session->train_id = env->train_id;
640 (void)ab_gp_train_handle_connect(db, env->train_id);
642 }
643 /* REQ-RBC-001 (revised): records env->nid_lrbg (the real Packet 0
644 * field this function's own M24/M15 decision below does not use)
645 * for AB/logic's own cyclic route-connect sweep to consume - see
646 * ab_gp_train_handle_position_report()'s own doc. */
648 session->cycle = env->cycle;
649 session->d_lrbg = env->d_lrbg;
650 /* v_train/m_mode: on direct request, persisted now so
651 * RBC_MSG_CTC_TRAIN_INFO can carry them (see that struct's
652 * own doc, ab_gp_train.h) - M136 always carried these on the
653 * wire, they were simply never stored anywhere before. */
654 session->v_train = env->v_train;
655 session->m_mode = env->m_mode;
657 /* Every M136 gets a plain M24 ack, unconditionally - the old
658 * D_LRBG-threshold decision (below RTE_EXAMPLE_DLRBG_THRESHOLD
659 * -> M24, at/above it -> M15) is retired on direct request: M15
660 * was never a real Subset-026 Conditional Emergency Stop trigger
661 * here, just this simulated ack picking a different message
662 * kind on its own, with no operational meaning behind the
663 * switch. M24 stays the position-report answer for every
664 * M136 UNLESS this train has some other message actually due
665 * this cycle instead - ab_gp_train_build_outputs() below already
666 * sends M3 (Movement Authority) as an ADDITIONAL output when
667 * session->ma_pending_send is set, alongside this M24, so that
668 * case needs no special-casing here. */
669 local_msg_type = RTE_EXAMPLE_MSG_M24;
670 cyc_msg_type_due[idx] = local_msg_type;
671 cyc_position_reply_due[idx] = true;
672 session->position_reply_pending = true;
673 session->position_reply_msg_type = local_msg_type;
674 {
675 const ab_site_balise_t *bal = ab_site_get_balise(env->nid_lrbg);
676 const char *bal_name = (bal != NULL) ? bal->name : "UNKNOWN_BG";
677
678 RTE_EXAMPLE_LOG("[%s] cycle %u: [ONLINE] train %u M136 NID_LRBG=%u (%s) D_LRBG=%u (its cycle %u) -> M%u\n", role_tag,
679 cycle, (unsigned int)env->train_id, (unsigned int)env->nid_lrbg, bal_name, env->d_lrbg, env->cycle, local_msg_type);
680 {
681 char extra[96];
682 char type_buf[8];
683
684 (void)snprintf(extra, sizeof(extra), "train_id=%u nid_lrbg=%u (%s) d_lrbg=%u", (unsigned int)env->train_id,
685 (unsigned int)env->nid_lrbg, bal_name, env->d_lrbg);
686 (void)snprintf(type_buf, sizeof(type_buf), "M%u", local_msg_type);
687 rte_log_write_event(RTE_LOG_LEVEL_INFO, site_config_name(site), cycle, "Train", role_tag, type_buf,
688 "decision", extra);
689 }
690 }
691 return true;
692}
693
695static void handle_envelope_m146(ab_gp_train_session_t *session, const rbc_envelope_t *env, uint32_t cycle,
696 rte_example_site_t site, const char *role_tag)
697{
698 if (session->in_use && (env->ma_seq == session->ma_seq))
699 {
700 session->ma_acked = true;
701 rte_log_write_event(RTE_LOG_LEVEL_INFO, site_config_name(site), cycle, "Train", role_tag, "M146",
702 "MA acked", NULL);
703 }
704}
705
708 ab_gp_db_t *db, const rbc_envelope_t *env, uint32_t cycle,
709 rte_example_site_t site, const char *role_tag)
710{
711 if (session->in_use)
712 {
713 uint16_t held_routes[RTE_EXAMPLE_MAX_ROUTES_PER_TRAIN];
714 uint8_t held_count = 0U;
715 uint8_t r;
716
717 /* Release every route this train currently holds BEFORE
718 * removing it - ab_gp_train_remove() itself refuses to
719 * remove a train still holding routes (see its own doc),
720 * a deliberate safety guard this disconnect path must
721 * satisfy the same way any other route release would,
722 * not bypass. */
723 (void)ab_gp_db_train_get_routes(db, env->train_id, held_routes, &held_count);
724 for (r = 0U; r < held_count; r++)
725 {
726 if (held_routes[r] != 0U)
727 {
728 (void)ab_gp_db_route_disconnect_train(db, held_routes[r]);
729 }
730 }
731 (void)ab_gp_train_remove(db, env->train_id);
732 {
734 if (ertms_ctx != NULL)
735 {
736 (void)ab_gp_proc_session_handle_termination(ertms_ctx, db);
737 }
738 }
739 rte_mem_set(session, 0, sizeof(*session));
742 "[%s] cycle %u: [ONLINE] train %u disconnected (released %u route(s))\n", role_tag, cycle,
743 (unsigned int)env->train_id, (unsigned int)held_count);
744 rte_log_write_event(RTE_LOG_LEVEL_INFO, site_config_name(site), cycle, "Train", role_tag,
745 "DISCONNECT", "removed", NULL);
746 }
747}
748
750 const uint8_t env_bytes[RBC_ENVELOPE_WIRE_SIZE], uint32_t cycle, bool is_role_a,
751 rte_example_site_t site, const char *role_tag, uint32_t cyc_msg_type_due[],
752 bool cyc_position_reply_due[])
753{
754 uint8_t idx;
755 ab_gp_train_session_t *session;
756 bool have_channel_data = false;
757 rbc_envelope_t env_storage;
758 const rbc_envelope_t *env = &env_storage;
759
760 /* is_role_a was the M24/M15 single-channel fault-injection hook
761 * (role A only inverted its decision at D_LRBG=101) - retired with
762 * the M24/M15 decision itself. Kept in this public signature as a
763 * natural future role-asymmetry hook. */
764 (void)is_role_a;
765
766 if (!train_wire_decode(env_bytes, &env_storage))
767 {
768 RTE_EXAMPLE_LOG("[%s] cycle %u: malformed envelope from C (Train relay)\n", role_tag, cycle);
769 rte_log_write_event(RTE_LOG_LEVEL_ERROR, site_config_name(site), cycle, "C", role_tag, "ENVELOPE",
770 "malformed", NULL);
771 return false;
772 }
773 if (!train_id_in_range(env->train_id))
774 {
775 RTE_EXAMPLE_LOG("[%s] cycle %u: Train relay envelope kind %d has out-of-range train_id=%u - dropping\n",
776 role_tag, cycle, (int)env->kind, (unsigned int)env->train_id);
777 return false;
778 }
779 /* ADR-036: routing is by env->train_id - the Train relay is one
780 * multiplexed link now, there is no per-train channel to cross-check
781 * against. */
782 idx = (uint8_t)(env->train_id - 1U);
783 session = &sessions[idx];
784
785 switch (env->kind)
786 {
787 case RBC_MSG_P0:
788 handle_envelope_p0(session, sessions, db, env, cycle, site, role_tag);
789 break;
790 case RBC_MSG_M136:
791 have_channel_data = handle_envelope_m136(session, sessions, db, env, cycle, site, role_tag, idx,
792 cyc_msg_type_due, cyc_position_reply_due);
793 break;
794 case RBC_MSG_M146:
795 handle_envelope_m146(session, env, cycle, site, role_tag);
796 break;
798 handle_envelope_disconnect(session, sessions, db, env, cycle, site, role_tag);
799 break;
800 default:
802 "[%s] cycle %u: unexpected envelope kind %d on the Train relay channel - dropping\n", role_tag,
803 cycle, (int)env->kind);
804 break;
805 }
806 return have_channel_data;
807}
808
809uint8_t ab_gp_train_build_outputs(const ab_gp_train_session_t sessions[], const uint32_t cyc_msg_type_due[],
810 const bool cyc_position_reply_due[],
812{
813 uint8_t count = 0U;
814 uint8_t i;
815
816 for (i = 0U; i < (uint8_t)RTE_EXAMPLE_MAX_TRAINS; i++)
817 {
818 const ab_gp_train_session_t *session = &sessions[i];
819 uint8_t train_id = (uint8_t)(i + 1U);
820
821 if (cyc_position_reply_due[i] || session->position_reply_pending)
822 {
823 uint32_t reply_type = cyc_position_reply_due[i] ? cyc_msg_type_due[i] : session->position_reply_msg_type;
824 if (reply_type == 0U)
825 {
826 reply_type = RTE_EXAMPLE_MSG_M24;
827 }
828 (void)snprintf(out[count].tag, sizeof(out[count].tag), "M%u", (unsigned int)(reply_type & 0xFFU)); /* message ids fit one byte: at most 3 digits */
829 out[count].index = i;
830 ab_gp_train_build_position_answer(out[count].bytes, train_id, session->cycle, session->d_lrbg,
831 reply_type);
832 count++;
833 }
834 if (session->ma_pending_send)
835 {
836 rte_mem_copy(out[count].tag, "M3", 3U);
837 out[count].index = i;
838 ab_gp_train_build_movement_authority(out[count].bytes, train_id, session->ma_seq, session->granted_length);
839 count++;
840 }
841 }
842 return count;
843}
844
845/* ab_gp_train_session_get_in_use()/_get_d_lrbg()/_get_granted_length() -
846 * REMOVED, see ab_gp_train.h's own comment at their old declaration. */
847
848bool ab_gp_train_session_add_granted_length(ab_gp_train_session_t sessions[], uint8_t train_id, uint32_t delta)
849{
850 if (!train_id_in_range(train_id))
851 {
852 return false;
853 }
854 sessions[train_id - 1U].granted_length += delta;
855 return true;
856}
857
858bool ab_gp_train_session_set_granted_length(ab_gp_train_session_t sessions[], uint8_t train_id, uint32_t granted_length)
859{
860 if (!train_id_in_range(train_id))
861 {
862 return false;
863 }
864 sessions[train_id - 1U].granted_length = granted_length;
865 return true;
866}
867
868/* ab_gp_train_session_get_ma_seq() - REMOVED, same reasoning. */
869
871{
872 if (!train_id_in_range(train_id))
873 {
874 return false;
875 }
876 sessions[train_id - 1U].ma_seq++;
877 return true;
878}
879
880/* ab_gp_train_session_get_ma_pending_send() - REMOVED, same reasoning. */
881
882bool ab_gp_train_session_set_ma_pending_send(ab_gp_train_session_t sessions[], uint8_t train_id, bool pending)
883{
884 if (!train_id_in_range(train_id))
885 {
886 return false;
887 }
888 sessions[train_id - 1U].ma_pending_send = pending;
889 return true;
890}
891
892bool ab_gp_train_session_set_ma_acked(ab_gp_train_session_t sessions[], uint8_t train_id, bool acked)
893{
894 if (!train_id_in_range(train_id))
895 {
896 return false;
897 }
898 sessions[train_id - 1U].ma_acked = acked;
899 return true;
900}
901
902/* ab_gp_train_session_get_ctc_connected_sent() - REMOVED (dead: zero
903 * GP-internal callers and zero GA callers even before this pass). */
904
905bool ab_gp_train_session_set_ctc_connected_sent(ab_gp_train_session_t sessions[], uint8_t train_id, bool sent)
906{
907 if (!train_id_in_range(train_id))
908 {
909 return false;
910 }
911 sessions[train_id - 1U].ctc_connected_sent = sent;
912 return true;
913}
914
915/* ab_gp_train_session_get_ctc_ma_granted_sent() - REMOVED, same reasoning. */
916
917bool ab_gp_train_session_set_ctc_ma_granted_sent(ab_gp_train_session_t sessions[], uint8_t train_id, bool sent)
918{
919 if (!train_id_in_range(train_id))
920 {
921 return false;
922 }
923 sessions[train_id - 1U].ctc_ma_granted_sent = sent;
924 return true;
925}
926
927/* ab_gp_train_session_{get,set}_ctc_train_info_sent / _last_ctc_train_info /
928 * _get_v_train_m_mode REMOVED (interface-simplification pass) - see
929 * ab_gp_train_session_t's own comment at their old field location,
930 * ab_gp_train.h. */
931
932/* ab_gp_train_session_get_ctc_ma_notify_pending() - REMOVED, same reasoning. */
933
934bool ab_gp_train_session_set_ctc_ma_notify_pending(ab_gp_train_session_t sessions[], uint8_t train_id, bool pending)
935{
936 if (!train_id_in_range(train_id))
937 {
938 return false;
939 }
940 sessions[train_id - 1U].ctc_ma_notify_pending = pending;
941 return true;
942}
943
951static void train_sessions_collector_encode(void *context, const void *items, uint8_t *out)
952{
953 const ab_gp_train_session_t *sessions = (const ab_gp_train_session_t *)items;
954 uint8_t i;
955
956 (void)context;
957 for (i = 0U; i < (uint8_t)RTE_EXAMPLE_MAX_TRAINS; i++)
958 {
959 uint8_t *slot = &out[(uint32_t)i * RTE_EXAMPLE_TRAIN_SESSION_WIRE_SIZE];
960 const ab_gp_train_session_t *session = &sessions[i];
961 rte_buffer_t buf;
962
963 (void)rte_buffer_init(&buf, slot, RTE_EXAMPLE_TRAIN_SESSION_WIRE_SIZE);
964 slot[0] = session->in_use ? 1U : 0U;
965 slot[1] = session->train_id;
966 (void)rte_buffer_set_length(&buf, 2U);
967 (void)rte_buffer_write_u32_le(&buf, session->cycle);
968 (void)rte_buffer_write_u32_le(&buf, session->d_lrbg);
969 (void)rte_buffer_write_u32_le(&buf, session->granted_length);
970 (void)rte_buffer_write_u32_le(&buf, session->ma_seq);
971 slot[18] = session->ma_acked ? 1U : 0U;
972 }
973}
974
983static void train_sessions_collector_decode(void *context, const uint8_t *in, void *out_items)
984{
985 ab_gp_train_session_t *out_sessions = (ab_gp_train_session_t *)out_items;
986 uint8_t i;
987
988 (void)context;
989 for (i = 0U; i < (uint8_t)RTE_EXAMPLE_MAX_TRAINS; i++)
990 {
991 const uint8_t *slot = &in[(uint32_t)i * RTE_EXAMPLE_TRAIN_SESSION_WIRE_SIZE];
992 ab_gp_train_session_t *out_session = &out_sessions[i];
993 rte_buffer_t buf;
994 uint32_t cycle = 0U;
995 uint32_t d_lrbg = 0U;
996 uint32_t granted_length = 0U;
997 uint32_t ma_seq = 0U;
998
999 rte_mem_set(out_session, 0, sizeof(*out_session));
1000 if (rte_buffer_init(&buf, (uint8_t *)slot, RTE_EXAMPLE_TRAIN_SESSION_WIRE_SIZE) != RTE_STATUS_OK)
1001 {
1002 continue;
1003 }
1004 (void)rte_buffer_set_length(&buf, RTE_EXAMPLE_TRAIN_SESSION_WIRE_SIZE);
1005 if ((rte_buffer_read_u32_le(&buf, 2U, &cycle) != RTE_STATUS_OK)
1006 || (rte_buffer_read_u32_le(&buf, 6U, &d_lrbg) != RTE_STATUS_OK)
1007 || (rte_buffer_read_u32_le(&buf, 10U, &granted_length) != RTE_STATUS_OK)
1008 || (rte_buffer_read_u32_le(&buf, 14U, &ma_seq) != RTE_STATUS_OK))
1009 {
1010 continue;
1011 }
1012 out_session->in_use = (slot[0] != 0U);
1013 out_session->train_id = slot[1];
1014 out_session->cycle = cycle;
1015 out_session->d_lrbg = d_lrbg;
1016 out_session->granted_length = granted_length;
1017 out_session->ma_seq = ma_seq;
1018 out_session->ma_acked = (slot[18] != 0U);
1019 out_session->ma_pending_send = false; /* not on the wire - see ab_gp_train_session_t's own doc */
1020 }
1021}
1022
1024
1031
rte_status_t ab_gp_channel_payload_register_collector(uint32_t wire_size, ab_gp_channel_payload_encode_fn encode_fn, ab_gp_channel_payload_decode_fn decode_fn, void *context, ab_gp_channel_payload_collector_id_t *out_id)
Registers a collector: a module's own fixed-size contribution to the combined A<->B payload - a fan-o...
Generic, data-blind wire framing for the A<->B peer link's AB_SAMPLE frame (CRC-64-protected,...
uint32_t ab_gp_channel_payload_collector_id_t
Opaque handle to a registered collector - returned by ab_gp_channel_payload_register_collector() belo...
bool ab_gp_db_route_disconnect_train(ab_gp_db_t *db, uint16_t route_id)
Reverses ab_gp_db_route_connect_train(): clears the route's own used_in_ma/train_id and removes it fr...
Definition ab_gp_db.c:197
bool ab_gp_db_train_get_routes(const ab_gp_db_t *db, uint8_t train_id, uint16_t out_routes[RTE_EXAMPLE_MAX_ROUTES_PER_TRAIN], uint8_t *out_count)
Retrieves routes currently allocated to a train.
Definition ab_gp_db.c:444
RTE_DECLARE_CALLBACK_LIST(ab_gp_db_route_context_list_t, ab_gp_route_context_notify_fn, AB_GP_DB_MAX_ROUTE_CONTEXT_SUBSCRIBERS)
ab_gp_ertms_train_context_t * ab_gp_ertms_engine_get_train_context(uint8_t train_id)
Retrieves the ERTMS procedure context for a given train slot.
Unified ERTMS Subset-026 Chapter 5 Operational Procedure Orchestrator.
bool ab_gp_proc_session_handle_termination(ab_gp_ertms_train_context_t *ctx, ab_gp_db_t *db)
Handles session termination (Msg 156 / Msg 41).
ERTMS Subset-026 ยง5.3 Communication Session Management Procedure.
static bool build_train_context(const ab_gp_train_session_t sessions[], const ab_gp_db_t *db, uint8_t train_id, ab_gp_train_context_t *out_ctx)
Definition ab_gp_train.c:73
static bool train_wire_decode(const uint8_t in[RBC_ENVELOPE_WIRE_SIZE], rbc_envelope_t *out_env)
bool ab_gp_train_handle_connect(ab_gp_db_t *db, uint8_t train_id)
Handles an inbound P0 (connect handshake) - wraps ab_gp_train_add() with the same env->train_id-as-ni...
#define TRAIN_WIRE_HEADER_SIZE
#define TRAIN_WIRE_CONTENT_SIZE_M146
static ab_gp_train_context_list_t g_train_context_subscribers
Definition ab_gp_train.c:37
#define TRAIN_WIRE_CONTENT_SIZE_BASE
bool ab_gp_train_add(ab_gp_db_t *db, uint8_t train_id, uint8_t nid_engine)
Adds a train on connect: fills db->trains[train_id-1] with nid_engine, connected=true,...
bool ab_gp_train_remove(ab_gp_db_t *db, uint8_t train_id)
Removes a train on disconnect: resets db->trains[train_id-1] back to its all-zero/disconnected state.
bool ab_gp_train_set_last_position(ab_gp_db_t *db, uint8_t train_id, uint32_t nid_lrbg)
Records this train's own most recently reported NID_LRBG - the write side of the field logic/'s own c...
static uint8_t train_wire_nid_message_for_kind(rbc_msg_kind_t kind)
static void train_wire_encode(uint8_t out[RBC_ENVELOPE_WIRE_SIZE], const rbc_envelope_t *env)
static bool nid_engine_connected_elsewhere(const ab_gp_db_t *db, uint8_t nid_engine, uint8_t exclude_train_id)
bool ab_gp_train_apply_envelope(ab_gp_train_session_t sessions[], ab_gp_db_t *db, const uint8_t env_bytes[RBC_ENVELOPE_WIRE_SIZE], uint32_t cycle, bool is_role_a, rte_example_site_t site, const char *role_tag, uint32_t cyc_msg_type_due[], bool cyc_position_reply_due[])
THE single entry point com/'s ab_gp_channel.c calls for every raw envelope popped off its dedicated T...
uint8_t ab_gp_train_build_outputs(const ab_gp_train_session_t sessions[], const uint32_t cyc_msg_type_due[], const bool cyc_position_reply_due[], ab_gp_train_pending_output_t out[AB_GP_TRAIN_MAX_PENDING_OUTPUTS])
Decides which trains need a Train-directed envelope THIS cycle and builds all of them - see this file...
static void train_sessions_collector_encode(void *context, const void *items, uint8_t *out)
static void handle_envelope_p0(ab_gp_train_session_t *session, ab_gp_train_session_t sessions[], ab_gp_db_t *db, const rbc_envelope_t *env, uint32_t cycle, rte_example_site_t site, const char *role_tag)
RBC_MSG_P0 handler: connect a new session, or no-op (logged at DEBUG) if already in use.
bool ab_gp_train_set_nid_engine(ab_gp_db_t *db, uint8_t train_id, uint8_t nid_engine)
void ab_gp_train_build_movement_authority(uint8_t out[RBC_ENVELOPE_WIRE_SIZE], uint8_t train_id, uint32_t ma_seq, uint32_t ma_length)
Builds and wire-encodes the outbound M3 (Movement Authority) envelope for train_id (kind=RBC_MSG_M3) ...
ab_gp_db_train_t * ab_gp_train_find(ab_gp_db_t *db, uint8_t train_id)
Finds a connected train's own slot for direct field access.
bool ab_gp_train_find_by_nid_engine(const ab_gp_db_t *db, uint8_t nid_engine, uint8_t *out_train_id)
Reverse lookup: which train_id (if any) is nid_engine currently connected as.
static bool handle_envelope_m136(ab_gp_train_session_t *session, ab_gp_train_session_t sessions[], ab_gp_db_t *db, const rbc_envelope_t *env, uint32_t cycle, rte_example_site_t site, const char *role_tag, uint8_t idx, uint32_t cyc_msg_type_due[], bool cyc_position_reply_due[])
RBC_MSG_M136 handler: record position, ack with M24. Sets have_channel_data.
bool ab_gp_train_is_connected(const ab_gp_db_t *db, uint8_t train_id)
void ab_gp_train_build_position_answer(uint8_t out[RBC_ENVELOPE_WIRE_SIZE], uint8_t train_id, uint32_t cycle, uint32_t d_lrbg, uint32_t msg_type)
Builds and wire-encodes the outbound M24/M15 Position Report answer for train_id (kind=RBC_MSG_M24_M1...
static uint16_t train_wire_content_size_for_kind(rbc_msg_kind_t kind)
#define TRAIN_WIRE_CONTENT_SIZE_M136
static void train_sessions_collector_decode(void *context, const uint8_t *in, void *out_items)
static void handle_envelope_disconnect(ab_gp_train_session_t *session, ab_gp_train_session_t sessions[], ab_gp_db_t *db, const rbc_envelope_t *env, uint32_t cycle, rte_example_site_t site, const char *role_tag)
RBC_MSG_TRAIN_DISCONNECT handler: release every held route, then remove the train.
bool ab_gp_train_handle_position_report(ab_gp_db_t *db, uint8_t train_id, uint32_t nid_lrbg)
Handles an inbound M136 (Position Report) - adds the train first (ab_gp_train_handle_connect()) if th...
static bool train_id_in_range(uint8_t train_id)
bool ab_gp_train_get_nid_engine(const ab_gp_db_t *db, uint8_t train_id, uint8_t *out_nid_engine)
bool ab_gp_train_get_route_count(const ab_gp_db_t *db, uint8_t train_id, uint8_t *out_route_count)
static ab_gp_channel_payload_collector_id_t g_train_payload_collector_id
static void notify_train_context(const ab_gp_train_session_t sessions[], const ab_gp_db_t *db, uint8_t train_id, ab_gp_train_update_type_t update_type)
rte_status_t ab_gp_train_init(void)
Registers this module's own ab_gp_train_session_t codec as a payload collector with com/ (AB/GP/com/a...
static void handle_envelope_m146(ab_gp_train_session_t *session, const rbc_envelope_t *env, uint32_t cycle, rte_example_site_t site, const char *role_tag)
RBC_MSG_M146 handler: ack the MA if the sequence number matches.
uint32_t ab_gp_train_payload_collector_id(void)
A/B's own train lifecycle handling against db/'s runtime table.
#define RTE_EXAMPLE_TRAIN_SESSION_WIRE_SIZE
#define AB_GP_TRAIN_MAX_PENDING_OUTPUTS
#define AB_GP_TRAIN_MAX_CONTEXT_SUBSCRIBERS
#define RTE_EXAMPLE_LOG(...)
#define RTE_EXAMPLE_MAX_ROUTES_PER_TRAIN
#define RTE_EXAMPLE_MSG_M24
#define RBC_ENVELOPE_WIRE_SIZE
#define RTE_EXAMPLE_MAX_TRAINS
rte_status_t ab_gp_register_train_context_notify(ab_gp_train_context_notify_fn fn, const void *context)
Definition ab_gp_train.c:39
bool ab_gp_train_session_set_ctc_ma_granted_sent(ab_gp_train_session_t *sessions, uint8_t train_id, bool sent)
bool ab_gp_train_session_set_ctc_connected_sent(ab_gp_train_session_t *sessions, uint8_t train_id, bool sent)
bool ab_gp_train_session_set_ma_pending_send(ab_gp_train_session_t *sessions, uint8_t train_id, bool pending)
void ab_gp_train_notify_context_changed(const ab_gp_train_session_t *sessions, const ab_gp_db_t *db, uint8_t train_id, ab_gp_train_update_type_t update_type)
void(*) ab_gp_train_context_notify_fn(const void *context, const ab_gp_train_context_t *ctx, ab_gp_train_update_type_t update_type)
bool ab_gp_train_get_last_position(const ab_gp_db_t *db, uint8_t train_id, uint32_t *out_nid_lrbg)
bool ab_gp_train_session_set_ma_acked(ab_gp_train_session_t *sessions, uint8_t train_id, bool acked)
bool ab_gp_train_set_has_ma(ab_gp_db_t *db, uint8_t train_id, bool has_ma)
bool ab_gp_train_session_increment_ma_seq(ab_gp_train_session_t *sessions, uint8_t train_id)
bool ab_gp_train_session_add_granted_length(ab_gp_train_session_t *sessions, uint8_t train_id, uint32_t delta)
bool ab_gp_train_session_set_ctc_ma_notify_pending(ab_gp_train_session_t *sessions, uint8_t train_id, bool pending)
bool ab_gp_train_session_set_granted_length(ab_gp_train_session_t *sessions, uint8_t train_id, uint32_t granted_length)
ab_gp_train_update_type_t
@ AB_TRAIN_REGISTERED
@ AB_TRAIN_REMOVED
@ AB_TRAIN_POSITION
bool ab_gp_get_train_context(const ab_gp_train_session_t *sessions, const ab_gp_db_t *db, uint8_t train_id, ab_gp_train_context_t *out_ctx)
rbc_msg_kind_t
Which of this envelope's fields are meaningful, and which direction it travels - see this file's own ...
@ RBC_MSG_M146
@ RBC_MSG_M24_M15
@ RBC_MSG_M136
@ RBC_MSG_P0
@ RBC_MSG_M3
@ RBC_MSG_TRAIN_DISCONNECT
const char * site_config_name(rte_example_site_t site)
Definition site_config.c:52
rte_example_site_t
Definition site_config.h:20
ab_gp_db_train_t trains[RTE_EXAMPLE_MAX_TRAINS]
Definition ab_gp_db.h:134
One train A/B's simplified database knows about.
Definition ab_gp_db.h:99
uint8_t nid_engine
Definition ab_gp_db.h:100
uint8_t route_count
Definition ab_gp_db.h:110
uint16_t route_ids[RTE_EXAMPLE_MAX_ROUTES_PER_TRAIN]
Definition ab_gp_db.h:108
uint32_t last_nid_lrbg
Definition ab_gp_db.h:112
Complete ERTMS state and procedure context for one train slot.
uint16_t route_ids[RTE_EXAMPLE_MAX_ROUTES_PER_TRAIN]
uint32_t position_reply_msg_type
One decoded envelope - see this file's own module doc for where the wire<->struct conversion now live...
rbc_msg_kind_t kind
uint32_t l_doubtunder