Datos a transmitir

Interacción del dispositivo

Especificación de cada dato

  • Device ID: bytes en hexadecimal de la MAC (WiFi) del ESP32 sin los dos puntos. Abreviado como {DID}. Los caracteres deben representarse en minúsculas. En otras palabras debe poder pasar la expresión regular:
    ^([0-9a-f]{2}){6}$
  • time: el ESP32 carece de un reloj de tiempo real pero puede leer periodicamente el topic datakeeper/heartbeat y mantener la hora internamente con cierto margen de error. Devuelve el epoch UNIX en una variable de tipo time_t.

  • battmV: tensión de la batería de alimentación del dispositivo en miliVolts, para evitar punto decimal.

  • Datos de los sensores de oximetría basados en lo especificado por el MAX32664A. Se definen en el topic reads/{DID}.

  • Temperatura corporal: Se definen en el topic reads/{DID}.

Cada tipo se describe en un encabezado en la biblioteca mosimpaqt.

La idea es usar CBOR... pero podemos decidir usar JSON puro. El esquema aquí presentado es para dar una idea de como se deben formatear los datos y en lo posible documentar todos los datos posibles.

Topic devices/info

Diagrama de flujo enviar datos

Los dispositivos deben escribir a este topic cuando se inician y brindar información básica sobre los mismos.

También deberán escribir esta información periódicamente, no menos de una vez por minuto.

{
  "WiFiMAC" : "aabbccddeeff",
  "hw_ver" : "<string>",
  "firm_ver" : "x.y.z",
  "battmV" : uint16_t,
  "time" : time_t,
  "msgId": uint32_t
}
  • WifiMAC: Requerido. Idem Device ID.
  • hw_ver: Opcional. String libre pero sin espacios. Ver si es realmente posible hacer esto (porque el firmware debería poder detectarlo).
  • firm_ver: Opcional. Versión de firmware siguiendo Semantic versioning 2.0.
  • battmV: Requerido. Nivel de tensión de la batería en mili Volts.
  • msgId: Requerido. Contador de 32 bits sin signo. El primer mensaje es 0 y se incrementa en 1 cada vez que se transmite este mensaje. Debe reiniciarse cada vez que el equipo arranca. Se utiliza para detectar si un equipo se reinició.

Topic reads/{DID}

El dispositivo envía datos de los sensores que posee.

Diagrama de flujo enviar datos

{
  "spo2" : [
    {
      "time" : time_t,
      "SpO2" : uint16_t,
      "trust" : boolean
    }
  ],
  "bloodP" : [
    {
      "time" : time_t,
      "sys" : uint8_t,
      "dia" : uint8_t,
      "trust" : boolean
    }
  ],
  "heartR" : [
    {
      "time" : time_t,
      "heartR": uint16_t,
      "trust" : boolean
    }
  ],
  "bodyT" : [
    {
      "time" : time_t,
      "trust": boolean
    }
  ]
}

Donde:

  • SpO2: entero de 16 bits sin signo, donde el LSB representa 0.1%.
  • sys: entero de 8 bits sin signo, donde el LSB representa 1 mmHg.
  • dia: entero de 8 bits sin signo, donde el LSB representa 1 mmHg.
  • heartR: entero de 16 bits sin signo, donde el LSB representa 0.1 latidos por minuto.
  • temp: entero de 16 bits sin signo, donde el LSB representa 0.1 ºC.
  • conf: confidencia en el dato. Booleano que vale true si el valor es confiable, false en caso contrario.

Interacción con datakeeper

El sistema cuenta con la aplicación datakeeper que maneja la base de datos del mismo. Los monitores (aplicaciones que muestran los datos) pueden recurrir a la misma para obtener datos de pacientes, camas y valores históricos.

Topic datakeeper/heartbeat

datakeeper publicará un mensaje aproximadamente una vez por segundo con el siguiente payload:

{
  "time" : time_t
}

El propósito de este topic es dual:

Topic datakeeper/query y monitor/{MAC}

Los mensajes de query se dirigen al topic "datakeeper/query" y tienen la forma:

{
  "mac" : "aabbccddeeff",
  "command" : "<command>",
  "id" : "<string up to 32 characters>",
  <another parameters according to the command>
}

