Zum Inhalt springen

UNS (Unified Namespace)-Konnektor

Der UNS (Unified Namespace)-Konnektor ist ein Prozessor, der eine ISA-95-konforme Topic-Hierarchie aus der Konnektor-Konfiguration und der eingehenden Payload aufbaut und dann entweder eine JSON-Payload mit injiziertem _uns_topic-Schlüssel oder einen SparkplugB-artigen Umschlag ausgibt, der die Daten in einem Metrics-Array umhüllt. Es ist der kanonische Baustein für die Organisation von Anlagendaten in einem Unified Namespace, wie er von Walker Reynolds und dem ISA-95-Standard popularisiert wurde.

Konnektor-Typen:

  • UNS - Zustandsloser Prozessor, der Payloads mit einem ISA-95-Topic dekoriert und die Ausgabe als JSON oder SparkplugB umformt
  • ✅ ISA-95-Hierarchie: Enterprise / Site / Area / Line / Cell
  • ✅ Optionale payload-gesteuerte dynamische Topic-Ebenen (Mappings)
  • ✅ Zwei Ausgabeformate: json (Passthrough + _uns_topic) und sparkplugb (Umschlag mit Metrics-Array)
  • ✅ Überspringt leere Hierarchieebenen elegant
  • ✅ Pro-Nachricht-Zeitstempel (Unix-Millisekunden) bei SparkplugB-Ausgabe
{
"type": "UNS",
"config": {
"enterprise": "Acme",
"site": "Milan",
"area": "Assembly",
"line": "Line1",
"cell": "Station3",
"outputFormat": "json"
}
}
{
"type": "UNS",
"config": {
"enterprise": "Acme",
"site": "Milan",
"area": "Assembly",
"mappings": [
{ "inputKey": "machine_id", "topicLevel": "machine" },
{ "inputKey": "shift_id", "topicLevel": "shift" }
],
"outputFormat": "json"
}
}
{
"type": "UNS",
"config": {
"enterprise": "Acme",
"site": "Milan",
"area": "Packaging",
"line": "Line2",
"outputFormat": "sparkplugb"
}
}

Erforderlich. Das ISA-95-Top-Level-Unternehmen (Firmen-/Divisionsname).

{ "enterprise": "Acme" }

Erforderlich. Der physische Standort (Anlage, Werk, Gebäude).

{ "site": "Milan" }

Optional. Ein funktionaler Bereich innerhalb des Standorts.

{ "area": "Assembly" }

Optional. Eine Produktionslinie innerhalb des Bereichs.

{ "line": "Line1" }

Optional. Eine spezifische Zelle oder Arbeitsstation innerhalb der Linie.

{ "cell": "Station3" }

Optional. Ein Array von Payload-zu-Topic-Ebenen-Mappings. Für jedes Mapping sucht der Konnektor data[inputKey] und hängt <topicLevel>/<value> an das Topic an.

{
"mappings": [
{ "inputKey": "machine_id", "topicLevel": "machine" },
{ "inputKey": "shift_id", "topicLevel": "shift" }
]
}

Verhalten:

  • Sowohl inputKey als auch topicLevel sind für jeden Eintrag erforderlich
  • Fehlende Eingabeschlüssel werden über den Fehlerkanal gemeldet; die Topic-Ebene wird weggelassen
  • Nicht-String-Eingabewerte werden mit fmt.Sprintf("%v", ...) konvertiert

Erforderlich. Muss einer der folgenden sein:

  • json - Passthrough der ursprünglichen Payload mit eingefügtem _uns_topic
  • sparkplugb - Umschlag { topic, timestamp, metrics }, wobei metrics eine Liste von { name, value }-Paaren ist
{ "outputFormat": "sparkplugb" }

Topic-Ebenen werden mit / verbunden. Leere optionale Ebenen (Area, Line, Cell) werden übersprungen. Mappings erscheinen in der Reihenfolge, in der sie deklariert sind.

Beispiel:

Konfiguration:

{
"enterprise": "Acme",
"site": "Milan",
"area": "Assembly",
"line": "Line1",
"mappings": [
{ "inputKey": "machine_id", "topicLevel": "machine" }
]
}

Eingehende Payload:

{ "machine_id": "M-007", "temperature": 24.5 }

Resultierendes Topic:

Acme/Milan/Assembly/Line1/machine/M-007

