update:优化ota状态共享
This commit is contained in:
@@ -0,0 +1,227 @@
|
||||
import { computed, ref } from "vue";
|
||||
import { sendHardwareBoxUpdateAPI } from "@/apis";
|
||||
import useStore from "@/store";
|
||||
import { storeToRefs } from "pinia";
|
||||
|
||||
const OTA_PROGRESS_EVENT = "/addons/shoot/otaProgress";
|
||||
const OTA_RESULT_EVENT = "/addons/shoot/otaResult";
|
||||
const UPDATE_TIMEOUT = 10 * 60 * 1000;
|
||||
|
||||
const phaseTextMap = {
|
||||
started: "正在准备固件更新",
|
||||
downloading: "正在下载固件",
|
||||
installing: "正在安装固件",
|
||||
};
|
||||
|
||||
// OTA 状态由首页、WiFi 设置页和我的设备页共享,页面切换后仍可继续接收进度。
|
||||
const updating = ref(false);
|
||||
const progress = ref(0);
|
||||
const phase = ref("started");
|
||||
const resultVisible = ref(false);
|
||||
const resultStatus = ref("");
|
||||
const resultReason = ref("");
|
||||
|
||||
let deviceRef = null;
|
||||
let updateRunId = 0;
|
||||
let targetVersion = "";
|
||||
let lastFinishedVersion = "";
|
||||
let timeoutTimer = null;
|
||||
let successCallback = null;
|
||||
let socketListening = false;
|
||||
|
||||
const phaseText = computed(
|
||||
() => phaseTextMap[phase.value] || "正在进行固件更新"
|
||||
);
|
||||
const resultTitle = computed(() =>
|
||||
resultStatus.value === "success" ? "固件更新完成" : "固件更新失败"
|
||||
);
|
||||
const resultContent = computed(() => {
|
||||
if (resultStatus.value === "success") return "固件更新已完成";
|
||||
return resultReason.value || "更新失败,请检查设备及网络后重试";
|
||||
});
|
||||
|
||||
const clearTimers = () => {
|
||||
clearTimeout(timeoutTimer);
|
||||
timeoutTimer = null;
|
||||
};
|
||||
|
||||
const resetActiveUpdate = () => {
|
||||
clearTimers();
|
||||
successCallback = null;
|
||||
};
|
||||
|
||||
const finishUpdate = (status, reason = "", runId = updateRunId) => {
|
||||
if (runId !== updateRunId || !updating.value) return;
|
||||
const onSuccess = successCallback;
|
||||
resetActiveUpdate();
|
||||
updating.value = false;
|
||||
resultStatus.value = status;
|
||||
resultReason.value = reason;
|
||||
lastFinishedVersion = targetVersion;
|
||||
if (status === "success") {
|
||||
progress.value = 100;
|
||||
phase.value = "installing";
|
||||
onSuccess?.();
|
||||
}
|
||||
resultVisible.value = true;
|
||||
};
|
||||
|
||||
const isCurrentDeviceMessage = (data) => {
|
||||
const messageDeviceId = String(data?.deviceId || "");
|
||||
const currentDeviceId = String(deviceRef?.value?.deviceId || "");
|
||||
return !messageDeviceId || !currentDeviceId || messageDeviceId === currentDeviceId;
|
||||
};
|
||||
|
||||
const isCurrentVersionMessage = (data) => {
|
||||
const messageVersion = String(data?.versionNumber || "");
|
||||
return !messageVersion || !targetVersion || messageVersion === targetVersion;
|
||||
};
|
||||
|
||||
// 页面切换导致弹窗关闭后,使用下一条进度消息恢复当前 OTA 会话。
|
||||
const recoverUpdateFromMessage = (data) => {
|
||||
updateRunId += 1;
|
||||
const runId = updateRunId;
|
||||
targetVersion = String(data?.versionNumber || "");
|
||||
lastFinishedVersion = "";
|
||||
successCallback = null;
|
||||
resultVisible.value = false;
|
||||
resultStatus.value = "";
|
||||
resultReason.value = "";
|
||||
progress.value = 0;
|
||||
phase.value = "started";
|
||||
updating.value = true;
|
||||
clearTimers();
|
||||
timeoutTimer = setTimeout(() => {
|
||||
finishUpdate("failed", "固件更新超时,请稍后重试", runId);
|
||||
}, UPDATE_TIMEOUT);
|
||||
};
|
||||
|
||||
function handleSocketMessage(message) {
|
||||
if (Number(message?.code ?? 0) !== 0) return;
|
||||
if (![OTA_PROGRESS_EVENT, OTA_RESULT_EVENT].includes(message?.event)) return;
|
||||
if (!isCurrentDeviceMessage(message.data)) return;
|
||||
|
||||
const messageVersion = String(message.data?.versionNumber || "");
|
||||
|
||||
if (message.event === OTA_PROGRESS_EVENT) {
|
||||
if (!updating.value) {
|
||||
if (
|
||||
lastFinishedVersion &&
|
||||
(!messageVersion || messageVersion === lastFinishedVersion)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
recoverUpdateFromMessage(message.data);
|
||||
} else if (!isCurrentVersionMessage(message.data)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const nextProgress = Math.min(
|
||||
100,
|
||||
Math.max(0, Number(message.data?.progress) || 0)
|
||||
);
|
||||
progress.value = Math.max(progress.value, nextProgress);
|
||||
if (phaseTextMap[message.data?.phase]) {
|
||||
phase.value = message.data.phase;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!updating.value) {
|
||||
if (
|
||||
lastFinishedVersion &&
|
||||
(!messageVersion || messageVersion === lastFinishedVersion)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
recoverUpdateFromMessage(message.data);
|
||||
} else if (!isCurrentVersionMessage(message.data)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (message.data?.status === "success") {
|
||||
finishUpdate("success");
|
||||
} else if (message.data?.status === "failed") {
|
||||
finishUpdate("failed", message.data?.reason || "");
|
||||
}
|
||||
}
|
||||
|
||||
const startSocketListening = () => {
|
||||
if (socketListening) return;
|
||||
uni.$on("socket-inbox", handleSocketMessage);
|
||||
socketListening = true;
|
||||
};
|
||||
|
||||
export const useOtaUpdate = () => {
|
||||
const store = useStore();
|
||||
const { device } = storeToRefs(store);
|
||||
deviceRef = device;
|
||||
startSocketListening();
|
||||
|
||||
const startUpdate = async ({
|
||||
versionNumber,
|
||||
resourceUrl,
|
||||
wifiSsid = "",
|
||||
wifiPassword = "",
|
||||
onSuccess,
|
||||
}) => {
|
||||
if (updating.value) return false;
|
||||
|
||||
updateRunId += 1;
|
||||
const runId = updateRunId;
|
||||
targetVersion = String(versionNumber || "");
|
||||
lastFinishedVersion = "";
|
||||
successCallback = onSuccess || null;
|
||||
resultVisible.value = false;
|
||||
resultStatus.value = "";
|
||||
resultReason.value = "";
|
||||
progress.value = 0;
|
||||
phase.value = "started";
|
||||
updating.value = true;
|
||||
clearTimers();
|
||||
timeoutTimer = setTimeout(() => {
|
||||
finishUpdate("failed", "固件更新超时,请稍后重试", runId);
|
||||
}, UPDATE_TIMEOUT);
|
||||
|
||||
try {
|
||||
const updateResult = await sendHardwareBoxUpdateAPI({
|
||||
versionNumber: targetVersion,
|
||||
wifiSsid,
|
||||
wifiPassword,
|
||||
resourceUrl,
|
||||
});
|
||||
if (runId !== updateRunId || !updating.value) return false;
|
||||
if (!updateResult?.taskId) {
|
||||
finishUpdate("failed", "固件更新任务创建失败,请稍后重试", runId);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} catch (error) {
|
||||
if (runId === updateRunId) {
|
||||
finishUpdate(
|
||||
"failed",
|
||||
error?.message || "固件更新请求失败,请稍后重试",
|
||||
runId
|
||||
);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
const closeResult = () => {
|
||||
resultVisible.value = false;
|
||||
};
|
||||
|
||||
return {
|
||||
updating,
|
||||
progress,
|
||||
phase,
|
||||
phaseText,
|
||||
resultVisible,
|
||||
resultStatus,
|
||||
resultTitle,
|
||||
resultContent,
|
||||
startUpdate,
|
||||
closeResult,
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user