libexail 0.1.0

A rust library for communicating with Exail devices through their binary protocol
Documentation
use anyhow::Result;
use clap::Parser;
use libexail::{Message, NavigationBlock, reader::ExailReader};
use std::{collections::HashMap, fs::File};

#[derive(Parser)]
#[command(about = "Parse Exail frames from a binary file")]
struct Args {
    /// Path to the binary file
    file: String,

    /// Print full message debug output
    #[arg(short, long)]
    verbose: bool,
}

fn main() -> Result<()> {
    let args = Args::parse();

    eprintln!("Reading from file: {}", args.file);
    let file = File::open(&args.file)?;
    let reader = ExailReader::new(file);
    let mut stats: HashMap<String, usize> = HashMap::new();

    for result in reader {
        let msg = result?;
        if args.verbose {
            println!("{msg:?}");
        }

        let label = match &msg {
            Message::NavigationData(frame) => {
                if !args.verbose {
                    for block in &frame.navigation {
                        match block {
                            NavigationBlock::Position(p) => {
                                println!(
                                    "Position: lat={:.6} lon={:.6} alt={:.2}m",
                                    p.latitude, p.longitude, p.altitude,
                                );
                            }
                            NavigationBlock::AttitudeHeading(a) => {
                                println!(
                                    "Attitude: hdg={:.1} roll={:.1} pitch={:.1}",
                                    a.heading, a.roll, a.pitch,
                                );
                            }
                            NavigationBlock::SpeedGeographic(s) => {
                                println!(
                                    "Speed: N={:.2} E={:.2} U={:.2} m/s",
                                    s.north, s.east, s.up,
                                );
                            }
                            _ => {}
                        }
                    }
                }
                "NavigationData"
            }
            Message::InputData(_) => "InputData",
            Message::Command(_) => "Command",
            Message::Answer(_) => "Answer",
        };
        *stats.entry(label.to_string()).or_insert(0) += 1;
    }

    eprintln!("\n=== Message Statistics ===");
    let total: usize = stats.values().sum();
    let mut sorted: Vec<_> = stats.iter().collect();
    sorted.sort_by(|a, b| b.1.cmp(a.1));
    for (msg_type, count) in &sorted {
        eprintln!("{msg_type}: {count}");
    }
    eprintln!("Total messages: {total}");

    Ok(())
}