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.
ADN-DMR-Peer-Server/docs/en/server/protocols/report-v2.md

14 KiB

Report protocol v2 (JSON)

Status: schema + TCP slim wire (STATE_SND) shipped on adn-server 2.x; monitor 2.x consumer required for production pairing.

Goals

Replace monitor snapshots that today use pickle (CONFIG_SND, BRIDGE_SND) and CSV strings (BRDG_EVENT) with typed JSON that any client can decode.

Release policy: adn-server 1.0.x + adn-monitor 1.0.x = report v1 (frozen tag pair). 2.x server sends HELLO with report_protocol: 2 and v2 JSON on TCP. The monitor connect path is HELLO → STATE_SND (dashboard_state) plus async ROUTING_TABLE_SND / DELTA_SND for UA/SINGLE_TS chips and VOICE_EVENT_SND for live calls. topology is internal-only (used to build dashboard_state, never sent on TCP). adn-monitor 2.x decodes v1 wire from legacy peers and v2 JSON from adn-server 2.x. adn-monitor 1.0.0 cannot decode v2 opcodes — upgrade the monitor line for adn-server 2.x.

Transport (unchanged)

  • TCP netstring frames (Twisted NetstringReceiver), same port as v1 (REPORTS.REPORT_PORT).
  • Each frame: 1-byte opcode + UTF-8 JSON payload (v2) or pickle/CSV (v1).

Opcodes

Opcode Hex v1 payload v2 payload
HELLO 0xFF JSON hello (protocol: 1) JSON hello (report_protocol: 2)
CONFIG_SND 0x01 pickle SYSTEMS — (use STATE_SND)
BRIDGE_SND 0x03 pickle BRIDGES — (use ROUTING_TABLE_SND)
BRDG_EVENT 0x07 CSV text — (use VOICE_EVENT_SND)
STATE_SND 0x14 — JSON dashboard_state
TOPOLOGY_SND 0x10 — (internal schema only — not emitted on TCP)
ROUTING_TABLE_SND 0x11 — JSON routing_table
VOICE_EVENT_SND 0x12 — JSON voice_event
DELTA_SND 0x13 — JSON delta
flowchart TB
  subgraph v1 [Report v1 — adn-server 1.0.x + monitor 1.0.x]
    H1[HELLO protocol 1] --> C1[CONFIG_SND pickle]
    C1 --> B1[BRIDGE_SND pickle]
    B1 --> V1[BRDG_EVENT CSV]
  end

  subgraph v2 [Report v2 — adn-server 2.x + monitor 2.x]
    H2[HELLO report_protocol 2] --> S2[STATE_SND dashboard_state]
    S2 --> R2[ROUTING_TABLE_SND JSON async]
    R2 --> V2[VOICE_EVENT_SND JSON]
    R2 -.-> D2[DELTA_SND optional]
  end

Handshake (hello)

On connect the server sends HELLO (0xFF) first (same as today). v2 clients inspect report_protocol:

{
  "type": "hello",
  "server": "adn-server",
  "version": "2.0.0-alpha.1",
  "report_protocol": 2,
  "features": ["INGRESS", "END_TX_FORWARD", "PUSH_ON_CONNECT", "REPORT_V2", "ROUTING_TABLE_JSON", "VOICE_EVENT_JSON", "DELTA_UPDATES"],
  "systems": ["MASTER-A", "OBP-CL"]
}
Field Notes
report_protocol 2 for this schema. Distinct from legacy field protocol: 1.
features v1 tokens unchanged; v2 adds REPORT_V2 and payload capabilities.

Monitor 1.0.x does not speak this wire; use the 1.0.x server tag for that pair.

Message types

type Replaces Purpose
dashboard_state CONFIG_SND (slim) Linked masters/peers/openbridges snapshot (STATE_SND). Full snapshot — monitor prunes absent masters.
topology CONFIG_SND (full) Internal schema / ops only — not sent on TCP monitor wire.
routing_table BRIDGE_SND Active bridge legs per talkgroup / reflector key (UA/SINGLE_TS chips).
voice_event BRDG_EVENT Structured call start/end/ingress.
delta — Incremental routing_table patch since since_seq.

Reference payloads

Each frame payload is one JSON object with a required type. Examples below use anonymized IDs.

dashboard_state

