fix(manager): Fahrplanvorgabe linear interpolieren
This commit is contained in:
+4
-23
@@ -2757,29 +2757,10 @@ class Manager extends IPSModule implements ManagerSchnittstelle
|
||||
return null;
|
||||
}
|
||||
|
||||
$jetzt = time();
|
||||
$naechsterAbstand = PHP_INT_MAX;
|
||||
$vorgabe = null;
|
||||
foreach ($fahrplan['points'] as $punkt) {
|
||||
if (!is_array($punkt)
|
||||
|| !is_string($punkt['time'] ?? null)
|
||||
|| (!is_int($punkt['gridTargetW'] ?? null)
|
||||
&& !is_float($punkt['gridTargetW'] ?? null))
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
$zeitpunkt = strtotime($punkt['time']);
|
||||
if ($zeitpunkt === false) {
|
||||
continue;
|
||||
}
|
||||
$abstand = abs($zeitpunkt - $jetzt);
|
||||
if ($abstand < $naechsterAbstand) {
|
||||
$naechsterAbstand = $abstand;
|
||||
$vorgabe = (float) $punkt['gridTargetW'];
|
||||
}
|
||||
}
|
||||
|
||||
return $naechsterAbstand <= 1800 ? $vorgabe : null;
|
||||
return ManagerRegler::interpolierteFahrplanVorgabe(
|
||||
$fahrplan['points'],
|
||||
time()
|
||||
);
|
||||
}
|
||||
|
||||
private function synchronisiereAnlagentopologie(): void
|
||||
|
||||
@@ -19,6 +19,71 @@ final class ManagerRegler
|
||||
public const BETRIEBSART_PV = Nachrichtenvertrag::BETRIEBSART_PV;
|
||||
public const BETRIEBSART_PEAK = Nachrichtenvertrag::BETRIEBSART_PEAK;
|
||||
|
||||
/**
|
||||
* @param list<array<string, mixed>> $punkte
|
||||
*/
|
||||
public static function interpolierteFahrplanVorgabe(
|
||||
array $punkte,
|
||||
int $jetzt,
|
||||
int $maximalerPunktabstandSekunden = 1800
|
||||
): ?float {
|
||||
if ($maximalerPunktabstandSekunden < 0) {
|
||||
throw new InvalidArgumentException('Maximaler Fahrplanpunktabstand darf nicht negativ sein.');
|
||||
}
|
||||
|
||||
$gueltigePunkte = [];
|
||||
foreach ($punkte as $punkt) {
|
||||
if (!is_array($punkt)
|
||||
|| !is_string($punkt['time'] ?? null)
|
||||
|| (!is_int($punkt['gridTargetW'] ?? null)
|
||||
&& !is_float($punkt['gridTargetW'] ?? null))
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
$zeitpunkt = strtotime($punkt['time']);
|
||||
$vorgabe = (float) $punkt['gridTargetW'];
|
||||
if ($zeitpunkt === false || !is_finite($vorgabe)) {
|
||||
continue;
|
||||
}
|
||||
$gueltigePunkte[$zeitpunkt] = $vorgabe;
|
||||
}
|
||||
if ($gueltigePunkte === []) {
|
||||
return null;
|
||||
}
|
||||
ksort($gueltigePunkte, SORT_NUMERIC);
|
||||
|
||||
$vorherigerZeitpunkt = null;
|
||||
$vorherigeVorgabe = null;
|
||||
foreach ($gueltigePunkte as $zeitpunkt => $vorgabe) {
|
||||
if ($zeitpunkt === $jetzt) {
|
||||
return $vorgabe;
|
||||
}
|
||||
if ($zeitpunkt > $jetzt) {
|
||||
if ($vorherigerZeitpunkt === null) {
|
||||
return $zeitpunkt - $jetzt <= $maximalerPunktabstandSekunden
|
||||
? $vorgabe
|
||||
: null;
|
||||
}
|
||||
if ($jetzt - $vorherigerZeitpunkt > $maximalerPunktabstandSekunden
|
||||
|| $zeitpunkt - $jetzt > $maximalerPunktabstandSekunden
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$anteil = ($jetzt - $vorherigerZeitpunkt) / ($zeitpunkt - $vorherigerZeitpunkt);
|
||||
|
||||
return $vorherigeVorgabe + (($vorgabe - $vorherigeVorgabe) * $anteil);
|
||||
}
|
||||
$vorherigerZeitpunkt = $zeitpunkt;
|
||||
$vorherigeVorgabe = $vorgabe;
|
||||
}
|
||||
|
||||
return $vorherigerZeitpunkt !== null
|
||||
&& $jetzt - $vorherigerZeitpunkt <= $maximalerPunktabstandSekunden
|
||||
? $vorherigeVorgabe
|
||||
: null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, float|int> $monatsgrenzen Monatsnummer 1 bis 12 als Schluessel
|
||||
*/
|
||||
|
||||
@@ -11,6 +11,59 @@ use PHPUnit\Framework\TestCase;
|
||||
|
||||
final class ManagerReglerTest extends TestCase
|
||||
{
|
||||
public function testFahrplanVorgabeWirdZwischenPunktenLinearInterpoliert(): void
|
||||
{
|
||||
$punkte = [
|
||||
['time' => '2026-09-30T11:35:00+00:00', 'gridTargetW' => 20486.254],
|
||||
['time' => '2026-09-30T11:30:00+00:00', 'gridTargetW' => 15664.283],
|
||||
];
|
||||
|
||||
$vorgabe = ManagerRegler::interpolierteFahrplanVorgabe(
|
||||
$punkte,
|
||||
(int) strtotime('2026-09-30T11:32:30+00:00')
|
||||
);
|
||||
|
||||
self::assertEqualsWithDelta(18075.2685, $vorgabe, 0.0001);
|
||||
}
|
||||
|
||||
public function testFahrplanVorgabeVerwendetRandpunktInnerhalbDerToleranz(): void
|
||||
{
|
||||
$punkte = [
|
||||
['time' => '2026-09-30T11:30:00+00:00', 'gridTargetW' => 15664.283],
|
||||
['time' => '2026-09-30T11:35:00+00:00', 'gridTargetW' => 20486.254],
|
||||
];
|
||||
|
||||
self::assertSame(
|
||||
15664.283,
|
||||
ManagerRegler::interpolierteFahrplanVorgabe(
|
||||
$punkte,
|
||||
(int) strtotime('2026-09-30T11:29:44+00:00')
|
||||
)
|
||||
);
|
||||
self::assertSame(
|
||||
20486.254,
|
||||
ManagerRegler::interpolierteFahrplanVorgabe(
|
||||
$punkte,
|
||||
(int) strtotime('2026-09-30T11:35:10+00:00')
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public function testFahrplanVorgabeVerwirftZuGrosseZeitlicheLuecke(): void
|
||||
{
|
||||
$punkte = [
|
||||
['time' => '2026-09-30T11:00:00+00:00', 'gridTargetW' => 10000],
|
||||
['time' => '2026-09-30T12:00:00+00:00', 'gridTargetW' => 20000],
|
||||
];
|
||||
|
||||
self::assertNull(
|
||||
ManagerRegler::interpolierteFahrplanVorgabe(
|
||||
$punkte,
|
||||
(int) strtotime('2026-09-30T11:31:00+00:00')
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public function testOhnePeakBegrenzungBleibtSolarladenAktiv(): void
|
||||
{
|
||||
self::assertSame(
|
||||
|
||||
Reference in New Issue
Block a user