4. 变量与数据传递

在 TestStand 中,数据传递的混乱往往不是“变量不够用”,而是“用错了作用域”。同一个测试值,如果放在局部变量里,序列调用结束后就消失;如果放在序列参数里,只能沿调用链传递;如果放在文件全局变量里,同一序列文件内所有序列都能访问;如果放在站全局变量里,整个测试站的所有序列文件都能读写。理解这四类变量的生命周期和可见范围,是避免“数据丢失”“值被覆盖”“回调读不到参数”等问题的前提。

四类变量的作用域与生命周期

TestStand 的变量体系可以按“可见范围”和“生存周期”两个维度来划分。局部变量(Local)属于某个序列,序列开始执行时创建,序列结束时销毁,只有该序列内的步骤能访问。序列参数(Parameters)也属于某个序列,但它由调用者传入,分为输入参数和输出参数,生命周期与序列执行期一致,是序列间传递数据的标准通道。文件全局变量(File Globals)属于某个序列文件(.seq),文件被加载时创建,文件卸载时销毁,同一文件内所有序列和回调都能访问。站全局变量(Station Globals)属于整个测试站,TestStand 引擎启动时创建,引擎关闭时销毁,所有已加载的序列文件都能访问。

这四类变量的关键差异在于“谁能看见”和“活多久”。局部变量最安全但最短命;序列参数最规范但需要显式声明;文件全局变量适合同一文件内的共享状态;站全局变量适合跨文件的全局配置,但滥用会导致序列文件之间产生隐式耦合,降低可移植性。

TestStand 四类变量作用域与数据传递路径 站全局变量 Station Globals 生命周期:引擎启动 → 引擎关闭 | 可见范围:所有已加载序列文件 StationGlobals.TestConfig StationGlobals.OperatorID 文件全局变量 File Globals 生命周期:文件加载 → 文件卸载 | 可见范围:同一 .seq 文件内所有序列与回调 FileGlobals.TestCounter FileGlobals.LastResult 序列参数 Parameters 生命周期:序列调用期 | 可见范围:被调用序列内部 输入:VoltageSet 输出:MeasuredV 局部变量 Locals 生命周期:序列执行期 | 可见范围:仅当前序列内步骤 Locals.tempValue Locals.retryCount 跨文件读取 文件内共享 序列间传递:调用者 → 被调用者 Sequence Call 步骤通过参数映射传递数据:调用者将实参绑定到被调用序列的形参 回调(Callback)通过 ThisContext 访问当前序列上下文,读取参数或写入文件全局变量

步骤间传递:局部变量与步骤属性

同一序列内步骤之间传递数据,最直接的方式是使用局部变量。例如在“MainSequence”中,先执行一个“Statement”步骤把计算结果写入 Locals.measuredValue,后续的“Numeric Limit Test”步骤在 Limits 表达式中引用 Locals.measuredValue 进行上下限判断。局部变量的优势是隔离性好,不会污染其他序列;缺点是序列结束后值不再保留,如果下一个序列需要这个值,必须通过参数或全局变量传递。

步骤之间还可以通过步骤的“Post Expression”或“Pre Expression”读写变量。例如在“Call DMM”步骤的 Post Expression 中写 Locals.voltage = Step.Result.Measurement,把测量结果从步骤结果中提取到局部变量,供后续步骤使用。这种写法在 TestStand 中非常常见,关键是要区分“步骤结果”和“用户变量”两个不同的数据容器。

序列间传递:序列参数的输入与输出

序列间传递数据的标准做法是使用序列参数。假设有一个“MeasureVoltage”序列,它需要接收一个量程参数,并返回测量结果。在序列属性中定义输入参数 Range(Number)和输出参数 MeasuredV(Number)。调用者使用“Sequence Call”步骤,在参数映射中把 Locals.rangeSetting 绑定到 Range,把 MeasuredV 绑定到 Locals.result。这样数据就沿着调用链正确流动。

需要注意,输出参数必须在被调用序列内部被赋值,否则调用者读到的是默认值。如果被调用序列提前退出或发生错误,输出参数可能未被写入,调用者应通过 Step.Result.Error.Code 判断调用是否成功,再决定是否使用输出值。对于需要返回多个值的场景,可以把输出参数定义为容器(Container),在容器内放置多个子属性,减少参数数量。

回调间传递:ThisContext 与文件全局变量

回调(Callback)是 TestStand 在特定事件点自动调用的序列,例如 ProcessSetup、PreUUT、PostUUT、MainSequence 等。回调与普通序列一样,可以通过参数接收数据,但更常见的是通过 ThisContext 访问当前执行上下文。例如在 PreUUT 回调中,可以读取 ThisContext.TestSocket.Index 获取当前工位号,也可以写入 FileGlobals.OperatorID 供后续序列使用。

回调之间传递数据时,文件全局变量是最常用的桥梁。例如 ProcessSetup 回调中初始化 FileGlobals.TestCounter = 0,PostUUT 回调中执行 FileGlobals.TestCounter += 1,这样计数器在同一个序列文件内跨回调保持。如果需要在多个序列文件之间共享,则应使用站全局变量,但要注意站全局变量的修改会影响整个测试站,必须谨慎管理。

工程选择原则与常见陷阱

选择变量类型时,可以遵循以下原则:只在当前序列内使用的临时值,用局部变量;需要由调用者传入或返回给调用者的值,用序列参数;同一序列文件内多个序列或回调共享的状态,用文件全局变量;跨序列文件的全局配置或运行时状态,用站全局变量。优先级从高到低依次是:局部变量 → 序列参数 → 文件全局变量 → 站全局变量。越靠前的变量耦合度越低,越容易复用和调试。

常见陷阱包括:在回调中直接读写局部变量,但局部变量属于回调序列本身,无法访问调用者的局部变量;把站全局变量当作“万能存储”,导致序列文件无法独立运行;输出参数未赋值就使用,得到默认值或旧值;在并行测试中多个线程同时写同一个文件全局变量,产生竞态。对于并行场景,应使用锁(Lock)或把数据放在每个工位独立的容器中,避免共享写冲突。

实践建议:在序列文件开发初期就规划好变量表。建议在 FileGlobals 中只放“本文件内跨序列共享”的数据,在 StationGlobals 中只放“测试站级配置”(如设备地址、操作员信息、站点编号)。序列参数尽量使用容器类型,便于扩展。回调中优先使用 ThisContext 和 FileGlobals,避免依赖调用者的局部变量。
注意:站全局变量在 TestStand 引擎启动时创建,如果多个序列文件同时修改同一个站全局变量,后写入的值会覆盖先写入的值。在并行测试或多工位场景中,必须为每个工位使用独立的变量路径,例如 StationGlobals.Socket[0].Result 和 StationGlobals.Socket[1].Result,而不是共用一个 StationGlobals.Result。

掌握这四类变量的边界和传递路径后,实际开发中遇到“数据读不到”“值被覆盖”“回调拿不到参数”等问题时,就可以按“先看作用域、再看生命周期、最后看赋值时机”的顺序排查。大多数数据传递问题,根源都在于把本该用参数传递的数据放进了全局变量,或者把本该用文件全局变量共享的数据放进了局部变量。