crazyflie_lib/subsystems/
log.rs1use crate::crtp_utils::{TocCache, WaitForPacket};
40use crate::{Error, Result, Value, ValueType};
41use crazyflie_link::Packet;
42use flume as channel;
43use futures::lock::Mutex;
44use serde::{Deserialize, Serialize};
45use std::collections::HashMap;
46use std::convert::TryInto;
47use std::sync::Weak;
48use std::{collections::BTreeMap, convert::TryFrom, sync::Arc, time::Duration};
49
50use crate::crazyflie::LOG_PORT;
51
52#[derive(Debug)]
58pub struct Log {
59 uplink: channel::Sender<Packet>,
60 control_downlink: Arc<Mutex<channel::Receiver<Packet>>>,
61 toc: Arc<BTreeMap<String, (u16, LogItemInfo)>>,
62 next_block_id: Mutex<u8>,
63 data_channels: Arc<Mutex<BTreeMap<u8, flume::Sender<Packet>>>>,
64 active_blocks: Mutex<BTreeMap<u8, Weak<()>>>,
65}
66
67fn not_found(name: &str) -> Error {
68 Error::ParamError(format!("Log variable {} not found", name))
69}
70
71const CONTROL_CHANNEL: u8 = 1;
72
73const DELETE_BLOCK: u8 = 2;
74const START_BLOCK: u8 = 3;
75const STOP_BLOCK: u8 = 4;
76const RESET: u8 = 5;
77const CREATE_BLOCK_V2: u8 = 6;
78const APPEND_BLOCK_V2: u8 = 7;
79
80impl Log {
81 pub(crate) async fn new<T>(
82 downlink: channel::Receiver<Packet>,
83 uplink: channel::Sender<Packet>,
84 toc_cache: T,
85 ) -> Result<Self>
86 where
87 T: TocCache,
88 {
89 let (toc_downlink, control_downlink, data_downlink, _) =
90 crate::crtp_utils::crtp_channel_dispatcher(downlink);
91
92 let toc = crate::crtp_utils::fetch_toc(LOG_PORT, uplink.clone(), toc_downlink, toc_cache).await?;
93 let toc = Arc::new(toc);
94
95 let control_downlink = Arc::new(Mutex::new(control_downlink));
96
97 let next_block_id = Mutex::new(0);
98
99 let data_channels = Arc::new(Mutex::new(BTreeMap::new()));
100
101 let active_blocks = Mutex::new(BTreeMap::new());
102
103 let log = Self {
104 uplink,
105 control_downlink,
106 toc,
107 next_block_id,
108 data_channels,
109 active_blocks,
110 };
111 log.reset().await?;
112 log.spawn_data_dispatcher(data_downlink).await;
113
114 Ok(log)
115 }
116
117 async fn reset(&self) -> Result<()> {
118 let downlink = self.control_downlink.lock().await;
119
120 let pk = Packet::new(LOG_PORT, CONTROL_CHANNEL, vec![RESET]);
121 self.uplink
122 .send_async(pk)
123 .await
124 .map_err(|_| Error::Disconnected)?;
125
126 let pk = downlink
127 .wait_packet(LOG_PORT, CONTROL_CHANNEL, &[RESET])
128 .await?;
129 assert_eq!(pk.get_data()[2], 0);
130
131 Ok(())
132 }
133
134 async fn spawn_data_dispatcher(&self, data_downlink: flume::Receiver<Packet>) {
135 let data_channels = self.data_channels.clone();
136 tokio::spawn(async move {
137 while let Ok(packet) = data_downlink.recv_async().await {
138 if packet.get_data().len() > 1 {
139 let block_id = packet.get_data()[0];
140 let data_channels = data_channels.lock().await;
141 if data_channels.contains_key(&block_id)
142 && data_channels
143 .get(&block_id)
144 .unwrap()
145 .send_async(packet)
146 .await
147 .is_err()
148 {
149 break;
150 }
151 }
152 }
153 data_channels.lock().await.clear();
154 });
155 }
156
157 pub fn names(&self) -> Vec<String> {
162 self.toc.keys().cloned().collect()
163 }
164
165 pub fn get_type(&self, name: &str) -> Result<ValueType> {
167 Ok(self
168 .toc
169 .get(name)
170 .ok_or_else(|| not_found(name))?
171 .1
172 .item_type)
173 }
174
175 async fn generate_next_block_id(&self) -> Result<u8> {
176 let mut next_block_id = self.next_block_id.lock().await;
177 if *next_block_id == u8::MAX {
178 return Err(Error::LogError("No more block ID available!".into()));
179 }
180 let id = *next_block_id;
181 *next_block_id += 1;
182 Ok(id)
183 }
184
185 async fn cleanup_blocks(&self) -> Result<()> {
187 let mut active_blocks = self.active_blocks.lock().await;
188
189 for (block_id, canary) in active_blocks.clone().into_iter() {
190 if canary.upgrade() == None {
191 let control_downlink = self.control_downlink.lock().await;
193
194 let pk = Packet::new(LOG_PORT, CONTROL_CHANNEL, vec![DELETE_BLOCK, block_id]);
195 self.uplink
196 .send_async(pk)
197 .await
198 .map_err(|_| Error::Disconnected)?;
199
200 let pk = control_downlink
201 .wait_packet(LOG_PORT, CONTROL_CHANNEL, &[DELETE_BLOCK, block_id])
202 .await?;
203 let error = pk.get_data()[2];
204
205 if error != 0 {
206 return Err(Error::LogError(format!(
207 "Protocol error when deleting block: {}",
208 error
209 )));
210 }
211
212 active_blocks.remove_entry(&block_id);
213 }
214 }
215
216 Ok(())
217 }
218
219 pub async fn create_block(&self) -> Result<LogBlock> {
236 self.cleanup_blocks().await?;
237
238 let block_id = self.generate_next_block_id().await?;
239 let control_downlink = self.control_downlink.lock().await;
240
241 let pk = Packet::new(LOG_PORT, CONTROL_CHANNEL, vec![CREATE_BLOCK_V2, block_id]);
242 self.uplink
243 .send_async(pk)
244 .await
245 .map_err(|_| Error::Disconnected)?;
246
247 let pk = control_downlink
248 .wait_packet(LOG_PORT, CONTROL_CHANNEL, &[CREATE_BLOCK_V2, block_id])
249 .await?;
250 let error = pk.get_data()[2];
251
252 if error != 0 {
253 return Err(Error::LogError(format!(
254 "Protocol error when creating block: {}",
255 error
256 )));
257 }
258
259 let (tx, rx) = flume::unbounded();
261 self.data_channels.lock().await.insert(block_id, tx);
262
263 let canary = Arc::new(());
264 self.active_blocks
265 .lock()
266 .await
267 .insert(block_id, Arc::downgrade(&canary));
268
269 Ok(LogBlock {
270 _canary: canary,
271 toc: Arc::downgrade(&self.toc),
272 uplink: self.uplink.clone(),
273 control_downlink: Arc::downgrade(&self.control_downlink),
274 block_id,
275 variables: Vec::new(),
276 data_channel: rx,
277 })
278 }
279}
280
281#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
282struct LogItemInfo {
283 item_type: ValueType,
284}
285
286impl TryFrom<u8> for LogItemInfo {
287 type Error = Error;
288
289 fn try_from(log_type: u8) -> Result<Self> {
290 let item_type = match log_type {
291 1 => ValueType::U8,
292 2 => ValueType::U16,
293 3 => ValueType::U32,
294 4 => ValueType::I8,
295 5 => ValueType::I16,
296 6 => ValueType::I32,
297 7 => ValueType::F32,
298 8 => ValueType::F16,
299 _ => {
300 return Err(Error::ProtocolError(format!(
301 "Invalid log item type: {}",
302 log_type
303 )))
304 }
305 };
306
307 Ok(LogItemInfo { item_type })
308 }
309}
310
311impl TryInto<u8> for LogItemInfo {
312 type Error = Error;
313
314 fn try_into(self) -> Result<u8> {
315 let value = match self.item_type {
316 ValueType::U8 => 1,
317 ValueType::U16 => 2,
318 ValueType::U32 => 3,
319 ValueType::I8 => 4,
320 ValueType::I16 => 5,
321 ValueType::I32 => 6,
322 ValueType::F32 => 7,
323 ValueType::F16 => 8,
324 _ => {
325 return Err(Error::LogError(format!(
326 "Value type {:?} not handled by log",
327 self.item_type
328 )))
329 }
330 };
331 Ok(value)
332 }
333}
334
335pub struct LogBlock {
344 _canary: Arc<()>,
345 toc: Weak<BTreeMap<String, (u16, LogItemInfo)>>,
346 uplink: channel::Sender<Packet>,
347 control_downlink: Weak<Mutex<channel::Receiver<Packet>>>,
348 block_id: u8,
349 variables: Vec<(String, ValueType)>,
350 data_channel: flume::Receiver<Packet>,
351}
352
353impl LogBlock {
354 pub async fn start(self, period: LogPeriod) -> Result<LogStream> {
364 let control_uplink = self.control_downlink.upgrade().ok_or(Error::Disconnected)?;
365 let control_uplink = control_uplink.lock().await;
366
367 let pk = Packet::new(
368 LOG_PORT,
369 CONTROL_CHANNEL,
370 vec![START_BLOCK, self.block_id, period.0],
371 );
372 self.uplink
373 .send_async(pk)
374 .await
375 .map_err(|_| Error::Disconnected)?;
376
377 let answer = control_uplink
378 .wait_packet(LOG_PORT, CONTROL_CHANNEL, &[START_BLOCK, self.block_id])
379 .await?;
380 if answer.get_data().len() != 3 {
381 return Err(Error::ProtocolError(
382 "Malformed Log control packet".to_owned(),
383 ));
384 }
385 let error_code = answer.get_data()[2];
386 if error_code != 0 {
387 return Err(Error::LogError(format!(
388 "Error starting lock: {}",
389 error_code
390 )));
391 }
392
393 Ok(LogStream { log_block: self })
394 }
395
396 pub async fn add_variable(&mut self, name: &str) -> Result<()> {
404 let toc = self.toc.upgrade().ok_or(Error::Disconnected)?;
405 let (variable_id, info) = toc.get(name).ok_or(Error::VariableNotFound)?;
406
407 let control_uplink = self.control_downlink.upgrade().ok_or(Error::Disconnected)?;
409 let control_uplink = control_uplink.lock().await;
410
411 let mut payload = vec![APPEND_BLOCK_V2, self.block_id, (*info).try_into()?];
412 payload.extend_from_slice(&variable_id.to_le_bytes());
413 let pk = Packet::new(LOG_PORT, CONTROL_CHANNEL, payload);
414 self.uplink
415 .send_async(pk)
416 .await
417 .map_err(|_| Error::Disconnected)?;
418
419 let answer = control_uplink
420 .wait_packet(LOG_PORT, CONTROL_CHANNEL, &[APPEND_BLOCK_V2, self.block_id])
421 .await?;
422 if answer.get_data().len() != 3 {
423 return Err(Error::ProtocolError(
424 "Malformed Log control packet".to_owned(),
425 ));
426 }
427 let error_code = answer.get_data()[2];
428 if error_code != 0 {
429 return Err(Error::LogError(format!(
430 "Error appending variable to block: {}",
431 error_code
432 )));
433 }
434
435 self.variables.push((name.to_owned(), info.item_type));
437
438 Ok(())
439 }
440}
441
442pub struct LogStream {
452 log_block: LogBlock,
453}
454
455impl LogStream {
456 pub async fn stop(self) -> Result<LogBlock> {
464 let control_uplink = self
465 .log_block
466 .control_downlink
467 .upgrade()
468 .ok_or(Error::Disconnected)?;
469 let control_uplink = control_uplink.lock().await;
470
471 let pk = Packet::new(
472 LOG_PORT,
473 CONTROL_CHANNEL,
474 vec![STOP_BLOCK, self.log_block.block_id],
475 );
476 self.log_block
477 .uplink
478 .send_async(pk)
479 .await
480 .map_err(|_| Error::Disconnected)?;
481
482 let answer = control_uplink
483 .wait_packet(
484 LOG_PORT,
485 CONTROL_CHANNEL,
486 &[STOP_BLOCK, self.log_block.block_id],
487 )
488 .await?;
489 if answer.get_data().len() != 3 {
490 return Err(Error::ProtocolError(
491 "Malformed Log control packet".to_owned(),
492 ));
493 }
494 let error_code = answer.get_data()[2];
495 if error_code != 0 {
496 return Err(Error::LogError(format!(
497 "Error starting lock: {}",
498 error_code
499 )));
500 }
501
502 Ok(self.log_block)
503 }
504
505 pub async fn next(&self) -> Result<LogData> {
512 let packet = self
513 .log_block
514 .data_channel
515 .recv_async()
516 .await
517 .map_err(|_| Error::Disconnected)?;
518
519 self.decode_packet(&packet.get_data()[1..])
520 }
521
522 fn decode_packet(&self, data: &[u8]) -> Result<LogData> {
523 let mut timestamp = data[0..=2].to_vec();
524 timestamp.push(0);
525 let timestamp = u32::from_le_bytes(timestamp.try_into().unwrap());
527
528 let mut index = 3;
529 let mut log_data = HashMap::new();
530 for (name, value_type) in &self.log_block.variables {
531 let byte_length = value_type.byte_length();
532 log_data.insert(
533 name.clone(),
534 Value::from_le_bytes(&data[index..(index + byte_length)], *value_type)?,
535 );
536 index += byte_length;
537 }
538
539 Ok(LogData {
540 timestamp,
541 data: log_data,
542 })
543 }
544}
545
546#[derive(Debug)]
554pub struct LogData {
555 pub timestamp: u32,
557 pub data: HashMap<String, Value>,
559}
560
561pub struct LogPeriod(u8);
571
572impl LogPeriod {
573 pub fn from_millis(millis: u64) -> Result<Self> {
578 Duration::from_millis(millis).try_into()
579 }
580}
581
582impl TryFrom<Duration> for LogPeriod {
583 type Error = Error;
584
585 fn try_from(value: Duration) -> Result<Self> {
586 let period_ms = value.as_millis();
587 let period_arg = period_ms / 10;
588 if period_arg == 0 || period_arg > 255 {
589 return Err(Error::LogError(
590 "Invalid log period, should be between 10ms and 2550ms".to_owned(),
591 ));
592 }
593 Ok(LogPeriod(period_arg as u8))
594 }
595}
596
597#[cfg(test)]
598mod tests {
599 use super::*;
600
601 #[test]
602 fn log_toc_cache_format_stability() {
603 let info = LogItemInfo { item_type: ValueType::U8 };
607 let json = serde_json::to_string(&info).unwrap();
608 assert_eq!(json, r#"{"item_type":"U8"}"#);
609 }
610}