Skip to content

Latest commit

Β 

History

3 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 

Repository files navigation

🌊 AquaTrace

Real-time water contamination detection & source-tracing system

Find not just that a drain network is contaminated β€” but exactly which node it came from.

Node Central System Dashboard MQTT


πŸ“¦ The repositories

AquaTrace is split into three independently deployable services. This repo is just the map β€” πŸ”— head into each one for its own setup, deployment, and code-level docs.

# Repository What it is Stack
πŸ›°οΈ contamination-node The edge fleet β€” a simulated network of physical drain-monitoring devices, each running real classification, alerting & OTA logic Python Β· FastAPI Β· scikit-learn Β· asyncio-mqtt
🧠 contamination-central-system The brain β€” MQTT ingestion, Postgres storage, and the mass-balance tracing engine that pinpoints contamination sources Python Β· FastAPI Β· SQLAlchemy (async) Β· Postgres Β· Alembic
πŸ–₯️ contamination-dashboard The eyes β€” a live operator UI: network map, incident/plume playback, sensor health, model registry React Β· Vite Β· react-leaflet Β· recharts

None of them share code. node and central-system only ever agree over the wire (a shared MQTT protocol spec, implemented independently on each side); dashboard only ever talks to central-system's REST API. Exactly like three real, separately-owned systems would.


🎯 The problem AquaTrace solves

In a drain / industrial-effluent network, contamination β€” an illegal chemical discharge, a sanitation failure, or a sensor fault mimicking one β€” can enter at any node. By the time it's visible downstream, it's diluted, delayed by travel time, and mixed with everything else in the flow. A single sensor crossing a threshold tells you something is wrong upstream β€” it doesn't tell you where.

AquaTrace models the drain network as a directed acyclic graph (DAG) of monitoring nodes and edges (drain reaches), and solves localization with mass-balance reconciliation: at every node, does the contaminant mass actually observed match what its parents' mass β€” shifted by travel time and decayed appropriately β€” would predict? Wherever it doesn't, that's unexplained mass, and the tracing engine flags it as a contamination-source candidate.


πŸ—οΈ Architecture

                              MQTT (register Β· summary Β· alert Β· status Β· model-update/ack)
   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” ═══════════════════════════════════════════════════▢ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
   β”‚   πŸ›°οΈ  contamination-   β”‚                                                       β”‚  🧠  contamination-      β”‚
   β”‚       node             β”‚ ◀═══════════════════════════════════════════════════ β”‚      central-system      β”‚
   β”‚                         β”‚        OTA model download (plain HTTPS GET)         β”‚                           β”‚
   β”‚  fleet of simulated     β”‚                                                     β”‚  MQTT ingestion β†’ DB     β”‚
   β”‚  edge devices, one      β”‚ ◀───────────────────────────────────────────────────│  β†’ tracing engine β†’ API  β”‚
   β”‚  per drain node         β”‚   simulation-control proxy (demo only, HTTP)        β”‚                           β”‚
   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                                                       β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                                                                                    β”‚ REST API
                                                                                                    β–Ό
                                                                                     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                                                                                     β”‚  πŸ–₯️  contamination-      β”‚
                                                                                     β”‚      dashboard           β”‚
                                                                                     β”‚  map Β· incidents Β·       β”‚
                                                                                     β”‚  sensor health Β· models  β”‚
                                                                                     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Golden rule of this architecture: dashboard never talks to node directly β€” even the "press start" simulation controls are forwarded through a narrow, isolated proxy inside central-system (app/api/sim_proxy.py), which does nothing but forward the HTTP call verbatim. It touches no database table, no MQTT client, and no tracing code β€” the entire real ingestion/tracing pipeline has zero awareness a simulation even exists.


πŸ›°οΈ 1. contamination-node β€” the edge fleet

Each Virtual Node stands in for one physical device sitting at a point in the drain network. On every tick, it:

  • πŸ“ Derives hydraulics β€” flow rate (Q), flux, and mass per contaminant, from live level/speed via Manning's equation.
  • πŸ§ͺ Classifies locally, two ways at once:
    • Rule-based β€” against registry-defined safe/warn/red thresholds.
    • ML-based β€” a per-contaminant scikit-learn anomaly model, trained offline and shipped on-device, hot-swappable via OTA with a shadow-mode evaluation window before a new model is trusted.
  • 🩺 Gates on sensor health β€” a generic onboard-diagnostic check (range, stuck-value, and implausible-jump heuristics) marks a node/contaminant OK / SUSPECT / FAILED; unreliable readings are excluded downstream, both locally and by the central tracing engine.
  • 🚨 Alerts intelligently, not noisily β€” three trigger types working together:
    • fast_path β€” a single reading extreme enough not to wait for debounce.
    • debounced β€” classic k-of-n debounce (3-of-5) with hysteresis to clear.
    • cusum β€” a CUSUM detector on the residual against a rolling baseline, catching sustained loads that never individually cross into WARN/RED.
  • πŸ“‘ Publishes registration, periodic summaries, and alerts over MQTT β€” exactly as an independently-deployed physical device would.

Contaminants tracked: pH Β· temperature Β· conductivity Β· TSS Β· dissolved oxygen Β· BOD Β· COD Β· ammoniacal nitrogen Β· fecal coliform Β· chromium(VI) Β· urea β€” a mix of physical, biological, and industrial-discharge indicators, each with its own decay behaviour (conservative, reactive, biologically-reactive, or physically-settling) defined in a shared registry.json.


