Skip to content

Commit 350e9af

Browse files
committed
draft monitor for device
1 parent c647379 commit 350e9af

File tree

3 files changed

+91
-6
lines changed

3 files changed

+91
-6
lines changed

src/args.rs

+7-1
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,13 @@ pub struct EmulatorArgs {
247247
}
248248

249249
#[derive(Debug, Parser)]
250-
pub struct MonitorArgs {}
250+
pub struct MonitorArgs {
251+
#[arg(long, default_value = None)]
252+
pub port: Option<String>,
253+
254+
#[arg(long, default_value_t = 115_200)]
255+
pub baud_rate: u32,
256+
}
251257

252258
#[derive(Debug, Parser)]
253259
pub struct LogsArgs {

src/commands/logs.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use firefly_types::{serial::Response, Encode};
44
use std::time::Duration;
55

66
pub fn cmd_logs(args: &LogsArgs) -> Result<()> {
7-
let mut port = serialport::new(&args.port, 9600)
7+
let mut port = serialport::new(&args.port, args.baud_rate)
88
.timeout(Duration::from_millis(10))
99
.open()
1010
.context("open the serial port")?;
@@ -39,7 +39,7 @@ pub fn cmd_logs(args: &LogsArgs) -> Result<()> {
3939
}
4040

4141
// Given the binary stream so far, read the first COBS frame and return the rest of bytes.
42-
fn advance(chunk: &[u8]) -> (Vec<u8>, &[u8]) {
42+
pub(super) fn advance(chunk: &[u8]) -> (Vec<u8>, &[u8]) {
4343
// Skip the partial frame: all bytes before the separator.
4444
let maybe = chunk.iter().enumerate().find(|(_, b)| **b == 0x00);
4545
let Some((start, _)) = maybe else {

src/commands/monitor.rs

+82-3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use super::logs::advance;
12
use crate::args::MonitorArgs;
23
use crate::net::connect;
34
use anyhow::{Context, Result};
@@ -14,6 +15,8 @@ const RBORD: u16 = 21;
1415
const KB: u32 = 1024;
1516
const MB: u32 = 1024 * KB;
1617

18+
type Port = Box<dyn serialport::SerialPort>;
19+
1720
#[derive(Default)]
1821
struct Stats {
1922
update: Option<serial::Fuel>,
@@ -22,17 +25,65 @@ struct Stats {
2225
mem: Option<serial::Memory>,
2326
}
2427

28+
impl Stats {
29+
const fn is_default(&self) -> bool {
30+
self.update.is_none() && self.render.is_none() && self.cpu.is_none() && self.mem.is_none()
31+
}
32+
}
33+
2534
pub fn cmd_monitor(_vfs: &Path, args: &MonitorArgs) -> Result<()> {
2635
execute!(io::stdout(), terminal::EnterAlternateScreen).context("enter alt screen")?;
2736
execute!(io::stdout(), cursor::Hide).context("hide cursor")?;
2837
terminal::enable_raw_mode().context("enable raw mode")?;
29-
let res = monitor_emulator(args);
38+
let res = if let Some(port) = &args.port {
39+
monitor_device(port, args)
40+
} else {
41+
monitor_emulator()
42+
};
3043
terminal::disable_raw_mode().context("disable raw mode")?;
3144
execute!(io::stdout(), terminal::LeaveAlternateScreen).context("leave alt screen")?;
3245
res
3346
}
3447

35-
fn monitor_emulator(_args: &MonitorArgs) -> Result<()> {
48+
fn monitor_device(port: &str, args: &MonitorArgs) -> Result<()> {
49+
let mut port = connect_device(port, args)?;
50+
let mut stats = Stats::default();
51+
let mut buf = Vec::new();
52+
loop {
53+
if should_exit() {
54+
return Ok(());
55+
}
56+
buf = read_device(&mut port, buf, &mut stats)?;
57+
render_stats(&stats).context("render stats")?;
58+
}
59+
}
60+
61+
/// Connect to running emulator using serial USB port (JTag-over-USB).
62+
fn connect_device(port: &str, args: &MonitorArgs) -> Result<Port> {
63+
let mut port = serialport::new(port, args.baud_rate)
64+
.timeout(Duration::from_millis(10))
65+
.open()
66+
.context("open the serial port")?;
67+
68+
execute!(
69+
io::stdout(),
70+
terminal::Clear(terminal::ClearType::All),
71+
cursor::MoveTo(0, 0),
72+
style::Print("waiting for stats..."),
73+
)?;
74+
75+
// enable stats collection
76+
{
77+
let req = serial::Request::Stats(true);
78+
let buf = req.encode_vec().context("encode request")?;
79+
port.write_all(&buf[..]).context("send request")?;
80+
port.flush().context("flush request")?;
81+
}
82+
83+
Ok(port)
84+
}
85+
86+
fn monitor_emulator() -> Result<()> {
3687
let mut stream = connect_emulator()?;
3788
let mut stats = Stats::default();
3889
loop {
@@ -44,7 +95,7 @@ fn monitor_emulator(_args: &MonitorArgs) -> Result<()> {
4495
}
4596
}
4697

47-
/// Receive and parse stats from emulator.
98+
/// Receive and parse one stats message from emulator.
4899
fn read_emulator(mut stream: TcpStream, stats: &mut Stats) -> Result<TcpStream> {
49100
let mut buf = vec![0; 64];
50101
let size = stream.read(&mut buf).context("read response")?;
@@ -56,6 +107,31 @@ fn read_emulator(mut stream: TcpStream, stats: &mut Stats) -> Result<TcpStream>
56107
Ok(stream)
57108
}
58109

110+
/// Receive and parse one stats message from device.
111+
fn read_device(port: &mut Port, mut buf: Vec<u8>, stats: &mut Stats) -> Result<Vec<u8>> {
112+
let mut chunk = vec![0; 64];
113+
let n = match port.read(chunk.as_mut_slice()) {
114+
Ok(n) => n,
115+
Err(err) => {
116+
if err.kind() == std::io::ErrorKind::TimedOut {
117+
return Ok(buf);
118+
}
119+
return Err(err).context("read from serial port");
120+
}
121+
};
122+
123+
buf.extend_from_slice(&chunk[..n]);
124+
loop {
125+
let (frame, rest) = advance(&buf);
126+
buf = Vec::from(rest);
127+
if frame.is_empty() {
128+
break;
129+
}
130+
parse_stats(stats, &frame)?;
131+
}
132+
Ok(buf)
133+
}
134+
59135
/// Parse raw stats message using postcard. Does NOT handle COBS frames.
60136
fn parse_stats(stats: &mut Stats, buf: &[u8]) -> Result<()> {
61137
let resp = serial::Response::decode(buf).context("decode response")?;
@@ -136,6 +212,9 @@ fn should_exit() -> bool {
136212

137213
/// Display stats in the terminal.
138214
fn render_stats(stats: &Stats) -> Result<()> {
215+
if stats.is_default() {
216+
return Ok(());
217+
}
139218
execute!(io::stdout(), terminal::Clear(terminal::ClearType::All))?;
140219
if let Some(cpu) = &stats.cpu {
141220
render_cpu(cpu).context("render cpu table")?;

0 commit comments

Comments
 (0)