|
RBC_Template 0.1.0 build 5fb9afb
Workspace master specification — safety-related railway software around an ERTMS Radio Block Centre (RBC), CENELEC EN 50128 / SIL 4
|
Status: Describes what actually exists in the tree today, not a target design. Date: 2026-09-18 Applies to: Platform_RTE (the Safe Computing Platform implementation) and RBC_GP/GA (the Functional Application consuming it). See docs/rca/RCA_CONCEPT.dox for the broader architectural mapping this document is a detail of.
Not yet.** RCA/OCORA is currently a voluntary, industry-consortium-driven reference architecture, not an EU-mandated standard:
Per OCORA's OCORA-TWS03-030_SCP_Specification_of_the_PI_API_between_Application_and_Platform.pdf (v2.0), the PI API is the only interface a safety-relevant, replicated Functional Actor is meant to call - a constrained POSIX subset plus a small number of extensions, most importantly flows.h (a name-addressed publish/subscribe or request/response message endpoint: fl_open/fl_close/fl_send/fl_receive/fl_getattr/fl_setattr). Everything the Platform does to keep N replicas consistent - voting, redundancy management, fault reaction - happens beneath* this API, invisible to the application.
platform/Platform_RTE/include/rte/oal/flow/rte_flow.h is the first (and so far only) module in this codebase modeled directly on the PI API's own naming/shape, added in this migration's Phase 1 (see root TODO.dox).
| Published function | OCORA PI API equivalent | Purpose |
|---|---|---|
| rte_flow_open(rte_flow_storage_t *storage, const rte_flow_config_t *config, rte_flow_handle_t *out_handle) | fl_open(name, oflags) | Opens (joins/subscribes to) a named Flow. |
| rte_flow_close(rte_flow_handle_t handle) | fl_close(fldes) | Closes a Flow. |
| rte_flow_send(rte_flow_handle_t handle, const void *data, size_t data_size, rte_flow_channel_t channel, rte_duration_ms_t timeout_ms) | fl_send(fldes, data, msg_len, fl_channel) | Sends one message on the Flow's user or control channel. |
| rte_flow_receive(rte_flow_handle_t handle, void *out_data, size_t buffer_size, rte_flow_channel_t *out_channel, rte_duration_ms_t timeout_ms) | fl_receive(fldes, meta_data, msg_len, fl_channel) | Receives one message. |
| rte_flow_getattr(rte_flow_handle_t handle, rte_flow_attr_t *out_attr) | fl_getattr(fldes, attr) | Reads a Flow's static/dynamic attributes. |
| rte_flow_setattr(rte_flow_handle_t handle, const rte_flow_attr_t *new_attr, rte_flow_attr_t *out_old_attr) | fl_setattr(fldes, newattr, oldattr) | Sets a Flow's mutable attributes. |
Open-mode flags (rte_flow_oflag_t) are numerically identical to OCORA's own e_fl_oflags (O_REQUESTER=1/O_RESPONDER=2/O_PUBLISHER=4/O_SUBSCRIBER=8/O_NONBLOCK=16) so OCORA-authored configuration data does not need remapping. The channel discriminator (rte_flow_channel_t: RTE_FLOW_CHANNEL_USER=1/RTE_FLOW_CHANNEL_CTRL=2) matches OCORA's own e_fl_channels the same way. Backend-pluggable via rte_osadapter_flow_t (platform/Platform_RTE/include/rte_osadapter/flow/rte_osadapter_flow.h) - a POSIX/TCP OSAdapter can emulate the pub/sub model over point-to-point links; a real DDS OSAdapter (Phase 3, not yet built) implements it natively.
Not yet true to the spec**: voting/replication is not yet transparent beneath rte_flow - that is Phase 2b (see root TODO.dox), deliberately deferred until a real OSAdapter exists to build it against. Today rte_flow exists as a standalone module; nothing in RBC_GP/GA calls it yet.
RBC_GP includes these Platform_RTE headers directly (grep of every #include "rte/...") under src/application/, 2026-09-18):
| Header | Module | PI-API status |
|---|---|---|
| oal/netlink/rte_netlink.h | Point-to-point network link | Not PI-API-shaped (host:port, not name-addressed) - candidate to be replaced by rte_flow once Phase 2b/4 land. |
| redundancy/channel_link/rte_channel.h | Single redundant link primitive | Same as above - rte_flow supersedes this for actor-facing use once wired in. |
| redundancy/voter/rte_voter.h | N-way voting | Should be invisible to the application per the PI API model - GP registers channels with it directly today (checkpoint voter). Target: move beneath rte_flow. |
| redundancy/cross_comparator/rte_cross_comparator.h | 2-way peer comparison | Same as rte_voter - GP's A/B cross-compare (ab_gp_channel_crosscompare.c) calls this directly today. Target: move beneath rte_flow (Phase 2b). |
| redundancy/checkpoint/rte_checkpoint.h | Cross-channel rendezvous | Platform-internal service; GP calls it directly to drive its own cyclic checkpoint. |
| redundancy/watchdog/rte_watchdog.h | Liveness watchdog | Platform-internal service, called directly. |
| redundancy/checksum/rte_checksum.h | CRC-64 integrity | Platform-internal service, called directly. |
| redundancy/dual/rte_dual_channel.h, rte_dual_negotiator.h | Dual-transfer state negotiation, West/East negotiation | Platform-internal, called directly. |
| redundancy/channel_service/rte_channel_service.h | Resolver-backed channel factory | Platform-internal, called directly. |
| app/appmanager/rte_appmanager.h | Cyclic-executive lifecycle | The RTE's own main-loop driver - GP's main() calls this to run at all; not something a PI-API Functional Actor would normally own directly either, in a fully transparent deployment. |
| oal/timer/rte_timer.h, oal/log/rte_log.h, oal/reboot/rte_reboot.h, oal/platform/rte_platform.h, oal/memory/rte_mem_util.h | Timer, logging, reboot, realtime bring-up, memory | OS-facing OAL services - plausible PI-API POSIX-subset equivalents exist (timers, errno.h-adjacent), not yet mapped. |
| utils/safestate/rte_safestate.h | Safe-state transitions | Called both directly (RTE_SAFESTATE/RTE_REBOOT in GP's own fault paths, e.g. check_site_state()) and indirectly (via the Voter/cross-comparator's own now-unconditional transition, see RCA_CONCEPT.dox). |
| utils/lifecycle/rte_lifecycle.h, utils/notify/rte_notify.h, utils/buffer/, utils/cast/, utils/string/, utils/types/, core/rte_core_version.h | Core primitives | Header-only/utility surface, not really "platform services" in the OCORA sense. |
| posix_osadapter/rte_posix_osadapter.h, posix_osadapter/rte_posix_osadapter_channel_service.h | POSIX OSAdapter registration | Explicitly the HW/OS layer boundary - GP's own main() wiring this up at startup is expected even in a PI-API-compliant design (someone has to select and register the concrete OSAdapter). |
Bottom line**: today's RBC_GP is not PI-API-compliant - it reaches directly into most of the Platform's redundancy/RTE machinery rather than exclusively through a Flow-shaped surface. rte_flow (section 2) is the first concrete step toward closing that gap; the migration's remaining phases (root TODO.dox) narrow this table over time, not all at once.
OCORA's own Table 8 (OCORA-TWS03-030, §10.2.1, "Proposal for inclusion of POSIX functions in PI API for safety-relevant Functional Actors") splits the POSIX standard header-by-header. The split is not arbitrary - it tracks exactly where raw OS access would break replica consistency or safety:
Direct POSIX, no Platform adapter needed** - pure computation/type headers with no OS-service or replica-consistency concern: assert.h, ctype.h (excluding locale-taking functions), errno.h, fcntl.h, fenv.h, float.h, limits.h, stdarg.h, stdbool.h, stddef.h, stdint.h, sys/types.h, dirent.h, sys/stat.h, syslog.h, trace.h. Also a curated pthread.h subset (pthread_attr_init/_setdetachedstate/_setschedparam, pthread_create, pthread_mutex_init/_lock/_unlock, pthread_mutexattr_init, pthread_self only - no semaphores, no process scheduling, thread scheduling only during an actor's own initialization phase).
Explicitly excluded from direct use - a Platform adapter is required instead**:
stdio.h/stdlib.h get only a restricted subset, and only during an actor's initialization phase, not at runtime.
What this means for RBC_GP/GA**: both direct-POSIX and Platform-adapter usage are correct, split by category - plain C-library calls for pure computation (already how this codebase works throughout) alongside Platform_RTE's own OAL adapters (rte_timer/rte_memory/rte_task/rte_netlink+rte_flow/rte_log/rte_reboot) for anything OS-service-shaped. Notably, this project's own architecture (ADR-005) is stricter than OCORA strictly requires here - it wraps things (e.g. plain timers) that OCORA's own table would let a Functional Actor call raw, for its own independent reasons (multi-osadapter portability across POSIX/QNX/FreeRTOS, MISRA compliance, testability via mock OSAdapters) that have nothing to do with replica-transparency. Nothing in this codebase's existing OAL posture needs to change on account of this table - if anything, it confirms that posture was already on the safer side of what OCORA itself asks for.