This commit is contained in:
@@ -13,10 +13,10 @@ final class DokumentationsstrukturTest extends TestCase
|
||||
{
|
||||
return [
|
||||
'Verbrauchskostenreport' => ['Verbrauchskostenreport', 'Status: Implementiert'],
|
||||
'Virtuelle Batterie' => ['Virtuelle-Batterie', 'Status: Diskussionsentwurf'],
|
||||
'Virtuelle Batterie' => ['Virtuelle-Batterie', 'Status: Implementiert'],
|
||||
'CC100 Hardware' => ['CC100-Hardware', 'Status: Implementiert'],
|
||||
'Energiediagramm' => ['Energiediagramm', 'Status: Implementiert'],
|
||||
'VGT-Schnittstelle' => ['VGT-Schnittstelle', 'Status: Diskussionsentwurf'],
|
||||
'VGT-Schnittstelle' => ['VGT-Schnittstelle', 'Status: Implementiert'],
|
||||
'Shelly-Modul' => ['Shelly-Modul', 'Status: Implementiert'],
|
||||
];
|
||||
}
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Belevo\EnelixUtils\Tests;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
final class NeueModuleStrukturTest extends TestCase
|
||||
{
|
||||
/** @dataProvider module */
|
||||
public function testMetadatenUndFormulareSindGueltig(
|
||||
string $ordner,
|
||||
string $name,
|
||||
string $prefix
|
||||
): void {
|
||||
$metadata = json_decode(
|
||||
(string) file_get_contents(
|
||||
__DIR__ . '/../' . $ordner . '/module.json'
|
||||
),
|
||||
true,
|
||||
512,
|
||||
JSON_THROW_ON_ERROR
|
||||
);
|
||||
self::assertSame($name, $metadata['name']);
|
||||
self::assertSame($prefix, $metadata['prefix']);
|
||||
self::assertSame(3, $metadata['type']);
|
||||
self::assertMatchesRegularExpression(
|
||||
'/^\{[0-9A-F-]{36}\}$/',
|
||||
$metadata['id']
|
||||
);
|
||||
|
||||
$formular = json_decode(
|
||||
(string) file_get_contents(
|
||||
__DIR__ . '/../' . $ordner . '/form.json'
|
||||
),
|
||||
true,
|
||||
512,
|
||||
JSON_THROW_ON_ERROR
|
||||
);
|
||||
self::assertNotEmpty($formular['elements']);
|
||||
}
|
||||
|
||||
/** @return array<string, array{string, string, string}> */
|
||||
public function module(): array
|
||||
{
|
||||
return [
|
||||
'Virtuelle Batterie' => [
|
||||
'VirtuelleBatterie',
|
||||
'Virtuelle Batterie',
|
||||
'ENLVB',
|
||||
],
|
||||
'VGT-Schnittstelle' => [
|
||||
'VGTSchnittstelle',
|
||||
'VGTSchnittstelle',
|
||||
'ENLVGT',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function testVirtuelleBatterieStelltEVProxyregisterBereit(): void
|
||||
{
|
||||
$modul = (string) file_get_contents(
|
||||
__DIR__ . '/../VirtuelleBatterie/module.php'
|
||||
);
|
||||
foreach ([
|
||||
"'EVManagement'",
|
||||
"'EVModus'",
|
||||
"'EVLadeleistung'",
|
||||
"'EVEntladeleistung'",
|
||||
"'SDLSollleistung'",
|
||||
'VirtuelleBatterieRegler::kombiniere',
|
||||
] as $bestandteil) {
|
||||
self::assertStringContainsString($bestandteil, $modul);
|
||||
}
|
||||
}
|
||||
|
||||
public function testPhysischeSollwerteWerdenNurPerActionGeschrieben(): void
|
||||
{
|
||||
$modul = (string) file_get_contents(
|
||||
__DIR__ . '/../VirtuelleBatterie/module.php'
|
||||
);
|
||||
$anfang = strpos($modul, 'private function schreibeVerteilung');
|
||||
$ende = strpos($modul, 'private function sichererZustand');
|
||||
self::assertNotFalse($anfang);
|
||||
self::assertNotFalse($ende);
|
||||
$funktion = substr($modul, $anfang, $ende - $anfang);
|
||||
self::assertStringContainsString('RequestAction(', $funktion);
|
||||
self::assertStringNotContainsString('SetValue($zielID', $funktion);
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,18 @@ return [
|
||||
'Energiediagramm/*',
|
||||
],
|
||||
],
|
||||
'VGTSchnittstelle' => [
|
||||
'file' => __DIR__ . '/modules/VGTSchnittstelle.php',
|
||||
'paths' => [
|
||||
'VGTSchnittstelle/*',
|
||||
],
|
||||
],
|
||||
'VirtuelleBatterie' => [
|
||||
'file' => __DIR__ . '/modules/VirtuelleBatterie.php',
|
||||
'paths' => [
|
||||
'VirtuelleBatterie/*',
|
||||
],
|
||||
],
|
||||
'ShellyModul' => [
|
||||
'file' => __DIR__ . '/modules/ShellyModul.php',
|
||||
'paths' => [
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Belevo\EnelixUtils\SymconTest\TestContext;
|
||||
|
||||
return static function (TestContext $test): void {
|
||||
$instancesBefore = array_flip(IPS_GetInstanceList());
|
||||
$mqttId = $test->createInstance(
|
||||
'{C6D2AEB3-6E1F-4B2E-8E69-3A1A00246850}',
|
||||
'MQTT-Testserver'
|
||||
);
|
||||
$ioId = (int) IPS_GetInstance($mqttId)['ConnectionID'];
|
||||
if ($ioId > 0 && !isset($instancesBefore[$ioId])) {
|
||||
IPS_SetParent($ioId, $test->rootId());
|
||||
}
|
||||
|
||||
$moduleId = $test->createInstance(
|
||||
'{4BA1A557-7970-4FB9-93B8-1F6ADA0A5A94}',
|
||||
'VGT-Schnittstelle'
|
||||
);
|
||||
if ((int) IPS_GetInstance($moduleId)['ConnectionID'] !== $mqttId) {
|
||||
IPS_DisconnectInstance($moduleId);
|
||||
IPS_ConnectInstance($moduleId, $mqttId);
|
||||
IPS_ApplyChanges($moduleId);
|
||||
}
|
||||
|
||||
$test->runCase('MQTT-Elterninstanz wird verwendet', static function (
|
||||
TestContext $test
|
||||
) use ($moduleId, $mqttId): void {
|
||||
$test->assertSame(
|
||||
$mqttId,
|
||||
(int) IPS_GetInstance($moduleId)['ConnectionID']
|
||||
);
|
||||
});
|
||||
|
||||
$test->runCase('Konfigurationsformular ist gueltiges JSON', static function (
|
||||
TestContext $test
|
||||
) use ($moduleId): void {
|
||||
$form = json_decode(
|
||||
IPS_GetConfigurationForm($moduleId),
|
||||
true,
|
||||
512,
|
||||
JSON_THROW_ON_ERROR
|
||||
);
|
||||
$test->assertTrue(is_array($form));
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Belevo\EnelixUtils\SymconTest\TestContext;
|
||||
|
||||
return static function (TestContext $test): void {
|
||||
$moduleId = $test->createInstance(
|
||||
'{5B2A199B-2226-411F-B83B-FC1CBA58496B}',
|
||||
'Virtuelle Batterie'
|
||||
);
|
||||
|
||||
$test->runCase('Konfigurationsformular ist gueltiges JSON', static function (
|
||||
TestContext $test
|
||||
) use ($moduleId): void {
|
||||
$form = json_decode(
|
||||
IPS_GetConfigurationForm($moduleId),
|
||||
true,
|
||||
512,
|
||||
JSON_THROW_ON_ERROR
|
||||
);
|
||||
$test->assertTrue(is_array($form));
|
||||
});
|
||||
|
||||
$test->runCase('Sichere Startkonfiguration ist inaktiv', static function (
|
||||
TestContext $test
|
||||
) use ($moduleId): void {
|
||||
$aktivId = IPS_GetObjectIDByIdent('Aktiv', $moduleId);
|
||||
$test->assertSame(false, GetValue($aktivId));
|
||||
});
|
||||
};
|
||||
@@ -58,7 +58,14 @@ final class SymconTestContractTest extends TestCase
|
||||
public function testAffectedModuleResolverSelectsAllModulesForFrameworkChanges(): void
|
||||
{
|
||||
self::assertSame(
|
||||
['CC100Hardware', 'Energiediagramm', 'ShellyModul', 'Verbrauchskostenreport'],
|
||||
[
|
||||
'CC100Hardware',
|
||||
'Energiediagramm',
|
||||
'ShellyModul',
|
||||
'VGTSchnittstelle',
|
||||
'Verbrauchskostenreport',
|
||||
'VirtuelleBatterie',
|
||||
],
|
||||
$this->resolveAffectedModules(['tests/Symcon/TestContext.php'])
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Belevo\EnelixUtils\Tests;
|
||||
|
||||
use Belevo\EnelixUtils\VGTRegler;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
require_once __DIR__ . '/../VGTSchnittstelle/libs/VGTRegler.php';
|
||||
|
||||
final class VGTReglerTest extends TestCase
|
||||
{
|
||||
public function testPVActivateUndStopBleibenKompatibel(): void
|
||||
{
|
||||
self::assertSame(
|
||||
4000.0,
|
||||
VGTRegler::zielwert(0, 'activate', 4000.0, 10000.0, 0.0, 0.0, 0.0, 0.0, 0.0, 100.0)
|
||||
);
|
||||
self::assertSame(
|
||||
10000.0,
|
||||
VGTRegler::zielwert(0, 'stop', 4000.0, 10000.0, 0.0, 0.0, 0.0, 0.0, 0.0, 100.0)
|
||||
);
|
||||
}
|
||||
|
||||
public function testBatterieActivateInvertiertDenSetpointWieEnelixEins(): void
|
||||
{
|
||||
self::assertSame(
|
||||
-3500.0,
|
||||
VGTRegler::zielwert(1, 'activate', 3500.0, 10000.0, 50.0, 50.0, 2500.0, 2500.0, 10.0, 90.0)
|
||||
);
|
||||
}
|
||||
|
||||
public function testRestoreRegeltAufZielLadezustand(): void
|
||||
{
|
||||
self::assertSame(
|
||||
2500.0,
|
||||
VGTRegler::zielwert(1, 'restore', 0.0, 10000.0, 40.0, 50.0, 2500.0, 3000.0, 10.0, 90.0)
|
||||
);
|
||||
self::assertSame(
|
||||
-3000.0,
|
||||
VGTRegler::zielwert(1, 'restore', 0.0, 10000.0, 60.0, 50.0, 2500.0, 3000.0, 10.0, 90.0)
|
||||
);
|
||||
}
|
||||
|
||||
public function testLadezustandsgrenzenStoppenFalscheRichtung(): void
|
||||
{
|
||||
self::assertSame(
|
||||
0.0,
|
||||
VGTRegler::zielwert(1, 'activate', 3000.0, 10000.0, 10.0, 50.0, 2500.0, 2500.0, 10.0, 90.0)
|
||||
);
|
||||
}
|
||||
|
||||
public function testFesteMQTTSchnittstelleBleibtImModulErhalten(): void
|
||||
{
|
||||
$modul = file_get_contents(
|
||||
__DIR__ . '/../VGTSchnittstelle/module.php'
|
||||
);
|
||||
self::assertNotFalse($modul);
|
||||
foreach ([
|
||||
'feedback-request/',
|
||||
'feedback-response/',
|
||||
'remote-control-request/',
|
||||
'remote-control-response/',
|
||||
"'power_setpoint'",
|
||||
"'strategy'",
|
||||
"'power_production'",
|
||||
"'is_ready'",
|
||||
"'is_running'",
|
||||
"'state_of_charge'",
|
||||
"'min_soc'",
|
||||
"'max_soc'",
|
||||
] as $bestandteil) {
|
||||
self::assertStringContainsString($bestandteil, $modul);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Belevo\EnelixUtils\Tests;
|
||||
|
||||
use Belevo\EnelixUtils\VirtuelleBatterieRegler;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
require_once __DIR__
|
||||
. '/../VirtuelleBatterie/libs/VirtuelleBatterieRegler.php';
|
||||
|
||||
final class VirtuelleBatterieReglerTest extends TestCase
|
||||
{
|
||||
public function testAggregatIstKapazitaetsgewichtet(): void
|
||||
{
|
||||
$aggregat = VirtuelleBatterieRegler::aggregiere([
|
||||
$this->batterie(0, 10.0, 20.0, 5000.0, 5000.0),
|
||||
$this->batterie(1, 30.0, 80.0, 10000.0, 8000.0),
|
||||
], 3000.0, 2000.0, 0.5);
|
||||
|
||||
self::assertSame(40.0, $aggregat['Kapazitaet_kWh']);
|
||||
self::assertSame(65.0, $aggregat['Ladezustand_Prozent']);
|
||||
self::assertSame(15000.0, $aggregat['MaxLaden_W']);
|
||||
self::assertSame(13000.0, $aggregat['MaxEntladen_W']);
|
||||
self::assertSame(2.5, $aggregat['SDLKapazitaet_kWh']);
|
||||
self::assertSame(37.5, $aggregat['EigenverbrauchKapazitaet_kWh']);
|
||||
}
|
||||
|
||||
public function testSDLHatBeiGleicherRichtungVorrang(): void
|
||||
{
|
||||
$aggregat = VirtuelleBatterieRegler::aggregiere([
|
||||
$this->batterie(0, 20.0, 50.0, 10000.0, 10000.0),
|
||||
], 6000.0, 6000.0, 0.5);
|
||||
|
||||
$auftrag = VirtuelleBatterieRegler::kombiniere(
|
||||
8000.0,
|
||||
6000.0,
|
||||
$aggregat
|
||||
);
|
||||
|
||||
self::assertSame(6000.0, $auftrag['SDL_W']);
|
||||
self::assertSame(4000.0, $auftrag['Eigenverbrauch_W']);
|
||||
self::assertSame(10000.0, $auftrag['Gesamt_W']);
|
||||
}
|
||||
|
||||
public function testGegenlaeufigeAuftraegeWerdenNettoVerteilt(): void
|
||||
{
|
||||
$aggregat = VirtuelleBatterieRegler::aggregiere([
|
||||
$this->batterie(0, 20.0, 50.0, 10000.0, 10000.0),
|
||||
], 6000.0, 6000.0, 0.5);
|
||||
|
||||
$auftrag = VirtuelleBatterieRegler::kombiniere(
|
||||
4000.0,
|
||||
-3000.0,
|
||||
$aggregat
|
||||
);
|
||||
|
||||
self::assertSame(1000.0, $auftrag['Gesamt_W']);
|
||||
self::assertSame(-3000.0, $auftrag['SDL_W']);
|
||||
self::assertSame(4000.0, $auftrag['Eigenverbrauch_W']);
|
||||
}
|
||||
|
||||
public function testLadenBeginntBeiNiedrigstemLadezustand(): void
|
||||
{
|
||||
$verteilung = VirtuelleBatterieRegler::verteile(7000.0, [
|
||||
$this->batterie(0, 10.0, 80.0, 5000.0, 5000.0),
|
||||
$this->batterie(1, 10.0, 20.0, 5000.0, 5000.0),
|
||||
]);
|
||||
|
||||
self::assertSame(2000.0, $verteilung[0]);
|
||||
self::assertSame(5000.0, $verteilung[1]);
|
||||
}
|
||||
|
||||
public function testIstleistungOrdnetSDLZuerstZu(): void
|
||||
{
|
||||
self::assertSame(
|
||||
['Eigenverbrauch_W' => 4000.0, 'SDL_W' => -3000.0],
|
||||
VirtuelleBatterieRegler::ordneIstleistungZu(1000.0, -3000.0)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{
|
||||
* Index:int,
|
||||
* Name:string,
|
||||
* Kapazitaet_kWh:float,
|
||||
* MaxLaden_W:float,
|
||||
* MaxEntladen_W:float,
|
||||
* Ladezustand_Prozent:float,
|
||||
* Istleistung_W:float
|
||||
* }
|
||||
*/
|
||||
private function batterie(
|
||||
int $index,
|
||||
float $kapazitaet,
|
||||
float $ladezustand,
|
||||
float $maxLaden,
|
||||
float $maxEntladen
|
||||
): array {
|
||||
return [
|
||||
'Index' => $index,
|
||||
'Name' => 'Batterie ' . $index,
|
||||
'Kapazitaet_kWh' => $kapazitaet,
|
||||
'MaxLaden_W' => $maxLaden,
|
||||
'MaxEntladen_W' => $maxEntladen,
|
||||
'Ladezustand_Prozent' => $ladezustand,
|
||||
'Istleistung_W' => 0.0,
|
||||
];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user