Authoring TestStand sequences with ts-cli
Scope
You can create and edit .seq files. You cannot run them, deploy them,
or touch hardware. A good result is a file an engineer can open in TestStand
and finish by hand: correct sequences, steps, limits and values.
Prerequisites
- The B101 ATE Framework services are running with a valid license.
- The pc has NI TestStand installed with a development license.
The tool is ts-cli. Confirm it is live with ts-cli status (ready: true); if
it is not ready, stop and report.
The CLI
ts-cli --help and ts-cli <command> --help are the authority for flags and
defaults. Do not probe mutating commands with guessed arguments on real files.
Commands print JSON; errors are JSON on stderr with exit 1. Read ids such as
unique_step_id and step_ids from responses instead of inventing them.
Examples use $SEQ for the target .seq path and $DLL for a .NET assembly
path; use the paths you were given.
Reconnaissance
Before editing an existing .seq, narrow step by step; each call returns only
what you ask for.
ts-cli inspect --file "$SEQ" # sequences + globals
ts-cli inspect --file "$SEQ" --sequence PowerRails # locals + steps (ids)
ts-cli inspect --file "$SEQ" --step-id <id> # the step's properties
--sequences and --globals narrow the first level. unique_step_ids come
from the second. To verify a change, filter with
inspect --values --match <text>.
The API
| Operation | What it does |
|---|---|
status |
Engine/pipe readiness |
create |
New .seq containing a default MainSequence |
add-sequence |
Add another named sequence to the file |
add-prop |
Create a container, array or scalar property |
set-prop |
Change one property of a step, locals or FileGlobals |
delete-prop |
Delete a local or file global property |
insert-step |
Add one step, optionally with properties |
clone-step |
Copy an existing step, optionally renaming it |
move-step |
Reorder a step, optionally into another group |
delete-step |
Remove a step by its unique step id |
apply-batch |
Apply many operations in one write |
inspect |
Read the file: sequences/globals, one sequence or one step |
Step types use the unspaced TestStand names: NumericLimitTest,
StringValueTest, PassFailTest, Action, SequenceCall, Statement,
Goto, MessagePopup, NI_Wait, NI_MultipleNumericLimitTest.
Step groups: setup, main (default), cleanup. --index is 0-based;
-1 appends.
Property paths inside a step are relative to the step: Limits.Low,
Limits.High, Comp, TS.PassAction is not valid. Start with
inspect --step-id <id> on a step of the target type to discover the exact
paths; the fresh NumericLimitTest exposes TS, Result, Limits, Comp, CompExpr, UseCompExpr, InBuf, DataSource.
Modules: the .NET adapter
A step added with the default --adapter "None Adapter" has no module. To call a
.NET method, pass the adapter DotNet Adapter (that exact string; .NET Adapter is rejected) and build the call list under TS.SData.
Every call needs an instance. Calls[0] is normally the constructor of the
root class (MemberType 4, MemberName = the class, Param[0] = Return Value); the calls after it are methods on that instance (MemberType 1).
A method with no instance behind it is invalid — never configure only the method.
ts-cli set-prop --file "$SEQ" --step-id "$ID" \
--path TS.SData.AssemblyPath --text "$DLL"
ts-cli set-prop --file "$SEQ" --step-id "$ID" \
--path TS.SData.ClassName --text PowerRails
ts-cli set-prop --file "$SEQ" --step-id "$ID" \
--path TS.SData.FunctionName --text MeasureVcc
# Calls[0]: construct the root class
ts-cli set-prop --file "$SEQ" --step-id "$ID" \
--path TS.SData.Calls[0].MemberName --text PowerRails
ts-cli set-prop --file "$SEQ" --step-id "$ID" \
--path TS.SData.Calls[0].MemberType --number 4
ts-cli set-prop --file "$SEQ" --step-id "$ID" \
--path "TS.SData.Calls[0].Params[0].Name" --text "Return Value"
ts-cli set-prop --file "$SEQ" --step-id "$ID" \
--path "TS.SData.Calls[0].Params[0].Type" --number 0
ts-cli set-prop --file "$SEQ" --step-id "$ID" \
--path "TS.SData.Calls[0].Params[0].TypeName" --text PowerRails
# Calls[1]: the method and its arguments
ts-cli set-prop --file "$SEQ" --step-id "$ID" \
--path TS.SData.Calls[1].MemberName --text MeasureVcc
ts-cli set-prop --file "$SEQ" --step-id "$ID" \
--path TS.SData.Calls[1].MemberType --number 1
ts-cli set-prop --file "$SEQ" --step-id "$ID" \
--path "TS.SData.Calls[1].Params[0].Name" --text nominal
ts-cli set-prop --file "$SEQ" --step-id "$ID" \
--path "TS.SData.Calls[1].Params[0].ArgVal" --text 5
ts-cli set-prop --file "$SEQ" --step-id "$ID" \
--path "TS.SData.Calls[1].Params[0].Type" --number 6
Each Params[i] carries Name, ArgVal (a TestStand expression, so quote
string literals), Type (0 object, 1 string, 2 boolean, 6 number, 12
array) and TypeName for object types. Writing an index grows the array.
Reuse one instance across steps instead of constructing it in every step. Store the object in a reference variable and have later steps take it as the instance:
ts-cli add-prop --file "$SEQ" --sequence MainSequence --path Device --type reference
The creating step writes the constructor result into the variable (Params[0]
Return Value with ArgVal = Locals.Device). Each later step starts its call
list with MemberType 6 and MemberName Use Existing Object, whose
Params[0] Existing Object has ArgVal = Locals.Device and the interface or
class in TypeName, followed by the method calls.
Other adapters use their own key (Sequence Adapter for SequenceCall).
TestStand fills the argument list from the assembly only in its editor, so take
names and types from the spec and verify with inspect --step-id <id>.
Arrays and containers
Give exactly one target: --step-id (a step; path relative to the step),
--sequence (a sequence's locals; path relative to Locals) or --globals
(the file's FileGlobals).
ts-cli add-prop --file "$SEQ" --sequence MainSequence \
--path RailNames --type string --array --length 4
ts-cli set-prop --file "$SEQ" --sequence MainSequence \
--path "RailNames[0]" --text VCC_5V0
ts-cli add-prop --file "$SEQ" --globals --path Timeout --type number
ts-cli set-prop --file "$SEQ" --globals --path Timeout --number 30
--type is number, string, boolean or container. Array elements are
addressed as Name[i]; writing an index beyond the current length grows the
array automatically. Existing step arrays (e.g. NumericArray on
NI_MultipleNumericLimitTest) are written the same way.
delete-prop removes a local or file global (not a step property, and not a
single array element). Use the same --sequence/--globals targets; deleting
a container removes its children too. Anything a step expression references is
not rechecked, so review the file after deleting.
Reordering steps
move-step puts a step at a final 0-based position (--index, -1 appends).
By default the step stays in its own sequence and group; pass --sequence or
--group to move it elsewhere. Use inspect --sequence to see each group's
steps in order and pick the index. The unique_step_id does not change when a
step moves, so you can keep using it afterwards.
Batch
For anything bigger than a couple of changes, build a batch and apply it in a single write: it is atomic (if any operation fails, nothing is saved). For a single change, use the matching command.
Operations (JSON op): add_sequence, add_property, set_property,
delete_property, insert_step, clone_step, move_step, delete_step.
Give each inserted/cloned step a key; later operations reference that step by
key (or by an existing unique step id). add_property takes path, type,
optional array/length, and targets a step key, a sequence (its locals)
or globals: true. delete_property takes path and the same
sequence/globals targets.
{
"operations": [
{"op": "add_sequence", "name": "PowerRails"},
{"op": "insert_step", "key": "vcc", "sequence": "PowerRails",
"step_type": "NumericLimitTest", "name": "VCC_5V0",
"set": {"Limits.Low": 4.75, "Limits.High": 5.25}},
{"op": "clone_step", "key": "vcc3", "source": "vcc",
"sequence": "PowerRails", "name": "VCC_3V3"},
{"op": "set_property", "step": "vcc3", "path": "Limits.Low",
"value": 3.135}
]
}
ts-cli apply-batch --file "$SEQ" --batch batch.json
The response maps each key to its new unique_step_id, so you can keep
editing the same steps afterwards.
Workflow: test spec -> sequence
- Read the spec. Extract each measurable: name, type, low/high or expected string, units, test condition. Map each measurable to a step.
- Create the file.
ts-cli create --file "$SEQ" --overwrite - Add steps in order, one measurement per step, limits inline:
Name steps after the signal in the spec so the sequence reads like the limits table.ts-cli insert-step --file "$SEQ" \ --step-type NumericLimitTest --name "VCC_5V0" \ --set Limits.Low=4.75 --set Limits.High=5.25 - Fix individual properties when a value comes later:
ts-cli set-prop --file "$SEQ" \ --step-id <id> --path Limits.High --number 5.25 - Verify with
inspect --values --match <signal>and check every limit.insert-stepreturns theunique_step_id; use it forset-prop. - Report the produced file path and the list of steps created. Do not claim the sequence is production-ready.
Recipes
Limit sweep — one step per row of a limits table:
for row in "VCC_5V0 4.75 5.25" "VIO_3V3 3.135 3.465"; do
set -- $row
ts-cli insert-step --file "$SEQ" \
--step-type NumericLimitTest --name "$1" \
--set "Limits.Low=$2" --set "Limits.High=$3"
done
Group steps under their own sequence (one per test phase) by adding a
sequence first, then targeting it with --sequence:
ts-cli add-sequence --file "$SEQ" --name PowerRails
ts-cli insert-step --file "$SEQ" \
--sequence PowerRails --step-type NumericLimitTest --name VCC_5V0 \
--set Limits.Low=4.75 --set Limits.High=5.25
Clone a configured step to repeat it with different limits. clone-step
copies the whole step (all properties); set the ones that change on the
returned unique_step_id:
ts-cli clone-step --file "$SEQ" --step-id "$SRC" --name VCC_3V3
ts-cli set-prop --file "$SEQ" \
--step-id "$NEW" --path Limits.Low --number 3.135
Rules
- One measurement per step; never invent signals not present in the spec.
- If a property path is rejected,
inspect --step-idan existing step of that type and use the real path. Do not guess twice. inspectformats numbers with a decimal comma (1,5). Normalize when comparing to the spec.- Do not add
Setup/Cleanupsteps, sequence calls or custom data types unless the spec asks for them. - Every mutating command saves immediately; there is no undo. Back up an existing sequence before editing it.
- If the file is open in TestStand, never save from the editor; reload from disk so the agent's changes are not overwritten.
Troubleshooting
TestStand authoring is disabled.— this deployment has authoring turned off; sequences cannot be edited here.- Missing or expired license on a fresh install:
ts-cli license-ensure.
Known gaps (v0)
- Only normal sequences; no callback or model-override sequences.
- No custom data types (NamedType) and no user-defined enums; standard steps
expose enum-like values (
Comp,Limits.ThresholdType) as strings. - Property deletion covers locals and file globals, not step properties.
- .NET modules are configured by hand under
TS.SData(assembly, class, member and arguments); the method signature is not read from the assembly. - No sequence reordering and no moves between files.
- No execution.