use std::time::{Duration, Instant};
use rs_teststand::{
AdapterKeyName, Engine, Error, PropertyOptions, Sequence, StepGroup, UIMessageCode,
pump_thread_messages,
};
const TESTS: [(&str, &str, f64, f64); 3] = [
("Supply Voltage", "5.0", 4.75, 5.25),
("Bias Current", "9.0", 1.0, 8.0),
("Reference Clock", "10.0", 9.5, 10.5),
];
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_numeric_limit_test(
engine: &Engine,
sequence: &Sequence,
name: &str,
data_source: &str,
low: f64,
high: f64,
) -> Result<(), Error> {
let step = engine.new_step(AdapterKeyName::NoneAdapter.as_str(), "NumericLimitTest")?;
step.set_name(name)?;
step.set_record_result(true)?;
let properties = step.as_property_object()?;
properties.set_val_string("DataSource", insert_if_missing(), data_source)?;
properties.set_val_number("Limits.Low", insert_if_missing(), low)?;
properties.set_val_number("Limits.High", insert_if_missing(), high)?;
sequence.insert_step(
&step,
sequence.get_num_steps(StepGroup::Main)?,
StepGroup::Main,
)
}
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 report(results: &rs_teststand::PropertyObject) -> Result<(), Error> {
if !results.exists("ResultList", none())? {
println!("No results recorded.");
return Ok(());
}
let result_list = results.get_property_object("ResultList", none())?;
let count = result_list.get_num_elements()?;
println!(
"\n{count} of {} defined test(s) recorded a result:",
TESTS.len()
);
if count < i32::try_from(TESTS.len()).unwrap_or(i32::MAX) {
println!(" (a failure ended the run before the rest could execute)");
}
for index in 0..count {
let entry = result_list.get_property_object_by_offset(index, none())?;
let name = entry
.get_val_string("TS.StepName", none())
.unwrap_or_else(|_| "<unnamed>".to_owned());
let status = entry
.get_val_string("Status", none())
.unwrap_or_else(|_| "<no status>".to_owned());
match entry.get_val_number("Numeric", none()) {
Ok(measured) => println!(" {name}: {status} (measured {measured})"),
Err(_) => println!(" {name}: {status}"),
}
}
Ok(())
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let engine = Engine::new()?;
engine.set_ui_message_polling_enabled(true)?;
let sequence_file = engine.new_sequence_file()?;
let main_sequence = sequence_file.get_sequence_by_name("MainSequence")?;
for (name, data_source, low, high) in TESTS {
add_numeric_limit_test(&engine, &main_sequence, name, data_source, low, high)?;
}
let execution = engine.new_execution(&sequence_file, "MainSequence", None, false, 0)?;
println!("Running {} headless...", execution.display_name()?);
if wait_for_end(&engine, RUN_DEADLINE)? {
println!("Finished with status: {}", execution.result_status()?);
report(&execution.result_object()?)?;
} else {
println!("The run did not finish within {RUN_DEADLINE:?}; terminating.");
execution.terminate()?;
}
engine.release_sequence_file_ex(sequence_file, none())?;
Ok(())
}