feat(application): integrate measured-load ingestion training and planner source

This commit is contained in:
ENELIX Agent
2026-10-02 21:00:05 +00:00
parent 44362e4bf5
commit fc024fcfba
96 changed files with 8933 additions and 0 deletions
@@ -0,0 +1,45 @@
import copy
from datetime import timedelta
import tempfile
from pathlib import Path
import unittest
from netplan_v4.receiver_contract import control_context, provenance
from netplan_v4.store import PlannerStore
from netplan_v4.service import ingest, run_once, status
from test_v4 import inputs, AT, AID
class ReceiverContractTest(unittest.TestCase):
def setUp(self):
self.tmp=tempfile.TemporaryDirectory();self.s=PlannerStore(str(Path(self.tmp.name)/'qa.sqlite'))
op,fc,tar=inputs()
for kind,value in zip(('operation','forecast','tariffs'),(op,fc,tar)):ingest(self.s,AID,kind,value,AT)
def tearDown(self):self.s.close();self.tmp.cleanup()
def test_published_plan_and_response_bound_to_same_plant(self):
p=run_once(self.s,AT);r=status(self.s,AID,AT)
self.assertEqual(p['installationId'],AID);self.assertEqual(r['installationId'],AID)
self.assertEqual(r['receiverProtocolVersion'],1);self.assertTrue(r['fresh'])
self.assertFalse(r['liveEnabled']);self.assertFalse(p['liveEnabled'])
self.assertEqual(p['inputRefs'],{'operation':'op1','forecast':'fc1','tariffs':'tar1'})
def test_control_context_tracks_stable_policy_not_volatile_soc(self):
op=inputs()[0];a=control_context(op);op['batteries'][0]['socPercent']=80.;op['gridW']=1000.
self.assertEqual(a,control_context(op));op['batteries'][0]['minSocPercent']=15.
self.assertNotEqual(a,control_context(op))
def test_context_is_snapshot_not_reference_to_future_changes(self):
op,fc,tar=inputs();p=provenance({'operation':op,'forecast':fc,'tariffs':tar})
op['limits']['importW']=100.;self.assertEqual(p['controlContext']['limits']['importW'],10000.)
def test_no_plan_for_another_installation(self):
run_once(self.s,AT);r=status(self.s,'00000000-0000-4000-8000-000000000002',AT)
self.assertIsNone(r['plan']);self.assertFalse(r['fresh'])
def test_shadow_ack_is_not_execution(self):
p=run_once(self.s,AT)
self.s.acknowledge(AID,p['planId'],p['configRevision'],AT,step=p['points'][0]['time'],status='shadow_seen')
self.assertEqual(status(self.s,AID,AT)['acknowledgement']['status'],'shadow_seen')
with self.assertRaises(ValueError):self.s.acknowledge(AID,p['planId'],p['configRevision'],AT,status='applied')
def test_new_configuration_invalidates_fresh_flag(self):
run_once(self.s,AT);self.s.save_settings(AID,{'family':'23'},0,AT+timedelta(seconds=1))
self.assertFalse(status(self.s,AID,AT+timedelta(seconds=2))['fresh'])
def test_rearm_default_matches_php_contract(self):
op=inputs()[0];op['batteries'][0]['rearmSocPercent']=None
self.assertEqual(control_context(op)['batteries']['b']['rearmSocPercent'],10.)
if __name__=='__main__':unittest.main()