Eingabe:

{
"machine_id": "M-007",
"temperature": 24.5,
"pressure": 5.2
}

Konfiguration:

{
"enterprise": "Acme",
"site": "Milan",
"area": "Assembly",
"line": "Line1",
"mappings": [
{ "inputKey": "machine_id", "topicLevel": "machine" }
],
"outputFormat": "json"
}

Ausgabe:

{
"machine_id": "M-007",
"temperature": 24.5,
"pressure": 5.2,
"_uns_topic": "Acme/Milan/Assembly/Line1/machine/M-007"
}

Gleiche Eingabe und Basiskonfiguration mit 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 }
]
}

Der timestamp ist Unix-Millisekunden zum Verarbeitungszeitpunkt. Beachten Sie, dass die Reihenfolge der metrics-Einträge nicht garantiert ist (Go-Map-Iteration ist ungeordnet).

DataPayload → UNS → DataPayload + _uns_topic (json) ODER { topic, timestamp, metrics } (sparkplugb)

1. Überbrückung von SPS-Daten zu MQTT mit ISA-95-Topic

Abschnitt betitelt „1. Überbrückung von SPS-Daten zu MQTT mit ISA-95-Topic“

Rohe SPS-Payloads in ein UNS-konformes MQTT-Topic umformen:

{
"type": "UNS",
"config": {
"enterprise": "Acme",
"site": "Detroit",
"area": "Stamping",
"line": "PressLine1",
"outputFormat": "json"
}
}

Ein nachgelagerter MqttV5Writer verwendet dann _uns_topic als Publish-Topic über einen Reshape-Schritt, der _uns_topic → MQTT-Topic abbildet.

2. SparkplugB-Veröffentlichung für Ignition oder HiveMQ

Abschnitt betitelt „2. SparkplugB-Veröffentlichung für Ignition oder HiveMQ“

Viele SCADA-Plattformen (Ignition, HiveMQ usw.) erwarten SparkplugB-Umschläge. Verwenden Sie die sparkplugb-Ausgabe zur Übereinstimmung:

{
"type": "UNS",
"config": {
"enterprise": "Acme",
"site": "Milan",
"area": "Bottling",
"line": "Filler1",
"outputFormat": "sparkplugb"
}
}

3. Mandantenübergreifendes Topic-Routing nach Kunde

Abschnitt betitelt „3. Mandantenübergreifendes Topic-Routing nach Kunde“

Dynamische Mappings verwenden, um die Kundenkennung in das Topic einzubetten:

{
"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"
}
}

Resultierendes Topic für {tenant_id: "T-1", site_id: "S-99", device_id: "D-42"}:

ConnectCo/Cloud/tenant/T-1/customer-site/S-99/device/D-42

Problem: Der nachgelagerte Verbraucher sieht den _uns_topic-Schlüssel nicht

Lösungen:

  1. Bestätigen, dass outputFormat: json gesetzt ist (der SparkplugB-Umschlag ersetzt die Payload vollständig; er hat topic auf der obersten Ebene anstelle von _uns_topic)
  2. Überprüfen, dass der UNS-Konnektor tatsächlich in der Pipeline ist — einen Log-Writer verwenden, um Zwischen-Payloads auszugeben

Problem: Ein konfiguriertes Mapping referenziert einen Schlüssel, der nicht in der Payload vorhanden ist

Lösungen:

  1. Überprüfen, ob der Eingabeschlüssel exakt mit der vorgelagerten Payload übereinstimmt (Groß-/Kleinschreibung beachten)
  2. Einen Reshape oder Transform vorgelagert verwenden, um sicherzustellen, dass erforderliche Schlüssel existieren
  3. Wenn der Schlüssel manchmal fehlt, erwägen Sie, ihn als Teil des statischen area/line/cell zu machen

Problem: Ausgabeumschlag ist { topic, timestamp, metrics: [] }

Lösungen:

  1. Das Metrics-Array wird aus den Schlüsseln der Eingabe-Payload erstellt. Eine leere Eingabe → leeres Metrics
  2. Überprüfen, dass der vorgelagerte Konnektor nicht-leere Payloads liefert

Topic hat unerwartete Schrägstriche oder leere Segmente

Abschnitt betitelt „Topic hat unerwartete Schrägstriche oder leere Segmente“

