Einen Unified Namespace vertrauenswürdig machen – eine dreiteilige Serie: 1 · UNS oder Datensumpf · 2 · Data Contracts · 3 · Asset Models.
Teil 1 hat dargelegt, dass ein Unified Namespace ohne durchgesetzten Standard in einen Datensumpf abdriftet – und dass die Kur ein Standard ist, den du auch durchsetzt. In diesem Beitrag geht es darum, wo dieser Standard für die Daten lebt, die durch den Namespace fliessen: in den Contracts, die regeln, was jede Nachricht sagen darf.
„Der UNS definiert keine Data Governance.“ Irgendeine Variante dieses Satzes lese ich ungefähr einmal pro Woche – in einem LinkedIn-Kommentar, einer Konferenz-Q&A oder einem Vendor-Deck, stets als Schlussargument vorgetragen. Er stimmt, und er geht am Punkt vorbei. Der Unified Namespace ist ein Blueprint auf Basis weniger schlanker Best Practices – bewusst klein, bewusst unfertig. Der Blueprint endet dort, wo deine Domäne beginnt. Jedes Team, das tatsächlich einen aufgebaut hat, hat Data Contracts obendrauf geschrieben. Statt also noch einmal zu streiten, ob der Blueprint mehr tun sollte, lass uns Notizen vergleichen, was wir darüber bauen.
Zwei Contracts, zwei Aufgaben
Damit sich eine Flotte von Verpackungslinien wie eine Flotte verhält, müssen zwei verschiedene Einigungen vorliegen. Die meisten Projekte nageln nur eine davon fest – weshalb aus wir haben einen UNS so oft wir haben einen Broker wird.
Ein schematischer Contract ist die Einigung über die Form. Für eine gegebene Nachricht: welche Felder vorhanden sind, welche Typen sie haben, welche Pflicht sind, wie serialisiert wird. Das ist die Ebene, für die JSON Schema, Avro, Protobuf und Pydantic existieren. Sie hält die Leitung sauber – ein Producer kann kein Feld stillschweigend umbenennen, ein Consumer bekommt keinen Müll untergeschoben, den er nicht ablehnen kann.
Ein semantischer Contract ist die Einigung über die Bedeutung. Was heisst es, eine Verpackungslinien-Zustandsnachricht zu sein? In welchen realen Zuständen darf die Linie sein? Ist die Linie running, während der vorgelagerte Puffer leer ist und die Maschine hält? Zählt ein Operator, der einen Barcode scannt, als Rüstaktivität oder als eigenes Ereignis? Wenn zwei Standorte ein state-Feld veröffentlichen, das beide gegen dasselbe Schema validieren – reden sie wirklich über dasselbe?
Schematische Contracts lösen mechanische Probleme. Semantische Contracts lösen organisatorische – und sie sind die schwierigeren von beiden, weil die Antwort von Einigungen zwischen Menschen abhängt, nicht von einem Parser.
Keine Ebene ersetzt die andere. Beide Aufgaben mit demselben Artefakt erledigen zu wollen ist der Punkt, an dem die meisten Data-Model-Diskussionen hängen bleiben – meist als JSON Schema mit einem 40-zeiligen description-Feld, das niemand liest.
Die schematische Ebene: den Payload standardisieren
Die meisten Unified Namespaces bauen auf event-getriebenem MQTT auf, und MQTT ist bewusst gleichgültig gegenüber dem, was du hindurchschickst. Der Broker leitet einen Klumpen Bytes an ein Topic weiter und schaut nie hinein. Diese Gleichgültigkeit macht ihn schnell und entkoppelt – und sie ist auch der Grund, warum, sich selbst überlassen, jeder Producer seine eigene Payload-Form erfindet. Eine Linie veröffentlicht 142, eine andere {"value": "142"}, eine dritte {"val": 142, "t": 1699...}. Alle drei landen anstandslos im Namespace. Jeder Consumer, der sie lesen will, braucht dann massgeschneidertes, defensives Parsing für jede Quelle, und jeder Producer kann morgen ein Feld umbenennen, ohne dass irgendetwas ihn aufhält.
Die Lösung ist ein Payload-Standard: ein kleiner Satz von Payload-Typen mit jeweils fester Form, den alles veröffentlicht und alles liest. Eines unserer Arbeitspferde ist die Metric – der kanonische Payload für einen einzelnen Wert, der gemeldet wird, sobald er sich ändert: der Wert selbst plus die Metadaten, die man fast immer drumherum braucht, etwa der Zeitstempel der Messung. Sobald „ein Durchsatzwert“ immer eine Metric mit derselben Form ist, kann jeder Consumer jede Metric von jeder Linie lesen, ohne zu wissen, wer sie erzeugt hat, oder einen Parser dafür zu schreiben. Die Form ist der Contract.
Wie du diese Form im Code durchsetzt, ist eine Implementierungsfrage. franzmq ist unsere – die Open-Source-Bibliothek, die wir quer durch unsere PREKIT-Projekte einsetzen. Sie ist eine dünne Schicht über paho-mqtt: typisierte Payloads als Python-Dataclasses, klassenbasierte Topics, die hierarchische Pfade zusammensetzen, optionales ISA-95-Topic-Modeling und ein publish, das sich weigert, irgendetwas zu senden, das nicht zum deklarierten Payload-Typ passt. Ein konformes publish sieht so aus:
from franzmq import Client, Topic, Metric
client = Client.autocreate_and_connect(client_id="line-7-edge")
topic = Topic(payload_type=Metric, context=("line-7", "throughput"))
client.publish(topic, Metric(value=142.0))
Die Payload-Form wird von der Metric-Dataclass erzwungen, die Topic-Struktur von der Topic-Klasse, und das Versionspräfix lebt an einer Stelle. Es gibt keinen Selbst-Encodier-Pfad, in den ein Integrator abdriften könnte – der einfache Weg zu veröffentlichen ist zugleich der richtige.
Die Bibliothek ist nur ein Weg dorthin. Worauf du dich auch festlegst – franzmq, ein internes Paket, etwas von der Stange –, das Ziel ist dasselbe: einen wohlgeformten Payload zum Weg des geringsten Widerstands machen. Sobald die Leitung sauber ist, wird die schwierigere Ebene handhabbar.
Die semantische Ebene ist, wo die Arbeit liegt
Eine typisierte Metric hält die Leitung sauber, aber sie sagt dir nicht, was eine Verpackungslinie ist. Metric(value="running") ist wohlgeformt und trotzdem bedeutungslos: Nichts sagt, welche Werte erlaubt sind, was sie bedeuten oder ob zwei Standorte sich darüber einig sind. Diese Einigung ist der semantische Contract, und auch sie muss als Code aufgeschrieben werden.
Der verlockende Schritt ist, für jedes Konzept einen neuen Payload-Typ und ein neues Topic zu prägen – eine PackagingLineState-Nachricht auf einem eigenen .../packaging-line/state-Topic. Wir haben gelernt, es nicht zu tun. Es fragmentiert den Namespace: Jedes Konzept wird zu einer massgeschneiderten Topic-Form, die Consumer vorab kennen müssen, und Discovery verkommt zum Durchlesen irgendeiner Topic-Liste. Also reitet die Bedeutung stattdessen auf dem Standard-Payload. Alles fliesst weiterhin als Metric auf dem Standard-Topic des Signals; der semantische Contract schränkt ein, welchen Wert diese Metric haben darf, und das Signal wird mit dem Contract markiert, den es implementiert:
from enum import StrEnum
from typing import ClassVar
class LineState(StrEnum):
RUNNING = "running"
IDLE = "idle"
STOPPED = "stopped"
FAULTED = "faulted"
CHANGEOVER = "changeover"
class LineStateContract:
"""Semantischer Contract: Der Zustand einer Linie ist einer aus LineState,
getragen als Wert einer standardmässigen Metric – kein neuer Payload-Typ,
kein neues Topic."""
name: ClassVar[str] = "LineState"
expected_data_type: ClassVar[str] = "string"
Ein Signal, das den Linienzustand trägt, deklariert implements_contract = "LineState". Seine Werte werden dann als gewöhnliche Metrics auf dem gewöhnlichen Topic des Signals veröffentlicht – client.publish(signal.topic, Metric(value=LineState.IDLE)), wobei signal.topic eine normale alp/v1/_Metric/...-Adresse ist und kein eigens für dieses Konzept geprägtes. Ein Producer, der "runnnig" (Tippfehler), "production" (anderes Vokabular) oder "running fast" (ein reicherer Zustand, für den ein Consumer nicht gebaut war) senden will, schreibt jetzt gegen den Contract, das Enum und die Validierung der Plattform – nicht gegen einen offenen str.
Und Consumer raten nie ein Topic aus einem Konzeptnamen. Sie fragen die Plattform, welche Signale den Contract implementieren – GET /signals/?implements_contract=LineState – und abonnieren die zurückgegebenen Topics. Die Bedeutung ist festgenagelt, der Namespace bleibt einheitlich, und Discovery ist eine Abfrage statt Herrschaftswissen. (PREKIT liefert Contracts genau so aus – einen Liveness-Heartbeat, einen reicheren JSON-EdgeExecutionContext für Shopfloor-Ereignisse – und Projekte ergänzen ihre eigenen.) Wenn ein Contract sich weiterentwickeln muss, änderst du ihn an einer Stelle, und jedes Signal, das ihn implementiert, entwickelt sich mit.
Der schematische Teil davon ist über Kunden hinweg wiederverwendbar. Der semantische Teil wächst fast immer um kundenspezifische Erweiterungen, weil das, was als Verpackungslinie zählt – oder als CNC-Zelle, als Beschichtungslauf, als Qualitätsurteil –, teils universell und teils spezifisch für einen bestimmten Betrieb ist. Das ist zu erwarten. Es geht nicht darum, die semantischen Contracts jedes Kunden identisch zu machen. Es geht darum, sicherzustellen, dass sie existieren – als Code, im Tooling, auf dem Weg des geringsten Widerstands.
Durchsetzung: Code schlägt Geschriebenes
Die meisten Data-Model-Standards in der Industrie leben als PDFs. Sie liegen im Projekt-SharePoint, werden im Kickoff zitiert und hören ab der zweiten Integration leise auf, tragend zu sein. Nicht weil jemand nachlässig wäre – sondern weil ein PDF zu befolgen Aufwand erfordert, während es nicht zu befolgen automatisch passiert. Der Weg des geringsten Widerstands gewinnt jedes Mal.
Die Standards, die in der Produktion überleben, sind die, die in Werkzeuge eingebaut sind. Die, bei denen den Standard zu nutzen einfacher ist als ihn nicht zu nutzen, weil die Bibliothek das Encoding, die Validierung und die Topic-Konstruktion für dich übernimmt und die Alternative ist, alles von Hand zu bauen. Ein konformes publish ist eine Zeile; ein nicht-konformes ist ein Umweg. Drift wird zur schwierigeren Option.
Das ist der praktische Test für jedes Data-Model-Dokument, das du schreibst: Ist für den Techniker, der das in sechs Monaten in die Hand nimmt, der Weg des geringsten Widerstands der, der dem Standard folgt? Wenn nicht, werden die Leute unter Druck einen Weg am Standard vorbei finden.
Was Contracts noch nicht abdecken
Contracts regeln, was eine Nachricht sagen darf. Aber eine Nachricht wird von etwas veröffentlicht – einer Maschine, einer Linie, einer Zelle –, und die nächste Frage ist, was dieses Etwas ist und was es verpflichtet preiszugeben, wenn es behauptet, von dieser Art zu sein. Das ist das Asset Model: die Ebene über den Nachrichten-Contracts, auf der eine „Maschine“ zu einem benannten Typ wird, den ein Asset deklariert und die Plattform validiert. Und genau dort kommen KPIs her, denen man trauen kann. Das ist Teil 3.
Einen Unified Namespace vertrauenswürdig machen – eine dreiteilige Serie: 1 · UNS oder Datensumpf · 2 · Data Contracts · 3 · Asset Models.
Für mehr Hintergrund siehe unseren früheren Beitrag Was genau ist ein Unified Namespace? und die Lücke zwischen ERP und Shopfloor überbrücken.
