From f8f6ae52374b7a5a5487c83a9aa5e23f514ec9d1 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 13 Jul 2026 22:08:38 +0000 Subject: [PATCH 1/3] chore(release): 2.3.2 --- CHANGELOG.md | 8 ++++++++ README.md | 2 +- pyproject.toml | 2 +- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9842190..c829be0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,14 @@ All notable changes to **adn-server** are documented here. +## v2.3.2 (2026-07-13) + +### Bug Fixes + +- Inject scheduled voice via routing and restore monitor TX events + ([`62f429a`](https://github.com/ce5rpy/ADN-DMR-Peer-Server/commit/62f429aad90272e1684f554ac8a1999cf15eed43)) + + ## v2.3.1 (2026-07-13) ### Bug Fixes diff --git a/README.md b/README.md index dc257b4..e1f680e 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # ADN DMR Peer Server -**Version 2.3.1** — pairs with **adn-monitor 2.0.0-rc.4** (report v2 slim wire + JSON HELLO). +**Version 2.3.2** — pairs with **adn-monitor 2.0.0-rc.4** (report v2 slim wire + JSON HELLO). ADN DMR conference bridge server. Configuration is YAML; the codebase follows clean architecture (domain, application, infrastructure). v2 adds integrated **PROXY**, **SubscriptionStore** routing, report v2 to the monitor, and a unified **`adn-server.py`** entrypoint (`--echo`, `--doctor`, `--no-proxy`). See [CHANGELOG.md](CHANGELOG.md). diff --git a/pyproject.toml b/pyproject.toml index 266eca4..b19e576 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,7 +8,7 @@ build-backend = "setuptools.build_meta" [project] name = "adn-server" -version = "2.3.1" +version = "2.3.2" description = "ADN DMR Peer Server" readme = "README.md" license = { text = "GPL-3.0-or-later" } From e140822e68fc65331123f12ee1ece8b7122a2690 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rodrigo=20P=C3=A9rez?= Date: Mon, 13 Jul 2026 18:27:20 -0400 Subject: [PATCH 2/3] fix: gate OBP source ensure when bridge source is already active Skip ensure_obp_source_for_tg_store on OBP voice bursts when indexed store lookup shows an ACTIVE TS1 source for the TG table, avoiding two full subscription snapshots on every packet under concurrent OBP load. --- .../application/routing/obp_forward.py | 11 +++++++++-- .../application/subscription/obp_source_ops.py | 17 +++++++++++++++++ .../test_subscription_table_store_ops.py | 18 +++++++++++++++++- 3 files changed, 43 insertions(+), 3 deletions(-) diff --git a/src/adn_server/application/routing/obp_forward.py b/src/adn_server/application/routing/obp_forward.py index 66cd09f..496e1ce 100644 --- a/src/adn_server/application/routing/obp_forward.py +++ b/src/adn_server/application/routing/obp_forward.py @@ -81,9 +81,16 @@ class ObpForwardMixin: return if not (79 <= dst_int < 9990 or dst_int > 9999): return - from ..subscription.obp_source_ops import ensure_obp_source_for_tg_store + from ..subscription.obp_source_ops import ( + ensure_obp_source_for_tg_store, + obp_source_needs_ensure, + ) + + store = self._subscription_store + if not obp_source_needs_ensure(store, system_name, relay_table_key, dst_int): + return ensure_obp_source_for_tg_store( - self._subscription_store, + store, system_name, relay_table_key, dst_id_b, diff --git a/src/adn_server/application/subscription/obp_source_ops.py b/src/adn_server/application/subscription/obp_source_ops.py index e03404c..e029c2b 100644 --- a/src/adn_server/application/subscription/obp_source_ops.py +++ b/src/adn_server/application/subscription/obp_source_ops.py @@ -48,6 +48,23 @@ def _tgid_match(entry_tgid: Any, dst_id_b: bytes, dst_int: int) -> bool: return False +def obp_source_needs_ensure( + store: SubscriptionStore, + system_name: str, + relay_table_key: str, + dst_int: int, +) -> bool: + """True when a bridge table exists but OBP lacks an ACTIVE TS1 source for ``dst_int``.""" + active_tables = set(store.relay_tables_with_active_source(system_name, 1, dst_int)) + pending_keys: list[str] = [] + for key in (relay_table_key, "#" + relay_table_key): + if store.legs_in_table(key): + pending_keys.append(key) + if not pending_keys: + return False + return any(key not in active_tables for key in pending_keys) + + def ensure_obp_source_for_tg_store( store: SubscriptionStore, system_name: str, diff --git a/tests/application/test_subscription_table_store_ops.py b/tests/application/test_subscription_table_store_ops.py index a1adf41..8cc96fc 100644 --- a/tests/application/test_subscription_table_store_ops.py +++ b/tests/application/test_subscription_table_store_ops.py @@ -24,7 +24,10 @@ from __future__ import annotations from tests.harness.deterministic import active_routing_table, minimal_config -from adn_server.application.subscription.obp_source_ops import ensure_obp_source_for_tg_store +from adn_server.application.subscription.obp_source_ops import ( + ensure_obp_source_for_tg_store, + obp_source_needs_ensure, +) from adn_server.application.subscription.store_sync import replace_store_from_routing_table from adn_server.application.subscription.subscription_table_ops import ( ensure_dynamic_relay_store, @@ -64,9 +67,22 @@ def test_ensure_obp_source_activates_ts1_leg() -> None: if entry["SYSTEM"] == "OBP-CL": entry["ACTIVE"] = False store = _store(bridges) + assert obp_source_needs_ensure(store, "OBP-CL", "7305", 7305) is True ensure_obp_source_for_tg_store(store, "OBP-CL", "7305", bytes_3(7305), 7305, now=500.0) obp = next(s for s in store.snapshot() if s.system.value == "OBP-CL") assert obp.is_active() + assert obp_source_needs_ensure(store, "OBP-CL", "7305", 7305) is False + + +def test_obp_source_needs_ensure_false_when_no_bridge_table() -> None: + store = _store() + assert obp_source_needs_ensure(store, "OBP-CL", "7305", 7305) is False + + +def test_obp_source_needs_ensure_false_when_obp_source_already_active() -> None: + bridges = active_routing_table(7305, (("OBP-CL", 1), ("MASTER-A", 2))) + store = _store(bridges) + assert obp_source_needs_ensure(store, "OBP-CL", "7305", 7305) is False def test_ensure_dynamic_relay_store_obp_leg() -> None: From 2a530c24fde466f9a027471c890fb95dab1c09d2 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 13 Jul 2026 22:35:53 +0000 Subject: [PATCH 3/3] chore(release): 2.3.3 --- CHANGELOG.md | 8 ++++++++ README.md | 2 +- pyproject.toml | 2 +- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c829be0..c6d4395 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,14 @@ All notable changes to **adn-server** are documented here. +## v2.3.3 (2026-07-13) + +### Bug Fixes + +- Gate OBP source ensure when bridge source is already active + ([`e140822`](https://github.com/ce5rpy/ADN-DMR-Peer-Server/commit/e140822e68fc65331123f12ee1ece8b7122a2690)) + + ## v2.3.2 (2026-07-13) ### Bug Fixes diff --git a/README.md b/README.md index e1f680e..6c4258d 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # ADN DMR Peer Server -**Version 2.3.2** — pairs with **adn-monitor 2.0.0-rc.4** (report v2 slim wire + JSON HELLO). +**Version 2.3.3** — pairs with **adn-monitor 2.0.0-rc.4** (report v2 slim wire + JSON HELLO). ADN DMR conference bridge server. Configuration is YAML; the codebase follows clean architecture (domain, application, infrastructure). v2 adds integrated **PROXY**, **SubscriptionStore** routing, report v2 to the monitor, and a unified **`adn-server.py`** entrypoint (`--echo`, `--doctor`, `--no-proxy`). See [CHANGELOG.md](CHANGELOG.md). diff --git a/pyproject.toml b/pyproject.toml index b19e576..92ae522 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,7 +8,7 @@ build-backend = "setuptools.build_meta" [project] name = "adn-server" -version = "2.3.2" +version = "2.3.3" description = "ADN DMR Peer Server" readme = "README.md" license = { text = "GPL-3.0-or-later" }