// 测试操作页 — 间隔/超时机制 let autoRunning = false; let autoTotal = 0; let autoDone = 0; let autoFailed = 0; let autoRemaining = 0; let autoStartTime = ""; let localSinceStr = ""; let pollInterval = null; let nextCmdTimer = null; // 间隔等待定时器 let timeoutTimer = null; // 超时定时器 let timeoutAt = 0; // 超时时刻 (Date.now() + timeoutMs) let lastDoneCount = 0; // 上一轮 done 数,检测新响应 let cmdSentAt = 0; // 最近一次发送 0xB0 时间 let intervalMs = 10000; // 默认 10s let timeoutMs = 5000; // 默认 5s // ─── 手动指令 ───────────────────────────────── async function sendCmd(cmd) { try { const resp = await fetch("/api/command", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ dnt_id: DNT_ID, cmd }), }); const data = await resp.json(); if (data.ok) { console.log(`指令 ${cmd} 已下发: ${data.send_pkg}`); } } catch (e) { alert("发送失败: " + e.message); } } // ─── 自动化 ──────────────────────────────────── async function toggleAuto() { if (!autoRunning) { await startAuto(); } else { stopAuto(); } } async function startAuto() { const count = parseInt(document.getElementById("test-count").value) || 10; if (count < 1) return; // 读取参数 intervalMs = (parseFloat(document.getElementById("interval-sec").value) || 3) * 1000; timeoutMs = (parseFloat(document.getElementById("timeout-sec").value) || 10) * 1000; if (timeoutMs < 1000) timeoutMs = 1000; // 重置 autoRunning = true; autoTotal = count; autoDone = 0; autoFailed = 0; autoRemaining = count; lastDoneCount = 0; autoStartTime = new Date().toISOString(); const now = new Date(); localSinceStr = now.getFullYear() + "-" + String(now.getMonth() + 1).padStart(2, "0") + "-" + String(now.getDate()).padStart(2, "0") + " " + String(now.getHours()).padStart(2, "0") + ":" + String(now.getMinutes()).padStart(2, "0") + ":" + String(now.getSeconds()).padStart(2, "0"); // 显示开始时间 document.getElementById("auto-time").style.display = "block"; document.getElementById("time-start").textContent = new Date().toLocaleString(); document.getElementById("time-end").textContent = "-"; // 清空显示 resetAverages(); document.getElementById("latest-result").innerHTML = '
等待测试...
'; document.getElementById("latest-wave").innerHTML = '暂无波动数据...
'; document.getElementById("progress-bar").style.width = "0%"; document.getElementById("progress-text").textContent = "0/" + count + " (0 失败)"; document.getElementById("stat-done").textContent = "0"; document.getElementById("stat-failed").textContent = "0"; document.getElementById("stat-remaining").textContent = count; document.getElementById("auto-status").textContent = ""; document.querySelector("#records-table tbody").innerHTML = ""; document.getElementById("records-empty").style.display = "block"; const btn = document.getElementById("btn-auto"); btn.textContent = "结束"; btn.className = "btn-stop"; // 清除旧记录 + 插入第一条 try { const resp = await fetch("/api/automation/start", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ dnt_id: DNT_ID, count }), }); const data = await resp.json(); if (data.ok) { cmdSentAt = Date.now(); timeoutAt = cmdSentAt + timeoutMs; armTimeout(); setStatus(`已发送,超时 ${timeoutMs/1000}s...`); } } catch (e) { console.error("启动失败:", e); stopAuto(); } // 启动轮询 pollInterval = setInterval(pollProgress, 500); } function stopAuto() { autoRunning = false; clearInterval(pollInterval); pollInterval = null; clearTimeout(nextCmdTimer); nextCmdTimer = null; clearTimeout(timeoutTimer); timeoutTimer = null; document.getElementById("auto-status").textContent = ""; document.getElementById("time-end").textContent = new Date().toLocaleString(); const btn = document.getElementById("btn-auto"); btn.textContent = "开始"; btn.className = "btn-start"; } // ─── 发送下一条 0xB0 ──────────────────────────── async function sendNextCmd() { if (!autoRunning) return; if (autoRemaining <= 0) { // 检查是否还有等待完成的 if (autoDone + autoFailed >= autoTotal) { stopAuto(); return; } } try { const r = await fetch("/api/command", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ dnt_id: DNT_ID, cmd: "B0" }), }); const rd = await r.json(); if (rd.ok) { cmdSentAt = Date.now(); timeoutAt = cmdSentAt + timeoutMs; armTimeout(); setStatus(`已发送,超时 ${timeoutMs/1000}s...`); } } catch (e) { console.error("发送下一条失败:", e); } } // ─── 超时定时器 ───────────────────────────────── function armTimeout() { clearTimeout(timeoutTimer); timeoutTimer = setTimeout(onTimeout, timeoutMs + 500); // +500ms 容差 } function onTimeout() { if (!autoRunning) return; // 超时:即使没收到回复也计数失败,立即发下一条 autoFailed++; autoRemaining = autoTotal - autoDone - autoFailed; if (autoRemaining < 0) autoRemaining = 0; updateUI(); setStatus(`超时!立即发下一条...`); console.log(`超时 (${timeoutMs}ms),失败数: ${autoFailed}`); clearTimeout(nextCmdTimer); nextCmdTimer = null; if (autoRemaining > 0) { sendNextCmd(); } else { stopAuto(); } } // ─── 轮询 ────────────────────────────────────── async function pollProgress() { if (!autoRunning) return; try { const resp = await fetch(`/api/automation/${DNT_ID}/progress?since=${encodeURIComponent(localSinceStr)}`); const data = await resp.json(); const stats = data.stats; // 更新计数 const newDone = stats.done || 0; const newFailed = stats.failed || 0; if (newDone > lastDoneCount) { // 收到新回复 → 清除超时,开始间隔等待 const delta = newDone - lastDoneCount; lastDoneCount = newDone; autoDone = newDone; autoFailed = newFailed; autoRemaining = autoTotal - autoDone - autoFailed; if (autoRemaining < 0) autoRemaining = 0; clearTimeout(timeoutTimer); timeoutTimer = null; clearTimeout(nextCmdTimer); nextCmdTimer = null; updateUI(); if (autoRemaining > 0) { const wait = (intervalMs / 1000).toFixed(0); setStatus(`收到 ${delta} 条回复,等待 ${wait}s...`); nextCmdTimer = setTimeout(() => { setStatus("发送中..."); sendNextCmd(); }, intervalMs); } else { setStatus("全部完成"); stopAuto(); return; } } else { // 没有新回复,更新超时计数 autoFailed = newFailed; autoRemaining = autoTotal - autoDone - autoFailed; if (autoRemaining < 0) autoRemaining = 0; // 检查是否全部完成 if (autoRemaining <= 0 && autoDone + autoFailed >= autoTotal) { stopAuto(); return; } } // 显示最新结果 if (data.latest) renderLatest(data.latest); if (data.averages) renderAverages(data.averages); if (data.latest_wave) renderLatestWave(data.latest_wave); if (data.records) renderRecords(data.records); } catch (e) { console.error("轮询失败:", e); } } // ─── UI ──────────────────────────────────────── function setStatus(msg) { document.getElementById("auto-status").textContent = msg; } function updateUI() { document.getElementById("stat-done").textContent = autoDone; document.getElementById("stat-failed").textContent = autoFailed; document.getElementById("stat-remaining").textContent = autoRemaining; const total = autoTotal || 1; const pct = Math.round((autoDone / total) * 100); document.getElementById("progress-bar").style.width = pct + "%"; document.getElementById("progress-text").textContent = `${autoDone}/${autoTotal} (${autoFailed} 失败)`; } function toSpeed(v) { if (v === null || v === undefined || v === '') return '-'; return (parseFloat(v) / 10).toFixed(1); } function fmtTime(v) { if (!v) return '-'; const d = new Date(v); if (isNaN(d.getTime())) return String(v).substring(0, 19); const y = d.getFullYear(); const m = String(d.getMonth() + 1).padStart(2, '0'); const d2 = String(d.getDate()).padStart(2, '0'); const h = String(d.getHours()).padStart(2, '0'); const min = String(d.getMinutes()).padStart(2, '0'); const s = String(d.getSeconds()).padStart(2, '0'); return `${y}-${m}-${d2} ${h}:${min}:${s}`; } const RELAY_MAP = { 0: '无输出', 1: '存在信号', 2: '脉冲信号', 3: '存在信号; 脉冲信号', }; function decodeRelay(v) { if (v === null || v === undefined || v === '') return '-'; return RELAY_MAP[parseInt(v)] || `0x${parseInt(v).toString(16).toUpperCase().padStart(2, '0')}`; } // ─── 显示最新结果 ────────────────────────────── function renderLatest(data) { const div = document.getElementById("latest-result"); div.innerHTML = `设备型号:${data.str_type || '-'}
测试模式:${data.test_mode === 1 ? '波动测试' : '灵敏度测试'}
峰峰值:${data.ppvalue?.toFixed(2) || '-'} V
开始工作频率:${data.idle_freq || '-'} Hz
进入工作频率:${data.enter_freq || '-'} Hz
离开工作频率:${data.exit_freq || '-'} Hz
进入距离:${data.enter_dist || '-'} mm
离开距离:${data.exit_dist || '-'} mm
进入速度:${toSpeed(data.enter_speed)} m/s
离开速度:${toSpeed(data.exit_speed)} m/s
是否完成:${data.iffinish === '1' ? '是' : '否'}
故障信息:${data.fault_info || '无'}
继电器:${decodeRelay(data.relay_code)}
时间:${fmtTime(data.create_time)}
`; } // ─── 显示平均值 ──────────────────────────────── function renderAverages(data) { document.getElementById("avg-ppvalue").textContent = data.avg_ppvalue || "-"; document.getElementById("avg-idle-freq").textContent = data.avg_idle_freq || "-"; document.getElementById("avg-enter-freq").textContent = data.avg_enter_freq || "-"; document.getElementById("avg-enter-dist").textContent = data.avg_enter_dist || "-"; document.getElementById("avg-exit-dist").textContent = data.avg_exit_dist || "-"; document.getElementById("avg-enter-speed").textContent = data.avg_enter_speed || "-"; document.getElementById("avg-exit-speed").textContent = data.avg_exit_speed || "-"; } function resetAverages() { ["ppvalue", "idle-freq", "enter-freq", "enter-dist", "exit-dist", "enter-speed", "exit-speed"].forEach(id => { document.getElementById("avg-" + id).textContent = "-"; }); } // ─── 显示波动测试数据 ────────────────────────── function renderLatestWave(data) { const div = document.getElementById("latest-wave"); if (!data || !data.work_freq) { div.innerHTML = '暂无波动数据...
'; return; } div.innerHTML = `剩余次数:${data.remain_count || 0}
工作频率:${data.work_freq || '-'} Hz
当前距离:${data.curr_dist || '-'} mm
当前速度:${toSpeed(data.speed)} m/s
最近距离:${data.near_dist || '-'} mm
最远距离:${data.far_dist || '-'} mm
进入高度 (B4):${data.b4_enter_dist || '-'} mm
离开高度 (B4):${data.b4_leave_dist || '-'} mm
继电器:${decodeRelay(data.relay_code)}
时间:${fmtTime(data.create_time)}
`; } // ─── 显示本轮测试明细 ────────────────────────── function renderRecords(records) { if (!records || !records.length) { document.getElementById("records-empty").style.display = "block"; document.getElementById("records-table").style.display = "none"; return; } document.getElementById("records-empty").style.display = "none"; document.getElementById("records-table").style.display = ""; const tbody = document.querySelector("#records-table tbody"); tbody.innerHTML = records.map((r, i) => `