3、测试上位机软件架构:C#/Python测试框架选型、串口/USB通信协议封装、测试序列引擎设计、日志系统搭建
好,咱们进入第三章。这一章聊的是上位机软件,说白了就是PC端那个控制测试、收数据的“大脑”。很多做硬件的兄弟觉得软件是别人的事,自己只管电路和信号。我早年也这么想,结果被坑过几次——硬件调通了,上位机一塌糊涂,测试效率低得想砸键盘。后来我明白了,上位机架构设计得好,量产测试才能跑得顺。
这一章我重点讲四个东西:测试框架选型、通信协议封装、测试序列引擎、日志系统。这四个是骨架,搭好了,后面写代码就是填肉。
3.1 C# vs Python:测试框架怎么选?
先解决第一个问题:用C#还是Python?
我个人习惯是:看场景。没有银弹,只有合适。
| 对比维度 | C# (WinForms/WPF) | Python (PyQt/PySide) |
|---|---|---|
| 开发速度 | 中等,强类型编译 | 快,动态语言 |
| UI表现力 | WPF非常强,适合复杂界面 | PyQt够用,但复杂动画吃力 |
| 串口/USB库 | System.IO.Ports,成熟稳定 | pySerial,简单好用 |
| 部署难度 | 需要.NET运行时,但打包方便 | 需要Python环境,打包略麻烦 |
| 多线程 | 原生支持,Task/async await | GIL限制,需用多进程 |
| 适合场景 | 产线长期稳定运行 | 原型验证、小批量 |
我自己的经验是:量产线用C#,研发验证用Python。为什么?
产线环境复杂,PC可能三天不关机,C#的稳定性确实更靠谱。我在一个项目里用过Python做产线测试,跑了两个月,偶尔会内存泄漏,排查起来很头疼。后来换成C# WPF,稳如老狗。
但研发阶段,Python的灵活性太香了。改个协议、加个测试项,改两行代码就能跑。C#改完还得编译,产线停着等你?不现实。
我的建议:
- 小批量(<1000台/天):Python + PyQt,快速出活
- 大批量(>5000台/天):C# + WPF,稳定第一
- 混合方案:Python做脚本层,C#做框架层,通过IPC通信
3.2 串口/USB通信协议封装
通信协议封装,这是最容易出bug的地方。我见过太多人直接在界面里写串口收发代码,结果一跑多线程就乱套。
正确的做法是:封装一个独立的通信层,上层调用只管发命令、收数据,不用管底层是串口还是USB。
我习惯这样设计接口:
// C# 示例:通信接口抽象
public interface IDeviceCommunication
{
bool Connect(string portName, int baudRate);
void Disconnect();
byte[] SendCommand(byte[] command, int timeoutMs);
event EventHandler<byte[]> DataReceived;
}
// 串口实现
public class SerialCommunication : IDeviceCommunication { ... }
// USB HID实现
public class UsbHidCommunication : IDeviceCommunication { ... }
为什么要这样?
有一次我调试一个PDA,前期用串口开发,一切正常。量产时客户突然说要用USB。如果通信层没封装好,那得改所有调用代码,想想就崩溃。但有了接口抽象,我只需要写个UsbHidCommunication类,替换一下实例化代码就行。
避坑指南:
我曾经在串口接收时直接用ReadLine(),结果产线上偶尔丢包。后来改成ReadExisting() + 超时重试机制,问题解决。记住:串口通信永远不要假设数据一次收完,一定要做粘包处理。
协议格式我推荐用帧头+长度+数据+校验的结构:
帧头(2字节) | 长度(1字节) | 命令ID(1字节) | 数据(N字节) | 校验(1字节)
0xAA 0x55 | 0x0A | 0x01 | ... | XOR校验
嗯,这里要注意:校验一定要做。我见过有人图省事不加校验,结果产线上偶尔出现误码,校准数据写错,整批PDA返工。一个校验字节,省掉多少麻烦。
3.3 测试序列引擎设计
测试序列引擎,说白了就是怎么组织测试流程。是顺序执行?还是条件跳转?要不要支持重测?
我设计过几版,最后沉淀下来一个比较通用的模式:基于配置文件的测试序列。
核心思路:
- 每个测试项是一个插件,实现统一接口
- 测试序列写在JSON或XML里,动态加载
- 支持条件判断、循环、跳转
举个例子,测试序列配置文件长这样:
{
"sequence": [
{ "name": "开机检测", "type": "PowerOnTest", "params": {"voltage": 3.3} },
{ "name": "屏幕校准", "type": "DisplayCalibration", "params": {"points": 5} },
{ "name": "按键测试", "type": "KeyTest", "params": {"keys": ["PWR", "VOL+", "VOL-"]} },
{ "name": "WIFI测试", "type": "WifiTest", "params": {"ssid": "test_ap", "timeout": 10} },
{ "name": "结果判定", "type": "ResultJudge", "params": {"pass_rate": 0.95} }
]
}
为什么要用配置文件?
你想想看,产线上的测试项经常要调整。今天加个蓝牙测试,明天去掉个GPS测试。如果写死在代码里,每次都得重新编译、部署。用配置文件,改个JSON就行,产线技术员都能操作。
引擎核心逻辑:
- 加载配置文件,解析测试序列
- 遍历每个测试项,调用对应的插件
- 每个测试项返回 Pass/Fail + 数据
- 根据结果决定是否继续、重测、跳转
- 记录所有测试数据到日志
我遇到过最头疼的情况是:产线上某个PDA屏幕校准总失败,但单独测又没问题。后来发现是序列引擎里没有加延时,上一个测试刚结束,下一个测试立刻开始,屏幕还没准备好。加个200ms的间隔,问题解决。所以引擎里一定要支持测试间延时和重试次数配置。
3.4 日志系统搭建
日志系统,很多人不重视。觉得“能跑就行”。但量产出了问题,没有日志,你连问题在哪都不知道。
我要求日志系统必须做到:
- 分级记录:DEBUG、INFO、WARN、ERROR、FATAL
- 自动滚动:单个日志文件不超过10MB,自动切分
- 关键数据提取:测试结果、校准参数单独存一份CSV
- 时间戳精确到毫秒:方便定位时序问题
C#里我常用NLog或Serilog,配置简单,功能强大。Python里用logging模块就够了,配合RotatingFileHandler实现日志滚动。
// C# NLog 配置示例
<targets>
<target name="file" xsi:type="File"
fileName="${basedir}/logs/test-${shortdate}.log"
layout="${longdate} | ${level:uppercase=true} | ${message}"
archiveFileName="${basedir}/logs/archive/test-${shortdate}.{##}.log"
archiveAboveSize="10485760"
maxArchiveFiles="30" />
</targets>
重要提醒:
日志里不要只写Pass/Fail。我曾经吃过这个亏:产线报告一批PDA校准失败,但日志里只有“Calibration: Fail”。具体哪个参数超差?不知道。后来我强制要求:每个测试项必须记录实测值、上下限、判定结果。这样出了问题,一看日志就知道是哪个参数偏了。
另外,我习惯在日志系统里加一个实时看板功能。产线技术员不用翻日志文件,直接在软件界面上看到:当前测试进度、通过率、失败项分布。这个对产线管理太重要了。
嗯,这一章内容不少。总结一下:
- 框架选型:量产用C#,研发用Python
- 通信封装:接口抽象,粘包处理,校验必做
- 序列引擎:配置文件驱动,插件化,支持条件跳转
- 日志系统:分级记录,自动滚动,关键数据单独存
下一章咱们聊硬件测试治具设计,包括探针选型、夹具结构、信号完整性这些实战内容。到时候见。