// 测试操作页 let autoRunning = false; let autoTotal = 0; let autoDone = 0; let autoFailed = 0; let autoRemaining = 0; let autoStartTime = ""; let localSinceStr = ""; let pollInterval = null; let timeoutTimers = {}; // record_id → timer const TIMEOUT_MS = 10000; // ─── 手动指令 ───────────────────────────────── 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; // 重置 autoRunning = true; autoTotal = count; autoDone = 0; autoFailed = 0; autoRemaining = count; autoStartTime = new Date().toISOString(); // 记录开始时间 (UTC) // MySQL 存的是本地时间,需要转本地格式传给后端过滤 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.querySelector("#records-table tbody").innerHTML = ""; document.getElementById("records-empty").style.display = "block"; const btn = document.getElementById("btn-auto"); btn.textContent = "结束"; btn.className = "btn-stop"; // 插入第一条 0xB0 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) { // 启动超时计时器 startTimeout(data.first_record_id); } } catch (e) { console.error("启动失败:", e); stopAuto(); } // 启动轮询 pollInterval = setInterval(pollProgress, 1000); } function stopAuto() { autoRunning = false; clearInterval(pollInterval); pollInterval = null; // 记录结束时间 document.getElementById("time-end").textContent = new Date().toLocaleString(); // 清除所有超时计时器 for (const id in timeoutTimers) { clearTimeout(timeoutTimers[id]); } timeoutTimers = {}; const btn = document.getElementById("btn-auto"); btn.textContent = "开始"; btn.className = "btn-start"; } 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; // 更新计数 autoDone = stats.done || 0; autoFailed = stats.failed || 0; autoRemaining = autoTotal - autoDone - autoFailed; if (autoRemaining < 0) autoRemaining = 0; updateUI(); // 显示最新结果 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); } // 自动插入下一条 0xB0 if (autoRemaining > 0) { // 检查是否还有 pending 的记录,没有则插入新的 if (stats.pending === 0 && stats.sent === 0) { 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) { startTimeout(rd.record_id); } } catch (e) { console.error("插入下一条失败:", e); } } } else { // 全部完成 stopAuto(); } } catch (e) { console.error("轮询失败:", e); } } function startTimeout(recordId) { timeoutTimers[recordId] = setTimeout(async () => { // 超时:检查 record 的状态,如果还是 1 → 视为失败 // 后端串行轮询会自动处理超时标记为 state=3 // 前端稍后通过 pollProgress 更新计数 console.log(`记录 ${recordId} 可能已超时`); }, TIMEOUT_MS); } 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 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 || '无'}

时间:${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

继电器:${data.relay_out || '无'}

时间:${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) => ` ${i + 1} ${r.sn_state === 2 ? 'OK' : r.sn_state === 3 ? '超时' : '?'} ${r.test_mode === 1 ? '波动' : '灵敏度'} ${r.ppvalue?.toFixed(2) || '-'} ${r.idle_freq || '-'} ${r.enter_dist || '-'} ${r.exit_dist || '-'} ${toSpeed(r.enter_speed)} ${r.create_time || '-'} `).join(""); }