Ir al contenido

Device RPC y comandos

La telemetry fluye dispositivo→servidor. La RPC fluye en el otro sentido: la plataforma envía un comando a un dispositivo — fijar la velocidad de un ventilador, leer de vuelta una config, reiniciar, iniciar una actualización de firmware. Esta página cubre los tres tipos de comando, el ciclo de vida de estados por el que pasa cada comando, cómo se correlacionan las respuestas, qué ocurre cuando el dispositivo está offline, y las reglas de autorización y auditoría en torno a los comandos.

Tipo¿Espera un resultado?¿Sobrevive a un dispositivo offline?
Un sentidoNo — disparar y olvidarNo (entregado solo si está online)
Dos sentidosSí — devuelve la respuesta del dispositivoNo (expira si está offline)
PersistenteOpcionalmente — almacenado y entregado al reconectar, reintentado
  • Los comandos de un sentido se despachan y se olvidan. Úsalos para actuaciones idempotentes donde no necesitas respuesta: setFan, setLed.
  • Los comandos de dos sentidos llevan un timeout_ms y devuelven la respuesta del dispositivo en línea. Úsalos para leer de vuelta el estado: getConfig, getStatus.
  • Los comandos persistentes se almacenan de forma duradera y se entregan cuando el dispositivo se conecta a continuación, reintentándose hasta que tienen éxito, se cancelan o expiran. Úsalos para acciones que deben ocurrir en flotas conectadas de forma intermitente — incluyendo OTA, que viaja sobre RPC persistente.

Cada comando es un registro de RPC que pasa por un conjunto definido de estados:

stateDiagram-v2
  [*] --> queued : RPC creada
  queued --> sent : el adapter publica al dispositivo
  sent --> delivered : el dispositivo confirma recepción
  delivered --> successful : el dispositivo devuelve resultado
  sent --> successful : un sentido / resultado rápido

  queued --> timeout : no persistente, dispositivo offline
  sent --> timeout : sin respuesta antes de timeout_ms
  delivered --> failed : el dispositivo reporta un error

  queued --> expired : persistente, pasó expiration_time
  sent --> queued : reintento persistente (sin ack)

  queued --> cancelled : el operador cancela
  sent --> cancelled : el operador cancela

  successful --> [*]
  timeout --> [*]
  failed --> [*]
  expired --> [*]
  cancelled --> [*]

El conjunto completo de estados: queued, sent, delivered, successful, timeout, expired, failed, cancelled, deleted.

  • queued — registro creado, aún no en la red.
  • sent — el protocol adapter publicó el comando en el topic/canal de comandos del dispositivo.
  • delivered — el dispositivo confirmó la recepción.
  • successful — el dispositivo devolvió un resultado; aterriza en response_json.
  • timeout — sin respuesta dentro de timeout_ms (o un comando no persistente para un dispositivo offline).
  • failed — el dispositivo ejecutó pero reportó un error (error_code se establece).
  • expired — el expiration_time de un comando persistente pasó antes de la entrega.
  • cancelled — un operador lo canceló antes de completarse.

Cada comando lleva un correlation_id. El protocol adapter lo estampa en el mensaje saliente; el dispositivo lo refleja en su respuesta; el adapter empareja la respuesta de vuelta con el registro de RPC originario por ese id. Esto es lo que permite a un dispositivo —o a toda una flota sobre un topic de respuesta MQTT compartido— responder de forma asíncrona y que cada respuesta se enrute al comando correcto.

Una respuesta cuyo correlation_id no coincide con ninguna RPC activa se ignora (o se almacena como un diagnóstico huérfano, según la config del adapter), y corem_rpc_orphan_responses_total{protocol="…"} se incrementa.

Lo que le ocurre a un comando para un dispositivo offline depende enteramente de si es persistente.

Ningún adapter puede entregar el comando. Tras transcurrir timeout_ms la petición devuelve DEADLINE_EXCEEDED / HTTP 504 y el registro de RPC pasa a timeout.

