feat(v4): confirm feedback with device reads

This commit is contained in:
dh
2026-10-04 15:34:36 +00:00
parent 30f7652803
commit 13e49ff60c
15 changed files with 693 additions and 29 deletions
+51
View File
@@ -0,0 +1,51 @@
<?php
// Offline only, no IP-Symcon or real device access.
declare(strict_types=1);
if (function_exists('IPS_GetKernelVersion')) throw new RuntimeException('Offline test only');
require_once __DIR__.'/../../libs/NetzfahrplanV4Rueckmeldung.php';
require_once __DIR__.'/../../libs/NetzfahrplanV4Geraeteabruf.php';
use Belevo\EnelixEMS\NetzfahrplanV4Geraeteabruf as D;
$count=0;
function dcheck($ok,$name){global $count;if(!$ok)throw new RuntimeException('FAIL '.$name);$count++;echo "PASS $name\n";}
function dfails($fn,$name){try{$fn();}catch(Throwable $e){dcheck(true,$name);return;}throw new RuntimeException('FAIL no rejection '.$name);}
function dfixture(){
$now=1791032400;
$config=['version'=>1,'installationId'=>'e3a08f9e-af12-4695-99bd-8b51c0520021','assetId'=>'ev','managerId'=>17004,'batteryInstanceId'=>44234,
'mode'=>'physical_sum','allowEstimatedForTrial'=>false,'maxSkewSeconds'=>30,'maxAgeSeconds'=>60,'idleToleranceW'=>50.0,'trackingToleranceW'=>200.0,
'sources'=>[
['key'=>'grid','role'=>'grid','variableId'=>10,'parentId'=>20,'ident'=>'grid','factorToW'=>1.0],
['key'=>'battery_a','role'=>'physical','variableId'=>11,'parentId'=>21,'ident'=>'a','factorToW'=>1.0],
['key'=>'battery_b','role'=>'physical','variableId'=>12,'parentId'=>21,'ident'=>'b','factorToW'=>1.0],
]];
$records=[
10=>['value'=>3000.0,'updated'=>$now-100,'changed'=>$now-200,'parentID'=>20,'ident'=>'grid'],
11=>['value'=>-1000.0,'updated'=>$now-100,'changed'=>$now-200,'parentID'=>21,'ident'=>'a'],
12=>['value'=>-2000.0,'updated'=>$now-100,'changed'=>$now-200,'parentID'=>21,'ident'=>'b'],
];
return [$config,$records,$now];
}
[$c,$r,$n]=dfixture();$calls=[];$times=[$n-1,$n-1,$n];
$f=D::snapshot($c,fn($id)=>$id===20?D::MBUS_DEVICE:D::MODBUS_DEVICE,
function($id,$module)use(&$calls){$calls[]=$id;return true;},
fn($id)=>$r[$id],function()use(&$times){return array_shift($times);});
sort($calls);dcheck($calls===[20,21],'one refresh per physical parent');
dcheck($f['deviceReadConfirmed']===true&&$f['sourceOldestAt']===$n-1,'successful read confirmation is freshness basis');
dcheck($f['sourceVariableOldestAt']===$n-100,'original VariableUpdated retained separately');
dcheck($f['batteryW']===-3000.0&&$f['usableForTrial'],'unchanged values accepted after confirmed device response');
[$c,$r,$n]=dfixture();$times=[$n,$n,$n];
dfails(fn()=>D::snapshot($c,fn($id)=>D::MODBUS_DEVICE,fn()=>false,fn($id)=>$r[$id],function()use(&$times){return array_shift($times);}),
'failed device read rejected');
[$c,$r,$n]=dfixture();$times=[$n,$n,$n];$reads=0;
dfails(fn()=>D::snapshot($c,fn($id)=>D::MODBUS_DEVICE,fn()=>true,
function($id)use(&$r,&$reads){$reads++;$x=$r[$id];if($reads>3&&$id===10)$x['value']++;return $x;},
function()use(&$times){return array_shift($times);}), 'change after confirmed response rejected');
[$c,$r,$n]=dfixture();$times=[$n,$n,$n];
dfails(fn()=>D::snapshot($c,fn($id)=>'{00000000-0000-0000-0000-000000000000}',fn()=>true,fn($id)=>$r[$id],
function()use(&$times){return array_shift($times);}), 'unsupported device module rejected');
echo "TOTAL $count confirmed-device-read checks passed. Synthetic calls only.\n";