2. TestStand核心概念与执行模型

在动手搭建多工位自动测试系统之前,必须先厘清 TestStand 的五个核心对象以及它们之间的执行关系。很多初学者把 TestStand 当成“带界面的脚本编辑器”,结果在集成多工位时频繁遇到“为什么回调没触发”“为什么工位之间互相干扰”“为什么报告里步骤顺序不对”等问题。根源往往不是代码写错,而是对执行模型的理解有偏差。本节把 Sequence、Step、Sequence File、Process Model、执行入口点与回调机制串成一条完整的执行链来讲,帮助你在后续章节中做出正确的架构决策。

一、从最小执行单元到可复用容器

Step(步骤)是 TestStand 中最小的可执行单元。每个 Step 至少包含三部分信息:步骤类型(Step Type,决定它能做什么,例如 Numeric Limit Test、Pass/Fail Test、Sequence Call、Message Popup)、步骤属性(如限值、单位、超时、循环次数)以及该步骤在序列中的位置。Step 本身不保存“测试逻辑”,它保存的是“调用什么、用什么参数、如何判定结果”。真正的测试代码通常放在 LabVIEW、C#、Python 或 C 模块中,由 Step 通过适配器调用。

Sequence(序列)是 Step 的有序集合,是 TestStand 中“可被调用、可被复用”的基本单位。一个 Sequence 可以包含 Setup、Main、Cleanup 三个内置分组,也可以自定义分组。Sequence 支持传入参数(Parameters)和局部变量(Locals),这使得同一个序列可以被多个工位以不同参数调用。例如一个“测量电压”序列,接收通道号、量程、限值作为参数,就能被四个工位复用。

Sequence File(序列文件)是磁盘上的 .seq 文件,是 Sequence 的物理容器。一个 Sequence File 可以包含多个 Sequence,通常按功能组织:一个文件放公共工具序列,一个文件放单工位测试主流程,一个文件放校准序列。Sequence File 还承载 Station Globals、File Globals 以及 Process Model 回调的入口定义。多工位系统中,建议把“工位无关的公共序列”和“工位相关的测试序列”分文件管理,避免所有工位共用一个巨型文件导致版本冲突。

二、Process Model:TestStand 的执行骨架

Process Model 是 TestStand 区别于普通脚本引擎的关键。它定义了“一次测试执行”从开始到结束的完整生命周期,包括:初始化、加载序列文件、执行 Setup、执行 Main、执行 Cleanup、生成报告、记录数据库、处理错误、决定是否重测等。TestStand 自带 Sequential、Parallel、Batch 等模型,也允许你基于模型文件自定义。

Process Model 本身也是用 Sequence 写成的,它位于 <TestStand>\Components\Models\TestStandModels 目录下。理解这一点很重要:你可以在 Process Model 的各个入口点插入自己的逻辑,而不需要修改 TestStand 内核。多工位系统通常选择 Parallel 或 Batch 模型,因为需要多个执行线程同时运行各自的测试序列。

三、执行入口点与回调机制

执行入口点(Entry Point)是 Process Model 暴露给用户序列的“挂钩位置”。常见的入口点包括:ProcessSetup、PreUUT、PreUUTLoop、PostUUT、ProcessCleanup、TestReport 等。每个入口点对应 Process Model 中的一个 Sequence,TestStand 在生命周期的特定时刻自动调用它。

回调(Callback)是入口点的具体实现方式。你不需要修改 Process Model 文件,而是在自己的 Sequence File 中创建与入口点同名的 Sequence,TestStand 会在运行时优先调用你的实现。例如,在 Sequence File 中创建 PreUUT 序列,就能在每次 UUT 开始测试前执行自定义的工位初始化、扫码、夹具状态检查等操作。回调机制让“框架逻辑”和“业务逻辑”解耦,是多工位系统实现工位差异化行为的标准手段。

需要特别注意:回调的调用顺序由 Process Model 决定,而不是由 Sequence File 中的排列顺序决定。如果你在 PreUUT 中做了耗时操作,它会阻塞该执行线程进入 Main。多工位并行时,每个工位有独立的执行线程和独立的回调调用栈,但共享 Station Globals 和硬件资源,因此回调中访问共享资源必须加锁或做互斥设计。