sequenceDiagram
  participant Op as Operador
  participant Core as CORE-M
  participant Dev as Dispositivo (offline)
  Op->>Core: SendDeviceRpc (no persistente)
  Core-->>Core: status=queued, dispositivo offline
  Note over Core: transcurre timeout_ms
  Core->>Op: DEADLINE_EXCEEDED / 504 (status=timeout)
  Dev--xCore: nunca alcanzado

El expiration_time de un comando persistente es un plazo estricto. Una vez que pasa, el planificador de RPC marca el registro como expired, no se entrega ningún comando aunque el dispositivo se reconecte más tarde, y se publica el evento rpc.expired.{tenant}.{rpc_id}. Establece expiration_time al máximo tiempo durante el cual aún querrías que la acción ocurriese — un reinicio que pusiste en cola hace días probablemente ya no se desea.

  • Permiso. La RPC requiere el permiso rpc. Un miembro sin él es rechazado con PERMISSION_DENIED y no se crea ningún registro.
  • Scope del token. Un token de API con scope debe llevar rpc:execute. Un token con scope, por ejemplo, telemetry:read que llame a RPC es rechazado con PERMISSION_DENIED y rpc.denied registra reason="missing_scope".
  • Auditoría. Crear una RPC escribe el evento de auditoría rpc.created con el actor, el dispositivo, el método, la bandera de persistencia y un hash de los params. Si el método está marcado como sensible, los params en bruto se redactan — solo se conserva el hash.

Ejemplo resuelto: un comando de dos sentidos

Sección titulada «Ejemplo resuelto: un comando de dos sentidos»

Lee de vuelta la config de un dispositivo con una RPC de dos sentidos. Envías el comando y obtienes un correlation_id; luego recuperas la respuesta emparejada.

  1. Envía el comando. Llama al endpoint de RPC del dispositivo con el método, los params JSON y un timeout. Necesitas el scope rpc:execute (token) o el permiso rpc (usuario).

    Ventana de terminal
    curl -X POST https://api.kronoxdata.com/api/v1/devices/dev_8f3a/rpc \
    -H "Authorization: Bearer <token-with-rpc:execute>" \
    -H "Content-Type: application/json" \
    -d '{
    "method": "getConfig",
    "params_json": "{}",
    "timeout_ms": 10000
    }'
  2. Recibe el correlation id. La llamada devuelve de inmediato con el id que usas para recuperar el resultado.

    { "correlation_id": "c-7f3a91" }
  3. El dispositivo responde. El adapter entrega getConfig al dispositivo, el dispositivo responde {"report_interval":30} estampado con c-7f3a91, y el adapter lo empareja con el registro de RPC. El registro transiciona queued → sent → delivered → successful y la respuesta aterriza en response_json.

  4. Obtén el resultado. Sondea por correlation id.

    Ventana de terminal
    curl https://api.kronoxdata.com/api/v1/devices/rpc/c-7f3a91 \
    -H "Authorization: Bearer <token-with-rpc:execute>"
    {
    "found": true,
    "payload_json": "{\"report_interval\":30}"
    }

El registro de RPC es agnóstico al transporte; el protocol adapter gestiona la entrega real y la correlación de la respuesta.

ProtocoloComando salienteCorrelación de respuesta
MQTTPublicado en el topic de comandos configurado del dispositivoEl dispositivo publica en un topic de respuesta; el adapter empareja por correlation_id
HTTPEl dispositivo obtiene/hace long-poll; o el dispositivo envía un uplink de kind="rpc_response"El uplink lleva el correlation_id
CoAP / LwM2MEntregado sobre el canal CoAP del dispositivoRespuesta emparejada por correlation_id / id de petición del protocolo
GatewayRetransmitido a un dispositivo hijo vía su gatewayEl gateway retransmite la respuesta del hijo con correlation_id