Skip to main content

Sequence

Struct Sequence 

Source
pub struct Sequence { /* private fields */ }
Expand description

One sequence inside a SequenceFile.

A sequence owns two of the four variable scopes:

  • locals, storage private to one call of this sequence.
  • parameters, values the caller supplies.

The other two live elsewhere: file globals on the sequence file, and station globals on the engine.

Implementations§

Source§

impl Sequence

Source

pub fn name(&self) -> Result<String, Error>

The sequence’s name (Sequence.Name).

§Errors

Error if the COM call fails or returns an unexpected type.

Examples found in repository?
examples/sequence_build.rs (line 48)
46fn describe(sequence: &Sequence) -> Result<(), rs_teststand::Error> {
47    let count = sequence.get_num_steps(StepGroup::Main)?;
48    println!("{} holds {count} step(s) in Main:", sequence.name()?);
49    for index in 0..count {
50        let step = sequence.get_step(index, StepGroup::Main)?;
51        let properties = step.as_property_object()?;
52        println!("  [{index}] {}", step.name()?);
53        if properties.exists("Limits.Low", NO_OPTIONS)? {
54            println!(
55                "      limits: low={}, high={}",
56                properties.get_val_number("Limits.Low", NO_OPTIONS)?,
57                properties.get_val_number("Limits.High", NO_OPTIONS)?
58            );
59        }
60        let precondition = step.precondition()?;
61        if !precondition.is_empty() {
62            println!("      runs when: {precondition}");
63        }
64    }
65    Ok(())
66}
More examples
Hide additional examples
examples/template_manage_complex.rs (line 151)
131fn apply_templates(
132    sequence_file: &SequenceFile,
133    step: &PropertyObject,
134    sequence: &PropertyObject,
135    variable: &PropertyObject,
136) -> Result<(), Error> {
137    let main_sequence = sequence_file.get_sequence_by_name(MAIN_SEQUENCE)?;
138
139    // Inserting returns the copy on the Step interface, so it can be renamed
140    // and re-identified straight away. Without a new step ID the copy would go
141    // on claiming the prototype's identity.
142    let inserted_step = main_sequence.insert_step_from_template(step, 0, StepGroup::Main)?;
143    inserted_step.set_name("Step From Template")?;
144    inserted_step.create_new_unique_step_id()?;
145    println!("  step:     {}", inserted_step.name()?);
146
147    let inserted_sequence = sequence_file.insert_sequence_from_template(sequence)?;
148    inserted_sequence.create_new_unique_step_ids()?;
149    println!(
150        "  sequence: {} ({} step(s))",
151        inserted_sequence.name()?,
152        inserted_sequence.get_num_steps(StepGroup::Main)?
153    );
154
155    // A variable is the easy case: Locals is a property tree, so a clone drops
156    // straight in under the template's own name.
157    let locals = main_sequence.locals()?;
158    locals.set_property_object(
159        &variable.name()?,
160        PropertyOptions::INSERT_IF_MISSING.bits(),
161        &variable.clone_property("", PropertyOptions::NONE.bits())?,
162    )?;
163    println!(
164        "  variable: {} = {:?}",
165        variable.name()?,
166        locals.get_val_string(&variable.name()?, PropertyOptions::NONE.bits())?
167    );
168
169    // A file that does not believe it changed will not write anything.
170    sequence_file
171        .as_property_object_file()?
172        .inc_change_count()?;
173    Ok(())
174}
examples/step_insert.rs (line 120)
73fn main() -> Result<(), Box<dyn std::error::Error>> {
74    let engine = Engine::new()?;
75
76    // A file to insert into. Built here so the example depends on nothing that
77    // has to exist on the station first.
78    let sequence_file = engine.new_sequence_file()?;
79    let subsequence = engine.new_sequence()?;
80    subsequence.set_name("CustomSubsequence")?;
81    let existing = engine.new_step(AdapterKeyName::NoneAdapter.as_str(), "Action")?;
82    existing.set_name(TARGET_STEP)?;
83    subsequence.insert_step(&existing, 0, StepGroup::Main)?;
84    sequence_file.insert_sequence(&subsequence)?;
85
86    // Where to insert: in front of the target, or at the end if it is absent.
87    let insert_at = if let Some(index) = index_of(&subsequence, TARGET_STEP, StepGroup::Main)? {
88        println!("Found {TARGET_STEP} at index {index}; inserting in front of it.");
89        index
90    } else {
91        let end = subsequence.get_num_steps(StepGroup::Main)?;
92        println!("{TARGET_STEP} not found; appending at index {end}.");
93        end
94    };
95
96    for (offset, (name, vi, project, mode)) in [
97        (
98            "Measure Voltage (VI)",
99            r"instruments.lvlibp\measure_voltage.vi",
100            "",
101            RunMode::Normal,
102        ),
103        (
104            "Measure Current (VI)",
105            r"instruments.lvlibp\measure_current.vi",
106            r"instruments.lvproj",
107            RunMode::Skip,
108        ),
109    ]
110    .into_iter()
111    .enumerate()
112    {
113        let step = vi_call_step(&engine, name, vi, project, mode)?;
114        let at = insert_at + i32::try_from(offset).unwrap_or(0);
115        subsequence.insert_step(&step, at, StepGroup::Main)?;
116    }
117
118    println!(
119        "\n{} now holds {} step(s):",
120        subsequence.name()?,
121        subsequence.get_num_steps(StepGroup::Main)?
122    );
123    for index in 0..subsequence.get_num_steps(StepGroup::Main)? {
124        let step = subsequence.get_step(index, StepGroup::Main)?;
125        let properties = step.as_property_object()?;
126        let vi = properties
127            .get_val_string(VI_PATH, none())
128            .unwrap_or_else(|_| "<no VI>".to_owned());
129        println!(
130            "  [{index}] {} - {:?}, run mode {:?}, records {:?}",
131            step.name()?,
132            step.adapter_key_name()?,
133            step.run_mode()?,
134            step.result_recording_option()?
135        );
136        if vi != "<no VI>" {
137            println!("        calls {vi}");
138        }
139    }
140
141    let path = std::env::temp_dir().join("rs_teststand_step_insert.seq");
142    let path = path.to_string_lossy().into_owned();
143    sequence_file.save(&path)?;
144    println!("\nSaved to {path}");
145    Ok(())
146}
Source

pub fn set_name(&self, name: &str) -> Result<(), Error>

Renames the sequence (Sequence.Name).

§Errors

Error if the COM call fails.

