Status eines Datenpunkts
Jeder gespeicherte Zeitreihenpunkt trägt neben Zeitstempel und Wert einen Status, das Feld
flags (32-Bit-Ganzzahl). Sein Aufbau folgt dem StatusCode aus OPC UA (OPC 10000-4) – und zwar
für alle Quellen, nicht nur für OPC UA: Ein über MQTT gelieferter, importierter oder in der
Plattform verdichteter Wert wird mit denselben Bits beschrieben. Woher ein Wert stammt, steht nicht
im Status, sondern an der Metrik (Herkunft MQTT, OPC_UA, IMPORT, …).
Der Wert 0 bedeutet Good, Rohwert und ist der Normalfall.
Bitbelegung
| Bits | Bedeutung | Werte |
|---|---|---|
| 30–31 | Severity | 00 Good, 01 Uncertain, 10 Bad |
| 28–29 | reserviert (OPC UA) | – |
| 16–27 | SubCode, die genaue Ursache | z. B. Bad_NoData = 0x809B0000 |
| 14–15 | SemanticsChanged / StructureChanged | – |
| 13 | Plattform, derzeit nicht belegt | – |
| 12 | Plattform: gelöscht (4096) | gesetzt = Punkt ist ungültig gemacht |
| 10–11 | InfoType | 01 = die Bits 0–9 sind belegt |
| 8–9 | Grenzwert | Low, High, Constant |
| 7 | Overflow | – |
| 5–6 | reserviert (OPC UA) | – |
| 4 | MultiValue | mehrere Werte am selben Zeitstempel |
| 3 | ExtraData | – |
| 2 | Partial | berechnet über ein Fenster mit Datenlücke |
| 0–1 | Herkunft des Werts | 00 Rohwert, 01 berechnet, 10 interpoliert |
Die Bits 12 und 13 sind im StatusCode reserviert und gehören der Plattform. Eine Datenquelle kann sie nicht setzen; das Bit 12 markiert einen über die Plattform gelöschten Punkt.
Was die Plattform heute setzt
| Situation | Status |
|---|---|
| Wert von einem OPC-UA-Server | der StatusCode, den der Server mitgeliefert hat – auch Uncertain und Bad |
Wert über MQTT mit Feld status | dieser StatusCode |
Wert über MQTT ohne status, Import, Formular | 0 (Good, Rohwert) |
| Punkt über einen Kommentar oder die Plattform gelöscht | Bit 12 gesetzt |
Verdichteter Wert beim Lesen (Plot-Aggregation, AVG:1h u. Ä.) | der Status des Zeitfensters, siehe unten |
Die Bits 12 und 13 einer Quelle verwirft die Plattform beim Eingang.
Ein Bad-Wert wird ohne Zahl gespeichert (value ist leer, NULL), mit seinem Status – auch wenn
der Server einen Ersatzwert mitliefert. Ein Bad-Wert ist nach OPC UA nicht verwendbar, und eine
Ersatzzahl ginge sonst als Messwert in Plot, Aggregate und Zustandsüberwachung ein. Uncertain-Werte
behalten ihre Zahl. Leer statt NaN, weil Mittelwert, Minimum und Maximum einen leeren Wert überspringen, während ein
einziges NaN das Ergebnis eines ganzen Zeitfensters zu NaN macht. Im Plot erscheint ein
solcher Punkt als Lücke; die Zustandsüberwachung und der Plattform-Adapter des hetida designers
lassen ihn aus, und der „letzte Wert“ einer Kennzahl ist der letzte Punkt mit Zahl.
Status verdichteter Werte
Fordert ein Leser die Werte verdichtet an (Plot-Aggregation, AVG:1h, MAX:15m …), berechnet die
Plattform jedes Zeitfenster nach OPC UA Part 13 (OPC 10000-13):
- Nur Good-Werte gehen in die Zahl ein. Uncertain-Werte werden wie Bad behandelt
(
TreatUncertainAsBad, Voreinstellung der Norm) und bleiben draußen – auch beim Einzelwert über einen Zeitraum (/aggregation, z. B. für Kennzahlen). - Der Status des Fensters ergibt sich aus der Zahl der Werte:
| Fenster enthält | Status |
|---|---|
| nur Good-Werte | 1 (Good, berechnet) |
| Good- und andere Werte | Uncertain_DataSubNormal, berechnet |
| keinen Good-Wert | Bad_NoData, ohne Wert |
| gar keinen Wert | keine Zeile |
- Bei
LASTmacht ein schlechter Wert das Fenster nur dann unsicher, wenn er nach dem gelieferten letzten Good-Wert liegt. - Partial (Bit 2) tragen
MIN,MAXundLAST, wenn das Fenster vom Abfragezeitraum angeschnitten wird oder noch nicht abgeschlossen ist.AVGsetzt Partial nach der Norm nie,SUMundMEDIAN(kein Gegenstück in der Norm) verhalten sich wieAVG.
Status verdichteter Werte aus OPC UA und MQTT
Ist an einem OPC-UA-Knoten oder an einer MQTT-Metrik eine Verdichtung eingestellt (z. B. Mittelwert je 15 Minuten), trägt jeder verdichtete Wert ebenfalls einen Status nach OPC UA Part 13. Auch hier gehen nur Good-Werte in die Zahl ein:
| Verdichtung | Status |
|---|---|
| Wert am Rasterpunkt | der Status des Werts, der zum Rasterbeginn gilt – Rohwert, wenn er genau dort liegt, sonst interpoliert; ist er Bad, ein Punkt ohne Wert |
| Mittelwert | zeitgewichtet: nur Good-Zeit → Good, berechnet; mit schlechten Zeiten → Uncertain_DataSubNormal (die schlechte Zeit zählt nicht mit); keine Good-Zeit → Bad_NoData ohne Wert |
| Minimum, Maximum | nur Good-Werte → Good, berechnet; daneben andere → Uncertain_DataSubNormal; keiner Good → Bad_NoData ohne Wert; ein Fenster ohne eigenen Wert entfällt |
| Bei Änderung | der Status des Rohwerts; ein Statuswechsel erzeugt einen Punkt wie ein Wertsprung |
Verdichtet der OPC-UA-Server selbst, übernimmt die Plattform dessen Status; ein Fenster, für das der Server Bad meldet, ergibt keinen Punkt – der Server unterscheidet dort nicht zwischen „keine Daten“ und „nur schlechte Daten“.
Bis HET-2184 stand „gelöscht“ im Bit 2 (Wert 4). Das Bit heißt im StatusCode Partial; die
Plattform hat die gespeicherten Punkte beim Update auf Bit 12 umgestellt. Ein Wert 4 bedeutet
seitdem „berechnet über ein lückenhaftes Fenster“, nicht mehr „gelöscht“.
Status auslesen
Bei Severity Bad ist das oberste Bit gesetzt, die Zahl ist dann negativ. Die Severity liest man
deshalb über eine Verschiebung, nicht über eine Maske mit 0xC0000000 – die passt in SQL nicht in
einen integer.
SELECT ts_timestamp,
value,
CASE (flags >> 30) & 3
WHEN 0 THEN 'Good'
WHEN 1 THEN 'Uncertain'
WHEN 2 THEN 'Bad'
END AS severity,
(flags & 3) = 1 AS berechnet,
(flags & 4) <> 0 AS partial
FROM timeseries
WHERE metric_id = '…'
AND (flags & 4096) = 0; -- gelöschte Punkte ausblenden
def status(flags: int) -> dict:
flags &= 0xFFFFFFFF # negative int32 als vorzeichenlose 32 Bit lesen
return {
"severity": ("Good", "Uncertain", "Bad", "reserviert")[flags >> 30],
"sub_code": (flags >> 16) & 0xFFF,
"herkunft": ("Rohwert", "berechnet", "interpoliert", "reserviert")[flags & 3],
"partial": bool(flags & 4),
"geloescht": bool(flags & 4096),
}