feat(manager): integrate forecast controls and merge V4 development
Tests / test (push) Failing after 49s

Merge the V4 development history with current manager, SDL and setpoint fixes. Retain guarded trial behavior and consolidate controls in Prognose / Forecast. Exclude the unverified accounting-evidence change; no runtime deployment or new dispatch permission.

Validated: 357 PHPUnit tests / 1758 assertions, 150 PHP syntax checks, 310 backend tests, 16 portal tests and isolated UI/receiver checks. develop and beta publication explicitly approved by Daniel Haefliger.
This commit is contained in:
dh
2026-10-06 14:22:16 +00:00
207 changed files with 20340 additions and 11 deletions
+23
View File
@@ -0,0 +1,23 @@
<?php
// Run through actual Manager/Battery trial traits, fresh read helper, and simulated register driver.
[$m,$b,$s]=fixture();$b->props['NetzfahrplanV4RueckmeldungKonfiguration']='{}';
rejects(fn()=>start($m,$s),'trial cannot use old filtered feedback when mapping missing');check($b->writes===[],'missing corrected feedback sends no command');
[$m,$b,$s]=fixture();start($m,$s);tick($m);$deviceReadOk=false;
$a=tick($m);check(!$m->active()&&!$b->active()&&isset($a['Sollwerte'][44234]),'failed confirmed read returns battery to normal allocation');
check(end($b->writes)===0,'failed confirmed read revokes prior command');
[$m,$b,$s]=fixture();start($m,$s);tick($m);$deviceReadOk=false;$b->watchdog();
check(!$b->active()&&end($b->writes)===0,'independent battery read catches device failure without manager tick');
[$m,$b,$s]=fixture();start($m,$s);tick($m);$c=json_decode($b->props['NetzfahrplanV4RueckmeldungKonfiguration'],true);$c['trackingToleranceW']=250;
$b->props['NetzfahrplanV4RueckmeldungKonfiguration']=json_encode($c);$b->watchdog();
check(!$b->active()&&end($b->writes)===0,'mapping revision change revokes active battery session');
[$m,$b,$s]=fixture();start($m,$s);tick($m);$c=$m->command();$c['sequence']++;$c['feedbackConfigHash']=str_repeat('f',64);
rejects(fn()=>$b->V4BatterieTestBefehl(json_encode($c)),'changed feedback command binding rejected');
[$m,$b,$s]=fixture();start($m,$s);tick($m);$c=$m->command();check(isset($c['feedbackConfigHash'],$c['feedbackSourceOldestAt'])&&array_key_exists('importLimitW',$c),'command includes source provenance and limits');
$c['sequence']++;unset($c['exportLimitW']);rejects(fn()=>$b->V4BatterieTestBefehl(json_encode($c)),'missing explicit grid limit not interpreted as unlimited');
[$m,$b,$s]=fixture();start($m,$s);tick($m);$c=$m->command();$c['sequence']++;$c['importLimitW']=1000.;$c['watts']=0;
$m->op['gridW']=1500.;$b->measurements['Ladezustand']=15.;
$b->sample();
rejects(fn()=>$b->V4BatterieTestBefehl(json_encode($c)),'final device clipping cannot silently violate accepted grid boundary');
check(!$b->active()&&end($b->writes)===0,'unreachable final limit aborts software trial');
[$m,$b,$s]=fixture();start($m,$s);tick($m);$feedbackAt[50002]=$clock+1;tick($m);
check(!$m->active(),'future physical sample is not smoothed into control');