Handle relay command job timeouts for edge gateways
- Introduce `expireTimedOutRelayStatusCommandJobs` to clean up long-pending relay status command jobs. - Add `TIMED_OUT` status for relay command jobs and incorporate it into job status evaluations. - Refactor command job finalization to support timeout-specific error messaging. - Improve handling of fast-path failures in edge broker commands.
This commit is contained in:
@@ -323,6 +323,7 @@ class moduleSelfServeRoute
|
||||
}
|
||||
// Execute the command
|
||||
try {
|
||||
$this->applyShellyTransportOverride($lane);
|
||||
$args = new \modules\selfserve\classes\selfserve_lane_command_arguments();
|
||||
$args->setParameters([
|
||||
...$this->getParametersAsArray(), // Pass all parameters
|
||||
@@ -338,6 +339,7 @@ class moduleSelfServeRoute
|
||||
'elapsed_wash_time' => $lane->getElapsedWashTime(),
|
||||
'license_plate' => $lane->getLicensePlate(),
|
||||
'customer_number' => $lane->getCustomerNumber(),
|
||||
'transport' => $this->requestedShellyTransportOverride(),
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
if (
|
||||
@@ -409,12 +411,18 @@ class moduleSelfServeRoute
|
||||
}
|
||||
}
|
||||
// Persist on lane cache (overwrites previous allowed services)
|
||||
$relay_sync = $lane->syncMachineRelayFromVisibleServices($allowed_services, true);
|
||||
$response->success([
|
||||
'lane_id' => $lane_id,
|
||||
'allowed_services' => $allowed_services,
|
||||
'relay_sync' => $relay_sync,
|
||||
]);
|
||||
try {
|
||||
$this->applyShellyTransportOverride($lane);
|
||||
$relay_sync = $lane->syncMachineRelayFromVisibleServices($allowed_services, true);
|
||||
$response->success([
|
||||
'lane_id' => $lane_id,
|
||||
'allowed_services' => $allowed_services,
|
||||
'relay_sync' => $relay_sync,
|
||||
'transport' => $this->requestedShellyTransportOverride(),
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
$response->error('Failed to set allowed services: ' . $e->getMessage(), 400);
|
||||
}
|
||||
}, [
|
||||
'modules_selfserve_lane_services_set_allowed' => 'Set allowed services for a lane based on currently shown tasks (post-Q&A)'
|
||||
]);
|
||||
@@ -472,14 +480,7 @@ class moduleSelfServeRoute
|
||||
try {
|
||||
$this->applyShellyTransportOverride($lane);
|
||||
$status = $lane->getMachineProgramPickerRelayStatus();
|
||||
$response->success([
|
||||
'lane_id' => $lane_id,
|
||||
'relay' => 'MACHINE_PROGRAM_PICKER',
|
||||
'relay_id' => (string)$status['relay_id'],
|
||||
'online' => (bool)$status['online'],
|
||||
'on' => (bool)$status['on'],
|
||||
'transport' => $this->requestedShellyTransportOverride(),
|
||||
]);
|
||||
$response->success($this->buildRelayStatusResponse($lane_id, 'MACHINE_PROGRAM_PICKER', $status));
|
||||
} catch (\Exception $e) {
|
||||
$response->error('Failed to get MACHINE_PROGRAM_PICKER relay status: ' . $e->getMessage(), 400);
|
||||
}
|
||||
@@ -508,15 +509,9 @@ class moduleSelfServeRoute
|
||||
$this->applyShellyTransportOverride($lane);
|
||||
$lane->setMachineProgramPickerRelayStatus((bool)$on);
|
||||
$status = $lane->getMachineProgramPickerRelayStatus();
|
||||
$response->success([
|
||||
'lane_id' => $lane_id,
|
||||
'relay' => 'MACHINE_PROGRAM_PICKER',
|
||||
$response->success($this->buildRelayStatusResponse($lane_id, 'MACHINE_PROGRAM_PICKER', $status, [
|
||||
'requested_on' => (bool)$on,
|
||||
'relay_id' => (string)$status['relay_id'],
|
||||
'online' => (bool)$status['online'],
|
||||
'on' => (bool)$status['on'],
|
||||
'transport' => $this->requestedShellyTransportOverride(),
|
||||
]);
|
||||
]));
|
||||
} catch (\Exception $e) {
|
||||
$response->error('Failed to set MACHINE_PROGRAM_PICKER relay status: ' . $e->getMessage(), 400);
|
||||
}
|
||||
@@ -538,14 +533,7 @@ class moduleSelfServeRoute
|
||||
try {
|
||||
$this->applyShellyTransportOverride($lane);
|
||||
$status = $lane->getMachineCleanerRelayStatus();
|
||||
$response->success([
|
||||
'lane_id' => $lane_id,
|
||||
'relay' => 'MACHINE_CLEANER',
|
||||
'relay_id' => (string)$status['relay_id'],
|
||||
'online' => (bool)$status['online'],
|
||||
'on' => (bool)$status['on'],
|
||||
'transport' => $this->requestedShellyTransportOverride(),
|
||||
]);
|
||||
$response->success($this->buildRelayStatusResponse($lane_id, 'MACHINE_CLEANER', $status));
|
||||
} catch (\Exception $e) {
|
||||
$response->error('Failed to get MACHINE_CLEANER relay status: ' . $e->getMessage(), 400);
|
||||
}
|
||||
@@ -574,15 +562,9 @@ class moduleSelfServeRoute
|
||||
$this->applyShellyTransportOverride($lane);
|
||||
$lane->setMachineCleanerRelayStatus((bool)$on);
|
||||
$status = $lane->getMachineCleanerRelayStatus();
|
||||
$response->success([
|
||||
'lane_id' => $lane_id,
|
||||
'relay' => 'MACHINE_CLEANER',
|
||||
$response->success($this->buildRelayStatusResponse($lane_id, 'MACHINE_CLEANER', $status, [
|
||||
'requested_on' => (bool)$on,
|
||||
'relay_id' => (string)$status['relay_id'],
|
||||
'online' => (bool)$status['online'],
|
||||
'on' => (bool)$status['on'],
|
||||
'transport' => $this->requestedShellyTransportOverride(),
|
||||
]);
|
||||
]));
|
||||
} catch (\Exception $e) {
|
||||
$response->error('Failed to set MACHINE_CLEANER relay status: ' . $e->getMessage(), 400);
|
||||
}
|
||||
@@ -604,14 +586,7 @@ class moduleSelfServeRoute
|
||||
try {
|
||||
$this->applyShellyTransportOverride($lane);
|
||||
$status = $lane->getMachineRelayStatus();
|
||||
$response->success([
|
||||
'lane_id' => $lane_id,
|
||||
'relay' => 'MACHINE',
|
||||
'relay_id' => (string)$status['relay_id'],
|
||||
'online' => (bool)$status['online'],
|
||||
'on' => (bool)$status['on'],
|
||||
'transport' => $this->requestedShellyTransportOverride(),
|
||||
]);
|
||||
$response->success($this->buildRelayStatusResponse($lane_id, 'MACHINE', $status));
|
||||
} catch (\Exception $e) {
|
||||
$response->error('Failed to get MACHINE relay status: ' . $e->getMessage(), 400);
|
||||
}
|
||||
@@ -657,15 +632,9 @@ class moduleSelfServeRoute
|
||||
} catch (\Throwable $ignored) {}
|
||||
|
||||
$status = $lane->getMachineRelayStatus();
|
||||
$response->success([
|
||||
'lane_id' => $lane_id,
|
||||
'relay' => 'MACHINE',
|
||||
$response->success($this->buildRelayStatusResponse($lane_id, 'MACHINE', $status, [
|
||||
'requested_on' => (bool)$on,
|
||||
'relay_id' => (string)$status['relay_id'],
|
||||
'online' => (bool)$status['online'],
|
||||
'on' => (bool)$status['on'],
|
||||
'transport' => $this->requestedShellyTransportOverride(),
|
||||
]);
|
||||
]));
|
||||
} catch (\Exception $e) {
|
||||
$response->error('Failed to set MACHINE relay status: ' . $e->getMessage(), 400);
|
||||
}
|
||||
@@ -690,8 +659,15 @@ class moduleSelfServeRoute
|
||||
}
|
||||
$lane = $selfserve->lane($lane_id);
|
||||
try {
|
||||
$lane->turnOnRelay(selfserve_lane_relay::MACHINE_PROGRAM_PICKER);
|
||||
$response->success(['lane_id' => $lane_id, 'relay' => 'MACHINE_PROGRAM_PICKER', 'enabled' => true, 'duration' => $duration]);
|
||||
$this->applyShellyTransportOverride($lane);
|
||||
$lane->turnOnRelay(selfserve_lane_relay::MACHINE_PROGRAM_PICKER, $duration);
|
||||
$response->success([
|
||||
'lane_id' => $lane_id,
|
||||
'relay' => 'MACHINE_PROGRAM_PICKER',
|
||||
'enabled' => true,
|
||||
'duration' => $duration,
|
||||
'transport' => $this->requestedShellyTransportOverride(),
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
$response->error('Failed to enable MACHINE_PROGRAM_PICKER relay: ' . $e->getMessage(), 400);
|
||||
}
|
||||
@@ -716,8 +692,15 @@ class moduleSelfServeRoute
|
||||
}
|
||||
$lane = $selfserve->lane($lane_id);
|
||||
try {
|
||||
$lane->turnOnRelay(selfserve_lane_relay::MACHINE_CLEANER);
|
||||
$response->success(['lane_id' => $lane_id, 'relay' => 'MACHINE_CLEANER', 'enabled' => true, 'duration' => $duration]);
|
||||
$this->applyShellyTransportOverride($lane);
|
||||
$lane->turnOnRelay(selfserve_lane_relay::MACHINE_CLEANER, $duration);
|
||||
$response->success([
|
||||
'lane_id' => $lane_id,
|
||||
'relay' => 'MACHINE_CLEANER',
|
||||
'enabled' => true,
|
||||
'duration' => $duration,
|
||||
'transport' => $this->requestedShellyTransportOverride(),
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
$response->error('Failed to enable MACHINE_CLEANER relay: ' . $e->getMessage(), 400);
|
||||
}
|
||||
@@ -742,7 +725,8 @@ class moduleSelfServeRoute
|
||||
}
|
||||
$lane = $selfserve->lane($lane_id);
|
||||
try {
|
||||
$lane->turnOnRelay(selfserve_lane_relay::MACHINE);
|
||||
$this->applyShellyTransportOverride($lane);
|
||||
$lane->turnOnRelay(selfserve_lane_relay::MACHINE, $duration);
|
||||
// A started wash should also turn on cleaner when configured.
|
||||
try {
|
||||
if (
|
||||
@@ -753,7 +737,13 @@ class moduleSelfServeRoute
|
||||
$lane->setMachineCleanerRelayStatusHard(true);
|
||||
}
|
||||
} catch (\Throwable $ignored) {}
|
||||
$response->success(['lane_id' => $lane_id, 'relay' => 'MACHINE', 'enabled' => true, 'duration' => $duration]);
|
||||
$response->success([
|
||||
'lane_id' => $lane_id,
|
||||
'relay' => 'MACHINE',
|
||||
'enabled' => true,
|
||||
'duration' => $duration,
|
||||
'transport' => $this->requestedShellyTransportOverride(),
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
$msg = $e->getMessage();
|
||||
// If gating prevented activation, respond with 403
|
||||
@@ -783,8 +773,15 @@ class moduleSelfServeRoute
|
||||
}
|
||||
$lane = $selfserve->lane($lane_id);
|
||||
try {
|
||||
$this->applyShellyTransportOverride($lane);
|
||||
$lane->forceTurnOnRelay(selfserve_lane_relay::MACHINE_PROGRAM_PICKER, $duration);
|
||||
$response->success(['lane_id' => $lane_id, 'relay' => 'MACHINE_PROGRAM_PICKER', 'enabled' => true, 'duration' => $duration]);
|
||||
$response->success([
|
||||
'lane_id' => $lane_id,
|
||||
'relay' => 'MACHINE_PROGRAM_PICKER',
|
||||
'enabled' => true,
|
||||
'duration' => $duration,
|
||||
'transport' => $this->requestedShellyTransportOverride(),
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
$response->error('Failed to force enable MACHINE_PROGRAM_PICKER relay: ' . $e->getMessage(), 400);
|
||||
}
|
||||
@@ -804,8 +801,14 @@ class moduleSelfServeRoute
|
||||
self::requireMinValue($lane_id, 1);
|
||||
$lane = $selfserve->lane($lane_id);
|
||||
try {
|
||||
$this->applyShellyTransportOverride($lane);
|
||||
$lane->forceTurnOffRelay(selfserve_lane_relay::MACHINE_PROGRAM_PICKER);
|
||||
$response->success(['lane_id' => $lane_id, 'relay' => 'MACHINE_PROGRAM_PICKER', 'disabled' => true]);
|
||||
$response->success([
|
||||
'lane_id' => $lane_id,
|
||||
'relay' => 'MACHINE_PROGRAM_PICKER',
|
||||
'disabled' => true,
|
||||
'transport' => $this->requestedShellyTransportOverride(),
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
$response->error('Failed to force disable MACHINE_PROGRAM_PICKER relay: ' . $e->getMessage(), 400);
|
||||
}
|
||||
@@ -830,8 +833,15 @@ class moduleSelfServeRoute
|
||||
}
|
||||
$lane = $selfserve->lane($lane_id);
|
||||
try {
|
||||
$this->applyShellyTransportOverride($lane);
|
||||
$lane->forceTurnOnRelay(selfserve_lane_relay::MACHINE_CLEANER, $duration);
|
||||
$response->success(['lane_id' => $lane_id, 'relay' => 'MACHINE_CLEANER', 'enabled' => true, 'duration' => $duration]);
|
||||
$response->success([
|
||||
'lane_id' => $lane_id,
|
||||
'relay' => 'MACHINE_CLEANER',
|
||||
'enabled' => true,
|
||||
'duration' => $duration,
|
||||
'transport' => $this->requestedShellyTransportOverride(),
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
$response->error('Failed to force enable MACHINE_CLEANER relay: ' . $e->getMessage(), 400);
|
||||
}
|
||||
@@ -851,8 +861,14 @@ class moduleSelfServeRoute
|
||||
self::requireMinValue($lane_id, 1);
|
||||
$lane = $selfserve->lane($lane_id);
|
||||
try {
|
||||
$this->applyShellyTransportOverride($lane);
|
||||
$lane->forceTurnOffRelay(selfserve_lane_relay::MACHINE_CLEANER);
|
||||
$response->success(['lane_id' => $lane_id, 'relay' => 'MACHINE_CLEANER', 'disabled' => true]);
|
||||
$response->success([
|
||||
'lane_id' => $lane_id,
|
||||
'relay' => 'MACHINE_CLEANER',
|
||||
'disabled' => true,
|
||||
'transport' => $this->requestedShellyTransportOverride(),
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
$response->error('Failed to force disable MACHINE_CLEANER relay: ' . $e->getMessage(), 400);
|
||||
}
|
||||
@@ -878,6 +894,7 @@ class moduleSelfServeRoute
|
||||
$license_plate = $this->isParametersSet(['license_plate']) ? (string)$this->getParameter('license_plate') : null;
|
||||
$lane = $selfserve->lane($lane_id);
|
||||
try {
|
||||
$this->applyShellyTransportOverride($lane);
|
||||
// If lane looks idle/available, mark as occupied and in wash, set timers
|
||||
if ($lane->getLaneStatus()->equals(\modules\selfserve\helpers\selfserve_lane_status::AVAILABLE)) {
|
||||
$lane->setLaneStatus(\modules\selfserve\helpers\selfserve_lane_status::OCCUPIED);
|
||||
@@ -924,6 +941,7 @@ class moduleSelfServeRoute
|
||||
'status' => $lane->getLaneStatus()->name,
|
||||
'state' => $lane->getLaneState()->name,
|
||||
'wash_start_time' => $lane->getWashStartTime(),
|
||||
'transport' => $this->requestedShellyTransportOverride(),
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
$response->error('Failed to force-enable MACHINE: ' . $e->getMessage(), 400);
|
||||
@@ -945,6 +963,7 @@ class moduleSelfServeRoute
|
||||
$license_plate = $this->isParametersSet(['license_plate']) ? (string)$this->getParameter('license_plate') : null;
|
||||
$lane = $selfserve->lane($lane_id);
|
||||
try {
|
||||
$this->applyShellyTransportOverride($lane);
|
||||
// Ensure lane reflects an ongoing wash, but with machine off
|
||||
if ($lane->getLaneStatus()->equals(\modules\selfserve\helpers\selfserve_lane_status::AVAILABLE)) {
|
||||
$lane->setLaneStatus(\modules\selfserve\helpers\selfserve_lane_status::OCCUPIED);
|
||||
@@ -990,6 +1009,7 @@ class moduleSelfServeRoute
|
||||
'status' => $lane->getLaneStatus()->name,
|
||||
'state' => $lane->getLaneState()->name,
|
||||
'wash_start_time' => $lane->getWashStartTime(),
|
||||
'transport' => $this->requestedShellyTransportOverride(),
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
$response->error('Failed to force-disable MACHINE: ' . $e->getMessage(), 400);
|
||||
@@ -999,6 +1019,40 @@ class moduleSelfServeRoute
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string,mixed> $status
|
||||
* @param array<string,mixed> $extra
|
||||
* @return array<string,mixed>
|
||||
*/
|
||||
private function buildRelayStatusResponse(int $lane_id, string $relay, array $status, array $extra = []): array
|
||||
{
|
||||
$on = (bool)($status['on'] ?? false);
|
||||
$switch_status = is_array($status['status'] ?? null) ? $status['status'] : [];
|
||||
if (!isset($switch_status['switch:0']) || !is_array($switch_status['switch:0'])) {
|
||||
$switch_status['switch:0'] = [];
|
||||
}
|
||||
$switch_status['switch:0']['output'] = (bool)($switch_status['switch:0']['output'] ?? $on);
|
||||
|
||||
$payload = [
|
||||
'lane_id' => $lane_id,
|
||||
'relay' => $relay,
|
||||
...$extra,
|
||||
'relay_id' => (string)($status['relay_id'] ?? ''),
|
||||
'online' => (bool)($status['online'] ?? false),
|
||||
'on' => $on,
|
||||
'status' => $switch_status,
|
||||
'transport' => $this->requestedShellyTransportOverride(),
|
||||
];
|
||||
|
||||
foreach (['binding', 'execution', 'raw'] as $metadata_key) {
|
||||
if (isset($status[$metadata_key]) && is_array($status[$metadata_key]) && $status[$metadata_key] !== []) {
|
||||
$payload[$metadata_key] = $status[$metadata_key];
|
||||
}
|
||||
}
|
||||
|
||||
return $payload;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \Exception
|
||||
*/
|
||||
@@ -1036,7 +1090,7 @@ class moduleSelfServeRoute
|
||||
}
|
||||
|
||||
if (!in_array($normalized, ['local', 'cloud'], true)) {
|
||||
throw new \InvalidArgumentException('Invalid transport. Expected local or cloud.');
|
||||
throw new \InvalidArgumentException('Invalid transport. Expected local, gateway, or cloud.');
|
||||
}
|
||||
|
||||
return $normalized;
|
||||
|
||||
Reference in New Issue
Block a user