2. TestStand架构与Process Model执行机制

要真正掌握 Process Model 定制,必须先弄清楚 TestStand 在运行时到底由哪些部分协同工作,以及 Process Model 在测试执行生命周期的哪个时刻被调用、以什么身份被调用。很多工程师第一次改模型时,习惯直接打开 Model 文件改几步,结果发现"改了没生效"或"顺序不对",根源往往不是代码写错,而是没有建立引擎、序列编辑器、用户界面与 Process Model 四者之间的交互图景。

一、四个核心角色及其职责边界

TestStand 的运行时结构可以拆成四个角色:TestStand 引擎(Engine)、序列编辑器(Sequence Editor)、用户界面(User Interface,如默认的 Full-Featured UI),以及Process Model 及其关联的模型回调。

引擎是真正的执行内核,它负责加载序列文件、解析步骤、管理执行线程、维护运行时的变量与结果容器,并对外提供 ActiveX/COM 或 .NET API。序列编辑器本质上是"引擎 + 编辑界面"的组合,它既能编辑序列,也能直接运行调试。用户界面则是"引擎 + 操作界面"的组合,面向产线操作员,通常只暴露登录、开始测试、显示结果等有限入口。Process Model 不是独立进程,它是一组被引擎按约定调用的序列集合,通过回调(Callback)机制嵌入到执行流程中。

理解这一点很关键:Process Model 是"被引擎调用的序列",而不是"调用引擎的程序"。方向搞反,定制思路就会全盘错位。

二、执行生命周期与模型调用时机

当操作员在 UI 上点击"Start"后,实际发生的是:UI 通过引擎 API 启动一个执行(Execution),引擎加载入口序列文件(通常是 TestStation 或类似入口),然后按 Process Model 定义的回调顺序依次调用模型序列。整个生命周期大致经过:预加载(PreUUT / PreUUTLoop)→ 单件测试循环(Test UUTs)→ 单件收尾(PostUUT)→ 批次收尾(PostUUTLoop / ProcessCleanup)。

其中几个回调的调用时机最容易混淆,需要特别记住:

  • ProcessSetup:整个执行开始时调用一次,适合做仪器初始化、数据库连接、全局资源分配。
  • PreUUT:每颗 UUT 测试前调用,适合扫码、读取工单、设置 UUT 级变量。
  • MainSequence:这是模型真正"让位"给测试序列的地方,引擎在此调用被测产品的 MainSequence。
  • PostUUT:每颗 UUT 测试后调用,适合写数据库、判定 Pass/Fail、清理 UUT 级资源。
  • ProcessCleanup:整个执行结束时调用一次,用于释放仪器、关闭连接。

注意 MainSequence 并不是模型的一部分,而是模型通过回调"插入"的测试主体。模型负责的是它前后的框架逻辑,这正是 Process Model 定制的核心价值所在。

TestStand 执行生命周期与 Process Model 回调调用时机 TestStand 引擎(Engine)— 加载序列、调度线程、维护运行时变量与结果容器 UI 通过引擎 API 启动 Execution;编辑器通过引擎运行调试 用户界面 UI 操作员入口:登录 / 开始 / 结果 序列编辑器 工程师入口:编辑 / 调试 / 断点 Process Model(被引擎按回调顺序调用的序列集合) ProcessSetup 执行开始 · 一次 PreUUT 每颗 UUT · 前 MainSequence 测试主体 · 模型让位 PostUUT 每颗 UUT · 后 PreUUT → MainSequence → PostUUT 按 UUT 数量循环 ProcessCleanup 执行结束 · 一次 定制切入点 · 改回调内容 · 增删回调步骤 · 调整调用顺序 引擎职责 · 解析序列文件 · 调度回调顺序 · 维护结果容器

三、模型如何"接管"执行流程

Process Model 的接管方式,是通过在模型序列中调用引擎提供的回调入口。以 TestStand 自带的 Sequential Model 为例,其 Test UUTs 入口序列会依次调用 ModelPlugin 和若干回调。工程师定制时,通常不是重写整个模型,而是在特定回调中插入自己的步骤。

举个具体例子:假设产线要求每颗 UUT 测试前必须校验工单号是否与 MES 匹配,不匹配则直接判 Fail 并跳过测试。实现方式是在 PreUUT 回调中插入一个 Sequence Call 或 Statement 步骤,调用 MES 查询接口,将返回值写入 RunState.Root.Locals.UUT.WorkOrderValid,然后在 MainSequence 前加一个 If 判断。如果为 False,则设置 RunState.PreviousStep.Result.Status = "Failed" 并跳转到 PostUUT。这样模型就实现了"拦截"逻辑,而测试序列本身无需改动。

