readme update

This commit is contained in:
Logan
2026-04-06 02:54:31 -04:00
parent 6fdc36fe4c
commit fd6c2fd8bf
+148 -15
View File
@@ -47,15 +47,21 @@ Server/
│ │ ├── routers/ │ │ ├── routers/
│ │ │ ├── nodes.py # Node CRUD, approve/reject, command dispatch, system assignment │ │ │ ├── nodes.py # Node CRUD, approve/reject, command dispatch, system assignment
│ │ │ ├── systems.py # Radio system CRUD (P25/DMR/NBFM configs) │ │ │ ├── systems.py # Radio system CRUD (P25/DMR/NBFM configs)
│ │ │ ├── calls.py # Call log retrieval │ │ │ ├── calls.py # Call log retrieval (read-only — calls created by MQTT handler)
│ │ │ ├── tokens.py # Discord bot token pool — add/delete/list; assign_token/release_token helpers │ │ │ ├── tokens.py # Discord bot token pool — add/delete/list; assign_token/release_token helpers
│ │ │ ── upload.py # Audio file upload endpoint (called by edge nodes) │ │ │ ── upload.py # Audio file upload endpoint (called by edge nodes)
│ │ │ ├── incidents.py # [PLANNED] Incident CRUD, manual link/unlink calls, resolve
│ │ │ └── alerts.py # [PLANNED] Alert rule CRUD
│ │ └── internal/ │ │ └── internal/
│ │ ├── auth.py # Auth — Firebase ID token OR service key (Bearer) │ │ ├── auth.py # Auth — Firebase ID token OR service key (Bearer)
│ │ ├── firestore.py # Async Firestore wrappers (doc_get, doc_set, doc_update, collection_list) │ │ ├── firestore.py # Async Firestore wrappers (doc_get, doc_set, doc_update, collection_list)
│ │ ├── mqtt_handler.py # MQTT publisher — commands, config pushes, API key provisioning │ │ ├── mqtt_handler.py # MQTT subscriber — call_start/call_end handlers, node checkin/status
│ │ ├── node_sweeper.py # Background task — marks nodes offline after 90s without heartbeat │ │ ├── node_sweeper.py # Background task — marks nodes offline after 90s without heartbeat
│ │ ── storage.py # GCS audio file uploads │ │ ── storage.py # GCS audio file uploads
│ │ ├── transcription.py # [PLANNED] STT pipeline — Whisper or Google Speech API
│ │ ├── intelligence.py # [PLANNED] Keyword/entity extraction, severity scoring
│ │ ├── incident_correlator.py # [PLANNED] Match calls to incidents or create new ones
│ │ └── alerter.py # [PLANNED] Alert dispatch (Discord webhook, push, etc.)
│ ├── scripts/ │ ├── scripts/
│ │ └── set_admin.py # CLI to grant/revoke Firebase admin custom claim │ │ └── set_admin.py # CLI to grant/revoke Firebase admin custom claim
│ ├── gcp-key.json # GCP service account key — place here, NOT committed to git │ ├── gcp-key.json # GCP service account key — place here, NOT committed to git
@@ -74,11 +80,13 @@ Server/
└── drb-frontend/ # Next.js 14 admin dashboard └── drb-frontend/ # Next.js 14 admin dashboard
├── app/ ├── app/
│ ├── dashboard/ # Overview: active node grid + live call feed │ ├── dashboard/ # Overview: active node grid + live call feed
│ ├── map/ # Leaflet map — node locations, status colors, active call popups │ ├── map/ # Leaflet map — node locations + [PLANNED] incident pins
│ ├── calls/ # Full call history with duration, audio playback │ ├── calls/ # Call history — talkgroup, duration, audio, transcript (when populated)
│ ├── nodes/ # Node list + per-node detail (approve, reject, assign system) │ ├── nodes/ # Node list + per-node detail (approve, reject, assign system)
│ ├── systems/ # Radio system CRUD │ ├── systems/ # Radio system CRUD (P25 form with talkgroup editor, DMR/NBFM JSON)
│ ├── tokens/ # Discord bot token pool management │ ├── tokens/ # Discord bot token pool management
│ ├── incidents/ # [PLANNED] Incident list/detail — linked calls, location, resolve
│ ├── alerts/ # [PLANNED] Alert rule configuration
│ └── login/ # Firebase Auth login page │ └── login/ # Firebase Auth login page
├── components/ ├── components/
│ ├── MapView.tsx # react-leaflet map with status-colored markers │ ├── MapView.tsx # react-leaflet map with status-colored markers
@@ -208,16 +216,141 @@ Edge nodes join Discord voice channels using bot tokens managed by the server. A
5. Node sends `online` / `recording` / `unconfigured` status via 30s MQTT heartbeats 5. Node sends `online` / `recording` / `unconfigured` status via 30s MQTT heartbeats
6. `node_sweeper` background task marks any node offline after 90s without a heartbeat 6. `node_sweeper` background task marks any node offline after 90s without a heartbeat
## Call & Intelligence Pipeline
This is the full intended data lifecycle for every radio call — from raw RF to searchable, cross-referenced intelligence. The data models and Firestore schema are already designed for this; several pipeline stages are stubs awaiting implementation.
### Call lifecycle (current — fully working)
```
Edge node ──► MQTT call_start ──► c2-core creates CallRecord (status: active)
│ talkgroup_id, talkgroup_name, freq,
│ node_id, system_id, started_at
Firestore "calls" collection
Frontend live call feed / map popups
Edge node ──► MQTT call_end ──► c2-core updates CallRecord (status: ended)
│ ended_at, audio_url (GCS link)
Frontend call history / audio playback
```
### Intelligence pipeline (designed — implementation pending)
After a call ends, the following stages should fire in order:
```
CallRecord (ended, audio_url set)
[1] TRANSCRIPTION
Speech-to-text on the GCS audio file (Whisper or Google Speech-to-Text)
→ writes CallRecord.transcript
[2] INTELLIGENCE EXTRACTION
Analyze transcript for:
- Named entities: unit IDs, street addresses, location references
- Keywords / keyword sets: fire/EMS/police/hazmat/pursuit/shots fired/etc.
- Radio codes (10-codes, signals) mapped to plain English
- Severity scoring
→ writes CallRecord.tags[], CallRecord.location (geocoded if address found)
[3] INCIDENT CORRELATION
Given the extracted entities + tags, decide:
- Does this call match an existing active IncidentRecord?
(same location ± radius, overlapping tags, recent time window)
→ link: append CallRecord.call_id to IncidentRecord.call_ids,
set CallRecord.incident_id, update IncidentRecord.summary
- Or does this call describe a new event?
→ create IncidentRecord (type, location, title, tags, status: active)
link the call, set CallRecord.incident_id
[4] ALERTS
If incident is new OR severity exceeds threshold:
- Trigger configured alert channels (Discord webhook, push notification, etc.)
- Include: incident type, location, talkgroup, transcript excerpt
```
### Data model fields involved
**`CallRecord`** (Firestore collection: `calls`):
| Field | Type | Populated by |
|---|---|---|
| `call_id` | string | MQTT handler on call_start |
| `node_id` | string | MQTT handler |
| `system_id` | string | MQTT handler (from node's assigned system) |
| `talkgroup_id` | number | MQTT handler |
| `talkgroup_name` | string | MQTT handler |
| `freq` | number | MQTT handler |
| `started_at` | timestamp | MQTT handler |
| `ended_at` | timestamp | MQTT handler on call_end |
| `status` | `active` \| `ended` | MQTT handler |
| `audio_url` | string | Edge node upload → GCS |
| `transcript` | string \| null | **[stub]** Transcription pipeline |
| `incident_id` | string \| null | **[stub]** Incident correlation |
| `location` | `{lat, lng}` \| null | **[stub]** Geocoding from transcript |
| `tags` | string[] | **[stub]** Intelligence extraction |
**`IncidentRecord`** (Firestore collection: `incidents`):
| Field | Type | Description |
|---|---|---|
| `incident_id` | string | UUID |
| `title` | string | Auto-generated or manually set |
| `type` | string | `fire`, `ems`, `police`, `hazmat`, `pursuit`, etc. |
| `status` | `active` \| `resolved` | Updated as calls accumulate or manually resolved |
| `location` | `{lat, lng}` | Geocoded from first call with a location |
| `call_ids` | string[] | All linked CallRecord IDs |
| `started_at` | timestamp | Timestamp of first linked call |
| `updated_at` | timestamp | Updated on each new linked call |
| `summary` | string \| null | Auto-generated from transcripts or manually written |
| `tags` | string[] | Union of tags from all linked calls |
### Backend — what needs to be built
The following do **not exist yet** and need to be created:
- `drb-c2-core/app/routers/incidents.py` — CRUD + manual incident management
- `drb-c2-core/app/routers/alerts.py` — Alert rule configuration
- `drb-c2-core/app/internal/transcription.py` — STT integration (Whisper local or Google Speech API)
- `drb-c2-core/app/internal/intelligence.py` — Keyword/entity extraction, severity scoring
- `drb-c2-core/app/internal/incident_correlator.py` — Match calls to incidents or create new ones
- `drb-c2-core/app/internal/alerter.py` — Dispatch alerts (Discord webhook, etc.)
The `_on_call_end()` handler in `mqtt_handler.py` is the natural trigger point — after updating the CallRecord, it should enqueue the transcription + intelligence pipeline.
### Frontend — what needs to be built
The following pages and nav items are not yet implemented:
- `/incidents` — Incident list and detail view (linked calls, map pin, transcript summary, tags, resolve button)
- `/alerts` — Alert rule configuration (keyword sets, talkgroup filters, notification channels)
- `Nav.tsx` should add **Calls** and **Incidents** as primary nav items; the current Calls page exists but isn't in the design's intended nav hierarchy
The **Map** page should eventually show both node markers and incident pins — incidents with `location` set should appear as color-coded markers (by type/severity) alongside the node status markers.
## Frontend Pages ## Frontend Pages
| Page | URL | Description | | Page | URL | Status | Description |
|---|---|---| |---|---|---|---|
| Dashboard | `/dashboard` | Live node grid + active call stream | | Dashboard | `/dashboard` | Working | Live node grid + active call stream |
| Map | `/map` | Leaflet map — nodes color-coded by status, click for active call details | | Map | `/map` | Working | Leaflet map — nodes color-coded by status, active call popups |
| Calls | `/calls` | Full call history — talkgroup, duration, audio playback | | Calls | `/calls` | Working | Full call history — talkgroup, duration, audio playback, transcript (when populated) |
| Nodes | `/nodes` | Node list; per-node page for approve/reject/assign system | | Nodes | `/nodes` | Working | Node list; per-node detail for approve/reject/assign system |
| Systems | `/systems` | Create and manage radio system configurations | | Systems | `/systems` | Working | Create and manage P25/DMR/NBFM radio system configurations |
| Tokens | `/tokens` | Manage Discord bot token pool | | Tokens | `/tokens` | Working | Discord bot token pool management |
| Incidents | `/incidents` | **Not built** | Incident list/detail — linked calls, location, summary, tags, resolve |
| Alerts | `/alerts` | **Not built** | Alert rule configuration — keywords, talkgroups, notification channels |
## Makefile Targets ## Makefile Targets