feat: 波动测试模式前端适配 — tb_state_tst扩展+0xB4存库+页面更新

- edc_server/models.py: tb_state_tst DDL增加test_mode/data_source + B4字段
  + ALTER TABLE自动迁移 + insert_test_result扩展 + insert_wave_data
- edc_server/handlers.py: 0xB2处理传test_mode、0xB4处理调用insert_wave_data存库
- edc-web/models.py: 新增get_latest_wave_data/get_wave_records + test_mode筛选
- edc-web/routes: test_op返回wave数据、test_data支持test_mode筛选
- 前端: test_op页面增加波动数据显示区+测试模式列
  test_data页面增加test_mode下拉筛选+B4字段列+CSV导出适配
This commit is contained in:
wangfq
2026-06-03 14:14:52 +08:00
parent cf0b308e22
commit a69d7ab1d0
8 changed files with 368 additions and 8 deletions

View File

@@ -151,8 +151,11 @@ def get_latest_test_state(dnt_id: int) -> dict | None:
def get_test_data(page: int = 1, per_page: int = 20,
serial: str = "", date_from: str = "",
date_to: str = "") -> tuple[list[dict], int]:
"""分页查询测试数据JOIN dnt_info返回 (records, total)"""
date_to: str = "", test_mode: str = "") -> tuple[list[dict], int]:
"""分页查询测试数据JOIN dnt_info返回 (records, total)
test_mode: ''=全部, '0'=灵敏度, '1'=波动
"""
conn = get_conn()
try:
with conn.cursor() as cur:
@@ -167,6 +170,9 @@ def get_test_data(page: int = 1, per_page: int = 20,
if date_to:
where.append("t.create_time <= %s")
params.append(date_to + " 23:59:59")
if test_mode:
where.append("t.test_mode = %s")
params.append(int(test_mode))
where_clause = " AND ".join(where) if where else "1=1"
@@ -195,8 +201,11 @@ def get_test_data(page: int = 1, per_page: int = 20,
def get_all_test_data_for_export(serial: str = "", date_from: str = "",
date_to: str = "") -> list[dict]:
"""导出全部数据"""
date_to: str = "", test_mode: str = "") -> list[dict]:
"""导出全部数据
test_mode: ''=全部, '0'=灵敏度, '1'=波动
"""
conn = get_conn()
try:
with conn.cursor() as cur:
@@ -211,6 +220,9 @@ def get_all_test_data_for_export(serial: str = "", date_from: str = "",
if date_to:
where.append("t.create_time <= %s")
params.append(date_to + " 23:59:59")
if test_mode:
where.append("t.test_mode = %s")
params.append(int(test_mode))
where_clause = " AND ".join(where) if where else "1=1"
cur.execute(
@@ -285,6 +297,37 @@ def get_automation_records(dnt_id: int, since: str) -> list[dict]:
conn.close()
def get_latest_wave_data(dnt_id: int) -> dict | None:
"""获取设备最新一条 B4 波动测试数据"""
conn = get_conn()
try:
with conn.cursor() as cur:
cur.execute(
"SELECT * FROM tb_state_tst WHERE dnt_id=%s AND data_source='B4' "
"ORDER BY id DESC LIMIT 1",
(dnt_id,),
)
return cur.fetchone()
finally:
conn.close()
def get_wave_records(dnt_id: int, since: str) -> list[dict]:
"""获取本轮 B4 波动测试明细"""
conn = get_conn()
try:
with conn.cursor() as cur:
cur.execute(
"SELECT * FROM tb_state_tst "
"WHERE dnt_id=%s AND data_source='B4' AND create_time >= %s "
"ORDER BY id ASC",
(dnt_id, since),
)
return cur.fetchall()
finally:
conn.close()
# ─── 用户管理 ──────────────────────────────────────────────────────
def get_user_by_username(username: str) -> dict | None:

View File

@@ -23,8 +23,9 @@ def api_test_data():
serial = request.args.get("serial", "", type=str)
date_from = request.args.get("date_from", "", type=str)
date_to = request.args.get("date_to", "", type=str)
test_mode = request.args.get("test_mode", "", type=str)
records, total = get_test_data(page, per_page, serial, date_from, date_to)
records, total = get_test_data(page, per_page, serial, date_from, date_to, test_mode)
return jsonify({
"records": records,
"total": total,
@@ -40,8 +41,9 @@ def api_export():
serial = request.args.get("serial", "", type=str)
date_from = request.args.get("date_from", "", type=str)
date_to = request.args.get("date_to", "", type=str)
test_mode = request.args.get("test_mode", "", type=str)
records = get_all_test_data_for_export(serial, date_from, date_to)
records = get_all_test_data_for_export(serial, date_from, date_to, test_mode)
output = io.StringIO()
writer = csv.writer(output)

View File

@@ -9,6 +9,8 @@ from app.models import (
get_latest_test_state,
get_automation_averages,
get_automation_records,
get_latest_wave_data,
get_wave_records,
clear_serialnet_records,
insert_log,
)
@@ -118,9 +120,13 @@ def api_automation_progress(dnt_id):
latest = get_latest_test_state(dnt_id)
averages = get_automation_averages(dnt_id, since if since else None)
records = get_automation_records(dnt_id, since) if since else []
latest_wave = get_latest_wave_data(dnt_id)
wave_records = get_wave_records(dnt_id, since) if since else []
return jsonify({
"stats": stats,
"latest": latest,
"averages": averages,
"records": records,
"latest_wave": latest_wave,
"wave_records": wave_records,
})

View File

@@ -13,11 +13,13 @@ async function searchData(page = 1) {
const serial = document.getElementById("search-serial").value;
const dateFrom = document.getElementById("search-date-from").value;
const dateTo = document.getElementById("search-date-to").value;
const testMode = document.getElementById("search-test-mode").value;
const params = new URLSearchParams({ page, per_page: 20 });
if (serial) params.set("serial", serial);
if (dateFrom) params.set("date_from", dateFrom);
if (dateTo) params.set("date_to", dateTo);
if (testMode) params.set("test_mode", testMode);
try {
const resp = await fetch(`/api/test-data?${params}`);
@@ -33,7 +35,7 @@ async function searchData(page = 1) {
function renderTable(records) {
const tbody = document.querySelector("#test-data-table tbody");
if (!records.length) {
tbody.innerHTML = '<tr><td colspan="17" style="text-align:center;color:#999;">暂无数据</td></tr>';
tbody.innerHTML = '<tr><td colspan="24" style="text-align:center;color:#999;">暂无数据</td></tr>';
return;
}
tbody.innerHTML = records.map(r => `
@@ -43,6 +45,8 @@ function renderTable(records) {
<td>${r.dpg430_addr}</td>
<td>${r.sub_type === 1 ? 'PD132' : r.sub_type === 2 ? 'DLD110' : '-'}</td>
<td>${r.str_type || '-'}</td>
<td>${r.test_mode === 1 ? '波动测试' : '灵敏度测试'}</td>
<td>${r.data_source || '-'}</td>
<td>${r.iffinish === '1' ? '是' : '否'}</td>
<td>${r.fault_info || '无'}</td>
<td>${r.relay_out || '无'}</td>
@@ -54,6 +58,11 @@ function renderTable(records) {
<td>${r.exit_dist || '-'}</td>
<td>${toSpeed(r.enter_speed)}</td>
<td>${toSpeed(r.exit_speed)}</td>
<td>${r.remain_count || '-'}</td>
<td>${r.curr_dist || '-'}</td>
<td>${r.speed || '-'}</td>
<td>${r.near_dist || '-'}</td>
<td>${r.far_dist || '-'}</td>
<td>${r.create_time || '-'}</td>
</tr>
`).join("");
@@ -78,11 +87,13 @@ function exportCSV() {
const serial = document.getElementById("search-serial").value;
const dateFrom = document.getElementById("search-date-from").value;
const dateTo = document.getElementById("search-date-to").value;
const testMode = document.getElementById("search-test-mode").value;
const params = new URLSearchParams();
if (serial) params.set("serial", serial);
if (dateFrom) params.set("date_from", dateFrom);
if (dateTo) params.set("date_to", dateTo);
if (testMode) params.set("test_mode", testMode);
window.location.href = `/api/test-data/export?${params}`;
}

View File

@@ -67,6 +67,7 @@ async function startAuto() {
// 清空显示
resetAverages();
document.getElementById("latest-result").innerHTML = '<p class="placeholder">等待测试...</p>';
document.getElementById("latest-wave").innerHTML = '<p class="placeholder">暂无波动数据...</p>';
document.getElementById("progress-bar").style.width = "0%";
document.getElementById("progress-text").textContent = "0/" + count + " (0 失败)";
document.getElementById("stat-done").textContent = "0";
@@ -145,6 +146,11 @@ async function pollProgress() {
renderAverages(data.averages);
}
// 显示波动测试数据
if (data.latest_wave) {
renderLatestWave(data.latest_wave);
}
// 显示本轮测试明细
if (data.records) {
renderRecords(data.records);
@@ -209,6 +215,7 @@ function renderLatest(data) {
const div = document.getElementById("latest-result");
div.innerHTML = `
<p>设备型号:<strong>${data.str_type || '-'}</strong></p>
<p>测试模式:<strong>${data.test_mode === 1 ? '波动测试' : '灵敏度测试'}</strong></p>
<p>峰峰值:${data.ppvalue?.toFixed(2) || '-'} V</p>
<p>开始工作频率:${data.idle_freq || '-'} Hz</p>
<p>进入工作频率:${data.enter_freq || '-'} Hz</p>
@@ -242,6 +249,28 @@ function resetAverages() {
});
}
// ─── 显示波动测试数据 ──────────────────────────
function renderLatestWave(data) {
const div = document.getElementById("latest-wave");
if (!data || !data.work_freq) {
div.innerHTML = '<p class="placeholder">暂无波动数据...</p>';
return;
}
div.innerHTML = `
<p>剩余次数:<strong>${data.remain_count || 0}</strong></p>
<p>工作频率:${data.work_freq || '-'} Hz</p>
<p>当前距离:${data.curr_dist || '-'} mm</p>
<p>当前速度:${toSpeed(data.speed)} m/s</p>
<p>最近距离:${data.near_dist || '-'} mm</p>
<p>最远距离:${data.far_dist || '-'} mm</p>
<p>进入高度 (B4)${data.b4_enter_dist || '-'} mm</p>
<p>离开高度 (B4)${data.b4_leave_dist || '-'} mm</p>
<p>继电器:${data.relay_out || '无'}</p>
<p>时间:${data.create_time || '-'}</p>
`;
}
// ─── 显示本轮测试明细 ──────────────────────────
function renderRecords(records) {
@@ -260,6 +289,7 @@ function renderRecords(records) {
<td style="color:${r.sn_state === 2 ? '#27ae60' : r.sn_state === 3 ? '#e74c3c' : '#888'}">
${r.sn_state === 2 ? 'OK' : r.sn_state === 3 ? '超时' : '?'}
</td>
<td>${r.test_mode === 1 ? '波动' : '灵敏度'}</td>
<td>${r.ppvalue?.toFixed(2) || '-'}</td>
<td>${r.idle_freq || '-'}</td>
<td>${r.enter_dist || '-'}</td>

View File

@@ -9,6 +9,14 @@
设备编码:
<input type="text" id="search-serial" placeholder="输入设备编码搜索...">
</label>
<label>
测试模式:
<select id="search-test-mode">
<option value="">全部</option>
<option value="0">灵敏度测试</option>
<option value="1">波动测试</option>
</select>
</label>
<label>
日期范围:
<input type="date" id="search-date-from">
@@ -27,6 +35,8 @@
<th>DG430地址</th>
<th>设备型号</th>
<th>类型</th>
<th>测试模式</th>
<th>数据来源</th>
<th>是否完成</th>
<th>故障信息</th>
<th>继电器</th>
@@ -38,6 +48,11 @@
<th>离开距离(mm)</th>
<th>进入速度(m/s)</th>
<th>离开速度(m/s)</th>
<th>剩余次数</th>
<th>当前距离(mm)</th>
<th>速度(dm/s)</th>
<th>最近距离(mm)</th>
<th>最远距离(mm)</th>
<th>时间</th>
</tr>
</thead>

View File

@@ -49,6 +49,11 @@
<p class="placeholder">等待设备上报...</p>
</div>
<h3>波动测试数据</h3>
<div id="latest-wave">
<p class="placeholder">暂无波动数据...</p>
</div>
<h3>自动化平均值</h3>
<table id="avg-table">
<tr><td>平均峰峰值</td><td id="avg-ppvalue">-</td><td>V</td></tr>
@@ -65,7 +70,7 @@
<table id="records-table" style="font-size:11px;">
<thead>
<tr>
<th>#</th><th>串口状态</th><th>峰峰值(V)</th><th>开始频率</th><th>进入距离</th><th>离开距离</th><th>速度(m/s)</th><th>时间</th>
<th>#</th><th>串口状态</th><th>模式</th><th>峰峰值(V)</th><th>开始频率</th><th>进入距离</th><th>离开距离</th><th>速度(m/s)</th><th>时间</th>
</tr>
</thead>
<tbody></tbody>