Problem: Topic enthält // oder Trailing-Slashes

Lösungen:

  1. Schließen Sie keine Schrägstriche in enterprise, site usw. ein — der Konnektor fügt sie ein. Eingebettete Schrägstriche erzeugen fehlerhafte Pfade
  2. Vermeiden Sie leere Strings für optionale Ebenen — lassen Sie das Feld ungesetzt, anstatt "" zu übergeben

Problem: Metrics-Reihenfolge ist zwischen Läufen inkonsistent

Lösungen:

  1. Go-Map-Iteration ist absichtlich ungeordnet. Wenn nachgelagerten Verbrauchern die Reihenfolge wichtig ist, vorgelagert über einen Reshape sortieren, der die Metrics in eine explizite Liste konvertiert, bevor sie diesen Konnektor erreichen
  2. Oder lassen Sie den Verbraucher beim Empfang nach name sortieren

Die Topic-Hierarchie wird von Dashboards, Historien und anderen Meddle-Pipelines konsumiert. Sobald veröffentlicht, behandeln Sie sie als stabile Schnittstelle — das nachträgliche Umbenennen von area oder line bricht nachgelagerte Verbraucher.

2. Statische Ebenen für stabile Hierarchien verwenden

Abschnitt betitelt „2. Statische Ebenen für stabile Hierarchien verwenden“

Bevorzugen Sie statische enterprise/site/area/line/cell gegenüber Mappings, wann immer die Hierarchie nicht von der Payload abhängt. Mappings sind mächtig, fügen aber eine Abhängigkeit von der Payload-Form hinzu.

3. JSON für Greenfield, SparkplugB für SCADA-Bridging bevorzugen

Abschnitt betitelt „3. JSON für Greenfield, SparkplugB für SCADA-Bridging bevorzugen“

json ist einfacher und funktioniert mit jedem nachgelagerten Verbraucher. Verwenden Sie sparkplugb nur, wenn Sie mit einem bestehenden SparkplugB-Ökosystem (Ignition, HiveMQ Edge usw.) interoperieren müssen.

Die meisten MQTT-Writer erfordern, dass das Topic explizit gesetzt wird. Nach dem UNS-Konnektor einen Reshape-Schritt verwenden, der _uns_topic in das Publish-Topic-Feld befördert:

... → UNS → Reshape (verschiebe _uns_topic → topic) → MqttV5Writer

Wählen Sie einmal ein ISA-95-Schema für die gesamte Organisation und wenden Sie es konsistent über alle Standorte und Linien an. Schema-Drift über Meddle-Bereitstellungen erzeugt inkompatible Topic-Bäume.

ModbusReader → Reshape → UNS → Reshape → MqttV5Writer
  1. ModbusReader: Holt rohe Registerdaten von einer SPS
  2. Reshape: Benennt Roh-Adressen in menschenlesbare Schlüssel um
  3. UNS: Dekoriert mit _uns_topic
  4. Reshape: Befördert _uns_topic in das MQTT-topic-Feld
  5. MqttV5Writer: Veröffentlicht in einem Cloud-Broker, organisiert unter dem UNS
OpcuaReader → Predictive → UNS (sparkplugb) → MqttV5Writer
  1. OpcuaReader: Aggregiert von mehreren OPC UA-Endpunkten
  2. Predictive: Fügt Trend/RUL/Gesundheitsscore hinzu
  3. UNS: Umhüllt die angereicherte Payload in einem SparkplugB-Umschlag
  4. MqttV5Writer: Veröffentlicht in einem SparkplugB-fähigen Broker (z.B. HiveMQ Edge → Ignition)
[Standort A Pipeline] ─┐
[Standort B Pipeline] ─┼─→ UNS → InfluxDb2Writer
[Standort C Pipeline] ─┘

Jede Standort-Pipeline kennzeichnet ihre Daten mit ihrer eigenen UNS-Konfiguration, sodass nachgelagerte Verbraucher allein anhand des Topics unterscheiden können.

  • MQTT v5 - UNS-dekorierte Payloads an einen Broker veröffentlichen
  • MQTT v3 - Legacy-MQTT-Veröffentlichung
  • Reshape - _uns_topic in das Topic-Feld des Writers befördern
  • Filter - Nach _uns_topic-Mustern filtern
  • Router - Verzweigung basierend auf der UNS-Hierarchie