Skip to main content

grapple_config/
lib.rs

1#![cfg_attr(not(test), no_std)]
2
3#![doc = include_str!("../README.md")]
4
5use core::marker::PhantomData;
6
7#[derive(Debug)]
8pub enum ConfigurationError {
9  BusError,
10  SerialisationError,
11  BlankError
12}
13
14impl ConfigurationError {
15  fn can_retry(&self) -> bool {
16    match self {
17      ConfigurationError::BusError => true,
18      ConfigurationError::SerialisationError => false,
19      ConfigurationError::BlankError => false
20    }
21  }
22}
23
24pub trait ConfigurationMarshal<Config>
25{
26  type Error: Into<ConfigurationError>;
27
28  fn write(&mut self, config: &Config) -> Result<(), Self::Error>;
29  fn read(&mut self) -> Result<Config, Self::Error>;
30
31  fn write_with_retry(&mut self, config: &Config, retries: usize) -> Result<(), ConfigurationError> {
32    let mut i = 0;
33
34    while let Err(e) = self.write(&config) {
35      let config_err = e.into();
36      if !config_err.can_retry() {
37        return Err(config_err)
38      }
39
40      i = i + 1;
41      if i >= retries {
42        return Err(config_err)
43      }
44    }
45
46    return Ok(());
47  }
48
49  fn read_with_retry(&mut self, retries: usize) -> Result<Config, ConfigurationError> {
50    let mut i = 0;
51
52    loop {
53      match self.read() {
54        Ok(cfg) => {
55          return Ok(cfg)
56        },
57        Err(e) => {
58          let config_err = e.into();
59          if !config_err.can_retry() {
60            return Err(config_err)
61          }
62
63          i = i + 1;
64          if i >= retries {
65            return Err(config_err)
66          }
67        }
68      }
69    }
70  }
71}
72
73pub trait GenericConfigurationProvider<Config>
74where
75  Config: Clone
76{
77  fn current(&self) -> &Config;
78  fn current_mut(&mut self) -> &mut Config;
79
80  fn commit(&mut self, reload: bool) -> Result<(), ConfigurationError>;
81  fn reload(&mut self) -> Result<(), ConfigurationError>;
82}
83
84pub struct ConfigurationProvider<Config, Marshal> {
85  volatile: Config,
86  marshal: Marshal,
87  max_retries: usize,
88}
89
90impl<Config, Marshal> ConfigurationProvider<Config, Marshal>
91where
92  Config: Default + Clone,
93  Marshal: ConfigurationMarshal<Config>
94{
95  pub fn new(mut marshal: Marshal, max_retries: usize, load_default_if_blank: bool) -> Result<Self, ConfigurationError> {
96    let current = marshal.read_with_retry(5);
97    match current {
98      Ok(c) => Ok(Self { volatile: c, marshal, max_retries }),
99      Err(ConfigurationError::BlankError) if load_default_if_blank => {
100        let c = Config::default();
101        marshal.write_with_retry(&c, max_retries)?;
102        Ok(Self { volatile: c, marshal, max_retries })
103      },
104      Err(e) => Err(e)
105    }
106  }
107
108  // Will also load default if the config is invalid (such as via an upgrade)
109  pub fn new_or_default(mut marshal: Marshal, max_retries: usize) -> Result<Self, ConfigurationError> {
110    let current = marshal.read_with_retry(5);
111    match current {
112      Ok(c) => Ok(Self { volatile: c, marshal, max_retries }),
113      Err(ConfigurationError::BusError) => {
114        Err(ConfigurationError::BusError)
115      },
116      Err(e) => {
117        let c = Config::default();
118        marshal.write_with_retry(&c, max_retries)?;
119        Ok(Self { volatile: c, marshal, max_retries })
120      },
121    }
122  }
123
124  pub fn set_retries(&mut self, retries: usize) {
125    self.max_retries = retries
126  }
127}
128
129impl<Config, Marshal> GenericConfigurationProvider<Config> for ConfigurationProvider<Config, Marshal>
130where
131  Config: Default + Clone,
132  Marshal: ConfigurationMarshal<Config>
133{
134  fn current(&self) -> &Config {
135    &self.volatile
136  }
137
138  fn current_mut(&mut self) -> &mut Config {
139    &mut self.volatile
140  }
141
142  fn commit(&mut self, reload: bool) -> Result<(), ConfigurationError> {
143    self.marshal.write_with_retry(&self.volatile, self.max_retries)?;
144
145    if reload {
146      self.reload()?;
147    }
148
149    Ok(())
150  }
151
152  fn reload(&mut self) -> Result<(), ConfigurationError> {
153    self.volatile = self.marshal.read_with_retry(self.max_retries)?;
154    Ok(())
155  }
156}
157
158pub struct VolatileMarshal<Config>(PhantomData<Config>);
159
160impl<Config> VolatileMarshal<Config> {
161  pub fn new() -> Self {
162    Self(PhantomData)
163  }
164}
165
166impl<Config> ConfigurationMarshal<Config> for VolatileMarshal<Config>
167where
168  Config: Default
169{
170  type Error = ConfigurationError;
171
172  fn write(&mut self, _config: &Config) -> Result<(), Self::Error> {
173    Ok(())
174  }
175
176  fn read(&mut self) -> Result<Config, Self::Error> {
177    Ok(Config::default())
178  }
179}
180
181#[cfg(feature = "m24c64")]
182pub mod m24c64 {
183  extern crate alloc;
184
185  use core::marker::PhantomData;
186
187  use binmarshal::{DemarshalOwned, Marshal, MarshalError, rw::{BitView, BitWriter, VecBitWriter}};
188  use embedded_hal::blocking::{i2c, delay::DelayMs};
189  use grapple_m24c64::M24C64;
190  use alloc::vec;
191
192  use crate::{ConfigurationError, ConfigurationMarshal};
193
194  pub struct M24C64ConfigurationMarshal<Config, I2C, Delay> {
195    delay: Delay,
196    address_offset: usize,
197    eeprom: M24C64<I2C>,
198    marker: PhantomData<Config>
199  }
200
201  #[derive(Debug)]
202  pub enum M24C64ConfigurationError<E, SErr> {
203    Serialisation(SErr),
204    I2C(E),
205    BlankEeprom
206  }
207
208  impl<E, SErr> Into<ConfigurationError> for M24C64ConfigurationError<E, SErr> {
209    fn into(self) -> ConfigurationError {
210      match self {
211        M24C64ConfigurationError::Serialisation(_) => ConfigurationError::SerialisationError,
212        M24C64ConfigurationError::I2C(_) => ConfigurationError::BusError,
213        M24C64ConfigurationError::BlankEeprom => ConfigurationError::BlankError,
214      }
215    }
216  }
217
218  impl<Config, I2C, Delay> M24C64ConfigurationMarshal<Config, I2C, Delay> {
219    #[allow(unused)]
220    pub fn new(eeprom: M24C64<I2C>, address: usize, delay: Delay) -> Self {
221      Self { delay, address_offset: address, eeprom, marker: PhantomData }
222    }
223  }
224
225  impl<'a, I2C, Delay, Config, E> ConfigurationMarshal<Config> for M24C64ConfigurationMarshal<Config, I2C, Delay>
226  where
227    Config: Marshal<()> + DemarshalOwned + Default + Clone,
228    I2C: i2c::Write<u8, Error = E> + i2c::WriteRead<u8, Error = E>,
229    Delay: DelayMs<u16>
230  {
231    type Error = M24C64ConfigurationError<E, MarshalError>;
232
233    fn write(&mut self, config: &Config) -> Result<(), Self::Error> {
234      let mut writer = VecBitWriter::new();
235      if let Err(e) = config.clone().write(&mut writer, ()) {
236        return Err(Self::Error::Serialisation(e));
237      }
238      self.delay.delay_ms(10u16);
239      let bytes = writer.slice();
240      self.eeprom.write(self.address_offset, &(bytes.len() as u16).to_le_bytes(), &mut self.delay).map_err(|e| Self::Error::I2C(e))?;
241      self.delay.delay_ms(10u16);
242      self.eeprom.write(self.address_offset + 0x02, &bytes[..], &mut self.delay).map_err(|e| Self::Error::I2C(e))?;
243      self.delay.delay_ms(10u16);
244      Ok(())
245    }
246
247    fn read(&mut self) -> Result<Config, Self::Error> {
248      let mut len_buf = [0u8; 2];
249      self.delay.delay_ms(1u16);
250      self.eeprom.read(self.address_offset, &mut len_buf[..]).map_err(|e| Self::Error::I2C(e))?;
251
252      if len_buf[0] == 255 && len_buf[1] == 255 {
253        return Err(Self::Error::BlankEeprom);
254      }
255
256      let mut buf = vec![0u8; u16::from_le_bytes(len_buf) as usize];
257      self.eeprom.read(self.address_offset + 0x02, &mut buf[..]).map_err(|e| Self::Error::I2C(e))?;
258      match Config::read(&mut BitView::new(&buf), ()) {
259        Ok(c) => Ok(c),
260        Err(e) => Err(Self::Error::Serialisation(e)),
261      }
262    }
263  }
264}