IoT industrial

Templates e UDTs do Sparkplug Explicados

Os templates do Sparkplug trazem para o MQTT os tipos definidos pelo usuário no estilo dos CLPs. Este guia explica definições e instâncias de templates, parâmetros, aninhamento e versionamento, com código Python que monta um template de bomba e dicas para manter os modelos consistentes entre edge nodes.

Diagrama de templates do Sparkplug mostrando uma definição de bomba e várias instâncias de bomba publicadas por edge nodes
Rode isso no seu próprio broker MQTTTenha um broker MQTT privado e gerenciado no plano Free e comece com exemplos de código prontos.
Teste grátis Exemplos de código Sem cartão de crédito

Os templates do Sparkplug são a forma do Sparkplug B de definir tipos de equipamento reutilizáveis, o equivalente MQTT de um tipo definido pelo usuário (UDT) em um CLP ou sistema SCADA. Neste guia você vai aprender como funcionam as definições e as instâncias de templates, onde entram parâmetros, aninhamento e versões, como montar um template de bomba em Python e como manter o mesmo modelo consistente em muitos edge nodes.

Por que os templates existem

Pense numa empresa de saneamento com dezenas de estações de bombeamento. Sem templates, uma bomba é só um conjunto de métricas vagamente relacionadas: Pump1/Speed, Pump1/Running, Pump1/Pressure. Uma segunda bomba repete os mesmos nomes com outro prefixo, e nada informa à aplicação host que as duas são bombas. Se um edge node chama a métrica de Speed e outro a chama de RPM, todo consumidor precisa conhecer essa diferença.

Os templates resolvem isso separando o tipo da instância. Você define um Pump uma única vez, com suas métricas membro, tipos de dados e valores padrão. Cada bomba física passa a ser uma instância que referencia a definição. Uma aplicação host que entende templates consegue criar objetos correspondentes automaticamente, aplicar as mesmas telas e alarmes a todas as bombas, alimentar um gêmeo digital de cada ativo e detectar quando uma instância não corresponde ao seu tipo.

Definições e instâncias

Tanto as definições quanto as instâncias são métricas com datatype 19 (Template), cujo valor é uma mensagem Template:

Campo do template Finalidade Em uma definição Em uma instância
version String de versão opcional Recomendado Opcional
metrics Métricas membro Membros com tipos de dados e valores padrão Membros com valores atuais
parameters Constantes nomeadas e tipadas Declarados com valores padrão Sobrescritos por instância
template_ref Nome da definição utilizada Não definido Obrigatório, aponta para a definição
is_definition Marca uma definição true false

Onde ficam as definições

As definições de templates são publicadas no NBIRTH, antes de qualquer instância que as utilize. Elas não se repetem nas mensagens de dados. Algumas plataformas prefixam os nomes das definições com uma pasta como _types_/ para separar tipos de tags ao vivo; isso é uma convenção da plataforma e não uma exigência, então siga o que a sua aplicação host espera.

Onde ficam as instâncias

As instâncias podem aparecer no NBIRTH (equipamentos em nível de node) ou em um DBIRTH (equipamentos atrás de um device). Cada instância carrega no birth o conjunto completo de métricas membro com os valores atuais. As mensagens de dados passam então a transportar as mudanças.

Um exemplo decodificado

Esta é uma definição de bomba como aparece em um NBIRTH depois de decodificada:

{
  "name": "_types_/Pump",
  "datatype": 19,
  "templateValue": {
    "version": "1.2",
    "isDefinition": true,
    "parameters": [
      { "name": "RatedSpeed", "type": 9, "floatValue": 1500.0 }
    ],
    "metrics": [
      { "name": "Running", "datatype": 11, "booleanValue": false },
      { "name": "Speed", "datatype": 9, "floatValue": 0.0 },
      { "name": "DischargePressure", "datatype": 9, "floatValue": 0.0 },
      { "name": "StartCount", "datatype": 8, "longValue": "0" }
    ]
  }
}

E uma instância dela em um DBIRTH:

{
  "name": "CoolingPump1",
  "alias": 20,
  "datatype": 19,
  "templateValue": {
    "templateRef": "_types_/Pump",
    "version": "1.2",
    "isDefinition": false,
    "parameters": [
      { "name": "RatedSpeed", "type": 9, "floatValue": 2900.0 }
    ],
    "metrics": [
      { "name": "Running", "datatype": 11, "booleanValue": true },
      { "name": "Speed", "datatype": 9, "floatValue": 2875.5 },
      { "name": "DischargePressure", "datatype": 9, "floatValue": 3.4 },
      { "name": "StartCount", "datatype": 8, "longValue": "1287" }
    ]
  }
}

