Добавить множитель и IQ в Python-тренды
This commit is contained in:
@@ -54,6 +54,8 @@ class TrendSignal:
|
||||
deviceType: int = 7
|
||||
device: int = 13
|
||||
byteOffset: int = 0
|
||||
multiplier: float = 1.0
|
||||
iq: int = 0
|
||||
extended: bool = True
|
||||
|
||||
@classmethod
|
||||
@@ -72,9 +74,11 @@ class TrendSignal:
|
||||
for field in ("id", "name", "source", "address", "color", "valueType"):
|
||||
if type(getattr(self, field)) is not str:
|
||||
raise ValueError(f"{field} must be a string")
|
||||
for field in ("order", "deviceType", "device", "byteOffset"):
|
||||
for field in ("order", "deviceType", "device", "byteOffset", "iq"):
|
||||
if type(getattr(self, field)) is not int:
|
||||
raise ValueError(f"{field} must be an integer")
|
||||
if type(self.multiplier) not in (int, float) or not math.isfinite(self.multiplier):
|
||||
raise ValueError("multiplier must be a finite number")
|
||||
if type(self.visible) is not bool or type(self.extended) is not bool:
|
||||
raise ValueError("Visibility and CAN format must be boolean")
|
||||
if not self.id.strip() or len(self.id) > 80 or not 1 <= self.order <= 9999:
|
||||
@@ -98,6 +102,11 @@ class TrendSignal:
|
||||
raise ValueError("Invalid ProtoCAN device")
|
||||
if self.source == "CAN_RAW" and not 0 <= self.byteOffset <= 6:
|
||||
raise ValueError("Word offset must be 0..6")
|
||||
if not 0 <= self.iq <= 30:
|
||||
raise ValueError("IQ must be 0..30")
|
||||
|
||||
def display_value(self, raw_value: float) -> float:
|
||||
return raw_value * self.multiplier / (1 << self.iq)
|
||||
|
||||
def validate_settings(settings: Mapping[str, list[TrendSignal]]) -> None:
|
||||
ids = set()
|
||||
@@ -132,14 +141,15 @@ def decode_settings(text: str) -> dict[str, list[TrendSignal]]:
|
||||
raise ValueError("profiles must be an object")
|
||||
result = {}
|
||||
fields = set(TrendSignal.__dataclass_fields__)
|
||||
required_fields = fields - {"multiplier", "iq"}
|
||||
for profile, items in profiles.items():
|
||||
if not isinstance(items, list) or len(items) > MAX_SIGNALS:
|
||||
raise ValueError("Expected up to 64 signals")
|
||||
signals = []
|
||||
for item in items:
|
||||
if not isinstance(item, dict) or not fields <= item.keys():
|
||||
if not isinstance(item, dict) or not required_fields <= item.keys():
|
||||
raise ValueError("Missing signal fields")
|
||||
signals.append(TrendSignal(**{key: item[key] for key in fields}))
|
||||
signals.append(TrendSignal(**{key: item[key] for key in fields if key in item}))
|
||||
result[profile] = signals
|
||||
validate_settings(result)
|
||||
return result
|
||||
|
||||
@@ -14,6 +14,16 @@ FIXTURE = Path(__file__).resolve().parents[2] / "c/set-protocol/tests/fixtures/t
|
||||
|
||||
|
||||
class TrendTests(unittest.TestCase):
|
||||
def test_multiplier_and_iq_scale_display_and_old_json_defaults(self):
|
||||
signal = TrendSignal("scaled", multiplier=2.5, iq=3)
|
||||
self.assertEqual(10.0, signal.display_value(32.0))
|
||||
signal.validate("TMS2812")
|
||||
payload = json.loads(encode_settings({"TMS2812": [signal]}))
|
||||
payload["profiles"]["TMS2812"][0].pop("multiplier")
|
||||
payload["profiles"]["TMS2812"][0].pop("iq")
|
||||
restored = decode_settings(json.dumps(payload))["TMS2812"][0]
|
||||
self.assertEqual((1.0, 0), (restored.multiplier, restored.iq))
|
||||
|
||||
def test_shared_kotlin_fixture_and_round_trip(self):
|
||||
settings = decode_settings(FIXTURE.read_text(encoding="utf-8"))
|
||||
self.assertEqual(5, sum(map(len, settings.values())))
|
||||
|
||||
Reference in New Issue
Block a user