From 93a9ad7e2d43fee34044e43d4d77f06ed62904e3 Mon Sep 17 00:00:00 2001 From: sky121113 Date: Tue, 12 May 2026 16:41:52 +0800 Subject: [PATCH] =?UTF-8?q?[FIX]=20=E5=84=AA=E5=8C=96=20MQTT=20=E9=81=A0?= =?UTF-8?q?=E7=AB=AF=E9=87=8D=E5=95=9F=E9=82=8F=E8=BC=AF=E8=88=87=E6=A9=9F?= =?UTF-8?q?=E5=8F=B0=E5=BF=99=E7=A2=8C=E7=8B=80=E6=85=8B=E5=88=A4=E6=96=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 在 MyApp 實作 getBusyReason(),整合對話框、購物車、NFC 支付狀態,提供更精確的忙碌診斷資訊。 2. 重構 MqttService 遠端解鎖/重啟邏輯,使用新的忙碌檢查機制並記錄詳細拒絕原因。 3. 修正 HomeActivity 進入時強制清空購物車,確保逾時回首頁後機台能正確進入閒置狀態。 4. 更新 FontendActivity 重啟邏輯,改用 isMachineBusy() 提升穩定性。 5. 移除出貨對話框 (DispatchDialog) 的「異常回報」按鈕,簡化使用者介面。 6. 優化購物車 Adapter (Electric/VMC),確保遠端庫存更新時能即時同步 UI 點選狀態。 --- .../com/unibuy/smartdevice/HomeActivity.java | 3 ++ .../java/com/unibuy/smartdevice/MyApp.java | 33 ++++++++++++++++++- .../smartdevice/service/MqttService.java | 25 +++++++------- .../smartdevice/ui/FontendActivity.java | 11 ++++--- .../RecyclerElectricBuyListAdpter.java | 2 +- .../ui/devs/vmc/RecyclerVmcBuyListAdpter.java | 2 +- .../smartdevice/ui/dialog/DispatchDialog.java | 18 ---------- app/src/main/res/layout/dialog_dispatch.xml | 12 +------ 8 files changed, 58 insertions(+), 48 deletions(-) diff --git a/app/src/main/java/com/unibuy/smartdevice/HomeActivity.java b/app/src/main/java/com/unibuy/smartdevice/HomeActivity.java index 67096fe..54cd485 100644 --- a/app/src/main/java/com/unibuy/smartdevice/HomeActivity.java +++ b/app/src/main/java/com/unibuy/smartdevice/HomeActivity.java @@ -192,6 +192,9 @@ public class HomeActivity extends AppCompatActivityAbstract { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + // 🚩 進入首頁時強制清空購物車,確保機台處於閒置狀態,避免 MQTT 遠端重啟被阻擋 + MyApp.getInstance().getBuyList().clear(); + // 啟動鎖定檢查 com.unibuy.smartdevice.database.SettingsDao settingsDao = new com.unibuy.smartdevice.database.SettingsDao(getApplicationContext()); if (settingsDao.isMachineLocked()) { diff --git a/app/src/main/java/com/unibuy/smartdevice/MyApp.java b/app/src/main/java/com/unibuy/smartdevice/MyApp.java index 37db3d8..9861d71 100644 --- a/app/src/main/java/com/unibuy/smartdevice/MyApp.java +++ b/app/src/main/java/com/unibuy/smartdevice/MyApp.java @@ -118,7 +118,6 @@ public class MyApp extends Application { public List invoiceItems; public ReportDispatchInfoStructure reportDispatchInfoData; public CreditCardReceiptStructure creditCardReceiptInfoData; - public boolean isShowDialog; public volatile boolean isNfcPaymentActive; // 用於控制 NFC 支付感應器的活動門禁 public boolean isDetectHost; @@ -808,14 +807,46 @@ public class MyApp extends Application { this.closeSlot = closeSlot; } + private volatile boolean isShowDialog; + public boolean isShowDialog() { return isShowDialog; } public void setShowDialog(boolean showDialog) { + if (this.isShowDialog != showDialog) { + logs.info("setShowDialog: " + isShowDialog + " -> " + showDialog); + } isShowDialog = showDialog; } + public boolean isMachineBusy() { + return !getBusyReason().isEmpty(); + } + + /** + * 取得機台忙碌的原因,若不忙碌則回傳空字串 + * @return 忙碌原因描述 + */ + public String getBusyReason() { + StringBuilder reason = new StringBuilder(); + if (isShowDialog()) { + reason.append("[Dialog Open] "); + } + if (!getBuyList().isEmpty()) { + reason.append("[Cart not empty: ").append(getBuyList().size()).append(" items] "); + } + if (isNfcPaymentActive()) { + reason.append("[NFC Payment Active] "); + } + + String result = reason.toString().trim(); + if (!result.isEmpty()) { + logs.debug("Machine busy check: " + result); + } + return result; + } + public String getSuAccess() { return suAccess; } diff --git a/app/src/main/java/com/unibuy/smartdevice/service/MqttService.java b/app/src/main/java/com/unibuy/smartdevice/service/MqttService.java index 167e0d3..1e5a4df 100644 --- a/app/src/main/java/com/unibuy/smartdevice/service/MqttService.java +++ b/app/src/main/java/com/unibuy/smartdevice/service/MqttService.java @@ -95,15 +95,20 @@ public class MqttService extends Service { logs.info("Executing remote reboot command..."); long idle = MyApp.getInstance().getIdleSeconds(); - boolean isDialog = MyApp.getInstance().isShowDialog(); + String busyReason = MyApp.getInstance().getBusyReason(); - if (idle < 60 || isDialog) { - String reason = isDialog ? "Dialog is open" : "User interacting (idle: " + idle + "s)"; - logs.info("Remote reboot rejected: " + reason); - mqttManager.publishCommandAck(cmdId, "failed", "Machine busy: " + reason); + // 🚩 若機台忙碌(對話框開著、購物車有東西、NFC 交易中)則拒絕 + if (!busyReason.isEmpty()) { + logs.info("Remote reboot rejected: " + busyReason); + mqttManager.publishCommandAck(cmdId, "failed", "Machine busy: " + busyReason); return; } + // 🚩 若不忙碌但閒置時間不足,僅記錄警告但允許重啟(遠端控制優先) + if (idle < 60) { + logs.info("Remote reboot accepted despite low idle (" + idle + "s), as machine is not busy."); + } + mqttManager.publishCommandAck(cmdId, "success", "Rebooting soon..."); // 🚩 優雅重啟:ACK 發出後先 gracefulDisconnect(不觸發 LWT),再 killProcess new android.os.Handler(android.os.Looper.getMainLooper()) @@ -136,13 +141,11 @@ public class MqttService extends Service { } else if ("unlock".equals(cmd)) { logs.info("Executing remote unlock command..."); - long idle = MyApp.getInstance().getIdleSeconds(); - boolean isDialog = MyApp.getInstance().isShowDialog(); + String busyReason = MyApp.getInstance().getBusyReason(); - if (idle < 60 || isDialog) { - String reason = isDialog ? "Dialog is open" : "User interacting (idle: " + idle + "s)"; - logs.info("Remote unlock/reboot rejected: " + reason); - mqttManager.publishCommandAck(cmdId, "failed", "Machine busy: " + reason); + if (!busyReason.isEmpty()) { + logs.info("Remote unlock rejected: " + busyReason); + mqttManager.publishCommandAck(cmdId, "failed", "Machine busy: " + busyReason); return; } diff --git a/app/src/main/java/com/unibuy/smartdevice/ui/FontendActivity.java b/app/src/main/java/com/unibuy/smartdevice/ui/FontendActivity.java index 3599e92..d3ab362 100644 --- a/app/src/main/java/com/unibuy/smartdevice/ui/FontendActivity.java +++ b/app/src/main/java/com/unibuy/smartdevice/ui/FontendActivity.java @@ -436,7 +436,8 @@ public class FontendActivity extends AppCompatActivityAbstract { } idleRunnable = () -> { - getLogs().info("⚠️ 無操作 3 分鐘,自動返回首頁"); + getLogs().info("⚠️ 無操作 3 分鐘,自動返回首頁(清空購物車)"); + MyApp.getInstance().getBuyList().clear(); // 🚩 逾時返回首頁時,務必清空購物車,避免機台進入永久忙碌狀態 Intent intent = new Intent(FontendActivity.this, HomeActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(intent); @@ -684,7 +685,7 @@ public class FontendActivity extends AppCompatActivityAbstract { protected void execute(HandlerMain handlerMain) { for (int testIndex=0; testIndex < 30; testIndex++) { if (testCount < 3) { - if (!MyApp.getInstance().isShowDialog()) { + if (!MyApp.getInstance().isMachineBusy()) { getLogs().debug("RestartHost testCount:" + testCount); testCount++; } else { @@ -749,9 +750,9 @@ public class FontendActivity extends AppCompatActivityAbstract { // 最多等待 10 分鐘(120 次 * 5 秒),確保機台有機會重啟 for (int i = 0; i < 120; i++) { long idle = MyApp.getInstance().getIdleSeconds(); - boolean isDialog = MyApp.getInstance().isShowDialog(); - if (idle >= 90 && !isDialog) { - getLogs().info("RestartAPP2:閒置 " + idle + " 秒且無對話框,執行真重啟"); + boolean isBusy = MyApp.getInstance().isMachineBusy(); + if (idle >= 90 && !isBusy) { + getLogs().info("RestartAPP2:閒置 " + idle + " 秒且機台不忙碌,執行真重啟"); // 切回主執行緒執行重啟 new android.os.Handler(android.os.Looper.getMainLooper()).post(() -> { Intent intent = new Intent(getApplicationContext(), com.unibuy.smartdevice.HomeActivity.class); diff --git a/app/src/main/java/com/unibuy/smartdevice/ui/devs/electric/RecyclerElectricBuyListAdpter.java b/app/src/main/java/com/unibuy/smartdevice/ui/devs/electric/RecyclerElectricBuyListAdpter.java index 0503750..b06a0fd 100644 --- a/app/src/main/java/com/unibuy/smartdevice/ui/devs/electric/RecyclerElectricBuyListAdpter.java +++ b/app/src/main/java/com/unibuy/smartdevice/ui/devs/electric/RecyclerElectricBuyListAdpter.java @@ -186,7 +186,7 @@ public class RecyclerElectricBuyListAdpter extends RecyclerView.Adapter { - getSrcHandlerMain().start(getClass().getSimpleName(), FontendActivity.Option.RESET_DATA.getOption(), "update data"); - - OneTimeWorkRequest oneTimeWorkRequest = new OneTimeWorkRequest.Builder(UploadLogOneTimeWorker.class) - .setInitialDelay(5, TimeUnit.SECONDS) - .setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 5, TimeUnit.MINUTES) - .build(); - - WorkManager.getInstance(getCtx()).enqueueUniqueWork( - "oneTimeWorkRequest", // 設定唯一名稱,避免重複 - ExistingWorkPolicy.REPLACE, - oneTimeWorkRequest - ); - - getSrcHandlerMain().start(getClass().getSimpleName(), "@"+getCtx().getString(R.string.reported_please_contact_site_for_processing));//已回報,請現場聯絡處理 - dialogCancel(); - }); - setButtonOkText(0); closeDialogCountdown = new CloseDialogOnThreadHandler(getHandlerMain()); diff --git a/app/src/main/res/layout/dialog_dispatch.xml b/app/src/main/res/layout/dialog_dispatch.xml index b2e4dba..729eb2c 100644 --- a/app/src/main/res/layout/dialog_dispatch.xml +++ b/app/src/main/res/layout/dialog_dispatch.xml @@ -182,19 +182,9 @@ android:gravity="center" android:orientation="horizontal"> -