SafeAPI RBC 2oo2 Generic Application
IL/CTC route and train logic for the safeAPIRBC2oo2 reference RBC
Loading...
Searching...
No Matches
ab_ga_il.h File Reference

A/B's own route handling against GP's own runtime table (via AB/GP/ga_interface/ga_interface.h - see that file's own doc: this module never #includes any GP submodule header directly) and site/'s static track layout - today's slice: verify a route reported by ILSim against the layout, and add/remove/set-status/assign-train/unassign-train against GP's own route table. More...

#include <stdbool.h>
#include <stdint.h>
#include "safeapi/utils/status/sapi_status.h"
#include "rbc_wire_types.h"
#include "site_config.h"
#include "ga_interface.h"
#include "ab_siteGA.h"
Include dependency graph for ab_ga_il.h:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Functions

void ab_ga_il_on_route_context_changed (ab_gp_train_session_t *sessions, ab_gp_db_t *db, const ab_gp_route_context_t *ctx, ab_gp_route_update_type_t update_type)
 GA's single dispatch module (safeAPIRBC2oo2GA/src/AB/main/ ab_ga_dispatch.c) is the ONLY thing that registers with GP now (interface-simplification pass) - it owns the ONE route-context subscription and fans AB_ROUTE_CONNECTED/_DISCONNECTED here. AB_ROUTE_ADDED/_STATUS_CHANGED are ignored (IL never cared about those).
bool ab_ga_il_site_has_route (uint16_t route_id, const ab_site_ga_route_layout_t **out_layout)
 Pure site-layout lookup - does route_id exist in this site's own static track layout at all (ab_site_ga_track_layout, ab_site.h)?
bool ab_ga_il_site_has_route_by_signals (uint16_t start_signal, uint16_t end_signal, const ab_site_ga_route_layout_t **out_layout)
 Pure site-layout lookup by route IDENTITY - does a route with this exact (start_signal, end_signal) pair exist in this site's own static track layout? Same shape as ab_ga_il_site_has_route() above, but by signal pair rather than synthetic id - the wire's own route-identity model (rbc_wire_types.h's own doc) - used to resolve an inbound ROUTE_ADD/ROUTE_RELEASE's own start_signal/end_signal fields to this site's internal route_id before touching db->routes[].
bool ab_ga_il_route_verify (uint16_t route_id, uint16_t start_signal, uint16_t end_signal)
 Verifies a route as reported by ILSim is OK: it must both exist in this site's own static layout AND report the same start/end signals the layout itself has for that route id - ILSim is external, untrusted input (same REQ-RBC-002 "reject a malformed frame, don't index blind" posture already applied to wire input elsewhere in this project), so its own claimed signals are cross-checked against the site's own authoritative layout, not taken on faith.
bool ab_ga_il_route_add (ab_gp_db_t *db, uint16_t route_id, uint16_t start_signal, uint16_t end_signal)
 Adds a new route to GP's own route table, after verifying it via ab_ga_il_route_verify() above - thin wrapper over ga_interface.h's own ab_gp_db_route_add().
bool ab_ga_il_route_remove (ab_gp_db_t *db, uint16_t route_id)
 Removes a tracked route from GP's own route table, freeing its slot - thin wrapper over ga_interface.h's own ab_gp_db_route_remove().
bool ab_ga_il_route_set_status (ab_gp_db_t *db, uint16_t route_id, ab_gp_db_route_il_status_t status)
 Updates a tracked route's own il_status - IL's last-reported status for it - thin wrapper over ga_interface.h's own ab_gp_db_route_set_status(). Does not touch occupied (a separate, non-IL-sourced concept).
bool ab_ga_il_route_set_type (ab_gp_db_t *db, uint16_t route_id, uint32_t route_type)
 Updates a tracked route's own route_type - IL's last-reported supervision mode (rbc_route_type_t: RBC_ROUTE_TYPE_FS/_OS/_SH, rbc_wire_types.h) for it - thin wrapper over ga_interface.h's own ab_gp_db_route_set_type().
bool ab_ga_il_route_assign_train (ab_gp_db_t *db, uint16_t route_id, uint8_t train_id)
 Assigns a route to a train - thin wrapper over ga_interface.h's own ab_gp_db_route_connect_train() (which fires GP's own route-connected notify on success).
bool ab_ga_il_route_unassign_train (ab_gp_db_t *db, uint16_t route_id)
 Reverses ab_ga_il_route_assign_train() - thin wrapper over ga_interface.h's own ab_gp_db_route_disconnect_train().
bool ab_ga_il_handle_route_add (ab_gp_db_t *db, uint8_t train_id)
 Handles the MA-length-bookkeeping half of an inbound ROUTE_ADD - marks this train as now holding a valid MA (ab_gp_train_set_has_ma()). Deliberately unaware of route identity/type - that half (resolving/creating the real ab_gp_db_route_t) is handle_route_add()'s own job (ab_ga_il.c, file-static) - this function stays the same narrow "train now has an MA" bookkeeping step it always was, kept public since ab_ga_il_apply_envelope()'s own ROUTE_ADD path still calls it alongside the newer route-identity handling.
bool ab_ga_il_apply_envelope (const void *context, ab_gp_train_session_t *sessions, ab_gp_db_t *db, const uint8_t env_bytes[RBC_ENVELOPE_WIRE_SIZE], uint32_t cycle, sapi_example_site_t site, const char *role_tag)
 THE handler this module registers with GP (ab_gp_register_il_envelope_handler(), ga_interface.h, via AB/main/ab_ga_dispatch.c's own ab_ga_dispatch_init()) for every raw envelope popped off its dedicated IL relay channel (ROUTE_ADD/ROUTE_RELEASE - see site_config.h's own sapi_example_relay_kind_t doc) - "pure GA" now (later session, on direct request): GP no longer calls this by bare name, only ever through the registered function pointer, so context (unused here - matches ab_gp_il_envelope_handler_fn's own signature purely so this function can be registered directly, no trampoline needed) is always NULL. Wire-decodes the envelope (this module's own il_wire_decode(), ab_ga_il.c) and dispatches by kind to this module's own file-static handle_route_add()/handle_route_release().
uint8_t ab_ga_il_build_outputs (ab_gp_il_pending_output_t out[AB_GP_IL_MAX_PENDING_OUTPUTS])
 Drains this module's own TrainPositionInRoute event queue (populated by this module's own route-connected/-disconnected notify subscribers, registered from ab_ga_il_init() - see ga_interface.h's own ab_gp_db_register_route_connected_notify()/ _disconnected_notify()) and wire-encodes each into out. Deliberately takes no sessions/db parameters, unlike AB/GP/train/ab_gp_train.h's/AB/GA/ctc/ab_ga_ctc.h's own build_outputs() - everything this needs was already captured into the internal event queue by the notify callbacks at the moment each route-connect/-disconnect actually happened, not re-derived here from current table state. Called from AB/GP/main/post_execute.c's own ab_gp_channel_post_execute() (the real forward-to-C logic, step 3 of that function), same "GP orchestrates, GA builds" pattern CTC/Train already use.

Detailed Description

A/B's own route handling against GP's own runtime table (via AB/GP/ga_interface/ga_interface.h - see that file's own doc: this module never #includes any GP submodule header directly) and site/'s static track layout - today's slice: verify a route reported by ILSim against the layout, and add/remove/set-status/assign-train/unassign-train against GP's own route table.

ab_ga_il_handle_route_add() below is this module's own inbound envelope handler: com/'s ab_gp_channel.c now forwards the raw ROUTE_ADD/ ROUTE_RELEASE wire bytes here rather than decoding them itself - see ab_ga_il_apply_envelope()'s own doc. IL<->RBC route-identity pass: the wire now carries a real route identity (start_signal, end_signal - see rbc_wire_types.h's own doc), so ROUTE_ADD genuinely creates/looks up a real ab_gp_db_route_t now (via ab_ga_il_site_has_route_by_signals() below), and ROUTE_RELEASE (new) lets IL explicitly tell RBC a route is no longer needed. Reporting a route's own occupancy status back to ILSim (the "send back info to sim whether train is inside route or not" half of this module's original scope) IS now implemented - ab_ga_il_build_outputs() below, driven by GP's own route-connected/ -disconnected notify hooks (ga_interface.h), the same way AB/GA/ctc/ab_ga_ctc.h's own ab_ga_ctc_build_outputs() already works for CTC.

