Una buena estructura de topics UNS es lo que hace navegable un Unified Namespace: cualquiera que conozca la planta puede adivinar dónde está un valor sin abrir la documentación. Esta guía te ofrece árboles de topics listos para usar en fabricación discreta y de proceso, las reglas de nombres y de payload que los mantienen limpios, y un pequeño validador que puedes ejecutar en CI o en el gateway para frenar los topics incorrectos antes de que lleguen al broker.
Los principios de una estructura de topics UNS
Todos los ejemplos de este artículo siguen los mismos cinco principios. Si adaptas los árboles, conserva los principios.
- Modela la planta, no la tecnología. Los topics describen dónde está algo y qué es, no qué protocolo o marca de PLC lo produjo.
opcua/ys7/no pertenecen a la ruta. - Primero la jerarquía, con ISA-95 como columna vertebral. Empresa, sitio, área, línea o celda, y equipo forman una estructura que operaciones ya entiende. La guía sobre la jerarquía de equipos ISA-95 explica cada nivel.
- Profundidad fija siempre que sea posible. Cuando todas las máquinas están en el mismo nivel, suscripciones con comodín como
+/+/+/+/telemetryfuncionan en toda la empresa. - Un conjunto reducido de tipos de hoja. Termina cada rama de equipo en un conjunto predecible de namespaces como
telemetry,state,alarms,eventsycmd. - Identificadores estables. Nombres cortos, en minúsculas y con guiones que sobrevivan a las reorganizaciones. Los números de activo suelen ser mejores que los nombres descriptivos que cambian.
Ejemplo 1: Fabricación discreta
Una planta discreta con líneas de montaje, celdas de mecanizado y servicios auxiliares compartidos:
acme/ enterprise
dallas/ site
machining/ area
cell-03/ work cell
cnc-01/ equipment
telemetry {"spindle_rpm":..., "load_pct":..., "ts":...}
state "running" | "idle" | "down" (retained)
alarms active alarm list (retained)
events/cycle-complete one message per finished part
cmd/ commands, request/response
robot-01/
telemetry
state
assembly/
line-02/ production line
station-10/
telemetry
events/part-scanned
station-20/
telemetry
line-02/kpi/oee line-level calculated values
utilities/
compressors/
comp-01/telemetry
Fíjate en la rama kpi a nivel de línea. Los valores calculados, como el OEE o el rendimiento, pertenecen al nivel que describen y los publica la aplicación que los calcula. Así los datos brutos de máquina y los datos derivados quedan claramente separados sin salir del mismo árbol.
Ejemplo 2: Fabricación de proceso
Las plantas de proceso piensan en celdas de proceso y unidades, no en líneas y estaciones. ISA-95 admite ambos enfoques, así que el árbol cambia de vocabulario pero no de forma:
acme/
izmir/ site
brewing/ area
brewhouse-1/ process cell
mash-tun/ unit
telemetry {"temp_c":..., "level_pct":..., "ts":...}
state "heating" | "resting" | "transferring"
batch current batch id and recipe (retained)
lauter-tun/
telemetry
fermentation/
fv-07/
telemetry
batch
utilities/
boiler-1/telemetry
water-treatment/ro-1/telemetry
La hoja batch es el equivalente en una planta de proceso a una orden de trabajo: retenida, pequeña y actualizada cuando cambia el lote. Los consumidores combinan el contexto del lote con la telemetría mediante la ruta del topic, sin consultar una base de datos aparte.
Ejemplo 3: Empresa multisitio con servicios compartidos
Cuando varios sitios comparten un broker o un namespace enlazado mediante bridge, añade una rama de primer nivel para los sistemas corporativos, de modo que los datos de MES, ERP y calidad tengan un sitio propio que no finja ser un equipo:
acme/dallas/... site trees as above
acme/izmir/...
acme/_enterprise/erp/work-orders/dallas work orders per site (retained)
acme/_enterprise/quality/spc/dallas/line-02
acme/_enterprise/mes/schedule/izmir
El guion bajo inicial mantiene la rama ordenada aparte de los nombres de sitio y deja claro que no es una ubicación física.
Cómo encaja Sparkplug B en una estructura de topics UNS
Sparkplug B usa su propio formato de topic fijo, spBv1.0/<group>/<message type>/<edge node>/<device>. Ese formato está optimizado para la gestión de estado entre nodos edge y aplicaciones host, no para navegar por la jerarquía de la planta. Nuestro artículo sobre el namespace de topics de Sparkplug lo explica en detalle.
No tienes que elegir. Un patrón habitual mantiene Sparkplug para el tráfico edge-host y republica los valores en el árbol ISA-95 para todos los demás. ConnectMQ lo hace automáticamente con el tráfico Sparkplug B: decodifica el payload Protobuf o JSON y lo republica en topics ISA-95, de modo que spBv1.0/factory1/DDATA/line1/cnc1 se convierte en connectmq/factory1/production/line1/cnc1/telemetry. Los dashboards y los historians se suscriben a JSON legible en el árbol ISA-95, mientras que los hosts SCADA siguen usando Sparkplug nativo.
Reglas de nombres y de payload
| Regla | Correcto | Evitar |
|---|---|---|
| Solo minúsculas | line-02 |
Line_02, LINE02 |
| Guiones, sin espacios | mash-tun |
mash tun, mash%20tun |
| Sin barra inicial ni final | acme/dallas/... |
/acme/dallas/ |
Sin comodines ni $ en los nombres |
cnc-01 |
cnc#1, $cnc |
| Unidades en los nombres de campo | temp_c, speed_bpm |
temp, speed |
| Timestamps en los payloads | "ts": "2026-10-16T08:00:00Z" |
Depender de la hora de llegada |
| Retener el estado actual | state, batch, alarms |
Retener flujos de eventos |
| Versionar los payloads | "v": 2 |
Cambios incompatibles silenciosos |
La barra inicial es un error clásico: crea un primer nivel vacío, de modo que /acme/# y acme/# son suscripciones distintas. Mezclar mayúsculas y minúsculas es otro: los topics MQTT distinguen mayúsculas, y Line-02 y line-02 se convierten en dos ramas diferentes que dividen tus datos sin que nadie lo note.
Valida los topics antes de que lleguen al broker
El script siguiente comprueba los topics frente a las reglas anteriores y la estructura de equipos de cinco niveles. Úsalo en un gateway, en un test de CI sobre tu archivo de mapeo de tags o como suscriptor de monitorización que informa de las infracciones.
import re
LEVEL = re.compile(r"^[a-z0-9]+(-[a-z0-9]+)*$")
LEAVES = {"telemetry", "state", "alarms", "batch", "events", "cmd", "kpi"}
def validate(topic: str, depth: int = 5) -> list[str]:
"""Return a list of problems; an empty list means the topic is valid."""
problems = []
if topic.startswith("/") or topic.endswith("/"):
problems.append("leading or trailing slash")
levels = topic.strip("/").split("/")
if any(ch in topic for ch in "+#$"):
problems.append("wildcard or $ character in topic name")
if any(" " in level for level in levels):
problems.append("space in topic level")
if len(levels) < depth + 1:
problems.append(f"expected at least {depth + 1} levels, got {len(levels)}")
return problems
for level in levels[:depth]:
if not (LEVEL.match(level) or level.startswith("_")):
problems.append(f"level '{level}' is not lowercase-hyphenated")
if levels[depth] not in LEAVES:
problems.append(f"leaf '{levels[depth]}' not in {sorted(LEAVES)}")
return problems
if __name__ == "__main__":
tests = [
"acme/dallas/machining/cell-03/cnc-01/telemetry",
"acme/dallas/machining/cell-03/CNC_01/telemetry",
"/acme/dallas/assembly/line-02/station-10/telemetry",
"acme/dallas/assembly/line-02/station-10/temperature",
]
for t in tests:
result = validate(t)
print("OK " if not result else "FAIL", t, "" if not result else result)
Ejecútalo con python validate_topics.py y ajusta LEAVES y depth a tu estándar.
Evolucionar la estructura sin romper a los consumidores
Ninguna estructura de topics sobrevive intacta al contacto con una planta real. Las líneas se reconstruyen, las máquinas cambian de celda, se adquiere un sitio nuevo con sus propias costumbres de nombres. El objetivo no es evitar el cambio, sino hacerlo seguro.
- Añade, nunca renombres in situ. Cuando una rama deba cambiar, publica en la ruta nueva y en la antigua en paralelo durante un periodo definido y después retira la antigua. Comunica la fecha de retirada a cada responsable de consumidor.
- Borra los retained messages al retirar. Una rama retirada sigue devolviendo valores retenidos a los nuevos suscriptores. Publica un mensaje retenido vacío en cada topic de estado antiguo para eliminarlo.
- Mantén un registro. Basta con un archivo en control de versiones que liste cada rama, su responsable, sus tipos de hoja y la versión de su esquema de payload. El validador anterior puede leer sus reglas de ese archivo.
- Versiona los payloads, no los topics. Añade un campo
val payload cuando cambie el esquema. Los topics con/v2/en la ruta tienden a multiplicarse y a confundir las suscripciones con comodín. - Revisa las ramas nuevas. Una revisión breve por parte del responsable del namespace antes de poner en marcha un gateway detecta la mayoría de los problemas cuando todavía es barato corregirlos.
Trata la estructura como una API de la que depende toda la planta, porque eso es exactamente lo que se convierte cuando dashboards, historians e integraciones MES se suscriben a ella.
Prueba la estructura en un broker real
Antes de desplegar una estructura, publica unos cuantos mensajes de ejemplo y explóralos. En el servidor MQTT público gratuito en broker.connectmq.com puedes hacerlo sin cuenta, siempre que uses un prefijo único y recuerdes que el área pública es legible por cualquiera y sirve para pruebas y prototipos, no para datos de producción. El broker público admite 8 niveles de topic, así que connectmq/demo/<id>/ más el árbol de cinco niveles y una hoja encaja exactamente si prescindes del nivel de empresa:
mosquitto_pub -h broker.connectmq.com -p 1883 -i uns-test-pub-q7 -r \
-t "connectmq/demo/q7w2/dallas/machining/cell-03/cnc-01/state" -m "running"
mosquitto_sub -h broker.connectmq.com -p 1883 -i uns-test-sub-q7 \
-t "connectmq/demo/q7w2/dallas/+/+/+/state" -v
En un broker privado de ConnectMQ puedes usar el árbol completo sin el prefijo de demo, y el explorador de topics en vivo y la vista de dispositivos ISA-95 del portal muestran de un vistazo si los dispositivos aterrizan en la empresa, el sitio, el área y la línea correctos. Para las reglas de codificación paso a paso, continúa con topics MQTT ISA-95, y para el proceso de construcción completo consulta cómo construir un Unified Namespace. La visión de conjunto está en nuestra guía de MQTT para IoT industrial. Un árbol coherente también es la base para visualizar la planta en un gemelo digital 3D, ya sea una planta de manufactura en Monterrey o una de proceso en Valencia.
Lleva tu estructura de topics UNS a ConnectMQ
Una estructura de topics vale lo que valen el broker que la transporta y la visibilidad que tienes sobre ella. ConnectMQ es un broker MQTT gestionado que detecta topics ISA-95 y de Unified Namespace, decodifica Sparkplug B y lo republica en tu árbol ISA-95. Crea un broker en el plan gratuito Serverless en app.connectmq.com, sin tarjeta de crédito, y publica tu primera rama del namespace en minutos.
Preguntas frecuentes
¿Cuántos niveles debe tener una estructura de topics UNS?
La mayoría de las plantas usan cinco o seis: empresa, sitio, área, línea o celda, equipo y una hoja como telemetry o state. Una profundidad fija mantiene simples las suscripciones con comodín.
¿Debe cada métrica tener su propio topic?
Ambos enfoques funcionan. Un objeto JSON por topic de telemetría de equipo es más sencillo y económico; un topic por métrica conviene a las herramientas que asignan topics directamente a tags. Elige uno por tipo de hoja y aplícalo de forma coherente.
¿Dónde van los KPI calculados?
En el nivel que describen, publicados por la aplicación que los calcula, por ejemplo una rama kpi/oee bajo la línea.
¿Pueden los topics Sparkplug B formar parte de un UNS?
Sí, pero no están organizados según la jerarquía de la planta. Republica los valores Sparkplug en topics ISA-95 para que todos los consumidores puedan navegar por un único árbol.