Examples found in repository?
examples/template_manage_complex.rs (line 112)
110fn sequence_template(engine: &Engine) -> Result<PropertyObject, Error> {
111    let sequence = engine.new_sequence()?;
112    sequence.set_name(SEQUENCE_TEMPLATE)?;
113
114    let step = engine.new_step(NO_ADAPTER, "Statement")?;
115    step.set_name("Inside_Sequence_Template")?;
116    sequence.insert_step(&step, 0, StepGroup::Main)?;
117
118    sequence.as_property_object()
119}
More examples
Hide additional examples
examples/execution_run_subsequence.rs (line 68)
59fn build(engine: &Engine) -> Result<SequenceFile, Error> {
60    let sequence_file = engine.new_sequence_file()?;
61
62    let main_sequence = sequence_file.get_sequence_by_name("MainSequence")?;
63    for name in ["Initialize", "Run Test", "Shut Down"] {
64        add_action(engine, &main_sequence, name)?;
65    }
66
67    let diagnostics = engine.new_sequence()?;
68    diagnostics.set_name(SUBSEQUENCE)?;
69    // A parameter is an ordinary property in the sequence's Parameters scope.
70    diagnostics.parameters()?.new_sub_property(
71        PARAMETER,
72        PropValType::String,
73        false,
74        "",
75        insert_if_missing(),
76    )?;
77    for name in ["Check Power Rails", "Check Clock"] {
78        add_action(engine, &diagnostics, name)?;
79    }
80    sequence_file.insert_sequence(&diagnostics)?;
81
82    Ok(sequence_file)
83}
examples/sequence_build.rs (line 99)
68fn main() -> Result<(), Box<dyn std::error::Error>> {
69    let engine = Engine::new()?;
70    let sequence_file = engine.new_sequence_file()?;
71    let main_sequence = sequence_file.get_sequence_by_name("MainSequence")?;
72
73    // Steps are placed by group and index, so order is explicit.
74    main_sequence.insert_step(
75        &numeric_limit_test(
76            &engine,
77            "Temperature Check",
78            "Locals.TempSensorPresent == True",
79            15.0,
80            85.0,
81        )?,
82        0,
83        StepGroup::Main,
84    )?;
85    main_sequence.insert_step(
86        &numeric_limit_test(
87            &engine,
88            "Voltage Monitor",
89            "Locals.DUTPowered == True",
90            4.75,
91            5.25,
92        )?,
93        1,
94        StepGroup::Main,
95    )?;
96
97    // A subsequence, so a later example has something to call.
98    let subsequence = engine.new_sequence()?;
99    subsequence.set_name("CustomSubsequence")?;
100    sequence_file.insert_sequence(&subsequence)?;
101
102    let init_step = engine.new_step(NO_ADAPTER, "Action")?;
103    init_step.set_name("Initialize Hardware")?;
104    subsequence.insert_step(&init_step, 0, StepGroup::Main)?;
105
106    describe(&main_sequence)?;
107    println!();
108    describe(&subsequence)?;
109
110    // Cleanup runs even when Main fails, which is why it is worth showing.
111    println!(
112        "\nGroup sizes in MainSequence: setup={}, main={}, cleanup={}",
113        main_sequence.get_num_steps(StepGroup::Setup)?,
114        main_sequence.get_num_steps(StepGroup::Main)?,
115        main_sequence.get_num_steps(StepGroup::Cleanup)?
116    );
117
118    let path = std::env::temp_dir().join("rs_teststand_built_sequence.seq");
119    sequence_file.save(&path.to_string_lossy())?;
120    println!("\nSaved to {}", path.display());
121
122    engine.release_sequence_file_ex(sequence_file, NO_OPTIONS)?;
123    Ok(())
124}
examples/step_insert.rs (line 80)
73fn main() -> Result<(), Box<dyn std::error::Error>> {
74    let engine = Engine::new()?;
75
76    // A file to insert into. Built here so the example depends on nothing that
77    // has to exist on the station first.
78    let sequence_file = engine.new_sequence_file()?;
79    let subsequence = engine.new_sequence()?;
80    subsequence.set_name("CustomSubsequence")?;
81    let existing = engine.new_step(AdapterKeyName::NoneAdapter.as_str(), "Action")?;
82    existing.set_name(TARGET_STEP)?;
83    subsequence.insert_step(&existing, 0, StepGroup::Main)?;
84    sequence_file.insert_sequence(&subsequence)?;
85
86    // Where to insert: in front of the target, or at the end if it is absent.
87    let insert_at = if let Some(index) = index_of(&subsequence, TARGET_STEP, StepGroup::Main)? {
88        println!("Found {TARGET_STEP} at index {index}; inserting in front of it.");
89        index
90    } else {
91        let end = subsequence.get_num_steps(StepGroup::Main)?;
92        println!("{TARGET_STEP} not found; appending at index {end}.");
93        end
94    };
95
96    for (offset, (name, vi, project, mode)) in [
97        (
98            "Measure Voltage (VI)",
99            r"instruments.lvlibp\measure_voltage.vi",
100            "",
101            RunMode::Normal,
102        ),
103        (
104            "Measure Current (VI)",
105            r"instruments.lvlibp\measure_current.vi",
106            r"instruments.lvproj",
107            RunMode::Skip,
108        ),
109    ]
110    .into_iter()
111    .enumerate()
112    {
113        let step = vi_call_step(&engine, name, vi, project, mode)?;
114        let at = insert_at + i32::try_from(offset).unwrap_or(0);
115        subsequence.insert_step(&step, at, StepGroup::Main)?;
116    }
117
118    println!(
119        "\n{} now holds {} step(s):",
120        subsequence.name()?,
121        subsequence.get_num_steps(StepGroup::Main)?
122    );
123    for index in 0..subsequence.get_num_steps(StepGroup::Main)? {
124        let step = subsequence.get_step(index, StepGroup::Main)?;
125        let properties = step.as_property_object()?;
126        let vi = properties
127            .get_val_string(VI_PATH, none())
128            .unwrap_or_else(|_| "<no VI>".to_owned());
129        println!(
130            "  [{index}] {} - {:?}, run mode {:?}, records {:?}",
131            step.name()?,
132            step.adapter_key_name()?,
133            step.run_mode()?,
134            step.result_recording_option()?
135        );
136        if vi != "<no VI>" {
137            println!("        calls {vi}");
138        }
139    }
140
141    let path = std::env::temp_dir().join("rs_teststand_step_insert.seq");
142    let path = path.to_string_lossy().into_owned();
143    sequence_file.save(&path)?;
144    println!("\nSaved to {path}");
145    Ok(())
146}
Source

pub fn get_num_steps(&self, group: StepGroup) -> Result<i32, Error>

How many steps a group holds (Sequence.GetNumSteps).

§Errors

Error if the COM call fails or returns an unexpected type.