Every function below that takes ab_gp_db_t * explicitly does so as the caller-owned, GP-opaque parameter (same "caller-owned storage, no hidden state" convention as ab_gp_channel_context_t) - this module holds no state of its own beyond its own small pending-output queue (ab_ga_il_build_outputs() below, which drains AND clears it - see that function's own doc for why it accumulates rather than being reset on a fixed cycle boundary), and never dereferences db itself, only passes it through to ga_interface.h's own getter/setter functions.

Definition in file ab_ga_il.h.

Function Documentation

◆ ab_ga_il_apply_envelope()

bool ab_ga_il_apply_envelope ( const void * context,
ab_gp_train_session_t * sessions,
ab_gp_db_t * db,
const uint8_t env_bytes[RBC_ENVELOPE_WIRE_SIZE],
uint32_t cycle,
sapi_example_site_t site,
const char * role_tag )

THE handler this module registers with GP (ab_gp_register_il_envelope_handler(), ga_interface.h, via AB/main/ab_ga_dispatch.c's own ab_ga_dispatch_init()) for every raw envelope popped off its dedicated IL relay channel (ROUTE_ADD/ROUTE_RELEASE - see site_config.h's own sapi_example_relay_kind_t doc) - "pure GA" now (later session, on direct request): GP no longer calls this by bare name, only ever through the registered function pointer, so context (unused here - matches ab_gp_il_envelope_handler_fn's own signature purely so this function can be registered directly, no trampoline needed) is always NULL. Wire-decodes the envelope (this module's own il_wire_decode(), ab_ga_il.c) and dispatches by kind to this module's own file-static handle_route_add()/handle_route_release().

Parameters
sessionscom's own ab_gp_train_session_t table (opaque to this module - see this file's own module doc) (SAFEAPI_EXAMPLE_MAX_TRAINS entries) - mutated in place via ga_interface.h's own setters.
dbAB's own Train/Route database (opaque) - mutated in place.
env_bytesRaw wire bytes, exactly RBC_ENVELOPE_WIRE_SIZE - not yet decoded.
cycleThis AB cycle's own number (logging only).
siteThis channel's own site (logging only).
role_tagThis channel's own "A/WEST" etc. tag (logging only).
Returns
true if this envelope produced fresh channel data worth cross-comparing this cycle; false otherwise (a malformed frame, an out-of-range train_id, an unrecognized/foreign kind, a ROUTE_ADD for an unknown route or unconnected train, or a ROUTE_RELEASE for an unknown/untracked route - all logged and dropped).

