use std::time::{Duration, Instant};
use rs_teststand::{
AdapterKeyName, Engine, Error, PropValType, PropertyOptions, Sequence, SequenceFile, StepGroup,
UIMessageCode, pump_thread_messages,
};
const SUBSEQUENCE: &str = "Diagnostics";
const PARAMETER: &str = "FixtureId";
const RUN_DEADLINE: Duration = Duration::from_secs(60);
const fn none() -> i32 {
PropertyOptions::NONE.bits()
}
const fn insert_if_missing() -> i32 {
PropertyOptions::INSERT_IF_MISSING.bits()
}
fn add_action(engine: &Engine, sequence: &Sequence, name: &str) -> Result<(), Error> {
let step = engine.new_step(AdapterKeyName::NoneAdapter.as_str(), "Action")?;
step.set_name(name)?;
step.set_record_result(true)?;
sequence.insert_step(
&step,
sequence.get_num_steps(StepGroup::Main)?,
StepGroup::Main,
)
}
fn build(engine: &Engine) -> Result<SequenceFile, Error> {
let sequence_file = engine.new_sequence_file()?;
let main_sequence = sequence_file.get_sequence_by_name("MainSequence")?;
for name in ["Initialize", "Run Test", "Shut Down"] {
add_action(engine, &main_sequence, name)?;
}
let diagnostics = engine.new_sequence()?;
diagnostics.set_name(SUBSEQUENCE)?;
diagnostics.parameters()?.new_sub_property(
PARAMETER,
PropValType::String,
false,
"",
insert_if_missing(),
)?;
for name in ["Check Power Rails", "Check Clock"] {
add_action(engine, &diagnostics, name)?;
}
sequence_file.insert_sequence(&diagnostics)?;
Ok(sequence_file)
}
fn wait_for_end(engine: &Engine, deadline: Duration) -> Result<bool, Error> {
let started = Instant::now();
while started.elapsed() < deadline {
if pump_thread_messages() {
return Ok(false);
}
while !engine.is_ui_message_queue_empty()? {
let message = engine.get_ui_message()?;
let ended = matches!(
UIMessageCode::from_bits(message.event()?),
Ok(UIMessageCode::EndExecution)
);
message.acknowledge()?;
if ended {
return Ok(true);
}
}
}
Ok(false)
}
fn run(engine: &Engine, sequence_file: &SequenceFile, entry_point: &str) -> Result<(), Error> {
let execution = engine.new_execution(sequence_file, entry_point, None, false, 0)?;
println!("\nRunning {entry_point}...");
if !wait_for_end(engine, RUN_DEADLINE)? {
println!(" did not finish within {RUN_DEADLINE:?}; terminating");
execution.terminate()?;
return Ok(());
}
println!(" status: {}", execution.result_status()?);
let results = execution.result_object()?;
if !results.exists("ResultList", none())? {
println!(" no results recorded");
return Ok(());
}
let result_list = results.get_property_object("ResultList", none())?;
for index in 0..result_list.get_num_elements()? {
let entry = result_list.get_property_object_by_offset(index, none())?;
println!(
" {}: {}",
entry
.get_val_string("TS.StepName", none())
.unwrap_or_else(|_| "<unnamed>".to_owned()),
entry
.get_val_string("Status", none())
.unwrap_or_else(|_| "<no status>".to_owned())
);
}
Ok(())
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let engine = Engine::new()?;
engine.set_ui_message_polling_enabled(true)?;
let sequence_file = build(&engine)?;
println!("File holds {} sequence(s).", sequence_file.num_sequences()?);
run(&engine, &sequence_file, "MainSequence")?;
let diagnostics = sequence_file.get_sequence_by_name(SUBSEQUENCE)?;
diagnostics
.parameters()?
.set_val_string(PARAMETER, none(), "FIXTURE-07")?;
println!(
"\n{SUBSEQUENCE}.{PARAMETER} default is now {:?}",
diagnostics
.parameters()?
.get_val_string(PARAMETER, none())?
);
run(&engine, &sequence_file, SUBSEQUENCE)?;
engine.release_sequence_file_ex(sequence_file, none())?;
Ok(())
}