Compare commits
3
Commits
6c55d32f90
...
75cfcd66ee
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
75cfcd66ee | ||
|
|
7955b37ec5 | ||
|
|
9e6ad4cf50 |
+119
-48
@@ -1,5 +1,5 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import {onMounted, onUnmounted, ref} from "vue";
|
import {onMounted, onUnmounted, ref, watch} from "vue";
|
||||||
import {onShareAppMessage, onShareTimeline, onShow} from "@dcloudio/uni-app";
|
import {onShareAppMessage, onShareTimeline, onShow} from "@dcloudio/uni-app";
|
||||||
import Container from "@/components/Container.vue";
|
import Container from "@/components/Container.vue";
|
||||||
import AppFooter from "@/components/AppFooter.vue";
|
import AppFooter from "@/components/AppFooter.vue";
|
||||||
@@ -52,20 +52,41 @@ const otaInfo = ref({
|
|||||||
forceUpdate: false,
|
forceUpdate: false,
|
||||||
});
|
});
|
||||||
const isStartingOta = ref(false);
|
const isStartingOta = ref(false);
|
||||||
|
let isCheckingOta = false;
|
||||||
|
let otaCheckQueuedForOnline = false;
|
||||||
let otaProgressTimer = null;
|
let otaProgressTimer = null;
|
||||||
let otaStatusTimer = null;
|
let otaStatusTimer = null;
|
||||||
let otaTimeoutTimer = null;
|
let otaTimeoutTimer = null;
|
||||||
|
let otaResultTimer = null;
|
||||||
|
let otaStatusPollCount = 0;
|
||||||
|
let otaUpdateRunId = 0;
|
||||||
|
// 首页 OTA 轮询采用请求次数和总时长双重兜底,避免更新中状态长期卡住。
|
||||||
|
const OTA_TASK_STATUS_POLL_INTERVAL = 2000;
|
||||||
|
const OTA_TASK_STATUS_MAX_POLL_COUNT = 15;
|
||||||
|
const OTA_TASK_STATUS_TIMEOUT = 30000;
|
||||||
|
|
||||||
// 清理首页 OTA 更新定时器,避免弹窗关闭或页面卸载后继续轮询。
|
// 清理首页 OTA 更新定时器,避免弹窗关闭或页面卸载后继续轮询。
|
||||||
const clearOtaUpdateTimers = () => {
|
const clearOtaUpdateTimers = () => {
|
||||||
clearInterval(otaProgressTimer);
|
clearInterval(otaProgressTimer);
|
||||||
clearTimeout(otaStatusTimer);
|
clearTimeout(otaStatusTimer);
|
||||||
clearTimeout(otaTimeoutTimer);
|
clearTimeout(otaTimeoutTimer);
|
||||||
|
clearTimeout(otaResultTimer);
|
||||||
otaProgressTimer = null;
|
otaProgressTimer = null;
|
||||||
otaStatusTimer = null;
|
otaStatusTimer = null;
|
||||||
otaTimeoutTimer = null;
|
otaTimeoutTimer = null;
|
||||||
|
otaResultTimer = null;
|
||||||
|
otaStatusPollCount = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 使当前 OTA 运行失效,确保旧请求返回后不会继续轮询或覆盖新状态。
|
||||||
|
const invalidateOtaUpdateRun = () => {
|
||||||
|
otaUpdateRunId += 1;
|
||||||
|
clearOtaUpdateTimers();
|
||||||
|
};
|
||||||
|
|
||||||
|
const isOtaUpdateRunActive = (runId) =>
|
||||||
|
runId === otaUpdateRunId && otaState.value === "update_progress";
|
||||||
|
|
||||||
// 启动首页 OTA 本地进度动画,最终成功失败以后端任务状态为准。
|
// 启动首页 OTA 本地进度动画,最终成功失败以后端任务状态为准。
|
||||||
const startOtaProgressAnimation = () => {
|
const startOtaProgressAnimation = () => {
|
||||||
clearInterval(otaProgressTimer);
|
clearInterval(otaProgressTimer);
|
||||||
@@ -91,21 +112,51 @@ const applyOtaVersionInfo = (versionInfo) => {
|
|||||||
|
|
||||||
// 检查当前设备盒子是否存在可升级版本。
|
// 检查当前设备盒子是否存在可升级版本。
|
||||||
const checkOtaUpdate = async () => {
|
const checkOtaUpdate = async () => {
|
||||||
let versionInfo;
|
if (isCheckingOta || otaVisible.value) return;
|
||||||
|
isCheckingOta = true;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
versionInfo = await getHardwareBoxVersionAPI();
|
let deviceStatus;
|
||||||
} catch (err) {
|
try {
|
||||||
|
deviceStatus = await getDeviceBatteryAPI();
|
||||||
|
} catch (err) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (deviceStatus?.online !== true || otaVisible.value) return;
|
||||||
|
|
||||||
|
let versionInfo;
|
||||||
|
try {
|
||||||
|
versionInfo = await getHardwareBoxVersionAPI();
|
||||||
|
} catch (err) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!versionInfo?.needUpdate || otaVisible.value) return;
|
||||||
|
applyOtaVersionInfo(versionInfo);
|
||||||
|
|
||||||
|
const dismissedAt = uni.getStorageSync("ota_dismissed_at");
|
||||||
|
const now = Date.now();
|
||||||
|
if (!otaInfo.value.forceUpdate && dismissedAt && now - dismissedAt < 24 * 60 * 60 * 1000) return;
|
||||||
|
otaState.value = "new_version";
|
||||||
|
otaVisible.value = true;
|
||||||
|
} finally {
|
||||||
|
isCheckingOta = false;
|
||||||
|
const shouldRecheckForOnline = otaCheckQueuedForOnline;
|
||||||
|
otaCheckQueuedForOnline = false;
|
||||||
|
if (shouldRecheckForOnline && !otaVisible.value) {
|
||||||
|
void checkOtaUpdate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 设备由 WS 通知上线时补查 OTA;已显示任何 OTA 弹窗时保留当前流程和结果。
|
||||||
|
watch(online, (nextOnline, previousOnline) => {
|
||||||
|
if (previousOnline !== false || nextOnline !== true || otaVisible.value) return;
|
||||||
|
if (isCheckingOta) {
|
||||||
|
otaCheckQueuedForOnline = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!versionInfo?.needUpdate) return;
|
void checkOtaUpdate();
|
||||||
applyOtaVersionInfo(versionInfo);
|
});
|
||||||
|
|
||||||
const dismissedAt = uni.getStorageSync("ota_dismissed_at");
|
|
||||||
const now = Date.now();
|
|
||||||
if (!otaInfo.value.forceUpdate && dismissedAt && now - dismissedAt < 24 * 60 * 60 * 1000) return;
|
|
||||||
otaState.value = "new_version";
|
|
||||||
otaVisible.value = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
// 拼接 OTA WiFi 页参数,让未连 WiFi 的设备继续使用同一份版本信息。
|
// 拼接 OTA WiFi 页参数,让未连 WiFi 的设备继续使用同一份版本信息。
|
||||||
const getOtaWifiUrl = () => {
|
const getOtaWifiUrl = () => {
|
||||||
@@ -125,61 +176,78 @@ const handleOtaDismiss = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// 将首页 OTA 直连更新流程标记为失败。
|
// 将首页 OTA 直连更新流程标记为失败。
|
||||||
const failHomeOtaUpdate = () => {
|
const failHomeOtaUpdate = (runId) => {
|
||||||
clearOtaUpdateTimers();
|
if (!isOtaUpdateRunActive(runId)) return;
|
||||||
|
invalidateOtaUpdateRun();
|
||||||
isStartingOta.value = false;
|
isStartingOta.value = false;
|
||||||
otaState.value = "update_failure";
|
otaState.value = "update_failure";
|
||||||
otaVisible.value = true;
|
otaVisible.value = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
// 将首页 OTA 直连更新流程标记为成功。
|
// 将首页 OTA 直连更新流程标记为成功。
|
||||||
const completeHomeOtaUpdate = () => {
|
const completeHomeOtaUpdate = (runId) => {
|
||||||
clearOtaUpdateTimers();
|
if (!isOtaUpdateRunActive(runId)) return;
|
||||||
|
invalidateOtaUpdateRun();
|
||||||
isStartingOta.value = false;
|
isStartingOta.value = false;
|
||||||
otaProgress.value = 100;
|
otaProgress.value = 100;
|
||||||
setTimeout(() => {
|
const completedRunId = otaUpdateRunId;
|
||||||
|
otaResultTimer = setTimeout(() => {
|
||||||
|
otaResultTimer = null;
|
||||||
|
if (completedRunId !== otaUpdateRunId) return;
|
||||||
otaState.value = "update_success";
|
otaState.value = "update_success";
|
||||||
otaVisible.value = true;
|
otaVisible.value = true;
|
||||||
}, 300);
|
}, 300);
|
||||||
};
|
};
|
||||||
|
|
||||||
// 轮询首页直接发起的 OTA 更新任务状态。
|
// 轮询首页直接发起的 OTA 更新任务状态。
|
||||||
const pollHomeOtaTaskStatus = (taskId) => {
|
const pollHomeOtaTaskStatus = async (taskId, runId) => {
|
||||||
clearTimeout(otaStatusTimer);
|
if (!isOtaUpdateRunActive(runId)) return;
|
||||||
otaStatusTimer = setTimeout(async () => {
|
|
||||||
try {
|
otaStatusPollCount += 1;
|
||||||
const taskStatus = await getHardwareBoxTaskStatusAPI(taskId);
|
try {
|
||||||
const status = Number(taskStatus?.status);
|
const taskStatus = await getHardwareBoxTaskStatusAPI(taskId);
|
||||||
if (status === 2) {
|
if (!isOtaUpdateRunActive(runId)) return;
|
||||||
completeHomeOtaUpdate();
|
|
||||||
return;
|
const status = Number(taskStatus?.status);
|
||||||
}
|
if (status === 2) {
|
||||||
if (status === 3) {
|
completeHomeOtaUpdate(runId);
|
||||||
failHomeOtaUpdate();
|
return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (status === 0 || status === 1) {
|
|
||||||
pollHomeOtaTaskStatus(taskId);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
failHomeOtaUpdate();
|
|
||||||
} catch (err) {
|
|
||||||
failHomeOtaUpdate();
|
|
||||||
}
|
}
|
||||||
}, 3000);
|
if (status === 3) {
|
||||||
|
failHomeOtaUpdate(runId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (status === 0 || status === 1) {
|
||||||
|
if (otaStatusPollCount >= OTA_TASK_STATUS_MAX_POLL_COUNT) {
|
||||||
|
failHomeOtaUpdate(runId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
otaStatusTimer = setTimeout(() => {
|
||||||
|
otaStatusTimer = null;
|
||||||
|
if (!isOtaUpdateRunActive(runId)) return;
|
||||||
|
void pollHomeOtaTaskStatus(taskId, runId);
|
||||||
|
}, OTA_TASK_STATUS_POLL_INTERVAL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
failHomeOtaUpdate(runId);
|
||||||
|
} catch (err) {
|
||||||
|
if (!isOtaUpdateRunActive(runId)) return;
|
||||||
|
failHomeOtaUpdate(runId);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 设备盒子已连 WiFi 时,从首页直接传空 WiFi 信息发起 OTA 更新。
|
// 设备盒子已连 WiFi 时,从首页直接传空 WiFi 信息发起 OTA 更新。
|
||||||
const startHomeOtaUpdate = async () => {
|
const startHomeOtaUpdate = async () => {
|
||||||
|
invalidateOtaUpdateRun();
|
||||||
|
const runId = otaUpdateRunId;
|
||||||
otaState.value = "update_progress";
|
otaState.value = "update_progress";
|
||||||
otaVisible.value = true;
|
otaVisible.value = true;
|
||||||
otaProgress.value = 0;
|
otaProgress.value = 0;
|
||||||
startOtaProgressAnimation();
|
startOtaProgressAnimation();
|
||||||
otaTimeoutTimer = setTimeout(() => {
|
otaTimeoutTimer = setTimeout(() => {
|
||||||
if (otaState.value === "update_progress") {
|
if (!isOtaUpdateRunActive(runId)) return;
|
||||||
failHomeOtaUpdate();
|
failHomeOtaUpdate(runId);
|
||||||
}
|
}, OTA_TASK_STATUS_TIMEOUT);
|
||||||
}, 5 * 60 * 1000);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const updateResult = await sendHardwareBoxUpdateAPI({
|
const updateResult = await sendHardwareBoxUpdateAPI({
|
||||||
@@ -188,13 +256,15 @@ const startHomeOtaUpdate = async () => {
|
|||||||
wifiPassword: "",
|
wifiPassword: "",
|
||||||
resourceUrl: otaInfo.value.resourceUrl,
|
resourceUrl: otaInfo.value.resourceUrl,
|
||||||
});
|
});
|
||||||
|
if (!isOtaUpdateRunActive(runId)) return;
|
||||||
if (!updateResult?.taskId) {
|
if (!updateResult?.taskId) {
|
||||||
failHomeOtaUpdate();
|
failHomeOtaUpdate(runId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
pollHomeOtaTaskStatus(updateResult.taskId);
|
void pollHomeOtaTaskStatus(updateResult.taskId, runId);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
failHomeOtaUpdate();
|
if (!isOtaUpdateRunActive(runId)) return;
|
||||||
|
failHomeOtaUpdate(runId);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -240,6 +310,7 @@ const handleOtaDone = () => {
|
|||||||
|
|
||||||
// 处理 OTA 更新失败后的重试按钮,重新走立即更新判断流程。
|
// 处理 OTA 更新失败后的重试按钮,重新走立即更新判断流程。
|
||||||
const handleOtaRetry = () => {
|
const handleOtaRetry = () => {
|
||||||
|
invalidateOtaUpdateRun();
|
||||||
handleOtaUpdate();
|
handleOtaUpdate();
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -351,7 +422,7 @@ onMounted(async () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
clearOtaUpdateTimers();
|
invalidateOtaUpdateRun();
|
||||||
});
|
});
|
||||||
|
|
||||||
onShareAppMessage(() => {
|
onShareAppMessage(() => {
|
||||||
|
|||||||
Reference in New Issue
Block a user