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.h File Reference

A/B's own train lifecycle handling against db/'s runtime table. More...

#include <stdbool.h>
#include <stdint.h>
#include "rte/utils/status/rte_status.h"
#include "../../common/common_config.h"
#include "../../common/rbc_wire_types.h"
#include "../../common/site_config.h"
#include "../db/ab_gp_db.h"
Include dependency graph for ab_gp_train.h:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Data Structures

struct  ab_gp_train_session_t
struct  ab_gp_train_context_t
struct  ab_gp_train_pending_output_t

Macros

#define TRAIN_SESSION_T_DEFINED
 One connected train's live session - fundamentally train state, not transport state, even though com/'s ab_gp_channel_crosscompare.c/ab_gp_channel_negotiate.c read/wire-encode it (via this module's own registered payload collector - see ab_gp_train_init() below - and its own field-by-field getters/ setters for the direct comparisons crosscompare.c makes). Owned entirely by A/B (the vital 2-channel-voting decision-maker); C is a pure relay with no session state of its own (monitor_c_types.h). Fixed-size array (RTE_EXAMPLE_MAX_TRAINS entries, common_config.h), never a dynamic list (CLAUDE.md: no malloc/free/realloc).
#define RTE_EXAMPLE_TRAIN_SESSION_WIRE_SIZE   19U

Functions

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/ab_gp_channel_payload.h's own ab_gp_channel_payload_register_collector()) - this module is ab_gp_train_session_t's own real owner, so it is the one that provides the whole-array wire packing/unpacking ab_gp_channel_payload_encode_collector()/_decode_collector() (and, via those, ab_gp_channel_payload_encode_ab_sample()/ _decode_ab_sample()) call for its own slice - the same registration pattern already used between GA and GP, applied here between two GP submodules instead. Setup-phase only, call once from ab_gp_channel_train_codec_setup() (AB/GP/com/ab_gp_channel.c), before any cycle that might touch the peer link can run.
uint32_t ab_gp_train_payload_collector_id (void)
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, has_ma=false, and an empty route_ids[]/route_count=0.
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.
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.
bool ab_gp_train_is_connected (const ab_gp_db_t *db, 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)
bool ab_gp_train_set_nid_engine (ab_gp_db_t *db, uint8_t train_id, uint8_t nid_engine)
bool ab_gp_train_set_has_ma (ab_gp_db_t *db, uint8_t train_id, bool has_ma)
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_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 cyclic route-connect sweep reads (ab_gp_logic.h).
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 Train relay channel (P0/M136/M146 - see site_config.h's own rte_example_relay_kind_t doc) - wire-decodes it (this module's own train_wire_decode(), ab_gp_train.c; the old shared rbc_wire_decode() is retired, see this file's own module doc), applies it to sessions (com's own cross-compare/state-transfer table) and to db, and computes the M24/M15 decision (including the D_LRBG fault-injection scenario) for an M136. com/'s ab_gp_channel.c contains no train-specific logic of its own after this - not even train_id bounds-checking or wire decode, both of which this function does itself (same posture every other entry point in this file already takes).
Inbound envelope handling

com/'s own ab_gp_channel.c is now a thin receive-and-dispatch layer (ab_gp_channel_apply_inbound_envelope()): once it has decoded an inbound rbc_envelope_t and applied it to its OWN cross-compare-relevant ctx->sessions[] state (ab_gp_train_session_t, ab_gp_channel_types.h - stays in com/, unaffected by this module), it hands the fields this module actually needs off to the functions below rather than touching db->trains[] itself. Every field is a plain, already-decoded value (train_id, nid_lrbg) - these functions never see raw wire bytes.

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-nid_engine stand-in caveat as that function's own doc. Idempotent (safe to call again for an already-connected train_id - just returns false, changes nothing, same as ab_gp_train_add() itself).
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 this is the first data seen for it (tolerates M136 arriving before/without a P0, same lenient posture com's own ctx->sessions[] handling takes), then records nid_lrbg via ab_gp_train_set_last_position() if it is nonzero (0 = no real balise reported this cycle, ab_gp_db.h's own sentinel - left unchanged rather than overwritten with "none").
ab_gp_train_session_t getters/setters (the encapsulation boundary for GA)

Every ab_gp_train_session_t field AB/GA/il/ab_ga_il.c's and AB/GA/ctc/ab_ga_ctc.c's own code used to touch directly (before this session's own GA/GP opacity pass) now has its own accessor here, all keyed by (sessions, train_id) rather than a live ab_gp_train_session_t * (no pointer into the table itself ever crosses into GA - see AB/GP/db/ ab_gp_db.h's own identical rationale for its route getters/setters). sessions is still ab_gp_train_session_t * here, not something more opaque, because GA's own ab_ga_il_apply_envelope()/ab_ga_ctc_build_outputs() signatures already receive this same array by pointer from com/'s ab_gp_channel.c and must keep passing that exact pointer through unmodified (ab_gp_train_session_t's own storage is com-owned, ab_gp_channel_types.h) - GA never dereferences it itself, only forwards it into these functions.

bool ab_gp_train_session_add_granted_length (ab_gp_train_session_t sessions[], uint8_t train_id, uint32_t delta)
 Adds delta to this session's own granted_length - exposed as an accumulating setter (not a plain set) since that is the only way il/'s own ROUTE_ADD handling ever changes this field.
bool ab_gp_train_session_set_granted_length (ab_gp_train_session_t sessions[], uint8_t train_id, uint32_t granted_length)
 Sets this session's own granted_length directly.
bool ab_gp_train_session_get_ma_seq (const ab_gp_train_session_t sessions[], uint8_t train_id, uint32_t *out_ma_seq)
bool ab_gp_train_session_increment_ma_seq (ab_gp_train_session_t sessions[], uint8_t train_id)
 Increments this session's own ma_seq by 1 - exposed as an accumulating setter, same rationale as ab_gp_train_session_add_granted_length() above.
bool ab_gp_train_session_set_ma_pending_send (ab_gp_train_session_t sessions[], uint8_t train_id, bool pending)
bool ab_gp_train_session_set_ma_acked (ab_gp_train_session_t sessions[], uint8_t train_id, bool acked)
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_ctc_ma_granted_sent (ab_gp_train_session_t sessions[], uint8_t train_id, bool sent)
bool ab_gp_train_session_set_ctc_ma_notify_pending (ab_gp_train_session_t sessions[], uint8_t train_id, bool pending)

GA notification - train/'s own single GP->GA hook

train/ is the module that actually mutates db->trains[]/sessions[] (a train's own connected/has_ma/position status), so train/ is the module that notifies about it - same "whichever GP module performs a mutation is the one that fires the matching notify" rule AB/GP/db/ab_gp_db.h's own route hook follows too. ONE subscription (ab_gp_register_train_context_notify() below) covers every train change - collapsed from two registrations on direct instruction, same rationale as the route side's own collapse (ab_gp_db.h). Fan-out, fail-open (zero subscribers is fine). Setup-phase only (rte_lifecycle_check_setup_allowed()) - GA's own single dispatch module (RBC_GA/src/AB/main/ab_ga_dispatch.c) is the ONLY registrant, called once from its own _init(), itself called once from ab_gp_channel_train_codec_setup() (AB/GP/com/ab_gp_channel.c).

#define AB_GP_TRAIN_UPDATE_TYPE_T_DEFINED
 What kind of change a single ab_gp_train_context_notify_fn call is reporting.
#define AB_GP_TRAIN_CONTEXT_T_DEFINED
 A flat, fully-visible (NOT opaque, unlike ab_gp_train_session_t itself) snapshot of one train's current state - what ab_gp_train_context_notify_fn hands its subscriber. A copy, not a live pointer into sessions[]/db->trains[] - GA reads real struct fields directly (no getter calls needed for the common case), while still never touching GP's actual internal storage.
#define AB_GP_TRAIN_NOTIFY_FN_DEFINED
#define AB_GP_TRAIN_MAX_CONTEXT_SUBSCRIBERS   4U
enum  ab_gp_train_update_type_t {
  AB_TRAIN_REGISTERED , AB_TRAIN_REMOVED , AB_TRAIN_POSITION , AB_TRAIN_MA_CHANGED ,
  AB_TRAIN_EMERGENCY_REACTION_RAISED
}
rte_status_t ab_gp_register_train_context_notify (ab_gp_train_context_notify_fn fn, const void *context)
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)
 One-shot pull equivalent of the push notification above - the SAME snapshot-building logic (real impl: ab_gp_train.c's own build_train_context(), shared with notify_train_context()), for a caller that just needs train_id's current state right now rather than reacting to a change. Replaces the individual ab_gp_train_session_get_*()/ab_gp_train_get_has_ma() getters GA used to call one field at a time (later session, on direct request - "it will be enough if GA can getTrainContext / getRouteContext; setters stay separate since they require validation"). Mutation still goes through the individual setters below - this is READ-ONLY, deliberately.
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)
 Explicit trigger GA calls (through ga_interface.h) once it has finished a multi-step train-context change it owns the sequencing of - a route grant/release (AB/GA/il/ab_ga_il.c's own handle_route_add()/handle_route_release(), which update granted_length/ma_seq/has_ma across several separate setter calls) being the only real case today. NOT fired automatically from any individual setter - see ab_gp_train_set_has_ma()'s own doc for why firing per-setter would either see a half-updated context or fire multiple times for one logical change.

Outbound envelope building

The other direction of the same split. Two layers:

  • ab_gp_train_build_position_answer()/_build_movement_authority() below: given a caller-decided WHAT (already-known field values), build and wire-encode ONE envelope (this module's own train_wire_encode(), ab_gp_train.c - see this file's own module doc). No I/O, no gating.
  • ab_gp_train_build_outputs() below THAT: given this cycle's own ctx->cyc_msg_type_due[]/cyc_position_reply_due[] and sessions (com's own ma_pending_send latch), decides WHICH trains need an M24/M15 and/or M3 this cycle and builds all of them - the actual per-train WHEN, not just WHAT/HOW. com/'s ab_gp_channel_forward_to_c_if_needed() only decides the higher-level "send to C at all this cycle" gate (should_forward_to_c - AGREE/role-A/SINGLE mode, ab_gp_channel.c's own concern) and then asks this module (and AB/GA/ctc/ab_ga_ctc.h) to build their own outputs and sends whatever came back over ab_gp_channel_io.c's own relay channels, without iterating per-train itself for Train's own messages. Neither layer reads db/ at all, since none of that data lives there yet (see ab_gp_db_train_t.has_ma's own doc for the one place it partly does, via il/'s ab_ga_il_handle_route_add()).
#define AB_GP_TRAIN_MAX_PENDING_OUTPUTS   ((uint8_t)(RTE_EXAMPLE_MAX_TRAINS * 2U))
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_M15) directly into out. msg_type is RTE_EXAMPLE_MSG_M24 or _M15 (common_config.h) - the caller's own decision, not recomputed here.
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) directly into out (same contract as ab_gp_train_build_position_answer() above).
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's own "Outbound envelope building" doc above for the two-layer split this sits on top of. Does not send anything and does not mutate sessions - com/'s ab_gp_channel_forward_to_c_if_needed() owns actually sending each built entry (ab_gp_channel_send_envelope_to_c()) and owns clearing session->ma_pending_send once both this function and AB/GA/ctc/ab_ga_ctc.h's own ab_ga_ctc_build_outputs() have each had their own chance to read it this cycle (the flag is genuinely shared between the two - see ab_gp_channel_forward_to_c_if_needed()'s own doc for why the coordinator, not either domain, clears it).

