feat: edc-web 支持车检器序列号输入与显示

- 自动化测试区域新增「车检器序列号」输入框
- 回车键自动触发「开始」按钮
- /api/automation/start 接收 detector_serial,写入 tb_pending_detector
- 测试操作页 + 测试信息页(全部/B2/B4)显示序列号,空时显示 '-'
- 页面加载和测试结束后焦点自动回到序列号输入框(全选),方便连续测试
This commit is contained in:
wangfq
2026-06-15 10:02:51 +08:00
parent 4ac6cbb2fe
commit 521cbe4107
6 changed files with 73 additions and 6 deletions

View File

@@ -942,3 +942,21 @@ def delete_device_logs(serial: str = "", event_type: str = "",
return cnt return cnt
finally: finally:
conn.close() conn.close()
# ─── tb_pending_detector ───────────────────────────────────────────
def set_pending_detector_serial(dnt_id: int, detector_serial: str):
"""设置待插入的车检器序列号UPSERT"""
conn = get_conn()
try:
with conn.cursor() as cur:
cur.execute(
"""INSERT INTO tb_pending_detector (dnt_id, detector_serial)
VALUES (%s, %s)
ON DUPLICATE KEY UPDATE detector_serial = VALUES(detector_serial)""",
(dnt_id, detector_serial),
)
conn.commit()
finally:
conn.close()

View File

@@ -13,6 +13,7 @@ from app.models import (
get_wave_records, get_wave_records,
clear_serialnet_records, clear_serialnet_records,
insert_log, insert_log,
set_pending_detector_serial,
) )
bp = Blueprint("test_op", __name__) bp = Blueprint("test_op", __name__)
@@ -89,10 +90,15 @@ def api_automation_start():
data = request.get_json() data = request.get_json()
dnt_id = data.get("dnt_id") dnt_id = data.get("dnt_id")
count = int(data.get("count", 1)) count = int(data.get("count", 1))
detector_serial = (data.get("detector_serial") or "").strip()
device = get_device_by_id(dnt_id) device = get_device_by_id(dnt_id)
target = f"{device['serial']}" if device else f"dnt_id={dnt_id}" target = f"{device['serial']}" if device else f"dnt_id={dnt_id}"
# 存储待插入的车检器序列号
if detector_serial:
set_pending_detector_serial(dnt_id, detector_serial)
# 清除旧记录,然后插入第一条 0xB0 # 清除旧记录,然后插入第一条 0xB0
clear_serialnet_records(dnt_id) clear_serialnet_records(dnt_id)
record_id = insert_serialnet(dnt_id, COMMANDS["B0"]) record_id = insert_serialnet(dnt_id, COMMANDS["B0"])

View File