TestStand 执行模型:从 Sequence File 到回调触发 Sequence File (.seq) Sequence: 工位1测试 Setup 分组 Main 分组(Step 序列) Cleanup 分组 回调序列(同名覆盖) PreUUT PostUUT TestReport 数据与参数 • Parameters(入参) • Locals(局部变量) • File Globals(文件全局) • Station Globals(站全局) Process Model(执行骨架) ProcessSetup 入口点 PreUUTLoop 入口点 PreUUT 入口点 执行 Main 序列 PostUUT 入口点 TestReport 入口点 ProcessCleanup 入口点 模型文件位置: Components\Models\ TestStandModels 执行线程(多工位并行) 工位1 线程 独立调用栈 独立 Locals 共享 Station Globals 工位2 线程 独立调用栈 独立 Locals 共享 Station Globals 工位3 线程 独立调用栈 独立 Locals 共享 Station Globals 共享资源竞争点 • 仪器句柄 • 数据库连接 • 报告文件 • 扫码枪 / 夹具 → 需加锁或互斥 加载 同名覆盖 调度 执行顺序由 Process Model 决定;回调在 Sequence File 中同名实现;多工位并行时每个工位拥有独立执行线程,但共享 Station Globals 与硬件资源

四、多工位场景下的关键设计约束

把上述概念放到多工位系统中,有三条约束必须遵守。第一,工位差异化逻辑放在回调中,而不是复制多份 Main 序列。例如四个工位的夹具初始化不同,可以在 PreUUT 中根据 RunState.TestSockets.MyIndex 或 Station Globals 中的工位号分支处理,Main 序列保持统一。第二,共享资源访问必须串行化。如果两个工位同时调用同一台电源的 VISA 会话,会出现超时或读数错乱。标准做法是在回调或 Step 中使用 TestStand 的 Lock 机制,或者把仪器访问封装成带队列的公共序列。第三,报告与数据库写入要区分工位标识。TestReport 回调中应把工位号写入 UUT 属性,否则报告无法区分是哪个工位测的。

回调序列的创建方式:在 Sequence File 的 Sequences 面板右键 → Insert Sequence → 从 Process Model 入口点列表中选择目标入口点,TestStand 会自动生成同名空序列并建立关联。不要手动改名,否则关联会断开。

五、一个可参考的工位回调配置示例

假设四工位系统使用 Parallel 模型,工位号存放在 StationGlobals.StationIndex(1~4)。下面给出 PreUUT 回调中判断夹具就绪并记录开始时间的配置思路,参数与判断条件均为示例,实际限值需按硬件手册确定。

PreUUT 回调序列(示例)
Step 1: Sequence Call → 公共序列 "CheckFixtureReady"
        参数: StationIndex = StationGlobals.StationIndex
        超时: 5000 ms
        判定: 返回布尔值 True 才继续

Step 2: Statement → 记录开始时间
        RunState.UUT.StartTime = Seconds()

Step 3: Numeric Limit Test → 读取夹具气压传感器
        通道: "AI0"
        下限: 0.45 MPa
        上限: 0.65 MPa
        单位: MPa
        失败动作: 终止当前 UUT 测试并记录错误码 E_FIXTURE_PRESSURE

执行时,TestStand 在进入 Main 之前自动调用该回调。如果 Step 1 返回 False 或 Step 3 超限,当前工位的 UUT 会被标记为失败,但其他工位线程不受影响,继续各自执行。这正是回调机制在多工位系统中的价值:故障隔离在工位级别,而不是系统级别。

六、常见理解误区

  • 把 Sequence File 当成“一个测试程序”:实际上一个 Sequence File 可以包含多个 Sequence,且可以被多个工位同时加载。文件是容器,不是执行实例。
  • 认为回调必须写在 Process Model 里:回调写在你的 Sequence File 中即可,Process Model 只负责在正确时机查找并调用同名序列。
  • 忽略 Setup 与 Cleanup 的执行时机:Setup 在 Main 之前执行,Cleanup 在 Main 之后执行,但两者都在 PreUUT/PostUUT 回调的范围内。如果 Cleanup 中释放了共享仪器,而其他工位还在使用,就会出错。
  • 在回调中做重活:回调应尽量轻量。把耗时初始化放到 ProcessSetup,把逐 UUT 的轻量检查放到 PreUUT,避免拖慢节拍。