diff --git a/app/Http/Controllers/Admin/MachineController.php b/app/Http/Controllers/Admin/MachineController.php index fe8d57b..6386ce4 100644 --- a/app/Http/Controllers/Admin/MachineController.php +++ b/app/Http/Controllers/Admin/MachineController.php @@ -208,9 +208,12 @@ class MachineController extends AdminController ->get(); // 取得該機台目前所有等待中的庫存更新指令 (Pending Commands) + // 僅將「1 分鐘內」的 pending 視為鎖定中;超過 1 分鐘未收到機台回傳 (ACK) + // 視為逾時,自動解除鎖定,讓使用者可重新編輯與重新下發 (與 dispense 逾時邏輯一致) $pendingSlotNos = \App\Models\Machine\RemoteCommand::where('machine_id', $machine->id) ->where('command_type', 'reload_stock') ->where('status', 'pending') + ->where('created_at', '>=', now()->subMinute()) ->get() ->pluck('payload.slot_no') ->flatten() diff --git a/app/Services/Machine/MachineService.php b/app/Services/Machine/MachineService.php index 7964815..d93b21d 100644 --- a/app/Services/Machine/MachineService.php +++ b/app/Services/Machine/MachineService.php @@ -508,10 +508,15 @@ class MachineService ->first(); if ($pendingCommand) { - // 直接覆蓋:將前一個尚未執行的指令標記為「已取代」 + // 重新下發時覆蓋前一筆尚未執行的指令。 + // 若舊指令已超過 1 分鐘未收到機台回傳 (ACK),視為逾時 (timeout); + // 否則為使用者主動覆蓋 (superseded)。與 dispense 逾時邏輯一致。 + $isTimedOut = $pendingCommand->created_at->lt(now()->subMinute()); $pendingCommand->update([ - 'status' => 'superseded', - 'note' => __('Superseded by new command'), + 'status' => $isTimedOut ? 'timeout' : 'superseded', + 'note' => $isTimedOut + ? __('Superseded by new command (Timeout)') + : __('Superseded by new command'), ]); } diff --git a/resources/views/admin/remote/stock.blade.php b/resources/views/admin/remote/stock.blade.php index e0f8090..5e239cc 100644 --- a/resources/views/admin/remote/stock.blade.php +++ b/resources/views/admin/remote/stock.blade.php @@ -296,6 +296,7 @@ case 'sent': return 'bg-cyan-100 text-cyan-600 dark:bg-cyan-500/10 dark:text-cyan-400 border-cyan-200 dark:border-cyan-500/20'; case 'success': return 'bg-emerald-100 text-emerald-600 dark:bg-emerald-500/10 dark:text-emerald-400 border-emerald-200 dark:border-emerald-500/20'; case 'failed': return 'bg-rose-100 text-rose-600 dark:bg-rose-500/10 dark:text-rose-400 border-rose-200 dark:border-rose-500/20'; + case 'timeout': return 'bg-rose-100 text-rose-600 dark:bg-rose-500/10 dark:text-rose-400 border-rose-200 dark:border-rose-500/20'; case 'superseded': return 'bg-slate-100 text-slate-500 dark:bg-slate-500/10 dark:text-slate-400 border-slate-200 dark:border-slate-500/20 opacity-80'; default: return 'bg-slate-100 text-slate-600 border-slate-200'; } @@ -322,6 +323,7 @@ 'sent': {{ Js::from(__('Sent')) }}, 'success': {{ Js::from(__('Success')) }}, 'failed': {{ Js::from(__('Failed')) }}, + 'timeout': {{ Js::from(__('Timeout')) }}, 'superseded': {{ Js::from(__('Superseded')) }} }; return statuses[status] || status;