use anyhow::anyhow;
use crc32fast::Hasher;
use indexmap::IndexMap;
use log::debug;
use num_derive::{FromPrimitive, ToPrimitive};
use num_traits::FromPrimitive;
use serde::{Deserialize, Serialize, Serializer, ser::SerializeStruct};
use std::fmt;
use crate::{
command_arg_tuple::MechFsmCommandArgTuple,
variant::VariantValue,
};
pub use crate::ipc::{Action, CommandMessage, CommandMessageResult};
#[derive(Serialize, Deserialize, Debug, Clone, Copy, ToPrimitive, FromPrimitive, Eq, PartialEq)]
#[repr(i16)]
#[serde(try_from = "i16", into = "i16")]
pub enum MechFsmControl {
NoOp = 0,
Restart = 1,
Init = 5,
Idle = 10,
Exec = 11,
Write = 12,
Read = 15,
AckDone = 101,
}
impl TryFrom<i16> for MechFsmControl {
type Error = anyhow::Error;
fn try_from(value: i16) -> Result<Self, Self::Error> {
num_traits::FromPrimitive::from_i16(value)
.ok_or_else(|| anyhow::anyhow!("Invalid MechFsmControl value: {}", value))
}
}
impl Into<i16> for MechFsmControl {
fn into(self) -> i16 {
self as i16
}
}
#[derive(Serialize, Deserialize, Debug, Clone, Copy, ToPrimitive, FromPrimitive, Eq, PartialEq)]
#[repr(i16)]
pub enum MechFsmState {
Invalid = 0,
StartUp = 1,
Init = 5,
Idle = 10,
Executing = 20,
CmdDone = 100,
Error = 900,
}
impl fmt::Display for MechFsmState {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let description = match self {
MechFsmState::Invalid => "Invalid",
MechFsmState::StartUp => "StartUp",
MechFsmState::Init => "Init",
MechFsmState::Idle => "Idle",
MechFsmState::Executing => "Executing",
MechFsmState::CmdDone => "CmdDone",
MechFsmState::Error => "Error",
};
write!(f, "{}", description)
}
}
#[derive(Serialize, Deserialize, Debug, Clone, Copy)]
pub struct MechFsmCommandTuple {
pub transaction_id: u32,
pub control: MechFsmControl,
pub index: u16,
pub sub_index: u16,
pub crc: u32,
pub data: MechFsmCommandArgTuple,
}
impl MechFsmCommandTuple {
pub fn new() -> Self {
Self {
transaction_id: 0,
control: MechFsmControl::NoOp,
index: 0,
sub_index: 0,
crc: 0,
data: MechFsmCommandArgTuple::new(),
}
}
pub fn from_control_code(ctrl: MechFsmControl) -> Self {
Self {
transaction_id: 0,
control: ctrl,
index: 0,
sub_index: 0,
crc: 0,
data: MechFsmCommandArgTuple::new(),
}
}
pub fn to_bytes(&self) -> [u8; 1036] {
let mut ret = [0 as u8; 1036];
ret[0..4].copy_from_slice(&self.transaction_id.to_le_bytes());
ret[4..6].copy_from_slice(&(self.control as i16).to_le_bytes());
ret[6..8].copy_from_slice(&self.index.to_le_bytes());
ret[8..10].copy_from_slice(&self.sub_index.to_le_bytes());
ret[10..14].copy_from_slice(&self.crc.to_le_bytes());
ret[14..1036].copy_from_slice(&self.data.to_bytes());
return ret;
}
pub fn calculate_crc32(command: &MechFsmCommandTuple) -> u32 {
let mut hasher = Hasher::new();
let mut crc_excluded_tuple = command.clone();
crc_excluded_tuple.crc = 0;
let bytes = crc_excluded_tuple.to_bytes();
hasher.update(&bytes);
let crc32 = hasher.finalize();
return crc32;
}
pub fn update_crc32(&mut self) {
self.crc = MechFsmCommandTuple::calculate_crc32(self);
}
pub fn is_valid(&self) -> bool {
if self.control == MechFsmControl::NoOp {
return false;
}
if self.transaction_id == 0 {
return false;
}
let crc_res = MechFsmCommandTuple::calculate_crc32(self);
if crc_res != self.crc {
debug!("CRC Check failed! {} != {}", crc_res, self.crc);
debug!("{:?}", self);
return false;
} else {
return true;
}
}
}
impl TryFrom<VariantValue> for MechFsmCommandTuple {
type Error = anyhow::Error;
fn try_from(value: VariantValue) -> Result<Self, anyhow::Error> {
match value.clone() {
VariantValue::Object(map) => {
let control = map
.get("control")
.and_then(|v| v.to_int16().ok())
.and_then(MechFsmControl::from_i16)
.unwrap_or(MechFsmControl::NoOp);
let crc = map.get("crc").and_then(|v| v.to_uint32().ok()).unwrap_or(0);
let data = map
.get("data")
.and_then(|v| v.clone().try_into().ok())
.unwrap_or(MechFsmCommandArgTuple::new());
let index = map
.get("index")
.and_then(|v| v.to_uint16().ok())
.unwrap_or(0);
let sub_index = map
.get("sub_index")
.and_then(|v| v.to_uint16().ok())
.unwrap_or(0);
let transaction_id = map
.get("transaction_id")
.and_then(|v| v.to_uint32().ok())
.unwrap_or(0);
let ret = MechFsmCommandTuple {
control,
crc,
data,
index,
sub_index,
transaction_id,
};
return Ok(ret);
}
_ => {
return Err(anyhow::anyhow!("VariantValue is not an Object"));
}
}
}
}
impl TryInto<VariantValue> for MechFsmCommandTuple {
type Error = anyhow::Error;
fn try_into(self) -> Result<VariantValue, anyhow::Error> {
let mut map = IndexMap::new();
let data_var: VariantValue = self.data.try_into().unwrap();
map.insert(
"control".to_string(),
VariantValue::Int16(self.control as i16),
);
map.insert("crc".to_string(), VariantValue::UInt32(self.crc));
map.insert("data".to_string(), data_var);
map.insert("index".to_string(), VariantValue::UInt16(self.index));
map.insert(
"sub_index".to_string(),
VariantValue::UInt16(self.sub_index),
);
map.insert(
"transaction_id".to_string(),
VariantValue::UInt32(self.transaction_id),
);
let ret = VariantValue::Object(Box::new(map));
return Ok(ret);
}
}
#[derive(Deserialize, Debug, Clone, Copy)]
pub struct MechFsmStatusTuple {
pub transaction_id: u32,
pub state: MechFsmState,
pub crc: u32,
pub data: MechFsmCommandArgTuple,
}
impl MechFsmStatusTuple {
pub fn new() -> Self {
Self {
transaction_id: 0,
state: MechFsmState::Invalid,
crc: 0,
data: MechFsmCommandArgTuple::new(),
}
}
pub fn to_bytes(&self) -> [u8; 1032] {
let mut ret = [0 as u8; 1032];
ret[0..4].copy_from_slice(&self.transaction_id.to_le_bytes());
ret[4..6].copy_from_slice(&(self.state as i16).to_le_bytes());
ret[6..10].copy_from_slice(&(self.crc as u32).to_le_bytes());
ret[10..1032].copy_from_slice(&self.data.to_bytes());
return ret;
}
pub fn update_crc32(&mut self) {
let mut crc_excluded_tuple = self.clone();
crc_excluded_tuple.crc = 0;
let bytes = crc_excluded_tuple.to_bytes();
let mut hasher = crc32fast::Hasher::new();
hasher.update(&bytes);
self.crc = hasher.finalize();
}
}
impl TryFrom<VariantValue> for MechFsmStatusTuple {
type Error = anyhow::Error;
fn try_from(value: VariantValue) -> Result<Self, anyhow::Error> {
match value.to_json() {
Ok(json) => match serde_json::from_value(json) {
Ok(ret) => {
return Ok(ret);
}
Err(err) => {
return Err(anyhow!(
"Failed to deserialize MechFsmStatusTuple from json: {}",
err
));
}
},
Err(err) => {
return Err(anyhow!(
"Failed to deserialize MechFsmStatusTuple to json: {}",
err
));
}
}
}
}
impl TryInto<VariantValue> for MechFsmStatusTuple {
type Error = anyhow::Error;
fn try_into(self) -> Result<VariantValue, anyhow::Error> {
match serde_json::to_value(self) {
Ok(json) => match VariantValue::from_json(&json) {
Ok(ret) => {
return Ok(ret);
}
Err(err) => {
return Err(anyhow!(
"Failed to serialize MechFsmCommandTuple to VariantValue: {}",
err
));
}
},
Err(err) => {
return Err(anyhow!(
"Failed to serialize MechFsmCommandTuple to json: {}",
err
));
}
}
}
}
impl Serialize for MechFsmStatusTuple {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let data = serde_json::to_value(self.data).unwrap();
let mut state = serializer.serialize_struct("MechFsmStatusTuple", 4)?;
state.serialize_field("transaction_id", &self.transaction_id)?;
state.serialize_field("state", &(self.state as i16))?;
state.serialize_field("crc", &self.crc)?;
state.serialize_field("data", &data)?;
return state.end();
}
}