Health Chain¶
When the health toggle in the dashboard header is on, every simulator card replaces its position view with a four-node diagnostic chain. The chain shows where, in the dashboard -> emulator -> simulator -> GPS-data pipeline, things are healthy and where they are not.
This view is the difference between "I have no idea why this card is gray" and "the emulator is fine but the simulator's GPS application crashed." It's worth knowing exactly what each segment means.
The chain¶
[Dashboard] -- [Emulator] -- [Simulator] -- [GPS Data]
| | | |
icon: bar icon: PC icon: plane icon: satellite
Four nodes, three connectors. Each connector is colored to indicate the health of the segment between two nodes; each node is colored based on whether that node itself has detected a failure or is "downstream of a failure."
What each segment checks¶
| Segment | What's actually checked | Source field |
|---|---|---|
| Dashboard -> Emulator | The dashboard received a heartbeat packet from the rebroadcaster within the last 3 seconds. | emulator_online (derived from last_heartbeat) |
| Emulator -> Simulator | The rebroadcaster's last ICMP ping to SIMULATOR_IP succeeded. |
sim_reachable (from the heartbeat payload) |
| Simulator -> GPS Data | A position packet (not a heartbeat) has arrived at the dashboard within the last 5 seconds. | is_online (the same flag that drives the green ONLINE pill in position view) |
The leftmost node (Dashboard) is always green - if you're rendering the chain at all, the dashboard is up.
States the chain can be in¶
The chain has six distinct visual states.
1. All implicitly OK¶
When GPS data is flowing (is_online == true), everything upstream is implicitly working. The chain shows four green nodes and three green connectors. Footer message: "All systems operational" on a green background.
This is the only "happy path" state. Every other state has at least one red element.
2. Emulator down¶
emulator_online == false. No heartbeat in 3 s.
| Element | State |
|---|---|
| Dashboard node | Green (it's the dashboard itself rendering this) |
| Dashboard -> Emulator connector | Red |
| Emulator node | Red border, gray interior, red icon |
| Emulator -> Simulator connector | Gray (after-failure) |
| Simulator node | Gray (after-failure) |
| Simulator -> GPS connector | Gray |
| GPS Data node | Gray |
Footer message: "Is the emulator container running?" on a yellow background.
Likely fixes:
- The rebroadcaster container is stopped.
docker compose psanddocker compose up -d. - The rebroadcaster is running but the UDP retransmit target IP is wrong - heartbeats are going somewhere else.
- The rebroadcaster is running with the right IP but the port is wrong - same effect.
- Network outage between the rebroadcaster host and the dashboard host.
3. Simulator unreachable¶
emulator_online == true (heartbeats arriving) but sim_reachable == false (the ping to SIMULATOR_IP is failing from inside the container).
| Element | State |
|---|---|
| Dashboard node | Green |
| Dashboard -> Emulator connector | Green |
| Emulator node | Green |
| Emulator -> Simulator connector | Red |
| Simulator node | Red border, gray interior, red icon |
| Remaining elements | Gray |
Footer message: "Is the simulator powered on? If yes, possible network issue." on a yellow background.
Likely fixes:
- The flight simulator host (X-Plane, MSFS, Cygnus box) is off.
- The simulator host is on but unreachable (different VLAN, host-side firewall blocking ICMP).
SIMULATOR_IPis set to the wrong IP.
If SIMULATOR_IP is empty
The rebroadcaster sends sim_reachable: false by convention when no SIMULATOR_IP is configured. So the Simulator segment will show red even if the upstream sim is fine. Either set SIMULATOR_IP or accept that this segment is always red for deployments that aren't ping-able.
4. GPS data not arriving¶
Heartbeats are arriving (emulator_online == true), the upstream simulator is reachable (sim_reachable == true), but no position packet has reached the dashboard in the last 5 seconds (is_online == false and the heartbeat says receiving_udp == false).
| Element | State |
|---|---|
| Dashboard node | Green |
| Dashboard -> Emulator connector | Green |
| Emulator node | Green |
| Emulator -> Simulator connector | Green |
| Simulator node | Green |
| Simulator -> GPS connector | Red |
| GPS Data node | Red border, gray interior, red icon |
Footer message: "Not receiving GPS data. Start or Restart GPSConnect application on {gps_system}." on a yellow background.
The {gps_system} is replaced with the simulator's SIM_N_GPS_SYSTEM env-var value (e.g., Avionics, Avionics 2, rehost). If SIM_N_GPS_SYSTEM is unset, the message falls back to the generic "Not receiving GPS data. Start or Restart the GPSConnect application on the simulator."
This is the most common red state in normal operation - it means the GPS-producing software on the upstream simulator host has crashed or hasn't been started yet. The tech-friendly wording is deliberate: a sim tech sees "go restart GPSConnect on Avionics 2" and knows exactly which physical system to walk over to.
5. Loading - first paint before any packets¶
If the dashboard starts but no card has yet had a packet, the cards in health view show the dashboard node green and everything downstream gray (no specific failure - just no data yet). The footer message is "All systems operational" because the smart-gating logic treats "no failure detected" as OK by default.
This usually only lasts a couple of seconds. If it persists, you're in state 2 ("Emulator down") - the dashboard simply hasn't logged that yet.
6. Disconnected (the dashboard itself is offline from your browser)¶
If the WebSocket from your browser to the dashboard drops, the cards don't update at all. The connection badge in the header goes red. Health chain remains in whatever state it was last in - it doesn't crash, it just freezes.
This is not a state of the chain - it's a state of your browser's connection to the dashboard. The chain only diagnoses the simulator side.
The "smart gating" detail¶
The health chain has a small but important optimization: if GPS data is flowing, everything upstream is implicitly OK and the chain renders all-green even if individual flags are stale.
Why this matters: heartbeats are 1 Hz, but the emulator->simulator ping is one shot per second with a 1-second timeout. The ping can fail intermittently (network jitter) without anything actually being broken. Without the gating, you'd see the Simulator segment flicker red even while position is flowing fine.
With the gating, the chain is green when it doesn't matter - and red only when something is actually wrong from the operator's perspective.
How the chain reads the data¶
flowchart LR
Heartbeat["Heartbeat<br/>(every 1s from rebroadcaster)"]
Position["Position packet<br/>(every 1s when GPS flowing)"]
Dashboard["Dashboard backend"]
HealthChainComp["HealthChain.jsx<br/>(in browser)"]
Heartbeat -->|sim_reachable, receiving_udp, uptime| Dashboard
Position -->|lat, lon, alt, speed, heading| Dashboard
Dashboard -->|fleet_state over WebSocket| HealthChainComp
HealthChainComp -->|render four nodes + connectors| User["Operator browser"]
The dashboard does no per-second polling - it's all push from the rebroadcasters (heartbeat + position) and then pushed onward to browsers (WebSocket fleet_state).
When the message is misleading¶
| Footer message | But what's actually wrong |
|---|---|
| "Is the simulator powered on?" | SIMULATOR_IP is set to an unreachable IP even though the simulator itself is fine. Check the env var. |
| "Is the simulator powered on?" | The simulator host is up but ICMP is filtered. Try ping <SIMULATOR_IP> from another host on the same segment. |
| "Not receiving GPS data" | GPS application is running but the rebroadcaster isn't listening on the right port - so packets are arriving on the host but never the dashboard. Check tcpdump -i any 'udp port <SIM_N_PORT>' on the dashboard host. |
| "Is the emulator container running?" | The rebroadcaster IS running but AUTO_START_UDP_RETRANSMIT_IP is wrong - heartbeats are going to a different host. |
Persistent state¶
The health view itself is not persisted - it's an in-memory React state in App.jsx. Every fresh page load starts in position view (health toggle off).
The data that the chain reads (emulator_online, sim_reachable, is_online) comes entirely from the WebSocket and is not persisted server-side either.
What's next¶
- Simulator Card - the position view that the chain replaces.
- Configuration - how
SIM_N_GPS_SYSTEMcontrols the GPS failure message. - Fleet Monitoring - end-to-end multi-simulator setup, including how
SIMULATOR_IPandAUTO_START_UDP_RETRANSMIT_*line up with the dashboard side.