Wait for stopListening before listenUDP to avoid EADDRINUSE when GENERATOR
collapses (D-APRS-0 -> D-APRS). Migrate instance-0 protocol on bind unchanged.
Reload completion runs via Deferred callback.
Multi-hotspot SYSTEM inject must not REPEAT or bridge-downlink TGs to peers
whose RPTO OPTIONS omit the call TG. Exempt parrot/echo 9990–9999 for the
RX_PEER that keyed the service TG.
Add real-stack REPEAT and proxy fan-in E2E tests so regressions outside
DeterministicScenario are caught. Remove duplicate or fragile tests,
consolidate monitor remap cases, and document harness vs integration policy.
Overlay Talker Alias in repeated DMRD bursts so WPSD/MMDVMHost can display
aliases on RX, buffer TA from OBP voice, and inject the template at VHEAD
when the source sends no TA.
Match bridge_tables_with_active_source and to_target forward selection on
subscription rows; add parity tests vs legacy BRIDGES scan including OBP dedup.
Add opt-in REPORTS.MQTT with retained shared state and live voice_event
topics, topology static TG in v2 payloads, dashboard_state builder, and
SIGHUP reload.
Decouple bridge events from TCP send via a reactor-drained queue (50ms, 2048
events). Export peer connected_at in topology so monitor timers survive refresh.
Move Talker Alias / embedded LC and BRIDGES table lifecycle into
application/bridge/; BridgeUseCases remains the public facade with
dmrd_received and unit/private paths only.
Move shared TG/OBP helpers and legacy timer loops (rule_timer, trimmers,
bridge_reset, bridge_debug) into application/bridge/; BridgeUseCases keeps
the public facade via BridgeTimerMixin.
Introduce RuntimeContextHolder with ConfigProxy so main wiring keeps a stable
config view while SIGHUP reload builds a deep copy, applies reload_server_config,
then swaps the pointer only on success.
Inject ReportingUseCases into BridgeUseCases and centralize BRDG_EVENT
emission through _send_bridge_event; use reporting use cases for periodic
CONFIG/BRIDGE snapshots in main.
Stop renumbering DMRD sequence to 1..255 on replay; duplicate seq values
after ~15–30s caused MMDVMHost to drop voice while TX continued. Match
legacy playback.py (stream ID rewrite only).
Add TALKER_ALIAS_TEXT_FORMAT: single or comma-separated encodings for
embedded LC (e.g. utf8,iso8 for Motorola and Hytera). Example template
defaults to both formats; runtime default remains utf8 when omitted.
Extract corrected encode_emblc and TA embedded-LC wiring from domain into
infrastructure adapters. Inject DmrEmbeddedLcEncoder and TalkerAliasEmblcEncoder
from main.py so application layers stay free of infrastructure imports. Use the
fixed encoder for bridge group LC and pkt_gen as well as Talker Alias overlay.
- both: passthrough when source sends TA (DMRA or embedded voice), inject
template otherwise; OBP sources inject immediately at VHEAD
- Always rewrite destination group embedded LC on bridge forward (legacy
parity; fixes OBP TG mismatch packet loss introduced by TA branch)
- Fix dmr_utils3 encode_emblc index bug for lossless injected TA text
- Log decoded TA from voice; strict MMDVM DMRA wire layout
- chore: ruff per-file E402 ignore for main/parrot entrypoints
Popping STATUS[stream_id] on BCSQ (and forward-leg END,TX) destroyed live
stream state mid-call, causing repeated *CALL START* churn, a BCSQ storm and
GROUP_HANGTIME ping-pong on HBP masters that left receivers (MMDVMHost) with
only the voice header before the watchdog expired. Legacy hblink only records
_bcsq[tgid] and skips the quenched target; the stream entry survives until the
trimmer removes it by age. Emit a one-shot monitor END,TX and keep STATUS.
Clear stale OpenBridge TX chips in the monitor when BCSQ quenches a forward
leg or the source stream goes idle, using the same BRDG_EVENT format as legacy.
Two report paths passed eight format arguments into seven placeholders, so duration was silently dropped. Remove unused get_bridges() call in bridge_reset_loop.
Run routerHBP group controls before bridge scans, pass UDP receive time
into dmrd_received for rate/timeout parity with legacy, and set RX_START
on new HBP streams in the router. Talker Alias behaviour is unchanged.
Add idle (5s) and max (180s) recording timers, synthetic VTERM when the
hotspot ends without terminator, and non-blocking reactor playback.
Handle hotspot re-key mid-PTT without a second VHEAD and renumber playback
seq so bridge ingress does not drop frames. Restore prohibited static bridge
legs (TG 9990 ECHO) after bridge reset and options refresh.
Align OpenBridge group voice with bridge_master.routerOBP: use the legacy
RATE DROP formula, run loop control only from the second packet onward, and
send BCSQ once on loop loser instead of periodic retransmits.
Notify report clients immediately after SIGHUP reload, emit END events when
removing systems, and keep OPTIONS/TS1_STATIC/TS2_STATIC when merging YAML.
Reload GLOBAL, REPORTS, ALIASES and SYSTEMS without full restart:
add/remove UDP listeners (OBP, GENERATOR expansion), update bind
addresses, preserve PEERS/STATS and active streams on unchanged ports.
Inject Talker Alias on HBP bridge/repeat via standalone DMRA packets
and embedded LC in forwarded DMRD voice (FLCO 4–7) for MMDVMHost/
DMRGateway. UTF-8 encoding (format 2) and LC/TA superframe alternation
for the full stream; clear embed state on VTERM.
When LOGGER.ENABLED is false, setup_logging attaches only a NullHandler
so legacy configs without the key keep current behavior. Document the
option in EN/ES configuration guides and example YAML templates.
Use a flat OBP STATUS dict like legacy routerOBP, iterate it in
stream_trimmer_loop, stop HBP STATUS[stream_id] writes, cap BCSQ log
dedup with a bounded deque, and sweep stale voice-announcement entries.
Add reopen_file_handlers() and register SIGUSR2 after logging setup in
main and parrot. Document create/postrotate/kill -USR2 in monitoring
guide (EN/ES). SIGUSR2 only reopens file handlers; does not reload YAML.
Skip options_config_for_system when static TS/timer fingerprint matches the last
successful RPTO apply to avoid log spam and redundant work.
Document why make_single_bridge must not rebuild 9990–9999 when the bridge
already exists: it drops the ECHO ACTIVE row and breaks the parrot path.
When BRIDGES[tg] existed without TS1/TS2 rows for a MASTER, make_static_tg
only updated existing rows and that hotspot never received traffic. Append
inactive legs like make_stat_bridge / legacy UA re-add.
- Compute rate from elapsed time since stream START (not packets/START).
- Drop duplicate frames by CRC even when seq is 0 (retransmissions).
- Remove _log_trace helper and TRACE calls for BCSQ/BCKA/ACL skips.
- on_obp_bcsq_received: clear forward STATUS only; do not send_bridge_event
- Loop-control loser: report END,TX instead of END,RX (no LH 0s / global OBP pop)
- Sync TARGET_SOCK from actual peer when RELAX_CHECKS accepts DMRD/DMRE
- Fallback TARGET_IP/PORT for BCSQ send; warn if no destination
- Resend BCSQ every 2s while loop-control loser (UDP loss)
- Emit END,RX on first loser transition for monitor
- Robust _bcsq TG key matching in to_target
- Emit END,TX for OBP forward destinations when source stream times out in trimmer
- After VTERM END,RX, sweep remaining forward legs so monitor state stays consistent
- Log MAX HOPS drops at DEBUG to avoid per-packet warning floods
- Drop obsolete OBP_BRIDGE_MESH_OWNER_BYPASS_BCSQ comments from example yaml
Match bridge_master/hblink behaviour: BRIDGES table iteration for
to_target, OBP VTERM/_fin timing, stream seq/crc handling, and
document related GLOBAL options.
- Match BRIDGES source rules with effective TS1 for OpenBridge (has_source).
- Normalize DMRE receive slot to 1 for OPENBRIDGE before STATUS/dmrd_received.
- Dedupe OBP (SYSTEM, TS) per packet like legacy sysIgnore (TG + reflector lists).
- Run dynamic-bridge deactivate for dst 4000 before ACL (group/unit/vcsbk; master+peer)
- Send PRIVATE VOICE,END,RX to report; set slot RX_START on private stream start; fix slot STATUS setdefault
- dmrd_received: return early for private call to 4000 (legacy parity)
- Replace _broadcast_active with _broadcast_active_tgs (set)
- Serialize only same-TG broadcasts; different TGs run in parallel
- Add _mark_slots_busy/_mark_slots_free for TS conflict prevention
- Add ffmpeg system dependency note in requirements.txt
- Use DefaultVoiceProvider when Audio dir exists (was gated by ANNOUNCEMENT_LANGUAGES)
- Create Audio dir at startup when voice config present
- ANNOUNCEMENT_LANGUAGES optional (ident only); each item has own LANGUAGE
- ident: fallback to per-system ANNOUNCEMENT_LANGUAGE when empty
- disconnected_voice: use system language directly
- Clarify ENABLED, ANNOUNCEMENT_LANGUAGES in adn-voice.example.yaml
- Add Audio/.gitkeep, update .gitignore to keep folder structure
- Port playback.py to clean architecture: PlaybackUseCases, parrot_main.py, adn-parrot.py
- Fix critical PEER auth bug: RPTACK/MSTNAK data offsets were off by 1 (5→6) preventing PEER login
- Extract config normalization to shared config_normalizer.py (used by both main and parrot)