Donde:

  • "command" puede ser alguno de: internments, spo2, heartR, bloodP, bodyT, set_alarms y alarms_thresholds.
  • "id" string que asigna el monitor de hasta 32 caracteres. Para uso interno del monitor (por ejemplo identificar cada query).

Las respuestas se obtienen en el topic "monitor/{mac}"

Comando internments

El comando internments le pide a datakeeper una lista de las internaciones activas.

La respuesta tiene la siguiente forma:

{
  "id" : <string up to 32 characters>,
  "internments":
  [
    {
      "internment_id" : <int32_t>,
      "from_date" : <int64_t>,
      "device" : "<mac>",
      "location" :
      {
        "desc" : "<string UTF-8>",
        "type" : "<string UTF-8>"
      },
      "patient" :
      {
        "name" : "<string UTF-8>",
        "surname" : "<string UTF-8>",
        "age" : <uint8_t>,
        "gender" "<char>"
      }
    }
  ]
}

donde:

  • id: la misma string que usó el monitor para identificar la query.
  • internment_id: número de internación.
  • from_date: fecha y hora del inicio de la internación expresados en segundos a partir del epoch Unix.
  • device: dirección MAC del dispositivo asociado.
  • location: objeto con valores de la ubicación.
  • desc: descripción de la ubicación del paciente.
  • type: tipo de ubicación. "local" para internaciones locales y "external" para ubicaciones externas (domiciliarias, por ejemplo).

  • patient: objeto con los valores del paciente.

  • name: Nombres del paciente.
  • surname: Apellidos del paciente.
  • age: edad del paciente.
  • gender: sexo del paciente. Puede ser 'm', 'f' u 'o'.

Diagrama de flujo general para query a datakeeper

Comando spo2

Este comando sirve para obtener valores históricos de spo2 para una internación en particular.

La query completa queda de la forma:

{
  "mac" : "aabbccddeeff",
  "command" : "spo2",
  "id" : "<string up to 32 characters>",
  "internment_id" : <int32_t>,
  "from_time" : <time_t>,
  "num_of_sam" : <int32_t>
}
  • internment_id: algún valor de internment_id devuelto por el comando internments.
  • from_time: tiempo en segundos desde el epoch Unix desde el cual se requieren datos. Debe ser mayor o igual a cero (>= 0).
  • num_of_sam: devuelve las últimas num_of_sam entradas (limita el número total). Debe ser >= 0, con 0 indicando sin límite.

La respuesta tiene la siguiente forma:

{
  "id" : "<string up to 32 characters>",
  "internment_id" : <int32_t>,
  "spo2" : [
    {
      "time" : time_t,
      "SpO2" : uint16_t,
      "R" : uint16_t
    }
  ]
}

Comando heartR

Este comando sirve para obtener valores históricos de heart rate para una internación en particular.

La query completa queda de la forma:

{
  "mac" : "aabbccddeeff",
  "command" : "heartR",
  "id" : "<string up to 32 characters>",
  "internment_id" : <int32_t>,
  "from_time" : <time_t>,
  "num_of_sam" : <int32_t>
}
  • internment_id: algún valor de internment_id devuelto por el comando internments.
  • from_time: tiempo en segundos desde el epoch Unix desde el cual se requieren datos. Debe ser mayor o igual a cero (>= 0).
  • num_of_sam: devuelve las últimas num_of_sam entradas (limita el número total). Debe ser >= 0, con 0 indicando sin límite.

La respuesta tiene la siguiente forma:

{
  "id" : "<string up to 32 characters>",
  "internment_id" : <int32_t>,
  "heartR" : [
    {
      "time" : time_t,
      "heartR": uint16_t
    }
  ]
}

Comando bloodP

Este comando sirve para obtener valores históricos de presión sanguínea para una internación en particular.

La query completa queda de la forma:

{
  "mac" : "aabbccddeeff",
  "command" : "bloodP",
  "id" : "<string up to 32 characters>",
  "internment_id" : <int32_t>,
  "from_time" : <time_t>,
  "num_of_sam" : <int32_t>
}
  • internment_id: algún valor de internment_id devuelto por el comando internments.
  • from_time: tiempo en segundos desde el epoch Unix desde el cual se requieren datos. Debe ser mayor o igual a cero (>= 0).
  • num_of_sam: devuelve las últimas num_of_sam entradas (limita el número total). Debe ser >= 0, con 0 indicando sin límite.

