System Topology Live infrastructure & pipeline flow — read-only

← Dashboard History / Report → Devices → Simulation → Model Reports → Auto Reports → refresh 30 s
Pipeline loading… Reading live state…

Live topology

Sources → pipeline → infrastructure. Depth = architectural layer.
—
devices · sources
loading…
pipeline · inside the app container
Model queues + workers
Decision Output
infrastructure · docker-compose.yml
external
httpx GET
TLS :5014
compose network · default
psycopg2 :5432
redis-py :6379
critical = the device's own alarm limit was crossed (a fact, not a forecast) worker thread down traced device stopped reporting
reachable / flowing idle · no traffic observed unreachable / down stale — stopped reporting

How this was verified

Infrastructure — read from docker-compose.yml + Dockerfile

  • Exactly three compose services exist: postgres (postgres:16, :5432), redis (redis:7-alpine, :6379), app (built from ./Dockerfile, uvicorn on :8000). No others are drawn because no others exist.
  • app declares depends_on both stores with real healthchecks (pg_isready / redis-cli ping), so it starts only once both accept connections.
  • app → postgres is a SQLAlchemy/psycopg2 TCP connection (postgres:5432/water_anomaly_v2) — the compose env overrides .env's localhost value for the container only.
  • app → redis is plain key/value + list commands over redis:6379/0 (SET/GET/SADD/SMEMBERS/LPUSH/LTRIM/SCAN, TTL'd) from RedisStateCache and RawHistoryCache, which share one client. There is no pub/sub anywhere in the app — a grep for pubsub/publish/subscribe returns nothing, so no message-bus edge is drawn.
  • app → device API is an outbound httpx call to api.enelsa.com:5014, which is outside the compose network — hence drawn outside the dashed boundary.
  • Volumes: named postgres_data / redis_data, plus two bind mounts (./config, ./storage) that exist so the device roster and the model artifacts survive down/up --build.

Pipeline — read from the live code

  • MEASUREMENT_TYPES is 6 types including ec_combined — not 6 channels plus a 7th. GET /system-health reports those 6 worker threads plus null and persistence, so this page draws 8 threads, not the 6 the previous version showed.
  • The persistence worker is a real registered thread with its own queue; it was missing from the previous diagram and is drawn here.
  • Critical is never emitted by a model — the deployed models are 3-class. It is decided after aggregate() by critical_threshold_rule (ph, orp, temperature, ec_in) and device_percent_rule (ec_combined). Normal / watch / warning come from the models on every channel; critical means the device's own alarm limit was crossed.

Data sources — all pre-existing and read-only

  • GET /system-health — worker liveness, queue depths, Redis/DB reachability, counters.
  • GET /devices/status — per-device rollup, last-updated, data age.
  • GET /monitoring/barrier-timeouts — real barrier timeouts per device.
  • GET /devices/{id}/status — fetched only when you click a device to trace it.

This page adds no failure-detection logic of its own and exposes no action — every state shown is something the system already tracks. Connectors animate only while the pipeline can actually move data; they burst on a real increase in barrier completions between polls, red out a downed lane, and freeze (keeping the last known picture) if a poll fails.