feat(control): connect physical feedback to bounded V4 command and fallback paths
This commit is contained in:
@@ -14,6 +14,11 @@ namespace Belevo\EnelixEMS {
|
||||
function IPS_GetInstance($id) { return ['InstanceStatus'=>$GLOBALS['objects'][$id]->ready?102:201,
|
||||
'ModuleInfo'=>['ModuleID'=>$id===44234?'{437FB683-517F-4FEC-8CCB-FE6B0A62B69E}':'{6F771B18-59D4-4C8A-B951-3B2FE9F6A2C4}']]; }
|
||||
function IPS_GetProperty($id,$key) { return $GLOBALS['objects'][$id]->props[$key]; }
|
||||
function IPS_VariableExists($id) { return in_array($id,[50001,50002],true); }
|
||||
function IPS_GetVariable($id) { $t=$GLOBALS['feedbackAt'][$id]??$GLOBALS['clock'];return ['VariableUpdated'=>$t,'VariableChanged'=>$t]; }
|
||||
function IPS_GetObject($id) { return ['ParentID'=>200,'ObjectIdent'=>'source'.$id]; }
|
||||
function GetValue($id) { return $id===50001?$GLOBALS['objects'][17004]->op['gridW']:$GLOBALS['objects'][17004]->actual['b']; }
|
||||
|
||||
function IPS_RequestAction($id,$key,$value) {
|
||||
$GLOBALS['calls'][]=[$id,$key];$b=$GLOBALS['objects'][$id];
|
||||
if ($key==='NetzfahrplanV4TestStart') $b->V4BatterieTestStarten($value);
|
||||
@@ -76,7 +81,10 @@ namespace Belevo\EnelixEMS {
|
||||
private function berechtigungLizenziert($k) { return $this->licensed; }
|
||||
private function actieveDummy() {}
|
||||
private function aktiveVerbraucherIDs() { return [44234]; }
|
||||
private function netzfahrplanV4LokaleVorschauwerte() { return [$this->op,$this->actual]; }
|
||||
private function netzfahrplanV4LokaleVorschauwerte() {
|
||||
$op=$this->op;$f=json_decode($GLOBALS['objects'][44234]->GetV4BatterieRueckmeldung(),true);
|
||||
$op['localFeedback']=['b'=>$f];return [$op,$this->actual];
|
||||
}
|
||||
private function regeln($renew) { $this->fallback++; }
|
||||
public function tick(&$allocation,$consumers=null,$sync=true) {
|
||||
$this->v4ManagerTestUebergabe($allocation,$consumers??[['InstanzID'=>44234,'Daten'=>[]]],$sync);
|
||||
@@ -105,7 +113,14 @@ namespace {
|
||||
'maxChargeW'=>5000.,'maxDischargeW'=>5000.,'actuatorWatchdogEvidenceId'=>'synthetic-hardware-watchdog',
|
||||
'accountingEvidenceId'=>'synthetic-meter-boundary-v1','acceptEstimatedPeak'=>true,'controlContext'=>$r['plan']['controlContext']];
|
||||
$r['controlledTrial']=$serverAuthority?$a:null;
|
||||
$m=new ManagerHarness($r,$op);$b=new BatteryHarness();$GLOBALS['objects']=[17004=>$m,44234=>$b];
|
||||
$m=new ManagerHarness($r,$op);$b=new BatteryHarness();$GLOBALS['objects']=[17004=>$m,44234=>$b];$GLOBALS['feedbackAt']=[];
|
||||
$fc=['version'=>1,'installationId'=>$id,'assetId'=>'b','managerId'=>17004,'batteryInstanceId'=>44234,
|
||||
'mode'=>'physical_sum','allowEstimatedForTrial'=>false,'maxSkewSeconds'=>30,'maxAgeSeconds'=>60,
|
||||
'idleToleranceW'=>50.0,'trackingToleranceW'=>200.0,'sources'=>[]];
|
||||
foreach ([50001=>'grid',50002=>'physical'] as $vid=>$role) $fc['sources'][]=['key'=>'source'.$vid,'role'=>$role,'variableId'=>$vid,'parentId'=>200,'ident'=>'source'.$vid,'factorToW'=>1.0];
|
||||
$b->props['NetzfahrplanV4RueckmeldungKonfiguration']=json_encode($fc);
|
||||
$op['localFeedback']=['b'=>json_decode($b->GetV4BatterieRueckmeldung(),true)];
|
||||
|
||||
$m->props['NetzfahrplanV4RegeltestErlaubt']=$permissions;$b->props['NetzfahrplanV4RegeltestErlaubt']=$permissions;
|
||||
$b->props['NetzfahrplanV4GeraeteWatchdogNachweis']='synthetic-hardware-watchdog';
|
||||
$start=['confirmation'=>'START_BOUNDED_V4_CONTROL_TRIAL','sessionId'=>$a['sessionId'],'durationSeconds'=>300,'maxChargeW'=>5000.,'maxDischargeW'=>5000.];
|
||||
@@ -157,5 +172,6 @@ namespace {
|
||||
$bad=$r;$bad['controlledTrial']['validUntil']=gmdate('c',$now+120);rejects(fn()=>Gate::befehl($bad,$op,['b'=>0.],$local,$now,$now),'oversized server authorization lease');
|
||||
$bad=$r;$bad['controlledTrial']['acceptEstimatedPeak']=false;rejects(fn()=>Gate::befehl($bad,$op,['b'=>0.],$local,$now,$now),'peak uncertainty needs explicit acceptance');
|
||||
require __DIR__ . '/additional_checks.php';
|
||||
require __DIR__ . '/feedback_checks.php';
|
||||
echo "TOTAL $count checks passed. Mocked IPS, clocks and register writes only.\n";
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
<?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);$feedbackAt[50002]=$clock-61;
|
||||
$a=tick($m);check(!$m->active()&&!$b->active()&&isset($a['Sollwerte'][44234]),'physical feedback expiry returns battery to normal allocation');
|
||||
check(end($b->writes)===0,'expired physical feedback revokes prior command');
|
||||
[$m,$b,$s]=fixture();start($m,$s);tick($m);$feedbackAt[50002]=$clock-61;$b->watchdog();
|
||||
check(!$b->active()&&end($b->writes)===0,'independent battery read catches staleness 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.;
|
||||
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');
|
||||
Reference in New Issue
Block a user