The application tests threaded code generated by the editor of hierarchical state machines. The original scheme can be seen on the switch_reset.svg attached to the project. It's model of a switch affected by two events: TURN and RESET. The first switches two states ON and OFF, the second resets the state machine to the OFF state regardless of what state it was in before.
The editor's Planner module was supplemented with Rust code generator, which automatically create the switch_reset_helper.rs file with the transfer functions. This file also contain function create_helper builds QHsmHelper structure for processing these functions. A core has also been added to the application, which services the launch of threaded code and the impact of events on it. This is a set of several very simple structures placed to the core.rs file: EventWrapper, which describes and keep an event, QHsmHelper with the IQHsmStateMachineHelper interface, which contains a container of threaded codes and ensures its execution under the influence of events, ThreadedCodeExecutor - a structure ensures the launch of threaded code for a specific state and event.
The generated switch_reset_helper.rs file is a skeleton for the logical part of the application, namely the list and bodies of empty transfer functions that can and should be filled with some content. For example, with trace elements in the simplest case. Some functions may not be used and should be deleted or commented out:
// File switch_reset_helper.rs automatically generated at 2024-12-31 14:10:12
use std::sync::Arc;
use crate::core::{QHsmHelper, ThreadedCodeExecutor};
use std::any::Any;
// pub fn switch_entry(_data: Option<&Box<dyn Any>>) {
// }
// pub fn switch_init(_data: Option<&Box<dyn Any>>) {
// }
pub fn off_entry(_data: Option<&Box<dyn Any>>) {
println!("OFF");
}
pub fn off_reset(_data: Option<&Box<dyn Any>>) {
println!("@RESET");
}
// pub fn off_exit(_data: Option<&Box<dyn Any>>) {
// }
pub fn off_turn(_data: Option<&Box<dyn Any>>) {
println!("OFF: TURN");
}
pub fn on_entry(_data: Option<&Box<dyn Any>>) {
println!("ON");
}
// pub fn on_exit(_data: Option<&Box<dyn Any>>) {
// }
pub fn on_turn(_data: Option<&Box<dyn Any>>) {
println!("ON: TURN");
}
pub fn create_helper(helper: &Arc<QHsmHelper>) {
let switch_init_functions: Vec<Box<dyn Fn(Option<&Box<dyn Any>>)>> = vec![
// Box::new(switch_entry),
// Box::new(switch_init),
Box::new(off_entry)
];
helper.insert("switch".to_string(), "init".to_string(), Arc::new(ThreadedCodeExecutor::new(switch_init_functions, "off".to_string())));
let off_reset_functions: Vec<Box<dyn Fn(Option<&Box<dyn Any>>)>> = vec![
Box::new(off_reset),
// Box::new(off_exit),
// Box::new(switch_init),
Box::new(off_entry)
];
helper.insert("off".to_string(), "RESET".to_string(), Arc::new(ThreadedCodeExecutor::new(off_reset_functions, "off".to_string())));
let off_turn_functions: Vec<Box<dyn Fn(Option<&Box<dyn Any>>)>> = vec![
Box::new(off_turn),
Box::new(on_entry)
];
helper.insert("off".to_string(), "TURN".to_string(), Arc::new(ThreadedCodeExecutor::new(off_turn_functions, "on".to_string())));
let on_reset_functions: Vec<Box<dyn Fn(Option<&Box<dyn Any>>)>> = vec![
Box::new(off_reset),
// Box::new(on_exit),
// Box::new(off_exit),
// Box::new(switch_init),
Box::new(off_entry)
];
helper.insert("on".to_string(), "RESET".to_string(), Arc::new(ThreadedCodeExecutor::new(on_reset_functions, "off".to_string())));
let on_turn_functions: Vec<Box<dyn Fn(Option<&Box<dyn Any>>)>> = vec![
Box::new(on_turn),
// Box::new(on_exit),
// Box::new(off_exit),
// Box::new(switch_init),
Box::new(off_entry)
];
helper.insert("on".to_string(), "TURN".to_string(), Arc::new(ThreadedCodeExecutor::new(on_turn_functions, "off".to_string())));
}To test the threaded code for hierarchical state machine, need to manually create two small modules that ensure the launch of the application:
test.rs
use std::sync::Arc;
use crate::core::{QHsmHelper, Runner, post};
use crate::switch_reset_helper::create_helper;
pub fn test_switch() {
let helper = Arc::new(QHsmHelper::new("switch".to_string()));
let runner = Runner::new(helper.clone());
create_helper(&helper);
post(&runner, "init".to_string(), None);
post(&runner, "TURN".to_string(), None);
post(&runner, "RESET".to_string(), None);
post(&runner, "TURN".to_string(), None);
post(&runner, "TURN".to_string(), None);
post(&runner, "RESET".to_string(), None);
}main.rs
mod core;
mod test;
mod switch_reset_helper;
use test::test_switch;
fn main() {
test_switch();
}The application is created as a ubuntu console application and can be launched in terminal mode. There are two ways to launch the application:
In debug mode
micrcx@micrcx-desktop:~/rust/switch_tc$ cargo run
warning: unused manifest key: core
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.05s
Running `target/debug/switch_tc`
OFF
OFF: TURN
ON
@RESET
OFF
OFF: TURN
ON
ON: TURN
OFF
@RESET
OFF
micrcx@micrcx-desktop:~/rust/switch_tc$
In release mode
micrcx@micrcx-desktop:~/rust/switch_tc$
micrcx@micrcx-desktop:~/rust/switch_tc$ cargo build --release
warning: unused manifest key: core
Finished `release` profile [optimized] target(s) in 0.02s
micrcx@micrcx-desktop:~/rust/switch_tc$
micrcx@micrcx-desktop:~/rust/switch_tc$ cd target/release
micrcx@micrcx-desktop:~/rust/switch_tc/target/release$ ./switch_tc
OFF
OFF: TURN
ON
@RESET
OFF
OFF: TURN
ON
ON: TURN
OFF
@RESET
OFF
micrcx@micrcx-desktop:~/rust/switch_tc/target/release$