use std::time::Duration;
use async_trait::async_trait;
use mechutil::{
command::{MechFsmCommandTuple, MechFsmControl, MechFsmState, MechFsmStatusTuple},
command_arg_tuple::MechFsmCommandArgTuple,
command_fsm::{
CommandFsmHandle, CommandFsmHandler, CommandFsmMessage, CommandFsmResult, StateChangeError,
},
variant::VariantValue,
};
use tokio::sync::mpsc;
use log::error;
use simplelog::*;
pub struct CustomStateMachine {
}
#[async_trait]
impl CommandFsmHandler for CustomStateMachine {
async fn on_startup(&mut self, status: &mut MechFsmStatusTuple) -> Result<(), anyhow::Error> {
return Ok(());
}
async fn on_exec_command(
&mut self,
sender: &mpsc::Sender<CommandFsmMessage>,
_command: &MechFsmCommandTuple,
status: &mut MechFsmStatusTuple,
) -> Result<CommandFsmResult, anyhow::Error> {
let tx = sender.clone();
let mut status_clone = status.clone();
tokio::spawn(async move {
println!("Taking forever to execute command...");
tokio::time::sleep(Duration::from_millis(7000)).await;
println!("Sending message that CmdDone...");
let mut v = MechFsmCommandArgTuple::new();
let _ = v.push_value_u16(1);
status_clone.data = v;
if let Err(err) = tx
.send(CommandFsmMessage::ChangeState {
state: MechFsmState::CmdDone,
status: status_clone,
})
.await
{
error!("Failed to send change state command: {}", err);
}
});
Ok(CommandFsmResult::Running)
}
async fn on_state_change(&mut self, new_state: &MechFsmState) -> Result<(), StateChangeError> {
println!("State changed to: {:?}", new_state);
return Ok(());
}
async fn read_command(
&mut self,
_command: &mut MechFsmCommandTuple,
) -> Result<(), anyhow::Error> {
println!("Read the command...");
return Ok(());
}
async fn write_status(
&mut self,
status: &mut mechutil::command::MechFsmStatusTuple,
) -> Result<(), anyhow::Error> {
println!("Write the status: {:?}", status.state);
return Ok(());
}
async fn write_heartbeat(&mut self, heartbeat_count: i64) -> Result<(), anyhow::Error> {
println!("heartbeat: {}", heartbeat_count);
return Ok(());
}
}
#[tokio::main]
async fn main() {
CombinedLogger::init(vec![TermLogger::new(
LevelFilter::Debug,
Config::default(),
TerminalMode::Mixed,
ColorChoice::Auto,
)])
.unwrap();
let mut status_hash_test = MechFsmStatusTuple::new();
status_hash_test.state = MechFsmState::Init;
status_hash_test.update_crc32();
println!("STATUS CRC IS CALCULATED AS: {}", status_hash_test.crc);
let mut cmd_hash_test = MechFsmCommandTuple::new();
cmd_hash_test.transaction_id = 2000;
cmd_hash_test.control = MechFsmControl::Idle;
cmd_hash_test.index = 7;
cmd_hash_test.sub_index = 9;
cmd_hash_test.update_crc32();
println!("COMMAND CRC IS CALCULAED AS: {}", cmd_hash_test.crc);
let mut cmd_test = MechFsmCommandTuple::new();
cmd_test.transaction_id = 32865;
cmd_test.control = MechFsmControl::Idle;
cmd_test.crc = 733694397;
let var: VariantValue = cmd_test.try_into().unwrap();
println!("VAR: {:?}\nDATA {:?}", var, cmd_test.data);
let cmd: MechFsmCommandTuple;
match var.try_into() {
Ok(res) => cmd = res,
Err(err) => panic!("Converion from variant->MechFsmCommandTuple failed.{}", err),
}
println!("RES CMD: {:?}", cmd);
assert_eq!(cmd.transaction_id, cmd_test.transaction_id);
assert_eq!(cmd.control, cmd_test.control);
assert_eq!(cmd.crc, cmd_test.crc);
assert_eq!(cmd.data.num_rows, cmd_test.data.num_rows);
let custom_state_machine: Box<dyn CommandFsmHandler + Send> = Box::new(CustomStateMachine {});
let handle = CommandFsmHandle::new(custom_state_machine, Duration::from_millis(500));
tokio::time::sleep(Duration::from_millis(100)).await;
let idle_cmd = MechFsmCommandTuple::from_control_code(MechFsmControl::Idle);
if let Err(err) = handle.command(&idle_cmd).await {
error!("Failed to inject idle command: {}", err);
}
tokio::time::sleep(Duration::from_millis(100)).await;
let exec_cmd = MechFsmCommandTuple::from_control_code(MechFsmControl::Exec);
if let Err(err) = handle.command(&exec_cmd).await {
error!("Failed to inject execute command: {}", err);
}
tokio::time::sleep(Duration::from_millis(200)).await;
let exec_cmd = MechFsmCommandTuple::from_control_code(MechFsmControl::AckDone);
if let Err(err) = handle.command(&exec_cmd).await {
error!("Failed to inject AckDone command: {}", err);
}
tokio::time::sleep(Duration::from_millis(100)).await;
let _ = handle.shutdown().await;
}