#!/usr/bin/env python3 """SAFETY-87: at one inverter, is the peak guard byte-identical to before? python3 tools/safety87_identity.py # prints one digest per scenario safety84_identity's harness (the REAL Controller on a P1 meter, every claim and the guard's decision hashed) with the battery under, crossing and above the peak reserve, the house over the aim, in a maintenance charge and HOLD, and under a month peak banked at 4000 W. Uses only the API release/1.0 b8ddfd7 has, so the same file runs on both trees; test_safety87.py holds this tree to the digests printed there. """ import os import sys from datetime import timedelta sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) import opt11_identity as rig # noqa: E402 import safety84_identity as s84 # noqa: E402 DUR = 1800.0 def crossing(u, t): """30 % falling 1 % a minute to 12 %: crosses the 20 % reserve.""" return max(12.0, 30.0 - t / 60.0) def store(phase=None): """In `phase` from the first cycle, or idle with a cycle done yesterday.""" s = rig._Store() if phase is None: s.data["last_completed"] = (rig.BASE - timedelta(days=1)).isoformat() else: s.data["phase"], s.data["phase_started"] = phase, rig.BASE.isoformat() return s SCENARIOS = { "n1_under": dict(soc=lambda u, t: 15.0, load=lambda t: 5500.0), "n1_crossing": dict(soc=crossing, load=lambda t: 5000.0), "n1_above": dict(soc=lambda u, t: 60.0, load=lambda t: 5500.0), "n1_under_banked": dict(soc=lambda u, t: 15.0, load=lambda t: 6000.0, month_peak=4000.0), "n1_charge_low": dict(soc=lambda u, t: 11.0, load=lambda t: 5000.0, store="charge"), "n1_hold": dict(soc=lambda u, t: 99.0, load=lambda t: 5500.0, store="hold", cheap=False, extra={"maintenance_hold_min": 600}), } def digests() -> dict: out = {} for k, v in SCENARIOS.items(): v = dict(v) st = store(v.pop("store", None)) out[k] = s84.digest(s84.run(1, dur=DUR, store=st, **v)[0]) return out if __name__ == "__main__": for k, v in digests().items(): print(f"{k} {v}")