Examples found in repository?
examples/step_insert.rs (line 65)
64fn index_of(sequence: &Sequence, name: &str, group: StepGroup) -> Result<Option<i32>, Error> {
65    for index in 0..sequence.get_num_steps(group)? {
66        if sequence.get_step(index, group)?.name()? == name {
67            return Ok(Some(index));
68        }
69    }
70    Ok(None)
71}
72
73fn main() -> Result<(), Box<dyn std::error::Error>> {
74    let engine = Engine::new()?;
75
76    // A file to insert into. Built here so the example depends on nothing that
77    // has to exist on the station first.
78    let sequence_file = engine.new_sequence_file()?;
79    let subsequence = engine.new_sequence()?;
80    subsequence.set_name("CustomSubsequence")?;
81    let existing = engine.new_step(AdapterKeyName::NoneAdapter.as_str(), "Action")?;
82    existing.set_name(TARGET_STEP)?;
83    subsequence.insert_step(&existing, 0, StepGroup::Main)?;
84    sequence_file.insert_sequence(&subsequence)?;
85
86    // Where to insert: in front of the target, or at the end if it is absent.
87    let insert_at = if let Some(index) = index_of(&subsequence, TARGET_STEP, StepGroup::Main)? {
88        println!("Found {TARGET_STEP} at index {index}; inserting in front of it.");
89        index
90    } else {
91        let end = subsequence.get_num_steps(StepGroup::Main)?;
92        println!("{TARGET_STEP} not found; appending at index {end}.");
93        end
94    };
95
96    for (offset, (name, vi, project, mode)) in [
97        (
98            "Measure Voltage (VI)",
99            r"instruments.lvlibp\measure_voltage.vi",
100            "",
101            RunMode::Normal,
102        ),
103        (
104            "Measure Current (VI)",
105            r"instruments.lvlibp\measure_current.vi",
106            r"instruments.lvproj",
107            RunMode::Skip,
108        ),
109    ]
110    .into_iter()
111    .enumerate()
112    {
113        let step = vi_call_step(&engine, name, vi, project, mode)?;
114        let at = insert_at + i32::try_from(offset).unwrap_or(0);
115        subsequence.insert_step(&step, at, StepGroup::Main)?;
116    }
117
118    println!(
119        "\n{} now holds {} step(s):",
120        subsequence.name()?,
121        subsequence.get_num_steps(StepGroup::Main)?
122    );
123    for index in 0..subsequence.get_num_steps(StepGroup::Main)? {
124        let step = subsequence.get_step(index, StepGroup::Main)?;
125        let properties = step.as_property_object()?;
126        let vi = properties
127            .get_val_string(VI_PATH, none())
128            .unwrap_or_else(|_| "<no VI>".to_owned());
129        println!(
130            "  [{index}] {} - {:?}, run mode {:?}, records {:?}",
131            step.name()?,
132            step.adapter_key_name()?,
133            step.run_mode()?,
134            step.result_recording_option()?
135        );
136        if vi != "<no VI>" {
137            println!("        calls {vi}");
138        }
139    }
140
141    let path = std::env::temp_dir().join("rs_teststand_step_insert.seq");
142    let path = path.to_string_lossy().into_owned();
143    sequence_file.save(&path)?;
144    println!("\nSaved to {path}");
145    Ok(())
146}
More examples
Hide additional examples
examples/result_list_parse.rs (line 48)
36fn add_pass_fail(
37    engine: &Engine,
38    sequence: &Sequence,
39    name: &str,
40    source: &str,
41) -> Result<(), Error> {
42    let step = engine.new_step(AdapterKeyName::NoneAdapter.as_str(), "PassFailTest")?;
43    step.set_name(name)?;
44    step.as_property_object()?
45        .set_val_string("DataSource", insert_if_missing(), source)?;
46    sequence.insert_step(
47        &step,
48        sequence.get_num_steps(StepGroup::Main)?,
49        StepGroup::Main,
50    )
51}
52
53/// Adds a numeric limit test with a fixed measurement.
54fn add_numeric(
55    engine: &Engine,
56    sequence: &Sequence,
57    name: &str,
58    source: &str,
59    low: f64,
60    high: f64,
61) -> Result<(), Error> {
62    let step = engine.new_step(AdapterKeyName::NoneAdapter.as_str(), "NumericLimitTest")?;
63    step.set_name(name)?;
64    let properties = step.as_property_object()?;
65    properties.set_val_string("DataSource", insert_if_missing(), source)?;
66    properties.set_val_number("Limits.Low", insert_if_missing(), low)?;
67    properties.set_val_number("Limits.High", insert_if_missing(), high)?;
68    sequence.insert_step(
69        &step,
70        sequence.get_num_steps(StepGroup::Main)?,
71        StepGroup::Main,
72    )
73}
74
75/// Adds a statement step that records nothing, to show the gap it leaves.
76fn add_unrecorded_filler(engine: &Engine, sequence: &Sequence, name: &str) -> Result<(), Error> {
77    let step = engine.new_step(AdapterKeyName::NoneAdapter.as_str(), "Statement")?;
78    step.set_name(name)?;
79    step.set_result_recording_option(ResultRecordingOption::Disabled)?;
80    sequence.insert_step(
81        &step,
82        sequence.get_num_steps(StepGroup::Main)?,
83        StepGroup::Main,
84    )
85}
86
87/// Waits for the run, pumping the thread's messages and draining the engine's.
88fn wait_for_end(engine: &Engine, deadline: Duration) -> Result<bool, Error> {
89    let started = Instant::now();
90    while started.elapsed() < deadline {
91        if pump_thread_messages() {
92            return Ok(false);
93        }
94        while !engine.is_ui_message_queue_empty()? {
95            let message = engine.get_ui_message()?;
96            let ended = matches!(
97                UIMessageCode::from_bits(message.event()?),
98                Ok(UIMessageCode::EndExecution)
99            );
100            message.acknowledge()?;
101            if ended {
102                return Ok(true);
103            }
104        }
105    }
106    Ok(false)
107}
108
109fn main() -> Result<(), Box<dyn std::error::Error>> {
110    let engine = Engine::new()?;
111    engine.set_ui_message_polling_enabled(true)?;
112
113    let sequence_file = engine.new_sequence_file()?;
114    let main_sequence = sequence_file.get_sequence_by_name("MainSequence")?;
115
116    add_pass_fail(&engine, &main_sequence, "Verify Power Rail", "True")?;
117    add_unrecorded_filler(&engine, &main_sequence, "Filler (not recorded)")?;
118    add_numeric(
119        &engine,
120        &main_sequence,
121        "Verify Current Draw",
122        "1.5",
123        1.0,
124        2.0,
125    )?;
126    add_pass_fail(&engine, &main_sequence, "Verify Ground Bond", "True")?;
127
128    let step_count = main_sequence.get_num_steps(StepGroup::Main)?;
129    println!("Running {step_count} steps...");
130    let execution = engine.new_execution(&sequence_file, "MainSequence", None, false, 0)?;
131
132    if !wait_for_end(&engine, RUN_DEADLINE)? {
133        println!("The run did not finish within {RUN_DEADLINE:?}; terminating.");
134        execution.terminate()?;
135        return Ok(());
136    }
137    println!("Finished with status: {}\n", execution.result_status()?);
138
139    let results = execution.result_list()?;
140    let parsed = results.parse()?;
141
142    println!("{} of {step_count} steps recorded a result:", parsed.len());
143    for (index, result) in parsed.iter().enumerate() {
144        let measurement = match &result.value {
145            Some(ResultValue::Number(number)) => format!("measured {number}"),
146            Some(ResultValue::Text(text)) => format!("measured {text:?}"),
147            None => "no measurement".to_owned(),
148        };
149        println!(
150            "  [{index}] {} ({}) -> {}, {measurement}",
151            result.name, result.step_type, result.status
152        );
153    }
154
155    // The step whose recording was switched off is absent, by design.
156    println!(
157        "\nThe unrecorded step left no entry, which is why {} < {step_count}.",
158        parsed.len()
159    );
160
161    // The parsed results are plain data, so they outlive the run and can be
162    // handed to anything: a report writer, a database, a serde format.
163    let failures: Vec<&str> = parsed
164        .iter()
165        .filter(|result| result.status == "Failed")
166        .map(|result| result.name.as_str())
167        .collect();
168    if failures.is_empty() {
169        println!("No failures.");
170    } else {
171        println!("Failed steps: {}", failures.join(", "));
172    }
173
174    engine.release_sequence_file_ex(sequence_file, none())?;
175    Ok(())
176}
examples/ui_messages_handle.rs (line 34)
22fn add_statement(
23    engine: &Engine,
24    sequence: &Sequence,
25    name: &str,
26    expression: &str,
27) -> Result<(), rs_teststand::Error> {
28    let step = engine.new_step(NO_ADAPTER, "Statement")?;
29    step.set_name(name)?;
30    step.as_property_object()?
31        .set_val_string("TS.PostExpr", INSERT_IF_MISSING, expression)?;
32    sequence.insert_step(
33        &step,
34        sequence.get_num_steps(StepGroup::Main)?,
35        StepGroup::Main,
36    )?;
37    Ok(())
38}
examples/execution_run_subsequence.rs (line 53)
44fn add_action(engine: &Engine, sequence: &Sequence, name: &str) -> Result<(), Error> {
45    // The None adapter is what actually means "no code module"; an empty key
46    // would let the step type pick, and a step on a real adapter fails at run
47    // time with "module has not yet been specified".
48    let step = engine.new_step(AdapterKeyName::NoneAdapter.as_str(), "Action")?;
49    step.set_name(name)?;
50    step.set_record_result(true)?;
51    sequence.insert_step(
52        &step,
53        sequence.get_num_steps(StepGroup::Main)?,
54        StepGroup::Main,
55    )
56}
examples/execution_run_test_headless.rs (line 81)
62fn add_numeric_limit_test(
63    engine: &Engine,
64    sequence: &Sequence,
65    name: &str,
66    data_source: &str,
67    low: f64,
68    high: f64,
69) -> Result<(), Error> {
70    let step = engine.new_step(AdapterKeyName::NoneAdapter.as_str(), "NumericLimitTest")?;
71    step.set_name(name)?;
72    step.set_record_result(true)?;
73
74    let properties = step.as_property_object()?;
75    properties.set_val_string("DataSource", insert_if_missing(), data_source)?;
76    properties.set_val_number("Limits.Low", insert_if_missing(), low)?;
77    properties.set_val_number("Limits.High", insert_if_missing(), high)?;
78
79    sequence.insert_step(
80        &step,
81        sequence.get_num_steps(StepGroup::Main)?,
82        StepGroup::Main,
83    )
84}
examples/sequence_build.rs (line 47)
46fn describe(sequence: &Sequence) -> Result<(), rs_teststand::Error> {
47    let count = sequence.get_num_steps(StepGroup::Main)?;
48    println!("{} holds {count} step(s) in Main:", sequence.name()?);
49    for index in 0..count {
50        let step = sequence.get_step(index, StepGroup::Main)?;
51        let properties = step.as_property_object()?;
52        println!("  [{index}] {}", step.name()?);
53        if properties.exists("Limits.Low", NO_OPTIONS)? {
54            println!(
55                "      limits: low={}, high={}",
56                properties.get_val_number("Limits.Low", NO_OPTIONS)?,
57                properties.get_val_number("Limits.High", NO_OPTIONS)?
58            );
59        }
60        let precondition = step.precondition()?;
61        if !precondition.is_empty() {
62            println!("      runs when: {precondition}");
63        }
64    }
65    Ok(())
66}
67
68fn main() -> Result<(), Box<dyn std::error::Error>> {
69    let engine = Engine::new()?;
70    let sequence_file = engine.new_sequence_file()?;
71    let main_sequence = sequence_file.get_sequence_by_name("MainSequence")?;
72
73    // Steps are placed by group and index, so order is explicit.
74    main_sequence.insert_step(
75        &numeric_limit_test(
76            &engine,
77            "Temperature Check",
78            "Locals.TempSensorPresent == True",
79            15.0,
80            85.0,
81        )?,
82        0,
83        StepGroup::Main,
84    )?;
85    main_sequence.insert_step(
86        &numeric_limit_test(
87            &engine,
88            "Voltage Monitor",
89            "Locals.DUTPowered == True",
90            4.75,
91            5.25,
92        )?,
93        1,
94        StepGroup::Main,
95    )?;
96
97    // A subsequence, so a later example has something to call.
98    let subsequence = engine.new_sequence()?;
99    subsequence.set_name("CustomSubsequence")?;
100    sequence_file.insert_sequence(&subsequence)?;
101
102    let init_step = engine.new_step(NO_ADAPTER, "Action")?;
103    init_step.set_name("Initialize Hardware")?;
104    subsequence.insert_step(&init_step, 0, StepGroup::Main)?;
105
106    describe(&main_sequence)?;
107    println!();
108    describe(&subsequence)?;
109
110    // Cleanup runs even when Main fails, which is why it is worth showing.
111    println!(
112        "\nGroup sizes in MainSequence: setup={}, main={}, cleanup={}",
113        main_sequence.get_num_steps(StepGroup::Setup)?,
114        main_sequence.get_num_steps(StepGroup::Main)?,
115        main_sequence.get_num_steps(StepGroup::Cleanup)?
116    );
117
118    let path = std::env::temp_dir().join("rs_teststand_built_sequence.seq");
119    sequence_file.save(&path.to_string_lossy())?;
120    println!("\nSaved to {}", path.display());
121
122    engine.release_sequence_file_ex(sequence_file, NO_OPTIONS)?;
123    Ok(())
124}
Source

