🎉 Initial commit
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
"""OPC DA quality decode tables (SPECIFICATION §7).
|
||||
|
||||
Quality is a 16-bit field: bits 7–6 of the low byte give the class
|
||||
(0xC0 GOOD / 0x40 UNCERTAIN / 0x00 BAD); bits 5–0 are the substatus. Every
|
||||
quality event must carry the raw hex code **and** the decoded name.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
from enum import Enum
|
||||
|
||||
|
||||
class QualityClass(str, Enum):
|
||||
GOOD = "GOOD"
|
||||
UNCERTAIN = "UNCERTAIN"
|
||||
BAD = "BAD"
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class QualityInfo:
|
||||
"""Decoded quality for one read (SPECIFICATION §7)."""
|
||||
|
||||
raw: int
|
||||
name: str
|
||||
quality_class: QualityClass
|
||||
|
||||
@property
|
||||
def raw_hex(self) -> str:
|
||||
"""Raw code formatted for events, e.g. ``0x18``."""
|
||||
return f"0x{self.raw & 0xFF:02X}"
|
||||
|
||||
|
||||
# Codes from SPECIFICATION §7.
|
||||
# TODO(Phase 1): complete the remaining DA 2.05a UNCERTAIN subcodes in this
|
||||
# table — they decode by the same scheme (spec §7 closing note).
|
||||
_QUALITY_NAMES: dict[int, str] = {
|
||||
0xC0: "GOOD",
|
||||
0xD8: "GOOD_LOCAL_OVERRIDE",
|
||||
0x40: "UNCERTAIN",
|
||||
0x44: "UNCERTAIN_LAST_USABLE",
|
||||
0x00: "BAD",
|
||||
0x04: "BAD_CONFIG_ERROR",
|
||||
0x08: "BAD_NOT_CONNECTED",
|
||||
0x0C: "BAD_DEVICE_FAILURE",
|
||||
0x10: "BAD_SENSOR_FAILURE",
|
||||
0x14: "BAD_LAST_KNOWN_VALUE",
|
||||
0x18: "BAD_COMM_FAILURE",
|
||||
0x1C: "BAD_OUT_OF_SERVICE",
|
||||
}
|
||||
|
||||
|
||||
def _class_of(low: int) -> QualityClass:
|
||||
"""Class comes from bits 7–6 of the low byte."""
|
||||
bits = low & 0xC0
|
||||
if bits == 0xC0:
|
||||
return QualityClass.GOOD
|
||||
if bits == 0x40:
|
||||
return QualityClass.UNCERTAIN
|
||||
return QualityClass.BAD
|
||||
|
||||
|
||||
def decode_quality(raw: int) -> QualityInfo:
|
||||
"""Decode a raw OPC DA quality word into a named :class:`QualityInfo`."""
|
||||
low = raw & 0xFF
|
||||
name = _QUALITY_NAMES.get(low)
|
||||
if name is None:
|
||||
name = f"{_class_of(low).value}_0x{low:02X}"
|
||||
return QualityInfo(raw=raw, name=name, quality_class=_class_of(low))
|
||||
Reference in New Issue
Block a user