telemetry improvements / cli CSV export

This commit is contained in:
2026-05-20 10:13:16 -07:00
parent 5e8d6e2a34
commit ab09c98084
8 changed files with 396 additions and 46 deletions
+5 -1
View File
@@ -5,7 +5,11 @@ edition = "2024"
[dependencies]
async-hid = "0.5.1"
clap = { version = "4.6.1", features = ["derive"] }
colog = "1.4.0"
csv = "1.4.0"
deku = "0.20.3"
futures-lite = "2.6.1"
log = { version = "0.4.29", features = ["std"] }
pollster = { version = "0.4.0", features = ["macro"] }
shared = { path = "../shared" }
shared = { path = "../shared", features = ["serde"] }
+63 -1
View File
@@ -1,10 +1,67 @@
use std::io;
use async_hid::{AsyncHidRead, HidBackend, HidResult};
use clap::{Parser, ValueEnum};
use deku::DekuContainerRead;
use futures_lite::StreamExt;
use shared::hid::AudioTelemetryReport;
#[derive(Clone, Copy, Debug, ValueEnum)]
enum Format {
Debug,
Csv,
}
#[derive(Parser, Debug)]
#[command(version, about)]
struct Args {
#[arg(short, long, default_value = "debug")]
format: Format,
}
trait StateEmitter<W: io::Write> {
fn from_writer(writer: W) -> Self
where
Self: Sized;
fn emit(&mut self, r: &AudioTelemetryReport);
}
struct DebugEmitter<T: io::Write> {
writer: T,
}
impl<W: io::Write> StateEmitter<W> for DebugEmitter<W> {
fn from_writer(writer: W) -> Self {
Self { writer }
}
fn emit(&mut self, r: &AudioTelemetryReport) {
writeln!(self.writer, "{r:?}");
}
}
struct CsvEmitter<W: io::Write> {
csv: csv::Writer<W>,
}
impl<W: io::Write> StateEmitter<W> for CsvEmitter<W> {
fn from_writer(writer: W) -> Self {
Self {
csv: csv::Writer::from_writer(writer),
}
}
fn emit(&mut self, r: &AudioTelemetryReport) {
if let Err(e) = self.csv.serialize(r) {
eprintln!("Serialization error: {e:?}");
} else {
self.csv.flush().ok();
}
}
}
#[pollster::main]
async fn main() -> HidResult<()> {
colog::init();
let args = Args::parse();
let usbhid = HidBackend::default();
let dev = usbhid
.enumerate()
@@ -13,12 +70,17 @@ async fn main() -> HidResult<()> {
.await
.expect("GUAC device not found or not accessible (try as root?)");
let mut reader = dev.open_readable().await?;
let mut writer: Box<dyn StateEmitter<_>> = match args.format {
Format::Debug => Box::new(DebugEmitter::from_writer(io::stdout())),
Format::Csv => Box::new(CsvEmitter::from_writer(io::stdout())),
};
let mut buf = [0u8; core::mem::size_of::<AudioTelemetryReport>()];
while let Ok(r) = reader.read_input_report(&mut buf).await {
log::debug!("read {}: {:?}", r, &buf[..r]);
let buf = &buf[..r];
match AudioTelemetryReport::from_bytes((buf, 0)) {
Ok((_, r)) => println!("{:?}", r),
Ok((_, r)) => writer.emit(&r),
Err(e) => eprintln!("Unable to parse report: {:?}", e),
}
}