From c2425783c1fe899610f06170f4bf86c5d56b850e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rodrigo=20P=C3=A9rez?= Date: Mon, 25 May 2026 23:33:52 -0400 Subject: [PATCH] feat: push CONFIG_SND to monitor on MASTER peer connect/disconnect Notify report clients immediately when a hotspot registers, closes, or times out, instead of waiting for REPORT_INTERVAL. --- docs/en/server/user-guide/monitoring.md | 2 +- docs/es/server/user-guide/monitoring.md | 2 +- .../infrastructure/twisted_adapters/udp_hbp.py | 18 ++++++++++++++++++ 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/docs/en/server/user-guide/monitoring.md b/docs/en/server/user-guide/monitoring.md index 5a242f3..384cb02 100644 --- a/docs/en/server/user-guide/monitoring.md +++ b/docs/en/server/user-guide/monitoring.md @@ -5,7 +5,7 @@ When **`REPORTS`** is enabled in the server config, the **ADN DMR Peer Server** listens on TCP and **report clients** (typically **adn-monitor**) connect and receive: - **HELLO** (opcode **`0xFF`**) — JSON sent **first** on each new TCP connection by **new-adn-server** (`adn-server`): `server` name, package **`version`**, **`protocol`** number, and **`features`** (e.g. `INGRESS`, `END_TX_FORWARD`, `PUSH_ON_CONNECT`). Lets the monitor tag the session as **v2** before any pickled payloads. -- **CONFIG_SND** / **BRIDGE_SND** — pickled snapshots of systems and bridges (sent immediately after HELLO on connect, and again on updates / request). +- **CONFIG_SND** / **BRIDGE_SND** — pickled snapshots of systems and bridges (sent immediately after HELLO on connect, on **`CONFIG_REQ`** / **`BRIDGE_REQ`**, on **SIGHUP** config reload, when a **MASTER** hotspot **registers or disconnects**, and on the periodic **`REPORT_INTERVAL`** loop). - **BRDG_EVENT** — text events for calls (`GROUP VOICE`, `PRIVATE VOICE`, etc.). Older stacks (**legacy** `adn-dmr-server`-style) may **omit** HELLO. **adn-monitor** waits up to **`ADN_CONNECTION.HELLO_TIMEOUT_MS`** (see [Monitor configuration](../../monitor/configuration.md#adn_connection)); if no HELLO arrives, it assumes **legacy** reporting. diff --git a/docs/es/server/user-guide/monitoring.md b/docs/es/server/user-guide/monitoring.md index 9e6719c..6136eaa 100644 --- a/docs/es/server/user-guide/monitoring.md +++ b/docs/es/server/user-guide/monitoring.md @@ -5,7 +5,7 @@ Cuando **`REPORTS`** está habilitado en la config del servidor, el **ADN DMR Peer Server** escucha en TCP y los **clientes de informes** (típicamente **adn-monitor**) se conectan y reciben: - **HELLO** (opcode **`0xFF`**) — JSON enviado **el primero** en cada conexión TCP por **new-adn-server** (`adn-server`): nombre **`server`**, **`version`** del paquete, número de **`protocol`** y lista **`features`** (p. ej. `INGRESS`, `END_TX_FORWARD`, `PUSH_ON_CONNECT`). Permite al monitor marcar la sesión como **v2** antes de las cargas pickle. -- **CONFIG_SND** / **BRIDGE_SND** — instantáneas pickle de sistemas y bridges (tras HELLO al conectar, y de nuevo en actualizaciones / petición). +- **CONFIG_SND** / **BRIDGE_SND** — instantáneas pickle de sistemas y bridges (tras HELLO al conectar, en **`CONFIG_REQ`** / **`BRIDGE_REQ`**, en **reload** de config (**SIGHUP**), cuando un hotspot **MASTER** **registra o desconecta**, y en el bucle periódico **`REPORT_INTERVAL`**). - **BRDG_EVENT** — eventos de texto para llamadas (`GROUP VOICE`, `PRIVATE VOICE`, etc.). Las pilas antiguas (**legado** estilo `adn-dmr-server`) pueden **omitir** HELLO. **adn-monitor** espera hasta **`ADN_CONNECTION.HELLO_TIMEOUT_MS`** (ver [Configuración del monitor](../../monitor/configuration.md#adn_connection)); si no llega HELLO, asume informes **legacy**. diff --git a/src/adn_server/infrastructure/twisted_adapters/udp_hbp.py b/src/adn_server/infrastructure/twisted_adapters/udp_hbp.py index 3d93fe4..fa85210 100644 --- a/src/adn_server/infrastructure/twisted_adapters/udp_hbp.py +++ b/src/adn_server/infrastructure/twisted_adapters/udp_hbp.py @@ -432,6 +432,20 @@ class HBPProtocol(DatagramProtocol): for _pi in getattr(self, "_peers", {}): self.proxy_IPBlackList(_pi, self._peers[_pi]["SOCKADDR"]) + def _push_config_to_monitor(self) -> None: + """Push CONFIG_SND when MASTER peer list or connection state changes.""" + report = self._report + if report is None: + return + if not self._CONFIG.get("REPORTS", {}).get("REPORT", True): + return + systems = self._CONFIG.get("SYSTEMS", {}) + if hasattr(report, "set_systems"): + report.set_systems(systems) + if hasattr(report, "send_config"): + report.send_config() + logger.debug("(REPORT) Pushed CONFIG_SND after peer state change on %s", self._system) + def datagramReceived(self, data: bytes, addr: tuple[str, int]) -> None: if len(data) < 4: return @@ -472,6 +486,8 @@ class HBPProtocol(DatagramProtocol): del sys_cfg["OPTIONS"] logger.info("(%s) Deleting HBP Options", self._system) sys_cfg["_reset"] = True + if remove_list: + self._push_config_to_monitor() def _master_datagram_received(self, _data: bytes, _sockaddr: tuple[str, int]) -> None: """Direct port of hblink.py master_datagramReceived (lines 888-1146).""" @@ -751,6 +767,7 @@ class HBPProtocol(DatagramProtocol): logger.info("(%s) Deleting HBP Options", self._system) del sys_cfg["OPTIONS"] sys_cfg["_reset"] = True + self._push_config_to_monitor() else: _peer_id = _data[4:8] if _peer_id in self._peers and self._peers[_peer_id]["CONNECTION"] == "WAITING_CONFIG" and self._peers[_peer_id]["SOCKADDR"] == _sockaddr: @@ -782,6 +799,7 @@ class HBPProtocol(DatagramProtocol): else: self.send_peer(_peer_id, b"".join([RPTACK, _peer_id])) logger.info("(%s) Peer %s (%s) has sent repeater configuration, Package ID: %s, Software ID: %s, Desc: %s", self._system, _this_peer["CALLSIGN"], _this_peer["RADIO_ID"], self._peers[_peer_id]["PACKAGE_ID"].decode("utf8", errors="replace").rstrip(), self._peers[_peer_id]["SOFTWARE_ID"].decode("utf8", errors="replace").rstrip(), self._peers[_peer_id]["DESCRIPTION"].decode("utf8", errors="replace").rstrip()) + self._push_config_to_monitor() else: self.transport.write(b"".join([MSTNAK, _peer_id]), _sockaddr) logger.info("(%s) Peer info from Radio ID that has not logged in: %s", self._system, int_id(_peer_id))