La respuesta tiene la siguiente forma:

{
  "id" : "<string up to 32 characters>",
  "internment_id" : <int32_t>,
  "bloodP" : [
    {
      "time" : time_t,
      "sys" : uint8_t,
      "dia" : uint8_t
    }
  ]
}

Comando bodyT

Este comando sirve para obtener valores históricos de temperatura corporal para una internación en particular.

La query completa queda de la forma:

{
  "mac" : "aabbccddeeff",
  "command" : "bodyT",
  "id" : "<string up to 32 characters>",
  "internment_id" : <int32_t>,
  "from_time" : <time_t>,
  "num_of_sam" : <int32_t>
}
  • internment_id: algún valor de internment_id devuelto por el comando internments.
  • from_time: tiempo en segundos desde el epoch Unix desde el cual se requieren datos. Debe ser mayor o igual a cero (>= 0).
  • num_of_sam: devuelve las últimas num_of_sam entradas (limita el número total). Debe ser >= 0, con 0 indicando sin límite.

La respuesta tiene la siguiente forma:

{
  "id" : "<string up to 32 characters>",
  "internment_id" : <int32_t>,
  "bodyTP" : [
    {
      "time" : time_t,
      "temp": int16_t
    }
  ]
}

Comando alarms_thresholds

Se utiliza para obtener los valores actuales de disparo de alarmas.

La query completa queda de la forma:

{
  "mac" : "aabbccddeeff",
  "command" : "alarms_thresholds",
  "id" : "<string up to 32 characters>",
  "internment_id" : <int32_t>
}

La respuesta tiene la forma:

{
  "id" : "<string up to 32 characters>",
  "internment_id" : <int32_t>,
  "alarms_thresholds" : {
    "last_update": "<string with ISO 8601 format>",
    "spo2_lt" : <uint16_t>,
    "spo2_delay_s" : <uint8_t>,
    "hr_lt" : <uint16_t>,
    "hr_gt" : <uint16_t>,
    "hr_delay_s" : <uint8_t>,
    "bt_lt" : <int16_t>,
    "bt_gt" : <int16_t>,
    "bt_delay_s" : <uint8_t>,
    "bp_sys_lt" : <uint8_t>,
    "bp_sys_gt" : <uint8_t>,
    "bp_delay_s" : <uint8_t>
  }
}

Donde:

  • last_update: último momento en que se cambió la alarma. Se deberá utilizar al cambiar los mismos para validar que no hubo un cambio intermedio.
  • spo2_alarm_lt: valor de activación de la alarma de SpO2 cuando la lectura actual es menor o igual a éste.
  • spo2_delay_s: retraso de disparo de alarma en segundos.
  • hr_lt: valor de activación de la alarma de ritmo cardíaco cuando la lectura actual es menor o igual a este valor.
  • hr_gt: valor de activación de la alarma de ritmo cardíaco cuando la lectura actual es mayor o igual a este valor.
  • hr_delay_s: retraso de disparo de alarma en segundos.
  • bt_lt: valor de activación de la alarma de temperatura corporal cuando la lectura actual es menor o igual a este valor.
  • bt_gt: valor de activación de la alarma de temperatura corporal cuando la lectura actual es mayor o igual a este valor.
  • bt_delay_s: retraso de disparo de alarma en segundos.
  • bp_sys_lt: valor de activación de la alarma de presión sanguínea sistólica cuando la lectura actual es menor o igual a este valor.
  • bp_sys_gt: valor de activación de la alarma de presión sanguínea sistólica cuando la lectura actual es mayor o igual a este valor.
  • bp_delay_s: retraso de disparo de alarma en segundos.

Comando set_alarms

Se utiliza para establecer nuevos valores de disparo de las alarmas.