@@ -30,6 +30,7 @@ const VIEWS = {
cols: [ cols: [
{ key: 'id', title: 'ID' }, { key: 'id', title: 'ID' },
{ key: 'serial', title: '设备编码' }, { key: 'serial', title: '设备编码' },
{ key: 'detector_serial', title: '车检器序列号', render: r => r.detector_serial || '-' },
{ key: 'model', title: '型号', render: r => getDevTypeName(r.sub_type) }, { key: 'model', title: '型号', render: r => getDevTypeName(r.sub_type) },
{ key: 'data_source', title: '来源' }, { key: 'data_source', title: '来源' },
{ key: 'test_mode', title: '测试模式', render: r => r.test_mode === 1 ? '波动' : '灵敏度' }, { key: 'test_mode', title: '测试模式', render: r => r.test_mode === 1 ? '波动' : '灵敏度' },
@@ -66,6 +67,7 @@ const VIEWS = {
cols: [ cols: [
{ key: 'id', title: 'ID' }, { key: 'id', title: 'ID' },
{ key: 'serial', title: '设备编码' }, { key: 'serial', title: '设备编码' },
{ key: 'detector_serial', title: '车检器序列号', render: r => r.detector_serial || '-' },
{ key: 'model', title: '型号', render: r => getDevTypeName(r.sub_type) }, { key: 'model', title: '型号', render: r => getDevTypeName(r.sub_type) },
{ key: 'test_mode', title: '测试模式', render: r => r.test_mode === 1 ? '波动' : '灵敏度' }, { key: 'test_mode', title: '测试模式', render: r => r.test_mode === 1 ? '波动' : '灵敏度' },
{ key: 'iffinish', title: '完成', render: r => r.iffinish === '1' ? '是' : '否' }, { key: 'iffinish', title: '完成', render: r => r.iffinish === '1' ? '是' : '否' },
@@ -89,6 +91,7 @@ const VIEWS = {
cols: [ cols: [
{ key: 'id', title: 'ID' }, { key: 'id', title: 'ID' },
{ key: 'serial', title: '设备编码' }, { key: 'serial', title: '设备编码' },
{ key: 'detector_serial', title: '车检器序列号', render: r => r.detector_serial || '-' },
{ key: 'remain_count', title: '剩余次数' }, { key: 'remain_count', title: '剩余次数' },
{ key: 'work_freq', title: '工作频率(Hz)' }, { key: 'work_freq', title: '工作频率(Hz)' },
{ key: 'curr_dist', title: '当前距离(mm)' }, { key: 'curr_dist', title: '当前距离(mm)' },

View File

@@ -63,9 +63,12 @@ async function toggleAuto() {
async function startAuto() { async function startAuto() {
if (!checkDeviceOnline()) return; if (!checkDeviceOnline()) return;
const count = parseInt(document.getElementById("test-count").value) || 10; const count = parseInt(document.getElementById("test-count").value) || 1;
if (count < 1) return; if (count < 1) return;
// 读取车检器序列号
const detectorSerial = document.getElementById("detector-serial").value.trim();
// 读取参数 // 读取参数
intervalMs = (parseFloat(document.getElementById("interval-sec").value) || 3) * 1000; intervalMs = (parseFloat(document.getElementById("interval-sec").value) || 3) * 1000;
timeoutMs = (parseFloat(document.getElementById("timeout-sec").value) || 10) * 1000; timeoutMs = (parseFloat(document.getElementById("timeout-sec").value) || 10) * 1000;
@@ -118,7 +121,7 @@ async function startAuto() {
const resp = await fetch("/api/automation/start", { const resp = await fetch("/api/automation/start", {
method: "POST", method: "POST",
headers: { "Content-Type": "application/json" }, headers: { "Content-Type": "application/json" },
body: JSON.stringify({ dnt_id: DNT_ID, count }), body: JSON.stringify({ dnt_id: DNT_ID, count, detector_serial: detectorSerial }),
}); });
const data = await resp.json(); const data = await resp.json();
if (data.ok) { if (data.ok) {
@@ -150,6 +153,9 @@ function stopAuto() {
const btn = document.getElementById("btn-auto"); const btn = document.getElementById("btn-auto");
btn.textContent = "开始"; btn.textContent = "开始";
btn.className = "btn-start"; btn.className = "btn-start";
// 自动化结束 → 焦点回到车检器序列号输入框(全选),方便下一个车检器测试
setTimeout(focusDetectorSerial, 100);
} }
// ─── 发送下一条 0xB0 ──────────────────────────── // ─── 发送下一条 0xB0 ────────────────────────────
@@ -404,9 +410,37 @@ async function loadInitialData() {
} catch (e) { } catch (e) {
// 初始加载静默失败 // 初始加载静默失败
} }
// 页面加载后焦点落到车检器序列号输入框(全选)
setTimeout(focusDetectorSerial, 200);
} }
loadInitialData(); loadInitialData();
// ─── 回车键触发"开始"按钮 ─────────────────────
/** 将焦点移到车检器序列号输入框并全选已有文本 */
function focusDetectorSerial() {
const input = document.getElementById("detector-serial");
if (input) {
input.focus();
input.select();
}
}
document.addEventListener("DOMContentLoaded", function() {
const detectorInput = document.getElementById("detector-serial");
if (detectorInput) {
detectorInput.addEventListener("keydown", function(e) {
if (e.key === "Enter") {
e.preventDefault();
const btn = document.getElementById("btn-auto");
if (btn && !autoRunning) {
btn.click();
}
}
});
}
});
// ─── 设备状态异步刷新 ────────────────────────── // ─── 设备状态异步刷新 ──────────────────────────
async function refreshDeviceStatus() { async function refreshDeviceStatus() {
@@ -543,6 +577,7 @@ function renderLatest(data) {
typeName = devTypeNameCache[data.sub_type] || `Unknown(${data.sub_type})`; typeName = devTypeNameCache[data.sub_type] || `Unknown(${data.sub_type})`;
} }
div.innerHTML = ` div.innerHTML = `
<p>车检器序列号:<strong>${data.detector_serial || '-'}</strong></p>
<p>设备型号:<strong>${typeName || '-'}</strong></p> <p>设备型号:<strong>${typeName || '-'}</strong></p>
<p>测试模式:<strong>${data.test_mode === 1 ? '波动测试' : '灵敏度测试'}</strong></p> <p>测试模式:<strong>${data.test_mode === 1 ? '波动测试' : '灵敏度测试'}</strong></p>
<p>峰峰值:${data.ppvalue?.toFixed(2) || '-'} V</p> <p>峰峰值:${data.ppvalue?.toFixed(2) || '-'} V</p>

View File

@@ -58,17 +58,22 @@
<h3>自动化测试</h3> <h3>自动化测试</h3>
<div class="automation"> <div class="automation">
<label>
车检器序列号:
<input type="text" id="detector-serial" placeholder="选填" style="width:180px;">
</label>
<br style="margin-bottom:8px;">
<label> <label>
测试次数: 测试次数:
<input type="number" id="test-count" value="10" min="1" max="9999"> <input type="number" id="test-count" value="1" min="1" max="9999">
</label> </label>
<label style="margin-left:16px;"> <label style="margin-left:16px;">
间隔时间(秒) 间隔时间(秒)
<input type="number" id="interval-sec" value="10" min="0" max="300" style="width:60px;"> <input type="number" id="interval-sec" value="5" min="0" max="300" style="width:60px;">
</label> </label>
<label style="margin-left:16px;"> <label style="margin-left:16px;">
超时时间(秒) 超时时间(秒)
<input type="number" id="timeout-sec" value="5" min="1" max="600" style="width:60px;"> <input type="number" id="timeout-sec" value="4" min="1" max="600" style="width:60px;">
</label> </label>
<button id="btn-auto" class="btn-start" onclick="toggleAuto()">开始</button> <button id="btn-auto" class="btn-start" onclick="toggleAuto()">开始</button>
<div class="progress-container"> <div class="progress-container">