2 Commits

Author SHA1 Message Date
Joseph Doherty b5f120c370 feat(observability): honest stream-subscriber name + real data-connection gauges (row 54)
scadabridge_site_connection_up never counted data connections. It counts open
central->site gRPC subscriber streams (Debug View + the site-wide alarm feed),
so it correctly reads 0 on a healthy site nobody is watching — and the name
invited reading it as device health. That misreading has already cost a live
debugging session: register row 48's 'connection_up is 0 on all six site
nodes' was healthy steady state, not the fault it was diagnosed as.

Two halves, per the row:

RENAME. scadabridge.site.connection.up -> scadabridge.site.stream.subscribers.active
(helpers SiteConnectionOpened/Closed -> SiteStreamSubscriberOpened/Closed), and
the instrument gains the unit:"1" the other gauges already carried. Every
reference updated: the gRPC server, both test suites, Component-Communication.md,
components/Commons.md, docker/README.md and docker/seed-live-probe.sh. A test
now pins the new name AND asserts the old one is gone, so it cannot creep back.

NEW. scadabridge.site.data_connections.connected / .total — the first exported
data-connection signal (the state existed on the health report and in the CLI,
but never as a metric). connected < total is the alert condition; neither
number alone separates 'a device is down' from 'nothing is configured', which
is why the denominator ships with the numerator.

Sourcing is deliberate on two counts. It is fed by the DCL at its four state
transitions rather than derived from the health report, so the metric survives
a dead report pipeline — the exact failure the 'metrics stale' flag exists to
catch, and a derived gauge would go blind at the same moment. And it uses a
static registry reached through ScadaBridgeTelemetry (the SiteConnectionOpened
precedent) rather than a DI-injected holder: the DCL actors already take
ISiteHealthCollector through their constructors, and threading a second
dependency through both would ripple into every test that builds them. It also
keeps SiteHealthCollector untouched, which matters right now — another agent is
concurrently editing that file for the tag-resolution path.

Removal is wired too: an undeployed connection leaves the registry, so it stops
counting toward the total instead of latching connected < total on forever.

Both gauges read 0 on the standby (connections open on the active node only) —
healthy, documented, and called out in the docs so it does not become the next
site_connection_up.

No ObservedMeters allowlist change: all three instruments ride the existing
ZB.MOM.WW.ScadaBridge meter. (An unlisted meter exports nothing, silently.)

Tests: Commons 711 passed (9 new, delta-based against a baseline with unique
per-test connection names, so they do not inherit the register row 32
QueueDepthGaugeTests static-state flake); Communication 717; DCL 319.
2026-08-15 12:53:12 -04:00
Joseph Doherty b5e6821caa chore(docker): seed script for a deployable live-data probe instance on the rig
Deferred-work register row 48. The rig had no deployable instance — all seven
soak-* rows fail pre-deployment validation (their bound data connections no longer
exist on the sites) — so debug snapshot returned instanceNotFound, no gRPC site
stream was ever opened, and the R2 batching histogram
scadabridge.site.stream.batch_size had nothing to record. Any live probe of the
data or streaming path was unverifiable.

seed-live-probe.sh rebuilds the minimum working set via the CLI only (no DB edits):
RigProbe template (Spike <- ns=3;s=SpikeData, RandomInt <- ns=3;s=RandomSignedInt32,
both ~1 Hz; Heartbeat static attribute + a trust-clean 5s interval script) and a
deployed rig-probe-1 on site-a against connection 1.

Records the two traps that cost time here:
- The custom tags in infra/opcua/nodes.json (Motor.*, Tank.*, Valve.*,
  TestChildObject.*) are DECLARED but not simulated by opc-plc — they read 0/false/
  epoch forever, so binding to them yields a Connected, Good-quality connection with
  zero event traffic. Only the built-in nodes move.
- scadabridge_site_connection_up counts open central->site gRPC SUBSCRIBER STREAMS,
  not OPC UA data connections. It reads 0 on a healthy site with no subscriber, which
  is how row 48 read it as a data-connection fault. Data-connection health lives in
  the health report's dataConnectionEndpoints / dataConnectionTagQuality.
2026-08-15 11:46:57 -04:00