Skip to main content

UIMessage

Struct UIMessage 

Source
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

Source

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?
examples/execution_run_subsequence.rs (line 95)
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
Hide additional examples
examples/result_list_parse.rs (line 97)
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}
examples/execution_run_test_headless.rs (line 99)
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}
examples/ui_messages_handle.rs (line 85)
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}
Source

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

The message’s code, resolved.

Err carries the raw number for a message a sequence posted, or one newer than this build knows, both of which are ordinary, not failures.

§Errors

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

Source

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?
examples/ui_messages_handle.rs (line 101)
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}
Source

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?
examples/ui_messages_handle.rs (line 99)
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}
Source

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?
examples/ui_messages_handle.rs (line 100)
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}
Source

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.

Source

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.

Source

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

The thread that posted this, when there is one (UIMessage.Thread).

Returned as a property tree, which is how a thread’s run state is reached.

§Errors

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

Source

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

The message as a property tree (UIMessage.AsPropertyObject).

§Errors

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

Source

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?
examples/execution_run_subsequence.rs (line 98)
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
Hide additional examples
examples/result_list_parse.rs (line 100)
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}
examples/execution_run_test_headless.rs (line 104)
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}
examples/ui_messages_handle.rs (line 104)
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}

Trait Implementations§

Source§

impl Debug for UIMessage

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.