Definition at line 163 of file ab_ga_il.c.

◆ ab_ga_il_build_outputs()

uint8_t ab_ga_il_build_outputs ( ab_gp_il_pending_output_t out[AB_GP_IL_MAX_PENDING_OUTPUTS])

Drains this module's own TrainPositionInRoute event queue (populated by this module's own route-connected/-disconnected notify subscribers, registered from ab_ga_il_init() - see ga_interface.h's own ab_gp_db_register_route_connected_notify()/ _disconnected_notify()) and wire-encodes each into out. Deliberately takes no sessions/db parameters, unlike AB/GP/train/ab_gp_train.h's/AB/GA/ctc/ab_ga_ctc.h's own build_outputs() - everything this needs was already captured into the internal event queue by the notify callbacks at the moment each route-connect/-disconnect actually happened, not re-derived here from current table state. Called from AB/GP/main/post_execute.c's own ab_gp_channel_post_execute() (the real forward-to-C logic, step 3 of that function), same "GP orchestrates, GA builds" pattern CTC/Train already use.

The pending-output element type/capacity this module fills (ab_gp_il_pending_output_t/AB_GP_IL_MAX_PENDING_OUTPUTS) are GP-owned now, declared in ga_interface.h - the "consumer owns the shape" posture ab_gp_route_context_t/ab_gp_train_context_t already take (see that header's own doc).

Deliberately an ACCUMULATING queue, not reset at the top of every cycle - found live: an earlier version of this module did reset it every cycle (before ab_gp_logic_connect_routes_and_trains() could enqueue anything new), reasoning that the non-forwarding channel's own copy needed clearing to avoid filling up in steady state. That reasoning was wrong on two counts: (1) ab_gp_db_route_connect_train()/_disconnect_train() only fire their own notify ONCE, at the genuine state transition - ab_gp_logic_connect_route_for_train()'s own idempotent already_assigned_to_this_train() check means a route already connected is a silent no-op on every later cycle, so nothing re-enqueues repeatedly; (2) the reset was unconditional on EVERY channel, including the currently-forwarding one - an event enqueued on a cycle where should_forward_to_c happened to be false that same cycle (e.g. ab_gp_channel_crosscompare.c's own routine per-train sync-skip window) was silently destroyed by the very next cycle's reset before this function ever got a chance to drain and send it - confirmed live via a real Docker run where "IL notified: route N connected" logged but the IL sim never received the matching TrainPositionInRoute. Instead, this queue simply accumulates (bounded by AB_GP_IL_MAX_PENDING_OUTPUTS' own drop-and-log ceiling below) until a cycle where this function actually runs and drains it - on the non-forwarding channel that may never happen while it stays non-forwarding, which is fine: those events would only become relevant if this channel is later promoted, at which point sending its accumulated history (in order) on first forwarding is correct, not stale.

Parameters
outDestination array, AB_GP_IL_MAX_PENDING_OUTPUTS entries. Must not be NULL.
Returns
the number of entries actually filled in out (0..AB_GP_IL_MAX_PENDING_OUTPUTS).

Definition at line 233 of file ab_ga_il.c.

◆ ab_ga_il_handle_route_add()

bool ab_ga_il_handle_route_add ( ab_gp_db_t * db,
uint8_t train_id )

Handles the MA-length-bookkeeping half of an inbound ROUTE_ADD - marks this train as now holding a valid MA (ab_gp_train_set_has_ma()). Deliberately unaware of route identity/type - that half (resolving/creating the real ab_gp_db_route_t) is handle_route_add()'s own job (ab_ga_il.c, file-static) - this function stays the same narrow "train now has an MA" bookkeeping step it always was, kept public since ab_ga_il_apply_envelope()'s own ROUTE_ADD path still calls it alongside the newer route-identity handling.

Returns
false if train_id is not a currently-connected train; otherwise true.

Definition at line 158 of file ab_ga_il.c.

◆ ab_ga_il_on_route_context_changed()

void ab_ga_il_on_route_context_changed ( ab_gp_train_session_t * sessions,
ab_gp_db_t * db,
const ab_gp_route_context_t * ctx,
ab_gp_route_update_type_t update_type )

GA's single dispatch module (safeAPIRBC2oo2GA/src/AB/main/ ab_ga_dispatch.c) is the ONLY thing that registers with GP now (interface-simplification pass) - it owns the ONE route-context subscription and fans AB_ROUTE_CONNECTED/_DISCONNECTED here. AB_ROUTE_ADDED/_STATUS_CHANGED are ignored (IL never cared about those).

GA's single dispatch module (ab_ga_dispatch.c) fans every route change here for AB_ROUTE_CONNECTED/AB_ROUTE_DISCONNECTED - the only two IL cares about (see this file's own module doc). Was two separate GP-registered callbacks (il_on_route_connected/ _disconnected) - collapsed into this one dispatch-driven function on direct request, same rationale as ab_ga_ctc_on_route_context_changed()'s own doc.

Movement Authority granting/shrinking lives HERE now (later session, on direct request: "route<->train connection is GP/GA's own decision, based on train position - IL's own ROUTE_ADD/ROUTE_RELEASE wire messages must not dictate which train a route grant applies to"). Previously handle_route_add()/handle_route_release() (this file's own file-static handlers) read env->train_id straight off the wire and granted/shrank MA directly, entirely bypassing GP's own position-based connect gate (ab_gp_logic_connect_routes_and_trains(), AB/GP/ma_logic/ab_gp_logic.c, which already ran every cycle independently, but used to only produce a cosmetic TrainPositionInRoute indication, never an actual MA change) and the registered route-connection VALIDATOR (validate_route_assignment(), AB/ma_logic_adaptation/ab_ga_ma_logic_adaptation.c) that gate is supposed to gate through. Now: handle_route_add() only tracks route existence (adds it to GP's own route table); handle_route_release() only untracks it. The actual MA grant/shrink happens HERE, in reaction to GP's own AB_ROUTE_CONNECTED/_DISCONNECTED notification - i.e. only once GP's position-based sweep has ACTUALLY connected (or disconnected) this route to a specific train, using ctx->train_id (GP's own real, tracked association - db->routes[].train_id, never anything IL supplied on the wire) and layout->length_m (this route's own known length, resolved by ctx->id - never a wire- supplied length either, on the same "IL doesn't dictate this" basis).

Parameters
sessionsThis channel's own long-lived Train session table - needed now to grant/shrink MA on AB_ROUTE_CONNECTED/_DISCONNECTED (was previously only threaded through the route-connection validator, a different call path).
dbThis channel's own Train/Route database (opaque) - forwarded to ab_gp_train_notify_context_changed()/ ab_ga_il_handle_route_add() below.
ctxThe route's own current-state snapshot - see that struct's own doc, ga_interface.h.
update_typeWhich kind of change this is - only AB_ROUTE_CONNECTED/_DISCONNECTED are acted on.

GA's single dispatch module (safeAPIRBC2oo2GA/src/AB/main/ ab_ga_dispatch.c) is the ONLY thing that registers with GP now (interface-simplification pass) - it owns the ONE route-context subscription and fans AB_ROUTE_CONNECTED/_DISCONNECTED here. AB_ROUTE_ADDED/_STATUS_CHANGED are ignored (IL never cared about those).

Definition at line 707 of file ab_ga_il.c.

◆ ab_ga_il_route_add()

bool ab_ga_il_route_add ( ab_gp_db_t * db,
uint16_t route_id,
uint16_t start_signal,
uint16_t end_signal )

Adds a new route to GP's own route table, after verifying it via ab_ga_il_route_verify() above - thin wrapper over ga_interface.h's own ab_gp_db_route_add().

Returns
false if verification fails, this route_id is already tracked (call ab_ga_il_route_remove() first to replace it), or GP's own route table (SAFEAPI_EXAMPLE_MAX_ROUTES) is full.

Definition at line 124 of file ab_ga_il.c.

◆ ab_ga_il_route_assign_train()

bool ab_ga_il_route_assign_train ( ab_gp_db_t * db,
uint16_t route_id,
uint8_t train_id )

Assigns a route to a train - thin wrapper over ga_interface.h's own ab_gp_db_route_connect_train() (which fires GP's own route-connected notify on success).

Parameters
train_id1..SAFEAPI_EXAMPLE_MAX_TRAINS (same 1-based convention as ab_gp_train_session_t.train_id).
Returns
false if route_id is not tracked, train_id is out of range, the route is already used_in_ma (call ab_ga_il_route_unassign_train() first - never silently reassigned), it is already in that same train's route_ids[], or that train's route_ids[] (SAFEAPI_EXAMPLE_MAX_ROUTES_PER_TRAIN) is full.

Definition at line 148 of file ab_ga_il.c.

◆ ab_ga_il_route_remove()

bool ab_ga_il_route_remove ( ab_gp_db_t * db,
uint16_t route_id )

Removes a tracked route from GP's own route table, freeing its slot - thin wrapper over ga_interface.h's own ab_gp_db_route_remove().

Returns
false if route_id is not tracked, or it is still used_in_ma (call ab_ga_il_route_unassign_train() first - a route actively backing a train's Movement Authority is never silently dropped).

Definition at line 133 of file ab_ga_il.c.

◆ ab_ga_il_route_set_status()

bool ab_ga_il_route_set_status ( ab_gp_db_t * db,
uint16_t route_id,
ab_gp_db_route_il_status_t status )

Updates a tracked route's own il_status - IL's last-reported status for it - thin wrapper over ga_interface.h's own ab_gp_db_route_set_status(). Does not touch occupied (a separate, non-IL-sourced concept).

Returns
false if route_id is not tracked.

Definition at line 138 of file ab_ga_il.c.

◆ ab_ga_il_route_set_type()

bool ab_ga_il_route_set_type ( ab_gp_db_t * db,
uint16_t route_id,
uint32_t route_type )

Updates a tracked route's own route_type - IL's last-reported supervision mode (rbc_route_type_t: RBC_ROUTE_TYPE_FS/_OS/_SH, rbc_wire_types.h) for it - thin wrapper over ga_interface.h's own ab_gp_db_route_set_type().

Returns
false if route_id is not tracked.

Definition at line 143 of file ab_ga_il.c.

◆ ab_ga_il_route_unassign_train()

bool ab_ga_il_route_unassign_train ( ab_gp_db_t * db,
uint16_t route_id )

Reverses ab_ga_il_route_assign_train() - thin wrapper over ga_interface.h's own ab_gp_db_route_disconnect_train().

Returns
false if route_id is not tracked, or it is not currently used_in_ma (nothing to unassign).

Definition at line 153 of file ab_ga_il.c.

◆ ab_ga_il_route_verify()

bool ab_ga_il_route_verify ( uint16_t route_id,
uint16_t start_signal,
uint16_t end_signal )

Verifies a route as reported by ILSim is OK: it must both exist in this site's own static layout AND report the same start/end signals the layout itself has for that route id - ILSim is external, untrusted input (same REQ-RBC-002 "reject a malformed frame, don't index blind" posture already applied to wire input elsewhere in this project), so its own claimed signals are cross-checked against the site's own authoritative layout, not taken on faith.

Returns
true only if both checks pass.

Definition at line 113 of file ab_ga_il.c.

◆ ab_ga_il_site_has_route()

bool ab_ga_il_site_has_route ( uint16_t route_id,
const ab_site_ga_route_layout_t ** out_layout )

Pure site-layout lookup - does route_id exist in this site's own static track layout at all (ab_site_ga_track_layout, ab_site.h)?

Parameters
route_idRoute identity to look up. 0 always returns false (ab_gp_db.h/ab_site.h's own "no route" sentinel, never a real route id).
out_layoutIf non-NULL and route_id is found, receives a pointer to that route's own static layout entry (owned by ab_site_ga_track_layout[], not the caller - never free()d). Left unmodified if not found.
Returns
true if route_id is a real entry in this site's layout.

Definition at line 71 of file ab_ga_il.c.

◆ ab_ga_il_site_has_route_by_signals()

bool ab_ga_il_site_has_route_by_signals ( uint16_t start_signal,
uint16_t end_signal,
const ab_site_ga_route_layout_t ** out_layout )

Pure site-layout lookup by route IDENTITY - does a route with this exact (start_signal, end_signal) pair exist in this site's own static track layout? Same shape as ab_ga_il_site_has_route() above, but by signal pair rather than synthetic id - the wire's own route-identity model (rbc_wire_types.h's own doc) - used to resolve an inbound ROUTE_ADD/ROUTE_RELEASE's own start_signal/end_signal fields to this site's internal route_id before touching db->routes[].

Parameters
out_layoutSame contract as ab_ga_il_site_has_route()'s own.
Returns
true if a route with this exact signal pair is a real entry in this site's layout.

Definition at line 93 of file ab_ga_il.c.