Pular para o conteúdo principal

Payload Parser

Visão Geral

O Payload Parser é um componente essencial na integração de dispositivos IoT, pois sensores ou fontes de dados externas transmitem dados em diversos formatos como binário, hexadecimal, JSON e outros. Sua principal função é decodificar os payloads brutos e convertê-los em um Valeiot Data Point. Isso garante que os dados de qualquer sensor ou componente externo possam ser facilmente interpretados, armazenados e utilizados dentro da plataforma, como ilustrado no diagrama abaixo.

Parser-Diagram

O Payload Parser é um script em JavaScript, que processa os dados recebidos dos sensores e os converte em valores legíveis. O script segue as especificações do protocolo definidas pela documentação do fabricante do hardware, para decodificar corretamente os campos de dados.

Características Principais

  • Pré-processamento: Payload Parsers permitem transformar payloads de entrada arbitrários (ex.: JSON customizado, binário ou formatos específicos de fabricantes) em Valeiot Data Points padronizados antes de serem armazenados.
  • Filtragem: Filtre ou descarte seletivamente campos específicos do payload para evitar que dados indesejados sejam inseridos no dispositivo.
  • Linguagens atualmente suportadas: Javascript

Casos de Uso

  • Decodificação de Dados: O Payload Parser decodifica os payloads brutos transmitidos por sensores ou fontes de dados externas, convertendo dados binários ou hexadecimais em valores significativos como latitude, longitude, voltagem da bateria, velocidade e outras métricas do dispositivo.
  • Normalização de Valeiot Data Point: Ajuste de JSONs personalizados para conformidade com a estrutura do Valeiot Data Point, garantindo inserção bem-sucedida nos Data Sources.
  • Flexibilidade: Capacidade de lidar com diferentes tipos de mensagens de uplink e permitir processamento customizado antes da inserção no Data Source.
  • Tratamento de Erros: Detecção e tratamento de formatos de dados inesperados, garantindo robustez mesmo em casos de dados corrompidos ou incompletos.
  • Personalização: O parser pode ser adaptado para atender a requisitos específicos, como ignorar determinados pontos de dados ou adicionar metadados adicionais aos dados decodificados.

Estrutura do Payload Parser

O Payload Parser deve conter obrigatoriamente uma função de entrada chamada parse, executada sempre que uma requisição for enviada. Essa função recebe como parâmetro o payload, que contém a mensagem da requisição. Ao final, ela deve retornar um array de Valeiot Data Points.

function parse(payload) {
let dataList = [];

// sua lógica vai aqui

return dataList;
}

Validação do Código

Um parser válido consiste em uma ou mais declarações de função, tendo parse como ponto de entrada que o runtime chamará com o payload recebido.

O código do parser deve atender às seguintes regras:

  1. Sem referências a this.
    O código não deve conter a substring this.. O acesso ao objeto global utilizando this não é permitido.

  2. JavaScript válido
    O código deve ser um JavaScript válido. Qualquer erro de parsing (por exemplo, erro de sintaxe) fará com que a validação falhe.

  3. Apenas declarações de função no nível superior
    O escopo de nível superior do script deve conter apenas declarações de função (por exemplo, function parse(payload) { ... }).
    Qualquer outro tipo de instrução no nível superior — como expressões, var, let, const ou atribuições — será rejeitado.

  4. Limite de tamanho do código
    O tamanho máximo permitido para o código do parser é 10240 caracteres.

Funções nativas no runtime

As funções abaixo são injetadas no runtime JavaScript e ficam disponíveis globalmente.
Por razões de segurança, eval é removido (definido como undefined).

base64ToHex(base64String)

Converte uma string codificada em Base64 para sua representação hexadecimal.

  • Argumento: string (codificada em Base64).
  • Retorno: string (hexadecimal em letras minúsculas).
  • Erros:
    • Erro de tipo se o argumento estiver ausente ou não for uma string.
    • Erro de tipo se a string não for um Base64 válido.

hexToBytes(hexStringOrBytes)

Converte uma string hexadecimal em um array de bytes.

  • Argumento: string ou array de bytes (codificado em hex).
  • Retorno: array de bytes.
  • Erros:
    • Erro de tipo se o argumento estiver ausente ou não for string/array de bytes.
    • Erro de tipo se o valor não for hexadecimal válido.

bytesToString(bytes)

Converte um array de bytes em uma string UTF-8.

  • Argumento: array de bytes.
  • Retorno: string (interpretação UTF-8 dos bytes).
  • Erros:
    • Erro de tipo se o argumento estiver ausente ou não for um array de bytes.

toDatapoints(obj, [timestamp])

