M07 — Spatial Correlation, Fire Panel, and Alerts
One-line goal: when the fire panel reports a zone alarm, the system identifies all cameras with line-of-sight to that zone via PostGIS, runs Tier 2 priority analysis on those frames, and surfaces a correlated alert with evidence to the operator within 30 seconds.
This is the customer-visible feature that justifies the platform. Everything before M07 is plumbing; M07 is the demo moment.
Tracks involved
- Backend — primary. Alert engine, spatial correlation queries, alert routing, evidence packaging.
- Edge — fire-panel adapter (driver #1 for the pilot’s specific panel).
- AI Worker — priority-queue support for alarm-correlated escalations (jumps ahead of routine queue).
- Frontend — 3D digital twin scene, camera placement UI, alert console with evidence rendering.
Dependencies
- M03 (
alarm_event,alert,alert_action,floor_plan,zone_geometrytables exist). - M04 (cameras streaming).
- M05 (edge producing escalations to the cloud).
- M06 (cloud Tier-2 LLM producing
AnalysisEventverdicts). - External: fire panel model identified at site survey (reminders.md Q1); 3D model delivered from the modeler.
Deliverables
1. Fire-panel adapter
A driver in apps/edge-app/drivers/fire_panel/ implementing the FirePanelAdapter interface.
Pilot driver is TBD on panel model but most likely one of:
BacnetIpAdapter— usingBAC0orbacpypes3. Connects to BACnet/IP fire panels (Honeywell, Siemens, Johnson Controls). Subscribes to alarm objects via COV (Change-Of-Value). Maps panel-native zone IDs to our canonicalzone_idvia a per-building mapping config.ModbusTcpAdapter— usingpymodbus. Polls alarm registers; emits events on state change.ContactClosureAdapter— universal fallback per05-contingencies.md§5. USB-to-GPIO board connected to the panel’s alarm-relay outputs; reads state at 1 Hz.
The driver:
- Connects to the panel using the configured protocol.
- Subscribes to alarm events (COV or polling-and-diff).
- Normalizes each event to the canonical
AlarmEventshape (per contracts package). - Maps panel-native zone identifier to canonical
zone_idvia the per-building config — failed mapping logs a warning but the event is still forwarded withmapped_zone_id = nulland the alert is built fromprotocol_attributes. - Forwards
AlarmEventover the edge tunnel to the cloudalarm-eventsPub/Sub topic. - Reports panel-health telemetry separately (loss of comm to panel is itself an alert).
2. Zone-to-panel mapping
A configuration step in the building-onboarding flow:
- Admin views a list of zones from the panel (the adapter calls
list_zones()). - Admin maps each panel zone to a
zone_idin our system. - Mapping stored in
system_configkeyed bybuilding_id+panel_zone_native_id→zone_id. - Mapping can be edited later; an audit log entry records each change.
3. Spatial correlation engine
A service module in apps/api/src/spatial-correlation/ (running as part of the alert engine).
When an AlarmEvent arrives:
-- pseudo-SQL; real implementation uses Prisma + raw PostGIS
SELECT c.id, c.name,
ST_Distance(zg.centroid, c.position::geometry) AS distance,
/* coverage_score, viewing_angle_score — application-side */
FROM camera c
JOIN zone_geometry zg ON zg.zone_id = (SELECT mapped_zone_id FROM alarm_event WHERE id = $1)
WHERE c.status = 'online'
AND ST_DWithin(zg.boundary, c.position::geometry, 15.0) -- 15 m line-of-sight radius
ORDER BY distance ASC
LIMIT 6; Each candidate camera gets a relevance score combining:
- 40% coverage fraction (how much of the alarm zone polygon is in the camera’s view frustum, computed from
camera.position,orientation_deg,fov_deg, and the zone polygon) - 30% distance inverse
- 30% viewing angle (cameras pointing directly at the zone outrank ones with the zone at the edge of FOV)
Top 6 cameras are returned. If fewer than 3 cameras are spatially relevant, the correlation falls back to “all cameras on the same floor as the alarm zone” with a flag indicating the fallback is in use.
4. Alert engine
Service in apps/api/src/alert-engine/. Subscribes to alarm-events, analysis-events, and tier2-events Pub/Sub topics.
4.1 Alarm-driven alert flow
AlarmEventarrives.- Engine creates an
Alertrow with statusnew, severity from the alarm type. - Spatial correlation produces top-6 cameras.
- Engine asks AI Worker for priority Tier 2 inference on the most recent frame from each of those cameras (priority bypasses the routine Tier 2 queue).
- As each Tier 2 verdict arrives, engine attaches it to the alert as an
EvidenceItem. - Alert is updated in real time on the WebSocket fan-out (operators see new evidence appearing as it lands).
- Once all 6 verdicts (or a 20 s timeout) complete, the alert moves to status
investigatingif there is consensus, orescalatedif any verdict is REAL FIRE.
4.2 AI-driven alert flow
Tier2AnalysisEventarrives withthreat_detected = true.- Engine creates (or updates) an
Alert, severity based on the LLM’sseverity. - Evidence is attached: the source frame, the LLM verdict, related recent AnalysisEvents on the same camera.
- Alert published on WebSocket.
4.3 Alert rules
alert_rule rows (M03) provide configurable thresholds:
- Crowd excess:
person_count > zone.max_occupancy × 0.9→ severity HIGH. - Restricted zone entry: any person detection in
zone.is_restricted = trueoutside operating hours → severity MEDIUM. - Equipment fault: camera offline > 10 min → severity LOW.
- Connectivity: edge gateway offline > 5 min → severity HIGH.
Per-building / per-zone overrides allowed. Rules editable by admins; changes audit-logged.
4.4 Cooldown
Each rule has a cooldown_seconds. Once an alert fires from a rule, subsequent matching events don’t create new alerts for the cooldown period (they append to the existing one as additional evidence). Prevents flap.
5. Evidence-package generation
For each alert, an EvidenceItem collection is built:
- Camera snapshots — JPEG frames from each correlated camera at the moment of the event, with per-camera Tier 2 verdict overlaid as caption text.
- Tier 2 LLM verdicts — the full structured response from each camera analysis.
- Tier 1 detections — recent AnalysisEvents (last 60 s) for each correlated camera.
- Spatial view — the digital-twin scene rendered into a static image with the alarm zone and cameras highlighted (server-side render using a headless Three.js or a pre-rendered template).
- Audit trail — who has been notified, when, and any operator actions taken so far.
Evidence is stored in GCS with the rest of the tenant’s data; the alert references it by URL.
6. Alert routing
For the pilot, routing is minimal:
- All alerts visible to operators authorized for the building.
- FD-view (M08) is automatically populated with active alerts (no SMS or external integrations needed for the pilot).
- Escalation to specific roles based on severity (CRITICAL → all roles; LOW → operator only).
- Configurable per building if Civil Defence needs different routing.
Mobile alerting and dispatch-system integration deferred — call it M07.5 if it becomes urgent post-pilot.
7. Digital twin integration
The 3D scene is the platform’s spatial-awareness surface — see M08 for full polish. M07 delivers the minimum:
- 3D model file loaded into a Three.js scene (driver from
04-abstractions §5.10—GlbLoaderexpected). - Camera positions placed in the scene (one marker per camera; click to drill into the camera).
- Zone polygons rendered as semi-transparent volumes.
- During an alert: affected zone flashes red; relevant cameras’ markers highlight.
- Smooth camera-control (orbit, pan, zoom) with
@react-three/dreicontrols.
8. Camera placement UI
Admin tool to position cameras in the 3D scene:
- Load building’s 3D model.
- Drag-and-drop camera markers onto positions.
- Rotate orientation, set FOV angle, adjust placement height.
- Saves
position,orientation_deg,fov_degto thecameratable. - Visual check: camera’s view frustum is rendered as a cone; admin can verify it points at the right zones.
9. Operator alert console
apps/dashboard/src/views/Alerts.tsx:
- Active alerts list (real-time via WebSocket).
- Filters: severity, building, zone, alert type, time range.
- Click an alert → detail panel with all evidence items, the Tier 2 verdict, the spatial view, and action buttons (acknowledge / dismiss / confirm / escalate / add note / mark false alarm / resolve).
- Each action writes an
AlertActionrow; alert status moves through the lifecycle. - Resolved alerts archive after 30 days (configurable).
Verification
- End-to-end alarm flow. Trigger a simulated FACP zone alarm (via the panel adapter’s test-mode or a manually-published
AlarmEvent). Within 30 s: alert appears on the dashboard with the zone highlighted in the 3D twin, top-6 cameras shown with snapshots, Tier 2 verdicts visible, severity coded. - Spatial correlation finds the right cameras. With cameras placed in the 3D scene, trigger a zone alarm. The top-6 cameras returned are the ones a human would point at as “those have line-of-sight to that zone.” Verified visually plus by an automated test against a seeded scene.
- False-alarm verdict correctly down-weights. Trigger an alarm in a zone where the only nearby camera shows visible bakhoor smoke. Tier 2 returns PROBABLE FALSE ALARM; the alert’s banner reads accordingly; operator can mark it as false alarm.
- Real-fire verdict escalates correctly. Same setup with simulated fire imagery. Tier 2 returns REAL FIRE; the alert is severity CRITICAL; the FD-view sees it.
- Acknowledgement updates the lifecycle. Operator acknowledges; status →
acknowledged;AlertActionrow created; audit log entry exists. - Cooldown works. Two alarms from the same rule within the cooldown window produce one alert with multiple evidence items, not two alerts.
- Camera placement saves and renders correctly. Move a camera marker in the admin UI; refresh; position persists. Operator view sees the camera at the new position.
- Panel offline = its own alert. Disconnect the fire panel’s network cable; within 1 min an alert of severity HIGH appears noting “fire panel disconnected — fire correlation degraded”.
- Concurrent alerts handled. Trigger two alarms in different zones within 5 s of each other. Both alerts appear independently, each with its own evidence package and spatial highlighting.
- Restricted-zone rule fires. Place a test person in a
is_restricted=truezone outside hours; within ~5 s arestricted_zone_entryalert appears.
Risks
| Risk | Likelihood | Mitigation |
|---|---|---|
| Fire panel model unknown until site survey | High | Three protocol fallback options ready; contact-closure as universal last resort |
| 3D model arrives late or in wrong format | Medium | Asset spec given to modeler in week 1; fallback drivers (OBJ, 2D extrusion) ready |
| Spatial correlation incorrectly excludes a relevant camera | Medium | Fallback to “all on same floor”; manual override per-zone available |
| Tier 2 latency makes 30s SLO tight | Medium | Priority queue for alarm-correlated escalations bypasses routine queue |
| Operator overwhelmed by too many alerts during peak | Medium | Cooldowns; per-rule rate limits; severity-based filtering by default |
| False-alarm mark feeds back wrong signal | Low | Operator marking is audit-logged; used for prompt-template improvement, not autonomous model retraining |
Open questions
- Specific fire panel model at the Arafat site — reminders.md Q1. Affects driver implementation; should be confirmed week 1 of M07.
- Zone-to-panel-zone mapping data source. Does the panel expose human-readable zone names, or just numeric IDs? Depends on panel.
- Should restricted-zone rules respect prayer-time variants? Prayer halls have very different normal-occupancy patterns at prayer times vs other times. A
time_scheduletable linked to alert rules would solve this elegantly — deferred to M08 if time permits, M11+ otherwise.
Exit criteria
All 10 verification items pass. The signature demo flow (alarm → correlated evidence on operator screen in < 30 s) recorded on video as the customer demo artifact. M07 sign-off entry in docs/plan/COMPLETION_LOG.md.