{
  "type": "dashboard_state",
  "ts": 1717555200.0,
  "server_id": "7302",
  "ctable": {
    "MASTERS": {
      "MASTER-A": {
        "mode": "MASTER",
        "ip": "10.0.0.1",
        "port": 62030,
        "peers": {
          "3120001": {
            "id": 3120001,
            "connected": true,
            "ip": "10.0.0.50",
            "port": 62031,
            "callsign": "CE5RPY"
          }
        }
      }
    },
    "PEERS": {},
    "OPENBRIDGES": {
      "OBP-CL": {
        "mode": "OPENBRIDGE",
        "network_id": 73010,
        "ip": "44.31.61.68",
        "port": 62999,
        "streams": {}
      }
    }
  }
}
Field Type Required Notes
ts float yes Epoch time of snapshot.
server_id string no GLOBAL.SERVER_ID (MQTT retained state).
ctable.MASTERS object yes Masters with connected peers only.
ctable.PEERS object yes Connected upstream PEER/XLXPEER systems.
ctable.OPENBRIDGES object yes Enabled OpenBridge legs (streams empty; live chips from voice_event).

Committed example: schemas/examples/dashboard_state.json.

topology

{
  "type": "topology",
  "seq": 1,
  "ts": 1717555200.0,
  "systems": [
    {
      "name": "MASTER-A",
      "mode": "MASTER",
      "enabled": true,
      "ip": "10.0.0.1",
      "port": 62030,
      "repeat": true,
      "peers": [
        { "id": 3120001, "connected": true, "ip": "10.0.0.50", "port": 62031 }
      ]
    },
    {
      "name": "OBP-CL",
      "mode": "OPENBRIDGE",
      "enabled": true,
      "ip": "10.0.0.2",
      "port": 62044,
      "enhanced_obp": true,
      "peers": []
    }
  ]
}
Field Type Required Notes
seq, ts int, float yes Monotonic sequence and epoch time.
systems[].name string yes System key (matches config).
systems[].mode string yes MASTER, PEER, or OPENBRIDGE.
systems[].enabled bool yes Config enabled flag.
systems[].ip, port string, int no Listen/connect endpoint.
systems[].repeat bool no Master repeat flag.
systems[].enhanced_obp bool no OpenBridge enhanced mode.
systems[].peers[] array no {id, connected, ip?, port?} per peer radio.

No passwords or encryption material are included (unlike legacy pickle).

routing_table

