RTE RBC 2oo2 Test Environment 0.1.0 build 3384d2c
End-to-end Robot Framework test suite + Docker Compose stack for the RBC_GP system
Loading...
Searching...
No Matches
web_runner

A local, developer-only web server and live visualisation page for the rbc_scenario suite. Run it by hand against a local test environment with etc/scripts/runWebRunner.sh [--port N] [--railml PATH] [--open] (default http://localhost:8765/; it wraps python3 backend/server.py); never host it, because it executes real build, start, stop and run subprocesses.

Layout

Path Role
backend/server.py HTTP and WebSocket server, script picker, build, start, stop and run action runner
backend/events.py Parses the merged log line shape written by ContainerLogAttachListener.py into event dictionaries; the single parser for the live stream and for offline replay
backend/site_data.py Parses ab_site.railml.xml per /api/site request
backend/parse_log.py Offline replay
backend/versions.py Product versions for /api/versions (paths relative to the workspace root)
backend/test_web_runner.py End-to-end regression: starts server.py as a subprocess and speaks real HTTP and WebSocket
frontend/live.html The whole browser-side page

Design decisions

  • Train and route state is built only from CTC. The train position and Movement Authority panel uses the CTC_TRAIN_INFO indication exclusively, matching the principle that CTC is the one real observation point. events.py decodes the reused-field layout, including the signed two's-complement ma_offset; the wire layout is documented in RBC_GA's design notes. Train wire traffic (M136, M3, P0) is still parsed for the raw log view.
  • Read-only accessors. Train, Route and RBC objects in live.html are read-only accessors over the existing globals, not a rewrite of the render pipeline; the page has no visual regression coverage.
  • Log view filters. Sender and receiver dropdowns are populated as new endpoints are seen and combine with the category buttons and text search.
  • A hover tooltip on each train marker shows the same data through Train.get().

Event path (Robot listener to browser)

robot/supportFunctions/ContainerLogAttachListener.py (the Robot listener) is the only producer. Each batch of normalized log lines it emits goes to three places: the terminal, testEnv/logs/_run_stream.txt (durable copy) and, when the run was started by this server (RTE_WEB_RUNNER_PORT), an HTTP POST /api/log_push carrying {"offset": <file offset after the append>, "text": <the exact text>}. The server parses the text at once and broadcasts the events over the WebSocket (/stream), including the derived train and route state, so nothing waits for a file poll. The offset lets the server's file tailer skip exactly those bytes: every byte is processed once, and if the push fails (no route, a proxy, a busy server) the tailer still delivers the same lines from the file within 0.3 s. The push bypasses http_proxy. GET /api/stream_info and the status line on the Wire Event Log tab show whether the file grows, lines parse and browsers are connected.

RBC 2oo2 status panel (2026-10-07)

The listener writes every RBC line with source RBC West or RBC East and puts the originating process in front of the info field ([a-west | GP] promoted to ONLINE); events.py takes the unit (a-west ... c-east) from that tag. Until 2026-10-07 the parser did not accept the nested brackets, so no RBC status type was produced and the A/B nodes stayed grey; the listener also cut every Info= text after its first word. The status types and the RBC lines they come from:

Type RBC line Panel
negotiation_role cycle N: negotiation ok (role=ONLINE/STANDBY/FAULTED, ...) (Platform_RTE rte_site_role.c, every cycle while the inter-site link is up) node role; FAULTED paints the node red
rbc_status Type=STATUS Info=self with role=, site_wait=, site_why= (RBC_GP ab_gp_status.c, also while the link is down) node role, tooltip "Site role: waits for ..."
role Type=ROLE (promoted, demoted, takeover, dual ONLINE, promotion held back) node role where the text states one
startup_ts site startup timestamp N ms (own N, sibling N/none, adopted sibling/own kept/sibling not reached within 3000 ms) (RBC_GP ab_gp_channel_negotiate.c) tooltip "Site startup timestamp"
startup_role start-up: no A/B sibling state, negotiating from timestamps or start-up: A/B sibling reports X, resuming that role tooltip "Start-up role"
sibling_mismatch site state mismatch with local peer (mismatch count N/3 ...) and Type=NEGOTIATION_MISMATCH (REQ-SITEROLE-014) tooltip "A/B site state check"; the final event paints the node red
takeover Type=TAKEOVER (silent counterpart, confirmation received or rejected, key state) tooltip "Last takeover event"
agree / disagree Type=AGREE / Type=DISAGREE (A/B cross-compare) site nodes green / unit red
safe_state any A/B line containing entering SAFE state (e.g. watchdog clock failed - entering SAFE state) unit red
alarm, lifecycle_gateway Type=ALARM; Type=LIFECYCLE Info=gateway service starting/shutting down unit red; C node green

The A/B node tooltip lists the channels ab-peer, ab-crosscompare, ab-negotiate, ab-relay-*, the C node tooltip c-sim-*, c-relay-*, c-broker-* (RBC_GP etc/config/*.json).

Paths

PROJECT_ROOT is test/RBC_Test_Env; the workspace root is two levels above it. Site data comes from application/RBC_SA/etc/site/, GP configuration from application/RBC_GP/etc/config/.

Verification

backend/test_web_runner.py must pass after any change to server.py or events.py: it parses one current line of each RBC status type (_RBC_STATUS_LINES, no server needed), then starts server.py and checks the HTTP endpoints, the WebSocket stream, the derived train and route state and the exactly-once push. The page itself is checked by hand against a live local environment.

Train front, end of MA and route hover

  • Train front. The train marker is drawn with its leading edge on the nearest signal that faces the train's direction of movement (train 1 east, train 2 west), at or ahead of the position computed from the balise and d_lrbg. The train tooltip shows that signal and how far ahead it is.
  • End of MA. Drawn separately: a red ring on the signal named by ma_target_signal (from CTC_TRAIN_INFO) with the label "MA end <signal> [remaining / granted]". The MA arrow runs from the train front to it. Without a target signal the old chainage estimate is used.
  • Route hover. Hovering a route's rails shows start signal, end signal, its points with the required position, the authored length (length_m from the railML routeDescription) and direction, then the status and the IL/RBC messages.

Docker mode (2026-09-26)

The environment selector next to the buttons applies to Build, Start, Stop and Run. With docker, Run starts runTests.sh --env docker --stream-logs --no-destructive: the fast Docker suite, or - when a scenario is selected - just that test (--test NAME, same as the local path; 2026-10-07). Container logs stream live the same way as a local run: the Robot listener spawns one docker logs -f -t --tail 0 tail per container and feeds its lines through the same normalizer as the local per-process files (before 2026-10-07 it only scanned testEnv/logs/*.log, which a Docker run never writes, so a Docker run streamed only Robot's own steps). A test whose name matches nothing under env:docker/env:any fails with Robot's own "did not match any test" message. The scenario list shows each test's env:* tag in square brackets.