A instância sobrescreve o parâmetro RatedSpeed porque essa bomba tem um motor diferente. Os parâmetros são ideais para dados de placa e limites que variam por ativo, mas não mudam em tempo de execução.

Monte um template em Python

Este trecho monta a definição e uma instância usando classes geradas a partir do arquivo sparkplug_b.proto do Eclipse Tahu. Nosso tutorial sobre como decodificar Sparkplug B em Python mostra o passo único de geração de código.

import time

import sparkplug_b_pb2 as spb

BOOLEAN, FLOAT, UINT64, TEMPLATE = 11, 9, 8, 19
MEMBERS = [("Running", BOOLEAN), ("Speed", FLOAT),
           ("DischargePressure", FLOAT), ("StartCount", UINT64)]


def set_value(metric, datatype, value):
    metric.datatype = datatype
    if datatype == BOOLEAN:
        metric.boolean_value = value
    elif datatype == FLOAT:
        metric.float_value = value
    elif datatype == UINT64:
        metric.long_value = value


def add_pump_definition(payload):
    m = payload.metrics.add()
    m.name = "_types_/Pump"
    m.datatype = TEMPLATE
    t = m.template_value
    t.version = "1.2"
    t.is_definition = True
    p = t.parameters.add()
    p.name, p.type, p.float_value = "RatedSpeed", FLOAT, 1500.0
    for name, dtype in MEMBERS:
        member = t.metrics.add()
        member.name = name
        set_value(member, dtype, False if dtype == BOOLEAN else 0)


def add_pump_instance(payload, name, alias, rated_speed, values):
    m = payload.metrics.add()
    m.name = name
    m.alias = alias
    m.timestamp = int(time.time() * 1000)
    m.datatype = TEMPLATE
    t = m.template_value
    t.template_ref = "_types_/Pump"
    t.version = "1.2"
    t.is_definition = False
    p = t.parameters.add()
    p.name, p.type, p.float_value = "RatedSpeed", FLOAT, rated_speed
    for name, dtype in MEMBERS:
        member = t.metrics.add()
        member.name = name
        set_value(member, dtype, values[name])


nbirth = spb.Payload(timestamp=int(time.time() * 1000), seq=0)
add_pump_definition(nbirth)

dbirth = spb.Payload(timestamp=int(time.time() * 1000), seq=1)
add_pump_instance(dbirth, "CoolingPump1", 20, 2900.0,
                  {"Running": True, "Speed": 2875.5,
                   "DischargePressure": 3.4, "StartCount": 1287})

print(len(nbirth.SerializeToString()), "byte NBIRTH,",
      len(dbirth.SerializeToString()), "byte DBIRTH")

Em um edge node real, o NBIRTH também contém bdSeq e Node Control/Rebirth, como mostrado em certificados de birth e death do Sparkplug. Publique esses payloads em spBv1.0/{group}/NBIRTH/{node} e spBv1.0/{group}/DBIRTH/{node}/{device}.

Atualizando valores de templates em tempo de execução

Os births carregam instâncias completas, mas as mensagens de dados devem continuar pequenas. As plataformas diferem na forma como reportam mudanças em um membro de template. Algumas enviam de novo a métrica da instância no NDATA ou DDATA contendo apenas os membros que mudaram. Outras expõem cada membro como uma métrica própria com alias depois do birth, de modo que uma mudança de velocidade trafega como um único valor escalar. As duas abordagens dependem do birth para estabelecer nomes, tipos de dados e aliases, e é por isso que um host que perde o birth também não consegue interpretar as atualizações de templates.

Antes de se comprometer com um design, publique um birth e algumas mensagens de dados a partir da sua plataforma de borda e observe o resultado decodificado. Confirme que a sua aplicação host atualiza o membro certo da instância, que os membros inalterados mantêm os seus valores e que um rebirth restaura a instância completa. Fazer isso com duas ou três bombas em um group de teste leva minutos e evita descobrir uma incompatibilidade depois de centenas de ativos configurados.

Aninhamento e composição

Uma métrica membro de um template pode ser, ela mesma, um template. Um template Skid pode conter duas instâncias de Pump, uma instância de Tank e algumas métricas escalares. A composição mantém os modelos legíveis e permite reutilizar tipos pequenos e bem testados.

Mantenha o aninhamento raso. Todo host precisa resolver cada nível, e hierarquias profundas deixam os births grandes. Births grandes importam em infraestrutura compartilhada: o servidor MQTT público aceita mensagens de até 64 KB, e cada plano tem o seu próprio limite de tamanho de mensagem. Se um node modela centenas de ativos, verifique o tamanho do birth logo no início.

