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}