Sparkplug B Driver — Technische Referenz
Der Sparkplug B-Treiber von PlantPulse Edge ist eine native Java-Implementierung eines MQTT 3.1.1-Clients + Sparkplug B Payload-Protobuf-Decoders. Es bestehen keine Abhängigkeiten zu externen paho/tahu-Jars, sodass er bei Auslieferung als Single-Jar ohne zusätzliche Konfiguration funktioniert.
Quelle: plantpulse.driver.protocol.sparkplug.*
| Klasse | Verantwortung |
|---|---|
SparkplugBDriver | ProtocolDriver-Implementierung — connect/read/write/close, Alias- und Metric-Cache |
SparkplugMqttClient | MQTT 3.1.1 self-contained Client (CONNECT / SUBSCRIBE / PUBLISH / PINGREQ) |
SparkplugBPayload | Sparkplug B Payload Protobuf Wire-Encoder/-Decoder (varint / fixed32 / fixed64 / length-delim) |
Ablauf
broker ──PUBLISH──> SparkplugMqttClient ──onMessage(topic, payload)──> SparkplugBDriver
│
├─ topic 파싱 (spBv1.0/G/<msgType>/edge[/dev])
├─ Payload 디코드 → metric list
├─ NBIRTH/DBIRTH 시 alias→name 등록
└─ metricCache[edge[/dev]/name] = formatValue()
PlantPulse 폴러 ──read(addr)──> metricCache[addr] (in-memory hit, 외부 RTT 없음)
write(addr, value) ──encode──> NCMD/DCMD publish → broker
Optionen
| Schlüssel | Standardwert | Beschreibung |
|---|---|---|
group-id | default | Sparkplug Group ID. Wird beim Subscribe als Teil des Wildcards verwendet. Nachrichten mit abweichender Group werden vom Treiber verworfen. |
edge-node-id | + | Edge-Node-Filter. Bei + alle Nodes innerhalb der Group. |
keep-alive | 60 | MQTT-Keep-Alive in Sekunden. Bei 0 ist Ping deaktiviert. |
connect-timeout | 5000 | TCP-Verbindungs-Timeout in ms. |
Das Subscribe-Topic wird nach dem Muster spBv1.0/<group-id>/+/<edge-node-id>/# aufgebaut (+ ist message_type, # das Wildcard für den Device-Teil).
Sparkplug-Datatype-Zuordnung
SparkplugBPayload.Metric.formatValue() konvertiert je nach Wire-Datatype in einen passenden String.
| Sparkplug datatype | Speicherfeld | format-Ergebnis |
|---|---|---|
| Int8 / Int16 / Int32 | intValue (varint) | signed int (Integer.toString) |
| UInt8 / UInt16 / UInt32 | intValue | unsigned (& 0xFFFFFFFFL) |
| Int64 / DateTime | longValue | signed long |
| UInt64 | longValue | Long.toUnsignedString |
| Float | floatValue (fixed32) | Float.toString |
| Double | doubleValue (fixed64) | Double.toString |
| Boolean | booleanValue (varint) | "true" / "false" |
| String / Text / UUID | stringValue | UTF-8 unverändert |
| Bytes / File | bytesValue | Hex-String |
is_null=true-Metriken werden als leerer String ausgewertet.
Alias-Lernen über NBIRTH / DBIRTH
Kern der Sparkplug-Effizienz: BIRTH überträgt Paare aus Metric-Name und Alias → anschließend senden NDATA/DDATA nur noch den Alias.
1) edge1 의 NBIRTH:
metric { name="Temperature" alias=7 datatype=Float float_value=23.5 }
→ driver: aliasByPrefix.put("edge1", {7 → "Temperature"})
metricCache.put("edge1/Temperature", "23.5")
2) edge1 의 NDATA (이름 생략):
metric { alias=7 datatype=Float float_value=24.1 }
→ driver: name 없음 → alias=7 → "Temperature"
metricCache.put("edge1/Temperature", "24.1")
Trifft NDATA ein, ohne dass zuvor NBIRTH empfangen wurde, schlägt das Alias-Lookup fehl und die Nachricht wird verworfen (keine Cache-Aktualisierung). Es wird gewartet, bis der Publisher nach einem Broker-Neustart NBIRTH erneut veröffentlicht.
NDEATH / DDEATH
Wird ein Edge Node oder ein Device beendet, veröffentlicht der Broker NDEATH/DDEATH über LWT (Last Will and Testament). Der Treiber leert lediglich die Alias-Map, der Cache bleibt erhalten — der letzte bekannte Wert wird damit als Polling-Ergebnis bewahrt (der Monitor von PlantPulse zeigt die lastValue-UI unverändert an).
NCMD / DCMD senden
write(ProtocolAddress) verzweigt automatisch anhand der Anzahl der Address-Segmente.
| address | publish topic | Hinweis |
|---|---|---|
edge1/SetPoint | spBv1.0/<group>/NCMD/edge1 | Befehl auf Node-Ebene |
edge1/dev1/SetPoint | spBv1.0/<group>/DCMD/edge1/dev1 | Befehl auf Device-Ebene |
Phase 1 veröffentlicht alle Befehle mit dem Sparkplug-Datatype String — die Empfängerseite (Tahu / Ignition) konvertiert sie in den für die Metric definierten Datatype und verarbeitet sie entsprechend.
Einschränkungen und geplante Arbeiten
- TLS / Authentifizierung — derzeit nur Klartext + anonym. Optionen für username/password bzw. mTLS folgen.
- QoS — alle publish/subscribe mit QoS 0. Sparkplug empfiehlt QoS 1 nur für NBIRTH, NDATA/DDATA mit QoS 0.
- DataSet-/Template-Metriken — auf dem Wire als Raw Bytes erhalten (
datasetRaw/templateRaw), decode/format jedoch nicht implementiert. - Properties / Metadata — nur Erhalt als Raw Bytes, decode nicht implementiert.
- bdSeq — bdSeq-Abgleich aus NBIRTH / NDEATH-Prüfung nicht implementiert.
Speicherort der Tests
test/java/plantpulse/driver/protocol/sparkplug/SparkplugBDriverTest.java — 27 Tests.
- Initialzustand / Options-Parsing
- Sicheres read/write im nicht verbundenen Zustand
- BIRTH-Alias-Registrierung, Alias-only-Auflösung bei NDATA, Blockieren bei abweichender Group, Alias-Bereinigung bei DEATH
- Korrektheit von formatValue je Datatype (Int32 / Float / Double / Boolean / String / UInt32 / UInt64 / is_null)
- Payload encode→decode Round-Trip
- MQTT Remaining Length / utf8-Wire-Utility
- safeId clientId-Sanitization