Ir al contenido

Gateway y dispositivos hijos

Un gateway es un dispositivo que se conecta a CORE-M en nombre de otros dispositivos que no pueden alcanzar la plataforma por sí mismos. El caso clásico es un bus industrial: una docena de sensores Modbus RTU cuelgan de un único segmento RS-485, y un gateway de borde habla Modbus con ellos y CORE-M con la nube. El gateway se autentica una vez y hace fan-in de la telemetry de todos sus hijos, cada uno de los cuales aparece en CORE-M como su propio dispositivo de primera clase.

Los hijos detrás de un gateway no tienen credencial directa ni ruta directa a CORE-M. El gateway proporciona ambas: es el uplink autenticado, y CORE-M mapea el nombre local de cada hijo a un device_id real para que la telemetry del hijo se almacene, se procese por reglas y se ancle exactamente como si se hubiera conectado directamente.

CORE-M resuelve los hijos con una route key:

{tenant_id}:{gateway_device_id}:{child_identity} → device_id

La route key ata la identidad local de un hijo (un índice de slot, un número de serie o un nombre lógico) a su dispositivo en la plataforma, acotada al gateway y al tenant propietarios.

flowchart LR
    subgraph Field
      C1["Hijo: modbus-17<br/>(sensor de temperatura)"]
      C2["Hijo: modbus-18<br/>(medidor de caudal)"]
      C3["Hijo: modbus-19<br/>(válvula)"]
    end
    C1 -- Modbus RTU --> GW["Gateway G1<br/>(dispositivo autenticado)"]
    C2 -- Modbus RTU --> GW
    C3 -- Modbus RTU --> GW
    GW -- "HTTP / gRPC (API key)" --> CM["CORE-M gateway :8080"]
    CM --> RP[["Redpanda<br/>telemetry.raw.{tenant}"]]
    RP --> D17[("Device D17")]
    RP --> D18[("Device D18")]
    RP --> D19[("Device D19")]
  1. Registra el gateway. Aprovisiona el dispositivo de borde normalmente. Recibe un device_id y una API key; esta es la única credencial que los hijos necesitan.

  2. Conecta cada hijo. El gateway llama a GatewayConnectDevice con un child_name estable. CORE-M crea (o busca) el dispositivo hijo y devuelve su child_device_id. Esto establece la ruta para ese hijo.

  3. Envía telemetry. El gateway llama a GatewaySubmitTelemetry con un lote de puntos, cada uno direccionado por child_name. CORE-M resuelve cada nombre a un dispositivo hijo y publica un punto de telemetry en bruto por hijo.

GatewayConnectDevice es idempotente: llamarlo de nuevo con el mismo child_name devuelve el mismo hijo. Internamente el hijo se almacena con un hardware ID de la forma gw:{gateway_device_id}:{child_name}, que es lo que hace estable la búsqueda.

Ventana de terminal
curl -X POST https://ingest.kronoxdata.com:8080/api/v1/gateway/G1/connect \
-H "Authorization: Bearer sk_live_gateway_key..." \
-H "Content-Type: application/json" \
-d '{
"gateway_device_id": "G1",
"child_name": "modbus-17"
}'
{
"child_device_id": "d17b1c0e-2f44-4a91-9b2e-2c5a1f0e9d17"
}

GatewaySubmitTelemetry hace fan-in de un lote. Cada punto lleva el child_name, un timestamp opcional (el servidor usa ahora si está ausente) y las lecturas numéricas/de texto. CORE-M resuelve cada nombre y publica un punto en bruto por hijo, atribuido al device_id del hijo para almacenamiento, reglas y anchoring.

Ventana de terminal
curl -X POST https://ingest.kronoxdata.com:8080/api/v1/gateway/G1/telemetry \
-H "Authorization: Bearer sk_live_gateway_key..." \
-H "Content-Type: application/json" \
-d '{
"gateway_device_id": "G1",
"points": [
{
"child_name": "modbus-17",
"timestamp": "2026-05-29T14:03:00Z",
"numeric_values": { "temperature": 41.2 }
},
{
"child_name": "modbus-18",
"numeric_values": { "flow_rate": 12.7 }
}
]
}'

La respuesta cuenta los puntos a lo largo del lote, con la misma forma accepted / rejected que la ingesta directa:

{
"accepted": 2,
"rejected": 0
}
sequenceDiagram
    participant GW as Gateway G1
    participant CM as CORE-M gateway :8080
    participant Reg as Registro de dispositivos
    participant RP as Redpanda telemetry.raw.{tenant}

    GW->>CM: POST /api/v1/gateway/G1/telemetry (lote por child_name)
    CM->>CM: Verificar device_id del invocador == G1
    loop cada punto
        CM->>Reg: Resolver {tenant}:G1:child_name → device_id
        alt la ruta existe (o auto_create)
            CM->>RP: Publicar punto como device_id del hijo
        else hijo desconocido, auto_create=false
            CM->>CM: Rechazar (corem_gateway_child_rejections_total +1)
        end
    end
    CM-->>GW: { accepted, rejected }

Lo que ocurre cuando llega telemetry para un child_name que aún no tiene ruta depende de la bandera auto_create:

  • auto_create = true — CORE-M crea el dispositivo hijo al vuelo a partir de la primera telemetry, así puedes omitir el paso explícito GatewayConnectDevice.
  • auto_create = false — la telemetry para un hijo desconocido se rechaza, y corem_gateway_child_rejections_total{reason="unknown_child"} se incrementa. Usa esto cuando quieras que los hijos existan solo tras un connect deliberado.

Los hijos de un gateway comparten su suerte en el uplink. Si la política del profile dice que el estado del hijo sigue al del gateway, entonces cuando el gateway pasa a offline, el verificador de desconexión marca también offline a todos los hijos enrutados a través de él, y publica un evento device.status por cada hijo afectado. Esto mantiene el panel honesto — un bus de sensores no aparece como online cuando lo único que puede alcanzarlos se ha caído.

De forma independiente, cada hijo aún cambia a online con su propia primera telemetry, y a offline tras el umbral de desconexión (por defecto 120 segundos) de silencio.

Recurre a un gateway cuando…

  • Los hijos hablan un field bus (Modbus RTU, RS-485, BACnet) y no pueden alcanzar la nube.
  • Quieres una credencial y un uplink para un clúster de dispositivos cercanos.
  • Necesitas que cada hijo siga siendo un dispositivo de primera clase para reglas, gráficos y anchoring.
  • Una caja de borde ya agrega sensores locales y puede reenviar lotes.

Para dispositivos que pueden alcanzar CORE-M directamente, conéctalos con HTTP, MQTT, CoAP o LwM2M en su lugar.