use crate::error::{CliError, CliResult};
use crate::output::{MessageOutput, OutputFormatter};
use canlink_hal::{BackendConfig, BackendRegistry, CanId};
use std::time::{Duration, Instant};
pub fn execute(
backend_name: &str,
channel: u32,
count: usize,
formatter: &OutputFormatter,
) -> CliResult<()> {
let registry = BackendRegistry::global();
if !registry.is_registered(backend_name) {
return Err(CliError::BackendNotFound(backend_name.to_string()));
}
let config = BackendConfig::new(backend_name);
let mut backend = registry.create(backend_name, &config)?;
backend.initialize(&config)?;
backend.open_channel(channel as u8)?;
let mut received = 0;
let timeout = Duration::from_secs(5);
let start = Instant::now();
if !formatter.is_json() {
if count == 0 {
formatter.print_message("Receiving messages (Ctrl+C to stop)...")?;
} else {
formatter.print_message(&format!("Receiving {} message(s)...", count))?;
}
}
loop {
if start.elapsed() > timeout && received == 0 {
backend.close_channel(channel as u8)?;
backend.close()?;
return Err(CliError::Timeout);
}
match backend.receive_message()? {
Some(message) => {
received += 1;
let id_str = match message.id() {
CanId::Standard(id) => format!("0x{:03X}", id),
CanId::Extended(id) => format!("0x{:08X}", id),
};
let data_str = message
.data()
.iter()
.map(|b| format!("{:02X}", b))
.collect::<Vec<_>>()
.join(" ");
let timestamp_str = message.timestamp().map(|ts| {
format!(
"{}.{:06}",
ts.as_micros() / 1_000_000,
ts.as_micros() % 1_000_000
)
});
let mut flags = Vec::new();
if message.flags().contains(canlink_hal::MessageFlags::FD) {
flags.push("FD".to_string());
}
if message.flags().contains(canlink_hal::MessageFlags::BRS) {
flags.push("BRS".to_string());
}
if message.flags().contains(canlink_hal::MessageFlags::ESI) {
flags.push("ESI".to_string());
}
if message.flags().contains(canlink_hal::MessageFlags::RTR) {
flags.push("RTR".to_string());
}
let output = MessageOutput {
id: id_str,
data: data_str,
timestamp: timestamp_str,
flags,
};
if formatter.is_json() {
formatter.print(&output)?;
} else {
println!("{}", output.format_human());
}
if count > 0 && received >= count {
break;
}
}
None => {
std::thread::sleep(Duration::from_millis(10));
if count > 0 && start.elapsed() > timeout {
break;
}
}
}
}
backend.close_channel(channel as u8)?;
backend.close()?;
if received == 0 {
return Err(CliError::NoMessages);
}
if !formatter.is_json() {
formatter.print_success(&format!("Received {} message(s)", received))?;
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_receive_nonexistent_backend() {
let _registry = BackendRegistry::new();
let formatter = OutputFormatter::new(false);
let result = execute("nonexistent", 0, 1, &formatter);
assert!(result.is_err());
}
}