pub fn get_step(&self, index: i32, group: StepGroup) -> Result<Step, Error>

A step by position within a group (Sequence.GetStep).

§Errors

Error if the index is out of range or the COM call fails.

Examples found in repository?
examples/step_insert.rs (line 66)
64fn index_of(sequence: &Sequence, name: &str, group: StepGroup) -> Result<Option<i32>, Error> {
65    for index in 0..sequence.get_num_steps(group)? {
66        if sequence.get_step(index, group)?.name()? == name {
67            return Ok(Some(index));
68        }
69    }
70    Ok(None)
71}
72
73fn main() -> Result<(), Box<dyn std::error::Error>> {
74    let engine = Engine::new()?;
75
76    // A file to insert into. Built here so the example depends on nothing that
77    // has to exist on the station first.
78    let sequence_file = engine.new_sequence_file()?;
79    let subsequence = engine.new_sequence()?;
80    subsequence.set_name("CustomSubsequence")?;
81    let existing = engine.new_step(AdapterKeyName::NoneAdapter.as_str(), "Action")?;
82    existing.set_name(TARGET_STEP)?;
83    subsequence.insert_step(&existing, 0, StepGroup::Main)?;
84    sequence_file.insert_sequence(&subsequence)?;
85
86    // Where to insert: in front of the target, or at the end if it is absent.
87    let insert_at = if let Some(index) = index_of(&subsequence, TARGET_STEP, StepGroup::Main)? {
88        println!("Found {TARGET_STEP} at index {index}; inserting in front of it.");
89        index
90    } else {
91        let end = subsequence.get_num_steps(StepGroup::Main)?;
92        println!("{TARGET_STEP} not found; appending at index {end}.");
93        end
94    };
95
96    for (offset, (name, vi, project, mode)) in [
97        (
98            "Measure Voltage (VI)",
99            r"instruments.lvlibp\measure_voltage.vi",
100            "",
101            RunMode::Normal,
102        ),
103        (
104            "Measure Current (VI)",
105            r"instruments.lvlibp\measure_current.vi",
106            r"instruments.lvproj",
107            RunMode::Skip,
108        ),
109    ]
110    .into_iter()
111    .enumerate()
112    {
113        let step = vi_call_step(&engine, name, vi, project, mode)?;
114        let at = insert_at + i32::try_from(offset).unwrap_or(0);
115        subsequence.insert_step(&step, at, StepGroup::Main)?;
116    }
117
118    println!(
119        "\n{} now holds {} step(s):",
120        subsequence.name()?,
121        subsequence.get_num_steps(StepGroup::Main)?
122    );
123    for index in 0..subsequence.get_num_steps(StepGroup::Main)? {
124        let step = subsequence.get_step(index, StepGroup::Main)?;
125        let properties = step.as_property_object()?;
126        let vi = properties
127            .get_val_string(VI_PATH, none())
128            .unwrap_or_else(|_| "<no VI>".to_owned());
129        println!(
130            "  [{index}] {} - {:?}, run mode {:?}, records {:?}",
131            step.name()?,
132            step.adapter_key_name()?,
133            step.run_mode()?,
134            step.result_recording_option()?
135        );
136        if vi != "<no VI>" {
137            println!("        calls {vi}");
138        }
139    }
140
141    let path = std::env::temp_dir().join("rs_teststand_step_insert.seq");
142    let path = path.to_string_lossy().into_owned();
143    sequence_file.save(&path)?;
144    println!("\nSaved to {path}");
145    Ok(())
146}
More examples
Hide additional examples
examples/sequence_build.rs (line 50)
46fn describe(sequence: &Sequence) -> Result<(), rs_teststand::Error> {
47    let count = sequence.get_num_steps(StepGroup::Main)?;
48    println!("{} holds {count} step(s) in Main:", sequence.name()?);
49    for index in 0..count {
50        let step = sequence.get_step(index, StepGroup::Main)?;
51        let properties = step.as_property_object()?;
52        println!("  [{index}] {}", step.name()?);
53        if properties.exists("Limits.Low", NO_OPTIONS)? {
54            println!(
55                "      limits: low={}, high={}",
56                properties.get_val_number("Limits.Low", NO_OPTIONS)?,
57                properties.get_val_number("Limits.High", NO_OPTIONS)?
58            );
59        }
60        let precondition = step.precondition()?;
61        if !precondition.is_empty() {
62            println!("      runs when: {precondition}");
63        }
64    }
65    Ok(())
66}
examples/step_insert_from_template.rs (line 96)
65fn main() -> Result<(), Box<dyn std::error::Error>> {
66    let engine = Engine::new()?;
67    describe_templates(&engine)?;
68
69    let prototype = build_prototype(&engine)?;
70    println!(
71        "Prototype: {} via {:?}, run mode {:?}",
72        prototype.name()?,
73        prototype.adapter_key_name()?,
74        prototype.run_mode()?
75    );
76
77    let sequence_file = engine.new_sequence_file()?;
78    let main_sequence = sequence_file.get_sequence_by_name("MainSequence")?;
79    let template = prototype.as_property_object()?;
80
81    for copy_number in 1..=COPIES {
82        let index = main_sequence.get_num_steps(StepGroup::Main)?;
83        let inserted =
84            main_sequence.insert_step_from_template(&template, index, StepGroup::Main)?;
85        inserted.set_name(&format!("Measure {copy_number} (from template)"))?;
86        // Each copy needs an identity of its own; the clone brought the
87        // prototype's.
88        inserted.create_new_unique_step_id()?;
89    }
90
91    println!(
92        "\nMainSequence now holds {} step(s):",
93        main_sequence.get_num_steps(StepGroup::Main)?
94    );
95    for index in 0..main_sequence.get_num_steps(StepGroup::Main)? {
96        let step = main_sequence.get_step(index, StepGroup::Main)?;
97        println!(
98            "  [{index}] {} -> {}",
99            step.name()?,
100            step.as_property_object()?
101                .get_val_string(VI_PATH, PropertyOptions::NONE.bits())?
102        );
103    }
104
105    // The prototype is untouched and can go on producing copies.
106    println!("\nPrototype still named: {}", prototype.name()?);
107
108    let path = std::env::temp_dir().join("rs_teststand_from_template.seq");
109    let path = path.to_string_lossy().into_owned();
110    sequence_file.save(&path)?;
111    println!("Saved to {path}");
112    Ok(())
113}
Source

