pub struct PropertyObjectFile { /* private fields */ }Expand description
A file holding property objects (PropertyObjectFile).
The file view of something that also has a richer identity, a sequence file
reached through as_property_object_file, or a workspace’s options file.
This is where a file’s registered types live.
Implementations§
Source§impl PropertyObjectFile
impl PropertyObjectFile
Sourcepub fn save_file_if_modified(&self, prompt: bool) -> Result<bool, Error>
pub fn save_file_if_modified(&self, prompt: bool) -> Result<bool, Error>
Writes the file to disk if it has changed
(PropertyObjectFile.SaveFileIfModified).
Does nothing when the file is unmodified. The path written is whatever
path reports.
Pass prompt = false from a host with no operator. With true the
engine puts a dialog on screen offering to save, and a headless caller
would block on a question nobody can answer. The returned false means
only that someone declined at that dialog, so under prompt = false a
false should not happen.
§Errors
Error if the COM call fails or returns an unexpected type.
Sourcepub fn type_usage_list(&self) -> Result<TypeUsageList, Error>
pub fn type_usage_list(&self) -> Result<TypeUsageList, Error>
The types registered in this file (TypeUsageList).
§Errors
Error if the COM call fails or returns an unexpected type.
Examples found in repository?
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}Sourcepub fn inc_change_count(&self) -> Result<(), Error>
pub fn inc_change_count(&self) -> Result<(), Error>
Marks the file as modified (IncChangeCount).
Saving does nothing when the file does not believe it has changed, so a change made through the API needs this before the save will write.
§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
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}Sourcepub fn data(&self) -> Result<PropertyObject, Error>
pub fn data(&self) -> Result<PropertyObject, Error>
The root of the file’s property tree (Data).
Everything a file stores hangs off here, which is how a file with no richer identity, the templates file, for one, is read at all.
§Errors
Error if the COM call fails or returns an unexpected type.
Examples found in repository?
33fn describe_templates(engine: &Engine) -> Result<(), Error> {
34 let templates_file = engine.get_templates_file(GetTemplatesFileOptions::LOAD_IF_NOT_LOADED)?;
35 let root = templates_file
36 .data()?
37 .get_property_object("Root", PropertyOptions::NONE.bits())?;
38 for index in 0..root.get_num_elements()? {
39 let category = root.get_property_object_by_offset(index, PropertyOptions::NONE.bits())?;
40 if category.name()? == "Steps" {
41 println!(
42 "Step templates defined on this station: {}",
43 category.get_num_elements()?
44 );
45 }
46 }
47 Ok(())
48}More examples
40fn describe_station_templates(engine: &Engine) -> Result<(), Error> {
41 let templates_file = engine.get_templates_file(GetTemplatesFileOptions::LOAD_IF_NOT_LOADED)?;
42 let root = templates_file
43 .data()?
44 .get_property_object("Root", PropertyOptions::NONE.bits())?;
45
46 println!("Station templates file: {}", templates_file.path()?);
47 for index in 0..root.get_num_elements()? {
48 let category = root.get_property_object_by_offset(index, PropertyOptions::NONE.bits())?;
49 println!(
50 " {}: {} template(s)",
51 category.name()?,
52 category.get_num_elements()?
53 );
54 }
55 Ok(())
56}Sourcepub fn path(&self) -> Result<String, Error>
pub fn path(&self) -> Result<String, Error>
Examples found in repository?
40fn describe_station_templates(engine: &Engine) -> Result<(), Error> {
41 let templates_file = engine.get_templates_file(GetTemplatesFileOptions::LOAD_IF_NOT_LOADED)?;
42 let root = templates_file
43 .data()?
44 .get_property_object("Root", PropertyOptions::NONE.bits())?;
45
46 println!("Station templates file: {}", templates_file.path()?);
47 for index in 0..root.get_num_elements()? {
48 let category = root.get_property_object_by_offset(index, PropertyOptions::NONE.bits())?;
49 println!(
50 " {}: {} template(s)",
51 category.name()?,
52 category.get_num_elements()?
53 );
54 }
55 Ok(())
56}More examples
29fn main() -> Result<(), rs_teststand::Error> {
30 let engine = Engine::new()?;
31
32 // Passing no profile means the user inherits nothing.
33 let user = engine.new_user(None)?;
34 user.set_login_name("operator1")?;
35 user.set_full_name("Test Operator")?;
36 user.set_password("ts-secret")?;
37
38 println!("User: {} ({})", user.login_name()?, user.full_name()?);
39
40 // Check a credential without handling the stored value.
41 println!(
42 " password 'ts-secret' valid: {}",
43 user.validate_password("ts-secret")?
44 );
45 println!(
46 " password 'wrong' valid: {}",
47 user.validate_password("wrong")?
48 );
49
50 println!(" privilege checks:");
51 report_privileges(
52 &user,
53 &[
54 UserPrivilege::Operate,
55 UserPrivilege::Execute,
56 UserPrivilege::Develop,
57 UserPrivilege::Debug,
58 UserPrivilege::EditUsers,
59 ],
60 )?;
61
62 // A privilege can also be named by its full path, which is how a leaf
63 // inside a category is reached.
64 println!(
65 " Debug.RunSelectedTests: {}",
66 user.has_privilege_named("Debug.RunSelectedTests")?
67 );
68
69 // The privilege tree is nested: categories hold the individual rights.
70 let privileges = user.privileges()?;
71 let count = privileges.get_num_sub_properties("")?;
72 println!(" privilege tree ({count} categories):");
73 for index in 0..count {
74 let name = privileges.get_nth_sub_property_name("", index, 0)?;
75 let members = privileges
76 .get_property_object(&name, 0)?
77 .get_num_sub_properties("")?;
78 println!(" {name:<12} {members} member(s)");
79 }
80
81 // The station itself, read-only.
82 println!("\nStation:");
83 match engine.current_user()? {
84 Some(current) => println!(" logged in as {}", current.login_name()?),
85 None => println!(" nobody logged in (usual when login is not required)"),
86 }
87 println!(
88 " is 'operator1' a real account here? {}",
89 engine.user_name_exists("operator1")?
90 );
91
92 // Where accounts actually live. Everything above this point exists only in
93 // memory: `new_user` builds a User, it does not enrol one. A user becomes
94 // part of the station when it is inserted into this file's list and the
95 // file is written.
96 let users_file = engine.users_file()?;
97 let file = users_file.as_property_object_file()?;
98 println!(
99 "
100Users file:"
101 );
102 println!(" path {}", file.path()?);
103 println!(
104 " users {}",
105 users_file.user_list()?.get_num_elements()?
106 );
107 println!(
108 " groups {}",
109 users_file.user_group_list()?.get_num_elements()?
110 );
111 println!(
112 " profiles {}",
113 users_file.user_profile_list()?.get_num_elements()?
114 );
115
116 // Deliberately not run: it would edit the station this example is reading.
117 // The two calls a host makes to enrol the user built above are
118 //
119 // users_file.user_list()?.set_num_elements(count + 1, 0)?; // then fill
120 // file.save_file_if_modified(false)?; // then write
121 //
122 // `false` matters: with `true` the engine puts a save dialog on screen, and
123 // a host with no operator would wait for an answer that never comes.
124 println!(
125 "
126 (this example does not write; enrolling a user would alter the station)"
127 );
128
129 Ok(())
130}