# MSP-2 twins: stimulus glue (Candidates A and B)

Behaviour for the seven C++ clients of `msp2_A.vsi.cmd` and of `msp2_B.vsi.cmd`, so
a run moves data and the platform side reacts to it, instead of seven idle stubs.
One script serves both: port and signal names are identical on the two card
blocks, and `apply_glue.py` detects which card a twin holds from its `src` folder.

| file | role |
|---|---|
| `msp2_stim.h` | header-only, no VSI dependency: scripted stimulus (`TimeSource`, `CardDiscretes`, `SafetyHeartbeat`) and reactions (`CardTimeLock`, `ChassisMonitor`, `Watchdog`), plus a CSV `Trace`. Every scenario number is an env var `MSP2_<NAME>` with a default. |
| `test_msp2_stim.cxx` | host-side proof: runs the scenario for 100 steps with the twin's wiring and asserts the outcome, with two negative controls. `g++ -std=c++11 -static -o t test_msp2_stim.cxx && ./t` (the bundled `mingw64-11.2.0\bin\g++.exe`; `-static` or the mingw `bin` on PATH, else the exe cannot find `libgcc_s_sjlj-1.dll`). |
| `apply_glue.py` | writes the calls into the generated skeletons **by user-region name** (C++ `//` markers), copies the header into `<twin>\src`; re-run after any `generate -overwrite`, then `mingw32-make compile build` in the twin folder |

## The scenario (100 steps of 100 us)

| step | who | what a run must show |
|---|---|---|
| 0.. | Time Source | `REFCLK` toggles every step; `TIME_1PPS` pulses every 10 steps; `TIME_IRIGB` counts seconds from 43200 |
| 3 | card (bpDisc) | `CARD_PRESENT` rises: chassis controller ABSENT -> BOOTING |
| 20 | card | `HEALTH_OK` rises: chassis BOOTING -> HEALTHY; card time lock on the 3rd pulse |
| 60..64 | card | `BIT_FAIL` high: chassis HEALTHY -> FAULT -> HEALTHY |
| 70..84 | card (bpSafety) | `SAFETY_MON` heartbeat stops: safety monitor watchdog TRIPS at 74, clears at 85 |
| every 5 | card | one `SAFETY_BUS` frame (counter), 20 in the run |

Scripted versus reacting, stated plainly: the card's satellites are separate VSI
processes with no shared state, so the card's timeline is driven by the step
count. The chassis controller, the safety monitor and the card's time lock react
only to what arrives on their inputs. That reaction is what a sim proves and what
the host test already proves for the arithmetic.

What each client writes: `<component>_trace.csv` in its working directory with
`step,time_ns` and its signals, and a console line on every state change
(`[chassis] card in slot 3 -> HEALTHY at step 20`, `[safety monitor] heartbeat
LOST: watchdog TRIPPED at step 74`).

## Known limits

- The two Ethernet buses (control-plane Ethernet, mission data link) have
  sockets but no signal connections in the generated twin, so the Mission Data
  Recorder only traces its unchanged inputs. Wiring signals over the Ethernet
  gateway is the generator's next open item, not this glue's.
- `SAFETY_INHIBIT` is really an inhibit INTO the card; the generator guessed the
  card as the source of that bus, so the card sends a constant 0 and the header
  says why.
- Inputs are read in the step AFTER they arrive (the generated callback unpacks
  the packet; the glue reads `mySignals` in *After sending the packet*), so every
  reaction lands one step late by construction.

## Status 2026-09-06

Host test: ALL CHECKS PASSED (10 checks, 2 negative controls). Glue applied to all
seven components of BOTH candidates and each twin rebuilt with `mingw32-make
compile build`, zero errors (A 36 s, B see the main README). Not run: the fabric still needs the `saltd`-vendor VeloceStratoOS licence
(see `../LICENCE-REQUEST.md`).