pub fn insert_step( &self, step: &Step, index: i32, group: StepGroup, ) -> Result<(), Error>

Places a step in a group at a position (Sequence.InsertStep).

§Errors

Error if the index is out of range, the step is not a live object, or the COM call fails.

Examples found in repository?
examples/template_manage_complex.rs (line 116)
110fn sequence_template(engine: &Engine) -> Result<PropertyObject, Error> {
111    let sequence = engine.new_sequence()?;
112    sequence.set_name(SEQUENCE_TEMPLATE)?;
113
114    let step = engine.new_step(NO_ADAPTER, "Statement")?;
115    step.set_name("Inside_Sequence_Template")?;
116    sequence.insert_step(&step, 0, StepGroup::Main)?;
117
118    sequence.as_property_object()
119}
More examples
Hide additional examples
examples/result_list_parse.rs (lines 46-50)
36fn add_pass_fail(
37    engine: &Engine,
38    sequence: &Sequence,
39    name: &str,
40    source: &str,
41) -> Result<(), Error> {
42    let step = engine.new_step(AdapterKeyName::NoneAdapter.as_str(), "PassFailTest")?;
43    step.set_name(name)?;
44    step.as_property_object()?
45        .set_val_string("DataSource", insert_if_missing(), source)?;
46    sequence.insert_step(
47        &step,
48        sequence.get_num_steps(StepGroup::Main)?,
49        StepGroup::Main,
50    )
51}
52
53/// Adds a numeric limit test with a fixed measurement.
54fn add_numeric(
55    engine: &Engine,
56    sequence: &Sequence,
57    name: &str,
58    source: &str,
59    low: f64,
60    high: f64,
61) -> Result<(), Error> {
62    let step = engine.new_step(AdapterKeyName::NoneAdapter.as_str(), "NumericLimitTest")?;
63    step.set_name(name)?;
64    let properties = step.as_property_object()?;
65    properties.set_val_string("DataSource", insert_if_missing(), source)?;
66    properties.set_val_number("Limits.Low", insert_if_missing(), low)?;
67    properties.set_val_number("Limits.High", insert_if_missing(), high)?;
68    sequence.insert_step(
69        &step,
70        sequence.get_num_steps(StepGroup::Main)?,
71        StepGroup::Main,
72    )
73}
74
75/// Adds a statement step that records nothing, to show the gap it leaves.
76fn add_unrecorded_filler(engine: &Engine, sequence: &Sequence, name: &str) -> Result<(), Error> {
77    let step = engine.new_step(AdapterKeyName::NoneAdapter.as_str(), "Statement")?;
78    step.set_name(name)?;
79    step.set_result_recording_option(ResultRecordingOption::Disabled)?;
80    sequence.insert_step(
81        &step,
82        sequence.get_num_steps(StepGroup::Main)?,
83        StepGroup::Main,
84    )
85}
examples/ui_messages_handle.rs (lines 32-36)
22fn add_statement(
23    engine: &Engine,
24    sequence: &Sequence,
25    name: &str,
26    expression: &str,
27) -> Result<(), rs_teststand::Error> {
28    let step = engine.new_step(NO_ADAPTER, "Statement")?;
29    step.set_name(name)?;
30    step.as_property_object()?
31        .set_val_string("TS.PostExpr", INSERT_IF_MISSING, expression)?;
32    sequence.insert_step(
33        &step,
34        sequence.get_num_steps(StepGroup::Main)?,
35        StepGroup::Main,
36    )?;
37    Ok(())
38}
examples/execution_run_subsequence.rs (lines 51-55)
44fn add_action(engine: &Engine, sequence: &Sequence, name: &str) -> Result<(), Error> {
45    // The None adapter is what actually means "no code module"; an empty key
46    // would let the step type pick, and a step on a real adapter fails at run
47    // time with "module has not yet been specified".
48    let step = engine.new_step(AdapterKeyName::NoneAdapter.as_str(), "Action")?;
49    step.set_name(name)?;
50    step.set_record_result(true)?;
51    sequence.insert_step(
52        &step,
53        sequence.get_num_steps(StepGroup::Main)?,
54        StepGroup::Main,
55    )
56}
examples/execution_run_test_headless.rs (lines 79-83)
62fn add_numeric_limit_test(
63    engine: &Engine,
64    sequence: &Sequence,
65    name: &str,
66    data_source: &str,
67    low: f64,
68    high: f64,
69) -> Result<(), Error> {
70    let step = engine.new_step(AdapterKeyName::NoneAdapter.as_str(), "NumericLimitTest")?;
71    step.set_name(name)?;
72    step.set_record_result(true)?;
73
74    let properties = step.as_property_object()?;
75    properties.set_val_string("DataSource", insert_if_missing(), data_source)?;
76    properties.set_val_number("Limits.Low", insert_if_missing(), low)?;
77    properties.set_val_number("Limits.High", insert_if_missing(), high)?;
78
79    sequence.insert_step(
80        &step,
81        sequence.get_num_steps(StepGroup::Main)?,
82        StepGroup::Main,
83    )
84}
examples/sequence_build.rs (lines 74-84)
68fn main() -> Result<(), Box<dyn std::error::Error>> {
69    let engine = Engine::new()?;
70    let sequence_file = engine.new_sequence_file()?;
71    let main_sequence = sequence_file.get_sequence_by_name("MainSequence")?;
72
73    // Steps are placed by group and index, so order is explicit.
74    main_sequence.insert_step(
75        &numeric_limit_test(
76            &engine,
77            "Temperature Check",
78            "Locals.TempSensorPresent == True",
79            15.0,
80            85.0,
81        )?,
82        0,
83        StepGroup::Main,
84    )?;
85    main_sequence.insert_step(
86        &numeric_limit_test(
87            &engine,
88            "Voltage Monitor",
89            "Locals.DUTPowered == True",
90            4.75,
91            5.25,
92        )?,
93        1,
94        StepGroup::Main,
95    )?;
96
97    // A subsequence, so a later example has something to call.
98    let subsequence = engine.new_sequence()?;
99    subsequence.set_name("CustomSubsequence")?;
100    sequence_file.insert_sequence(&subsequence)?;
101
102    let init_step = engine.new_step(NO_ADAPTER, "Action")?;
103    init_step.set_name("Initialize Hardware")?;
104    subsequence.insert_step(&init_step, 0, StepGroup::Main)?;
105
106    describe(&main_sequence)?;
107    println!();
108    describe(&subsequence)?;
109
110    // Cleanup runs even when Main fails, which is why it is worth showing.
111    println!(
112        "\nGroup sizes in MainSequence: setup={}, main={}, cleanup={}",
113        main_sequence.get_num_steps(StepGroup::Setup)?,
114        main_sequence.get_num_steps(StepGroup::Main)?,
115        main_sequence.get_num_steps(StepGroup::Cleanup)?
116    );
117
118    let path = std::env::temp_dir().join("rs_teststand_built_sequence.seq");
119    sequence_file.save(&path.to_string_lossy())?;
120    println!("\nSaved to {}", path.display());
121
122    engine.release_sequence_file_ex(sequence_file, NO_OPTIONS)?;
123    Ok(())
124}
Source