{
  "mac" : "aabbccddeeff",
  "command" : "set_alarms",
  "id" : "<string up to 32 characters>",
  "internment_id" : <int32_t>,
  "alarms" :
    {
      "last_update" ; "<string with ISO 8601 format>",
      "spo2_lt" : <uint16_t>,
      "spo2_delay_s" : <uint8_t>,
      "hr_lt" : <uint16_t>,
      "hr_gt" : <uint16_t>,
      "hr_delay_s" : <uint8_t>,
      "bt_lt" : <int16_t>,
      "bt_gt" : <int16_t>,
      "bt_delay_s" : <uint8_t>,
      "bp_sys_lt" : <uint8_t>,
      "bp_sys_gt" : <uint8_t>,
      "bp_delay_s" : <uint8_t>
    }
}

-- last_update: debe ser el valor obtenido del comando alarms. El servidor lo utiliza para establecer si hubo o no cambios entre los valores obtenidos previamente por el monitor. En caso de que hayan cambiado el servidor rechazará los cambios propuestos en este mensaje. El monitor deberá volver a pedir los valores actuales.

La respuesta tiene la siguiente forma:

{
  "id" : "<string up to 32 characters>",
  "internment_id" : <int32_t>,
  "command": "set_alarms",
  "result" : "<result>"
}

Donde result puede ser "OK", "UPDATE" o "ERROR". UPDATE indica que los valores fueron cambiados desde la última consulta.

Comando alarms_ranges

Si bien estos valores se mantendrían relativamente estables sería aconsejable refrescarlos periódicamente, por ejemplo con cada refresco de internaciones.

El mismo se pide con un mensaje:

{
  "mac" : "aabbccddeeff",
  "id" : "<string up to 32 characters>",
  "command" : "alarms_ranges"
}

Notar que éstos valores se aplican a todas las internaciones, ya que son rangos.

La respuesta tiene la forma:

{
  "id" : "<string up to 32 characters>",
  "command" : "alarms_ranges",
  "spo2_eq_or_less_perc" : "Vi,Vs",
  "spo2_delay_s" : "db,dt",
  "heart_rate_eq_or_less_bpm" : "Vi,Vs",
  "heart_rate_eq_or_more_bpm" : "Vi,Vs",
  "heart_rate_delay_s" : "db,dt",
  "body_temperature_eq_or_less_cels" : "Vi,Vs",
  "body_temperature_eq_or_more_cels" : "Vi,Vs",
  "body_temperature_delay_s" : "db,dt",
  "blood_pressure_sys_eq_or_less" : "Vi,Vs",
  "blood_pressure_sys_eq_or_more" : "Vi,Vs"
  "blood_pressure_delay_s" : "db,dt"
}

Where:

  • Vi Valor inferior del rango.
  • Vs Valor superior del rango.
  • spo2_eq_or_less_perc Rango de valores de alarma de SpO2 en %.
  • spo2_delay_s Rango de retraso del disparo de la alarma en segundos.
  • heart_rate_eq_or_less_bpm Rango de valores de alarma de ritmo cardíaco bajo en latidos por minuto.
  • heart_rate_eq_or_more_bpm Rango de valores de alarma de ritmo cardíaco alto en latidos por minuto.
  • heart_rate_delay_s Rango de retraso del disparo de la alarma en segundos.
  • body_temperature_eq_or_less_cels Rango de valores de alarma de temperatura corporal baja en grados Celcius.
  • body_temperature_eq_or_more_cels Rango de valores de alarma de temperatura corporal alta en grados Celcius.
  • body_temperature_delay_s Rango de retraso del disparo de la alarma en segundos.
  • blood_pressure_sys_eq_or_less Rango de valores de alarma de presión sanguínea sistólica baja.
  • blood_pressure_sys_eq_or_more Rango de valores de alarma de presión sanguínea sistólica alta.
  • blood_pressure_delay_s Rango de retraso del disparo de la alarma en segundos.

Comando gen_report

Se utiliza para pedirle a datakeeper que genere un reporte de los datos de una internación.

El mismo se pide con un mensaje:

{
  "mac" : "aabbccddeeff",
  "id" : "<string up to 32 characters>",
  "command" : "gen_report",
  "internment_id": <int32_t>,
  "from_time" : <time_t>,
  "to_time" : <time_t>
}
  • from_time: tiempo en segundos desde el epoch Unix desde el cual se requieren datos. Debe ser mayor o igual a cero (>= 0).
  • to_time: tiempo en segundos desde el epoch Unix hasta el cual se requieren datos. Debe ser mayor o igual a from_time + 3600.

