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:
- Mantenha uma biblioteca central de definições sob controle de versão e gere a configuração dos edge nodes a partir dela.
- Preencha o campo
versione incremente-o a cada mudança em membros, tipos de dados ou parâmetros. - Nunca altere o datatype de um membro no lugar; adicione um novo membro ou uma nova versão.
- Use o mesmo nome de definição em todos os lugares, inclusive em maiúsculas e minúsculas.
- Trate as diferenças entre ativos com parâmetros, não com membros extras.
- Depois de implantar, compare as definições do NBIRTH decodificado de cada node com a biblioteca.
- 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.



