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

209 lines
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**:
```text
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).
```mermaid
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`).
```mermaid
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)
```mermaid
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:
```text
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:
```text
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](../user-guide/bridges-and-talkgroups.md), [Arquitectura](architecture.md), [Rendimiento (2.x)](performance.md), [Protocolo de informes v2](../protocols/report-v2.md#routing_table).

Powered by TurnKey Linux.