1use core::convert::Infallible;
6
7use zencan_common::{
8 constants::values::SAVE_CMD,
9 objects::{ObjectCode, SubInfo},
10 sdo::AbortCode,
11 AtomicCell,
12};
13
14use crate::object_dict::{ODEntry, ObjectAccess};
15
16pub type StoreObjectsCallback =
18 dyn Fn(&mut dyn embedded_io::Read<Error = Infallible>, usize) + Sync;
19
20#[derive(Default)]
21#[allow(missing_debug_implementations)]
22pub struct StorageContext {
24 pub(crate) store_callback: AtomicCell<Option<&'static StoreObjectsCallback>>,
25}
26
27impl StorageContext {
28 pub const fn new() -> Self {
30 Self {
31 store_callback: AtomicCell::new(None),
32 }
33 }
34}
35
36#[allow(missing_debug_implementations)]
38pub struct StorageCommandObject {
39 od: &'static [ODEntry<'static>],
40 storage_context: &'static StorageContext,
41}
42
43impl StorageCommandObject {
44 pub const fn new(
46 od: &'static [ODEntry<'static>],
47 storage_context: &'static StorageContext,
48 ) -> Self {
49 Self {
50 od,
51 storage_context,
52 }
53 }
54}
55
56impl ObjectAccess for StorageCommandObject {
57 fn read(&self, sub: u8, offset: usize, buf: &mut [u8]) -> Result<usize, AbortCode> {
58 match sub {
59 0 => {
60 if offset != 0 || buf.len() != 1 {
61 Err(AbortCode::DataTypeMismatch)
62 } else {
63 buf[0] = 1;
64 Ok(1)
65 }
66 }
67 1 => {
68 let mut value = 0u32;
71 if self.storage_context.store_callback.load().is_some() {
72 value |= 1;
73 }
74 let value_bytes = value.to_le_bytes();
75 if offset < value_bytes.len() {
76 let read_len = buf.len().min(value_bytes.len() - offset);
77 buf[..read_len].copy_from_slice(&value_bytes[offset..offset + read_len]);
78 Ok(read_len)
79 } else {
80 Ok(0)
81 }
82 }
83 _ => Err(AbortCode::NoSuchSubIndex),
84 }
85 }
86
87 fn read_size(&self, sub: u8) -> Result<usize, AbortCode> {
88 match sub {
89 0 => Ok(1),
90 1 => Ok(4),
91 _ => Err(AbortCode::NoSuchSubIndex),
92 }
93 }
94
95 fn write(&self, sub: u8, data: &[u8]) -> Result<(), AbortCode> {
96 match sub {
97 0 => Err(AbortCode::ReadOnly),
98 1 => {
99 if data.len() != 4 {
100 Err(AbortCode::DataTypeMismatch)
101 } else {
102 let value = u32::from_le_bytes(data[0..4].try_into().unwrap());
103 if value == SAVE_CMD {
105 if let Some(cb) = self.storage_context.store_callback.load() {
106 crate::persist::serialize(self.od, cb);
107 Ok(())
108 } else {
109 Err(AbortCode::ResourceNotAvailable)
110 }
111 } else {
112 Err(AbortCode::IncompatibleParameter)
113 }
114 }
115 }
116 _ => Err(AbortCode::NoSuchSubIndex),
117 }
118 }
119
120 fn object_code(&self) -> ObjectCode {
121 ObjectCode::Record
122 }
123
124 fn sub_info(&self, sub: u8) -> Result<SubInfo, AbortCode> {
125 match sub {
126 0 => Ok(SubInfo::MAX_SUB_NUMBER),
127 1 => Ok(SubInfo::new_u32().rw_access()),
128 _ => Err(AbortCode::NoSuchSubIndex),
129 }
130 }
131}