{
  "type": "routing_table",
  "seq": 42,
  "ts": 1717555260.5,
  "routes": [
    {
      "relay_table_key": "52090",
      "legs": [
        {
          "system": "MASTER-A",
          "ts": 2,
          "tgid": 52090,
          "active": true,
          "to_type": "ON",
          "timer_expires_at": 1717555320.0
        },
        {
          "system": "MASTER-B",
          "ts": 2,
          "tgid": 52090,
          "active": true,
          "to_type": "ON"
        }
      ]
    },
    {
      "relay_table_key": "#310",
      "legs": [
        {
          "system": "MASTER-A",
          "ts": 2,
          "tgid": 310,
          "active": false,
          "to_type": "NONE"
        }
      ]
    }
  ]
}
Field Type Required Notes
routes[].relay_table_key string yes Talkgroup id or reflector key (#nnn).
legs[].system string yes Target system name.
legs[].ts 1 | 2 yes Timeslot (legacy TS).
legs[].tgid int yes Talkgroup (1–16777215).
legs[].active bool yes Leg active in BRIDGES table.
legs[].to_type string yes ON, OFF, STAT, or NONE.
legs[].timer_expires_at float no rule_timer expiry (legacy TIMER).

voice_event

Legacy CSV:

GROUP VOICE,START,RX,MASTER-A,2155905152,1001,3120001,2,52090

v2 equivalent:

{
  "type": "voice_event",
  "ts": 1717555201.234,
  "call_family": "GROUP",
  "phase": "START",
  "direction": "RX",
  "system": "MASTER-A",
  "stream_id": 2155905152,
  "peer_id": 1001,
  "src_id": 3120001,
  "slot": 2,
  "dst_id": 52090,
  "duration_s": null,
  "is_announcement": false
}
Field Type Required Values / notes
call_family string yes GROUP, PRIVATE, UNIT, VCSSBK.
phase string yes INGRESS, START, END.
direction string yes RX, TX.
stream_id int yes 32-bit HBP stream id.
peer_id, src_id, dst_id int yes DMR ids (1–16777215).
slot int yes 1 or 2.
duration_s float | null no Set on END.
is_announcement bool yes true when the call is the announcement/TTS synthetic PTT, not a real connected peer — the reported peer_id/src_id must not be attributed to any logged-in bridge.

OpenBridge INGRESS vs START semantics match Monitoring and reports: use phase: "INGRESS" for first sight, phase: "START" after loop control.

delta

{
  "type": "delta",
  "seq": 43,
  "ts": 1717555261.0,
  "since_seq": 42,
  "patch": {
    "type": "routing_table",
    "seq": 43,
    "ts": 1717555261.0,
    "routes": [
      {
        "relay_table_key": "52090",
        "legs": [
          {
            "system": "MASTER-A",
            "ts": 2,
            "tgid": 52090,
            "active": false,
            "to_type": "ON"
          }
        ]
      }
    ]
  }
}
Field Type Required Notes
since_seq int yes Last seq the client applied.
patch object yes Partial topology or routing_table (same shape).

Sequencing

  • topology and routing_table messages carry monotonic seq (uint) and ts (float epoch).
  • Clients track last applied seq; delta messages set since_seq to the client watermark.
  • Full snapshots may still be sent on connect and on REPORTS.REPORT_INTERVAL (same triggers as v1 CONFIG/BRIDGE).

Configuration

REPORTS:
  REPORT: true
  REPORT_PORT: 4321

No PROTOCOL switch on 2.x — wire is always JSON (infrastructure/twisted_adapters/report/wire.py). Payload mapping: application/report/.

Optional MQTT mirror

By default the server sends reports only over TCP netstring (adn-monitor and other TCP clients). MQTT is disabled unless you explicitly enable it.

Enable only when both are set:

  1. REPORTS.MQTT.ENABLED: true (boolean true, not merely present)
  2. REPORTS.MQTT.URL — broker URL (mqtt://host:1883 or mqtts://host:8883)
REPORTS:
  REPORT: true
  REPORT_PORT: 4321
  MQTT:
    ENABLED: true
    URL: mqtt://127.0.0.1:1883
    TOPIC_PREFIX: adn/73010   # optional; default adn/{GLOBAL.SERVER_ID}
    USERNAME: my-mqtt-user     # optional; overrides user in URL
    PASSWORD: my-mqtt-secret   # optional; overrides password in URL
    CAFILE: /path/to/ca.pem    # optional; broker TLS trust store (mqtts://)
    QOS: 0                    # optional, 0–2

Client ID: auto-generated at startup as adn-server-{GLOBAL.SERVER_ID}-{random} (not configurable; random suffix avoids broker session collisions on restart).

Authentication: username/password via USERNAME and PASSWORD, or embedded in the URL (mqtt://user:pass@host:1883). YAML credentials override URL userinfo. Password may be empty if the broker allows it. With mqtts://, set CAFILE when the broker uses a private CA.

Requires optional dependency: pip install 'adn-server[mqtt]' (paho-mqtt). If MQTT is enabled but the library is missing, the server logs an error and continues with TCP only.

MQTT wire (fixed, not configurable): only voice_event (telemetry) and state (snapshot). TCP-only types (topology, routing_table, delta, hello) are not published on MQTT.

Topic convention (shared under {prefix}):

Topic Direction JSON type Retain
voice_event server → broker voice_event no
state server → broker dashboard_state yes

state carries masters with connected peers, homebrew peers, and openbridges (monitor WebSocket conf,lnksys + conf,opb intent). It is retained so new subscribers receive the last snapshot without requesting it. Topology-driven refreshes republish {prefix}/state when the dashboard changes (dedup).

Triggers: live voice_event; retained {prefix}/state on topology changes and after MQTT connect (dedup).

Example (SERVER_ID 7302):

mosquitto_sub -h BROKER -p 1883 -u USER -P PASS -t 'adn/7302/state' -v
mosquitto_sub -h BROKER -p 1883 -u USER -P PASS -t 'adn/7302/voice_event' -v

Broker ACL: consumers need subscribe on adn/7302/state and adn/7302/voice_event; the server client_id needs publish on those two topics only (no server-side subscribe).

Reload (systemctl reload / SIGHUP): when REPORTS.MQTT changes (enable/disable, URL, credentials, TLS, TOPIC_PREFIX, QOS), the server disconnects the old MQTT client and connects with the new settings, or stays offline if ENABLED becomes false.

QoS: configurable via MQTT.QOS (default 0).

rule_timer, stat_trimmer, and bridgeDebug use routing deltas when only part of BRIDGES changed; connect, REPORT_INTERVAL, reload, and client CONFIG_REQ / BRIDGE_REQ send full snapshots.

Version pairing (supported combinations)

Server Monitor Report wire Notes
1.0.x 1.0.x v1 (pickle/CSV) Frozen pair; no cross-upgrade of report protocol
2.0.0-alpha.* 2.x (dev) v2 only Current develop line
2.0.0 2.0.x v2 only GA pair; monitor 1.0.x not supported

Do not run monitor 1.0.x against server 2.0.0 or monitor 2.x against server 1.0.x for production.

See also Monitoring and reports.

Powered by TurnKey Linux.