You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
204 lines
5.4 KiB
204 lines
5.4 KiB
# Reporting Model
|
|
|
|
Decision: FreeDMR 2 replaces the legacy dashboard/report socket model with a new structured reporting event model.
|
|
|
|
The existing dashboard is not a compatibility constraint for the FreeDMR 2 core. It must be updated separately or supported by an optional out-of-process adapter.
|
|
|
|
Reporting is observational only. Packet routing must not depend on dashboard/report consumers.
|
|
|
|
The v2 event schema is the compatibility contract. Raw `BRIDGES`/`SYSTEM` state should not be exposed as the primary v2 API. Old dashboard/report socket event names should not shape the FreeDMR 2 core.
|
|
|
|
Any old dashboard compatibility must live in a sidecar/adapter, not inside packet routing. FreeDMR 1.x/current code remains live until FreeDMR 2 is ready, so FreeDMR 2 can make a clean reporting break.
|
|
|
|
## Preferred Transport
|
|
|
|
MQTT is the preferred external live reporting transport.
|
|
|
|
Architecture:
|
|
|
|
```text
|
|
packet path -> non-blocking bounded local event queue -> MQTT publisher worker -> local broker/feed
|
|
```
|
|
|
|
Constraints:
|
|
|
|
- MQTT publishing must be asynchronous from the packet worker.
|
|
- Use a bounded queue.
|
|
- The bounded local event queue is the only coupling from packet path to reporting worker.
|
|
- Drop or coalesce low-priority events under pressure.
|
|
- Emit a later reporting-health event rather than blocking packet handling.
|
|
- Voice stability takes precedence over reporting completeness.
|
|
- Reconnect with exponential backoff.
|
|
- Refresh retained state after reconnect.
|
|
- Reporting backpressure must be visible through reporting-health events but must not delay DMR packets.
|
|
|
|
Reporting is the first major candidate for out-of-process execution. The MQTT publisher should be an independent worker or sidecar where practical. The global lastheard exporter should be a separate process. Dashboard aggregation should not run in the packet hot path.
|
|
|
|
Reporting worker crash must not affect packet routing. Reporting worker restart should refresh retained state after reconnect.
|
|
|
|
## Local Dashboard and Global Lastheard
|
|
|
|
Local dashboard:
|
|
|
|
- Consumes local MQTT live state/events.
|
|
- Displays live client/repeater/server traffic.
|
|
- Recovers from retained state after reconnect.
|
|
|
|
Global lastheard:
|
|
|
|
- Central/non-real-time.
|
|
- Consumes summaries, not packet-plane traffic.
|
|
- Should preferably be fed by a separate exporter process.
|
|
- Central outage must not affect local packet handling or local dashboard.
|
|
|
|
Preferred flow:
|
|
|
|
```text
|
|
FreeDMR core -> local MQTT feed -> local dashboard
|
|
-> global-exporter process -> network MQTT/collector
|
|
```
|
|
|
|
## Initial Event Families
|
|
|
|
- `server.started`
|
|
- `server.stopping`
|
|
- `client.connected`
|
|
- `client.disconnected`
|
|
- `client.options_changed`
|
|
- `subscription.activated`
|
|
- `subscription.deactivated`
|
|
- `subscription.expired`
|
|
- `call.started`
|
|
- `call.ended`
|
|
- `call.lost`
|
|
- `mesh.peer_up`
|
|
- `mesh.peer_down`
|
|
- `mesh.source_quench`
|
|
- `mesh.stun`
|
|
- `loop.detected`
|
|
- `packet.rate_limited`
|
|
- `reporting.queue_overflow`
|
|
- `reporting.publisher_disconnected`
|
|
- `reporting.publisher_reconnected`
|
|
- `reporting.events_dropped`
|
|
|
|
## Suggested MQTT Topics
|
|
|
|
```text
|
|
freedmr/v2/{server_id}/state
|
|
freedmr/v2/{server_id}/client/{client_id}/state
|
|
freedmr/v2/{server_id}/client/{client_id}/slot/{slot}/activity
|
|
freedmr/v2/{server_id}/subscription/{subscription_id}/state
|
|
freedmr/v2/{server_id}/call/{stream_id}/start
|
|
freedmr/v2/{server_id}/call/{stream_id}/end
|
|
freedmr/v2/{server_id}/mesh/{peer_id}/state
|
|
freedmr/v2/{server_id}/event
|
|
```
|
|
|
|
Use retained messages for current state and non-retained messages for transient events.
|
|
|
|
## Example Events
|
|
|
|
```json
|
|
{
|
|
"event": "server.started",
|
|
"server_id": "2345",
|
|
"version": "2.0-dev",
|
|
"time": "2026-05-24T12:00:00Z"
|
|
}
|
|
```
|
|
|
|
```json
|
|
{
|
|
"event": "client.connected",
|
|
"server_id": "2345",
|
|
"client_id": 2345001,
|
|
"listener": "hbp-public",
|
|
"endpoint": "198.51.100.10:62031",
|
|
"time": "2026-05-24T12:00:01Z"
|
|
}
|
|
```
|
|
|
|
```json
|
|
{
|
|
"event": "subscription.activated",
|
|
"server_id": "2345",
|
|
"subscription_id": "2345001-2-9-4400",
|
|
"client_id": 2345001,
|
|
"slot": 2,
|
|
"rf_tg": 9,
|
|
"conference_tg": 4400,
|
|
"mode": "dial",
|
|
"source": "dial-a-tg",
|
|
"time": "2026-05-24T12:00:02Z"
|
|
}
|
|
```
|
|
|
|
```json
|
|
{
|
|
"event": "call.started",
|
|
"server_id": "2345",
|
|
"stream_id": 12345678,
|
|
"client_id": 2345001,
|
|
"slot": 2,
|
|
"source_id": 2345678,
|
|
"rf_tg": 9,
|
|
"conference_tg": 4400,
|
|
"source": "hbp",
|
|
"time": "2026-05-24T12:00:03Z"
|
|
}
|
|
```
|
|
|
|
```json
|
|
{
|
|
"event": "call.ended",
|
|
"server_id": "2345",
|
|
"stream_id": 12345678,
|
|
"reason": "terminator",
|
|
"duration_ms": 18420,
|
|
"packets": 614,
|
|
"time": "2026-05-24T12:00:21Z"
|
|
}
|
|
```
|
|
|
|
```json
|
|
{
|
|
"event": "call.lost",
|
|
"server_id": "2345",
|
|
"stream_id": 12345678,
|
|
"reason": "timeout",
|
|
"last_seen_ms_ago": 7000,
|
|
"time": "2026-05-24T12:00:28Z"
|
|
}
|
|
```
|
|
|
|
```json
|
|
{
|
|
"event": "mesh.source_quench",
|
|
"server_id": "2345",
|
|
"peer_id": "2350",
|
|
"stream_id": 12345678,
|
|
"conference_tg": 4400,
|
|
"reason": "duplicate-source",
|
|
"time": "2026-05-24T12:00:04Z"
|
|
}
|
|
```
|
|
|
|
```json
|
|
{
|
|
"event": "reporting.queue_overflow",
|
|
"server_id": "2345",
|
|
"dropped_events": 42,
|
|
"queue_limit": 2048,
|
|
"policy": "drop-low-priority",
|
|
"time": "2026-05-24T12:00:05Z"
|
|
}
|
|
```
|
|
|
|
## Open Questions
|
|
|
|
- Broker packaging and defaults: Mosquitto, embedded broker, external broker, or optional dependency.
|
|
- Exact retained-state expiry policy.
|
|
- Authentication model for MQTT clients.
|
|
- Whether legacy dashboard compatibility is a supplied sidecar or a separate dashboard migration task.
|