crazyflie_lib/subsystems/memory/
mod.rs

1//! # Memory subsystem
2//!
3//! The Crazyflie exposes a memory subsystem that allows to easily read and
4//! write various memories in the Crazyflie.
5//!
6//! During connection the memory subsystem fetches information about all the
7//! memories present in the Crazyflie. For interacting with a specific memory
8//! it's possible to using a wrapper, there's one for each memory type, or to
9//! get raw read and write access to the memory.
10
11use crate::{crtp_utils::WaitForPacket, Error, Result};
12use crazyflie_link::Packet;
13use flume as channel;
14use std::{collections::HashMap, convert::{TryFrom, TryInto}};
15use std::sync::Arc;
16use tokio::sync::Mutex;
17
18mod memory_types;
19mod eeprom_config;
20mod raw;
21mod ow;
22
23use crate::crazyflie::MEMORY_PORT;
24
25pub use memory_types::*;
26pub use eeprom_config::*;
27pub use raw::*;
28pub use ow::*;
29
30/// # Access to the Crazyflie Memory Subsystem
31///
32/// This struct provide methods to interact with the memory subsystem. See the
33/// [memory module documentation](crate::subsystems::memory) for more context and information.
34#[derive(Debug)]
35pub struct Memory {
36    memories: Vec<MemoryDevice>,
37    backends: Vec<Mutex<Option<MemoryBackend>>>,
38    memory_read_dispatcher: MemoryDispatcher,
39    memory_write_dispatcher: MemoryDispatcher,
40}
41
42const INFO_CHANNEL: u8 = 0;
43const READ_CHANNEL: u8 = 1;
44const WRITE_CHANNEL: u8 = 2;
45
46const _CMD_INFO_VER: u8 = 0;
47const CMD_INFO_NBR: u8 = 1;
48const CMD_INFO_DETAILS: u8 = 2;
49
50#[derive(Debug)]
51struct MemoryDispatcher {
52  senders: Arc<Mutex<HashMap<u8, channel::Sender<Packet>>>>,
53}
54
55impl MemoryDispatcher {
56  fn new(downlink: channel::Receiver<Packet>, channel: u8) -> Self {
57
58    let senders: Arc<Mutex<HashMap<u8, channel::Sender<Packet>>>> = Arc::new(Mutex::new(HashMap::new()));
59    let internal_senders = senders.clone();
60
61    tokio::spawn(async move {
62      while let Ok(pk) = downlink.recv_async().await {
63        if pk.get_channel() == channel {
64          let memory_id = pk.get_data()[0];
65          if let Some(sender) = internal_senders.lock().await.get(&memory_id) {
66            let _ = sender.send_async(pk).await;
67          } else {
68            println!("Warning: Received memory read response for unknown memory ID {}", memory_id);
69          }
70        } else {
71          println!("Warning: Received packet on unexpected channel {}", pk.get_channel());
72        }
73      }
74    });
75
76    Self {
77      senders: senders,
78    }
79  }
80
81  async fn get_channel(&mut self, memory_id: u8) -> channel::Receiver<Packet> {
82    if !self.senders.lock().await.contains_key(&memory_id) {
83      let (tx, rx) = channel::unbounded();
84      self.senders.lock().await.insert(memory_id, tx);
85      rx
86    } else {
87      panic!("Channel for memory ID {} already exists", memory_id)
88    }
89  }
90}
91
92impl Memory {
93    pub(crate) async fn new(
94        downlink: channel::Receiver<Packet>,
95        uplink: channel::Sender<Packet>,
96    ) -> Result<Self> {
97        let (info_channel_downlink, read_channel_downlink, write_channel_downlink, _misc_downlink) =
98            crate::crtp_utils::crtp_channel_dispatcher(downlink);
99
100        let mut memory = Self {
101            memories: Vec::new(),
102            backends: Vec::new(),
103            memory_read_dispatcher: MemoryDispatcher::new(read_channel_downlink.clone(), READ_CHANNEL),
104            memory_write_dispatcher: MemoryDispatcher::new(write_channel_downlink.clone(), WRITE_CHANNEL),
105        };
106
107        memory.update_memories(uplink.clone(), info_channel_downlink).await?;
108
109        Ok(memory)
110    }
111
112    async fn update_memories(&mut self, uplink: channel::Sender<Packet>, downlink: channel::Receiver<Packet>) -> Result<()> {
113      let pk = Packet::new(MEMORY_PORT, INFO_CHANNEL, vec![CMD_INFO_NBR]);
114      uplink
115          .send_async(pk)
116          .await
117          .map_err(|_| Error::Disconnected)?;
118
119      let pk = downlink.wait_packet(MEMORY_PORT, INFO_CHANNEL, &[CMD_INFO_NBR]).await?;
120      let memory_count = pk.get_data()[1];
121
122      for i in 0..memory_count {
123        let pk = Packet::new(MEMORY_PORT, INFO_CHANNEL, vec![CMD_INFO_DETAILS, i]);
124        uplink
125            .send_async(pk)
126            .await
127            .map_err(|_| Error::Disconnected)?;
128
129        let pk = downlink.wait_packet(MEMORY_PORT, INFO_CHANNEL, &[CMD_INFO_DETAILS, i]).await?;
130        let data = pk.get_data();
131        let memory_id = data[1];
132        let memory_type = MemoryType::try_from(data[2])?;
133        let memory_size = u32::from_le_bytes(data[3..7].try_into()?);
134
135        self.memories.push(MemoryDevice {
136          memory_id: memory_id,
137          memory_type: memory_type,
138          size: memory_size
139        });
140
141        self.backends.push(Mutex::new(Some(MemoryBackend {
142          memory_id: memory_id,
143          memory_type: memory_type,
144          uplink: uplink.clone(),
145          read_downlink: self.memory_read_dispatcher.get_channel(memory_id).await,
146          write_downlink: self.memory_write_dispatcher.get_channel(memory_id).await,
147        })));
148      }
149      Ok(())
150
151    }
152
153    /// Get the list of memories in the Crazyflie, optionally filtered by type.
154    /// 
155    /// If `memory_type` is None, all memories are returned
156    /// If `memory_type` is Some(type), only memories of that type are returned
157    /// # Example
158    /// ```no_run
159    /// use crazyflie_lib::subsystems::memory::MemoryType;
160    /// use crazyflie_lib::{Crazyflie, Value, Error};
161    /// use crazyflie_link::LinkContext;
162    /// async fn example() -> Result<(), Error> {
163    ///   let context = LinkContext::new();
164    ///   let cf = Crazyflie::connect_from_uri(&context, "radio://0/60/2M/E7E7E7E7E7").await?;
165    ///   let memories = cf.memory.get_memories(Some(MemoryType::OneWire));
166    ///   Ok(())
167    /// };
168    /// 
169    /// ```
170    /// # Example
171    /// ```no_run
172    /// use crazyflie_lib::subsystems::memory::MemoryType;
173    /// use crazyflie_lib::{Crazyflie, Value, Error};
174    /// use crazyflie_link::LinkContext;
175    /// async fn example() -> Result<(), Error> {
176    ///   let context = LinkContext::new();
177    ///   let cf = Crazyflie::connect_from_uri(&context, "radio://0/60/2M/E7E7E7E7E7").await?;
178    ///   let memories = cf.memory.get_memories(None);
179    ///   Ok(())
180    /// };
181    /// ```
182    /// # Returns
183    /// A vector of references to MemoryDevice structs
184    /// If no memories are found, an empty vector is returned
185    pub fn get_memories(&self, memory_type: Option<MemoryType>) -> Vec<&MemoryDevice> {
186      match memory_type {
187        Some(ty) => self.memories.iter().filter(|m| m.memory_type == ty).collect(),
188        None => self.memories.iter().collect(),
189      }
190    }
191
192    /// Get a specific memory by its ID
193    /// 
194    /// # Arguments
195    /// * `memory` - The MemoryDevice struct representing the memory to get
196    /// # Returns
197    /// An Option containing a reference to the MemoryDevice struct if found, or None if not found
198    pub async fn open_memory<T: FromMemoryBackend>(&self, memory: MemoryDevice) -> Option<Result<T>> {
199      let backend = self.backends.get(memory.memory_id as usize)?.lock().await.take()?;
200      Some(T::from_memory_backend(backend).await)
201    }
202
203    /// Close a memory
204    /// 
205    /// # Arguments
206    /// * `memory_device` - The MemoryDevice struct representing the memory to close
207    /// * `backend` - The MemoryBackend to return to the subsystem
208    pub async fn close_memory<T: FromMemoryBackend>(&self, device: T) {
209      let backend = device.close_memory();
210      if let Some(mutex) = self.backends.get(backend.memory_id as usize) {
211        let mut guard = mutex.lock().await;
212        if guard.is_none() {
213          *guard = Some(backend);
214        } else {
215          println!("Warning: Attempted to close memory ID {} which is already closed", backend.memory_id);
216        }
217      } else {
218        println!("Warning: Attempted to close memory ID {} which does not exist", backend.memory_id);
219      }
220    }
221
222    /// Get a specific memory by its ID and initialize it according to the defaults. Note that the
223    /// values will not be written to the memory by default, the user needs to handle this.
224    /// 
225    /// # Arguments
226    /// * `memory` - The MemoryDevice struct representing the memory to get
227    /// # Returns
228    /// An Option containing a reference to the MemoryDevice struct if found, or None if not found
229    pub async fn initialize_memory<T: FromMemoryBackend>(&self, memory: MemoryDevice) -> Option<Result<T>> {
230        let backend = self.backends.get(memory.memory_id as usize)?.lock().await.take()?;
231        Some(T::initialize_memory_backend(backend).await)
232    }
233
234}