Detailed Description

A/B's own train lifecycle handling against db/'s runtime table.

  • add a train on connect, remove it on disconnect, find it again later, typed getters/setters for its own fields, and full ownership of ab_gp_train_session_t and everything that decides its content, INCLUDING the Train-domain wire codec itself (train_wire_encode()/_decode(), ab_gp_train.c - file-static, no public API of their own); this module owns encoding/decoding every kind that flows over its own dedicated Train relay channel (P0/M136/M24_M15/M3/M146) end to end. com/'s ab_gp_channel.c contains no train-specific logic at all, not even train_id bounds-checking or wire decode/encode; see ab_gp_train_apply_envelope()'s own doc below.

"train_id" throughout this file is the 1..RTE_EXAMPLE_MAX_TRAINS connection-slot identity (same convention ab_gp_train_session_t.train_id and il/'s ab_ga_il_route_assign_train() already use, ab_ga_il.h) - it is the array position in db->trains[]/sessions[], not ab_gp_db_train_t's own nid_engine (the real Subset-026 identity, a plain data field, not an index). ab_gp_train_find_by_nid_engine() below is the bridge from one to the other.

route_ids[]/route_count are deliberately NOT settable here - those are il/'s own responsibility (ab_ga_il_route_assign_train()/ _unassign_train(), ab_ga_il.h), the only code that keeps a train's own route list and each route's own back-reference consistent with each other. This module only reads them (ab_gp_train_get_route_count()).

Every function takes the caller-owned ab_gp_db_t/ab_gp_train_session_t[] explicitly (same "caller-owned storage, no hidden state" convention as ab_gp_channel_context_t/ab_ga_il.h) - this module holds no state of its own.

Definition in file ab_gp_train.h.

Macro Definition Documentation

◆ TRAIN_SESSION_T_DEFINED

#define TRAIN_SESSION_T_DEFINED

One connected train's live session - fundamentally train state, not transport state, even though com/'s ab_gp_channel_crosscompare.c/ab_gp_channel_negotiate.c read/wire-encode it (via this module's own registered payload collector - see ab_gp_train_init() below - and its own field-by-field getters/ setters for the direct comparisons crosscompare.c makes). Owned entirely by A/B (the vital 2-channel-voting decision-maker); C is a pure relay with no session state of its own (monitor_c_types.h). Fixed-size array (RTE_EXAMPLE_MAX_TRAINS entries, common_config.h), never a dynamic list (CLAUDE.md: no malloc/free/realloc).

Cross-site continuity (REQ-RBC-006, Platform_RTE/docs/requirements/ SRS.dox): this whole table is what ab_gp_channel_negotiate.c's apply_state_transfer() carries across a STANDBY->ONLINE promotion (superseding the old single-scalar {cycle, dlrbg, decision} snapshot) - a promoted site must already know every connected train's granted MA length/sequence before the client even notices anything happened, not just the position/decision of one implicit train.

Cross-compare (REQ-RBC-007): the wire-relevant fields below (everything except ma_pending_send, a pure local "have I sent M3 yet this cycle" scratch flag with no cross-compare meaning) are what ab_gp_channel_crosscompare.c's xcompare_recv_local()/_peer() now compare, widened from the old single uint32_t M24/M15 decision.

Definition at line 78 of file ab_gp_train.h.

◆ RTE_EXAMPLE_TRAIN_SESSION_WIRE_SIZE

#define RTE_EXAMPLE_TRAIN_SESSION_WIRE_SIZE   19U

One encoded ab_gp_train_session_t's wire size: in_use(1) + train_id(1) + cycle(4) + d_lrbg(4) + granted_length(4) + ma_seq(4) + ma_acked(1) = 19 bytes, little-endian - see ab_gp_train.c's own registered payload collector (ab_gp_train_init() below), shared by both the AB_SAMPLE cross-compare payload and ab_gp_channel_negotiate.c's own extra DATA payload (RTE_EXAMPLE_SITE_EXTRA_PAYLOAD_SESSION_SIZE, common_config.h - must stay equal to this constant; a compile-time check enforces it, see ab_gp_channel_negotiate.c). ma_pending_send is deliberately NOT included - see ab_gp_train_session_t's own doc.

Definition at line 171 of file ab_gp_train.h.

◆ AB_GP_TRAIN_UPDATE_TYPE_T_DEFINED

#define AB_GP_TRAIN_UPDATE_TYPE_T_DEFINED

What kind of change a single ab_gp_train_context_notify_fn call is reporting.

Definition at line 222 of file ab_gp_train.h.

◆ AB_GP_TRAIN_CONTEXT_T_DEFINED

#define AB_GP_TRAIN_CONTEXT_T_DEFINED

A flat, fully-visible (NOT opaque, unlike ab_gp_train_session_t itself) snapshot of one train's current state - what ab_gp_train_context_notify_fn hands its subscriber. A copy, not a live pointer into sessions[]/db->trains[] - GA reads real struct fields directly (no getter calls needed for the common case), while still never touching GP's actual internal storage.

Definition at line 249 of file ab_gp_train.h.

◆ AB_GP_TRAIN_NOTIFY_FN_DEFINED

#define AB_GP_TRAIN_NOTIFY_FN_DEFINED

Definition at line 280 of file ab_gp_train.h.

◆ AB_GP_TRAIN_MAX_CONTEXT_SUBSCRIBERS

#define AB_GP_TRAIN_MAX_CONTEXT_SUBSCRIBERS   4U

One subscriber (GA's own dispatch module) with headroom to spare.

Definition at line 286 of file ab_gp_train.h.

◆ AB_GP_TRAIN_MAX_PENDING_OUTPUTS

#define AB_GP_TRAIN_MAX_PENDING_OUTPUTS   ((uint8_t)(RTE_EXAMPLE_MAX_TRAINS * 2U))

Worst case one cycle can ever produce: every train (RTE_EXAMPLE_MAX_TRAINS) with both an M24/M15 AND a pending M3 due in the same cycle.

Definition at line 495 of file ab_gp_train.h.

Enumeration Type Documentation

◆ ab_gp_train_update_type_t

Enumerator
AB_TRAIN_REGISTERED 

ab_gp_train_add() just registered this train.

AB_TRAIN_REMOVED 

ab_gp_train_remove() just removed this train - ctx reflects its now-cleared state (in_use=false).

AB_TRAIN_POSITION 

A fresh M136 just updated cycle/d_lrbg/nid_lrbg/ v_train/m_mode (ab_gp_train_apply_envelope()'s own RBC_MSG_M136 case).

AB_TRAIN_MA_CHANGED 

granted_length/ma_seq/has_ma just changed (a ROUTE_ADD/_RELEASE-driven grant, flat or ERTMS).

AB_TRAIN_EMERGENCY_REACTION_RAISED 

Reserved for a future emergency-stop reaction - no real firing site yet (this codebase's own emergency-stop handling is still scaffolding, see root CLAUDE.md's ERTMS notes) - included now so the enum doesn't need another interface-shape change once it is wired up.

Definition at line 223 of file ab_gp_train.h.

Function Documentation

◆ ab_gp_train_init()

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/ab_gp_channel_payload.h's own ab_gp_channel_payload_register_collector()) - this module is ab_gp_train_session_t's own real owner, so it is the one that provides the whole-array wire packing/unpacking ab_gp_channel_payload_encode_collector()/_decode_collector() (and, via those, ab_gp_channel_payload_encode_ab_sample()/ _decode_ab_sample()) call for its own slice - the same registration pattern already used between GA and GP, applied here between two GP submodules instead. Setup-phase only, call once from ab_gp_channel_train_codec_setup() (AB/GP/com/ab_gp_channel.c), before any cycle that might touch the peer link can run.

Returns
RTE_STATUS_OK on success; whatever ab_gp_channel_payload_register_collector() itself returned on failure.

Definition at line 1025 of file ab_gp_train.c.

◆ ab_gp_train_payload_collector_id()

uint32_t ab_gp_train_payload_collector_id ( void )
Returns
This module's own registered payload-collector id (see ab_gp_train_init() above) - pass as the index into ab_gp_channel_payload_encode_collector()/_decode_collector() and ab_gp_channel_payload_encode_ab_sample()'s/_decode_ab_sample()'s own sources/dests arrays (AB/GP/com/ab_gp_channel_payload.h). Only meaningful after ab_gp_train_init() has run.

Definition at line 1032 of file ab_gp_train.c.

◆ ab_gp_register_train_context_notify()

rte_status_t ab_gp_register_train_context_notify ( ab_gp_train_context_notify_fn fn,
const void * context )
Returns
RTE_STATUS_OK on success; RTE_STATUS_INVALID_PARAM if fn is NULL; RTE_STATUS_INVALID_STATE if the setup phase is already locked; RTE_STATUS_RESOURCE_EXHAUSTED if AB_GP_TRAIN_MAX_CONTEXT_SUBSCRIBERS are already registered.

Definition at line 39 of file ab_gp_train.c.

◆ ab_gp_get_train_context()

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 )

One-shot pull equivalent of the push notification above - the SAME snapshot-building logic (real impl: ab_gp_train.c's own build_train_context(), shared with notify_train_context()), for a caller that just needs train_id's current state right now rather than reacting to a change. Replaces the individual ab_gp_train_session_get_*()/ab_gp_train_get_has_ma() getters GA used to call one field at a time (later session, on direct request - "it will be enough if GA can getTrainContext / getRouteContext; setters stay separate since they require validation"). Mutation still goes through the individual setters below - this is READ-ONLY, deliberately.

Returns
false if train_id is out of range; otherwise true with *out_ctx fully populated.

Definition at line 149 of file ab_gp_train.c.

◆ ab_gp_train_notify_context_changed()

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 )

Explicit trigger GA calls (through ga_interface.h) once it has finished a multi-step train-context change it owns the sequencing of - a route grant/release (AB/GA/il/ab_ga_il.c's own handle_route_add()/handle_route_release(), which update granted_length/ma_seq/has_ma across several separate setter calls) being the only real case today. NOT fired automatically from any individual setter - see ab_gp_train_set_has_ma()'s own doc for why firing per-setter would either see a half-updated context or fire multiple times for one logical change.

Parameters
update_typeAlways AB_TRAIN_MA_CHANGED today; the parameter exists so a future multi-step sequence (e.g. a real emergency-reaction handler) can reuse this same explicit-trigger mechanism instead of growing a new one.

Definition at line 143 of file ab_gp_train.c.

◆ ab_gp_train_add()

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, has_ma=false, and an empty route_ids[]/route_count=0.

Parameters
train_id1..RTE_EXAMPLE_MAX_TRAINS.
Returns
false if train_id is out of range, that slot is already connected (call ab_gp_train_remove() first to replace it), or nid_engine is already connected in a DIFFERENT slot (the same physical train can never occupy two slots at once).

Definition at line 374 of file ab_gp_train.c.

◆ ab_gp_train_remove()

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.

Returns
false if train_id is out of range, that slot is not connected, or it still has route_count > 0 (release every route via ab_ga_il_route_unassign_train() first, ab_ga_il.h - a train is never dropped while a route's own back-reference still points at it, same posture ab_ga_il_route_remove() already takes for used_in_ma).

Definition at line 406 of file ab_gp_train.c.

◆ ab_gp_train_find()

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.

Parameters
train_id1..RTE_EXAMPLE_MAX_TRAINS.
Returns
pointer into db->trains[] (caller-owned storage, never free()d), or NULL if train_id is out of range or that slot is not currently connected.

Definition at line 428 of file ab_gp_train.c.

◆ ab_gp_train_find_by_nid_engine()

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.

Parameters
out_train_idReceives the 1-based train_id if found. Left unmodified otherwise.
Returns
true if a currently-connected train has this nid_engine.

Definition at line 441 of file ab_gp_train.c.

◆ ab_gp_train_is_connected()

bool ab_gp_train_is_connected ( const ab_gp_db_t * db,
uint8_t train_id )
Returns
true if train_id is in range AND currently connected - never "fails" the way the getters below can, always a plain yes/no.

Definition at line 459 of file ab_gp_train.c.

◆ ab_gp_train_get_nid_engine()

bool ab_gp_train_get_nid_engine ( const ab_gp_db_t * db,
uint8_t train_id,
uint8_t * out_nid_engine )
Returns
false if train_id is out of range or not connected; otherwise true with *out_nid_engine set.

Definition at line 468 of file ab_gp_train.c.

◆ ab_gp_train_get_route_count()

bool ab_gp_train_get_route_count ( const ab_gp_db_t * db,
uint8_t train_id,
uint8_t * out_route_count )
Returns
false if train_id is out of range or not connected; otherwise true with *out_route_count set (read-only view of il/'s own bookkeeping - see this file's own module doc).

Definition at line 478 of file ab_gp_train.c.

◆ ab_gp_train_set_nid_engine()

bool ab_gp_train_set_nid_engine ( ab_gp_db_t * db,
uint8_t train_id,
uint8_t nid_engine )
Returns
false if train_id is out of range, not connected, or nid_engine is already connected in a different slot; otherwise true with the field updated.

Definition at line 488 of file ab_gp_train.c.

◆ ab_gp_train_set_has_ma()

bool ab_gp_train_set_has_ma ( ab_gp_db_t * db,
uint8_t train_id,
bool has_ma )
Returns
false if train_id is out of range or not connected; otherwise true with the field updated.

Definition at line 502 of file ab_gp_train.c.

◆ ab_gp_train_get_last_position()

bool ab_gp_train_get_last_position ( const ab_gp_db_t * db,
uint8_t train_id,
uint32_t * out_nid_lrbg )
Returns
false if train_id is out of range or not connected; otherwise true with *out_nid_lrbg set (0 = no position reported yet).

Definition at line 522 of file ab_gp_train.c.

◆ ab_gp_train_set_last_position()

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 cyclic route-connect sweep reads (ab_gp_logic.h).

Returns
false if train_id is out of range or not connected; otherwise true with the field updated.

Definition at line 532 of file ab_gp_train.c.

◆ ab_gp_train_handle_connect()

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-nid_engine stand-in caveat as that function's own doc. Idempotent (safe to call again for an already-connected train_id - just returns false, changes nothing, same as ab_gp_train_add() itself).

Definition at line 542 of file ab_gp_train.c.

◆ ab_gp_train_handle_position_report()

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 this is the first data seen for it (tolerates M136 arriving before/without a P0, same lenient posture com's own ctx->sessions[] handling takes), then records nid_lrbg via ab_gp_train_set_last_position() if it is nonzero (0 = no real balise reported this cycle, ab_gp_db.h's own sentinel - left unchanged rather than overwritten with "none").

Definition at line 547 of file ab_gp_train.c.

◆ ab_gp_train_build_position_answer()

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_M15) directly into out. msg_type is RTE_EXAMPLE_MSG_M24 or _M15 (common_config.h) - the caller's own decision, not recomputed here.

Parameters
outDestination, exactly RBC_ENVELOPE_WIRE_SIZE bytes. Must not be NULL.

Definition at line 560 of file ab_gp_train.c.

◆ ab_gp_train_build_movement_authority()

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) directly into out (same contract as ab_gp_train_build_position_answer() above).

Definition at line 574 of file ab_gp_train.c.

◆ ab_gp_train_build_outputs()

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's own "Outbound envelope building" doc above for the two-layer split this sits on top of. Does not send anything and does not mutate sessions - com/'s ab_gp_channel_forward_to_c_if_needed() owns actually sending each built entry (ab_gp_channel_send_envelope_to_c()) and owns clearing session->ma_pending_send once both this function and AB/GA/ctc/ab_ga_ctc.h's own ab_ga_ctc_build_outputs() have each had their own chance to read it this cycle (the flag is genuinely shared between the two - see ab_gp_channel_forward_to_c_if_needed()'s own doc for why the coordinator, not either domain, clears it).

Parameters
sessionscom's own ab_gp_train_session_t table (RTE_EXAMPLE_MAX_TRAINS entries) - read-only.
cyc_msg_type_dueThis cycle's own per-train scratch array (ab_gp_channel_read_channels()'s own output) - RTE_EXAMPLE_MSG_M24/_M15 per train with cyc_position_reply_due true.
cyc_position_reply_dueThis cycle's own per-train scratch array - true if that train had a fresh M136 this cycle.
outDestination array, AB_GP_TRAIN_MAX_PENDING_OUTPUTS entries. Must not be NULL.
Returns
the number of entries actually filled in out (0..AB_GP_TRAIN_MAX_PENDING_OUTPUTS).

Definition at line 809 of file ab_gp_train.c.

◆ ab_gp_train_apply_envelope()

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 Train relay channel (P0/M136/M146 - see site_config.h's own rte_example_relay_kind_t doc) - wire-decodes it (this module's own train_wire_decode(), ab_gp_train.c; the old shared rbc_wire_decode() is retired, see this file's own module doc), applies it to sessions (com's own cross-compare/state-transfer table) and to db, and computes the M24/M15 decision (including the D_LRBG fault-injection scenario) for an M136. com/'s ab_gp_channel.c contains no train-specific logic of its own after this - not even train_id bounds-checking or wire decode, both of which this function does itself (same posture every other entry point in this file already takes).

Parameters
sessionscom's own ab_gp_train_session_t table (RTE_EXAMPLE_MAX_TRAINS entries) - mutated in place.
dbAB's own Train/Route database - mutated in place via this file's own ab_gp_train_handle_connect()/ _handle_position_report().
channel_indexWhich Train[index] relay channel this envelope arrived on (0-based) - purely a defensive diagnostic cross-check against the decoded env.train_id (logged, not fatal, on a mismatch): routing itself is already fully structural (this IS the Train channel, by construction of the caller's own per-instance queue drain), so a mismatch here means a misconfigured sim/wrong port, not a misrouted envelope.
env_bytesRaw wire bytes, exactly RBC_ENVELOPE_WIRE_SIZE - not yet decoded.
cycleThis AB cycle's own number (logging only).
is_role_aWhether this channel is role A - only the M136 D_LRBG fault-injection scenario needs this.
siteThis channel's own site (logging only).
role_tagThis channel's own "A/WEST" etc. tag (logging only).
cyc_msg_type_duePer-train scratch array (RTE_EXAMPLE_MAX_TRAINS entries) - set for this envelope's own train_id if it was an M136 (the computed M24/M15 answer); left unmodified otherwise.
cyc_position_reply_duePer-train scratch array, same size - set true alongside cyc_msg_type_due for an M136; left unmodified otherwise.
Returns
true if this envelope produced fresh channel data worth cross-comparing this cycle (an M136); false otherwise (including a malformed frame, an out-of-range train_id, or an unrecognized/foreign kind - all logged and dropped).

Definition at line 749 of file ab_gp_train.c.

◆ ab_gp_train_session_add_granted_length()

bool ab_gp_train_session_add_granted_length ( ab_gp_train_session_t sessions[],
uint8_t train_id,
uint32_t delta )

Adds delta to this session's own granted_length - exposed as an accumulating setter (not a plain set) since that is the only way il/'s own ROUTE_ADD handling ever changes this field.

Returns
false if train_id is out of range; otherwise true.

Definition at line 848 of file ab_gp_train.c.

◆ ab_gp_train_session_set_granted_length()

bool ab_gp_train_session_set_granted_length ( ab_gp_train_session_t sessions[],
uint8_t train_id,
uint32_t granted_length )

Sets this session's own granted_length directly.

Returns
false if train_id is out of range; otherwise true.

Definition at line 858 of file ab_gp_train.c.

◆ ab_gp_train_session_get_ma_seq()

bool ab_gp_train_session_get_ma_seq ( const ab_gp_train_session_t sessions[],
uint8_t train_id,
uint32_t * out_ma_seq )
Returns
false if train_id is out of range; otherwise true with *out_ma_seq set.

◆ ab_gp_train_session_increment_ma_seq()

bool ab_gp_train_session_increment_ma_seq ( ab_gp_train_session_t sessions[],
uint8_t train_id )

Increments this session's own ma_seq by 1 - exposed as an accumulating setter, same rationale as ab_gp_train_session_add_granted_length() above.

Returns
false if train_id is out of range; otherwise true.

Definition at line 870 of file ab_gp_train.c.

◆ ab_gp_train_session_set_ma_pending_send()

bool ab_gp_train_session_set_ma_pending_send ( ab_gp_train_session_t sessions[],
uint8_t train_id,
bool pending )
Returns
false if train_id is out of range; otherwise true with the field updated.

Definition at line 882 of file ab_gp_train.c.

◆ ab_gp_train_session_set_ma_acked()

bool ab_gp_train_session_set_ma_acked ( ab_gp_train_session_t sessions[],
uint8_t train_id,
bool acked )
Returns
false if train_id is out of range; otherwise true with the field updated.

Definition at line 892 of file ab_gp_train.c.

◆ ab_gp_train_session_set_ctc_connected_sent()

bool ab_gp_train_session_set_ctc_connected_sent ( ab_gp_train_session_t sessions[],
uint8_t train_id,
bool sent )
Returns
false if train_id is out of range; otherwise true with the field updated.

Definition at line 905 of file ab_gp_train.c.

◆ ab_gp_train_session_set_ctc_ma_granted_sent()

bool ab_gp_train_session_set_ctc_ma_granted_sent ( ab_gp_train_session_t sessions[],
uint8_t train_id,
bool sent )
Returns
false if train_id is out of range; otherwise true with the field updated.

Definition at line 917 of file ab_gp_train.c.

◆ ab_gp_train_session_set_ctc_ma_notify_pending()

bool ab_gp_train_session_set_ctc_ma_notify_pending ( ab_gp_train_session_t sessions[],
uint8_t train_id,
bool pending )
Returns
false if train_id is out of range; otherwise true with the field updated - see ab_gp_train_session_t's own doc on why this is a SEPARATE flag from ma_pending_send.

Definition at line 934 of file ab_gp_train.c.