10 KiB
BRIDGES (legacy) vs Subscriptions (servidor nuevo)
adn-dmr-server y adn-server 2.x reenvían voz de grupo igual en el wire: una “tabla de bridge” por talkgroup decide qué systems reciben copia del stream, con reescritura LC por pata. Lo que cambia en 2.x es cómo se representa esa tabla en código — no las reglas visibles para el operador.
Para el operador
En runtime el comportamiento de bridges es el mismo: TG, slots, OPTIONS, UA, TG 4000, OpenBridge. Subscriptions es el nombre interno del motor de enrutado en 2.x; no es un modo de operación distinto ni algo que configures aparte.
Sin cambios operativos
- Configuración:
SYSTEMS, OPTIONS del hotspot,SELF_SERVICE/ MariaDB — igual que con adn-dmr-server. No hay bloqueBRIDGESen YAML en ninguno de los dos. - Paridad de reglas: fila origen ACTIVE, tablas
#…, timers UA,GEN_STAT_BRIDGES, etc.
Mejoras reales en 2.x
| Área | Efecto |
|---|---|
| Estabilidad del enrutado | El estado de bridges vive en un store dedicado; informes y timers ya no comparten la misma estructura mutable que el forward de voz. Menos desvíos entre lo que reenvía el servidor y lo que muestra el panel bajo carga. |
| Monitor | Informe v2 (routing_table, topology) hacia adn-monitor 2.x sustituye pickle/CSV; el BTABLE refleja mejor el estado del peer. |
| TG dinámicos | Con DATABASE, los dinámicos por peer se persisten y se restauran al reconectar (≥ 2.0.0-rc.3). |
| Evolución | Parches de timers, OpenBridge, ACL o self-service no pasan por un dict global compartido con todo el proceso. |
El término subscription solo importa si lees código o esta guía; en el panel y en el aire sigues hablando de bridges y talkgroups.
Resumen
Legacy (adn-dmr-server) |
Nuevo (adn-server 2.x) |
|
|---|---|---|
| Autoridad en runtime | Dict global BRIDGES (bridge_master.py) |
SubscriptionStore (objetos Subscription de dominio) |
| Estructura | bridge_key → [ fila, fila, … ] |
Una subscription por pata de system en un canal |
| Resolución de reenvío | Recorrer filas, llamar to_target |
SubscriptionRouter.resolve() → ForwardLeg |
| Wire monitor / informes | Pickle BRIDGE_SND = BRIDGES |
JSON routing_table (v2) o export BRIDGES (compat v1) |
Bloque YAML BRIDGES: |
No se carga desde config en ninguno; filas en runtime | Igual — filas desde OPTIONS, UA, STAT, OpenBridge, bootstrap echo |
El comportamiento observable (guardia de fila origen, UA dinámico, TG estática, claves reflector #…, match OpenBridge TS1, timers) sigue paridad legacy con bridge_master.py.
Legacy: dict BRIDGES
En adn-dmr-server, el estado de enrutado es un diccionario global:
BRIDGES["52090"] = [
{ "SYSTEM": "MASTER-A", "TS": 2, "TGID": b'...', "ACTIVE": True, "TO_TYPE": "ON", "TIMER": …, … },
{ "SYSTEM": "MASTER-B", "TS": 2, "TGID": b'...', "ACTIVE": True, "TO_TYPE": "ON", … },
{ "SYSTEM": "OBP-UK", "TS": 1, "TGID": b'...', "ACTIVE": False, … },
]
BRIDGES["#310"] = [ … ] # tablas reflector / marcado
Cada fila es una pata. Campos importantes:
SYSTEM— nombre del system configurado (master HBP o pata OpenBridge).TS— slot 1 o 2 (fuentes OpenBridge usan TS 1 en el match).TGID— bytes de destino para reescritura LC hacia esa pata.ACTIVE— la pata participa en el reenvío si es true.TIMEOUT/TIMER/TO_TYPE/ON/OFF/RESET— timers UA, static/stat, reglas VTERM in-band.
Camino de voz (dmrd_received):
- Obtener clave de bridge desde la TG destino (y tablas
#…cuando aplica). - Crear tabla dinámica si no existe (UA / STAT / OPTIONS estáticas — mismos disparadores que legacy).
- Buscar fila origen ACTIVE que coincida con system + slot + TGID actuales.
- Por cada otra fila ACTIVE en esa misma tabla, llamar
to_target(contención, ACL, LC/TA, control de bucle OpenBridge).
flowchart TB
IN[Ingreso DMRD / DMRE] --> DM[dmrd_received]
DM --> KEY{¿existe bridge key?}
KEY -->|no| CREATE[ensure dynamic / stat / static]
CREATE --> BR
KEY -->|si| BR[(dict BRIDGES)]
BR --> SRC{¿fila origen ACTIVE\nSYSTEM + TS + TGID?}
SRC -->|si| SCAN[otras patas ACTIVE\nmisma tabla]
SCAN --> TT[to_target por pata]
TT --> OUT[Salida HBP / OpenBridge]
SRC -->|no| DROP[sin reenvío]
El monitor lee el mismo dict vía pickle BRIDGE_SND.
Servidor nuevo: Subscription + SubscriptionStore
En adn-server 2.x, el modelo de dominio sustituye filas ad hoc:
AudioChannel— TG lógica + slot(tgid, slot).Subscription— participación de un system: role, política de activación, state (fase, timer), target_tgid (LC),relay_table_keyopcional (tablas#…).SubscriptionStore— única autoridad de enrutado en runtime (sin mutarBRIDGESen paralelo).
Camino de voz (misma semántica, otros tipos):
RoutingUseCases.dmrd_receivedactualiza el store (crear relay, TG estática, timeout UA — hooks legacy).SubscriptionRouter.relay_tables_with_active_source— tablas donde el system de ingreso tiene subscription ACTIVE para slot/TG.SubscriptionRouter.resolve— devuelveForwardLeg(system, slot, tgid) del resto de subscriptions ACTIVE en esas tablas.- Mixins de forward envían paquetes (paridad
to_target).
flowchart TB
IN[Ingreso DMRD / DMRE] --> RU[RoutingUseCases.dmrd_received]
RU --> SS[(SubscriptionStore)]
RU --> SR[SubscriptionRouter]
SS --> SR
SR --> LEGS[Lista ForwardLeg]
LEGS --> FWD[Mixins HBP / OBP]
SS --> EXP[RoutingTableLegacyView\nsolo export]
EXP --> MON[Pickle BRIDGE_SND\no JSON routing_table]
Importante: routing_table_for_report() / BRIDGE_SND es un export unidireccional para paneles (RoutingTableLegacyView). No decide reenvíos. Evita el patrón legacy de mutar un dict global compartido con informes.
Mapeo fila → subscription
Fila legacy BRIDGES |
Subscription de dominio |
|---|---|
Clave tabla ("52090", "#310") |
relay_table_key + TG del canal |
SYSTEM |
system (SystemId) |
TS + contexto TG |
channel.slot / channel.tgid |
TGID (bytes) |
target_tgid (reescritura LC) |
ACTIVE |
state.phase (ACTIVE / IDLE) |
TIMER |
state.timer_expires_at |
TIMEOUT |
timeout_seconds |
TO_TYPE (ON, OFF, STAT, NONE) |
role + policy |
ON / OFF / RESET |
triggers (InbandTriggers) |
Helpers import/export: routing_table_import.py, routing_table_export.py (espejo de bridges_export legacy).
Comparación extremo a extremo (un frame de voz)
sequenceDiagram
participant Radio
participant Server
participant Peer as Otro system
Radio->>Server: Voz grupo TG 52090 TS2
rect rgb(40,40,50)
note right of Server: Legacy
Server->>Server: ¿Fila origen ACTIVE en BRIDGES["52090"]?
Server->>Server: foreach pata ACTIVE en tabla
Server->>Peer: to_target (reescribe LC)
end
rect rgb(30,50,40)
note right of Server: Servidor nuevo (mismas reglas)
Server->>Server: SubscriptionRouter.resolve()
Server->>Peer: forward ForwardLeg(s)
end
Lo que no cambió
- Claves de bridge (
52090,#reflector, …) y tablas multi-pata. - Guardia de fila origen — reenviar solo desde una tabla donde este system es origen ACTIVE para ese contexto TG/slot.
- UA dinámico, OPTIONS estáticas,
GEN_STAT_BRIDGES, TG 4000, bootstrap echo 9990. - Pasadas de timer (
rule_timer,bridgeDebug, …) — misma tabla lógica, implementada sobre el store en 2.x.
Ejemplo concreto
Bridge (concepto de red): “la TG 52090 une estos systems y reenvía voz entre ellos”.
Subscription (solo en código 2.x): una pata de esa tabla — p. ej. “MASTER-A en TG 52090, slot 2, ACTIVE, con su LC y timer”.
No es bridge o subscription: en 2.x un bridge es un conjunto de subscriptions sobre el mismo canal (TG + slot).
Escenario
Alguien habla en TG 52090 y deben oírlo MASTER-A, MASTER-B y una pata OpenBridge. En el panel y en el aire eso es un bridge (tabla de la TG 52090).
Legacy (adn-dmr-server) — todo en un dict global:
BRIDGES["52090"] = [
{ SYSTEM: "MASTER-A", TS: 2, ACTIVE: True, TGID: …, TIMER: … },
{ SYSTEM: "MASTER-B", TS: 2, ACTIVE: True, TGID: … },
{ SYSTEM: "OBP-UK", TS: 1, ACTIVE: False, TGID: … },
]
Cuando llega un frame de voz:
- Buscar la fila donde este system es origen ACTIVE (mismo TG/slot).
- Recorrer las demás filas ACTIVE de la misma tabla.
- Por cada una,
to_target→ reenvío con LC reescrito.
El monitor lee el mismo dict (pickle BRIDGE_SND).
Servidor nuevo (adn-server 2.x) — misma tabla, otro formato interno:
SubscriptionStore — TG 52090 / slot 2:
- subscription MASTER-A (ACTIVE, target_tgid, timer…)
- subscription MASTER-B (ACTIVE, …)
- subscription OBP-UK (IDLE, …)
Cuando llega voz, SubscriptionRouter.resolve() aplica las mismas reglas (origen ACTIVE, resto de patas ACTIVE) y devuelve ForwardLeg para reenviar. El monitor recibe una vista exportada (BRIDGE_SND o JSON routing_table); esa exportación no decide el reenvío.
Dónde leer código
| Tema | Legacy | Nuevo |
|---|---|---|
| Ingreso voz | adn-dmr-server/bridge_master.py (routerHBP.dmrd_received) |
application/routing_use_cases.py |
| Reenvío a pata | to_target |
application/routing/hbp_forward.py, obp_forward.py |
| Estado tabla | BRIDGES global |
application/subscription/ (store, router, ops) |
| Export monitor | send_routing_table / pickle |
routing_table_legacy_view.py, informe v2 routing_table |
Ver también: Bridges y talkgroups, Arquitectura, Rendimiento (2.x), Protocolo de informes v2.