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就行,产线技术员都能操作。

引擎核心逻辑:

  1. 加载配置文件,解析测试序列
  2. 遍历每个测试项,调用对应的插件
  3. 每个测试项返回 Pass/Fail + 数据
  4. 根据结果决定是否继续、重测、跳转
  5. 记录所有测试数据到日志

我遇到过最头疼的情况是:产线上某个PDA屏幕校准总失败,但单独测又没问题。后来发现是序列引擎里没有加延时,上一个测试刚结束,下一个测试立刻开始,屏幕还没准备好。加个200ms的间隔,问题解决。所以引擎里一定要支持测试间延时重试次数配置。

3.4 日志系统搭建

日志系统,很多人不重视。觉得“能跑就行”。但量产出了问题,没有日志,你连问题在哪都不知道。

我要求日志系统必须做到:

  • 分级记录:DEBUG、INFO、WARN、ERROR、FATAL
  • 自动滚动:单个日志文件不超过10MB,自动切分
  • 关键数据提取:测试结果、校准参数单独存一份CSV
  • 时间戳精确到毫秒:方便定位时序问题

C#里我常用NLogSerilog,配置简单,功能强大。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
  • 通信封装:接口抽象,粘包处理,校验必做
  • 序列引擎:配置文件驱动,插件化,支持条件跳转
  • 日志系统:分级记录,自动滚动,关键数据单独存

下一章咱们聊硬件测试治具设计,包括探针选型、夹具结构、信号完整性这些实战内容。到时候见。