Compare commits
4 Commits
b4c27e30c8
...
8148aef332
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8148aef332 | ||
|
|
87752f12e6 | ||
|
|
aadd498851 | ||
|
|
86c6046fbc |
108
docs/reports/weekly-2026-06-05.md
Normal file
108
docs/reports/weekly-2026-06-05.md
Normal file
@@ -0,0 +1,108 @@
|
||||
# V2.0.4 周报 — 车检器测试工装项目
|
||||
|
||||
**报告周期**:2026年6月1日 ~ 6月5日
|
||||
**项目**:vd_test_fixture(车检器自动化测试工装)
|
||||
**作者**:wangfq
|
||||
|
||||
---
|
||||
|
||||
## 一、概述
|
||||
|
||||
本周完成 V2.0.3 波动测试模式全链路实现、继电器状态存储重构、测试操作/信息页面多项增强,以及若干关键 Bug 修复。共提交 **24 个 commits**(主仓库 19 + edc_server 子模块 4 + edc-web 1)。
|
||||
|
||||
---
|
||||
|
||||
## 二、波动测试模式(V2.0.3)
|
||||
|
||||
### 2.1 协议文档
|
||||
- DG430 串口协议升级至 V2.0.3
|
||||
- 扩展 0x4B/0x4C 字段:新增 FarTol、NearTol、StepTol、BackForth、NearStay、FarStay(6 个波动参数)
|
||||
- 新增 0xB4 波动测试上报指令定义
|
||||
- 第 6 章拆分为 6.1 灵敏度测试流程 + 6.2~6.4 波动测试流程
|
||||
|
||||
### 2.2 后端 edc_server
|
||||
| 模块 | 变更 |
|
||||
|------|------|
|
||||
| `dg430.py` | 新增 `DG430WaveStatus` dataclass、`parse_b4_wave_status()` 解析器;扩展 `DG430FixtureParams` + `parse_4c_params()` 支持 6 个新字段 |
|
||||
| `models.py` | DDL 增加 10 个新列(test_mode、data_source、remain_count、work_freq、b4_enter_dist 等);新增 `insert_wave_data()` 存库函数;ALTER TABLE 自动迁移逻辑 |
|
||||
| `handlers.py` | parse_loop 增加 0xB4 分支,波动数据路由入库;0x4C 处理传递新参数字段 |
|
||||
|
||||
### 2.3 前端 edc-web
|
||||
- **工装参数页**:表单增加 6 个波动参数输入框,JS 适配新字段
|
||||
- **测试操作页**:右侧新增「波动测试数据」显示区,实时展示 B4 上报数据
|
||||
- **测试信息页**:拆为三视图标签页(全部/灵敏度测试 B2/波动测试 B4),按 `data_source` 自动切换列布局
|
||||
- 波动测试数据支持 Excel 导出
|
||||
|
||||
---
|
||||
|
||||
## 三、继电器输出状态重构
|
||||
|
||||
### 3.1 存储层
|
||||
- `tb_state_tst` 新增 `relay_code TINYINT` 列,存储原始 hex 值
|
||||
- `0x00` = 无输出,`0x01` = 存在信号,`0x02` = 脉冲信号,`0x03` = 存在+脉冲
|
||||
- `relay_out` VARCHAR 列保留,兼容历史数据
|
||||
- B2/B4 解析后直接以 int 值写入 `relay_code`
|
||||
|
||||
### 3.2 前端
|
||||
- 新增 `decodeRelay()` 函数:整数 → 可读文本
|
||||
- 测试操作页、测试信息页(表格视图)统一使用解码显示
|
||||
- **图表视图**:新增第 4 Y 轴(继电器输出),红色三角阶梯线,tooltip 自动解码
|
||||
|
||||
---
|
||||
|
||||
## 四、测试操作页面增强
|
||||
|
||||
| 功能 | 说明 |
|
||||
|------|------|
|
||||
| 自动化间隔/超时 | 新增间隔时间(秒)和超时时间(秒)参数;重写为状态机驱动,收到回复后等待间隔再发下一条,超时立即发下一条 |
|
||||
| 渲染容错 | 每个 render 调用独立 try-catch,避免一处报错级联导致全部数据显示失败 |
|
||||
| 初始数据加载 | 页面打开时自动请求最新测试数据,无需等待启动自动化 |
|
||||
| 时间格式统一 | 所有区域统一显示 `yyyy-MM-dd HH:mm:ss`,修复 Flask jsonify "GMT" 导致的 UTC+8 时区偏移 |
|
||||
|
||||
---
|
||||
|
||||
## 五、测试信息页面增强
|
||||
|
||||
| 功能 | 说明 |
|
||||
|------|------|
|
||||
| 三视图标签页 | 全部 / 灵敏度测试 (B2) / 波动测试 (B4),独立列布局 |
|
||||
| ECharts 图表 | 表格/图表一键切换,B2 显示 8 条线(峰峰值/频率/距离/速度),B4 显示 7+1 条线(含继电器),三 Y 轴,dataZoom 缩放,保存为 2x PNG |
|
||||
| 分页条数 | 搜索栏增加「每页:20/50/100」下拉框 |
|
||||
| Admin 删除 | 仅 admin 可见,按设备编码/日期范围/数据来源筛选删除,确认框防误删,`tb_log` 留痕 |
|
||||
|
||||
---
|
||||
|
||||
## 六、Bug 修复
|
||||
|
||||
| 问题 | 根因 | 修复 |
|
||||
|------|------|------|
|
||||
| FarStay 字段长度 | 用户纠正:NearStay 和 FarStay 均为 2 字节 | 全量回退 1 字节改动,恢复协议+代码 2 字节设计 |
|
||||
| 数据库缺列 | 旧表缺少 V2.0.3 新增字段 | 服务启动时 ALTER TABLE ADD COLUMN IF NOT EXISTS 自动迁移 |
|
||||
| 时间显示偏移 8 小时 | Flask jsonify 给本地时间加 "GMT" 后缀,JS 误当 UTC 解析 | `fmtTime()` 先 strip "GMT" 再解析 |
|
||||
| 测试操作三个数据区不显示 | pollProgress 共用一个 try-catch,一处报错跳过后续渲染;全部完成时 return 跳过最终渲染 | 独立 try-catch;渲染代码移到 return 之前 |
|
||||
| 自动化平均值/明细不显示 | 同上(渲染代码在 return 之后) | 同上 |
|
||||
|
||||
---
|
||||
|
||||
## 七、Git 提交统计
|
||||
|
||||
**主仓库**(vd_test_fixture):19 commits
|
||||
**子模块**(edc_server):4 commits
|
||||
**合计**:23 commits
|
||||
|
||||
| 日期 | 主题 |
|
||||
|------|------|
|
||||
| 6/1 | 培训手册 V1.0、精简 requirements.txt |
|
||||
| 6/2 | DG430 V2.0.3 协议文档、后端实现、前端同步、ALTER TABLE 迁移 |
|
||||
| 6/3 | FarStay 字节修正、波动测试前端适配、三视图重构 |
|
||||
| 6/4 | 自动化间隔/超时、协议文档补充灵敏度流程 |
|
||||
| 6/5 | 时间格式化、分页条数、ECharts 图表、图表保存图片、admin 删除、继电器重构、时区修复、渲染容错、图表继电器系列 |
|
||||
|
||||
---
|
||||
|
||||
## 八、待办事项
|
||||
|
||||
- [ ] 重启 edc_server 使数据库迁移和新字段生效
|
||||
- [ ] edc-web 重启(用户已手动停止)
|
||||
- [ ] 端到端测试完整波动测试流程(参数设置 → 查询 → 执行 → B4 上报 → 前端展示)
|
||||
- [ ] 验证图表功能在不同数据量下的表现
|
||||
@@ -81,7 +81,10 @@ function toSpeed(v) {
|
||||
|
||||
function fmtTime(v) {
|
||||
if (!v) return '-';
|
||||
const d = new Date(v);
|
||||
// Flask jsonify 给 MySQL DATETIME 加 "GMT" 后缀,但实际值是服务器本地时间(UTC+8)
|
||||
// 去掉 "GMT" 让 JS 按本地时间解析,避免时区偏移 8 小时
|
||||
const cleaned = String(v).replace(/ GMT$/, '');
|
||||
const d = new Date(cleaned);
|
||||
if (isNaN(d.getTime())) return String(v).substring(0, 19);
|
||||
const y = d.getFullYear();
|
||||
const m = String(d.getMonth() + 1).padStart(2, '0');
|
||||
@@ -238,6 +241,27 @@ const CHART_SERIES = {
|
||||
],
|
||||
};
|
||||
|
||||
// 继电器系列(添加到任意视图的图表末尾)
|
||||
function buildRelaySeries(records) {
|
||||
return {
|
||||
name: '继电器输出',
|
||||
type: 'line',
|
||||
step: 'end',
|
||||
yAxisIndex: 3,
|
||||
symbol: 'triangle',
|
||||
symbolSize: 8,
|
||||
lineStyle: { type: 'dotted', width: 2, color: '#e74c3c' },
|
||||
itemStyle: { color: '#e74c3c' },
|
||||
data: records.map(r => r.relay_code ?? null),
|
||||
// tooltip 中显示解码后的文本
|
||||
tooltip: {
|
||||
valueFormatter: function (value) {
|
||||
return RELAY_MAP[value] || `未知(${value})`;
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function toggleChart() {
|
||||
const container = document.getElementById('chart-container');
|
||||
const btn = document.getElementById('btn-chart');
|
||||
@@ -314,6 +338,9 @@ async function loadChart() {
|
||||
connectNulls: false,
|
||||
}));
|
||||
|
||||
// 添加继电器状态系列
|
||||
series.push(buildRelaySeries(records));
|
||||
|
||||
// 渲染 ECharts
|
||||
if (chartInstance) chartInstance.dispose();
|
||||
chartInstance = echarts.init(container);
|
||||
@@ -341,7 +368,7 @@ async function loadChart() {
|
||||
},
|
||||
},
|
||||
},
|
||||
grid: { left: 60, right: 140, top: 60, bottom: 80 },
|
||||
grid: { left: 60, right: 200, top: 60, bottom: 80 },
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: times,
|
||||
@@ -355,6 +382,15 @@ async function loadChart() {
|
||||
{ type: 'value', name: '距离(mm)', nameTextStyle: { fontSize: 11 } },
|
||||
{ type: 'value', name: '速度(dm/s)',nameTextStyle: { fontSize: 11 },
|
||||
offset: 80 },
|
||||
{ type: 'value', name: '继电器输出', nameTextStyle: { fontSize: 11 },
|
||||
min: -0.5, max: 3.5, interval: 1,
|
||||
offset: 160,
|
||||
axisLabel: {
|
||||
formatter: function (v) {
|
||||
return RELAY_MAP[v] || '';
|
||||
},
|
||||
fontSize: 10,
|
||||
}},
|
||||
],
|
||||
dataZoom: [
|
||||
{ type: 'slider', start: 0, end: 100, height: 20, bottom: 30 },
|
||||
|
||||
@@ -200,6 +200,12 @@ async function pollProgress() {
|
||||
const data = await resp.json();
|
||||
const stats = data.stats;
|
||||
|
||||
// ── 先渲染数据(放在所有 return 之前,避免完成时跳过渲染)──
|
||||
try { if (data.latest) 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.latest_wave) renderLatestWave(data.latest_wave); } catch (e) { console.error("renderLatestWave:", e); }
|
||||
try { if (data.records && data.records.length) renderRecords(data.records); } catch (e) { console.error("renderRecords:", e); }
|
||||
|
||||
// 更新计数
|
||||
const newDone = stats.done || 0;
|
||||
const newFailed = stats.failed || 0;
|
||||
@@ -244,17 +250,24 @@ async function pollProgress() {
|
||||
}
|
||||
}
|
||||
|
||||
// 显示最新结果
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
// ─── 页面加载时获取初始数据 ──────────────────────
|
||||
async function loadInitialData() {
|
||||
try {
|
||||
const resp = await fetch(`/api/automation/${DNT_ID}/progress`);
|
||||
const data = await resp.json();
|
||||
if (data.latest) renderLatest(data.latest);
|
||||
if (data.latest_wave) renderLatestWave(data.latest_wave);
|
||||
} catch (e) {
|
||||
// 初始加载静默失败
|
||||
}
|
||||
}
|
||||
loadInitialData();
|
||||
|
||||
// ─── UI ────────────────────────────────────────
|
||||
|
||||
function setStatus(msg) {
|
||||
@@ -280,7 +293,10 @@ function toSpeed(v) {
|
||||
|
||||
function fmtTime(v) {
|
||||
if (!v) return '-';
|
||||
const d = new Date(v);
|
||||
// Flask jsonify 给 MySQL DATETIME 加 "GMT" 后缀,但实际值是服务器本地时间(UTC+8)
|
||||
// 去掉 "GMT" 让 JS 按本地时间解析,避免时区偏移 8 小时
|
||||
const cleaned = String(v).replace(/ GMT$/, '');
|
||||
const d = new Date(cleaned);
|
||||
if (isNaN(d.getTime())) return String(v).substring(0, 19);
|
||||
const y = d.getFullYear();
|
||||
const m = String(d.getMonth() + 1).padStart(2, '0');
|
||||
|
||||
Reference in New Issue
Block a user