Constrói um array de datapoints do parser a partir de um objeto chave–valor.

  • Argumentos:

    • obj: objeto — cada chave se torna o nome da variable e cada valor se torna o value de um datapoint.
    • timestamp (opcional): número representando o tempo Unix em segundos. Se fornecido, será utilizado como o time para todos os datapoints gerados.
  • Retorno: array de objetos { time, variable, value }.
    Se timestamp não for informado, será utilizado o horário atual do servidor (RFC3339).

  • Erros:

    • Erro de tipo se o primeiro argumento estiver ausente ou não for um objeto.
    • Erro de tipo se o segundo argumento for informado mas não for um número (Unix seconds).

Esta função é útil quando o payload é um objeto simples (por exemplo: { temperature: 22.5, humidity: 80 }) e você deseja gerar um datapoint para cada chave.

unixToRFC3339(unixSeconds)

Converte tempo Unix em segundos para o formato RFC3339.

  • Argumento: número (tempo Unix em segundos).
  • Retorno: string no formato RFC3339.
  • Erros:
    • Erro de tipo se o argumento estiver ausente ou não for um número.

unixMilliToRFC3339(unixMilliseconds)

Converte tempo Unix em milissegundos para o formato RFC3339.

  • Argumento: número (tempo Unix em milissegundos).
  • Retorno: string no formato RFC3339.
  • Erros:
    • Erro de tipo se o argumento estiver ausente ou não for um número.
Debug de Payload

Se o parser não estiver funcionando como esperado, inspecione o dispositivo utilizando a ferramenta Live Inspector.

Acesse a página do dispositivo, abra a aba Live Inspector e aguarde ou force uma nova requisição de payload. O Live Inspector captura a requisição bruta e o resultado da execução do parser, permitindo analisar o processamento do payload passo a passo e identificar possíveis erros.

Exemplo

Abaixo está um exemplo de um Payload Parser baseado em JavaScript, que decodifica o payload enviado do dispositivo e o converte em Valeiot Data Points:

parserExample.js
function parse(payload) {
let dataList = [];

if (typeof payload === "string") {
const buffer = hexToUint8Array(payload);

if (buffer[0]) {
const batteryValue = buffer[0];
dataList.push({
variable: "battery",
value: batteryValue,
});
}

if (buffer[1]) {
const temperatureValue = buffer[1];
dataList.push({
variable: "temperature",
value: temperatureValue,
});
}

if (buffer[3]) {
const doorBit = buffer[3] & 0x08;
let doorState = "";
if (doorBit > 0x00) {
doorState = "Closed";
} else {
doorState = "Open";
}
dataList.push({
variable: "door_state",
value: doorState,
});
}

dataList = dataList.concat({ variable: "payload", value: payload });
} else {
dataList = dataList.concat(payload);
}

const ignoreVars = ["snr", "rssi", "gateway_eui", "fcnt", "fport"];
dataList = dataList.filter((x) => !ignoreVars.includes(x.variable));

return dataList;
}

O parser espera um payload como entrada, usando o seguinte exemplo de payload:

"621b6310";

O resultado esperado são os data points:

[
{ variable: "battery", value: 98 },
{ variable: "temperature", value: 27 },
{ variable: "position_state", value: "Open" },
{ variable: "payload", value: "621b6310" },
];

Explicação

O processo de decodificação é livre - você pode fazer basicamente o que quiser com JavaScript. Para o exemplo mencionado, o script decodifica a bateria, temperatura e estado da porta a partir do payload. Ele converte de hexadecimal para decimal e aplica uma simples operação de bitmask. Também remove algumas variáveis indesejadas como snr, rssi, gateway_eui, fcnt e fport.

function parse(payload) {
let dataList = [];

if (typeof payload === "string") {
const buffer = hexToUint8Array(payload);

if (buffer[0]) {
const batteryValue = buffer[0];
dataList.push({
variable: "battery",
value: batteryValue,
});
}

if (buffer[1]) {
const temperatureValue = buffer[1];
dataList.push({
variable: "temperature",
value: temperatureValue,
});
}

if (buffer[3]) {
const doorBit = buffer[3] & 0x08;
let doorState = "";
if (doorBit > 0x00) {
doorState = "Closed";
} else {
doorState = "Open";
}
dataList.push({
variable: "door_state",
value: doorState,
});
}

dataList = dataList.concat({ variable: "payload", value: payload });
} else {
dataList = dataList.concat(payload);
}

const ignoreVars = ["snr", "rssi", "gateway_eui", "fcnt", "fport"];
dataList = dataList.filter((x) => !ignoreVars.includes(x.variable));

return dataList;
}

A calculadora abaixo mostra os valores hexadecimal e binário.

Payload-Example

Bateria: 0110 0010 ou 0x62 ou 98

Temperatura: 0001 1011 ou 0x1B ou 27

Estado da Porta: 0001 0000 que é 0x10 quando operado 0x10 & 0x08 o resultado é 0x00

  00010000   (0x10)
& 00001000 (0x08)
--------
00000000 (Result)

Este exemplo demonstra como o Payload Parser pode ser usado para decodificar dados do dispositivo e convertê-los no formato Valeiot Data Point.