Parado ou trabalhando? Uma linha de JEXL separa os dois

Resumo. Um caminhão parado com o motor ligado tanto pode ser combustível desperdiçado quanto trabalho remunerado de tomada de força — a velocidade marca zero nos dois casos. Uma única expressão do IoT Logic lê o bit de status que separa as duas situações. Neste tutorial, um agente monta a regra inteira pela API: lê o schema ao vivo, constrói o grafo, habilita o fluxo, transmite a telemetria — e um canal independente confirma que a regra calculou exatamente o que você esperava. O canvas visual continua sendo o lugar onde o humano revisa, aprova e depura.
Um caminhão fica parado dez minutos com o motor ligado. Desperdício ou trabalho? Talvez o motorista só esteja esquentando a cabine e queimando diesel para nada. Ou o motor está acionando uma tomada de força — girando a bomba de um caminhão-pipa, movendo o guindaste de um caminhão munck — trabalho pelo qual a frota é paga.
A velocidade não separa os dois casos: nas duas situações, o velocímetro marca zero. O consumo de combustível quase não ajuda; o padrão é parecido demais. A diferença está escondida em um único bit de uma palavra de status que o gateway do veículo envia.
Antes de entrar em detalhes, o truque completo é este: uma expressão do IoT Logic lê esse bit e transforma "parado, motor ligado" em dois veredictos opostos — e um stream independente, o mesmo que nunca escreveu a regra, comprova que ela calcula o que você quis dizer.
Um ponto antes de seguir: o IoT Logic tem um editor visual de arrastar e soltar, em que você posiciona nós num canvas e os conecta — conveniente, e um ponto forte real da plataforma.
Mas ele também tem uma superfície programática documentada: um fluxo é um schema tipado que você lê e escreve pela REST, a documentação traz um guia de geração de fluxos com IA, e a plataforma expõe um servidor MCP somente leitura para auditoria. É essa superfície que permite que um agente trabalhe contra um contrato, e não contra uma tela.
Na execução mostrada a seguir, o agente faz o ciclo completo numa conta de teste descartável: lê o schema ao vivo, monta o grafo, habilita o fluxo para o teste, transmite telemetria e o desabilita de novo. O canvas não desaparece — um humano abre o mesmo fluxo e o Data Stream Analyzer para revisar o grafo, aprovar a habilitação em produção, checar a instalação no caminhão e depurar. O agente monta; o humano mantém a trava de habilitação e a verificação.
A resposta está em um bit da palavra de status
Veja o que chega pelo fio — um único número inteiro, uma palavra de status compactada:
can_status_word = 4 # 0b100 — PTO desligado
can_status_word = 5 # 0b101 — PTO ligado (bit 0 em 1)
PTO é a sigla de power take-off, a tomada de força que aciona a bomba ou o guindaste do parágrafo anterior — o termo usado no resto deste post. Esse número chega na plataforma como um campo de telemetria numérico comum. O gateway empacota a palavra de status — o parsing e o empacotamento do barramento acontecem no dispositivo —, então dentro da regra você trabalha com o valor já entregue, sem firmware e sem serviço externo.
Esta é uma palavra de status sintética, empacotada do jeito que um gateway a empacotaria; tire o layout de bits real do contrato do seu dispositivo ou do DBC, não deste post. O bit 0 é uma posição ilustrativa, escolhida para mostrar a técnica — você entra com o offset do seu próprio sinal real de PTO.
util:checkBit: a decodificação inteira em uma linha
O IoT Logic tem uma linguagem de expressões — o mesmo JEXL que você já deve ter visto em uma dúzia de outras ferramentas, mais algumas funções auxiliares para bits. Extrair o bit de que você precisa é assim:
util:checkBit(can_status_word, 0)
Acrescente a velocidade e você chega a três atributos computados. O sentido de cada um cabe em uma frase:
pto_engaged = util:checkBit(can_status_word, 0)
wasteful_idle = speed < 3 && !util:checkBit(can_status_word, 0) # estacionado, PTO desligado
productive_idle = speed < 3 && util:checkBit(can_status_word, 0) # estacionado, PTO ligado
Em produção você faria um AND entre speed < 3 e um sinal de ignição ou de motor ligado, para que "estacionado" signifique que o motor está mesmo funcionando; aqui, speed < 3 é só um substituto, para manter o foco no bit de PTO — a parte que a velocidade não revela.
Esse é o "decodificador" inteiro: três expressões em um nó de regra, sem microsserviço nem parser de backend para manter. Cada uma produz um booleano no qual o fluxo se ramifica — wasteful_idle verdadeiro é a condição sobre a qual você agiria. Transformar um booleano em uma notificação real é uma etapa separada (um sensor ou uma regra por cima); este fluxo só calcula e roteia. A sintaxe e a lista completa de operações de bit estão na referência de expressões da Navixy.
A regra em si é um grafo simples de quatro nós, construído pela REST: uma fonte de dados, um nó de atributos computados, um nó de lógica e um endpoint de saída. Ela não carrega nenhuma ação nem webhook — a regra calcula e roteia, mas não comanda nada. A primeira escrita entra com enabled:false, então mesmo um grafo correto não vai tocar em nenhum dado até uma habilitação separada e verificada.
O agente monta o grafo; você abre para confirmar
O mesmo grafo que o agente montou pela API abre no editor visual como um fluxo comum — nó por nó:
Ninguém arrastou um nó. Mas nada fica escondido: um humano abre essa tela, vê os mesmos quatro nós e a ramificação, edita qualquer um deles, ou desabilita o fluxo com um único interruptor. O canvas é onde o trabalho do agente se torna revisável.
Um bit inverte o veredito com velocidade zero
Montar o grafo não basta — você precisa ver que ele calcula o que você pretendia. Se o grafo sobreviveu à escrita, isso se confirma lendo-o de volta; como a regra se comporta com dados em tempo real é outro canal, o Data Stream Analyzer (DSA). Assine o stream WebSocket do DSA, envie três pacotes de telemetria — um por estado, cada um com sua própria velocidade e palavra de status —, e o DSA mostra, de forma independente, o que a regra calculou.
| O que o pacote enviou | O que o DSA mostrou no mesmo tick |
|---|---|
speed=45, can_status_word=4 — em movimento, PTO desligado |
pto=false, wasteful=false, productive=false, flagged=false |
speed=0, can_status_word=4 — estacionado, PTO desligado |
pto=false, wasteful=true, productive=false, flagged=true |
speed=0, can_status_word=5 — estacionado, PTO ligado |
pto=true, wasteful=false, productive=true, flagged=false |
Leia de cima para baixo. Em movimento não é ociosidade de forma alguma, então nada é sinalizado. O caminhão para com o PTO desligado — wasteful vira verdadeiro, e flagged junto com ele: aí está o ocioso improdutivo. Mude um bit da palavra de status, mantenha a velocidade em zero, e o quadro se inverte: flagged some e productive vira verdadeiro. Mesmo parado, veredito oposto — exatamente a diferença que a velocidade não vê.
Um detalhe transforma isso em prova, não em coincidência. Cada pacote carrega um marcador único; a verificação procura exatamente um tick do DSA com esse marcador, para comparar os campos enviados e os calculados dentro dele. A velocidade de uma mensagem não pode ser colada, por acidente, à palavra de status de outra. Esse é o hábito que sustenta tudo aqui: a prova vem de um canal que nunca escreveu nada.
Aqui está a execução inteira reduzida a quatro valores — uma execução sintética em uma conta de teste descartável, os três estados confirmados em seus respectivos ticks, e a regra deixada desabilitada depois da checagem:
{
"status": "succeeded",
"dsa_verified": true,
"final_disabled_verified": true,
"inventory_verified": true
}
Quem faz o quê: o agente escreve, o canvas confere
Cada ator responde por uma parte, então nenhuma resposta isolada precisa ser aceita por fé para as outras:
- O agente, pela REST, escreve a configuração e confirma, lendo-a de volta, que o grafo e o
enabled:truerealmente persistiram. - O NGP aceita a telemetria; seu HTTP 200 fala apenas sobre a entrega.
- O DSA mostra, de forma independente, o resultado calculado no stream.
- O User MCP fica do lado da leitura — o servidor User MCP lê e audita isso, enquanto toda mutação passa pela REST.
- O humano trabalha no editor visual: valida o fluxo montado pelo agente, aprova a habilitação, confere os dados do caminhão em campo, depura pelo DSA, controla o interruptor e é o dono da chave. Esse é o ponto de controle — humano no circuito, por cima do agente.
Na prática, o padrão que eu manteria é este: trate uma regra como funcionando não quando o create retornou sucesso, mas quando um canal que não participou da escrita mostra o resultado que você esperava. A rota direta pela REST tem uma irmã dentro do próprio produto — o Navixy AI Assistant, em que o assistente monta o fluxo e um humano confirma. Os dois chegam ao mesmo objeto, no mesmo canvas.
Quando você não precisa decodificar um bit
Essa técnica compensa num espaço estreito. Se o seu gateway já expõe um campo pronto do tipo "PTO ligado", use esse campo como ele vem e não decodifique nada. Se a pergunta é pontual e o volume de dados é pequeno, uma planilha e dez minutos resolvem melhor do que qualquer regra.
Decodificar bits no IoT Logic ganha o seu lugar quando o sinal chega compactado, em um stream contínuo, e você quer distinguir os estados na hora, em vez de reconstruí-los a partir de logs depois do fato.
Seu sinal: o que mudar, o que manter
Troque can_status_word e o bit de demonstração por um campo numérico real do seu rastreador ou gateway, e entre com o offset que o contrato do seu dispositivo ou o DBC atribui ao sinal que interessa a você. Mantenha o resto da ordem: primeiro o schema ao vivo, depois a construção com enabled:false, a leitura de volta, uma atualização completa na habilitação, e uma checagem no DSA amarrada a um marcador único.
O único comando que vale a pena rodar à mão primeiro é o preflight — ler o schema ao vivo e confirmar que seu ambiente tem os tipos de nó de que precisa. A chave é criada uma vez em Account Settings → API Keys e lida a partir de uma variável de ambiente:
curl -fsS \
-H "Authorization: NVX ${NAVIXY_API_KEY}" \
-H 'Accept: application/json' \
'https://api.us.navixy.com/v2/iot/logic/flow/schema'
Depois disso, vêm as três expressões vistas acima e uma leitura de volta depois de cada escrita. A leitura de volta confirma que o fluxo persistiu como foi escrito; a checagem independente do DSA confirma que ele calcula os estados que você quis dizer.
Tudo isso fica como chamadas separadas, com leitura de volta e checagem no DSA, por um motivo: cada etapa passa a ter um resultado binário e verificável por máquina — uma correspondência numa busca por rótulo, um tick do DSA com seu marcador único, um enabled:false final confirmado por uma leitura. O schema tipado, o guia de geração de fluxos com IA e o MCP somente leitura existem para que um agente consiga rodar esse ciclo.
O editor é o jeito familiar de montar um fluxo, e isso é conveniente. Mas eu apostaria que a montagem do grafo vai continuar migrando para os agentes, enquanto o canvas e o DSA permanecem como o lugar onde um humano valida, confere a instalação e depura — deixando de fora uma única operação que você não pode delegar: criar a chave, e revogá-la.
- A resposta está em um bit da palavra de status
- util:checkBit: a decodificação inteira em uma linha
- O agente monta o grafo; você abre para confirmar
- Um bit inverte o veredito com velocidade zero
- Quem faz o quê: o agente escreve, o canvas confere
- Quando você não precisa decodificar um bit
- Seu sinal: o que mudar, o que manter