这里的关键参数是 RunState 对象。它是引擎在每次执行时创建的运行时容器,模型和测试序列都通过它读写共享数据。常见的路径包括:

  • RunState.Root.Locals:执行级局部变量,ProcessSetup 中初始化。
  • RunState.Root.Locals.UUT:UUT 级变量,PreUUT 中赋值,PostUUT 中读取。
  • RunState.Sequence:当前序列上下文。
  • RunState.TestSockets:多工位并行时的工位信息。

理解 RunState 的生命周期,是判断"变量该在哪一层定义"的依据。执行级变量在 ProcessSetup 中创建,UUT 级变量在 PreUUT 中创建,两者作用域不同,混用会导致数据串扰。

四、UI 与模型的交互关系

用户界面并不直接调用 Process Model,它只调用引擎的 Execution API。模型与 UI 之间的通信,主要通过两种方式:一是通过 RunState 中的变量,UI 可以绑定显示;二是通过引擎的事件回调(如 Engine.NewExecution、Execution.EndExecution),UI 可以监听执行状态变化。

这意味着,如果定制模型时希望 UI 上显示自定义信息(例如当前工单号、MES 校验结果),正确做法是把数据写入 RunState 的某个变量,然后在 UI 的显示控件中绑定该变量路径,而不是在模型里直接操作 UI 控件。后者会破坏模型与 UI 的解耦,导致模型无法在无 UI 环境下运行。

模型定制的一条基本原则:模型只负责流程与数据,不负责界面。所有需要展示的信息,通过 RunState 变量暴露,由 UI 决定如何呈现。

五、调用时机的判断依据

实际定制中,最常见的困惑是"这段逻辑该放在哪个回调"。可以用一个简单的判断链来定位:

需求特征应放置的回调调用次数
仪器初始化、数据库连接ProcessSetup每次执行一次
扫码、工单校验、UUT 变量初始化PreUUT每颗 UUT 一次
测试主体逻辑MainSequence(产品序列)每颗 UUT 一次
结果写库、判定、UUT 资源释放PostUUT每颗 UUT 一次
仪器关闭、连接释放、日志归档ProcessCleanup每次执行一次

如果一段逻辑既不属于单颗 UUT,也不属于整个执行,例如"每测试 10 颗 UUT 后校准一次",则应放在 PostUUT 中,通过计数器判断是否达到阈值,再调用校准序列。这类逻辑无法靠固定回调位置解决,需要在回调内部做条件分支。

六、一个可复现的定制示例

下面给出一个最小可运行的定制片段,目标是在 PreUUT 中读取工单号并写入 RunState,供后续步骤使用。假设使用 TestStand 的 Statement 步骤调用 .NET 方法或 ActiveX 调用,伪代码如下:

// 在 PreUUT 回调中插入 Statement 步骤
// 1. 调用 MES 接口获取工单号
string workOrder = MES.GetWorkOrder(StationID);

// 2. 写入 RunState,供 MainSequence 和 PostUUT 使用
RunState.Root.Locals.UUT.WorkOrder = workOrder;

// 3. 校验工单有效性
if (string.IsNullOrEmpty(workOrder))
{
    RunState.Root.Locals.UUT.WorkOrderValid = false;
    RunState.PreviousStep.Result.Status = "Failed";
}
else
{
    RunState.Root.Locals.UUT.WorkOrderValid = true;
}

运行后,可以在序列编辑器的 Variables 面板中观察 RunState.Root.Locals.UUT.WorkOrder 的值是否被正确赋值。如果为空,说明 MES 接口调用失败或 StationID 配置错误;如果赋值成功但 MainSequence 中读不到,通常是变量路径写错或作用域层级不对。这类问题的排查,本质上就是对 RunState 层级结构的理解是否准确。

修改 Process Model 后,务必确认当前执行使用的是哪个模型文件。TestStand 允许在 Station Options 中指定默认模型,也可以在序列文件属性中覆盖。如果改了模型但执行时仍走旧逻辑,先检查模型路径是否指向了修改后的文件。

把引擎、编辑器、UI、模型四者的关系理清,把回调的调用时机和 RunState 的作用域记牢,后续无论是插入自定义步骤、调整流程顺序,还是做多工位并行定制,都能找到明确的切入点,而不是靠反复试错。