feat: 自动化测试增加间隔时间和超时时间参数
- 新增两个输入框:间隔时间(秒)、超时时间(秒) - 间隔逻辑:收到回复后等待间隔时间再发下一条 0xB0 - 超时逻辑:超时后不等间隔,立即发下一条 - 状态机驱动:IDLE→SENT→(回复→WAIT_INTERVAL→SEND)/(超时→SEND) - 增加实时状态提示栏
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
// 测试操作页
|
||||
// 测试操作页 — 间隔/超时机制
|
||||
|
||||
let autoRunning = false;
|
||||
let autoTotal = 0;
|
||||
@@ -7,9 +7,15 @@ let autoFailed = 0;
|
||||
let autoRemaining = 0;
|
||||
let autoStartTime = "";
|
||||
let localSinceStr = "";
|
||||
|
||||
let pollInterval = null;
|
||||
let timeoutTimers = {}; // record_id → timer
|
||||
const TIMEOUT_MS = 10000;
|
||||
let nextCmdTimer = null; // 间隔等待定时器
|
||||
let timeoutTimer = null; // 超时定时器
|
||||
let timeoutAt = 0; // 超时时刻 (Date.now() + timeoutMs)
|
||||
let lastDoneCount = 0; // 上一轮 done 数,检测新响应
|
||||
let cmdSentAt = 0; // 最近一次发送 0xB0 时间
|
||||
let intervalMs = 3000; // 默认 3s
|
||||
let timeoutMs = 10000; // 默认 10s
|
||||
|
||||
// ─── 手动指令 ─────────────────────────────────
|
||||
|
||||
@@ -43,14 +49,20 @@ 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;
|
||||
autoStartTime = new Date().toISOString(); // 记录开始时间 (UTC)
|
||||
// MySQL 存的是本地时间,需要转本地格式传给后端过滤
|
||||
lastDoneCount = 0;
|
||||
autoStartTime = new Date().toISOString();
|
||||
|
||||
const now = new Date();
|
||||
localSinceStr = now.getFullYear() + "-" +
|
||||
String(now.getMonth() + 1).padStart(2, "0") + "-" +
|
||||
@@ -73,8 +85,8 @@ async function startAuto() {
|
||||
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";
|
||||
|
||||
@@ -82,7 +94,7 @@ async function startAuto() {
|
||||
btn.textContent = "结束";
|
||||
btn.className = "btn-stop";
|
||||
|
||||
// 插入第一条 0xB0
|
||||
// 清除旧记录 + 插入第一条
|
||||
try {
|
||||
const resp = await fetch("/api/automation/start", {
|
||||
method: "POST",
|
||||
@@ -91,8 +103,10 @@ async function startAuto() {
|
||||
});
|
||||
const data = await resp.json();
|
||||
if (data.ok) {
|
||||
// 启动超时计时器
|
||||
startTimeout(data.first_record_id);
|
||||
cmdSentAt = Date.now();
|
||||
timeoutAt = cmdSentAt + timeoutMs;
|
||||
armTimeout();
|
||||
setStatus(`已发送,超时 ${timeoutMs/1000}s...`);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("启动失败:", e);
|
||||
@@ -100,26 +114,84 @@ async function startAuto() {
|
||||
}
|
||||
|
||||
// 启动轮询
|
||||
pollInterval = setInterval(pollProgress, 1000);
|
||||
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();
|
||||
// 清除所有超时计时器
|
||||
for (const id in timeoutTimers) {
|
||||
clearTimeout(timeoutTimers[id]);
|
||||
}
|
||||
timeoutTimers = {};
|
||||
|
||||
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;
|
||||
|
||||
@@ -129,67 +201,64 @@ async function pollProgress() {
|
||||
const stats = data.stats;
|
||||
|
||||
// 更新计数
|
||||
autoDone = stats.done || 0;
|
||||
autoFailed = stats.failed || 0;
|
||||
autoRemaining = autoTotal - autoDone - autoFailed;
|
||||
if (autoRemaining < 0) autoRemaining = 0;
|
||||
const newDone = stats.done || 0;
|
||||
const newFailed = stats.failed || 0;
|
||||
|
||||
updateUI();
|
||||
if (newDone > lastDoneCount) {
|
||||
// 收到新回复 → 清除超时,开始间隔等待
|
||||
const delta = newDone - lastDoneCount;
|
||||
lastDoneCount = newDone;
|
||||
autoDone = newDone;
|
||||
autoFailed = newFailed;
|
||||
autoRemaining = autoTotal - autoDone - autoFailed;
|
||||
if (autoRemaining < 0) autoRemaining = 0;
|
||||
|
||||
// 显示最新结果
|
||||
if (data.latest) {
|
||||
renderLatest(data.latest);
|
||||
}
|
||||
clearTimeout(timeoutTimer);
|
||||
timeoutTimer = null;
|
||||
clearTimeout(nextCmdTimer);
|
||||
nextCmdTimer = null;
|
||||
|
||||
// 显示平均值
|
||||
if (data.averages) {
|
||||
renderAverages(data.averages);
|
||||
}
|
||||
updateUI();
|
||||
|
||||
// 显示波动测试数据
|
||||
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);
|
||||
}
|
||||
if (autoRemaining > 0) {
|
||||
const wait = (intervalMs / 1000).toFixed(0);
|
||||
setStatus(`收到 ${delta} 条回复,等待 ${wait}s...`);
|
||||
nextCmdTimer = setTimeout(() => {
|
||||
setStatus("发送中...");
|
||||
sendNextCmd();
|
||||
}, intervalMs);
|
||||
} else {
|
||||
setStatus("全部完成");
|
||||
stopAuto();
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// 全部完成
|
||||
stopAuto();
|
||||
// 没有新回复,更新超时计数
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
function startTimeout(recordId) {
|
||||
timeoutTimers[recordId] = setTimeout(async () => {
|
||||
// 超时:检查 record 的状态,如果还是 1 → 视为失败
|
||||
// 后端串行轮询会自动处理超时标记为 state=3
|
||||
// 前端稍后通过 pollProgress 更新计数
|
||||
console.log(`记录 ${recordId} 可能已超时`);
|
||||
}, TIMEOUT_MS);
|
||||
// ─── UI ────────────────────────────────────────
|
||||
|
||||
function setStatus(msg) {
|
||||
document.getElementById("auto-status").textContent = msg;
|
||||
}
|
||||
|
||||
function updateUI() {
|
||||
|
||||
@@ -26,6 +26,14 @@
|
||||
测试次数:
|
||||
<input type="number" id="test-count" value="10" min="1" max="9999">
|
||||
</label>
|
||||
<label style="margin-left:16px;">
|
||||
间隔时间(秒):
|
||||
<input type="number" id="interval-sec" value="3" min="0" max="300" style="width:60px;">
|
||||
</label>
|
||||
<label style="margin-left:16px;">
|
||||
超时时间(秒):
|
||||
<input type="number" id="timeout-sec" value="10" min="1" max="600" style="width:60px;">
|
||||
</label>
|
||||
<button id="btn-auto" class="btn-start" onclick="toggleAuto()">开始</button>
|
||||
<div class="progress-container">
|
||||
<div class="progress-bar" id="progress-bar"></div>
|
||||
@@ -36,6 +44,7 @@
|
||||
<span>失败:<strong id="stat-failed">0</strong></span>
|
||||
<span>剩余:<strong id="stat-remaining">0</strong></span>
|
||||
</div>
|
||||
<div class="auto-status" id="auto-status" style="font-size:12px;color:#888;margin-top:4px;"></div>
|
||||
<div class="auto-time" id="auto-time" style="display:none;margin-top:8px;font-size:12px;color:#888;">
|
||||
开始:<span id="time-start">-</span> 结束:<span id="time-end">-</span>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user