4、测试系统软件架构:MES对接、测试执行框架、数据库设计、日志系统
好,咱们接着聊。前面几章我们把硬件平台、射频链路、自动化夹具都讲透了。这一章,我打算聊聊软件层面的事。
很多人觉得,CPE产测嘛,硬件调通了就完事了。其实不然。我见过太多项目,硬件指标测出来漂漂亮亮,一到量产就崩盘——为什么?软件架构没撑住。说白了,产测软件就是整条产线的大脑和神经系统。它要管指令下发、数据回传、跟MES握手、把结果存进数据库,还得把每台设备的“病历”记清楚。
这一章,我就把这几块掰开揉碎了讲。你想想看,如果连软件架构都搭不牢,产线跑起来就是一团乱麻。
4.1 MES对接:产线的“中央情报局”
MES,制造执行系统。它不直接测产品,但它管着所有产品的“身份”和“命运”。
我个人习惯,把MES对接分成三层:
- 工单层:MES下发工单,告诉产线“今天要测哪批货、测什么项目”。
- 设备层:测试工位向MES上报“我在测哪台设备、当前状态如何”。
- 结果层:每台设备测完,把PASS/FAIL、关键指标、不良代码一股脑推给MES。
我在项目中遇到过一种情况:MES接口文档写得像天书,字段定义模棱两可。结果产线跑了两小时,发现MES那边根本没收到数据。排查了一整天,最后发现是报文里的时间戳格式没对齐——他们用yyyy-MM-dd HH:mm:ss,我们用了yyyy/MM/dd HH:mm:ss。就一个斜杠的区别,整条线停摆。
这里给出一段典型的MES上报接口示例(HTTP JSON):
POST /api/mes/testResult
Content-Type: application/json
{
"workOrder": "WO20240521001",
"sn": "CPE2405210001",
"stationId": "STN_05",
"testResult": "PASS",
"testTime": "2024-05-21 14:30:25",
"items": [
{"name": "WIFI_2G_TX_POWER", "value": "18.5", "unit": "dBm", "limit": "16.0~20.0", "status": "PASS"},
{"name": "LTE_RSRP", "value": "-85", "unit": "dBm", "limit": "-110~-70", "status": "PASS"}
],
"failCode": ""
}
注意看failCode字段。我建议你设计成:PASS时传空字符串,FAIL时传一个预定义的错误码。比如E001代表“Wi-Fi功率超标”,E002代表“LTE灵敏度不足”。这样MES那边做统计分析时,一眼就能看出产线的瓶颈在哪。
4.2 测试执行框架:别让代码变成意大利面
测试执行框架,说白了就是“怎么把测试流程跑起来”。
我见过最糟糕的做法:把所有测试步骤写在一个巨大的main()函数里,几百行代码,if-else嵌套七八层。一旦要加一个新测试项,就得改核心逻辑。这种代码,我称之为“产线定时炸弹”。
我个人推荐用插件化+状态机的架构。每个测试项是一个独立的插件,框架只负责调度和状态流转。
举个例子,一个典型的CPE产测流程包含:
- 上电自检(Power On Self Test)
- 读取SN/IMEI/MAC
- Wi-Fi 2.4G/5G 射频测试
- LTE 4G/5G 射频测试
- 以太网吞吐测试
- 整机功耗测试
- 结果上传MES
每个步骤都是一个独立的类,继承自同一个基类:
class TestPlugin:
def __init__(self, name):
self.name = name
self.status = "IDLE"
def execute(self, context):
# context 是全局上下文,存放设备信息、配置参数
raise NotImplementedError
def report(self):
# 返回测试结果字典
pass
框架里维护一个状态机,每个插件执行完后,根据结果决定下一步:
- PASS → 进入下一个插件
- FAIL → 记录不良代码,进入“重测”或“报废”分支
- ERROR(设备断连、仪器超时)→ 进入“异常处理”分支
为什么要用状态机?因为产线不是实验室。实验室里你可以慢慢调,产线上每秒钟都在烧钱。状态机能让你清晰地定义“异常时怎么办”——是重试3次?还是直接跳过?还是整条线急停?
4.3 数据库设计:别把数据存成“一锅粥”
产测数据,每天少则几千条,多则几十万条。怎么存?
我建议用关系型数据库(比如MySQL/PostgreSQL)来存结构化数据,用时序数据库(比如InfluxDB)来存波形、频谱这类连续数据。但考虑到产线环境往往比较简陋,很多工厂连DBA都没有,我一般推荐直接用SQLite或MySQL搞定一切。
核心表设计,我习惯分成三张:
| 表名 | 用途 | 关键字段 |
|---|---|---|
test_records |
每台设备的主记录 | id, sn, work_order, station_id, test_time, result, fail_code |
test_items |
每台设备下各测试项的明细 | id, record_id, item_name, value, unit, limit_low, limit_high, status |
test_logs |
详细的日志信息 | id, record_id, log_level, message, timestamp |
为什么要分开?因为查询场景不同。MES那边只关心“这台设备PASS了没有”,查test_records就够了。产线工程师要分析“为什么这批设备Wi-Fi功率偏低”,那就得查test_items,按item_name分组看趋势。
嗯,这里要注意一个细节:索引设计。我见过有人把test_items表建了上百万条数据,查询一次要十几秒。为什么?因为没有给record_id和item_name建联合索引。加上索引后,查询时间降到几十毫秒。
CREATE INDEX idx_record_item ON test_items(record_id, item_name);
就这么一行,效果天差地别。
4.4 日志系统:产线的“黑匣子”
日志这东西,平时没人看,一出问题就抓瞎。我自己的经验是:日志不是写给机器看的,是写给3个月后的自己看的。
产测日志,我建议分三个级别:
- INFO:正常流程记录。比如“开始测试Wi-Fi 2.4G TX Power”、“仪器已复位”。
- WARNING:非致命异常。比如“设备响应超时,重试第1次”、“信号强度接近下限阈值”。
- ERROR:致命错误。比如“仪器连接断开”、“数据库写入失败”。
我曾经遇到过一个案例:产线半夜突然大批量FAIL,白班工程师来了查了半天,发现日志里只有“PASS”和“FAIL”,没有任何上下文。根本不知道是设备问题、仪器问题还是软件问题。后来我强制要求:每个测试项开始和结束时,必须打印一条带时间戳的日志。这样一旦出问题,可以精确到毫秒级定位。
日志格式,我推荐用结构化的方式,方便后续用工具分析:
2024-05-21 14:30:25.123 | INFO | STN_05 | SN:CPE2405210001 | 开始测试: WIFI_2G_TX_POWER
2024-05-21 14:30:25.456 | INFO | STN_05 | SN:CPE2405210001 | 仪器: 频谱仪 N9020A, 频率: 2412MHz
2024-05-21 14:30:26.789 | INFO | STN_05 | SN:CPE2405210001 | 测量值: 18.5 dBm, 限值: 16.0~20.0 dBm
2024-05-21 14:30:26.790 | INFO | STN_05 | SN:CPE2405210001 | 结果: PASS
你看,每一行都包含了时间、工位、设备SN、操作内容。就算产线同时跑20个工位,也能轻松追踪到某一台设备的所有操作。
最后提一句日志的存储策略。产线一天可能产生几个GB的日志,不能无限存。我一般建议:按天分文件,保留30天。超过30天的自动压缩归档,保留90天。再老的,如果没出问题,就可以删了。但注意,test_records和test_items表里的数据要永久保留——那是产品的“出生证明”。
好了,软件架构这块就聊到这儿。下一章我们讲讲产线搭建的实战细节,包括工位布局、节拍计算、人员培训这些接地气的东西。到时候见。