[FIX] 優化 MQTT 遠端重啟邏輯與機台忙碌狀態判斷
1. 在 MyApp 實作 getBusyReason(),整合對話框、購物車、NFC 支付狀態,提供更精確的忙碌診斷資訊。 2. 重構 MqttService 遠端解鎖/重啟邏輯,使用新的忙碌檢查機制並記錄詳細拒絕原因。 3. 修正 HomeActivity 進入時強制清空購物車,確保逾時回首頁後機台能正確進入閒置狀態。 4. 更新 FontendActivity 重啟邏輯,改用 isMachineBusy() 提升穩定性。 5. 移除出貨對話框 (DispatchDialog) 的「異常回報」按鈕,簡化使用者介面。 6. 優化購物車 Adapter (Electric/VMC),確保遠端庫存更新時能即時同步 UI 點選狀態。
This commit is contained in:
parent
ccab0905c6
commit
93a9ad7e2d
@ -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()) {
|
||||
|
||||
@ -118,7 +118,6 @@ public class MyApp extends Application {
|
||||
public List<InvoiceItemStructure> 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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -186,7 +186,7 @@ public class RecyclerElectricBuyListAdpter extends RecyclerView.Adapter<Recycler
|
||||
public void onClick(View v) {
|
||||
SlotStructure slotProduct = slotList.get(position);
|
||||
if (slotProduct.isLock()) return;
|
||||
if (MyApp.getInstance().isShowDialog) return;
|
||||
if (MyApp.getInstance().isShowDialog()) return;
|
||||
|
||||
logs.info("marketingPlan:" + slotProduct.getProduct().getMarketingPlan());
|
||||
|
||||
|
||||
@ -148,7 +148,7 @@ public class RecyclerVmcBuyListAdpter extends RecyclerView.Adapter<RecyclerVmcBu
|
||||
public void onClick(View v) {
|
||||
SlotStructure slotProduct = SlotList.get(position);
|
||||
if (slotProduct.isLock()) return;
|
||||
if (MyApp.getInstance().isShowDialog) return;
|
||||
if (MyApp.getInstance().isShowDialog()) return;
|
||||
|
||||
BuyStructure buyProduct = new BuyStructure(slotProduct.getField(), slotProduct.getSlot(), slotProduct.getProduct(), 1);
|
||||
MyApp.getInstance().getBuyList().add(buyProduct);
|
||||
|
||||
@ -296,24 +296,6 @@ public class DispatchDialog extends DialogAbstract {
|
||||
currentlyProcessingGoods();
|
||||
});
|
||||
|
||||
this.binding.buttonCancel.setOnClickListener(v -> {
|
||||
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());
|
||||
|
||||
@ -182,19 +182,9 @@
|
||||
android:gravity="center"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<Button
|
||||
android:id="@+id/buttonCancel"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:backgroundTint="@color/white"
|
||||
android:text="@string/error_report"
|
||||
android:textSize="20sp"
|
||||
android:textColor="@color/black" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/buttonOk"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:backgroundTint="@color/white"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user