use crate::register_value::MechCommandRegisterValue;
use crate::variant::{VariantTypeId, VariantValue};
use anyhow::anyhow;
use indexmap::IndexMap;
use num_traits::FromPrimitive;
use serde::{Deserialize, Serialize};
use serde_with::serde_as;
use serde::de::DeserializeOwned;
use std::convert::TryInto;
use std::mem::size_of;
#[repr(C)]
#[serde_as]
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub struct MechFsmCommandArgTuple {
#[serde_as(as = "[_; 255]")]
pub type_info: [VariantTypeId; 255], #[serde_as(as = "[_; 255]")]
pub data: [u8; 255], #[serde_as(as = "[_; 255]")]
pub value_len: [u8; 255], pub num_columns: u8, pub num_rows: u8, }
impl MechFsmCommandArgTuple {
pub fn new() -> Self {
Self {
type_info: [VariantTypeId::Null; 255],
data: [0; 255],
value_len: [0; 255],
num_columns: 0,
num_rows: 1,
}
}
pub fn to_bytes(&self) -> [u8; 1022] {
let mut ret = [0 as u8; 1022];
for i in 0..255 {
ret[i * 2..i * 2 + 2].copy_from_slice(&(self.type_info[i] as i16).to_le_bytes());
}
ret[510..765].copy_from_slice(&self.data);
ret[765..1020].copy_from_slice(&self.value_len);
ret[1020] = self.num_columns;
ret[1021] = self.num_rows;
ret
}
pub fn read_column_value<T>(&self, column_index: u8) -> Result<T, anyhow::Error>
where
T: DeserializeOwned + Sized, {
if column_index >= self.num_columns {
return Err(anyhow!(
"Out of range! {} >= {}!",
column_index,
self.num_columns
));
}
let expected_size = size_of::<T>();
if self.value_len[column_index as usize] as usize != expected_size {
return Err(anyhow!(
"Size mismatch! Column size {} != expected size {}",
self.value_len[column_index as usize],
expected_size
));
}
let mut offset = 0;
for i in 0..column_index {
offset += self.value_len[i as usize] as usize;
}
let data_slice = &self.data[offset..offset + expected_size];
let value: T = postcard::from_bytes(data_slice).map_err(|e| {
anyhow!(
"Failed to deserialize data at column {}: {:?}",
column_index,
e
)
})?;
Ok(value)
}
pub fn to_string(&self, column_index: u8) -> Result<String, anyhow::Error> {
match self.type_info[column_index as usize] {
VariantTypeId::String => {
let mut offset = 0;
for i in 0..column_index {
offset += self.value_len[i as usize] as usize;
}
let data_slice =
&self.data[offset..offset + self.value_len[column_index as usize] as usize];
match String::from_utf8(data_slice.to_vec()) {
Ok(val) => return Ok(val),
Err(err) => {
return Err(anyhow!("Failed to deserialize string: {}", err));
}
}
}
VariantTypeId::Bit => match self.read_column_value::<bool>(column_index) {
Ok(ret) => {
return Ok(ret.to_string());
}
_ => {
return Err(anyhow!("Failed to convert Bit value to bool."));
}
},
VariantTypeId::Byte => match self.read_column_value::<u8>(column_index) {
Ok(ret) => {
return Ok(ret.to_string());
}
_ => {
return Err(anyhow!("Failed to convert Byte value to u8."));
}
},
VariantTypeId::SByte => match self.read_column_value::<i8>(column_index) {
Ok(ret) => {
return Ok(ret.to_string());
}
_ => {
return Err(anyhow!("Failed to convert SByte value to i8."));
}
},
VariantTypeId::Int16 => match self.read_column_value::<i16>(column_index) {
Ok(ret) => {
return Ok(ret.to_string());
}
_ => {
return Err(anyhow!("Failed to convert Int16 value to i16."));
}
},
VariantTypeId::UInt16 => match self.read_column_value::<u16>(column_index) {
Ok(ret) => {
return Ok(ret.to_string());
}
_ => {
return Err(anyhow!("Failed to convert UInt16 value to u16."));
}
},
VariantTypeId::Int32 => match self.read_column_value::<i32>(column_index) {
Ok(ret) => {
return Ok(ret.to_string());
}
_ => {
return Err(anyhow!("Failed to convert Int32 value to i32."));
}
},
VariantTypeId::UInt32 => match self.read_column_value::<u32>(column_index) {
Ok(ret) => {
return Ok(ret.to_string());
}
_ => {
return Err(anyhow!("Failed to convert UInt32 value to u32."));
}
},
VariantTypeId::Int64 => match self.read_column_value::<i64>(column_index) {
Ok(ret) => {
return Ok(ret.to_string());
}
_ => {
return Err(anyhow!("Failed to convert Int64 value to i64."));
}
},
VariantTypeId::UInt64 => match self.read_column_value::<u64>(column_index) {
Ok(ret) => {
return Ok(ret.to_string());
}
_ => {
return Err(anyhow!("Failed to convert UInt64 value to u64."));
}
},
VariantTypeId::Real32 => match self.read_column_value::<f32>(column_index) {
Ok(ret) => {
return Ok(ret.to_string());
}
_ => {
return Err(anyhow!("Failed to convert Real32 value to f32."));
}
},
VariantTypeId::Real64 => match self.read_column_value::<f64>(column_index) {
Ok(ret) => {
return Ok(ret.to_string());
}
_ => {
return Err(anyhow!("Failed to convert Real64 value to f64."));
}
},
VariantTypeId::Null => Ok("<NULL>".to_string()),
VariantTypeId::Array => Ok("[ARRAY]".to_string()),
VariantTypeId::Object => Ok("{OBJECT}".to_string()),
}
}
pub fn to_variant(&self, column_index: u8) -> Result<VariantValue, anyhow::Error> {
match self.type_info[column_index as usize] {
VariantTypeId::Null => {
return Ok(VariantValue::Null);
}
VariantTypeId::Bit => match self.read_column_value::<bool>(column_index) {
Ok(ret) => return Ok(VariantValue::Bit(ret)),
Err(err) => return Err(anyhow!("Failed to conver to bool: {}", err)),
},
VariantTypeId::Byte => match self.read_column_value::<u8>(column_index) {
Ok(ret) => return Ok(VariantValue::Byte(ret)),
Err(err) => return Err(anyhow!("Failed to convert to byte: {}", err)),
},
VariantTypeId::SByte => match self.read_column_value::<i8>(column_index) {
Ok(ret) => return Ok(VariantValue::SByte(ret)),
Err(err) => return Err(anyhow!("Failed to convert to sbyte: {}", err)),
},
VariantTypeId::Int16 => match self.read_column_value::<i16>(column_index) {
Ok(ret) => return Ok(VariantValue::Int16(ret)),
Err(err) => return Err(anyhow!("Failed to convert to int16: {}", err)),
},
VariantTypeId::UInt16 => match self.read_column_value::<u16>(column_index) {
Ok(ret) => return Ok(VariantValue::UInt16(ret)),
Err(err) => return Err(anyhow!("Failed to convert to int16: {}", err)),
},
VariantTypeId::Int32 => match self.read_column_value::<i32>(column_index) {
Ok(ret) => return Ok(VariantValue::Int32(ret)),
Err(err) => return Err(anyhow!("Failed to convert to int32: {}", err)),
},
VariantTypeId::UInt32 => match self.read_column_value::<u32>(column_index) {
Ok(ret) => return Ok(VariantValue::UInt32(ret)),
Err(err) => return Err(anyhow!("Failed to convert to uint32: {}", err)),
},
VariantTypeId::Int64 => match self.read_column_value::<i64>(column_index) {
Ok(ret) => return Ok(VariantValue::Int64(ret)),
Err(err) => return Err(anyhow!("Failed to convert to int64: {}", err)),
},
VariantTypeId::UInt64 => match self.read_column_value::<u64>(column_index) {
Ok(ret) => return Ok(VariantValue::UInt64(ret)),
Err(err) => return Err(anyhow!("Failed to convert to uint64: {}", err)),
},
VariantTypeId::Real32 => match self.read_column_value::<f32>(column_index) {
Ok(ret) => return Ok(VariantValue::Real32(ret)),
Err(err) => return Err(anyhow!("Failed to convert to real32: {}", err)),
},
VariantTypeId::Real64 => match self.read_column_value::<f64>(column_index) {
Ok(ret) => return Ok(VariantValue::Real64(ret)),
Err(err) => return Err(anyhow!("Failed to convert to real64: {}", err)),
},
VariantTypeId::String => match self.to_string(column_index) {
Ok(ret) => return Ok(VariantValue::String(ret)),
Err(err) => return Err(anyhow!("Failed to convert to string: {}", err)),
},
VariantTypeId::Array => {
return Err(anyhow!("Conversion to array not currently supported."));
}
VariantTypeId::Object => {
return Err(anyhow!("Conversion to object not currently supported."));
}
}
}
pub fn to_register_value(&self) -> Result<MechCommandRegisterValue, anyhow::Error> {
if self.num_columns == 0 || self.type_info[0] == VariantTypeId::Null {
return Ok(MechCommandRegisterValue::new());
}
if self.type_info[0] == VariantTypeId::Array {
return Err(anyhow!("ARRAY type not supported."));
}
if self.type_info[0] == VariantTypeId::Object {
return Err(anyhow!("OBJECT type not supported."));
}
let total_length = self.value_len[0] as usize;
if total_length > 255 {
return Err(anyhow!(
"Data length exceeds maximum allowed size of 255 bytes. This MechFsmCommandArgument is in an invalid state."
));
} else if total_length == 0 {
return Err(anyhow!(
"Data length of first column is 0. This MechFsmCommandArgument is in an invalid state."
));
}
let mut register_data = [0u8; 255];
register_data[..total_length].copy_from_slice(&self.data[..total_length]);
Ok(MechCommandRegisterValue {
type_id: self.type_info[0], value_len: total_length as u32, num_columns: 1, num_rows: 1, data: register_data,
})
}
pub fn calculate_data_bytes(&self) -> usize {
let mut ret = 0;
for i in 0..self.num_columns {
ret += self.value_len[i as usize] as usize;
}
return ret;
}
pub fn clear(&mut self) {
self.num_columns = 0;
self.num_rows = 1;
self.type_info = [VariantTypeId::Null; 255];
self.data = [0; 255];
self.value_len = [0; 255];
}
pub fn push_value_bool(&mut self, value: bool) -> Result<(), anyhow::Error> {
if self.num_columns >= 255 {
return Err(anyhow!("Too many columns! Out of capacity."));
}
let start_index = self.calculate_data_bytes();
if start_index + 1 < 255 {
self.data[start_index] = value as u8;
self.type_info[self.num_columns as usize] = VariantTypeId::Bit;
self.value_len[self.num_columns as usize] = 1;
self.num_columns += 1;
return Ok(());
} else {
return Err(anyhow!(
"Out of capacity in data field for additional value."
));
}
}
pub fn push_value_byte(&mut self, value: u8) -> Result<(), anyhow::Error> {
if self.num_columns >= 255 {
return Err(anyhow!("Too many columns! Out of capacity."));
}
let start_index = self.calculate_data_bytes();
if start_index + 1 < 255 {
self.data[start_index] = value;
self.type_info[self.num_columns as usize] = VariantTypeId::Byte;
self.value_len[self.num_columns as usize] = 1;
self.num_columns += 1;
return Ok(());
} else {
return Err(anyhow!(
"Out of capacity in data field for additional value."
));
}
}
pub fn push_value_sbyte(&mut self, value: i8) -> Result<(), anyhow::Error> {
if self.num_columns >= 255 {
return Err(anyhow!("Too many columns! Out of capacity."));
}
let start_index = self.calculate_data_bytes();
if start_index + 1 < 255 {
self.data[start_index] = value as u8;
self.type_info[self.num_columns as usize] = VariantTypeId::SByte;
self.value_len[self.num_columns as usize] = 1;
self.num_columns += 1;
return Ok(());
} else {
return Err(anyhow!(
"Out of capacity in data field for additional value."
));
}
}
pub fn push_value_u16(&mut self, value: u16) -> Result<(), anyhow::Error> {
if self.num_columns >= 255 {
return Err(anyhow!("Too many columns! Out of capacity."));
}
let start_index = self.calculate_data_bytes();
if start_index + 2 < 255 {
self.data[start_index..start_index + 2].copy_from_slice(&value.to_le_bytes());
self.type_info[self.num_columns as usize] = VariantTypeId::UInt16;
self.value_len[self.num_columns as usize] = 2;
self.num_columns += 1;
return Ok(());
} else {
return Err(anyhow!(
"Out of capacity in data field for additional value."
));
}
}
pub fn push_value_i16(&mut self, value: i16) -> Result<(), anyhow::Error> {
if self.num_columns >= 255 {
return Err(anyhow!("Too many columns! Out of capacity."));
}
let start_index = self.calculate_data_bytes();
if start_index + 2 < 255 {
self.data[start_index..start_index + 2].copy_from_slice(&value.to_le_bytes());
self.type_info[self.num_columns as usize] = VariantTypeId::Int16;
self.value_len[self.num_columns as usize] = 2;
self.num_columns += 1;
return Ok(());
} else {
return Err(anyhow!(
"Out of capacity in data field for additional value."
));
}
}
pub fn push_value_u32(&mut self, value: u32) -> Result<(), anyhow::Error> {
if self.num_columns >= 255 {
return Err(anyhow!("Too many columns! Out of capacity."));
}
let start_index = self.calculate_data_bytes();
if start_index + 4 < 255 {
self.data[start_index..start_index + 4].copy_from_slice(&value.to_le_bytes());
self.type_info[self.num_columns as usize] = VariantTypeId::UInt32;
self.value_len[self.num_columns as usize] = 4;
self.num_columns += 1;
return Ok(());
} else {
return Err(anyhow!(
"Out of capacity in data field for additional value."
));
}
}
pub fn push_value_i32(&mut self, value: i32) -> Result<(), anyhow::Error> {
if self.num_columns >= 255 {
return Err(anyhow!("Too many columns! Out of capacity."));
}
let start_index = self.calculate_data_bytes();
if start_index + 4 < 255 {
self.data[start_index..start_index + 4].copy_from_slice(&value.to_le_bytes());
self.type_info[self.num_columns as usize] = VariantTypeId::Int32;
self.value_len[self.num_columns as usize] = 4;
self.num_columns += 1;
return Ok(());
} else {
return Err(anyhow!(
"Out of capacity in data field for additional value."
));
}
}
pub fn push_value_f32(&mut self, value: f32) -> Result<(), anyhow::Error> {
if self.num_columns >= 255 {
return Err(anyhow!("Too many columns! Out of capacity."));
}
let start_index = self.calculate_data_bytes();
if start_index + 4 < 255 {
self.data[start_index..start_index + 4].copy_from_slice(&value.to_le_bytes());
self.type_info[self.num_columns as usize] = VariantTypeId::Real32;
self.value_len[self.num_columns as usize] = 4;
self.num_columns += 1;
return Ok(());
} else {
return Err(anyhow!(
"Out of capacity in data field for additional value."
));
}
}
pub fn push_value_u64(&mut self, value: u64) -> Result<(), anyhow::Error> {
if self.num_columns >= 255 {
return Err(anyhow!("Too many columns! Out of capacity."));
}
let start_index = self.calculate_data_bytes();
if start_index + 8 < 255 {
self.data[start_index..start_index + 8].copy_from_slice(&value.to_le_bytes());
self.type_info[self.num_columns as usize] = VariantTypeId::UInt64;
self.value_len[self.num_columns as usize] = 8;
self.num_columns += 1;
return Ok(());
} else {
return Err(anyhow!(
"Out of capacity in data field for additional value."
));
}
}
pub fn push_value_i64(&mut self, value: i64) -> Result<(), anyhow::Error> {
if self.num_columns >= 255 {
return Err(anyhow!("Too many columns! Out of capacity."));
}
let start_index = self.calculate_data_bytes();
if start_index + 8 < 255 {
self.data[start_index..start_index + 8].copy_from_slice(&value.to_le_bytes());
self.type_info[self.num_columns as usize] = VariantTypeId::Int64;
self.value_len[self.num_columns as usize] = 8;
self.num_columns += 1;
return Ok(());
} else {
return Err(anyhow!(
"Out of capacity in data field for additional value."
));
}
}
pub fn push_value_f64(&mut self, value: f64) -> Result<(), anyhow::Error> {
if self.num_columns >= 255 {
return Err(anyhow!("Too many columns! Out of capacity."));
}
let start_index = self.calculate_data_bytes();
if start_index + 8 < 255 {
self.data[start_index..start_index + 8].copy_from_slice(&value.to_le_bytes());
self.type_info[self.num_columns as usize] = VariantTypeId::Real64;
self.value_len[self.num_columns as usize] = 8;
self.num_columns += 1;
return Ok(());
} else {
return Err(anyhow!(
"Out of capacity in data field for additional value."
));
}
}
pub fn push_value_string(&mut self, value: &str) -> Result<(), anyhow::Error> {
if self.num_columns >= 255 {
return Err(anyhow!("Too many columns! Out of capacity."));
}
let start_index = self.calculate_data_bytes();
let str_len = value.len();
if str_len > 254 {
return Err(anyhow!(
"String length of {} not allowed. Must be less than 254 chars.",
str_len
));
}
if str_len > 0 {
if start_index + str_len < 255 {
self.data[start_index..start_index + str_len].copy_from_slice(&value.as_bytes());
self.type_info[self.num_columns as usize] = VariantTypeId::String;
self.value_len[self.num_columns as usize] = str_len as u8;
self.num_columns += 1;
return Ok(());
} else {
return Err(anyhow!(
"Out of capacity in data field for additional string of {} chars.",
str_len
));
}
} else {
if start_index + 1 < 255 {
self.data[start_index] = 0; self.type_info[self.num_columns as usize] = VariantTypeId::String;
self.value_len[self.num_columns as usize] = 1;
self.num_columns += 1;
return Ok(());
} else {
return Err(anyhow!(
"Out of capacity in data field for additional string of {} chars.",
str_len
));
}
}
}
pub fn push_value_register(
&mut self,
value: &MechCommandRegisterValue,
) -> Result<(), anyhow::Error> {
let total_length = self.calculate_data_bytes();
if total_length + value.value_len as usize > 255 {
return Err(anyhow!(
"Data length exceeds maximum allowed size of 255 bytes"
));
}
if self.num_columns >= 255 {
return Err(anyhow!("Too many columns! Out of capacity."));
}
let start_index = total_length;
self.data[start_index..start_index + value.value_len as usize]
.copy_from_slice(&value.data[..value.value_len as usize]);
self.type_info[self.num_columns as usize] = value.type_id;
self.value_len[self.num_columns as usize] = value.value_len as u8; self.num_columns += value.num_columns as u8;
Ok(())
}
}
impl TryInto<VariantValue> for MechFsmCommandArgTuple {
type Error = anyhow::Error;
fn try_into(self) -> Result<VariantValue, anyhow::Error> {
let mut map = IndexMap::new();
let type_info_data: Vec<VariantValue> = self
.type_info
.iter()
.map(|&t| VariantValue::Int16(t as i16))
.collect();
println!("type_info_data: {:?}", type_info_data);
let data_data: Vec<VariantValue> =
self.data.iter().map(|&d| VariantValue::Byte(d)).collect();
let value_len_data: Vec<VariantValue> = self
.value_len
.iter()
.map(|&v| VariantValue::Byte(v))
.collect();
map.insert("type_info".to_string(), VariantValue::Array(type_info_data));
map.insert("data".to_string(), VariantValue::Array(data_data));
map.insert("value_len".to_string(), VariantValue::Array(value_len_data));
map.insert(
"num_columns".to_string(),
VariantValue::Byte(self.num_columns),
);
map.insert("num_rows".to_string(), VariantValue::Byte(self.num_rows));
Ok(VariantValue::Object(Box::new(map)))
}
}
impl TryFrom<VariantValue> for MechFsmCommandArgTuple {
type Error = anyhow::Error;
fn try_from(value: VariantValue) -> Result<Self, Self::Error> {
match value {
VariantValue::Object(map) => {
let type_info = map
.get("type_info")
.and_then(|v| v.to_array().ok())
.ok_or_else(|| anyhow!("Invalid type_info field"))?
.into_iter()
.map(|v| {
VariantTypeId::from_i16(v.to_int16().unwrap_or(0))
.unwrap_or(VariantTypeId::Null)
})
.collect::<Vec<VariantTypeId>>()
.try_into()
.unwrap_or([VariantTypeId::Null; 255]);
let data = map
.get("data")
.and_then(|v| v.to_array().ok())
.ok_or_else(|| anyhow!("Invalid data field"))?
.into_iter()
.map(|v| v.to_byte().unwrap_or(0))
.collect::<Vec<u8>>()
.try_into()
.unwrap_or([0; 255]);
let value_len = map
.get("value_len")
.and_then(|v| v.to_array().ok())
.ok_or_else(|| anyhow!("Invalid value_len field"))?
.into_iter()
.map(|v| v.to_byte().unwrap_or(0))
.collect::<Vec<u8>>()
.try_into()
.unwrap_or([0; 255]);
let num_columns = map
.get("num_columns")
.and_then(|v| v.to_byte().ok())
.unwrap_or(0);
let num_rows = map
.get("num_rows")
.and_then(|v| v.to_byte().ok())
.unwrap_or(1);
Ok(Self {
type_info,
data,
value_len,
num_columns,
num_rows,
})
}
_ => Err(anyhow!("VariantValue is not of Object type")),
}
}
}
#[test]
fn command_arg_to_bytes() {
let mut arg = MechFsmCommandArgTuple::new();
let _ = arg.push_value_f32(-293.7);
let _ = arg.push_value_f32(-9.9);
let _ = arg.push_value_f32(500.3);
let _ = arg.push_value_f32(180.0);
let _ = arg.push_value_f32(0.0);
let _ = arg.push_value_f32(0.0);
let _ = arg.push_value_u16(1234);
let _ = arg.push_value_bool(true);
println!("arg: {:?}", arg);
let bytes = arg.to_bytes();
println!("BYTES: {:?}", bytes);
for i in 510..527 {
println!("bytes[{}] = {}", i, bytes[i]);
}
for i in 765..775 {
println!("bytes[{}] = {}", i, bytes[i]);
}
println!("bytes[1020] = {}", bytes[1020]);
println!("bytes[1021] = {}", bytes[1021]);
use crc32fast::Hasher;
let mut hasher = Hasher::new();
hasher.update(&bytes);
let crc32 = hasher.finalize();
println!("CRC32 = {}", crc32);
assert_eq!(bytes[1020], 8);
assert_eq!(bytes[1021], 1);
assert_eq!(bytes[2], 10);
assert_eq!(bytes[510], 154);
assert_eq!(bytes[515], 102);
assert_eq!(crc32, 2242037589);
}