feat(manager): integrate forecast controls and merge V4 development
Tests / test (push) Failing after 49s
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:
+68
-8
@@ -6,8 +6,10 @@ require_once __DIR__ . '/../libs/VerbraucherSchnittstelle.php';
|
||||
require_once __DIR__ . '/../libs/VerbraucherBasisTrait.php';
|
||||
require_once __DIR__ . '/../libs/Nachrichtenvertrag.php';
|
||||
require_once __DIR__ . '/../libs/BatterieRegler.php';
|
||||
require_once __DIR__ . '/../libs/BatterieNetzfahrplanV4TestTrait.php';
|
||||
|
||||
use Belevo\EnelixEMS\BatterieRegler;
|
||||
use Belevo\EnelixEMS\NetzfahrplanV4Rueckmeldung;
|
||||
use Belevo\EnelixEMS\Nachrichtenvertrag;
|
||||
use Belevo\EnelixEMS\VerbraucherBasisTrait;
|
||||
use Belevo\EnelixEMS\VerbraucherSchnittstelle;
|
||||
@@ -15,6 +17,7 @@ use Belevo\EnelixEMS\VerbraucherSchnittstelle;
|
||||
class Batterie extends IPSModule implements VerbraucherSchnittstelle
|
||||
{
|
||||
use VerbraucherBasisTrait;
|
||||
use \Belevo\EnelixEMS\BatterieNetzfahrplanV4TestTrait;
|
||||
|
||||
private const MANAGER_MODULE_ID = '{6F771B18-59D4-4C8A-B951-3B2FE9F6A2C4}';
|
||||
private const STATUS_AKTIV = 102;
|
||||
@@ -68,6 +71,7 @@ class Batterie extends IPSModule implements VerbraucherSchnittstelle
|
||||
parent::Create();
|
||||
|
||||
$this->registriereVerbraucherBasis();
|
||||
$this->registriereV4BatterieTest();
|
||||
$this->RegisterPropertyInteger('Batterietyp', BatterieRegler::TYP_UNKONFIGURIERT);
|
||||
$this->RegisterPropertyInteger(
|
||||
'Batteriemanagement',
|
||||
@@ -152,6 +156,8 @@ class Batterie extends IPSModule implements VerbraucherSchnittstelle
|
||||
{
|
||||
parent::ApplyChanges();
|
||||
|
||||
$this->V4BatterieTestStoppen();
|
||||
$this->konfiguriereV4Rueckmeldung();
|
||||
$this->aktualisiereVariablen();
|
||||
$this->bereinigeUngueltigenSollwert();
|
||||
$this->registriereMesswertmeldungen();
|
||||
@@ -249,7 +255,25 @@ class Batterie extends IPSModule implements VerbraucherSchnittstelle
|
||||
$this->aktualisiere(true);
|
||||
return;
|
||||
|
||||
case 'NetzfahrplanV4TestStart':
|
||||
if (!is_string($wert)) throw new InvalidArgumentException('JSON-Testanmeldung erforderlich.');
|
||||
$this->V4BatterieTestStarten($wert);
|
||||
return;
|
||||
case 'NetzfahrplanV4RueckmeldungAktualisieren':
|
||||
$this->aktualisiereV4Rueckmeldung();
|
||||
return;
|
||||
case 'NetzfahrplanV4TestCommand':
|
||||
if (!is_string($wert)) throw new InvalidArgumentException('JSON-Testbefehl erforderlich.');
|
||||
$this->V4BatterieTestBefehl($wert);
|
||||
return;
|
||||
case 'NetzfahrplanV4TestStop':
|
||||
$this->V4BatterieTestStoppen();
|
||||
return;
|
||||
case 'NetzfahrplanV4TestWatchdog':
|
||||
$this->aktualisiere(true);
|
||||
return;
|
||||
case 'SichererZustand':
|
||||
$this->V4BatterieTestStoppen();
|
||||
$this->verwerfeSollwert();
|
||||
$this->schreibeRegister(0, true);
|
||||
$this->aktualisiere(true);
|
||||
@@ -288,6 +312,14 @@ class Batterie extends IPSModule implements VerbraucherSchnittstelle
|
||||
|
||||
/** @param array<string, mixed> $daten */
|
||||
public function ManagerdatenEmpfangen(array $daten): void
|
||||
{
|
||||
$lock = 'ENELIX.V4.Battery.' . $this->InstanceID;
|
||||
if (!IPS_SemaphoreEnter($lock, 1000)) throw new RuntimeException('Batterieannahme beschaeftigt.');
|
||||
try { $this->ManagerdatenEmpfangenIntern($daten); }
|
||||
finally { IPS_SemaphoreLeave($lock); }
|
||||
}
|
||||
|
||||
private function ManagerdatenEmpfangenIntern(array $daten): void
|
||||
{
|
||||
Nachrichtenvertrag::pruefeManagerdaten($daten);
|
||||
if ($daten['Kopf']['EmpfaengerID'] !== $this->InstanceID) {
|
||||
@@ -303,8 +335,13 @@ class Batterie extends IPSModule implements VerbraucherSchnittstelle
|
||||
);
|
||||
}
|
||||
|
||||
if ($this->v4BatterieTestAktiv()) {
|
||||
// Ordinary manager messages cannot overwrite or renew the independent short test lease.
|
||||
$this->planeMeldung();
|
||||
return;
|
||||
}
|
||||
$this->WriteAttributeString('Betriebsart', $daten['Betriebsart']);
|
||||
$this->aktualisiere(false);
|
||||
$this->aktualisiereIntern(false);
|
||||
|
||||
$sollleistung = $daten['Sollleistung_W'];
|
||||
if ($sollleistung === null) {
|
||||
@@ -315,7 +352,7 @@ class Batterie extends IPSModule implements VerbraucherSchnittstelle
|
||||
)
|
||||
) {
|
||||
$this->verwerfeSollwert();
|
||||
$this->aktualisiere(false);
|
||||
$this->aktualisiereIntern(false);
|
||||
}
|
||||
$this->planeMeldung();
|
||||
return;
|
||||
@@ -342,17 +379,26 @@ class Batterie extends IPSModule implements VerbraucherSchnittstelle
|
||||
$this->setzeZustand('Sollleistung', $sollleistung);
|
||||
$this->setzeZustand('SollwertGueltig', true);
|
||||
$this->planeVorgabeTimeout(time());
|
||||
$this->aktualisiere(false);
|
||||
$this->aktualisiereIntern(false);
|
||||
$this->planeMeldung();
|
||||
}
|
||||
|
||||
private function aktualisiere(bool $meldungPlanen): void
|
||||
{
|
||||
$lock = 'ENELIX.V4.Battery.' . $this->InstanceID;
|
||||
if (!IPS_SemaphoreEnter($lock, 0)) return;
|
||||
try { $this->aktualisiereIntern($meldungPlanen); }
|
||||
finally { IPS_SemaphoreLeave($lock); }
|
||||
}
|
||||
|
||||
private function aktualisiereIntern(bool $meldungPlanen): void
|
||||
{
|
||||
try {
|
||||
$jetzt = time();
|
||||
$this->aktualisiereEnergie($jetzt);
|
||||
$messwerte = $this->leseMesswerte();
|
||||
$messwerteGueltig = $messwerte['Gueltig'];
|
||||
$this->SetBuffer('V4FeedbackEstimated', ($messwerte['V4FeedbackEstimated'] ?? false) ? '1' : '0');
|
||||
|
||||
$this->setzeZustand('Ladezustand', $messwerte['Ladezustand']);
|
||||
$this->setzeZustand('LadezustandGueltig', $messwerteGueltig);
|
||||
@@ -363,7 +409,7 @@ class Batterie extends IPSModule implements VerbraucherSchnittstelle
|
||||
$this->setzeSichtbareVariable(
|
||||
'Leistungsquelle',
|
||||
$messwerteGueltig
|
||||
? Nachrichtenvertrag::LEISTUNGSQUELLE_GEMESSEN
|
||||
? (($messwerte['V4FeedbackEstimated'] ?? false) ? Nachrichtenvertrag::LEISTUNGSQUELLE_BERECHNET : Nachrichtenvertrag::LEISTUNGSQUELLE_GEMESSEN)
|
||||
: Nachrichtenvertrag::LEISTUNGSQUELLE_NICHT_VORHANDEN
|
||||
);
|
||||
$this->aktualisiereLadestatus(
|
||||
@@ -427,6 +473,8 @@ class Batterie extends IPSModule implements VerbraucherSchnittstelle
|
||||
$ziel = $verfuegbar && (bool) $this->leseZustand('SollwertGueltig')
|
||||
? (int) $this->leseZustand('Sollleistung')
|
||||
: 0;
|
||||
$v4Ziel = $this->v4BatterieTestZiel($messwerte, $verfuegbar, $jetzt);
|
||||
if ($v4Ziel !== null) $ziel = $v4Ziel;
|
||||
$this->schreibeRegister($ziel, false);
|
||||
$this->setzeZustand('Sollleistung', $ziel);
|
||||
$this->setzeZustand('Verfuegbar', $verfuegbar);
|
||||
@@ -486,6 +534,7 @@ class Batterie extends IPSModule implements VerbraucherSchnittstelle
|
||||
|
||||
$gueltig = true;
|
||||
foreach ($zuordnung as $property => $schluessel) {
|
||||
if ($this->v4BatterieTestAktiv() && in_array($schluessel,['Netzleistung','Istleistung'],true)) continue;
|
||||
$variablenID = $this->ReadPropertyInteger($property);
|
||||
if (!$this->istMesswertAktuell($variablenID)) {
|
||||
$gueltig = false;
|
||||
@@ -494,8 +543,17 @@ class Batterie extends IPSModule implements VerbraucherSchnittstelle
|
||||
$werte[$schluessel] = (float) GetValue($variablenID);
|
||||
}
|
||||
|
||||
if ($werte['MaxLaden'] <= 0.0
|
||||
|| $werte['MaxEntladen'] <= 0.0
|
||||
if ($this->v4BatterieTestAktiv()) {
|
||||
try {
|
||||
$f=$this->v4BatterieRueckmeldung();
|
||||
NetzfahrplanV4Rueckmeldung::requireTrial($f,$this->v4BatterieTestSitzung(),time());
|
||||
$werte['Istleistung']=$f['batteryW'];$werte['Netzleistung']=$f['gridW'];
|
||||
$werte['V4FeedbackEstimated']=$f['estimated'];
|
||||
} catch (Throwable $e) { $gueltig=false; }
|
||||
}
|
||||
|
||||
if ($werte['MaxLaden'] < 0.0
|
||||
|| $werte['MaxEntladen'] < 0.0
|
||||
|| $werte['Ladezustand'] < 0.0
|
||||
|| $werte['Ladezustand'] > 100.0
|
||||
) {
|
||||
@@ -536,7 +594,9 @@ class Batterie extends IPSModule implements VerbraucherSchnittstelle
|
||||
try {
|
||||
foreach ($werte as $register => $wert) {
|
||||
$property = self::REGISTER_PROPERTIES[$register];
|
||||
RequestAction($this->ReadPropertyInteger($property), $wert);
|
||||
if (RequestAction($this->ReadPropertyInteger($property), $wert) !== true) {
|
||||
throw new RuntimeException('Registeraktion nicht bestaetigt: ' . $register);
|
||||
}
|
||||
}
|
||||
} catch (Throwable $fehler) {
|
||||
throw new RuntimeException(
|
||||
@@ -673,7 +733,7 @@ class Batterie extends IPSModule implements VerbraucherSchnittstelle
|
||||
? (float) $this->leseZustand('Istleistung')
|
||||
: null,
|
||||
'Leistungsquelle' => $messwerteGueltig
|
||||
? Nachrichtenvertrag::LEISTUNGSQUELLE_GEMESSEN
|
||||
? ($this->GetBuffer('V4FeedbackEstimated') === '1' ? Nachrichtenvertrag::LEISTUNGSQUELLE_BERECHNET : Nachrichtenvertrag::LEISTUNGSQUELLE_GEMESSEN)
|
||||
: Nachrichtenvertrag::LEISTUNGSQUELLE_NICHT_VORHANDEN,
|
||||
'Zustand' => [
|
||||
[
|
||||
|
||||
Reference in New Issue
Block a user