pub struct UIMessage { /* private fields */ }Expand description
One message from the engine’s queue (UIMessage).
A running sequence reports what it is doing by posting these: stage descriptions, progress, results. A graphical host receives them through the UI controls; a headless one, a service, a test runner, anything speaking to another process, polls the queue instead.
Every message must be acknowledged. See
acknowledge: skipping it blocks a synchronous poster
indefinitely and stops the engine delivering anything further.
Implementations§
Source§impl UIMessage
impl UIMessage
Sourcepub fn event(&self) -> Result<i32, Error>
pub fn event(&self) -> Result<i32, Error>
The raw code identifying what this message reports (UIMessage.Event).
Use code to resolve it to a named engine code.
§Errors
Error if the COM call fails or returns an unexpected type.
Examples found in repository?
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}More examples
88fn wait_for_end(engine: &Engine, deadline: Duration) -> Result<bool, Error> {
89 let started = Instant::now();
90 while started.elapsed() < deadline {
91 if pump_thread_messages() {
92 return Ok(false);
93 }
94 while !engine.is_ui_message_queue_empty()? {
95 let message = engine.get_ui_message()?;
96 let ended = matches!(
97 UIMessageCode::from_bits(message.event()?),
98 Ok(UIMessageCode::EndExecution)
99 );
100 message.acknowledge()?;
101 if ended {
102 return Ok(true);
103 }
104 }
105 }
106 Ok(false)
107}90fn wait_for_end(engine: &Engine, deadline: Duration) -> Result<bool, Error> {
91 let started = Instant::now();
92 while started.elapsed() < deadline {
93 if pump_thread_messages() {
94 return Ok(false);
95 }
96 while !engine.is_ui_message_queue_empty()? {
97 let message = engine.get_ui_message()?;
98 let ended = matches!(
99 UIMessageCode::from_bits(message.event()?),
100 Ok(UIMessageCode::EndExecution)
101 );
102 // Acknowledging is what releases a synchronous poster; skipping it
103 // stalls the sequence rather than merely losing a notification.
104 message.acknowledge()?;
105 if ended {
106 return Ok(true);
107 }
108 }
109 }
110 Ok(false)
111}40fn main() -> Result<(), Box<dyn std::error::Error>> {
41 let engine = Engine::new()?;
42
43 // Nothing reaches the queue until polling is switched on.
44 engine.set_ui_message_polling_enabled(true)?;
45
46 let sequence_file = engine.new_sequence_file()?;
47 let main_sequence = sequence_file.get_sequence_by_name("MainSequence")?;
48
49 // Posted through the engine and tagged with the execution. Synchronous, so
50 // the sequence waits until the host acknowledges it.
51 add_statement(
52 &engine,
53 &main_sequence,
54 "Report Stage",
55 &format!(
56 "RunState.Engine.PostUIMessage(RunState.Execution, RunState.Thread, {STAGE_MESSAGE}, \
57 1, \"stage: configuring instruments\", Nothing, True)"
58 ),
59 )?;
60 // Posted through the thread. Asynchronous, so the sequence carries on.
61 add_statement(
62 &engine,
63 &main_sequence,
64 "Report Progress",
65 &format!(
66 "RunState.Thread.PostUIMessageEx({PROGRESS_MESSAGE}, 50, \"progress: halfway\", \
67 Nothing, False)"
68 ),
69 )?;
70
71 let execution = engine.new_execution(&sequence_file, "MainSequence", None, false, 0)?;
72 println!(
73 "Execution {} started; polling for messages...",
74 execution.id()?
75 );
76
77 // The end is detected from the message stream, not by waiting on the
78 // execution: a wait call does not pump the queue, so a synchronous message
79 // would sit unacknowledged and both sides would stop.
80 let started = Instant::now();
81 let mut ended = false;
82 while started.elapsed() < Duration::from_secs(60) && !ended {
83 while !engine.is_ui_message_queue_empty()? {
84 let message = engine.get_ui_message()?;
85 let code = message.event()?;
86 if matches!(
87 UIMessageCode::from_bits(code),
88 Ok(UIMessageCode::EndExecution)
89 ) {
90 ended = true;
91 }
92 let origin = if UIMessageCode::is_user_message(code) {
93 "sequence".to_owned()
94 } else {
95 format!("engine {:?}", UIMessageCode::from_bits(code))
96 };
97 println!(
98 " [{origin}] code={code} numeric={} string={:?} synchronous={}",
99 message.numeric_data()?,
100 message.string_data()?,
101 message.is_synchronous()?
102 );
103 // Required: releases a synchronous poster and asks for the next.
104 message.acknowledge()?;
105 }
106 }
107
108 println!("\nExecution ended: {ended}");
109 engine.release_sequence_file_ex(sequence_file, NO_OPTIONS)?;
110 Ok(())
111}Sourcepub fn is_synchronous(&self) -> Result<bool, Error>
pub fn is_synchronous(&self) -> Result<bool, Error>
Whether the posting thread is blocked until this is acknowledged
(UIMessage.IsSynchronous).
§Errors
Error if the COM call fails or returns an unexpected type.
Examples found in repository?
40fn main() -> Result<(), Box<dyn std::error::Error>> {
41 let engine = Engine::new()?;
42
43 // Nothing reaches the queue until polling is switched on.
44 engine.set_ui_message_polling_enabled(true)?;
45
46 let sequence_file = engine.new_sequence_file()?;
47 let main_sequence = sequence_file.get_sequence_by_name("MainSequence")?;
48
49 // Posted through the engine and tagged with the execution. Synchronous, so
50 // the sequence waits until the host acknowledges it.
51 add_statement(
52 &engine,
53 &main_sequence,
54 "Report Stage",
55 &format!(
56 "RunState.Engine.PostUIMessage(RunState.Execution, RunState.Thread, {STAGE_MESSAGE}, \
57 1, \"stage: configuring instruments\", Nothing, True)"
58 ),
59 )?;
60 // Posted through the thread. Asynchronous, so the sequence carries on.
61 add_statement(
62 &engine,
63 &main_sequence,
64 "Report Progress",
65 &format!(
66 "RunState.Thread.PostUIMessageEx({PROGRESS_MESSAGE}, 50, \"progress: halfway\", \
67 Nothing, False)"
68 ),
69 )?;
70
71 let execution = engine.new_execution(&sequence_file, "MainSequence", None, false, 0)?;
72 println!(
73 "Execution {} started; polling for messages...",
74 execution.id()?
75 );
76
77 // The end is detected from the message stream, not by waiting on the
78 // execution: a wait call does not pump the queue, so a synchronous message
79 // would sit unacknowledged and both sides would stop.
80 let started = Instant::now();
81 let mut ended = false;
82 while started.elapsed() < Duration::from_secs(60) && !ended {
83 while !engine.is_ui_message_queue_empty()? {
84 let message = engine.get_ui_message()?;
85 let code = message.event()?;
86 if matches!(
87 UIMessageCode::from_bits(code),
88 Ok(UIMessageCode::EndExecution)
89 ) {
90 ended = true;
91 }
92 let origin = if UIMessageCode::is_user_message(code) {
93 "sequence".to_owned()
94 } else {
95 format!("engine {:?}", UIMessageCode::from_bits(code))
96 };
97 println!(
98 " [{origin}] code={code} numeric={} string={:?} synchronous={}",
99 message.numeric_data()?,
100 message.string_data()?,
101 message.is_synchronous()?
102 );
103 // Required: releases a synchronous poster and asks for the next.
104 message.acknowledge()?;
105 }
106 }
107
108 println!("\nExecution ended: {ended}");
109 engine.release_sequence_file_ex(sequence_file, NO_OPTIONS)?;
110 Ok(())
111}Sourcepub fn numeric_data(&self) -> Result<f64, Error>
pub fn numeric_data(&self) -> Result<f64, Error>
The numeric payload (UIMessage.NumericData).
§Errors
Error if the COM call fails or returns an unexpected type.
Examples found in repository?
40fn main() -> Result<(), Box<dyn std::error::Error>> {
41 let engine = Engine::new()?;
42
43 // Nothing reaches the queue until polling is switched on.
44 engine.set_ui_message_polling_enabled(true)?;
45
46 let sequence_file = engine.new_sequence_file()?;
47 let main_sequence = sequence_file.get_sequence_by_name("MainSequence")?;
48
49 // Posted through the engine and tagged with the execution. Synchronous, so
50 // the sequence waits until the host acknowledges it.
51 add_statement(
52 &engine,
53 &main_sequence,
54 "Report Stage",
55 &format!(
56 "RunState.Engine.PostUIMessage(RunState.Execution, RunState.Thread, {STAGE_MESSAGE}, \
57 1, \"stage: configuring instruments\", Nothing, True)"
58 ),
59 )?;
60 // Posted through the thread. Asynchronous, so the sequence carries on.
61 add_statement(
62 &engine,
63 &main_sequence,
64 "Report Progress",
65 &format!(
66 "RunState.Thread.PostUIMessageEx({PROGRESS_MESSAGE}, 50, \"progress: halfway\", \
67 Nothing, False)"
68 ),
69 )?;
70
71 let execution = engine.new_execution(&sequence_file, "MainSequence", None, false, 0)?;
72 println!(
73 "Execution {} started; polling for messages...",
74 execution.id()?
75 );
76
77 // The end is detected from the message stream, not by waiting on the
78 // execution: a wait call does not pump the queue, so a synchronous message
79 // would sit unacknowledged and both sides would stop.
80 let started = Instant::now();
81 let mut ended = false;
82 while started.elapsed() < Duration::from_secs(60) && !ended {
83 while !engine.is_ui_message_queue_empty()? {
84 let message = engine.get_ui_message()?;
85 let code = message.event()?;
86 if matches!(
87 UIMessageCode::from_bits(code),
88 Ok(UIMessageCode::EndExecution)
89 ) {
90 ended = true;
91 }
92 let origin = if UIMessageCode::is_user_message(code) {
93 "sequence".to_owned()
94 } else {
95 format!("engine {:?}", UIMessageCode::from_bits(code))
96 };
97 println!(
98 " [{origin}] code={code} numeric={} string={:?} synchronous={}",
99 message.numeric_data()?,
100 message.string_data()?,
101 message.is_synchronous()?
102 );
103 // Required: releases a synchronous poster and asks for the next.
104 message.acknowledge()?;
105 }
106 }
107
108 println!("\nExecution ended: {ended}");
109 engine.release_sequence_file_ex(sequence_file, NO_OPTIONS)?;
110 Ok(())
111}Sourcepub fn string_data(&self) -> Result<String, Error>
pub fn string_data(&self) -> Result<String, Error>
The string payload (UIMessage.StringData).
§Errors
Error if the COM call fails or returns an unexpected type.
Examples found in repository?
40fn main() -> Result<(), Box<dyn std::error::Error>> {
41 let engine = Engine::new()?;
42
43 // Nothing reaches the queue until polling is switched on.
44 engine.set_ui_message_polling_enabled(true)?;
45
46 let sequence_file = engine.new_sequence_file()?;
47 let main_sequence = sequence_file.get_sequence_by_name("MainSequence")?;
48
49 // Posted through the engine and tagged with the execution. Synchronous, so
50 // the sequence waits until the host acknowledges it.
51 add_statement(
52 &engine,
53 &main_sequence,
54 "Report Stage",
55 &format!(
56 "RunState.Engine.PostUIMessage(RunState.Execution, RunState.Thread, {STAGE_MESSAGE}, \
57 1, \"stage: configuring instruments\", Nothing, True)"
58 ),
59 )?;
60 // Posted through the thread. Asynchronous, so the sequence carries on.
61 add_statement(
62 &engine,
63 &main_sequence,
64 "Report Progress",
65 &format!(
66 "RunState.Thread.PostUIMessageEx({PROGRESS_MESSAGE}, 50, \"progress: halfway\", \
67 Nothing, False)"
68 ),
69 )?;
70
71 let execution = engine.new_execution(&sequence_file, "MainSequence", None, false, 0)?;
72 println!(
73 "Execution {} started; polling for messages...",
74 execution.id()?
75 );
76
77 // The end is detected from the message stream, not by waiting on the
78 // execution: a wait call does not pump the queue, so a synchronous message
79 // would sit unacknowledged and both sides would stop.
80 let started = Instant::now();
81 let mut ended = false;
82 while started.elapsed() < Duration::from_secs(60) && !ended {
83 while !engine.is_ui_message_queue_empty()? {
84 let message = engine.get_ui_message()?;
85 let code = message.event()?;
86 if matches!(
87 UIMessageCode::from_bits(code),
88 Ok(UIMessageCode::EndExecution)
89 ) {
90 ended = true;
91 }
92 let origin = if UIMessageCode::is_user_message(code) {
93 "sequence".to_owned()
94 } else {
95 format!("engine {:?}", UIMessageCode::from_bits(code))
96 };
97 println!(
98 " [{origin}] code={code} numeric={} string={:?} synchronous={}",
99 message.numeric_data()?,
100 message.string_data()?,
101 message.is_synchronous()?
102 );
103 // Required: releases a synchronous poster and asks for the next.
104 message.acknowledge()?;
105 }
106 }
107
108 println!("\nExecution ended: {ended}");
109 engine.release_sequence_file_ex(sequence_file, NO_OPTIONS)?;
110 Ok(())
111}Sourcepub fn activex_data(&self) -> Result<Option<PropertyObject>, Error>
pub fn activex_data(&self) -> Result<Option<PropertyObject>, Error>
The object posted with this message (UIMessage.ActiveXData).
The third payload slot, beside
numeric_data and
string_data, and the only one that can carry
structured data. A sequence builds a container, fills it in, and posts a
reference to it; the host reads the tree back here instead of agreeing
on a string format with the sequence author.
None means the slot was left empty, which is the common case. It is not
only for messages a sequence posts, though: the engine fills the slot for
some of its own, including
StartFileExecution and
EndFileExecution, so a host
that reads the slot has to expect an object on messages it did not post.
Returned as a PropertyObject because that is what a sequence can
construct and post. The slot is declared to hold any object, so a poster
working from a code module could put something else in it; the reference
would still arrive, but property-tree calls on it would fail.
§This reference cannot leave the process
What arrives here is a COM interface pointer, valid only in the address space that received it. Forwarding it over gRPC, a socket or a pipe sends a number that means nothing on the other side, and a front end in another process cannot dereference it however it is encoded.
A host bridging to another process has to turn the tree into data before
it crosses, which is what rs-teststand-serde is for: read the object
here, serialize it, send the document. The receiver then needs no COM, no
engine, and no TestStand™ installation to read what the sequence sent.
§Errors
Error if the COM call fails, or if the object does not support
IDispatch and so cannot be used through this crate.
Sourcepub fn execution(&self) -> Result<Option<Execution>, Error>
pub fn execution(&self) -> Result<Option<Execution>, Error>
The execution that posted this, when there is one
(UIMessage.Execution).
Returns None for a message not tied to an execution, engine notices
about the station rather than about a run.
This is an Execution, not a property tree: its identifier comes from
Execution::id, not from a lookup path. Reading it as a property
object dispatches property-tree calls at an interface that does not
implement them.
§Errors
Error if the COM call fails or returns an unexpected type.
Sourcepub fn as_property_object(&self) -> Result<PropertyObject, Error>
pub fn as_property_object(&self) -> Result<PropertyObject, Error>
Sourcepub fn acknowledge(&self) -> Result<(), Error>
pub fn acknowledge(&self) -> Result<(), Error>
Signals that the host has finished with this message
(UIMessage.Acknowledge).
Two things depend on it. A synchronous message blocks the thread that posted it until this is called, and the engine treats it as the signal that the host is ready for the next message, so a queue that stops delivering is usually one that was not acknowledged.
§Errors
Error if the COM call fails.
Examples found in repository?
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}More examples
88fn wait_for_end(engine: &Engine, deadline: Duration) -> Result<bool, Error> {
89 let started = Instant::now();
90 while started.elapsed() < deadline {
91 if pump_thread_messages() {
92 return Ok(false);
93 }
94 while !engine.is_ui_message_queue_empty()? {
95 let message = engine.get_ui_message()?;
96 let ended = matches!(
97 UIMessageCode::from_bits(message.event()?),
98 Ok(UIMessageCode::EndExecution)
99 );
100 message.acknowledge()?;
101 if ended {
102 return Ok(true);
103 }
104 }
105 }
106 Ok(false)
107}90fn wait_for_end(engine: &Engine, deadline: Duration) -> Result<bool, Error> {
91 let started = Instant::now();
92 while started.elapsed() < deadline {
93 if pump_thread_messages() {
94 return Ok(false);
95 }
96 while !engine.is_ui_message_queue_empty()? {
97 let message = engine.get_ui_message()?;
98 let ended = matches!(
99 UIMessageCode::from_bits(message.event()?),
100 Ok(UIMessageCode::EndExecution)
101 );
102 // Acknowledging is what releases a synchronous poster; skipping it
103 // stalls the sequence rather than merely losing a notification.
104 message.acknowledge()?;
105 if ended {
106 return Ok(true);
107 }
108 }
109 }
110 Ok(false)
111}40fn main() -> Result<(), Box<dyn std::error::Error>> {
41 let engine = Engine::new()?;
42
43 // Nothing reaches the queue until polling is switched on.
44 engine.set_ui_message_polling_enabled(true)?;
45
46 let sequence_file = engine.new_sequence_file()?;
47 let main_sequence = sequence_file.get_sequence_by_name("MainSequence")?;
48
49 // Posted through the engine and tagged with the execution. Synchronous, so
50 // the sequence waits until the host acknowledges it.
51 add_statement(
52 &engine,
53 &main_sequence,
54 "Report Stage",
55 &format!(
56 "RunState.Engine.PostUIMessage(RunState.Execution, RunState.Thread, {STAGE_MESSAGE}, \
57 1, \"stage: configuring instruments\", Nothing, True)"
58 ),
59 )?;
60 // Posted through the thread. Asynchronous, so the sequence carries on.
61 add_statement(
62 &engine,
63 &main_sequence,
64 "Report Progress",
65 &format!(
66 "RunState.Thread.PostUIMessageEx({PROGRESS_MESSAGE}, 50, \"progress: halfway\", \
67 Nothing, False)"
68 ),
69 )?;
70
71 let execution = engine.new_execution(&sequence_file, "MainSequence", None, false, 0)?;
72 println!(
73 "Execution {} started; polling for messages...",
74 execution.id()?
75 );
76
77 // The end is detected from the message stream, not by waiting on the
78 // execution: a wait call does not pump the queue, so a synchronous message
79 // would sit unacknowledged and both sides would stop.
80 let started = Instant::now();
81 let mut ended = false;
82 while started.elapsed() < Duration::from_secs(60) && !ended {
83 while !engine.is_ui_message_queue_empty()? {
84 let message = engine.get_ui_message()?;
85 let code = message.event()?;
86 if matches!(
87 UIMessageCode::from_bits(code),
88 Ok(UIMessageCode::EndExecution)
89 ) {
90 ended = true;
91 }
92 let origin = if UIMessageCode::is_user_message(code) {
93 "sequence".to_owned()
94 } else {
95 format!("engine {:?}", UIMessageCode::from_bits(code))
96 };
97 println!(
98 " [{origin}] code={code} numeric={} string={:?} synchronous={}",
99 message.numeric_data()?,
100 message.string_data()?,
101 message.is_synchronous()?
102 );
103 // Required: releases a synchronous poster and asks for the next.
104 message.acknowledge()?;
105 }
106 }
107
108 println!("\nExecution ended: {ended}");
109 engine.release_sequence_file_ex(sequence_file, NO_OPTIONS)?;
110 Ok(())
111}