Conector UNS (Unified Namespace)
Descripción General
Sección titulada «Descripción General»El conector UNS (Unified Namespace) es un procesador que construye una jerarquía de tópicos alineada con ISA-95 a partir de la configuración del conector y la carga entrante, y luego emite o bien una carga JSON con una clave _uns_topic inyectada o un sobre estilo SparkplugB que envuelve los datos en un array de métricas. Es el bloque de construcción canónico para organizar los datos de planta en un Unified Namespace tal como lo popularizó Walker Reynolds y el estándar ISA-95.
Tipos de Conector:
UNS- Procesador sin estado que decora las cargas con un tópico ISA-95 y reforma la salida como JSON o SparkplugB
Características
Sección titulada «Características»- ✅ Jerarquía ISA-95: Enterprise / Site / Area / Line / Cell
- ✅ Niveles de tópico dinámicos opcionales basados en la carga (mappings)
- ✅ Dos formatos de salida:
json(passthrough +_uns_topic) ysparkplugb(sobre con array de métricas) - ✅ Omite niveles de jerarquía vacíos con elegancia
- ✅ Timestamp por mensaje (milisegundos Unix) en la salida SparkplugB
Configuración Básica
Sección titulada «Configuración Básica»Jerarquía ISA-95 Estática con Salida JSON
Sección titulada «Jerarquía ISA-95 Estática con Salida JSON»{ "type": "UNS", "config": { "enterprise": "Acme", "site": "Milan", "area": "Assembly", "line": "Line1", "cell": "Station3", "outputFormat": "json" }}Niveles de Tópico Dinámicos desde la Carga
Sección titulada «Niveles de Tópico Dinámicos desde la Carga»{ "type": "UNS", "config": { "enterprise": "Acme", "site": "Milan", "area": "Assembly", "mappings": [ { "inputKey": "machine_id", "topicLevel": "machine" }, { "inputKey": "shift_id", "topicLevel": "shift" } ], "outputFormat": "json" }}Salida con Sobre SparkplugB
Sección titulada «Salida con Sobre SparkplugB»{ "type": "UNS", "config": { "enterprise": "Acme", "site": "Milan", "area": "Packaging", "line": "Line2", "outputFormat": "sparkplugb" }}Parámetros de Configuración
Sección titulada «Parámetros de Configuración»Enterprise
Sección titulada «Enterprise»Obligatorio. La empresa ISA-95 de nivel superior (nombre de la compañía/división).
{ "enterprise": "Acme" }Obligatorio. El sitio físico (planta, fábrica, edificio).
{ "site": "Milan" }Opcional. Un área funcional dentro del sitio.
{ "area": "Assembly" }Opcional. Una línea de producción dentro del área.
{ "line": "Line1" }Opcional. Una célula o estación de trabajo específica dentro de la línea.
{ "cell": "Station3" }Mappings
Sección titulada «Mappings»Opcional. Un array de mapeos de carga a nivel de tópico. Para cada mapeo, el conector busca data[inputKey] y añade <topicLevel>/<value> al tópico.
{ "mappings": [ { "inputKey": "machine_id", "topicLevel": "machine" }, { "inputKey": "shift_id", "topicLevel": "shift" } ]}Comportamiento:
- Tanto
inputKeycomotopicLevelson obligatorios en cada entrada - Las claves de entrada faltantes se reportan a través del canal de errores; el nivel de tópico se omite
- Los valores de entrada no string se coercionan con
fmt.Sprintf("%v", ...)
Output Format
Sección titulada «Output Format»Obligatorio. Debe ser uno de:
json- Passthrough de la carga original con_uns_topicinyectadosparkplugb- Sobre{ topic, timestamp, metrics }donde metrics es una lista de pares{ name, value }
{ "outputFormat": "sparkplugb" }Construcción del Tópico
Sección titulada «Construcción del Tópico»Los niveles del tópico se unen con /. Los niveles opcionales vacíos (Area, Line, Cell) se omiten. Los mappings aparecen en el orden en el que se declaran.
Ejemplo:
Configuración:
{ "enterprise": "Acme", "site": "Milan", "area": "Assembly", "line": "Line1", "mappings": [ { "inputKey": "machine_id", "topicLevel": "machine" } ]}Carga entrante:
{ "machine_id": "M-007", "temperature": 24.5 }Tópico resultante:
Acme/Milan/Assembly/Line1/machine/M-007Ejemplos de Carga de Salida
Sección titulada «Ejemplos de Carga de Salida»Salida JSON
Sección titulada «Salida JSON»Entrada:
{ "machine_id": "M-007", "temperature": 24.5, "pressure": 5.2}Configuración:
{ "enterprise": "Acme", "site": "Milan", "area": "Assembly", "line": "Line1", "mappings": [ { "inputKey": "machine_id", "topicLevel": "machine" } ], "outputFormat": "json"}Salida:
{ "machine_id": "M-007", "temperature": 24.5, "pressure": 5.2, "_uns_topic": "Acme/Milan/Assembly/Line1/machine/M-007"}Salida con Sobre SparkplugB
Sección titulada «Salida con Sobre SparkplugB»Misma entrada y configuración base con outputFormat: sparkplugb:
{ "topic": "Acme/Milan/Assembly/Line1/machine/M-007", "timestamp": 1747740875123, "metrics": [ { "name": "machine_id", "value": "M-007" }, { "name": "temperature", "value": 24.5 }, { "name": "pressure", "value": 5.2 } ]}El timestamp es milisegundos Unix en el momento de procesado. Ten en cuenta que el orden de las entradas de metrics no está garantizado (la iteración de mapas de Go no está ordenada).
Flujo de Datos
Sección titulada «Flujo de Datos»DataPayload → UNS → DataPayload + _uns_topic (json) O { topic, timestamp, metrics } (sparkplugb)Casos de Uso Comunes
Sección titulada «Casos de Uso Comunes»1. Puenteo de Datos PLC a MQTT con Tópico ISA-95
Sección titulada «1. Puenteo de Datos PLC a MQTT con Tópico ISA-95»Reforma las cargas crudas del PLC en un tópico MQTT conforme a UNS:
{ "type": "UNS", "config": { "enterprise": "Acme", "site": "Detroit", "area": "Stamping", "line": "PressLine1", "outputFormat": "json" }}Un MqttV5Writer descendente usa luego _uns_topic como tópico de publicación a través de un paso Reshape que mapea _uns_topic → tópico MQTT.
2. Publicación SparkplugB para Ignition o HiveMQ
Sección titulada «2. Publicación SparkplugB para Ignition o HiveMQ»Muchas plataformas SCADA (Ignition, HiveMQ, etc.) esperan sobres SparkplugB. Usa la salida sparkplugb para coincidir:
{ "type": "UNS", "config": { "enterprise": "Acme", "site": "Milan", "area": "Bottling", "line": "Filler1", "outputFormat": "sparkplugb" }}3. Enrutamiento de Tópico Multi-Tenant por Cliente
Sección titulada «3. Enrutamiento de Tópico Multi-Tenant por Cliente»Usa mappings dinámicos para incrustar el identificador de cliente en el tópico:
{ "type": "UNS", "config": { "enterprise": "ConnectCo", "site": "Cloud", "mappings": [ { "inputKey": "tenant_id", "topicLevel": "tenant" }, { "inputKey": "site_id", "topicLevel": "customer-site" }, { "inputKey": "device_id", "topicLevel": "device" } ], "outputFormat": "json" }}Tópico resultante para {tenant_id: "T-1", site_id: "S-99", device_id: "D-42"}:
ConnectCo/Cloud/tenant/T-1/customer-site/S-99/device/D-42Solución de Problemas
Sección titulada «Solución de Problemas»Falta _uns_topic en la Salida
Sección titulada «Falta _uns_topic en la Salida»Problema: El consumidor descendente no ve la clave _uns_topic
Soluciones:
- Confirma que
outputFormat: jsonestá definido (el sobre SparkplugB reemplaza la carga por completo; tienetopicen el nivel superior en lugar de_uns_topic) - Verifica que el conector UNS está realmente en el pipeline — usa un escritor
Logpara volcar cargas intermedias
Errores input key %q not found in payload
Sección titulada «Errores input key %q not found in payload»Problema: Un mapping configurado referencia una clave que no está presente en la carga
Soluciones:
- Verifica que la clave de entrada coincide con la carga ascendente exactamente (sensible a mayúsculas)
- Usa un
ReshapeoTransformaguas arriba para asegurar que existen las claves requeridas - Si la clave a veces falta, considera hacerla parte de los valores estáticos
area/line/cell
El Array metrics del SparkplugB Es Vacío
Sección titulada «El Array metrics del SparkplugB Es Vacío»Problema: El sobre de salida es { topic, timestamp, metrics: [] }
Soluciones:
- El array de métricas se construye a partir de las claves de la carga de entrada. Una entrada vacía → métricas vacías
- Verifica que el conector ascendente está entregando cargas no vacías
El Tópico Tiene Barras Inesperadas o Segmentos Vacíos
Sección titulada «El Tópico Tiene Barras Inesperadas o Segmentos Vacíos»Problema: El tópico contiene // o barras finales
Soluciones:
- No incluyas barras en
enterprise,site, etc. — el conector las inserta. Las barras incrustadas producirán rutas malformadas - Evita cadenas vacías para los niveles opcionales — deja el campo sin definir en lugar de pasar
""
Preocupaciones de Ordenación para Métricas SparkplugB
Sección titulada «Preocupaciones de Ordenación para Métricas SparkplugB»Problema: La ordenación de las métricas es inconsistente entre ejecuciones
Soluciones:
- La iteración de mapas de Go no está ordenada intencionadamente. Si a los consumidores descendentes les importa el orden, ordena aguas arriba a través de un
Reshapeque convierta las métricas en una lista explícita antes de alcanzar este conector - O haz que el consumidor ordene por
nameal recibir
Mejores Prácticas
Sección titulada «Mejores Prácticas»1. Trata el Tópico UNS como un Contrato
Sección titulada «1. Trata el Tópico UNS como un Contrato»La jerarquía del tópico es consumida por dashboards, historiadores y otros pipelines de Meddle. Una vez publicado, trátalo como una interfaz estable — renombrar area o line después de los hechos rompe a los consumidores descendentes.
2. Usa Niveles Estáticos para Jerarquías Estables
Sección titulada «2. Usa Niveles Estáticos para Jerarquías Estables»Prefiere enterprise/site/area/line/cell estáticos sobre mappings siempre que la jerarquía no dependa de la carga. Los mappings son potentes pero añaden una dependencia de la forma de la carga.
3. Prefiere JSON para Greenfield, SparkplugB para Puente SCADA
Sección titulada «3. Prefiere JSON para Greenfield, SparkplugB para Puente SCADA»json es más simple y funciona con cualquier consumidor descendente. Usa sparkplugb sólo cuando debas interoperar con un ecosistema SparkplugB existente (Ignition, HiveMQ Edge, etc.).
4. Combina con un Reshape Antes de MQTT
Sección titulada «4. Combina con un Reshape Antes de MQTT»La mayoría de escritores MQTT requieren que el tópico se defina explícitamente. Después del conector UNS, usa un paso Reshape que promueva _uns_topic al campo de tópico de publicación:
... → UNS → Reshape (mueve _uns_topic → topic) → MqttV5Writer5. Documenta tu UNS a Nivel de Organización
Sección titulada «5. Documenta tu UNS a Nivel de Organización»Elige un esquema ISA-95 una vez para toda la organización y aplícalo de forma consistente en todos los sitios y líneas. La deriva de esquema entre despliegues de Meddle crea árboles de tópicos incompatibles.
Flujos de Trabajo de Ejemplo
Sección titulada «Flujos de Trabajo de Ejemplo»PLC → UNS → MQTT (Broker Cloud)
Sección titulada «PLC → UNS → MQTT (Broker Cloud)»ModbusReader → Reshape → UNS → Reshape → MqttV5Writer- ModbusReader: Extrae los datos de registros crudos de un PLC
- Reshape: Renombra direcciones crudas a claves legibles por humanos
- UNS: Decora con
_uns_topic - Reshape: Promueve
_uns_topical campotopicde MQTT - MqttV5Writer: Publica en un broker cloud, organizado bajo el UNS
Gateway Edge SparkplugB
Sección titulada «Gateway Edge SparkplugB»OpcuaReader → Predictive → UNS (sparkplugb) → MqttV5Writer- OpcuaReader: Agrega desde múltiples endpoints OPC UA
- Predictive: Añade trend/RUL/health-score
- UNS: Envuelve la carga enriquecida en un sobre SparkplugB
- MqttV5Writer: Publica en un broker compatible con SparkplugB (ej. HiveMQ Edge → Ignition)
Agregación Multi-Sitio
Sección titulada «Agregación Multi-Sitio»[pipeline Sitio A] ─┐[pipeline Sitio B] ─┼─→ UNS → InfluxDb2Writer[pipeline Sitio C] ─┘Cada pipeline de sitio etiqueta sus datos con su propia configuración UNS, asegurando que los consumidores descendentes puedan desambiguar sólo por tópico.
Conectores Relacionados
Sección titulada «Conectores Relacionados»- MQTT v5 - Publica cargas decoradas con UNS a un broker
- MQTT v3 - Publicación MQTT heredada
- Reshape - Promueve
_uns_topical campo de tópico del escritor - Filter - Filtra por patrones de
_uns_topic - Router - Enrutamiento ramificado basado en la jerarquía UNS
Recursos Adicionales
Sección titulada «Recursos Adicionales»- Estándar ISA-95 - Enterprise-Control System Integration
- Concepto de Unified Namespace (Walker Reynolds) - Primer del sector
- Especificación SparkplugB (Eclipse Tahu) - PDF de la especificación oficial
- Recursos Sparkplug de HiveMQ - Broker y herramientas de referencia
- Módulo Sparkplug de Ignition - Ejemplo de integración SCADA