pub fn insert_step_from_template( &self, template: &PropertyObject, index: i32, group: StepGroup, ) -> Result<Step, Error>

Inserts a copy of a template step and returns the copy (PropertyObject.Clone + Sequence.InsertStep).

Composed rather than a COM member of its own, because the raw sequence has a trap in it. Clone lives on PropertyObject, so a copied step arrives on the PropertyObject interface, and the Step interface shares none of its dispatch identifiers. Reading Name off such a copy would dispatch a step identifier against a property-object interface and take the process down rather than fail. Inserting first and reading the step back from the sequence is the route that stays on the right interface, so this returns the inserted step and never exposes the intermediate.

The template itself is untouched and can be inserted any number of times. Each copy still carries the template’s step ID until Step::create_new_unique_step_id is called on it.

§Errors

Error if the template is not a live object, the index is out of range, or a COM call fails.

Examples found in repository?
examples/template_manage_complex.rs (line 142)
131fn apply_templates(
132    sequence_file: &SequenceFile,
133    step: &PropertyObject,
134    sequence: &PropertyObject,
135    variable: &PropertyObject,
136) -> Result<(), Error> {
137    let main_sequence = sequence_file.get_sequence_by_name(MAIN_SEQUENCE)?;
138
139    // Inserting returns the copy on the Step interface, so it can be renamed
140    // and re-identified straight away. Without a new step ID the copy would go
141    // on claiming the prototype's identity.
142    let inserted_step = main_sequence.insert_step_from_template(step, 0, StepGroup::Main)?;
143    inserted_step.set_name("Step From Template")?;
144    inserted_step.create_new_unique_step_id()?;
145    println!("  step:     {}", inserted_step.name()?);
146
147    let inserted_sequence = sequence_file.insert_sequence_from_template(sequence)?;
148    inserted_sequence.create_new_unique_step_ids()?;
149    println!(
150        "  sequence: {} ({} step(s))",
151        inserted_sequence.name()?,
152        inserted_sequence.get_num_steps(StepGroup::Main)?
153    );
154
155    // A variable is the easy case: Locals is a property tree, so a clone drops
156    // straight in under the template's own name.
157    let locals = main_sequence.locals()?;
158    locals.set_property_object(
159        &variable.name()?,
160        PropertyOptions::INSERT_IF_MISSING.bits(),
161        &variable.clone_property("", PropertyOptions::NONE.bits())?,
162    )?;
163    println!(
164        "  variable: {} = {:?}",
165        variable.name()?,
166        locals.get_val_string(&variable.name()?, PropertyOptions::NONE.bits())?
167    );
168
169    // A file that does not believe it changed will not write anything.
170    sequence_file
171        .as_property_object_file()?
172        .inc_change_count()?;
173    Ok(())
174}
More examples
Hide additional examples
examples/step_insert_from_template.rs (line 84)
65fn main() -> Result<(), Box<dyn std::error::Error>> {
66    let engine = Engine::new()?;
67    describe_templates(&engine)?;
68
69    let prototype = build_prototype(&engine)?;
70    println!(
71        "Prototype: {} via {:?}, run mode {:?}",
72        prototype.name()?,
73        prototype.adapter_key_name()?,
74        prototype.run_mode()?
75    );
76
77    let sequence_file = engine.new_sequence_file()?;
78    let main_sequence = sequence_file.get_sequence_by_name("MainSequence")?;
79    let template = prototype.as_property_object()?;
80
81    for copy_number in 1..=COPIES {
82        let index = main_sequence.get_num_steps(StepGroup::Main)?;
83        let inserted =
84            main_sequence.insert_step_from_template(&template, index, StepGroup::Main)?;
85        inserted.set_name(&format!("Measure {copy_number} (from template)"))?;
86        // Each copy needs an identity of its own; the clone brought the
87        // prototype's.
88        inserted.create_new_unique_step_id()?;
89    }
90
91    println!(
92        "\nMainSequence now holds {} step(s):",
93        main_sequence.get_num_steps(StepGroup::Main)?
94    );
95    for index in 0..main_sequence.get_num_steps(StepGroup::Main)? {
96        let step = main_sequence.get_step(index, StepGroup::Main)?;
97        println!(
98            "  [{index}] {} -> {}",
99            step.name()?,
100            step.as_property_object()?
101                .get_val_string(VI_PATH, PropertyOptions::NONE.bits())?
102        );
103    }
104
105    // The prototype is untouched and can go on producing copies.
106    println!("\nPrototype still named: {}", prototype.name()?);
107
108    let path = std::env::temp_dir().join("rs_teststand_from_template.seq");
109    let path = path.to_string_lossy().into_owned();
110    sequence_file.save(&path)?;
111    println!("Saved to {path}");
112    Ok(())
113}
Source

pub fn as_property_object(&self) -> Result<PropertyObject, Error>

The sequence as a property tree (Sequence.AsPropertyObject).

This is also how a sequence becomes a template: a clone taken here is a complete, detached copy of the sequence and its steps.

§Errors

Error if the COM call fails or returns an unexpected type.

Examples found in repository?
examples/template_manage_complex.rs (line 118)
110fn sequence_template(engine: &Engine) -> Result<PropertyObject, Error> {
111    let sequence = engine.new_sequence()?;
112    sequence.set_name(SEQUENCE_TEMPLATE)?;
113
114    let step = engine.new_step(NO_ADAPTER, "Statement")?;
115    step.set_name("Inside_Sequence_Template")?;
116    sequence.insert_step(&step, 0, StepGroup::Main)?;
117
118    sequence.as_property_object()
119}
Source

pub fn create_new_unique_step_ids(&self) -> Result<(), Error>

Re-identifies every step in the sequence (Sequence.CreateNewUniqueStepIds).

