pub struct Step { /* private fields */ }Expand description
One step of a sequence (Step).
Built by Engine::new_step and placed with
Sequence::insert_step.
This type carries the properties every step has, whatever its type. Anything
specific to a step type, a numeric limit test’s limits, for instance, /// lives in the property tree reached through
as_property_object.
Implementations§
Source§impl Step
impl Step
Sourcepub fn name(&self) -> Result<String, Error>
pub fn name(&self) -> Result<String, Error>
Examples found in repository?
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
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}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}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}Sourcepub fn set_name(&self, name: &str) -> Result<(), Error>
pub fn set_name(&self, name: &str) -> Result<(), Error>
Examples found in repository?
102fn step_template(engine: &Engine) -> Result<PropertyObject, Error> {
103 let step = engine.new_step(NO_ADAPTER, "Statement")?;
104 step.set_name(STEP_TEMPLATE)?;
105 step.set_post_expression(r#"Locals.Result = "Hello from step template!""#)?;
106 step.as_property_object()
107}
108
109/// A whole sequence, one step included, as a single prototype.
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}
120
121/// A string variable carrying its default value.
122fn variable_template(engine: &Engine) -> Result<PropertyObject, Error> {
123 let variable =
124 engine.new_property_object(PropValType::String, false, "", PropertyOptions::NONE.bits())?;
125 variable.set_name(VARIABLE_TEMPLATE)?;
126 variable.set_val_string("", PropertyOptions::NONE.bits(), "Template Variable Value")?;
127 Ok(variable)
128}
129
130/// Stamps one copy of each template into a file that is already on disk.
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
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}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}28fn numeric_limit_test(
29 engine: &Engine,
30 name: &str,
31 precondition: &str,
32 low: f64,
33 high: f64,
34) -> Result<Step, rs_teststand::Error> {
35 let step = engine.new_step(NO_ADAPTER, "NumericLimitTest")?;
36 step.set_name(name)?;
37 step.set_precondition(precondition)?;
38 step.set_record_result(true)?;
39
40 let properties = step.as_property_object()?;
41 properties.set_val_number("Limits.High", INSERT_IF_MISSING, high)?;
42 properties.set_val_number("Limits.Low", INSERT_IF_MISSING, low)?;
43 Ok(step)
44}
45
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}51fn build_prototype(engine: &Engine) -> Result<Step, Error> {
52 // The maintained LabVIEW adapter. The standard-prototype key still exists
53 // but the documentation marks it obsolete in favour of this one.
54 let step = engine.new_step(AdapterKeyName::LabView.as_str(), "Action")?;
55 step.set_name("Measure (VI)")?;
56 step.set_run_mode(RunMode::Normal)?;
57 step.as_property_object()?.set_val_string(
58 VI_PATH,
59 PropertyOptions::INSERT_IF_MISSING.bits(),
60 r"example.lvlibp\measure.vi",
61 )?;
62 Ok(step)
63}
64
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}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}Sourcepub fn precondition(&self) -> Result<String, Error>
pub fn precondition(&self) -> Result<String, Error>
The expression deciding whether the step runs (Step.Precondition).
An empty precondition means the step always runs.
§Errors
Error if the COM call fails or returns an unexpected type.
Examples found in repository?
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}Sourcepub fn set_precondition(&self, expression: &str) -> Result<(), Error>
pub fn set_precondition(&self, expression: &str) -> Result<(), Error>
Sets the precondition expression (Step.Precondition).
The text is not checked here; a precondition that does not parse fails when the sequence runs, not when it is set.
§Errors
Error if the COM call fails.
Examples found in repository?
28fn numeric_limit_test(
29 engine: &Engine,
30 name: &str,
31 precondition: &str,
32 low: f64,
33 high: f64,
34) -> Result<Step, rs_teststand::Error> {
35 let step = engine.new_step(NO_ADAPTER, "NumericLimitTest")?;
36 step.set_name(name)?;
37 step.set_precondition(precondition)?;
38 step.set_record_result(true)?;
39
40 let properties = step.as_property_object()?;
41 properties.set_val_number("Limits.High", INSERT_IF_MISSING, high)?;
42 properties.set_val_number("Limits.Low", INSERT_IF_MISSING, low)?;
43 Ok(step)
44}Sourcepub fn run_mode(&self) -> Result<Option<RunMode>, Error>
pub fn run_mode(&self) -> Result<Option<RunMode>, Error>
What the engine does with the step when it reaches it (Step.RunMode).
None means the engine reported a mode this build does not name, which
is worth telling apart from a failure to read it at all.
§Errors
Error if the COM call fails or returns an unexpected type.
Examples found in repository?
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}More examples
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}Sourcepub fn set_run_mode(&self, mode: RunMode) -> Result<(), Error>
pub fn set_run_mode(&self, mode: RunMode) -> Result<(), Error>
Examples found in repository?
51fn build_prototype(engine: &Engine) -> Result<Step, Error> {
52 // The maintained LabVIEW adapter. The standard-prototype key still exists
53 // but the documentation marks it obsolete in favour of this one.
54 let step = engine.new_step(AdapterKeyName::LabView.as_str(), "Action")?;
55 step.set_name("Measure (VI)")?;
56 step.set_run_mode(RunMode::Normal)?;
57 step.as_property_object()?.set_val_string(
58 VI_PATH,
59 PropertyOptions::INSERT_IF_MISSING.bits(),
60 r"example.lvlibp\measure.vi",
61 )?;
62 Ok(step)
63}More examples
37fn vi_call_step(
38 engine: &Engine,
39 name: &str,
40 vi_path: &str,
41 project_path: &str,
42 run_mode: RunMode,
43) -> Result<Step, Error> {
44 let step = engine.new_step(AdapterKeyName::LabView.as_str(), "Action")?;
45 step.set_name(name)?;
46 step.set_run_mode(run_mode)?;
47 // Recorded explicitly rather than left to the default, so the step shows up
48 // in the result list a report is built from.
49 step.set_result_recording_option(ResultRecordingOption::Enabled)?;
50
51 let properties = step.as_property_object()?;
52 properties.set_val_string(VI_PATH, insert_if_missing(), vi_path)?;
53 if !project_path.is_empty() {
54 properties.set_val_string(PROJECT_PATH, insert_if_missing(), project_path)?;
55 }
56 Ok(step)
57}Sourcepub fn adapter_key_name(&self) -> Result<Option<AdapterKeyName>, Error>
pub fn adapter_key_name(&self) -> Result<Option<AdapterKeyName>, Error>
The adapter the step calls its code module through
(Step.AdapterKeyName).
None means the engine reported a key this build does not name, or the
step calls no code module at all.
§Errors
Error if the COM call fails or returns an unexpected type.
Examples found in repository?
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}More examples
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}Sourcepub fn post_expression(&self) -> Result<String, Error>
pub fn post_expression(&self) -> Result<String, Error>
Sourcepub fn set_post_expression(&self, expression: &str) -> Result<(), Error>
pub fn set_post_expression(&self, expression: &str) -> Result<(), Error>
Sourcepub fn create_new_unique_step_id(&self) -> Result<(), Error>
pub fn create_new_unique_step_id(&self) -> Result<(), Error>
Gives the step a fresh unique identity (Step.CreateNewUniqueStepId).
A copy of a step carries the original’s step ID, so a sequence built by
cloning a prototype ends up with several steps claiming the same
identity. Anything that refers to a step by ID, a result, a report
entry, a GoTo, then cannot tell them apart. Call this on each copy.
§Errors
Error if the COM call fails.
Examples found in repository?
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
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}Sourcepub fn result_recording_option(&self) -> Result<ResultRecordingOption, Error>
pub fn result_recording_option(&self) -> Result<ResultRecordingOption, Error>
Whether this step contributes an entry to the result list
(Step.ResultRecordingOption).
Distinct from record_result, the plain on/off
switch: this one can also say “record even when the sequence says not
to”. A step set to Disabled
leaves no entry in ResultList, which is the usual reason a parsed
report is shorter than the sequence that produced it.
§Errors
Error if the COM call fails or the engine reports an unnamed value.
Examples found in repository?
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}Sourcepub fn set_result_recording_option(
&self,
option: ResultRecordingOption,
) -> Result<(), Error>
pub fn set_result_recording_option( &self, option: ResultRecordingOption, ) -> Result<(), Error>
Sets whether this step records a result (Step.ResultRecordingOption).
§Errors
Error if the COM call fails.
Examples found in repository?
More examples
37fn vi_call_step(
38 engine: &Engine,
39 name: &str,
40 vi_path: &str,
41 project_path: &str,
42 run_mode: RunMode,
43) -> Result<Step, Error> {
44 let step = engine.new_step(AdapterKeyName::LabView.as_str(), "Action")?;
45 step.set_name(name)?;
46 step.set_run_mode(run_mode)?;
47 // Recorded explicitly rather than left to the default, so the step shows up
48 // in the result list a report is built from.
49 step.set_result_recording_option(ResultRecordingOption::Enabled)?;
50
51 let properties = step.as_property_object()?;
52 properties.set_val_string(VI_PATH, insert_if_missing(), vi_path)?;
53 if !project_path.is_empty() {
54 properties.set_val_string(PROJECT_PATH, insert_if_missing(), project_path)?;
55 }
56 Ok(step)
57}Sourcepub fn record_result(&self) -> Result<bool, Error>
pub fn record_result(&self) -> Result<bool, Error>
Sourcepub fn set_record_result(&self, record: bool) -> Result<(), Error>
pub fn set_record_result(&self, record: bool) -> Result<(), Error>
Sets whether the step’s result is recorded (Step.RecordResult).
§Errors
Error if the COM call fails.
Examples found in repository?
28fn numeric_limit_test(
29 engine: &Engine,
30 name: &str,
31 precondition: &str,
32 low: f64,
33 high: f64,
34) -> Result<Step, rs_teststand::Error> {
35 let step = engine.new_step(NO_ADAPTER, "NumericLimitTest")?;
36 step.set_name(name)?;
37 step.set_precondition(precondition)?;
38 step.set_record_result(true)?;
39
40 let properties = step.as_property_object()?;
41 properties.set_val_number("Limits.High", INSERT_IF_MISSING, high)?;
42 properties.set_val_number("Limits.Low", INSERT_IF_MISSING, low)?;
43 Ok(step)
44}More examples
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}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}Sourcepub fn as_property_object(&self) -> Result<PropertyObject, Error>
pub fn as_property_object(&self) -> Result<PropertyObject, Error>
The step as a property tree (Step.AsPropertyObject).
Type-specific settings live here, addressed by lookup path, /// Limits.High on a numeric limit test, for instance.
§Errors
Error if the COM call fails or returns an unexpected type.
Examples found in repository?
More examples
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}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}28fn numeric_limit_test(
29 engine: &Engine,
30 name: &str,
31 precondition: &str,
32 low: f64,
33 high: f64,
34) -> Result<Step, rs_teststand::Error> {
35 let step = engine.new_step(NO_ADAPTER, "NumericLimitTest")?;
36 step.set_name(name)?;
37 step.set_precondition(precondition)?;
38 step.set_record_result(true)?;
39
40 let properties = step.as_property_object()?;
41 properties.set_val_number("Limits.High", INSERT_IF_MISSING, high)?;
42 properties.set_val_number("Limits.Low", INSERT_IF_MISSING, low)?;
43 Ok(step)
44}
45
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}51fn build_prototype(engine: &Engine) -> Result<Step, Error> {
52 // The maintained LabVIEW adapter. The standard-prototype key still exists
53 // but the documentation marks it obsolete in favour of this one.
54 let step = engine.new_step(AdapterKeyName::LabView.as_str(), "Action")?;
55 step.set_name("Measure (VI)")?;
56 step.set_run_mode(RunMode::Normal)?;
57 step.as_property_object()?.set_val_string(
58 VI_PATH,
59 PropertyOptions::INSERT_IF_MISSING.bits(),
60 r"example.lvlibp\measure.vi",
61 )?;
62 Ok(step)
63}
64
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}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}Sourcepub fn step_type(&self) -> Result<PropertyObject, Error>
pub fn step_type(&self) -> Result<PropertyObject, Error>
Sourcepub fn break_on_step(&self) -> Result<bool, Error>
pub fn break_on_step(&self) -> Result<bool, Error>
Whether this step carries a breakpoint (Step.BreakOnStep).
Reads the step itself. To ask about one run instead, use
break_on_step_for.
True here does not mean a run will stop. Breakpoints are only honored
while they are switched on, which
Engine::breakpoints_enabled
controls for the session.
§Errors
Error if the COM call fails or returns an unexpected type.
Sourcepub fn break_on_step_for(
&self,
scope: BreakpointScope<'_>,
) -> Result<bool, Error>
pub fn break_on_step_for( &self, scope: BreakpointScope<'_>, ) -> Result<bool, Error>
Sourcepub fn set_break_on_step(
&self,
enabled: bool,
scope: BreakpointScope<'_>,
) -> Result<(), Error>
pub fn set_break_on_step( &self, enabled: bool, scope: BreakpointScope<'_>, ) -> Result<(), Error>
Sets or clears the breakpoint on this step (Step.SetBreakOnStepEx).
The scope decides how long it lasts.
BreakpointScope::Step writes it into
the step, so it survives the run and is saved with the sequence file.
BreakpointScope::Execution scopes
it to one run and leaves the file alone, which is what a host debugging
for a remote panel should use.
A stop announces itself as
UIMessageCode::BreakOnBreakpoint,
which arrived about 300 ms after the run started in a live measurement.
Continue with Execution::resume, not
Thread::resume, which does not release a breakpoint stop.
§Errors
Error if the COM call fails.
Sourcepub fn set_break_settings(
&self,
is_set: bool,
enabled: bool,
pass_count: i32,
condition: &str,
scope: BreakpointScope<'_>,
) -> Result<(), Error>
pub fn set_break_settings( &self, is_set: bool, enabled: bool, pass_count: i32, condition: &str, scope: BreakpointScope<'_>, ) -> Result<(), Error>
Sets a breakpoint together with its pass count and condition
(Step.SetBreakSettings).
is_set places or removes the breakpoint and enabled decides whether
it is armed, so a breakpoint can stay in place while switched off.
pass_count stops on the nth arrival rather than the first.
condition is an expression the engine evaluates when it arrives; an
empty string means stop unconditionally.
Reading these back needs Step.GetBreakSettings, which returns
everything through [out] parameters and is not wrapped yet.
§Errors
Error if the COM call fails.