|
SafeAPI RBC 2oo2 Generic Application
IL/CTC route and train logic for the safeAPIRBC2oo2 reference RBC
|
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"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. | |
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.
| 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().
| sessions | com'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. |
| db | AB's own Train/Route database (opaque) - mutated in place. |
| env_bytes | Raw wire bytes, exactly RBC_ENVELOPE_WIRE_SIZE - not yet decoded. |
| cycle | This AB cycle's own number (logging only). |
| site | This channel's own site (logging only). |
| role_tag | This channel's own "A/WEST" etc. tag (logging only). |
Definition at line 163 of file ab_ga_il.c.
| 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.
| out | Destination array, AB_GP_IL_MAX_PENDING_OUTPUTS entries. Must not be NULL. |
out (0..AB_GP_IL_MAX_PENDING_OUTPUTS). Definition at line 233 of file ab_ga_il.c.
| 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.
train_id is not a currently-connected train; otherwise true. Definition at line 158 of file ab_ga_il.c.
| 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).
| sessions | This 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). |
| db | This channel's own Train/Route database (opaque) - forwarded to ab_gp_train_notify_context_changed()/ ab_ga_il_handle_route_add() below. |
| ctx | The route's own current-state snapshot - see that struct's own doc, ga_interface.h. |
| update_type | Which 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.
| 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().
Definition at line 124 of file ab_ga_il.c.
| 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).
| train_id | 1..SAFEAPI_EXAMPLE_MAX_TRAINS (same 1-based convention as ab_gp_train_session_t.train_id). |
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.
| 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().
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.
| 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).
route_id is not tracked. Definition at line 138 of file ab_ga_il.c.
| 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().
route_id is not tracked. Definition at line 143 of file ab_ga_il.c.
| 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().
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.
| 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.
Definition at line 113 of file ab_ga_il.c.
| 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)?
| route_id | Route 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_layout | If 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. |
route_id is a real entry in this site's layout. Definition at line 71 of file ab_ga_il.c.
| 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[].
| out_layout | Same contract as ab_ga_il_site_has_route()'s own. |
Definition at line 93 of file ab_ga_il.c.