2. TestStand执行模型与异常传播

要真正掌握TestStand的异常处理,必须先理解它的执行模型:谁在执行、执行到哪里、出错后错误往哪里走、谁有机会拦截它。TestStand并不是一个简单的“顺序执行器”,而是一个由序列文件(Sequence File)、序列(Sequence)、步骤(Step)和执行入口点(Entry Point)构成的分层执行体系。异常(运行时错误)正是沿着这个层级结构向上传播的,而On Error回调与RunState对象则是我们在传播路径上设置的“拦截点”和“状态观察窗口”。

一、执行模型:从入口点到步骤的四层结构

TestStand的执行模型可以自上而下分为四层:

  • 序列文件:一个 .seq 文件,是组织单元,内部可包含多个序列。它本身不执行,只是容器。
  • 序列:真正被调用的执行单元,分为普通序列和入口点序列。入口点序列是TestStand引擎在特定时机自动调用的“钩子”,例如 MainSequence、ProcessSetup、ProcessCleanup、Setup、Cleanup 等。
  • 步骤:序列内的最小执行单位,每个步骤有类型(如 Numeric Limit Test、Sequence Call、Message Popup、Statement 等)、属性和运行结果。
  • 步骤模块:步骤实际调用的代码(LabVIEW VI、C DLL、.NET 程序集、Python 脚本等),是错误真正产生的地方。

当测试启动时,TestStand引擎按顺序调用入口点:先执行 ProcessSetup,再对每个待测单元(UUT)执行 Setup → MainSequence → Cleanup,最后执行 ProcessCleanup。这个调用链就是异常传播的“主干道”。

二、运行时错误的传播路径

错误在TestStand中是一个结构化的数据对象,包含 Code、Msg、Source、Occurrence 等字段。当某个步骤模块返回错误时,传播遵循以下规则:

  1. 步骤级:步骤执行后,TestStand检查其返回的错误簇。若 status 为真(出错),该步骤被标记为 Error。
  2. 序列级:默认情况下,序列遇到错误步骤会停止后续步骤的执行,并将错误向上返回给调用者。但序列的 Run Mode 可设为 Normal、Skip 或 Force Pass,影响是否继续。
  3. 调用链级:如果错误发生在被 Sequence Call 调用的子序列中,错误会沿调用栈逐层向上返回,直到被某个 On Error 回调拦截,或到达顶层入口点。
  4. 入口点级:若错误一直未被处理,最终会传递到 MainSequence 的调用者,TestStand会将UUT标记为失败,并继续执行 Cleanup 和 ProcessCleanup(除非配置为中止)。
关键点:TestStand的错误传播是“默认向上、可被拦截”。拦截点就是 On Error 回调。

三、On Error回调:异常处理的拦截器

TestStand提供了多个层级的 On Error 回调,它们本质上是特殊的序列,在错误发生时被自动调用:

  • SequenceFilePostStepOnError:步骤出错后立即调用,可访问出错步骤的上下文。
  • SequenceFilePostStepRuntimeError:专门处理运行时错误(Runtime Error),比通用On Error更具体。
  • SequenceFilePostSequenceOnError:序列执行出错后调用。
  • ProcessModelPostStepOnError:在过程模型层级拦截,影响整个测试流程。

这些回调的典型用途包括:记录错误日志、决定是否重试、清理资源、将错误转换为警告、或强制继续执行。回调内部通过 RunState 对象获取错误详情和当前执行状态。

四、RunState对象:异常处理的状态中枢

RunState 是TestStand运行时暴露给回调的核心对象,它携带了当前执行的全部上下文。在异常处理中,最常用的属性包括:

属性/方法作用
RunState.Error当前错误对象,包含 Code、Msg、Source 等
RunState.PreviousStep出错的前一步骤引用
RunState.NextStep下一步骤引用,可用于跳过或重定向
RunState.Sequence当前序列引用
RunState.Execution当前执行对象,可访问UUT信息、循环索引等
RunState.Caller调用者信息,用于追溯调用链

通过 RunState.Error 可以读取错误码和消息,通过 RunState.NextStep 可以修改执行流(例如跳过失败步骤继续测试),通过 RunState.Execution 可以获取当前UUT的序列号等标识信息,用于生成精确的错误报告。

五、一个典型的异常处理场景

假设我们在 MainSequence 中调用了一个子序列 TestVoltage,该子序列内部有一个 Numeric Limit Test 步骤,测量值超出上限,步骤返回错误。传播过程如下:

  1. 步骤模块返回错误,步骤被标记为 Error。
  2. TestVoltage 序列默认停止,错误向上返回。
  3. MainSequence 中的 Sequence Call 步骤收到错误,MainSequence 也停止。
  4. 此时 SequenceFilePostStepOnError 回调被触发,我们可以在其中读取 RunState.Error.Msg,判断错误码是否为“电压超限”。
  5. 如果是预期内的超限,我们可以将错误记录到数据库,并设置 RunState.NextStep 跳过后续电压测试,继续执行其他测试项。
  6. 如果不是预期错误,则保持错误状态,让UUT最终判定为失败。

这个场景说明:On Error回调不是简单地“捕获异常”,而是结合RunState对象,对错误进行分类、决策和重定向。这正是TestStand异常处理机制的核心价值。

TestStand 执行模型与异常传播路径 序列文件层 ProcessSetup ProcessCleanup SequenceFile 回调 序列层(入口点) Setup MainSequence Cleanup 步骤层 Step 1 Step 2 Step 3 (出错) Step 4 步骤模块层 LabVIEW VI C DLL / .NET Python 脚本 On Error 回调 SequenceFilePostStepOnError RunState 对象(状态中枢) • RunState.Error → Code / Msg / Source • RunState.PreviousStep / NextStep → 执行流控制 • RunState.Execution → UUT 信息 / 循环索引 分类决策 → 重试 / 跳过 / 记录 正常执行流 错误传播路径 处理决策流

六、实践中的关键配置与判断

在实际项目中,要让异常处理按预期工作,需要关注几个配置点:

  • 序列的 Run Mode:在序列属性中设置 Run Mode 为 Normal(默认,出错停止)、Skip(跳过该序列)或 Force Pass(强制通过)。这决定了错误是否继续向上传播。
  • 步骤的 Run Mode:单个步骤也可设置 Run Mode,例如将某个非关键步骤设为 Force Pass,即使出错也不影响整体流程。
  • On Error 回调的启用:在序列文件属性中勾选对应的回调,并确保回调序列内部逻辑正确。回调中通常使用 RunState.Error 判断错误类型,用 RunState.NextStep 控制后续执行。
  • 错误处理策略:常见策略包括“记录并继续”、“重试N次”、“跳转到清理步骤”、“中止测试”等。选择哪种策略取决于测试项的重要性和错误性质。
注意:On Error 回调中如果再次发生错误,可能导致递归调用或状态混乱。建议在回调内部使用简单的判断和赋值操作,避免调用可能出错的复杂步骤模块。