Skip to main content

SequenceFile

Struct SequenceFile 

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

A sequence file held open by the engine.

Obtained from crate::Engine::get_sequence_file_ex. Dropping this value releases the wrapper’s own reference, but the engine keeps the file in its cache until it is released explicitly, see crate::Engine::release_sequence_file_ex.

Implementations§

Source§

impl SequenceFile

Source

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

The file’s path on disk (Path).

§Errors

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

Source

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

How many sequences the file contains (NumSequences).

§Errors

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

Examples found in repository?
examples/execution_run_subsequence.rs (line 145)
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}
Source

pub fn insert_sequence(&self, sequence: &Sequence) -> Result<(), Error>

Adds a sequence to the file (SequenceFile.InsertSequence).

§Errors

Error if the sequence is not a live object or the COM call fails.

Examples found in repository?
examples/execution_run_subsequence.rs (line 80)
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}
More examples
Hide additional examples
examples/sequence_build.rs (line 100)
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 84)
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 insert_sequence_from_template( &self, template: &PropertyObject, ) -> Result<Sequence, Error>

Inserts a copy of a template sequence and returns the copy (PropertyObject.Clone + SequenceFile.InsertSequence).

The sequence-level counterpart of Sequence::insert_step_from_template, and composed for the same reason: a clone arrives on the PropertyObject interface, which shares no dispatch identifiers with Sequence. The copy is looked up by name after insertion so the caller only ever holds the right interface.

The copy keeps the template’s name, so inserting the same template twice without renaming the first copy puts two sequences of one name in the file. Every step in the copy also still carries the template’s step ID, /// see Sequence::create_new_unique_step_ids.

§Errors

Error if the template is not a live object, has no name, or a COM call fails.

Examples found in repository?
examples/template_manage_complex.rs (line 147)
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 as_property_object_file(&self) -> Result<PropertyObjectFile, Error>

The file seen as a property-object file (AsPropertyObjectFile).

This is the route to the file’s registered types.

§Errors

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

Examples found in repository?
examples/template_manage_complex.rs (line 171)
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/data_type_manage.rs (line 110)
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 file_globals_default_values(&self) -> Result<PropertyObject, Error>

The file’s edit-time global variables (FileGlobalsDefaultValues).

These are the defaults stored in the file, which is what an editor shows and what this API can change. A running execution works on its own run-time copy instead, and edits made there do not travel back here, /// reach that copy through the execution, not through this method.

§Errors

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

Examples found in repository?
examples/variables_manage.rs (line 195)
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}
Source

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

Looks a sequence up by name (GetSequenceByName).

§Errors

Error if no such sequence exists or the COM call fails.

Examples found in repository?
examples/execution_run_subsequence.rs (line 62)
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/execution_run_test_headless.rs (line 153)
146fn main() -> Result<(), Box<dyn std::error::Error>> {
147    let engine = Engine::new()?;
148    // Nothing reaches the queue until this is on, and without the queue there
149    // is no way to know the run ended.
150    engine.set_ui_message_polling_enabled(true)?;
151
152    let sequence_file = engine.new_sequence_file()?;
153    let main_sequence = sequence_file.get_sequence_by_name("MainSequence")?;
154    for (name, data_source, low, high) in TESTS {
155        add_numeric_limit_test(&engine, &main_sequence, name, data_source, low, high)?;
156    }
157
158    let execution = engine.new_execution(&sequence_file, "MainSequence", None, false, 0)?;
159    println!("Running {} headless...", execution.display_name()?);
160
161    if wait_for_end(&engine, RUN_DEADLINE)? {
162        println!("Finished with status: {}", execution.result_status()?);
163        report(&execution.result_object()?)?;
164    } else {
165        // Reported rather than ignored: a host that assumes success here would
166        // publish results from a run that never finished.
167        println!("The run did not finish within {RUN_DEADLINE:?}; terminating.");
168        execution.terminate()?;
169    }
170
171    engine.release_sequence_file_ex(sequence_file, none())?;
172    Ok(())
173}
examples/template_manage_complex.rs (line 137)
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_from_template.rs (line 78)
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}
examples/sequence_build.rs (line 71)
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/variables_manage.rs (line 202)
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}
Source

pub fn get_sequence(&self, index: i32) -> Result<Sequence, Error>

Fetches a sequence by position (GetSequence).

§Errors

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

Source

pub fn save(&self, path: &str) -> Result<(), Error>

Saves the file, optionally to a new path (Save).

An empty path saves in place.

§Errors

Error if the COM call fails.

Examples found in repository?
examples/template_manage_complex.rs (line 195)
176fn main() -> Result<(), Box<dyn std::error::Error>> {
177    let engine = Engine::new()?;
178    describe_station_templates(&engine)?;
179
180    println!("\nBuilding an in-memory template group...");
181    let group = new_template_group(&engine)?;
182    append_template(&group, &step_template(&engine)?)?;
183    append_template(&group, &sequence_template(&engine)?)?;
184    append_template(&group, &variable_template(&engine)?)?;
185    println!("  {} template(s) stored", group.get_num_elements()?);
186
187    let step = require(find_template(&group, STEP_TEMPLATE)?, STEP_TEMPLATE)?;
188    let sequence = require(find_template(&group, SEQUENCE_TEMPLATE)?, SEQUENCE_TEMPLATE)?;
189    let variable = require(find_template(&group, VARIABLE_TEMPLATE)?, VARIABLE_TEMPLATE)?;
190
191    // Saving first, then reopening, is deliberate: templates are worth having
192    // because they are applied to files a program did not build in this run.
193    let path = std::env::temp_dir().join("rs_teststand_from_templates.seq");
194    let path = path.to_string_lossy().into_owned();
195    engine.new_sequence_file()?.save(&path)?;
196
197    println!("\nApplying templates to the saved file...");
198    let target = engine.get_sequence_file_ex(
199        &path,
200        GetSeqFileOptions::DO_NOT_RUN_LOAD_CALLBACK,
201        rs_teststand::ConflictHandler::Error,
202    )?;
203    apply_templates(&target, &step, &sequence, &variable)?;
204    target.save(&path)?;
205    println!("\nSaved to {path}");
206
207    engine.release_sequence_file_ex(target, PropertyOptions::NONE.bits())?;
208    Ok(())
209}
More examples
Hide additional examples
examples/step_insert_from_template.rs (line 110)
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}
examples/sequence_build.rs (line 119)
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/data_type_manage.rs (line 171)
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}
examples/step_insert.rs (line 143)
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}

Trait Implementations§

Source§

impl Debug for SequenceFile

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.