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/es/server/development/bridges-vs-subscriptions.md

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 bloque BRIDGES en 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):

  1. Obtener clave de bridge desde la TG destino (y tablas #… cuando aplica).
  2. Crear tabla dinámica si no existe (UA / STAT / OPTIONS estáticas — mismos disparadores que legacy).
  3. Buscar fila origen ACTIVE que coincida con system + slot + TGID actuales.
  4. 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_key opcional (tablas #…).
  • SubscriptionStore — única autoridad de enrutado en runtime (sin mutar BRIDGES en paralelo).

Camino de voz (misma semántica, otros tipos):

  1. RoutingUseCases.dmrd_received actualiza el store (crear relay, TG estática, timeout UA — hooks legacy).
  2. SubscriptionRouter.relay_tables_with_active_source — tablas donde el system de ingreso tiene subscription ACTIVE para slot/TG.
  3. SubscriptionRouter.resolve — devuelve ForwardLeg (system, slot, tgid) del resto de subscriptions ACTIVE en esas tablas.
  4. 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:

  1. Buscar la fila donde este system es origen ACTIVE (mismo TG/slot).
  2. Recorrer las demás filas ACTIVE de la misma tabla.
  3. 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.

Powered by TurnKey Linux.