Compare commits
8 Commits
aa2815b5cc
...
8a6b5c6d07
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8a6b5c6d07 | ||
|
|
f0ec79ca2f | ||
|
|
4b082e35df | ||
|
|
521cbe4107 | ||
|
|
4ac6cbb2fe | ||
|
|
0dfb928375 | ||
|
|
366c7f909a | ||
|
|
b4b7387b39 |
68
docs/reports/weekly-2026-06-12.md
Normal file
68
docs/reports/weekly-2026-06-12.md
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
# 周报 — 2026.06.09 ~ 2026.06.12
|
||||||
|
|
||||||
|
## 一、设备型号动态管理
|
||||||
|
|
||||||
|
**问题**:车检器型号名称(如 DLD110SV)在代码中硬编码,新增型号后测试操作页显示 `Unknown(3)` 或 `0x03`,测试信息页型号列显示 `-`。
|
||||||
|
|
||||||
|
**修复**:
|
||||||
|
- **后端** `edc_server`:B2/B4 数据写入时,废弃硬编码 `{1:"PD132",2:"DLD110"}` 映射,改为查询 `tb_vechicle_base_test` 表获取 `type_num → dev_name`,带内存缓存避免高频 DB 查询。
|
||||||
|
- **前端** `edc-web`:
|
||||||
|
- `test_op.js` 工装配置概览面板、最新测试结果区域,从 `/api/vehicle-base-test` 动态获取型号映射。
|
||||||
|
- `test_data.js` 测试信息页型号列,同样改为动态查询。
|
||||||
|
- 每 5 秒自动刷新型号缓存,工装配置页新增型号后无需手动刷新。
|
||||||
|
|
||||||
|
## 二、测试操作页实时数据改进
|
||||||
|
|
||||||
|
**问题**:
|
||||||
|
1. 工装本地按键触发的测试数据上报后,网页端无法实时显示,必须依赖网页端"开始"按钮。
|
||||||
|
2. 数据轮询间隔偏长(5 秒),新记录无计数提示。
|
||||||
|
|
||||||
|
**实现**:
|
||||||
|
- 新增被动轮询机制,**每 3 秒**自动拉取最新测试数据,覆盖工装本地按键和网页手动指令两种触发方式。
|
||||||
|
- 自动化测试运行期间,被动轮询自动让位给 `pollProgress`(500ms 高频轮询),结束后无缝接回。
|
||||||
|
- 「当前测试数据」标题旁新增 **B2 新记录条数**显示 `(N 条新记录)`,页面加载/自动化开始时自动复位。
|
||||||
|
|
||||||
|
## 三、角色权限体系
|
||||||
|
|
||||||
|
| 角色 | 权限 |
|
||||||
|
|---|---|
|
||||||
|
| `admin` | 全部功能(含用户管理、删除数据) |
|
||||||
|
| `manager` | 管理功能(用户管理除外)— 工装配置、数据删除等 |
|
||||||
|
| `analyst` | 仅测试数据查询/下载 + 修改密码 |
|
||||||
|
| `operator` | 测试操作 + 测试数据查看(不含工装配置) |
|
||||||
|
|
||||||
|
- `analyst` 角色访问受限页面时自动跳转到测试数据页并提示。
|
||||||
|
- 所有用户可自行修改密码。
|
||||||
|
|
||||||
|
## 四、设备日志管理
|
||||||
|
|
||||||
|
- 新增设备日志管理页面,记录设备 TCP 连接/断开、异常事件。
|
||||||
|
- 支持按设备编码、事件类型、时间范围筛选。
|
||||||
|
- 支持 **CSV 导出**,修复时区偏移 8 小时问题。
|
||||||
|
- 设备列表页在线/离线状态每 5 秒实时刷新。
|
||||||
|
- 后端 `device_status_monitor` 增加全表扫描,修正状态不一致问题。
|
||||||
|
|
||||||
|
## 五、UI/UX 优化
|
||||||
|
|
||||||
|
- **继电器输出格式化**:明确区分"✅有输出"/"❌无输出",前端直接显示 DB 字段。
|
||||||
|
- **工装配置概览面板**:测试操作页顶部展示当前配置参数(型号、频率范围、线圈、车辆等),可折叠。
|
||||||
|
- **测试信息页重构**:
|
||||||
|
- 三视图切换(全部数据 / B2 灵敏度 / B4 波动),差异字段自动隐藏。
|
||||||
|
- 表格支持横向滚动,列宽自适应不换行。
|
||||||
|
- 故障信息列限制 12em 宽度,超长截断省略 + hover 显示全文。
|
||||||
|
- 配置页频率/峰峰值前端显示与 DB 原始值双向转换修复。
|
||||||
|
|
||||||
|
## 六、Bug 修复
|
||||||
|
|
||||||
|
| 问题 | 修复 |
|
||||||
|
|---|---|
|
||||||
|
| 浏览器缓存导致工装参数 GET 返回旧数据 | 响应头 `Cache-Control: no-store` |
|
||||||
|
| `renderLatest` 覆盖测试模式,灵敏度/波动显示回退 | 分离测试模式更新与数据渲染逻辑 |
|
||||||
|
| 测试操作页工装配置修改后不同步 | 每 5 秒刷新 + 禁用缓存 |
|
||||||
|
| 设备离线时仍可发送指令 | 在线状态检查,离线/通信不良时弹窗提示并阻止 |
|
||||||
|
| HeartBeat 大小写不匹配,交互未记录 | 统一大小写匹配 |
|
||||||
|
| 后端 `device_status_monitor` 状态不一致 | 增加 `dnt_info` 全表扫描修正 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**总计提交**:vd_test_fixture 24 次,edc_server 8 次。
|
||||||
@@ -149,11 +149,12 @@ def get_latest_test_state(dnt_id: int) -> dict | None:
|
|||||||
conn.close()
|
conn.close()
|
||||||
|
|
||||||
|
|
||||||
def get_test_data(page: int = 1, per_page: int = 20,
|
def get_test_data(page: int = 1, per_page: int = 100,
|
||||||
serial: str = "", date_from: str = "",
|
serial: str = "", date_from: str = "",
|
||||||
date_to: str = "", test_mode: str = "",
|
date_to: str = "", test_mode: str = "",
|
||||||
data_source: str = "") -> tuple[list[dict], int]:
|
data_source: str = "",
|
||||||
"""分页查询测试数据(JOIN dnt_info),返回 (records, total)
|
detector_serial: str = "") -> tuple[list[dict], int]:
|
||||||
|
"""分页查询测试数据(JOIN dnt_info),最多返回最近 6000 条,返回 (records, total)
|
||||||
|
|
||||||
test_mode: ''=全部, '0'=灵敏度, '1'=波动
|
test_mode: ''=全部, '0'=灵敏度, '1'=波动
|
||||||
data_source: ''=全部, 'B2', 'B4'
|
data_source: ''=全部, 'B2', 'B4'
|
||||||
@@ -166,6 +167,9 @@ def get_test_data(page: int = 1, per_page: int = 20,
|
|||||||
if serial:
|
if serial:
|
||||||
where.append("d.serial LIKE %s")
|
where.append("d.serial LIKE %s")
|
||||||
params.append(f"%{serial}%")
|
params.append(f"%{serial}%")
|
||||||
|
if detector_serial:
|
||||||
|
where.append("t.detector_serial LIKE %s")
|
||||||
|
params.append(f"%{detector_serial}%")
|
||||||
if date_from:
|
if date_from:
|
||||||
where.append("t.create_time >= %s")
|
where.append("t.create_time >= %s")
|
||||||
params.append(date_from if len(date_from) > 10 else date_from)
|
params.append(date_from if len(date_from) > 10 else date_from)
|
||||||
@@ -181,17 +185,21 @@ def get_test_data(page: int = 1, per_page: int = 20,
|
|||||||
|
|
||||||
where_clause = " AND ".join(where) if where else "1=1"
|
where_clause = " AND ".join(where) if where else "1=1"
|
||||||
|
|
||||||
# count
|
# count — 最多 6000 条
|
||||||
cur.execute(
|
cur.execute(
|
||||||
f"SELECT COUNT(*) as total FROM tb_state_tst t "
|
f"SELECT COUNT(*) as total FROM ("
|
||||||
f"JOIN dnt_info d ON t.dnt_id = d.id WHERE {where_clause}",
|
f"SELECT 1 FROM tb_state_tst t "
|
||||||
|
f"JOIN dnt_info d ON t.dnt_id = d.id "
|
||||||
|
f"WHERE {where_clause} ORDER BY t.id DESC LIMIT 6000"
|
||||||
|
f") sub",
|
||||||
params,
|
params,
|
||||||
)
|
)
|
||||||
total = cur.fetchone()["total"]
|
total = cur.fetchone()["total"]
|
||||||
|
|
||||||
# data
|
# data — 子查询限 6000 后再分页
|
||||||
offset = (page - 1) * per_page
|
offset = (page - 1) * per_page
|
||||||
cur.execute(
|
cur.execute(
|
||||||
|
f"SELECT * FROM ("
|
||||||
f"SELECT t.*, d.serial, "
|
f"SELECT t.*, d.serial, "
|
||||||
f"c.coil_num, c.name as coil_name, "
|
f"c.coil_num, c.name as coil_name, "
|
||||||
f"sc.simulate_num, sc.name as car_name "
|
f"sc.simulate_num, sc.name as car_name "
|
||||||
@@ -200,7 +208,8 @@ def get_test_data(page: int = 1, per_page: int = 20,
|
|||||||
f"LEFT JOIN tb_coil_info c ON t.coil_id = c.id "
|
f"LEFT JOIN tb_coil_info c ON t.coil_id = c.id "
|
||||||
f"LEFT JOIN tb_simulate_car sc ON t.simulate_car_id = sc.id "
|
f"LEFT JOIN tb_simulate_car sc ON t.simulate_car_id = sc.id "
|
||||||
f"WHERE {where_clause} "
|
f"WHERE {where_clause} "
|
||||||
f"ORDER BY t.id DESC LIMIT %s OFFSET %s",
|
f"ORDER BY t.id DESC LIMIT 6000"
|
||||||
|
f") sub ORDER BY id DESC LIMIT %s OFFSET %s",
|
||||||
params + [per_page, offset],
|
params + [per_page, offset],
|
||||||
)
|
)
|
||||||
records = cur.fetchall()
|
records = cur.fetchall()
|
||||||
@@ -212,8 +221,9 @@ def get_test_data(page: int = 1, per_page: int = 20,
|
|||||||
|
|
||||||
def get_all_test_data_for_export(serial: str = "", date_from: str = "",
|
def get_all_test_data_for_export(serial: str = "", date_from: str = "",
|
||||||
date_to: str = "", test_mode: str = "",
|
date_to: str = "", test_mode: str = "",
|
||||||
data_source: str = "") -> list[dict]:
|
data_source: str = "",
|
||||||
"""导出全部数据
|
detector_serial: str = "") -> list[dict]:
|
||||||
|
"""导出全部数据(最多最近 6000 条)
|
||||||
|
|
||||||
test_mode: ''=全部, '0'=灵敏度, '1'=波动
|
test_mode: ''=全部, '0'=灵敏度, '1'=波动
|
||||||
data_source: ''=全部, 'B2', 'B4'
|
data_source: ''=全部, 'B2', 'B4'
|
||||||
@@ -226,6 +236,9 @@ def get_all_test_data_for_export(serial: str = "", date_from: str = "",
|
|||||||
if serial:
|
if serial:
|
||||||
where.append("d.serial LIKE %s")
|
where.append("d.serial LIKE %s")
|
||||||
params.append(f"%{serial}%")
|
params.append(f"%{serial}%")
|
||||||
|
if detector_serial:
|
||||||
|
where.append("t.detector_serial LIKE %s")
|
||||||
|
params.append(f"%{detector_serial}%")
|
||||||
if date_from:
|
if date_from:
|
||||||
where.append("t.create_time >= %s")
|
where.append("t.create_time >= %s")
|
||||||
params.append(date_from if len(date_from) > 10 else date_from)
|
params.append(date_from if len(date_from) > 10 else date_from)
|
||||||
@@ -248,7 +261,7 @@ def get_all_test_data_for_export(serial: str = "", date_from: str = "",
|
|||||||
f"JOIN dnt_info d ON t.dnt_id = d.id "
|
f"JOIN dnt_info d ON t.dnt_id = d.id "
|
||||||
f"LEFT JOIN tb_coil_info c ON t.coil_id = c.id "
|
f"LEFT JOIN tb_coil_info c ON t.coil_id = c.id "
|
||||||
f"LEFT JOIN tb_simulate_car sc ON t.simulate_car_id = sc.id "
|
f"LEFT JOIN tb_simulate_car sc ON t.simulate_car_id = sc.id "
|
||||||
f"WHERE {where_clause} ORDER BY t.id DESC",
|
f"WHERE {where_clause} ORDER BY t.id DESC LIMIT 6000",
|
||||||
params,
|
params,
|
||||||
)
|
)
|
||||||
return cur.fetchall()
|
return cur.fetchall()
|
||||||
@@ -942,3 +955,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()
|
||||||
|
|||||||
@@ -21,15 +21,16 @@ def test_data_page():
|
|||||||
def api_test_data():
|
def api_test_data():
|
||||||
"""分页查询测试数据"""
|
"""分页查询测试数据"""
|
||||||
page = request.args.get("page", 1, type=int)
|
page = request.args.get("page", 1, type=int)
|
||||||
per_page = request.args.get("per_page", 20, type=int)
|
per_page = request.args.get("per_page", 100, type=int)
|
||||||
serial = request.args.get("serial", "", type=str)
|
serial = request.args.get("serial", "", type=str)
|
||||||
|
detector_serial = request.args.get("detector_serial", "", type=str)
|
||||||
date_from = request.args.get("date_from", "", type=str)
|
date_from = request.args.get("date_from", "", type=str)
|
||||||
date_to = request.args.get("date_to", "", type=str)
|
date_to = request.args.get("date_to", "", type=str)
|
||||||
test_mode = request.args.get("test_mode", "", type=str)
|
test_mode = request.args.get("test_mode", "", type=str)
|
||||||
data_source = request.args.get("data_source", "", type=str)
|
data_source = request.args.get("data_source", "", 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, data_source)
|
test_mode, data_source, detector_serial)
|
||||||
return jsonify({
|
return jsonify({
|
||||||
"records": records,
|
"records": records,
|
||||||
"total": total,
|
"total": total,
|
||||||
@@ -42,29 +43,33 @@ def api_test_data():
|
|||||||
@bp.route("/api/test-data/chart")
|
@bp.route("/api/test-data/chart")
|
||||||
@login_required
|
@login_required
|
||||||
def api_chart_data():
|
def api_chart_data():
|
||||||
"""返回图表所需全部数据(不分页)"""
|
"""返回图表所需全部数据(不分页,最多 6000 条)"""
|
||||||
serial = request.args.get("serial", "", type=str)
|
serial = request.args.get("serial", "", type=str)
|
||||||
|
detector_serial = request.args.get("detector_serial", "", type=str)
|
||||||
date_from = request.args.get("date_from", "", type=str)
|
date_from = request.args.get("date_from", "", type=str)
|
||||||
date_to = request.args.get("date_to", "", type=str)
|
date_to = request.args.get("date_to", "", type=str)
|
||||||
test_mode = request.args.get("test_mode", "", type=str)
|
test_mode = request.args.get("test_mode", "", type=str)
|
||||||
data_source = request.args.get("data_source", "", type=str)
|
data_source = request.args.get("data_source", "", 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, data_source)
|
test_mode, data_source,
|
||||||
|
detector_serial)
|
||||||
return jsonify({"records": records, "total": len(records)})
|
return jsonify({"records": records, "total": len(records)})
|
||||||
|
|
||||||
@bp.route("/api/test-data/export")
|
@bp.route("/api/test-data/export")
|
||||||
@login_required
|
@login_required
|
||||||
def api_export():
|
def api_export():
|
||||||
"""导出测试数据为 CSV"""
|
"""导出测试数据为 CSV(最多 6000 条)"""
|
||||||
serial = request.args.get("serial", "", type=str)
|
serial = request.args.get("serial", "", type=str)
|
||||||
|
detector_serial = request.args.get("detector_serial", "", type=str)
|
||||||
date_from = request.args.get("date_from", "", type=str)
|
date_from = request.args.get("date_from", "", type=str)
|
||||||
date_to = request.args.get("date_to", "", type=str)
|
date_to = request.args.get("date_to", "", type=str)
|
||||||
test_mode = request.args.get("test_mode", "", type=str)
|
test_mode = request.args.get("test_mode", "", type=str)
|
||||||
data_source = request.args.get("data_source", "", type=str)
|
data_source = request.args.get("data_source", "", 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, data_source)
|
test_mode, data_source,
|
||||||
|
detector_serial)
|
||||||
|
|
||||||
output = io.StringIO()
|
output = io.StringIO()
|
||||||
writer = csv.writer(output)
|
writer = csv.writer(output)
|
||||||
|
|||||||
@@ -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"])
|
||||||
|
|||||||
@@ -1,5 +1,26 @@
|
|||||||
// 测试信息页 — 三视图 (全部 / B2 / B4)
|
// 测试信息页 — 三视图 (全部 / B2 / B4)
|
||||||
|
|
||||||
|
// ─── 型号名称缓存 ─────────────────────────────────
|
||||||
|
let devTypeNameCache = {};
|
||||||
|
|
||||||
|
async function initDevTypeNames() {
|
||||||
|
try {
|
||||||
|
const resp = await fetch('/api/vehicle-base-test');
|
||||||
|
const tests = await resp.json();
|
||||||
|
devTypeNameCache = {};
|
||||||
|
tests.forEach(t => {
|
||||||
|
if (t.type_num != null && t.dev_name) {
|
||||||
|
devTypeNameCache[t.type_num] = t.dev_name;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (e) { console.error('加载型号名称失败:', e); }
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDevTypeName(subType) {
|
||||||
|
if (subType == null || subType === 0) return '-';
|
||||||
|
return devTypeNameCache[subType] || `Unknown(${subType})`;
|
||||||
|
}
|
||||||
|
|
||||||
// ─── 视图定义 ───────────────────────────────────
|
// ─── 视图定义 ───────────────────────────────────
|
||||||
|
|
||||||
const VIEWS = {
|
const VIEWS = {
|
||||||
@@ -7,18 +28,18 @@ const VIEWS = {
|
|||||||
label: '全部数据',
|
label: '全部数据',
|
||||||
data_source: '', // '' = 不过滤
|
data_source: '', // '' = 不过滤
|
||||||
cols: [
|
cols: [
|
||||||
{ key: 'id', title: 'ID' },
|
{ key: 'create_time', title: '时间', render: r => fmtTime(r.create_time) },
|
||||||
{ key: 'serial', title: '设备编码' },
|
{ key: 'serial', title: '设备编码', render: r => (r.serial || '').slice(-6) },
|
||||||
{ key: 'model', title: '型号', render: r => r.sub_type === 1 ? 'PD132' : r.sub_type === 2 ? 'DLD110' : '-' },
|
{ key: 'detector_serial', title: '车检器序列号', render: r => r.detector_serial || '-' },
|
||||||
|
{ 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: 'iffinish', title: '完成', render: r => r.data_source === 'B4' ? '-' : (r.iffinish === '1' ? '是' : '否') },
|
{ key: 'iffinish', title: '完成', render: r => r.data_source === 'B4' ? '-' : (r.iffinish === '1' ? '是' : '否') },
|
||||||
{ key: 'fault_info', title: '故障信息', render: r => r.data_source === 'B4' ? '-' : `<span style="display:inline-block;max-width:12em;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;" title="${escHtml(r.fault_info || '')}">${escHtml(r.fault_info || '-')}</span>` },
|
{ key: 'fault_info', title: '故障信息', render: r => r.data_source === 'B4' ? '-' : `<span style="display:inline-block;max-width:12em;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;" title="${escHtml(r.fault_info || '')}">${escHtml(r.fault_info || '-')}</span>` },
|
||||||
{ key: 'relay_out', title: '继电器', render: r => fmtRelay(r.relay_out) },
|
{ key: 'relay_out', title: '继电器', render: r => fmtRelay(r.relay_out) },
|
||||||
{ key: 'ppvalue', title: '峰峰值(V)', render: r => r.data_source === 'B4' ? '-' : (r.ppvalue != null ? r.ppvalue.toFixed(2) : '-') },
|
{ key: 'ppvalue', title: '峰峰值(V)', render: r => r.data_source === 'B4' ? '-' : (r.ppvalue != null ? r.ppvalue.toFixed(2) : '-') },
|
||||||
{ key: 'idle_freq', title: '开始频率(Hz)', render: r => r.data_source === 'B4' ? '-' : (r.idle_freq || '-') },
|
{ key: 'idle_freq', title: '开始频率(Hz)', render: r => r.data_source === 'B4' ? '-' : (r.idle_freq || '-') },
|
||||||
{ key: 'enter_freq', title: '进入频率(Hz)', render: r => r.data_source === 'B4' ? '-' : (r.enter_freq || '-') },
|
{ key: 'enter_freq', title: '触发频率(Hz)', render: r => r.data_source === 'B4' ? '-' : (r.enter_freq || '-') },
|
||||||
{ key: 'exit_freq', title: '离开频率(Hz)', render: r => r.data_source === 'B4' ? '-' : (r.exit_freq || '-') },
|
{ key: 'exit_freq', title: '释放频率(Hz)', render: r => r.data_source === 'B4' ? '-' : (r.exit_freq || '-') },
|
||||||
{ key: 'enter_dist', title: '触发距离(mm)', render: r => {
|
{ key: 'enter_dist', title: '触发距离(mm)', render: r => {
|
||||||
const v = r.data_source === 'B4' ? r.b4_enter_dist : r.enter_dist;
|
const v = r.data_source === 'B4' ? r.b4_enter_dist : r.enter_dist;
|
||||||
return v != null ? v + ' ' : '-';
|
return v != null ? v + ' ' : '-';
|
||||||
@@ -27,8 +48,8 @@ const VIEWS = {
|
|||||||
const v = r.data_source === 'B4' ? r.b4_leave_dist : r.exit_dist;
|
const v = r.data_source === 'B4' ? r.b4_leave_dist : r.exit_dist;
|
||||||
return v != null ? v + ' ' : '-';
|
return v != null ? v + ' ' : '-';
|
||||||
}},
|
}},
|
||||||
{ key: 'enter_speed', title: '进入速度(dm/s)', render: r => r.data_source === 'B4' ? '-' : toSpeed(r.enter_speed) },
|
{ key: 'enter_speed', title: '触发速度(dm/s)', render: r => r.data_source === 'B4' ? '-' : toSpeed(r.enter_speed) },
|
||||||
{ key: 'exit_speed', title: '离开速度(dm/s)', render: r => r.data_source === 'B4' ? '-' : toSpeed(r.exit_speed) },
|
{ key: 'exit_speed', title: '释放速度(dm/s)', render: r => r.data_source === 'B4' ? '-' : toSpeed(r.exit_speed) },
|
||||||
{ key: 'remain_count', title: '剩余次数', render: r => r.data_source === 'B2' ? '-' : (r.remain_count ?? '-') },
|
{ key: 'remain_count', title: '剩余次数', render: r => r.data_source === 'B2' ? '-' : (r.remain_count ?? '-') },
|
||||||
{ key: 'work_freq', title: '工作频率(Hz)', render: r => r.data_source === 'B2' ? '-' : (r.work_freq ?? '-') },
|
{ key: 'work_freq', title: '工作频率(Hz)', render: r => r.data_source === 'B2' ? '-' : (r.work_freq ?? '-') },
|
||||||
{ key: 'curr_dist', title: '当前距离(mm)', render: r => r.data_source === 'B2' ? '-' : (r.curr_dist != null ? r.curr_dist + ' ' : '-') },
|
{ key: 'curr_dist', title: '当前距离(mm)', render: r => r.data_source === 'B2' ? '-' : (r.curr_dist != null ? r.curr_dist + ' ' : '-') },
|
||||||
@@ -36,38 +57,39 @@ const VIEWS = {
|
|||||||
{ key: 'near_dist', title: '最近距离(mm)', render: r => r.data_source === 'B2' ? '-' : (r.near_dist != null ? r.near_dist + ' ' : '-') },
|
{ key: 'near_dist', title: '最近距离(mm)', render: r => r.data_source === 'B2' ? '-' : (r.near_dist != null ? r.near_dist + ' ' : '-') },
|
||||||
{ key: 'far_dist', title: '最远距离(mm)', render: r => r.data_source === 'B2' ? '-' : (r.far_dist != null ? r.far_dist + ' ' : '-') },
|
{ key: 'far_dist', title: '最远距离(mm)', render: r => r.data_source === 'B2' ? '-' : (r.far_dist != null ? r.far_dist + ' ' : '-') },
|
||||||
{ key: 'env', title: '测试环境', render: r => envLabel(r) },
|
{ key: 'env', title: '测试环境', render: r => envLabel(r) },
|
||||||
{ key: 'create_time', title: '时间', render: r => fmtTime(r.create_time) },
|
{ key: 'test_mode', title: '测试模式', render: r => r.test_mode === 1 ? '波动' : '灵敏度' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
b2: {
|
b2: {
|
||||||
label: '灵敏度测试',
|
label: '灵敏度测试',
|
||||||
data_source: 'B2',
|
data_source: 'B2',
|
||||||
cols: [
|
cols: [
|
||||||
{ key: 'id', title: 'ID' },
|
{ key: 'create_time', title: '时间', render: r => fmtTime(r.create_time) },
|
||||||
{ key: 'serial', title: '设备编码' },
|
{ key: 'serial', title: '设备编码', render: r => (r.serial || '').slice(-6) },
|
||||||
{ key: 'model', title: '型号', render: r => r.sub_type === 1 ? 'PD132' : r.sub_type === 2 ? 'DLD110' : '-' },
|
{ key: 'detector_serial', title: '车检器序列号', render: r => r.detector_serial || '-' },
|
||||||
{ key: 'test_mode', title: '测试模式', render: r => r.test_mode === 1 ? '波动' : '灵敏度' },
|
{ key: 'model', title: '型号', render: r => getDevTypeName(r.sub_type) },
|
||||||
{ key: 'iffinish', title: '完成', render: r => r.iffinish === '1' ? '是' : '否' },
|
{ key: 'iffinish', title: '完成', render: r => r.iffinish === '1' ? '是' : '否' },
|
||||||
{ key: 'fault_info', title: '故障信息', render: r => `<span style="display:inline-block;max-width:12em;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;" title="${escHtml(r.fault_info || '')}">${escHtml(r.fault_info || '-')}</span>` },
|
{ key: 'fault_info', title: '故障信息', render: r => `<span style="display:inline-block;max-width:12em;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;" title="${escHtml(r.fault_info || '')}">${escHtml(r.fault_info || '-')}</span>` },
|
||||||
{ key: 'relay_out', title: '继电器', render: r => fmtRelay(r.relay_out) },
|
{ key: 'relay_out', title: '继电器', render: r => fmtRelay(r.relay_out) },
|
||||||
{ key: 'ppvalue', title: '峰峰值(V)', render: r => r.ppvalue?.toFixed(2) || '-' },
|
{ key: 'ppvalue', title: '峰峰值(V)', render: r => r.ppvalue?.toFixed(2) || '-' },
|
||||||
{ key: 'idle_freq', title: '开始频率(Hz)' },
|
{ key: 'idle_freq', title: '开始频率(Hz)' },
|
||||||
{ key: 'enter_freq', title: '进入频率(Hz)' },
|
{ key: 'enter_freq', title: '触发频率(Hz)' },
|
||||||
{ key: 'exit_freq', title: '离开频率(Hz)' },
|
{ key: 'exit_freq', title: '释放频率(Hz)' },
|
||||||
{ key: 'enter_dist', title: '触发距离(mm)' },
|
{ key: 'enter_dist', title: '触发距离(mm)' },
|
||||||
{ key: 'exit_dist', title: '释放距离(mm)' },
|
{ key: 'exit_dist', title: '释放距离(mm)' },
|
||||||
{ key: 'enter_speed', title: '进入速度', render: r => toSpeed(r.enter_speed) },
|
{ key: 'enter_speed', title: '触发速度', render: r => toSpeed(r.enter_speed) },
|
||||||
{ key: 'exit_speed', title: '离开速度', render: r => toSpeed(r.exit_speed) },
|
{ key: 'exit_speed', title: '释放速度', render: r => toSpeed(r.exit_speed) },
|
||||||
{ key: 'env', title: '测试环境', render: r => envLabel(r) },
|
{ key: 'env', title: '测试环境', render: r => envLabel(r) },
|
||||||
{ key: 'create_time', title: '时间', render: r => fmtTime(r.create_time) },
|
{ key: 'test_mode', title: '测试模式', render: r => r.test_mode === 1 ? '波动' : '灵敏度' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
b4: {
|
b4: {
|
||||||
label: '波动测试',
|
label: '波动测试',
|
||||||
data_source: 'B4',
|
data_source: 'B4',
|
||||||
cols: [
|
cols: [
|
||||||
{ key: 'id', title: 'ID' },
|
{ key: 'create_time', title: '时间', render: r => fmtTime(r.create_time) },
|
||||||
{ key: 'serial', title: '设备编码' },
|
{ key: 'serial', title: '设备编码', render: r => (r.serial || '').slice(-6) },
|
||||||
|
{ 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)' },
|
||||||
@@ -78,7 +100,7 @@ const VIEWS = {
|
|||||||
{ key: 'b4_leave_dist', title: '释放高度(mm)' },
|
{ key: 'b4_leave_dist', title: '释放高度(mm)' },
|
||||||
{ key: 'relay_out', title: '继电器', render: r => fmtRelay(r.relay_out) },
|
{ key: 'relay_out', title: '继电器', render: r => fmtRelay(r.relay_out) },
|
||||||
{ key: 'env', title: '测试环境', render: r => envLabel(r) },
|
{ key: 'env', title: '测试环境', render: r => envLabel(r) },
|
||||||
{ key: 'create_time', title: '时间', render: r => fmtTime(r.create_time) },
|
{ key: 'test_mode', title: '测试模式', render: r => r.test_mode === 1 ? '波动' : '灵敏度' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -169,14 +191,16 @@ function getDatetime(dateId, timeId) {
|
|||||||
async function searchData(page = 1) {
|
async function searchData(page = 1) {
|
||||||
currentPage = page;
|
currentPage = page;
|
||||||
const serial = document.getElementById("search-serial").value;
|
const serial = document.getElementById("search-serial").value;
|
||||||
|
const detectorSerial = document.getElementById("search-detector-serial").value;
|
||||||
const dateFrom = getDatetime("search-date-from", "search-time-from");
|
const dateFrom = getDatetime("search-date-from", "search-time-from");
|
||||||
const dateTo = getDatetime("search-date-to", "search-time-to");
|
const dateTo = getDatetime("search-date-to", "search-time-to");
|
||||||
const v = VIEWS[currentView];
|
const v = VIEWS[currentView];
|
||||||
|
|
||||||
const perPage = parseInt(document.getElementById("per-page").value) || 20;
|
const perPage = parseInt(document.getElementById("per-page").value) || 100;
|
||||||
|
|
||||||
const params = new URLSearchParams({ page, per_page: perPage });
|
const params = new URLSearchParams({ page, per_page: perPage });
|
||||||
if (serial) params.set("serial", serial);
|
if (serial) params.set("serial", serial);
|
||||||
|
if (detectorSerial) params.set("detector_serial", detectorSerial);
|
||||||
if (dateFrom) params.set("date_from", dateFrom);
|
if (dateFrom) params.set("date_from", dateFrom);
|
||||||
if (dateTo) params.set("date_to", dateTo);
|
if (dateTo) params.set("date_to", dateTo);
|
||||||
// 按 data_source 过滤(全部不过滤)
|
// 按 data_source 过滤(全部不过滤)
|
||||||
@@ -247,12 +271,14 @@ function renderPagination() {
|
|||||||
|
|
||||||
function exportCSV() {
|
function exportCSV() {
|
||||||
const serial = document.getElementById("search-serial").value;
|
const serial = document.getElementById("search-serial").value;
|
||||||
|
const detectorSerial = document.getElementById("search-detector-serial").value;
|
||||||
const dateFrom = getDatetime("search-date-from", "search-time-from");
|
const dateFrom = getDatetime("search-date-from", "search-time-from");
|
||||||
const dateTo = getDatetime("search-date-to", "search-time-to");
|
const dateTo = getDatetime("search-date-to", "search-time-to");
|
||||||
const v = VIEWS[currentView];
|
const v = VIEWS[currentView];
|
||||||
|
|
||||||
const params = new URLSearchParams();
|
const params = new URLSearchParams();
|
||||||
if (serial) params.set("serial", serial);
|
if (serial) params.set("serial", serial);
|
||||||
|
if (detectorSerial) params.set("detector_serial", detectorSerial);
|
||||||
if (dateFrom) params.set("date_from", dateFrom);
|
if (dateFrom) params.set("date_from", dateFrom);
|
||||||
if (dateTo) params.set("date_to", dateTo);
|
if (dateTo) params.set("date_to", dateTo);
|
||||||
if (v.data_source) params.set("data_source", v.data_source);
|
if (v.data_source) params.set("data_source", v.data_source);
|
||||||
@@ -270,12 +296,12 @@ const CHART_SERIES = {
|
|||||||
b2: [
|
b2: [
|
||||||
{ key: 'ppvalue', name: '峰峰值', unit: 'V', yAxisIndex: 0 },
|
{ key: 'ppvalue', name: '峰峰值', unit: 'V', yAxisIndex: 0 },
|
||||||
{ key: 'idle_freq', name: '开始频率', unit: 'Hz', yAxisIndex: 0 },
|
{ key: 'idle_freq', name: '开始频率', unit: 'Hz', yAxisIndex: 0 },
|
||||||
{ key: 'enter_freq', name: '进入频率', unit: 'Hz', yAxisIndex: 0 },
|
{ key: 'enter_freq', name: '触发频率', unit: 'Hz', yAxisIndex: 0 },
|
||||||
{ key: 'exit_freq', name: '离开频率', unit: 'Hz', yAxisIndex: 0 },
|
{ key: 'exit_freq', name: '释放频率', unit: 'Hz', yAxisIndex: 0 },
|
||||||
{ key: 'enter_dist', name: '进入距离', unit: 'mm', yAxisIndex: 1 },
|
{ key: 'enter_dist', name: '触发距离', unit: 'mm', yAxisIndex: 1 },
|
||||||
{ key: 'exit_dist', name: '离开距离', unit: 'mm', yAxisIndex: 1 },
|
{ key: 'exit_dist', name: '释放距离', unit: 'mm', yAxisIndex: 1 },
|
||||||
{ key: 'enter_speed', name: '进入速度', unit: 'dm/s',yAxisIndex: 2 },
|
{ key: 'enter_speed', name: '触发速度', unit: 'dm/s',yAxisIndex: 2 },
|
||||||
{ key: 'exit_speed', name: '离开速度', unit: 'dm/s',yAxisIndex: 2 },
|
{ key: 'exit_speed', name: '释放速度', unit: 'dm/s',yAxisIndex: 2 },
|
||||||
],
|
],
|
||||||
b4: [
|
b4: [
|
||||||
{ key: 'work_freq', name: '工作频率', unit: 'Hz', yAxisIndex: 0 },
|
{ key: 'work_freq', name: '工作频率', unit: 'Hz', yAxisIndex: 0 },
|
||||||
@@ -283,8 +309,8 @@ const CHART_SERIES = {
|
|||||||
{ key: 'speed', name: '速度', unit: 'dm/s',yAxisIndex: 2 },
|
{ key: 'speed', name: '速度', unit: 'dm/s',yAxisIndex: 2 },
|
||||||
{ key: 'near_dist', name: '最近距离', unit: 'mm', yAxisIndex: 1 },
|
{ key: 'near_dist', name: '最近距离', unit: 'mm', yAxisIndex: 1 },
|
||||||
{ key: 'far_dist', name: '最远距离', unit: 'mm', yAxisIndex: 1 },
|
{ key: 'far_dist', name: '最远距离', unit: 'mm', yAxisIndex: 1 },
|
||||||
{ key: 'b4_enter_dist', name: '进入高度', unit: 'mm', yAxisIndex: 1 },
|
{ key: 'b4_enter_dist', name: '触发高度', unit: 'mm', yAxisIndex: 1 },
|
||||||
{ key: 'b4_leave_dist', name: '离开高度', unit: 'mm', yAxisIndex: 1 },
|
{ key: 'b4_leave_dist', name: '释放高度', unit: 'mm', yAxisIndex: 1 },
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -340,6 +366,7 @@ async function loadChart() {
|
|||||||
if (!container || container.style.display === 'none') return;
|
if (!container || container.style.display === 'none') return;
|
||||||
|
|
||||||
const serial = document.getElementById('search-serial').value;
|
const serial = document.getElementById('search-serial').value;
|
||||||
|
const detectorSerial = document.getElementById('search-detector-serial').value;
|
||||||
const dateFrom = getDatetime('search-date-from', 'search-time-from');
|
const dateFrom = getDatetime('search-date-from', 'search-time-from');
|
||||||
const dateTo = getDatetime('search-date-to', 'search-time-to');
|
const dateTo = getDatetime('search-date-to', 'search-time-to');
|
||||||
const v = VIEWS[currentView];
|
const v = VIEWS[currentView];
|
||||||
@@ -349,6 +376,7 @@ async function loadChart() {
|
|||||||
|
|
||||||
const params = new URLSearchParams();
|
const params = new URLSearchParams();
|
||||||
if (serial) params.set('serial', serial);
|
if (serial) params.set('serial', serial);
|
||||||
|
if (detectorSerial) params.set('detector_serial', detectorSerial);
|
||||||
if (dateFrom) params.set('date_from', dateFrom);
|
if (dateFrom) params.set('date_from', dateFrom);
|
||||||
if (dateTo) params.set('date_to', dateTo);
|
if (dateTo) params.set('date_to', dateTo);
|
||||||
if (ds) params.set('data_source', ds);
|
if (ds) params.set('data_source', ds);
|
||||||
@@ -457,7 +485,8 @@ async function loadChart() {
|
|||||||
// ─── 初始加载 ────────────────────────────────────
|
// ─── 初始加载 ────────────────────────────────────
|
||||||
|
|
||||||
renderHead();
|
renderHead();
|
||||||
searchData(1);
|
// 先加载型号名称再查询数据,确保型号列正确渲染
|
||||||
|
initDevTypeNames().then(() => searchData(1));
|
||||||
|
|
||||||
// ─── 删除(admin)─────────────────────────────────
|
// ─── 删除(admin)─────────────────────────────────
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ let autoStartTime = "";
|
|||||||
let localSinceStr = "";
|
let localSinceStr = "";
|
||||||
let currentTestMode = null; // 0=灵敏度, 1=波动, null=未加载
|
let currentTestMode = null; // 0=灵敏度, 1=波动, null=未加载
|
||||||
let currentDeviceState = null; // 当前设备状态 (0=离线 1=在线 2=通信不良 null=未加载)
|
let currentDeviceState = null; // 当前设备状态 (0=离线 1=在线 2=通信不良 null=未加载)
|
||||||
|
let devTypeNameCache = {}; // type_num → dev_name 映射(从 tb_vechicle_base_test)
|
||||||
|
|
||||||
let pollInterval = null;
|
let pollInterval = null;
|
||||||
let nextCmdTimer = null; // 间隔等待定时器
|
let nextCmdTimer = null; // 间隔等待定时器
|
||||||
@@ -62,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;
|
||||||
@@ -77,6 +81,8 @@ async function startAuto() {
|
|||||||
autoFailed = 0;
|
autoFailed = 0;
|
||||||
autoRemaining = count;
|
autoRemaining = count;
|
||||||
lastDoneCount = 0;
|
lastDoneCount = 0;
|
||||||
|
newB2Count = 0;
|
||||||
|
updateRecordCount();
|
||||||
autoStartTime = new Date().toISOString();
|
autoStartTime = new Date().toISOString();
|
||||||
|
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
@@ -115,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) {
|
||||||
@@ -147,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 ────────────────────────────
|
||||||
@@ -217,9 +226,15 @@ async function pollProgress() {
|
|||||||
const stats = data.stats;
|
const stats = data.stats;
|
||||||
|
|
||||||
// ── 先渲染数据(放在所有 return 之前,避免完成时跳过渲染)──
|
// ── 先渲染数据(放在所有 return 之前,避免完成时跳过渲染)──
|
||||||
try { if (data.latest) renderLatest(data.latest); } catch (e) { console.error("renderLatest:", e); }
|
try { if (data.latest) {
|
||||||
|
if (data.latest.id !== lastLatestId) {
|
||||||
|
lastLatestId = data.latest.id;
|
||||||
|
if (data.latest.data_source === "B2") { newB2Count++; updateRecordCount(); }
|
||||||
|
}
|
||||||
|
renderLatest(data.latest);
|
||||||
|
} } catch (e) { console.error("renderLatest:", e); }
|
||||||
try { if (data.averages) renderAverages(data.averages); } catch (e) { console.error("renderAverages:", e); }
|
try { if (data.averages) renderAverages(data.averages); } catch (e) { console.error("renderAverages:", e); }
|
||||||
try { if (data.latest_wave) renderLatestWave(data.latest_wave); } catch (e) { console.error("renderLatestWave:", e); }
|
try { if (data.latest_wave) { renderLatestWave(data.latest_wave); lastWaveId = data.latest_wave.id; } } catch (e) { console.error("renderLatestWave:", e); }
|
||||||
try { if (data.records && data.records.length) renderRecords(data.records); } catch (e) { console.error("renderRecords:", e); }
|
try { if (data.records && data.records.length) renderRecords(data.records); } catch (e) { console.error("renderRecords:", e); }
|
||||||
|
|
||||||
// 更新计数
|
// 更新计数
|
||||||
@@ -273,6 +288,19 @@ async function pollProgress() {
|
|||||||
|
|
||||||
// ─── 页面加载时获取初始数据 ──────────────────────
|
// ─── 页面加载时获取初始数据 ──────────────────────
|
||||||
|
|
||||||
|
async function loadDeviceTypeNames() {
|
||||||
|
try {
|
||||||
|
const resp = await fetch(`/api/vehicle-base-test`);
|
||||||
|
const tests = await resp.json();
|
||||||
|
devTypeNameCache = {};
|
||||||
|
tests.forEach(t => {
|
||||||
|
if (t.type_num != null && t.dev_name) {
|
||||||
|
devTypeNameCache[t.type_num] = t.dev_name;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (e) { console.error("加载型号名称失败:", e); }
|
||||||
|
}
|
||||||
|
|
||||||
async function loadTestMode() {
|
async function loadTestMode() {
|
||||||
try {
|
try {
|
||||||
const resp = await fetch(`/api/fixture/param/${DNT_ID}?_=${Date.now()}`);
|
const resp = await fetch(`/api/fixture/param/${DNT_ID}?_=${Date.now()}`);
|
||||||
@@ -315,8 +343,8 @@ function renderConfigOverview(param) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 型号
|
// 型号
|
||||||
const devTypeMap = {0: '未知', 1: 'PD132', 2: 'DLD110'};
|
document.getElementById("cfg-dev-type").textContent =
|
||||||
document.getElementById("cfg-dev-type").textContent = devTypeMap[param.DevType] || `0x${(param.DevType||0).toString(16)}`;
|
devTypeNameCache[param.DevType] || `0x${(param.DevType || 0).toString(16)}`;
|
||||||
|
|
||||||
// 距离 (DB cm → 显示 mm)
|
// 距离 (DB cm → 显示 mm)
|
||||||
document.getElementById("cfg-reset-dis").textContent = param.RestDis != null ? param.RestDis * 10 : '-';
|
document.getElementById("cfg-reset-dis").textContent = param.RestDis != null ? param.RestDis * 10 : '-';
|
||||||
@@ -369,19 +397,50 @@ function toggleConfig() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function loadInitialData() {
|
async function loadInitialData() {
|
||||||
|
await loadDeviceTypeNames();
|
||||||
await loadTestMode();
|
await loadTestMode();
|
||||||
refreshDeviceStatus();
|
refreshDeviceStatus();
|
||||||
|
newB2Count = 0;
|
||||||
|
updateRecordCount();
|
||||||
try {
|
try {
|
||||||
const resp = await fetch(`/api/automation/${DNT_ID}/progress`);
|
const resp = await fetch(`/api/automation/${DNT_ID}/progress`);
|
||||||
const data = await resp.json();
|
const data = await resp.json();
|
||||||
if (data.latest) renderLatest(data.latest);
|
if (data.latest) { renderLatest(data.latest); lastLatestId = data.latest.id; }
|
||||||
if (data.latest_wave) renderLatestWave(data.latest_wave);
|
if (data.latest_wave) { renderLatestWave(data.latest_wave); lastWaveId = data.latest_wave.id; }
|
||||||
} 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() {
|
||||||
@@ -407,8 +466,45 @@ async function refreshDeviceStatus() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 每 5 秒刷新设备状态 + 测试模式(工装页修改后能及时同步)
|
// ─── 被动轮询:实时显示上报数据(工装本地按键 / 网页手动指令触发)───
|
||||||
|
|
||||||
|
let lastLatestId = 0; // 最新测试数据 ID,用于判断是否有新数据
|
||||||
|
let lastWaveId = 0; // 最新波动数据 ID
|
||||||
|
let newB2Count = 0; // 本轮新收到的 B2(灵敏度测试) 记录条数
|
||||||
|
|
||||||
|
function updateRecordCount() {
|
||||||
|
const el = document.getElementById("new-record-count");
|
||||||
|
if (el) el.textContent = newB2Count > 0 ? `(${newB2Count} 条新记录)` : "";
|
||||||
|
}
|
||||||
|
|
||||||
|
async function refreshLatestData() {
|
||||||
|
// 自动化运行中由 pollProgress 负责渲染,避免冲突
|
||||||
|
if (autoRunning) return;
|
||||||
|
try {
|
||||||
|
const resp = await fetch(`/api/automation/${DNT_ID}/progress`);
|
||||||
|
const data = await resp.json();
|
||||||
|
if (data.latest && data.latest.id !== lastLatestId) {
|
||||||
|
lastLatestId = data.latest.id;
|
||||||
|
renderLatest(data.latest);
|
||||||
|
// 仅 B2(灵敏度测试) 记录计数
|
||||||
|
if (data.latest.data_source === "B2") {
|
||||||
|
newB2Count++;
|
||||||
|
updateRecordCount();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (data.latest_wave && data.latest_wave.id !== lastWaveId) {
|
||||||
|
lastWaveId = data.latest_wave.id;
|
||||||
|
renderLatestWave(data.latest_wave);
|
||||||
|
}
|
||||||
|
} catch (e) { /* 静默失败 */ }
|
||||||
|
}
|
||||||
|
|
||||||
|
// 最新测试数据每 3 秒轮询
|
||||||
|
setInterval(refreshLatestData, 3000);
|
||||||
|
|
||||||
|
// 每 5 秒刷新设备状态 + 测试模式 + 型号名称缓存(工装页修改后能及时同步)
|
||||||
async function refreshAll() {
|
async function refreshAll() {
|
||||||
|
await loadDeviceTypeNames();
|
||||||
await loadTestMode();
|
await loadTestMode();
|
||||||
refreshDeviceStatus();
|
refreshDeviceStatus();
|
||||||
}
|
}
|
||||||
@@ -475,17 +571,23 @@ function fmtRelay(s) {
|
|||||||
|
|
||||||
function renderLatest(data) {
|
function renderLatest(data) {
|
||||||
const div = document.getElementById("latest-result");
|
const div = document.getElementById("latest-result");
|
||||||
|
// 优先使用 str_type,为空时从缓存查找
|
||||||
|
let typeName = data.str_type;
|
||||||
|
if (!typeName && data.sub_type != null) {
|
||||||
|
typeName = devTypeNameCache[data.sub_type] || `Unknown(${data.sub_type})`;
|
||||||
|
}
|
||||||
div.innerHTML = `
|
div.innerHTML = `
|
||||||
<p>设备型号:<strong>${data.str_type || '-'}</strong></p>
|
<p>车检器序列号:<strong>${data.detector_serial || '-'}</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>
|
||||||
<p>开始工作频率:${data.idle_freq || '-'} Hz</p>
|
<p>开始频率:${data.idle_freq || '-'} Hz</p>
|
||||||
<p>进入工作频率:${data.enter_freq || '-'} Hz</p>
|
<p>触发频率:${data.enter_freq || '-'} Hz</p>
|
||||||
<p>离开工作频率:${data.exit_freq || '-'} Hz</p>
|
<p>释放频率:${data.exit_freq || '-'} Hz</p>
|
||||||
<p>进入距离:${data.enter_dist || '-'} mm</p>
|
<p>触发距离:${data.enter_dist || '-'} mm</p>
|
||||||
<p>离开距离:${data.exit_dist || '-'} mm</p>
|
<p>释放距离:${data.exit_dist || '-'} mm</p>
|
||||||
<p>进入速度:${toSpeed(data.enter_speed)} m/s</p>
|
<p>触发速度:${toSpeed(data.enter_speed)} m/s</p>
|
||||||
<p>离开速度:${toSpeed(data.exit_speed)} m/s</p>
|
<p>释放速度:${toSpeed(data.exit_speed)} m/s</p>
|
||||||
<p>是否完成:${data.iffinish === '1' ? '是' : '否'}</p>
|
<p>是否完成:${data.iffinish === '1' ? '是' : '否'}</p>
|
||||||
<p>故障信息:${data.fault_info || '无'}</p>
|
<p>故障信息:${data.fault_info || '无'}</p>
|
||||||
<p>继电器:${fmtRelay(data.relay_out) || decodeRelay(data.relay_code)}</p>
|
<p>继电器:${fmtRelay(data.relay_out) || decodeRelay(data.relay_code)}</p>
|
||||||
@@ -527,8 +629,8 @@ function renderLatestWave(data) {
|
|||||||
<p>当前速度:${toSpeed(data.speed)} m/s</p>
|
<p>当前速度:${toSpeed(data.speed)} m/s</p>
|
||||||
<p>最近距离:${data.near_dist || '-'} mm</p>
|
<p>最近距离:${data.near_dist || '-'} mm</p>
|
||||||
<p>最远距离:${data.far_dist || '-'} mm</p>
|
<p>最远距离:${data.far_dist || '-'} mm</p>
|
||||||
<p>进入高度 (B4):${data.b4_enter_dist || '-'} mm</p>
|
<p>触发高度 (B4):${data.b4_enter_dist || '-'} mm</p>
|
||||||
<p>离开高度 (B4):${data.b4_leave_dist || '-'} mm</p>
|
<p>释放高度 (B4):${data.b4_leave_dist || '-'} mm</p>
|
||||||
<p>继电器:${fmtRelay(data.relay_out) || decodeRelay(data.relay_code)}</p>
|
<p>继电器:${fmtRelay(data.relay_out) || decodeRelay(data.relay_code)}</p>
|
||||||
<p>时间:${fmtTime(data.create_time)}</p>
|
<p>时间:${fmtTime(data.create_time)}</p>
|
||||||
`;
|
`;
|
||||||
|
|||||||
@@ -21,7 +21,11 @@
|
|||||||
<div class="search-bar">
|
<div class="search-bar">
|
||||||
<label>
|
<label>
|
||||||
设备编码:
|
设备编码:
|
||||||
<input type="text" id="search-serial" placeholder="输入设备编码搜索...">
|
<input type="text" id="search-serial" placeholder="设备编码...">
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
车检器序列号:
|
||||||
|
<input type="text" id="search-detector-serial" placeholder="车检器序列号...">
|
||||||
</label>
|
</label>
|
||||||
<label>
|
<label>
|
||||||
时间范围:
|
时间范围:
|
||||||
@@ -38,7 +42,7 @@
|
|||||||
<select id="per-page" onchange="searchData(1)" style="width:70px;">
|
<select id="per-page" onchange="searchData(1)" style="width:70px;">
|
||||||
<option value="20">20</option>
|
<option value="20">20</option>
|
||||||
<option value="50">50</option>
|
<option value="50">50</option>
|
||||||
<option value="100">100</option>
|
<option value="100" selected>100</option>
|
||||||
</select>
|
</select>
|
||||||
</label>
|
</label>
|
||||||
<button id="btn-chart" class="btn-chart" onclick="toggleChart()">📈 图表</button>
|
<button id="btn-chart" class="btn-chart" onclick="toggleChart()">📈 图表</button>
|
||||||
|
|||||||
@@ -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">
|
||||||
@@ -89,7 +94,7 @@
|
|||||||
|
|
||||||
<!-- 右侧:测试信息显示区 -->
|
<!-- 右侧:测试信息显示区 -->
|
||||||
<div class="test-info">
|
<div class="test-info">
|
||||||
<h3>当前测试数据</h3>
|
<h3>当前测试数据 <span id="new-record-count" style="font-size:12px;color:#888;font-weight:normal;margin-left:8px;"></span></h3>
|
||||||
<div id="latest-result">
|
<div id="latest-result">
|
||||||
<p class="placeholder">等待设备上报...</p>
|
<p class="placeholder">等待设备上报...</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -104,12 +109,12 @@
|
|||||||
<h3>自动化平均值</h3>
|
<h3>自动化平均值</h3>
|
||||||
<table id="avg-table">
|
<table id="avg-table">
|
||||||
<tr><td>平均峰峰值</td><td id="avg-ppvalue">-</td><td>V</td></tr>
|
<tr><td>平均峰峰值</td><td id="avg-ppvalue">-</td><td>V</td></tr>
|
||||||
<tr><td>平均开始工作频率</td><td id="avg-idle-freq">-</td><td>Hz</td></tr>
|
<tr><td>平均开始频率</td><td id="avg-idle-freq">-</td><td>Hz</td></tr>
|
||||||
<tr><td>平均进入工作频率</td><td id="avg-enter-freq">-</td><td>Hz</td></tr>
|
<tr><td>平均触发频率</td><td id="avg-enter-freq">-</td><td>Hz</td></tr>
|
||||||
<tr><td>平均进入距离</td><td id="avg-enter-dist">-</td><td>mm</td></tr>
|
<tr><td>平均触发距离</td><td id="avg-enter-dist">-</td><td>mm</td></tr>
|
||||||
<tr><td>平均离开距离</td><td id="avg-exit-dist">-</td><td>mm</td></tr>
|
<tr><td>平均释放距离</td><td id="avg-exit-dist">-</td><td>mm</td></tr>
|
||||||
<tr><td>平均进入速度</td><td id="avg-enter-speed">-</td><td>m/s</td></tr>
|
<tr><td>平均触发速度</td><td id="avg-enter-speed">-</td><td>m/s</td></tr>
|
||||||
<tr><td>平均离开速度</td><td id="avg-exit-speed">-</td><td>m/s</td></tr>
|
<tr><td>平均释放速度</td><td id="avg-exit-speed">-</td><td>m/s</td></tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<h3 style="margin-top:20px;">本轮测试明细</h3>
|
<h3 style="margin-top:20px;">本轮测试明细</h3>
|
||||||
@@ -117,7 +122,7 @@
|
|||||||
<table id="records-table" style="font-size:11px;">
|
<table id="records-table" style="font-size:11px;">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>#</th><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>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody></tbody>
|
<tbody></tbody>
|
||||||
|
|||||||
Submodule edc_server updated: 3580f89552...a2f31b3bfe
Reference in New Issue
Block a user