use core::{convert::Infallible, sync::atomic::Ordering};
use portable_atomic::AtomicBool;
use zencan_common::{
constants::values::SAVE_CMD,
objects::{ObjectCode, SubInfo},
sdo::AbortCode,
};
use crate::object_dict::ObjectAccess;
pub type StoreObjectsCallback =
dyn Fn(&mut dyn embedded_io::Read<Error = Infallible>, usize) + Sync;
#[derive(Default)]
#[allow(missing_debug_implementations)]
pub struct StorageContext {
pub(crate) store_flag: AtomicBool,
pub(crate) store_supported: AtomicBool,
}
impl StorageContext {
pub const fn new() -> Self {
Self {
store_flag: AtomicBool::new(false),
store_supported: AtomicBool::new(false),
}
}
}
#[allow(missing_debug_implementations)]
pub struct StorageCommandObject {
storage_context: &'static StorageContext,
}
impl StorageCommandObject {
pub const fn new(storage_context: &'static StorageContext) -> Self {
Self { storage_context }
}
}
impl ObjectAccess for StorageCommandObject {
fn read(&self, sub: u8, offset: usize, buf: &mut [u8]) -> Result<usize, AbortCode> {
match sub {
0 => {
if offset != 0 || buf.len() != 1 {
Err(AbortCode::DataTypeMismatch)
} else {
buf[0] = 1;
Ok(1)
}
}
1 => {
let mut value = 0u32;
if self.storage_context.store_supported.load(Ordering::Relaxed) {
value |= 1;
}
let value_bytes = value.to_le_bytes();
if offset < value_bytes.len() {
let read_len = buf.len().min(value_bytes.len() - offset);
buf[..read_len].copy_from_slice(&value_bytes[offset..offset + read_len]);
Ok(read_len)
} else {
Ok(0)
}
}
_ => Err(AbortCode::NoSuchSubIndex),
}
}
fn read_size(&self, sub: u8) -> Result<usize, AbortCode> {
match sub {
0 => Ok(1),
1 => Ok(4),
_ => Err(AbortCode::NoSuchSubIndex),
}
}
fn write(&self, sub: u8, data: &[u8]) -> Result<(), AbortCode> {
match sub {
0 => Err(AbortCode::ReadOnly),
1 => {
if data.len() != 4 {
Err(AbortCode::DataTypeMismatch)
} else {
let value = u32::from_le_bytes(data[0..4].try_into().unwrap());
if value == SAVE_CMD {
if self.storage_context.store_supported.load(Ordering::Relaxed) {
self.storage_context
.store_flag
.store(true, Ordering::Relaxed);
Ok(())
} else {
Err(AbortCode::ResourceNotAvailable)
}
} else {
Err(AbortCode::IncompatibleParameter)
}
}
}
_ => Err(AbortCode::NoSuchSubIndex),
}
}
fn object_code(&self) -> ObjectCode {
ObjectCode::Record
}
fn sub_info(&self, sub: u8) -> Result<SubInfo, AbortCode> {
match sub {
0 => Ok(SubInfo::MAX_SUB_NUMBER),
1 => Ok(SubInfo::new_u32().rw_access()),
_ => Err(AbortCode::NoSuchSubIndex),
}
}
}