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
Loading...
Searching...
No Matches
Platform_RTE's Platform Independent API (PI API) - current surface

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.

0. Standardization status (is this mandatory?)

Not yet.** RCA/OCORA is currently a voluntary, industry-consortium-driven reference architecture, not an EU-mandated standard:

  • Developed and published by a member consortium of infrastructure managers (DB, SBB, SNCF, ...) and suppliers (Siemens, Thales, Hitachi, ...) under an open license (EUPL 1.2 + CC-BY-SA), via github.com/OCORA-Public. Active collaboration requires signing an "OCORA Code of Conduct and Member Agreement" - this is private-sector reference work, not EU regulation.
  • Still actively evolving, not frozen: releases are named Pre-Alpha -> Alpha -> Beta -> Gamma -> Post-Gamma -> Delta -> R1...R6. The PI API spec this document is based on (OCORA-TWS03-030, v2.0) has dozens of "Open Point: To be concluded later" entries still open in its own POSIX-inclusion table (section 3a below) - a vendor cannot yet claim full compliance against a specification that is itself incomplete.
  • A CCS TSI amendment entered into force 5 May 2026 (Commission Implementing Regulation (EU) 2026/693) - but it addresses ETCS Subset test-reference documentation and transition rules, not RCA/OCORA adoption. ERA's own public framing is that OCORA's (and EULYNX's) work gets "critically assessed and integrated into specification work where suitable" - selective future incorporation, not a current mandate.
  • Practical read: individual infrastructure managers driving OCORA are likely to start requiring* it in their own national tenders before any EU-wide TSI mandate exists (the same path EULYNX took: voluntary IM-driven reference first, procurement leverage second, TSI reference later). Being RCA-ready is a reasonable forward-looking position, not a current legal obligation.

1. What "PI API" means here

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.

2. The actual PI-API-shaped surface: rte_flow

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.

3. What the Functional Application (GP/GA) actually calls today - the leaky boundary

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.

3a. POSIX: which parts a Functional Actor may call directly vs. through a Platform adapter

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**:

  • Networking (sys/socket.h, sys/un.h, arpa/inet.h, netdb.h, net/if.h - all "Not included"): a Functional Actor may only reach the network through flows.h (this project's rte_flow, section 2 above). This is the concrete reason rte_flow exists as a distinct module rather than the Functional Application opening raw sockets itself.
  • Time (time.h/sys/time.h): a POSIX subset is included, but explicitly paired with the sync_time.h extension (SYNCHRONIZED_REALTIME/SYNCHRONIZED_MONOTONIC clocks) - plain unsynchronized POSIX time is not safe across replicas (their clocks can drift independently), so a Platform-provided synchronized clock is mandatory alongside it, not optional.
  • IPC (mqueue.h, sys/msg.h, sys/sem.h, sys/shm.h, semaphore.h - all "Not included").
  • 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.

4. Sources

  • OCORA PI API spec: OCORA-TWS03-030_SCP_Specification_of_the_PI_API_between_Application_and_Platform.pdf (v2.0, July 2022), github.com/OCORA-Public/Publications, 12_OCORA Release R6/.
  • OCORA licensing/collaboration model: github.com/OCORA-Public/Publications README (dual license EUPL 1.2 and CC-BY-SA, member consortium collaboration model).
  • ERA, "Amendment to the CCS TSI Enters into Force" - https://www.era.europa.eu/content/amendment-ccs-tsi-enters-force (Commission Implementing Regulation (EU) 2026/693, 19 March 2026, in force 5 May 2026).
  • docs/rca/RCA_CONCEPT.dox (this workspace) - the broader SCP/PI-API/Functional- Application classification this document details.