The bulk form of Step::create_new_unique_step_id, and what a sequence cloned from a template needs: every step in the copy arrives holding the identity of its counterpart in the original.

§Errors

Error if the COM call fails.

Examples found in repository?
examples/template_manage_complex.rs (line 148)
131fn apply_templates(
132    sequence_file: &SequenceFile,
133    step: &PropertyObject,
134    sequence: &PropertyObject,
135    variable: &PropertyObject,
136) -> Result<(), Error> {
137    let main_sequence = sequence_file.get_sequence_by_name(MAIN_SEQUENCE)?;
138
139    // Inserting returns the copy on the Step interface, so it can be renamed
140    // and re-identified straight away. Without a new step ID the copy would go
141    // on claiming the prototype's identity.
142    let inserted_step = main_sequence.insert_step_from_template(step, 0, StepGroup::Main)?;
143    inserted_step.set_name("Step From Template")?;
144    inserted_step.create_new_unique_step_id()?;
145    println!("  step:     {}", inserted_step.name()?);
146
147    let inserted_sequence = sequence_file.insert_sequence_from_template(sequence)?;
148    inserted_sequence.create_new_unique_step_ids()?;
149    println!(
150        "  sequence: {} ({} step(s))",
151        inserted_sequence.name()?,
152        inserted_sequence.get_num_steps(StepGroup::Main)?
153    );
154
155    // A variable is the easy case: Locals is a property tree, so a clone drops
156    // straight in under the template's own name.
157    let locals = main_sequence.locals()?;
158    locals.set_property_object(
159        &variable.name()?,
160        PropertyOptions::INSERT_IF_MISSING.bits(),
161        &variable.clone_property("", PropertyOptions::NONE.bits())?,
162    )?;
163    println!(
164        "  variable: {} = {:?}",
165        variable.name()?,
166        locals.get_val_string(&variable.name()?, PropertyOptions::NONE.bits())?
167    );
168
169    // A file that does not believe it changed will not write anything.
170    sequence_file
171        .as_property_object_file()?
172        .inc_change_count()?;
173    Ok(())
174}
Source

pub fn locals(&self) -> Result<PropertyObject, Error>

The sequence’s local variables (Sequence.Locals).

Locals are per call: each invocation of the sequence gets its own copy, so what this returns at edit time is the definition rather than any running instance’s values.

§Errors

Error if the COM call fails or returns an unexpected type.

Examples found in repository?
examples/template_manage_complex.rs (line 157)
131fn apply_templates(
132    sequence_file: &SequenceFile,
133    step: &PropertyObject,
134    sequence: &PropertyObject,
135    variable: &PropertyObject,
136) -> Result<(), Error> {
137    let main_sequence = sequence_file.get_sequence_by_name(MAIN_SEQUENCE)?;
138
139    // Inserting returns the copy on the Step interface, so it can be renamed
140    // and re-identified straight away. Without a new step ID the copy would go
141    // on claiming the prototype's identity.
142    let inserted_step = main_sequence.insert_step_from_template(step, 0, StepGroup::Main)?;
143    inserted_step.set_name("Step From Template")?;
144    inserted_step.create_new_unique_step_id()?;
145    println!("  step:     {}", inserted_step.name()?);
146
147    let inserted_sequence = sequence_file.insert_sequence_from_template(sequence)?;
148    inserted_sequence.create_new_unique_step_ids()?;
149    println!(
150        "  sequence: {} ({} step(s))",
151        inserted_sequence.name()?,
152        inserted_sequence.get_num_steps(StepGroup::Main)?
153    );
154
155    // A variable is the easy case: Locals is a property tree, so a clone drops
156    // straight in under the template's own name.
157    let locals = main_sequence.locals()?;
158    locals.set_property_object(
159        &variable.name()?,
160        PropertyOptions::INSERT_IF_MISSING.bits(),
161        &variable.clone_property("", PropertyOptions::NONE.bits())?,
162    )?;
163    println!(
164        "  variable: {} = {:?}",
165        variable.name()?,
166        locals.get_val_string(&variable.name()?, PropertyOptions::NONE.bits())?
167    );
168
169    // A file that does not believe it changed will not write anything.
170    sequence_file
171        .as_property_object_file()?
172        .inc_change_count()?;
173    Ok(())
174}
More examples
Hide additional examples
examples/variables_manage.rs (line 205)
186fn main() -> Result<(), rs_teststand::Error> {
187    let engine = Engine::new()?;
188
189    show_station_globals(&engine)?;
190
191    // The other three scopes need a sequence file.
192    if let Some(sequence_file) = open_sequence_file(&engine)? {
193        // File globals: shared by every sequence in this file. These are the
194        // defaults stored in the file; a running execution gets its own copy.
195        let file_globals = sequence_file.file_globals_default_values()?;
196        set_string(&file_globals, "BatchID", "BATCH-2026-Q2-001")?;
197        println!(
198            "FileGlobals.BatchID                   = '{}'",
199            file_globals.get_val_string("BatchID", 0)?
200        );
201
202        let main_sequence = sequence_file.get_sequence_by_name("MainSequence")?;
203
204        // Locals: private to one call of this sequence.
205        let locals = main_sequence.locals()?;
206        set_string(&locals, "OperatorName", "Alice")?;
207        println!(
208            "MainSequence.Locals.OperatorName      = '{}'",
209            locals.get_val_string("OperatorName", 0)?
210        );
211
212        // Parameters: supplied by whoever calls this sequence.
213        let parameters = main_sequence.parameters()?;
214        set_string(&parameters, "DUTSerial", "SN-000000")?;
215        println!(
216            "MainSequence.Parameters.DUTSerial     = '{}'",
217            parameters.get_val_string("DUTSerial", 0)?
218        );
219
220        println!("\nTemporary variable lifecycle (Locals.TempScratch):");
221        temporary_variable_lifecycle(&locals)?;
222
223        // Nothing is saved: the file is left exactly as it was found.
224        engine.release_sequence_file_ex(sequence_file, 0)?;
225    } else {
226        println!("\n(pass a .seq path to also demonstrate locals, parameters and file globals)");
227        println!("\nTemporary variable lifecycle (StationGlobals.TempScratch):");
228        temporary_variable_lifecycle(&engine.globals()?)?;
229    }
230
231    engine.commit_globals_to_disk(false)?;
232    println!("\nStation globals committed to disk.");
233    Ok(())
234}
examples/data_type_manage.rs (line 128)
107fn main() -> Result<(), Box<dyn std::error::Error>> {
108    let engine = Engine::new()?;
109    let sequence_file = engine.new_sequence_file()?;
110    let file = sequence_file.as_property_object_file()?;
111    let types = file.type_usage_list()?;
112
113    types.insert_type(
114        &build_multimeter_type(&engine)?,
115        types.num_types()?,
116        TypeCategory::CustomDataTypes,
117    )?;
118    let coupling = register_enum(
119        &engine,
120        &types,
121        "Coupling",
122        &[("AC", 0.0), ("DC", 1.0)],
123        true,
124    )?;
125
126    // A variable of the enum type, so the change below has an instance to update.
127    let main_sequence = sequence_file.get_sequence_by_name("MainSequence")?;
128    main_sequence.locals()?.new_sub_property(
129        "InputCoupling",
130        PropValType::NamedType,
131        false,
132        "Coupling",
133        INSERT_IF_MISSING,
134    )?;
135
136    println!(
137        "Registered custom data types ({} in file):",
138        types.num_types()?
139    );
140    print_enumerators(&coupling)?;
141
142    // Evolve it: add an enumerator and raise the version.
143    println!(
144        "\nCoupling version before update: {}",
145        coupling.type_version()?
146    );
147    coupling.update_enumerators(&enumerator_array(
148        &engine,
149        &[("AC", 0.0), ("DC", 1.0), ("GND", 2.0)],
150        true,
151    )?)?;
152
153    // Raising the lowest field signals a change the engine applies silently;
154    // raising a higher one marks it as deliberate.
155    let version = coupling.type_version()?;
156    let mut fields = version.split('.');
157    let major: u32 = fields.next().unwrap_or("0").parse().unwrap_or(0);
158    let minor: u32 = fields.next().unwrap_or("0").parse().unwrap_or(0);
159    coupling.set_type_version(&format!("{major}.{}.0.0", minor + 1))?;
160    println!(
161        "Coupling version after  update: {}",
162        coupling.type_version()?
163    );
164
165    println!("\nCoupling now defines (InputCoupling reflects this):");
166    print_enumerators(&coupling)?;
167
168    // Saving does nothing unless the file believes it changed.
169    file.inc_change_count()?;
170    let path = std::env::temp_dir().join("rs_teststand_with_custom_types.seq");
171    sequence_file.save(&path.to_string_lossy())?;
172    println!("\nSaved to {}", path.display());
173
174    engine.release_sequence_file_ex(sequence_file, NO_OPTIONS)?;
175    Ok(())
176}
Source

