2 changed files with 195 additions and 169 deletions
@ -0,0 +1,183 @@ |
|||||
|
import { computed, reactive } from 'vue' |
||||
|
import request from '@/utils/http' |
||||
|
import * as urls from '@/config/urls' |
||||
|
|
||||
|
const COMMAND_STATUS = { |
||||
|
sent: { name: '已下发', step: 2, result: '等待应答', terminal: false }, |
||||
|
acked: { name: '已确认', step: 3, result: '执行成功', terminal: true, ok: true }, |
||||
|
timeout: { name: '已超时', step: 3, result: '已超时', terminal: true, ok: false }, |
||||
|
terminal: { name: '执行失败', step: 3, result: '执行失败', terminal: true, ok: false }, |
||||
|
} |
||||
|
|
||||
|
export function useCommandProgress({ getRecord, ui, devices, reload }) { |
||||
|
const commandProgress = reactive({ |
||||
|
open: false, |
||||
|
title: '', |
||||
|
deviceName: '', |
||||
|
commandId: '', |
||||
|
status: '', // sent/acked/timeout/terminal
|
||||
|
result: '', |
||||
|
step: 1, // 1 确认下发 2 已下发 3 终态
|
||||
|
error: '', |
||||
|
buttonTitle: '', |
||||
|
}) |
||||
|
|
||||
|
let commandPollTimer = null |
||||
|
let commandAutoCloseTimer = null |
||||
|
let commandPollFails = 0 |
||||
|
let commandPollInFlight = false |
||||
|
let commandProgressEpoch = 0 |
||||
|
|
||||
|
function buttonStatusText(title) { |
||||
|
if (commandProgress.buttonTitle !== title || !commandProgress.status) return '状态:待执行' |
||||
|
const meta = COMMAND_STATUS[commandProgress.status] |
||||
|
return meta ? `状态:${meta.name}` : '状态:待执行' |
||||
|
} |
||||
|
|
||||
|
const commandProgressTerminalLabel = computed(() => { |
||||
|
if (commandProgress.status === 'acked') return '已确认' |
||||
|
return COMMAND_STATUS[commandProgress.status]?.name || '执行结果' |
||||
|
}) |
||||
|
|
||||
|
function clearCommandTimers() { |
||||
|
if (commandPollTimer) { |
||||
|
clearInterval(commandPollTimer) |
||||
|
commandPollTimer = null |
||||
|
} |
||||
|
if (commandAutoCloseTimer) { |
||||
|
clearTimeout(commandAutoCloseTimer) |
||||
|
commandAutoCloseTimer = null |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function closeCommandProgress() { |
||||
|
clearCommandTimers() |
||||
|
commandProgressEpoch += 1 |
||||
|
commandProgress.open = false |
||||
|
} |
||||
|
|
||||
|
function applyCommandStatus(cmd) { |
||||
|
const status = cmd?.status || 'sent' |
||||
|
const meta = COMMAND_STATUS[status] || COMMAND_STATUS.sent |
||||
|
commandProgress.status = status |
||||
|
commandProgress.step = meta.step |
||||
|
commandProgress.result = cmd?.ackResultCode || meta.result |
||||
|
commandProgress.error = '' |
||||
|
return meta |
||||
|
} |
||||
|
|
||||
|
function scheduleAutoClose() { |
||||
|
clearTimeout(commandAutoCloseTimer) |
||||
|
commandAutoCloseTimer = setTimeout(() => { |
||||
|
closeCommandProgress() |
||||
|
}, 5000) |
||||
|
} |
||||
|
|
||||
|
async function pollCommandOnce() { |
||||
|
if (!commandProgress.commandId || commandPollInFlight) return |
||||
|
const epoch = commandProgressEpoch |
||||
|
commandPollInFlight = true |
||||
|
try { |
||||
|
const cmd = await request.get(urls.COMMAND(commandProgress.commandId)) |
||||
|
if (epoch !== commandProgressEpoch) return |
||||
|
commandPollFails = 0 |
||||
|
const meta = applyCommandStatus(cmd) |
||||
|
if (meta.terminal) { |
||||
|
clearInterval(commandPollTimer) |
||||
|
commandPollTimer = null |
||||
|
scheduleAutoClose() |
||||
|
} |
||||
|
} catch (e) { |
||||
|
if (epoch !== commandProgressEpoch) return |
||||
|
commandPollFails += 1 |
||||
|
const status = e?.status ?? e?.response?.status |
||||
|
if (status === 403 || status === 404 || commandPollFails >= 3) { |
||||
|
clearInterval(commandPollTimer) |
||||
|
commandPollTimer = null |
||||
|
commandProgress.error = |
||||
|
status === 403 |
||||
|
? '无权限查看进度,请稍后在操作记录确认' |
||||
|
: status === 404 |
||||
|
? '未找到指令记录' |
||||
|
: (e.message || '进度查询失败') |
||||
|
scheduleAutoClose() |
||||
|
} |
||||
|
} finally { |
||||
|
commandPollInFlight = false |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function startCommandProgress(cmd, title, deviceName) { |
||||
|
clearCommandTimers() |
||||
|
commandPollInFlight = false |
||||
|
commandProgressEpoch += 1 |
||||
|
commandPollFails = 0 |
||||
|
commandProgress.open = true |
||||
|
commandProgress.title = title |
||||
|
commandProgress.deviceName = deviceName || '--' |
||||
|
commandProgress.commandId = String(cmd?.id || '') |
||||
|
commandProgress.buttonTitle = title |
||||
|
commandProgress.step = 1 |
||||
|
commandProgress.error = '' |
||||
|
applyCommandStatus(cmd || { status: 'sent' }) |
||||
|
|
||||
|
if (!commandProgress.commandId) { |
||||
|
commandProgress.error = '未返回指令 ID,无法跟踪进度' |
||||
|
scheduleAutoClose() |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
const ttl = Number(cmd?.ttlMs) > 0 ? Number(cmd.ttlMs) : 30000 |
||||
|
const deadline = Date.now() + Math.max(ttl, 30000) + 5000 |
||||
|
|
||||
|
commandPollTimer = setInterval(async () => { |
||||
|
if (Date.now() > deadline) { |
||||
|
clearInterval(commandPollTimer) |
||||
|
commandPollTimer = null |
||||
|
commandProgressEpoch += 1 |
||||
|
if (!COMMAND_STATUS[commandProgress.status]?.terminal) { |
||||
|
commandProgress.error = '等待超时,请稍后在操作记录查看' |
||||
|
commandProgress.step = 3 |
||||
|
} |
||||
|
scheduleAutoClose() |
||||
|
return |
||||
|
} |
||||
|
await pollCommandOnce() |
||||
|
}, 1500) |
||||
|
|
||||
|
// 立即查一次,避免干等 1.5s
|
||||
|
pollCommandOnce() |
||||
|
} |
||||
|
|
||||
|
async function runCommand(title, desc) { |
||||
|
const rec = getRecord() |
||||
|
if (!rec) return |
||||
|
if (!rec.online) { |
||||
|
ui.toast('设备当前离线,无法下发指令') |
||||
|
return |
||||
|
} |
||||
|
if (rec.kind === 'drone' && rec.bindingState !== 'bound') { |
||||
|
ui.toast('无人机未绑定机巢,无法下发指令') |
||||
|
return |
||||
|
} |
||||
|
const ok = await ui.confirm(title, desc) |
||||
|
if (!ok) return |
||||
|
try { |
||||
|
const cmd = await devices.sendCommand(rec, title) |
||||
|
ui.toast(`${title}指令已下发`) |
||||
|
startCommandProgress(cmd, title, rec.name) |
||||
|
await reload() |
||||
|
} catch (e) { |
||||
|
ui.toast(e.message || '指令下发失败') |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return { |
||||
|
commandProgress, |
||||
|
commandProgressTerminalLabel, |
||||
|
buttonStatusText, |
||||
|
startCommandProgress, |
||||
|
closeCommandProgress, |
||||
|
runCommand, |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue