From c005efa9669646d343bc9a7464071a0ad39a0a4b Mon Sep 17 00:00:00 2001 From: Austin Gill Date: Mon, 7 Aug 2023 10:59:46 -0500 Subject: [PATCH] Add example application to experiment with the drivers --- Cargo.toml | 7 ++++ examples/forward.rs | 100 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 107 insertions(+) create mode 100644 examples/forward.rs diff --git a/Cargo.toml b/Cargo.toml index 24cd3c1..4d043d3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,3 +12,10 @@ socketcan = { version = "2.0.0", optional = true } [features] default = ["socketcan"] socketcan = ["dep:socketcan"] + +[dev-dependencies] +clap = { version = "4.3.19", features = ["derive"] } +ctrlc = "3.4.0" +# TODO: Add optional tracing to the main library +tracing = "0.1.37" +tracing-subscriber = "0.3.17" diff --git a/examples/forward.rs b/examples/forward.rs new file mode 100644 index 0000000..4378405 --- /dev/null +++ b/examples/forward.rs @@ -0,0 +1,100 @@ +use std::sync::mpsc::channel; + +use ag_iso_stack::driver::{Driver, DriverReadError, DriverWriteError, Frame, SocketcanDriver}; +use clap::Parser; + +/// Forward CAN traffic from one interface to another +#[derive(Debug, Parser)] +#[clap(name = "forward", verbatim_doc_comment)] +struct Options { + /// The log level + #[clap(short, long, default_value_t = tracing::Level::DEBUG)] + pub log_level: tracing::Level, + + /// The interface to read traffic from + /// + /// Can be either a string interface name, or an integer interface index + #[clap(short, long, default_value_t = String::from("can0"))] + pub input_interface: String, + + /// The interface to write traffic to + /// + /// Can be either a string interface name, or an integer interface index + #[clap(short, long, default_value_t = String::from("can1"))] + pub output_interface: String, + + /// Whether to use blocking send()/recv()s + #[clap(short, long)] + pub blocking: bool, +} + +fn create_driver(iface: &str) -> impl Driver { + if let Ok(index) = iface.parse::() { + SocketcanDriver::new_by_index(index) + } else { + SocketcanDriver::new_by_name(iface) + } +} + +fn read(input: &mut impl Driver, frame: &mut Frame, blocking: bool) -> Result<(), DriverReadError> { + if blocking { + input.read_blocking(frame) + } else { + input.read_nonblocking(frame) + } +} + +fn write(output: &mut impl Driver, frame: &Frame, blocking: bool) -> Result<(), DriverWriteError> { + if blocking { + output.write_blocking(frame) + } else { + output.write_nonblocking(frame) + } +} + +fn main() { + let opts = Options::parse(); + + let subscriber = tracing_subscriber::fmt() + // ... add configuration + .finish(); + tracing::subscriber::set_global_default(subscriber) + .map_err(|_err| eprintln!("Unable to set global default subscriber")) + .unwrap(); + + tracing::info!( + "Forwarding CAN traffic from {} to {}", + opts.input_interface, + opts.output_interface + ); + + let mut input = create_driver(&opts.input_interface); + let mut output = create_driver(&opts.output_interface); + + input.open().unwrap(); + output.open().unwrap(); + + let (tx, rx) = channel(); + ctrlc::set_handler(move || tx.send(true).unwrap()).unwrap(); + + loop { + if rx.try_recv().is_ok() { + break; + } + + let mut frame = Frame::default(); + + match read(&mut input, &mut frame, opts.blocking) { + Ok(_) => { + tracing::info!("Read frame: {frame:?}"); + tracing::info!("Attempting to write frame"); + match write(&mut output, &frame, opts.blocking) { + Ok(_) => tracing::info!("Wrote frame: {frame:?}"), + Err(e) => tracing::info!("Failed to write frame: {e:?}"), + } + } + Err(DriverReadError::NoFrameReady) => {} + Err(e) => tracing::error!("Failed to read frame: {e:?}"), + } + } +}