feat: Anlagentopologie im Manager abbilden

This commit is contained in:
dh
2026-09-22 17:11:12 +00:00
parent a67018e68f
commit dbd728dd03
9 changed files with 1042 additions and 0 deletions
+156
View File
@@ -0,0 +1,156 @@
<?php
declare(strict_types=1);
namespace Belevo\EnelixEMS\Tests;
use Belevo\EnelixEMS\Anlagentopologie;
use InvalidArgumentException;
use PHPUnit\Framework\TestCase;
final class AnlagentopologieTest extends TestCase
{
public function testHybridwechselrichterVerknuepftPvUndBatterieMitGemeinsamerAcGrenze(): void
{
$topologie = Anlagentopologie::ausJson(
json_encode([[
'ID' => 'hybrid-1',
'Name' => 'Hybrid Ost',
'Typ' => 'hybrid',
'ACNennleistung_kW' => 10.0,
'ACEinspeisegrenze_kW' => 8.0,
'ACBezugsgrenze_kW' => 0.0,
]], JSON_THROW_ON_ERROR),
json_encode([
[
'ID' => 'pv-ost',
'Name' => 'Ostdach',
'DCLeistung_kWp' => 6.4,
'Neigung_Grad' => 25.0,
'Azimut_Grad' => -90.0,
'WechselrichterID' => 'hybrid-1',
'MPPT' => 'A',
'Modulanzahl' => 16,
'Modulleistung_Wp' => 400.0,
],
[
'ID' => 'pv-west',
'Name' => 'Westdach',
'DCLeistung_kWp' => 6.4,
'Neigung_Grad' => 25.0,
'Azimut_Grad' => 90.0,
'WechselrichterID' => 'hybrid-1',
'MPPT' => 'B',
'Modulanzahl' => 16,
'Modulleistung_Wp' => 400.0,
],
], JSON_THROW_ON_ERROR),
json_encode([[
'ID' => 'bat-1',
'Name' => 'Hausspeicher',
'Nennkapazitaet_kWh' => 13.5,
'Nutzkapazitaet_kWh' => 12.8,
'MaxLadeleistung_kW' => 5.0,
'MaxEntladeleistung_kW' => 5.0,
'WechselrichterID' => 'hybrid-1',
'Kopplung' => 'hybrid',
]], JSON_THROW_ON_ERROR),
'2a518707-8d62-4d97-82ce-606c95726500'
);
self::assertSame(Anlagentopologie::VERSION, $topologie['version']);
self::assertSame(12.8, $topologie['summary']['pvDcKwp']);
self::assertSame(10.0, $topologie['summary']['pvAcKw']);
self::assertSame(12.8, $topologie['summary']['usableBatteryKwh']);
self::assertSame([[
'inverterId' => 'hybrid-1',
'acRatedKw' => 10.0,
'pvArrayIds' => ['pv-ost', 'pv-west'],
'batteryIds' => ['bat-1'],
]], $topologie['sharedAcLimits']);
}
public function testLeereTopologieBleibtFuerBestandsinstallationenGueltig(): void
{
$topologie = Anlagentopologie::ausJson(
'[]',
'[]',
'[]',
'2a518707-8d62-4d97-82ce-606c95726500'
);
self::assertSame([], $topologie['inverters']);
self::assertSame([], $topologie['pvArrays']);
self::assertSame([], $topologie['batteries']);
self::assertSame(0.0, $topologie['summary']['pvDcKwp']);
}
public function testPvFlaecheBenoetigtEinenVorhandenenPassendenWechselrichter(): void
{
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('referenzierter Wechselrichter ist nicht vorhanden');
Anlagentopologie::ausJson(
'[]',
json_encode([[
'ID' => 'pv-1',
'DCLeistung_kWp' => 5.0,
'Neigung_Grad' => 30.0,
'Azimut_Grad' => 0.0,
'WechselrichterID' => 'wr-fehlt',
]], JSON_THROW_ON_ERROR),
'[]',
'2a518707-8d62-4d97-82ce-606c95726500'
);
}
public function testDcBatterieBenoetigtEinenHybridwechselrichter(): void
{
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('DC- und Hybridkopplung');
Anlagentopologie::ausJson(
json_encode([[
'ID' => 'bat-wr',
'Typ' => 'battery',
'ACNennleistung_kW' => 5.0,
]], JSON_THROW_ON_ERROR),
'[]',
json_encode([[
'ID' => 'bat-1',
'Nennkapazitaet_kWh' => 10.0,
'Nutzkapazitaet_kWh' => 9.0,
'MaxLadeleistung_kW' => 4.0,
'MaxEntladeleistung_kW' => 4.0,
'WechselrichterID' => 'bat-wr',
'Kopplung' => 'dc',
]], JSON_THROW_ON_ERROR),
'2a518707-8d62-4d97-82ce-606c95726500'
);
}
public function testModuldatenMuessenGemeinsamGesetztWerden(): void
{
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('Modulanzahl und Modulleistung');
Anlagentopologie::ausJson(
json_encode([[
'ID' => 'pv-wr',
'Typ' => 'pv',
'ACNennleistung_kW' => 5.0,
]], JSON_THROW_ON_ERROR),
json_encode([[
'ID' => 'pv-1',
'DCLeistung_kWp' => 6.0,
'Neigung_Grad' => 30.0,
'Azimut_Grad' => 0.0,
'WechselrichterID' => 'pv-wr',
'Modulanzahl' => 15,
'Modulleistung_Wp' => 0.0,
]], JSON_THROW_ON_ERROR),
'[]',
'2a518707-8d62-4d97-82ce-606c95726500'
);
}
}
+5
View File
@@ -56,6 +56,9 @@ final class ManagerModulstrukturTest extends TestCase
'LizenzInstallationID',
'LizenzInformation',
'VerbraucherAktualisieren',
'AnlagenWechselrichter',
'AnlagenPVFlaechen',
'AnlagenBatterien',
] as $property) {
self::assertStringContainsString($property, $json);
}
@@ -193,6 +196,8 @@ final class ManagerModulstrukturTest extends TestCase
{
$inhalt = (string) file_get_contents(__DIR__ . '/../Manager/module.php');
self::assertStringContainsString('implements ManagerSchnittstelle', $inhalt);
self::assertStringContainsString("require_once __DIR__ . '/../libs/Anlagentopologie.php'", $inhalt);
self::assertStringContainsString('public function AnlagentopologieExportieren(): string', $inhalt);
self::assertStringContainsString('VerbraucherdatenEmpfangen(array $daten)', $inhalt);
self::assertStringContainsString("'ManagerdatenEmpfangen'", $inhalt);
}
+50
View File
@@ -72,6 +72,35 @@ return static function (TestContext $test): void {
['InstanzID' => $warmwasserId, 'Aktiv' => true],
['InstanzID' => $pufferspeicherId, 'Aktiv' => true],
];
$wechselrichterKonfiguration = [[
'ID' => 'hybrid-1',
'Name' => 'Hybridwechselrichter',
'Typ' => 'hybrid',
'ACNennleistung_kW' => 10.0,
'ACEinspeisegrenze_kW' => 8.0,
'ACBezugsgrenze_kW' => 0.0,
]];
$pvKonfiguration = [[
'ID' => 'pv-1',
'Name' => 'Suedflaeche',
'DCLeistung_kWp' => 12.0,
'Neigung_Grad' => 30.0,
'Azimut_Grad' => 0.0,
'WechselrichterID' => 'hybrid-1',
'MPPT' => 'A',
'Modulanzahl' => 30,
'Modulleistung_Wp' => 400.0,
]];
$batterieKonfiguration = [[
'ID' => 'bat-1',
'Name' => 'Hausspeicher',
'Nennkapazitaet_kWh' => 13.5,
'Nutzkapazitaet_kWh' => 12.8,
'MaxLadeleistung_kW' => 5.0,
'MaxEntladeleistung_kW' => 5.0,
'WechselrichterID' => 'hybrid-1',
'Kopplung' => 'hybrid',
]];
$lizenzcode = trim((string) getenv('ENELIX_TEST_LICENSE_CODE'));
$managerId = $test->createInstance(
'{6F771B18-59D4-4C8A-B951-3B2FE9F6A2C4}',
@@ -87,6 +116,12 @@ return static function (TestContext $test): void {
'Lastspitzengrenze' => 0.0,
'SollwertSolarladen' => 0.0,
'Umschaltdifferenz' => 0.0,
'AnlagenWechselrichter' => json_encode(
$wechselrichterKonfiguration,
JSON_THROW_ON_ERROR
),
'AnlagenPVFlaechen' => json_encode($pvKonfiguration, JSON_THROW_ON_ERROR),
'AnlagenBatterien' => json_encode($batterieKonfiguration, JSON_THROW_ON_ERROR),
'DiagnosevariablenAnzeigen' => true,
]
);
@@ -163,6 +198,21 @@ return static function (TestContext $test): void {
$vorher = $lizenzInstallationID();
IPS_ApplyChanges($managerId);
$test->assertSame($vorher, $lizenzInstallationID());
$topologie = json_decode(
ENELIX_AnlagentopologieExportieren($managerId),
true,
512,
JSON_THROW_ON_ERROR
);
$test->assertSame($vorher, $topologie['installationId'] ?? null);
$test->assertSame('1.0', $topologie['version'] ?? null);
$test->assertSame(12.0, $topologie['summary']['pvDcKwp'] ?? null);
$test->assertSame(10.0, $topologie['summary']['pvAcKw'] ?? null);
$test->assertSame(12.8, $topologie['summary']['usableBatteryKwh'] ?? null);
$test->assertSame('hybrid-1', $topologie['sharedAcLimits'][0]['inverterId'] ?? null);
$test->assertSame(['pv-1'], $topologie['sharedAcLimits'][0]['pvArrayIds'] ?? null);
$test->assertSame(['bat-1'], $topologie['sharedAcLimits'][0]['batteryIds'] ?? null);
});
$zustandswert = static function (array $daten, int $instanzId, string $kennung) {
foreach ($daten['Verbraucher'] ?? [] as $verbraucher) {