pub fn parameters(&self) -> Result<PropertyObject, Error>

The sequence’s parameters (Sequence.Parameters).

§Errors

Error if the COM call fails or returns an unexpected type.

Examples found in repository?
examples/execution_run_subsequence.rs (line 70)
59fn build(engine: &Engine) -> Result<SequenceFile, Error> {
60    let sequence_file = engine.new_sequence_file()?;
61
62    let main_sequence = sequence_file.get_sequence_by_name("MainSequence")?;
63    for name in ["Initialize", "Run Test", "Shut Down"] {
64        add_action(engine, &main_sequence, name)?;
65    }
66
67    let diagnostics = engine.new_sequence()?;
68    diagnostics.set_name(SUBSEQUENCE)?;
69    // A parameter is an ordinary property in the sequence's Parameters scope.
70    diagnostics.parameters()?.new_sub_property(
71        PARAMETER,
72        PropValType::String,
73        false,
74        "",
75        insert_if_missing(),
76    )?;
77    for name in ["Check Power Rails", "Check Clock"] {
78        add_action(engine, &diagnostics, name)?;
79    }
80    sequence_file.insert_sequence(&diagnostics)?;
81
82    Ok(sequence_file)
83}
84
85/// Waits for a run to finish, pumping and draining as it goes.
86fn wait_for_end(engine: &Engine, deadline: Duration) -> Result<bool, Error> {
87    let started = Instant::now();
88    while started.elapsed() < deadline {
89        if pump_thread_messages() {
90            return Ok(false);
91        }
92        while !engine.is_ui_message_queue_empty()? {
93            let message = engine.get_ui_message()?;
94            let ended = matches!(
95                UIMessageCode::from_bits(message.event()?),
96                Ok(UIMessageCode::EndExecution)
97            );
98            message.acknowledge()?;
99            if ended {
100                return Ok(true);
101            }
102        }
103    }
104    Ok(false)
105}
106
107/// Starts a run at the named sequence and prints what it recorded.
108fn run(engine: &Engine, sequence_file: &SequenceFile, entry_point: &str) -> Result<(), Error> {
109    let execution = engine.new_execution(sequence_file, entry_point, None, false, 0)?;
110    println!("\nRunning {entry_point}...");
111
112    if !wait_for_end(engine, RUN_DEADLINE)? {
113        println!("  did not finish within {RUN_DEADLINE:?}; terminating");
114        execution.terminate()?;
115        return Ok(());
116    }
117    println!("  status: {}", execution.result_status()?);
118
119    let results = execution.result_object()?;
120    if !results.exists("ResultList", none())? {
121        println!("  no results recorded");
122        return Ok(());
123    }
124    let result_list = results.get_property_object("ResultList", none())?;
125    for index in 0..result_list.get_num_elements()? {
126        let entry = result_list.get_property_object_by_offset(index, none())?;
127        println!(
128            "  {}: {}",
129            entry
130                .get_val_string("TS.StepName", none())
131                .unwrap_or_else(|_| "<unnamed>".to_owned()),
132            entry
133                .get_val_string("Status", none())
134                .unwrap_or_else(|_| "<no status>".to_owned())
135        );
136    }
137    Ok(())
138}
139
140fn main() -> Result<(), Box<dyn std::error::Error>> {
141    let engine = Engine::new()?;
142    engine.set_ui_message_polling_enabled(true)?;
143
144    let sequence_file = build(&engine)?;
145    println!("File holds {} sequence(s).", sequence_file.num_sequences()?);
146
147    // The conventional entry point.
148    run(&engine, &sequence_file, "MainSequence")?;
149
150    // A subsequence run on its own has no caller, so nothing supplies its
151    // parameters, they keep whatever default the sequence carries. Setting
152    // that default is therefore how a direct run is given its input.
153    let diagnostics = sequence_file.get_sequence_by_name(SUBSEQUENCE)?;
154    diagnostics
155        .parameters()?
156        .set_val_string(PARAMETER, none(), "FIXTURE-07")?;
157    println!(
158        "\n{SUBSEQUENCE}.{PARAMETER} default is now {:?}",
159        diagnostics
160            .parameters()?
161            .get_val_string(PARAMETER, none())?
162    );
163
164    run(&engine, &sequence_file, SUBSEQUENCE)?;
165
166    engine.release_sequence_file_ex(sequence_file, none())?;
167    Ok(())
168}
More examples
Hide additional examples
examples/variables_manage.rs (line 213)
186fn main() -> Result<(), rs_teststand::Error> {
187    let engine = Engine::new()?;
188
189    show_station_globals(&engine)?;
190
191    // The other three scopes need a sequence file.
192    if let Some(sequence_file) = open_sequence_file(&engine)? {
193        // File globals: shared by every sequence in this file. These are the
194        // defaults stored in the file; a running execution gets its own copy.
195        let file_globals = sequence_file.file_globals_default_values()?;
196        set_string(&file_globals, "BatchID", "BATCH-2026-Q2-001")?;
197        println!(
198            "FileGlobals.BatchID                   = '{}'",
199            file_globals.get_val_string("BatchID", 0)?
200        );
201
202        let main_sequence = sequence_file.get_sequence_by_name("MainSequence")?;
203
204        // Locals: private to one call of this sequence.
205        let locals = main_sequence.locals()?;
206        set_string(&locals, "OperatorName", "Alice")?;
207        println!(
208            "MainSequence.Locals.OperatorName      = '{}'",
209            locals.get_val_string("OperatorName", 0)?
210        );
211
212        // Parameters: supplied by whoever calls this sequence.
213        let parameters = main_sequence.parameters()?;
214        set_string(&parameters, "DUTSerial", "SN-000000")?;
215        println!(
216            "MainSequence.Parameters.DUTSerial     = '{}'",
217            parameters.get_val_string("DUTSerial", 0)?
218        );
219
220        println!("\nTemporary variable lifecycle (Locals.TempScratch):");
221        temporary_variable_lifecycle(&locals)?;
222
223        // Nothing is saved: the file is left exactly as it was found.
224        engine.release_sequence_file_ex(sequence_file, 0)?;
225    } else {
226        println!("\n(pass a .seq path to also demonstrate locals, parameters and file globals)");
227        println!("\nTemporary variable lifecycle (StationGlobals.TempScratch):");
228        temporary_variable_lifecycle(&engine.globals()?)?;
229    }
230
231    engine.commit_globals_to_disk(false)?;
232    println!("\nStation globals committed to disk.");
233    Ok(())
234}

Trait Implementations§

Source§

impl Debug for Sequence

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.