Files
Enelix-EMS/libs/NetzfahrplanV4Geraeteabruf.php
T

102 lines
3.8 KiB
PHP

<?php
declare(strict_types=1);
namespace Belevo\EnelixEMS;
use InvalidArgumentException;
require_once __DIR__.'/NetzfahrplanV4Rueckmeldung.php';
/**
* Binds values to explicitly successful device reads.
*
* Transport calls, object metadata and the clock are injected so the complete
* coordination can be tested without IP-Symcon or real devices.
*/
final class NetzfahrplanV4Geraeteabruf
{
public const MBUS_DEVICE = '{B53BE2E5-892D-4537-94AC-EAC68A469188}';
public const MODBUS_DEVICE = '{90BF9234-B44C-7EB3-280B-FBD6075ADDAA}';
public const MODBUS_ADDRESS = '{CB197E50-273D-4535-8C91-BB35273E3CA5}';
public static function snapshot(
array $config,
callable $moduleId,
callable $refresh,
callable $reader,
callable $clock
): array {
$config = NetzfahrplanV4Rueckmeldung::configuration($config);
$groups = [];
$sourcesById = [];
foreach ($config['sources'] as $source) {
$sourcesById[$source['variableId']] = $source;
if (in_array($source['role'], ['grid', 'physical'], true)) {
$groups[$source['parentId']][] = $source;
}
}
$snapshot = [];
foreach ($groups as $parentId => $sources) {
$module = $moduleId($parentId);
if (!in_array($module, [self::MBUS_DEVICE, self::MODBUS_DEVICE, self::MODBUS_ADDRESS], true)) {
throw new InvalidArgumentException('feedback_refresh_module_unsupported:' . $sources[0]['key']);
}
if ($refresh($parentId, $module) !== true) {
throw new InvalidArgumentException('feedback_device_read_failed:' . $sources[0]['key']);
}
$confirmedAt = $clock();
if (!is_int($confirmedAt) || $confirmedAt < 1) {
throw new InvalidArgumentException('feedback_confirmation_time_invalid');
}
foreach ($sources as $source) {
$record = $reader($source['variableId']);
if (!is_array($record)) {
throw new InvalidArgumentException('feedback_reader_invalid:' . $source['key']);
}
$record['confirmedAt'] = $confirmedAt;
$record['confirmedModuleId'] = $module;
$snapshot[$source['variableId']] = $record;
}
}
foreach ($config['sources'] as $source) {
if (!isset($snapshot[$source['variableId']])) {
$record = $reader($source['variableId']);
if (!is_array($record)) {
throw new InvalidArgumentException('feedback_reader_invalid:' . $source['key']);
}
$snapshot[$source['variableId']] = $record;
}
}
foreach ($snapshot as $variableId => $record) {
$again = $reader($variableId);
$keys = ['value', 'updated', 'changed', 'parentID', 'ident'];
if (!is_array($again) || array_intersect_key($again, array_flip($keys))
!== array_intersect_key($record, array_flip($keys))) {
throw new InvalidArgumentException(
'feedback_changed_after_confirmed_read:' . $sourcesById[$variableId]['key']
);
}
}
$checkedAt = $clock();
if (!is_int($checkedAt) || $checkedAt < 1) {
throw new InvalidArgumentException('feedback_confirmation_time_invalid');
}
foreach ($snapshot as $record) {
if (isset($record['confirmedAt']) && $record['confirmedAt'] > $checkedAt) {
throw new InvalidArgumentException('feedback_confirmation_time_invalid');
}
}
return NetzfahrplanV4Rueckmeldung::read(
$config,
static fn(int $variableId): array => $snapshot[$variableId],
$checkedAt
);
}
}