initial commit
This commit is contained in:
@@ -0,0 +1,219 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domain\Integration;
|
||||
|
||||
use App\Jobs\SimulateCycleCompletion;
|
||||
use App\Models\Machine;
|
||||
use App\Models\MachineCommand;
|
||||
use App\Models\MachineEvent;
|
||||
use App\Models\MachineStatusHistory;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class SimulatedMachineProvider implements MachineProviderInterface
|
||||
{
|
||||
public function startCycle(Machine $machine, array $payload): MachineCommandResult
|
||||
{
|
||||
$correlationId = (string) Str::uuid();
|
||||
$externalReference = 'sim-'.Str::random(12);
|
||||
$provider = config('laverie.simulation.provider_name', 'simulated');
|
||||
$durationSeconds = (int) config('laverie.simulation.cycle_duration_seconds', 30);
|
||||
$cycleEndsAt = now()->addSeconds($durationSeconds);
|
||||
|
||||
$command = MachineCommand::query()->create([
|
||||
'uuid' => (string) Str::uuid(),
|
||||
'machine_id' => $machine->id,
|
||||
'provider' => $provider,
|
||||
'command_type' => 'start_cycle',
|
||||
'payload' => $payload,
|
||||
'status' => 'acknowledged',
|
||||
'external_reference' => $externalReference,
|
||||
'correlation_id' => $correlationId,
|
||||
'requested_by_user_id' => $payload['requested_by_user_id'] ?? null,
|
||||
'sent_at' => now(),
|
||||
'responded_at' => now(),
|
||||
]);
|
||||
|
||||
$previousStatus = $machine->status;
|
||||
|
||||
$machine->update([
|
||||
'status' => 'running',
|
||||
'current_user_id' => $payload['requested_by_user_id'] ?? $machine->current_user_id,
|
||||
'cycle_started_at' => now(),
|
||||
'cycle_ends_at' => $cycleEndsAt,
|
||||
'last_heartbeat_at' => now(),
|
||||
]);
|
||||
|
||||
$this->recordStatusChange($machine, $previousStatus, 'running', $provider, 'cycle_started');
|
||||
$this->recordEvent($machine, $provider, 'cycle_started', [
|
||||
'command_uuid' => $command->uuid,
|
||||
'wash_uuid' => $payload['wash_uuid'] ?? null,
|
||||
]);
|
||||
|
||||
SimulateCycleCompletion::dispatch(
|
||||
$machine->id,
|
||||
$command->id,
|
||||
$payload['wash_id'] ?? null,
|
||||
)->delay($cycleEndsAt);
|
||||
|
||||
return MachineCommandResult::success('acknowledged', $externalReference, $correlationId);
|
||||
}
|
||||
|
||||
public function stopCycle(Machine $machine, array $payload = []): MachineCommandResult
|
||||
{
|
||||
$provider = config('laverie.simulation.provider_name', 'simulated');
|
||||
$correlationId = (string) Str::uuid();
|
||||
|
||||
MachineCommand::query()->create([
|
||||
'uuid' => (string) Str::uuid(),
|
||||
'machine_id' => $machine->id,
|
||||
'provider' => $provider,
|
||||
'command_type' => 'stop_cycle',
|
||||
'payload' => $payload,
|
||||
'status' => 'acknowledged',
|
||||
'correlation_id' => $correlationId,
|
||||
'sent_at' => now(),
|
||||
'responded_at' => now(),
|
||||
]);
|
||||
|
||||
$previousStatus = $machine->status;
|
||||
|
||||
$machine->update([
|
||||
'status' => 'available',
|
||||
'current_user_id' => null,
|
||||
'cycle_started_at' => null,
|
||||
'cycle_ends_at' => null,
|
||||
'last_heartbeat_at' => now(),
|
||||
]);
|
||||
|
||||
$this->recordStatusChange($machine, $previousStatus, 'available', $provider, 'cycle_stopped');
|
||||
$this->recordEvent($machine, $provider, 'cycle_completed', ['reason' => 'manual_stop']);
|
||||
|
||||
return MachineCommandResult::success('acknowledged', null, $correlationId);
|
||||
}
|
||||
|
||||
public function refreshStatus(Machine $machine): MachineStatusSnapshot
|
||||
{
|
||||
$machine->refresh();
|
||||
|
||||
return new MachineStatusSnapshot(
|
||||
status: $machine->status,
|
||||
lastHeartbeat: $machine->last_heartbeat_at,
|
||||
metadata: [
|
||||
'cycle_started_at' => $machine->cycle_started_at?->toIso8601String(),
|
||||
'cycle_ends_at' => $machine->cycle_ends_at?->toIso8601String(),
|
||||
'current_user_id' => $machine->current_user_id,
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
public function handleInboundEvent(array $payload): void
|
||||
{
|
||||
$externalMachineId = $payload['external_machine_id'] ?? $payload['machine_id'] ?? null;
|
||||
|
||||
if ($externalMachineId === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$integration = \App\Models\MachineIntegration::query()
|
||||
->where('external_machine_id', (string) $externalMachineId)
|
||||
->where('is_active', true)
|
||||
->first();
|
||||
|
||||
if ($integration === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$machine = $integration->machine;
|
||||
$provider = $integration->provider;
|
||||
$eventType = $payload['event_type'] ?? 'status_changed';
|
||||
|
||||
if ($eventType === 'heartbeat') {
|
||||
$machine->update(['last_heartbeat_at' => now()]);
|
||||
|
||||
if ($machine->status === 'offline') {
|
||||
$previousStatus = $machine->status;
|
||||
$machine->update(['status' => 'available']);
|
||||
$this->recordStatusChange($machine, $previousStatus, 'available', $provider, 'heartbeat');
|
||||
}
|
||||
}
|
||||
|
||||
if ($eventType === 'machine_offline') {
|
||||
$previousStatus = $machine->status;
|
||||
$machine->update(['status' => 'offline']);
|
||||
$this->recordStatusChange($machine, $previousStatus, 'offline', $provider, 'machine_offline');
|
||||
}
|
||||
|
||||
if ($eventType === 'machine_online') {
|
||||
$previousStatus = $machine->status;
|
||||
$machine->update(['status' => 'available', 'last_heartbeat_at' => now()]);
|
||||
$this->recordStatusChange($machine, $previousStatus, 'available', $provider, 'machine_online');
|
||||
}
|
||||
|
||||
if ($eventType === 'cycle_completed') {
|
||||
$previousStatus = $machine->status;
|
||||
$machine->update([
|
||||
'status' => 'available',
|
||||
'current_user_id' => null,
|
||||
'cycle_started_at' => null,
|
||||
'cycle_ends_at' => null,
|
||||
'last_heartbeat_at' => now(),
|
||||
]);
|
||||
$this->recordStatusChange($machine, $previousStatus, 'available', $provider, 'cycle_completed');
|
||||
}
|
||||
|
||||
if ($eventType === 'error_reported') {
|
||||
$previousStatus = $machine->status;
|
||||
$machine->update(['status' => 'error']);
|
||||
$this->recordStatusChange($machine, $previousStatus, 'error', $provider, 'error_reported');
|
||||
}
|
||||
|
||||
$this->recordEvent(
|
||||
$machine,
|
||||
$provider,
|
||||
$eventType,
|
||||
$payload,
|
||||
$payload['external_event_id'] ?? null,
|
||||
);
|
||||
}
|
||||
|
||||
private function recordStatusChange(
|
||||
Machine $machine,
|
||||
?string $previousStatus,
|
||||
string $newStatus,
|
||||
string $source,
|
||||
string $reason,
|
||||
): void {
|
||||
MachineStatusHistory::query()->create([
|
||||
'machine_id' => $machine->id,
|
||||
'previous_status' => $previousStatus,
|
||||
'new_status' => $newStatus,
|
||||
'source' => $source,
|
||||
'reason' => $reason,
|
||||
'created_at' => Carbon::now(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $payload
|
||||
*/
|
||||
private function recordEvent(
|
||||
Machine $machine,
|
||||
string $provider,
|
||||
string $eventType,
|
||||
array $payload,
|
||||
?string $externalEventId = null,
|
||||
): void {
|
||||
MachineEvent::query()->create([
|
||||
'machine_id' => $machine->id,
|
||||
'provider' => $provider,
|
||||
'external_event_id' => $externalEventId,
|
||||
'event_type' => $eventType,
|
||||
'payload' => $payload,
|
||||
'occurred_at' => now(),
|
||||
'received_at' => now(),
|
||||
'processed_at' => now(),
|
||||
'processing_status' => 'processed',
|
||||
]);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user