feat(manager): add gated short-lease V4 control trial and local fallback

This commit is contained in:
ENELIX Agent
2026-10-02 14:01:41 +00:00
parent b5c1fa6be2
commit 9394fe3eb1
10 changed files with 918 additions and 4 deletions
@@ -0,0 +1,30 @@
<?php
// Included only by the offline harness. No device or network calls.
[$m,$b,$s]=fixture();start($m,$s);$m->op['gridW']=3000.;tick($m);
check(end($b->writes)===-3000,'discharge sign and current net load compensation');
$b->measurements['Ladezustand']=15.;$b->watchdog();check(end($b->writes)===0,'reserve stops an active discharge independently');
[$m,$b,$s]=fixture();start($m,$s);$m->op['gridW']=3000.;tick($m);$b->state['HystereseAktiv']=true;$b->watchdog();
check(end($b->writes)===0,'local discharge hysteresis overrides previous command');
[$m,$b,$s]=fixture();start($m,$s);$m->op['gridW']=3000.;tick($m);$b->measurements['MaxEntladen']=500.;$b->watchdog();
check(end($b->writes)===-500,'discharge availability decrease is immediate');
[$m,$b,$s]=fixture();start($m,$s);tick($m);$b->measurements['Ladezustand']=2.;$b->watchdog();
check(!$b->active()&&end($b->writes)===0,'technical minimum violation aborts rather than inventing SOC');
[$m,$b,$s]=fixture();$s['maxChargeW']=1000.;start($m,$s);tick($m);
check(end($b->writes)===1000,'independent local cap can be stricter than server cap');
[$m,$b,$s]=fixture();start($m,$s);$b->attrs['LetzterSollwert']=500;$b->attrs['AenderungFreigabeAb']=$clock+4;tick($m);
check(end($b->writes)===500,'switch lock holds power increase');
advance(5);tick($m);check(end($b->writes)===3000,'power increase only after switching lock');
[$m,$b,$s]=fixture();start($m,$s);$b->attrs['LetzterSollwert']=500;$b->attrs['AenderungFreigabeAb']=$clock+4;$m->op['gridW']=3000.;tick($m);
check(end($b->writes)===0,'sign reversal first passes zero during switching lock');
[$m,$b,$s]=fixture();start($m,$s);$b->attrs['LetzterSollwert']=3000;$b->attrs['AenderungFreigabeAb']=$clock+4;$b->measurements['MaxLaden']=100.;tick($m);
check(end($b->writes)===100,'safety reduction not delayed by switching lock');
[$m,$b,$s]=fixture();start($m,$s);tick($m);$b->measurements['Ladezustand']=15.;$b->props['NachladenMitMaximalleistung']=false;$b->props['MaximaleNachladeleistung']=200;$b->watchdog();
check(end($b->writes)===200,'configured reserve-recovery charging limit remains enforced');
[$m,$b,$s]=fixture();start($m,$s);tick($m);$m->licensed=false;tick($m);
check(!$m->active()&&!$b->active()&&end($b->writes)===0,'license withdrawal revokes both software sessions');
[$m,$b,$s]=fixture();$s['durationSeconds']=30;start($m,$s);tick($m);advance(30);tick($m);
check(!$m->active()&&!$b->active()&&end($b->writes)===0,'local shorter test expiration respected');
[$m,$b,$s]=fixture();start($m,$s);tick($m);$m->vars['Aktiv']=false;tick($m);
check(!$m->active()&&!$b->active(),'manual manager disable ends trial');
[$m,$b,$s]=fixture();start($m,$s);tick($m);$b->vars['Aktiv']=false;$b->watchdog();
check(!$b->active()&&end($b->writes)===0,'manual battery disable independently ends trial');