Mantendo os templates consistentes entre edge nodes

Os templates só compensam quando todos os edge nodes usam a mesma definição. Na prática, divergências aparecem quando os nodes são configurados por pessoas diferentes ou atualizados em momentos diferentes. Estes hábitos evitam o problema:

  1. Mantenha uma biblioteca central de definições sob controle de versão e gere a configuração dos edge nodes a partir dela.
  2. Preencha o campo version e incremente-o a cada mudança em membros, tipos de dados ou parâmetros.
  3. Nunca altere o datatype de um membro no lugar; adicione um novo membro ou uma nova versão.
  4. Use o mesmo nome de definição em todos os lugares, inclusive em maiúsculas e minúsculas.
  5. Trate as diferenças entre ativos com parâmetros, não com membros extras.
  6. Depois de implantar, compare as definições do NBIRTH decodificado de cada node com a biblioteca.
  7. Distribua novas versões node a node e confirme que o host aceita as duas durante a transição.

O passo 6 é onde uma visão decodificada ajuda. O ConnectMQ decodifica Sparkplug B no topic explorer do portal, então você pode abrir o NBIRTH de cada node e comparar as definições sem escrever um script. Observe que ele mostra os births quando são publicados; se um node já estiver online, solicite um rebirth para vê-los.

Problemas comuns

Sintoma Causa provável Solução
O host mostra os membros da instância como desconhecidos Definição ausente no NBIRTH Publique as definições no NBIRTH antes das instâncias
Instância rejeitada ou exibida parcialmente template_ref não corresponde ao nome da definição Nome exato, respeitando maiúsculas e minúsculas
O mesmo tipo se comporta de forma diferente em cada node As definições divergiram entre os nodes Biblioteca central e verificação de versões
Births grandes demais Muitas instâncias profundamente aninhadas Achate, divida os devices ou verifique os limites do plano
Alguns consumidores ignoram os templates A ferramenta não suporta o datatype 19 Use um host que suporte templates ou achate os dados para esse consumidor

Templates, ISA-95 e o Unified Namespace

Os templates modelam o que um equipamento é; o ISA-95 modela onde ele está. Os dois se complementam: um tipo Pump, instanciado sob site, área e linha. O ConnectMQ pode republicar dados Sparkplug em topics ISA-95 como connectmq/factory1/production/line1/cnc1/telemetry, o que oferece aos consumidores que não usam Sparkplug uma visão legível baseada em localização. Para saber mais sobre como estruturar os dados da planta, veja MQTT, Sparkplug B e o Unified Namespace, a referência de tipos de dados do Sparkplug B e o guia principal de IoT Industrial com MQTT.

Comece com o ConnectMQ

Rode os seus edge nodes baseados em templates em um broker MQTT gerenciado sem operar a infraestrutura você mesmo, ou faça antes um protótipo no servidor MQTT público, que serve para testes e não para dados de produção, com um group ID aleatório. Crie um broker Serverless gratuito em app.connectmq.com, sem cartão de crédito, e inspecione as definições e instâncias dos seus templates decodificadas no portal.

Perguntas frequentes

Os templates do Sparkplug são o mesmo que UDTs de CLP?

Eles servem ao mesmo propósito: um tipo reutilizável com membros nomeados e tipados. Muitas plataformas de borda mapeiam UDTs de CLP ou SCADA diretamente para templates Sparkplug.

Onde as definições de templates devem ser publicadas?

No NBIRTH, antes de qualquer instância que as referencie. As instâncias podem então aparecer no NBIRTH ou em mensagens DBIRTH.

Para que servem os parâmetros de template?

Os parâmetros guardam constantes por instância, como velocidade nominal, limites ou dados de placa. A definição fornece os valores padrão e cada instância pode sobrescrevê-los.

Todas as aplicações host Sparkplug suportam templates?

Não. O suporte varia, então verifique o seu host antes de modelar tudo como templates. Para consumidores sem suporte, achate os membros em métricas comuns ou use uma visão ISA-95 republicada e legível.

Artigos relacionados

Cliente MQTT no CODESYS: Publicando Variáveis do CLP em um Broker

Configure a biblioteca IIoT MQTT Client do CODESYS para publicar variáveis do CLP em JSON, verifique-as no servidor MQTT público broker.connectmq.com e depois migre para um broker privado com credenciais e TLS.

8 min de leitura

Todos os artigos →

Seu broker MQTT privado está a poucos cliques de distância

Crie uma conta, receba as credenciais do seu broker e conecte seu primeiro dispositivo — sem servidores para instalar, atualizar ou escalar.