sericom_core/debug.rs
1//! As of now, there is only one function, [`run_debug_output`], which is meant
2//! to debug the data being received over the serial connection. In future
3//! updates, this module is intended to be used for running tracing events with
4//! the [`tracing`](https://docs.rs/tracing/latest/tracing/) crate.
5
6use crate::serial_actor::SerialEvent;
7
8/// This function is used for debugging the data that is sent from a device.
9/// It will create a file "debug.txt" and print the data received from the device
10/// as the actual bytes received along with the corresponding ascii characters.
11///
12/// Data is sent from the [`SerialActor`][crate::serial_actor::SerialActor] to this function in batches,
13/// therefore a line written to "debug.txt" may look like this:
14///
15/// "\[04:41:27.550\] RX 9 bytes: \[0D, 0A, 53, 77, 69, 74, 63, 68\]... UTF8: ^M Switch#"
16///
17/// Each line will only print a maximum of 8 bytes, after 8 it will simply write "...".
18pub async fn run_debug_output(mut rx: tokio::sync::broadcast::Receiver<SerialEvent>) {
19 use std::io::{BufWriter, Write};
20 use std::path::Path;
21
22 let (write_tx, write_rx) = std::sync::mpsc::channel::<Vec<u8>>();
23 let write_handle = tokio::task::spawn_blocking(move || {
24 let path = Path::new("./debug.txt");
25 let file = match std::fs::File::create(path) {
26 Ok(f) => f,
27 Err(e) => {
28 eprintln!("Failed to create file: {e}");
29 return;
30 }
31 };
32 let mut writer = BufWriter::with_capacity(48 * 1024, file);
33 let mut last_flush = std::time::Instant::now();
34
35 writeln!(writer, "Session started at: {}", chrono::Utc::now()).ok();
36 while let Ok(data) = write_rx.recv() {
37 // let control_bytes_for_hex: Vec<u8> = data[..std::cmp::min(20, data.len())]
38 // .iter()
39 // .filter(|b| b.is_ascii_control())
40 // .cloned()
41 // .collect();
42 // Only prints the bytes of ASCII escape characters
43 // writeln!(
44 // writer,
45 // "RX {} bytes: {:02X?}{} UTF8: {}",
46 // data.len(),
47 // control_bytes_for_hex,
48 // if data.len() > 20 { "..." } else { "" },
49 // String::from_utf8_lossy(&data)
50 // )
51 // .ok();
52 // Prints bytes of all characters
53 writeln!(
54 writer,
55 "[{}] RX {} bytes: {:02X?}{} UTF8: {}",
56 chrono::Utc::now().format("%H:%M:%S%.3f"),
57 data.len(),
58 &data[..std::cmp::min(20, data.len())],
59 if data.len() > 10 { "..." } else { "" },
60 String::from_utf8_lossy(&data)
61 )
62 .ok();
63
64 let now = std::time::Instant::now();
65 if now.duration_since(last_flush) > std::time::Duration::from_millis(100)
66 || writer.buffer().len() > 32 * 1024
67 {
68 let _ = writer.flush();
69 last_flush = now;
70 }
71 }
72 let _ = writer.flush();
73 });
74
75 let data_streamer = tokio::spawn(async move {
76 let mut write_buf = Vec::with_capacity(4096);
77 let mut batch_timer = tokio::time::interval(tokio::time::Duration::from_millis(200));
78
79 loop {
80 tokio::select! {
81 event = rx.recv() => {
82 match event {
83 Ok(SerialEvent::Data(data)) => {
84 write_buf.extend_from_slice(&data);
85 if write_buf.len() >= 4096 && write_tx.send(std::mem::take(&mut write_buf)).is_err() {
86 break;
87 }
88 }
89 Err(tokio::sync::broadcast::error::RecvError::Lagged(skipped)) => {
90 eprintln!("File writer lagged, skipped {skipped} messages");
91 continue; // Don't break on lag
92 }
93 _ => break,
94 }
95 }
96 _ = batch_timer.tick() => {
97 if !write_buf.is_empty() && write_tx.send(std::mem::take(&mut write_buf)).is_err() {
98 break;
99 }
100 }
101 }
102 }
103 if !write_buf.is_empty() {
104 let _ = write_tx.send(std::mem::take(&mut write_buf));
105 }
106 drop(write_tx);
107 });
108
109 let _ = data_streamer.await;
110 let _ = write_handle.await;
111}