From 3293a76c9159c56b7d1286d078d6b6dde7077a87 Mon Sep 17 00:00:00 2001 From: sky121113 Date: Wed, 24 Jun 2026 08:57:56 +0800 Subject: [PATCH 1/2] =?UTF-8?q?[FIX]=20=E5=BA=AB=E5=AD=98=E6=95=88?= =?UTF-8?q?=E6=9C=9F=E7=AE=A1=E7=90=86=E8=A3=9C=E4=B8=8A=20MQTT=20?= =?UTF-8?q?=E4=B8=8B=E7=99=BC=E9=80=BE=E6=99=82=E8=87=AA=E5=8B=95=E8=A7=A3?= =?UTF-8?q?=E9=8E=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. slotsAjax 的 is_pending 僅計入 1 分鐘內的 pending reload_stock 指令,超過視為逾時自動解鎖,避免機台未回 ACK 時介面永久卡住 2. updateSlot 重新下發時,逾時舊指令標記為 timeout、未逾時標記為 superseded,與 dispense 逾時邏輯一致 Co-Authored-By: Claude Opus 4.8 (1M context) --- app/Http/Controllers/Admin/MachineController.php | 3 +++ app/Services/Machine/MachineService.php | 11 ++++++++--- 2 files changed, 11 insertions(+), 3 deletions(-) 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'), ]); } From a2faace4bd017aaf691739bd28c1aff1aa22b75a Mon Sep 17 00:00:00 2001 From: sky121113 Date: Wed, 24 Jun 2026 09:03:27 +0800 Subject: [PATCH 2/2] =?UTF-8?q?[FIX]=20=E9=81=A0=E7=AB=AF=E6=8C=87?= =?UTF-8?q?=E4=BB=A4=E5=BA=AB=E5=AD=98=E6=95=88=E6=9C=9F=E7=B4=80=E9=8C=84?= =?UTF-8?q?=E8=A3=9C=E4=B8=8A=20timeout=20=E7=8B=80=E6=85=8B=E5=A4=9A?= =?UTF-8?q?=E8=AA=9E=E7=B3=BB=E8=88=87=E5=BE=BD=E7=AB=A0=E6=A8=A3=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. stock.blade.php getCommandStatus 補 timeout 對照,避免直接顯示原字串 TIMEOUT 2. getCommandBadgeClass 補 timeout 徽章樣式 (rose,與 failed 一致) Co-Authored-By: Claude Opus 4.8 (1M context) --- resources/views/admin/remote/stock.blade.php | 2 ++ 1 file changed, 2 insertions(+) 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;