🧠 2. contamination-central-system β€” the control center

The backend of record, and home of the tracing engine. It:

  • πŸ“₯ Ingests MQTT idempotently β€” QoS-1 can redeliver, so dedup is enforced at the database layer (a unique constraint on node_id + seq), not just in application code.

  • πŸ—„οΈ Persists readings, topology, and incidents to Postgres via async SQLAlchemy + Alembic migrations.

  • πŸ” Runs the tracing engine on a schedule, walking the node DAG in topological order:

    for each node, parents-first:
        M_observed = mass actually measured at this node
        M_expected = Ξ£ over parents of (parent's mass, shifted by travel time Ο„, decayed by e^(-kΟ„))
        U          = M_observed βˆ’ M_expected                     # unexplained mass
        if |U| > 2 Γ— Οƒ_U (propagated uncertainty):
            β†’ flag as a contamination-source candidate
            β†’ classify cause: local injection Β· dilution loss Β· pass-through
            β†’ record an Incident with a confidence score
    

    Travel time (Ο„) and decay (k) per edge come from real hydraulics β€” reach geometry is first estimated from node geolocation + invert level at registration time, then continuously refined from live flow data. Headwater nodes (no parents) fall back to rolling-baseline anomaly detection instead, since there's nothing upstream to reconcile against.

  • 🌐 Serves the REST API the dashboard consumes: nodes, edges, incidents (+ time-series playback data for the plume view), sensor health, and the model registry.

  • πŸ”€ Proxies simulation control to node's orchestrator through the isolated router described above.


πŸ–₯️ 3. contamination-dashboard β€” the operator UI

A single-page React/Vite app with five views:

View What it shows
πŸ—ΊοΈ Network Map Live node/edge health on an OpenStreetMap base layer, polled continuously
⏱️ Incident List & Detail A time-slider plume playback β€” node colour and edge flux animate directly from the tracing engine's own mass/flux numbers, with the top candidate source highlighted throughout
🩺 Sensor Health / Replacement Queue Per-node, per-contaminant reliability table
πŸ”„ Model Registry OTA model versions & shadow-mode status, with a "Push Update" action
▢️ Simulation Control Panel Start/stop the demo network, inject a discharge event or a sensor fault, and watch detection + tracing happen live

πŸ”„ End-to-end data flow

  1. πŸ“‘ A node measures (or, in the demo, simulates) hydraulics and contaminant concentrations, and classifies the reading locally.
  2. πŸ“€ It publishes a summary β€” and any alert β€” over MQTT.
  3. πŸ“₯ central-system ingests and persists it, then periodically runs the tracing engine across the current topology.
  4. 🚩 Wherever observed mass isn't explained by upstream contributions, an Incident is recorded with a source candidate and confidence score.
  5. πŸ–₯️ dashboard queries the REST API and renders the live map, the incident's plume playback, and sensor/model health for an operator to act on.

πŸ”Œ The wire protocol

node and central-system are deliberately not sharing a package β€” both sides implement the same protocol spec independently (protocol/schemas.py / app/schemas.py, byte-for-byte duplicated, not imported), the way two real, separately-owned systems would.

Topics:

Topic Direction Purpose
nodes/{id}/register node β†’ CC A device announcing itself + its claimed geometry/parents
cc/{id}/register/ack CC β†’ node Assigned parents, baseline model refs, baselining period
nodes/{id}/summary node β†’ CC Periodic hydraulics + per-contaminant readings
nodes/{id}/alert/{contaminant} node β†’ CC A WARN/RED crossing, with its trigger type
nodes/{id}/status node β†’ CC Battery, RSSI, buffer depth
cc/{id}/model/update CC β†’ node Push a new model version for a contaminant
nodes/{id}/model/ack node β†’ CC Confirms load / shadow-mode result

πŸš€ Running the full stack locally

Each repo has its own detailed setup guide β€” here's the short version, in order:

# 1️⃣  central-system β€” Postgres + Mosquitto, migrations, then the API
git clone https://github.com/devraj-saini-02/contamination-central-system
cd contamination-central-system
docker-compose up -d && alembic upgrade head
uvicorn app.main:app --reload --port 8000

# 2️⃣  node β€” the simulated device fleet + its orchestrator
git clone https://github.com/devraj-saini-02/contamination-node
cd contamination-node
uvicorn orchestrator:app --reload --port 8100

# 3️⃣  dashboard β€” the operator UI
git clone https://github.com/devraj-saini-02/contamination-dashboard
cd contamination-dashboard
npm install && npm run dev   # β†’ http://localhost:5173

For cloud deployment (Render + Supabase for central-system, Render for node, Vercel/Netlify for dashboard, HiveMQ Cloud as the shared MQTT broker), see the Deploying section in each repo's own README. ☁️


πŸŽ›οΈ About the simulation

There's no real sensor hardware deployed for this build, so contamination-node stands in a physics-based World Simulator β€” hydraulics, advection-dispersion-reaction transport, diurnal contamination baselines, and injectable discharge/fault events β€” for what an actual field device would measure. Everything downstream of that fake input is real, production-shaped code: classification, alerting, health gating, the MQTT protocol, OTA updates, and the entire tracing engine in central-system would run unchanged against genuine sensors. The dashboard's Simulation Control Panel just lets an operator start the fleet and inject events for demo purposes β€” it has no bearing on how detection or tracing actually works. πŸ§ͺ

About

Base repository of Branched Project

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors