telemetry improvements / cli CSV export
This commit is contained in:
+5
-1
@@ -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
@@ -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),
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user