SimCore
Shared transport-layer plumbing for the ADR-029 Train/IL/CTC RBC simulators
Loading...
Searching...
No Matches
simcore.control_server Namespace Reference

Classes

class  _ControlClient
class  ControlServer

Detailed Description

Non-blocking line-based TCP command server (ADR-029) - gives any sim
built on SimCore a real TCP/IP control port, with its own dedicated
command table: `ControlServer` takes @handlers/@json_handlers as
constructor arguments rather than owning any commands itself, so
Train/IL/CTC each wire up only the commands that make sense for that
sim (e.g. Train: REPORT/PING + sendMessage/getMessage; IL: ADD_ROUTE/
PING + sendMessage; CTC: PING + getMessage only - CTC is receive-only in
this scenario) - no sim sees another sim's commands, and SimCore itself
defines none. Lets Robot Framework (safeAPITestEnv/robot/) drive a
Train/IL sim's real, test-relevant actions on demand (e.g. "add a route
now", "report this position now") instead of only ever firing on a
fixed wall-clock schedule. Robot talks to this with its own built-in
Telnet library (no extra Python dependency needed) - see
safeAPITestEnv/robot/ for the keywords that use it.

Owns its own small selectors.DefaultSelector, polled non-blockingly
(timeout=0) once per iteration of the sim's own main loop, right
alongside that loop's own dual_link.py poll - a second cheap select()
syscall per iteration, not a separate thread. One accepted control
connection is served at a time per sim, which is all a test harness
needs.

Two command styles share this one port/line-framing, distinguished by
the line's own first character:

- **Plain-text commands** (original ADR-029 protocol): one command per
  line, ASCII, `NAME arg1 arg2\\n`. Unrecognized commands get
  "ERR unknown command\\n"; a command's own handler decides its own
  success reply (e.g. "OK\\n"). Still used by REPORT/ADD_ROUTE/PING -
  unchanged.
- **JSON commands** (safeAPITestEnv/doc/design/DESIGN.md - structured
  sendMessage/getMessage): a line starting with `{` is parsed as one
  JSON object, `{"cmd": "<name>", ...}`; the handler receives the whole
  parsed dict and returns a dict, which is sent back JSON-encoded plus
  a trailing newline. Kept as a genuinely separate dispatch path (not
  merged into the plain-text one by, say, treating `{"cmd":...}` as a
  single whitespace-free token) specifically because a JSON payload
  routinely contains internal whitespace (`{"cmd": "sendMessage", ...}`)
  that plain-text's own `line.split()` would incorrectly tokenize.

Neither style is part of the RBC's own rbc_envelope_t wire protocol -
this whole channel is kept deliberately separate (different port,
different framing) so a malformed/foreign line here can never be
confused for real RBC traffic.