El algoritmo dividirá el rango temporal en espacios de 3600 segundos empezando por from_time. El tiempo restante será obviado. Ejemplo: si from_time = 1595017346 y to_time = 1595028669 solo se devolverá un reporte de las primeras 3 horas.

Respuesta: en caso de poder haber generado el reporte se devuelve

{
  "id" : "<string up to 32 characters>",
  "command" : "gen_report",
  "url" : "<URL string>"
}

El PDF será servido en la URL devuelta.

En caso de error durante la generación del reporte se recibirá:

{
  "id" : "<string up to 32 characters>",
  "internment_id" : <int32_t>,
  "command" : "gen_report",
  "result" : "ERROR"
}

Topic datakeeper/signals

En este tópico datakeeper anuncia información sobre cambios y eventos.

La forma del mensaje es:

{
  "signal" : "<signal>"[,]
  [extra data]
}

La señales disponibles son:

  • internments_changed La tabla internments fué modificada. No lleva parámetros extra.
  • alarms_thresholds_updated: indica que algún valor de disparo de alarma ha sido modificado.

Diagrama de flujo de señal

Señal alarms_thresholds_updated

La forma del mensaje es:

{
  "signal" : "alarms_thresholds_updated",
  "internment_id" : <int32_t>
}

Topic datakeeper/alarms

En este tópico datakeeper anuncia alarmas. El formato básico del mensaje es:

{
  "<type>": [<data according to alarm type>]
}

Diagrama de flujo de alarmas

Alarmas de internaciones

Anuncia alarmas disparadas por los valores de los sensores de cada internación.

{
  "internments" : [
    {
      "id" : <int32_t>,
      "<value 1>" : "<severity>",
      "<value 2>" : "<severity>", // Optional, might be more than one.
    }
  ]
}
  • id: internment_id, se abrevia porque queda claro que es de una internación.
  • value n: determina que tipo de sensor disparó la alarma. Puede ser spo2, heart_rate, body_temp o blood_pressure.
  • severity: string que representa la severidad de la alarma. Por el momento el sistema es binario (no hay escala de severidad de alarmas) por lo que solo se usará "critic" para alarma disparada, dejando lugar a agregar mas severidades de ser necesario. Los monitores por el momento deberán solo chequear que el valor está definido, permitiendo compatibilidad futura con versiones que si provean severidades.

Si una internación activa no se encuentra en la lista implica que los valores son normales. Es responsabilidad del monitor limpiar las alarmas que previamente pudiesen haber estado activas.

Alarmas de estado de dispositivos

{
  "devices" : [
    {
      "mac":"<DID>",
      "last_seen":"<timestamp with timezone, ISO8601>",
      "battmV":<uint16_t>,
      "device_missing":"<level>",
      "battery_low":"<level>",
    }
  ]
}
  • device_missing: puede tener los estados:
    • amarillo: visto hace 20 a 40 segundos (20 >= t < 40).
    • naranja: visto hace 40 a 60 segundos (40 >= t < 60).
    • rojo: visto hace mas de 60 segundos (t >= 60).
  • battery_low: niveles no seleccionados, ver https://gitlab.com/mosimpa/documentation/-/issues/103
  • last_seen última vez visto, timestamp con timezone según ISO8601. Por ejemplo: "2020-07-11 11:40:29-03"

Los niveles de la batería aún no han sido definidos.


Otros mensajes

Topic errors/{DID}

Los dispositivos tienen la posibilidad de enviar datos a este tópico si encuentran algún error. Naturalmente esto solo puede ocurrir si se logró establecer la conectividad dispositivo ←→ broker MQTT.

El formato básico del mensaje es:

{
  "file":"<filename>",
  "func":"<function name>",
  "line":<line number>,
  "retval":<returned value>
}

Donde:

  • file: archivo en donde se dá el error.
  • func: función en donde se dá el error.
  • line: linea en donde se dá el error.
  • retval: valor de error, si corresponde.