Add stats for frame types and source and destination addresses

This commit is contained in:
2026-03-07 10:25:03 -05:00
parent e801da4beb
commit 50a0e9abcb
2 changed files with 100 additions and 0 deletions

View File

@@ -20,6 +20,8 @@ use std::time::SystemTime;
mod print_frame; mod print_frame;
use print_frame::PrintFrame; use print_frame::PrintFrame;
mod stats;
use stats::Stats;
struct KissPkt { struct KissPkt {
timestamp: Duration, timestamp: Duration,
orig_len: u32, orig_len: u32,
@@ -78,6 +80,8 @@ fn tnc_loop<T: Read>(tx: Sender<KissPkt>, mut stream: T) -> Result<()> {
struct Arg { struct Arg {
#[arg(short, long, default_value_t = false)] #[arg(short, long, default_value_t = false)]
verbose: bool, verbose: bool,
#[arg(short, long, default_value_t = false)]
stats: bool,
#[arg(short, long, value_name = "FILE")] #[arg(short, long, value_name = "FILE")]
output: Option<PathBuf>, output: Option<PathBuf>,
input: String, input: String,
@@ -85,6 +89,10 @@ struct Arg {
fn main() -> Result<()> { fn main() -> Result<()> {
let arg = Arg::parse(); let arg = Arg::parse();
let mut stats = match arg.stats {
true => Some(Stats::new()),
false => None,
};
let mut output = arg.output.map(|path| { let mut output = arg.output.map(|path| {
//TODO automatically add file extension //TODO automatically add file extension
let file_out = File::create(path).expect("Error opening file for writing"); let file_out = File::create(path).expect("Error opening file for writing");
@@ -119,6 +127,9 @@ fn main() -> Result<()> {
} else { } else {
frame.print_short(); frame.print_short();
} }
if let Some(stats) = stats.as_mut() {
stats.record(&frame);
}
} else { } else {
let mut s = String::new(); let mut s = String::new();
for byte in pkt.data.iter() { for byte in pkt.data.iter() {
@@ -138,5 +149,9 @@ fn main() -> Result<()> {
} }
} }
if let Some(stats) = stats.take() {
stats.dump();
}
Ok(()) Ok(())
} }

85
src/stats.rs Normal file
View File

@@ -0,0 +1,85 @@
use std::collections::HashMap;
use ax25::frame::{Ax25Frame, FrameContent};
pub struct Stats {
frame_types: [usize; FrameContent::MAX],
dests: HashMap<String, usize>,
srcs: HashMap<String, usize>,
}
impl Stats {
pub fn new() -> Self {
Stats {
frame_types: [0; FrameContent::MAX],
dests: HashMap::new(),
srcs: HashMap::new(),
}
}
pub fn record(&mut self, frame: &Ax25Frame) {
self.frame_types[frame.content.get_index()] += 1;
if let Some(n) = self.srcs.get_mut(&frame.source.callsign) {
*n += 1;
} else {
self.srcs.insert(frame.source.callsign.clone(), 1);
}
if let Some(n) = self.dests.get_mut(&frame.destination.callsign) {
*n += 1;
} else {
self.dests.insert(frame.destination.callsign.clone(), 1);
}
}
pub fn dump(mut self) {
println!("\n---Frame type counts---");
println!(" Info: {}", self.frame_types[0]);
println!(" RecvRdy: {}", self.frame_types[1]);
println!(" RecvNotRdy: {}", self.frame_types[2]);
println!(" Reject: {}", self.frame_types[3]);
println!(" AsyncBalMode: {}", self.frame_types[4]);
println!(" Disconnect: {}", self.frame_types[5]);
println!(" DisconMode: {}", self.frame_types[6]);
println!(" UnNumAck: {}", self.frame_types[7]);
println!(" FrameReject: {}", self.frame_types[8]);
println!(" UnNumInfo: {}", self.frame_types[9]);
println!(" Unknown: {}", self.frame_types[10]);
println!("");
let mut top_senders: Vec<(String, usize)> = self.srcs.drain().collect();
top_senders.sort_unstable_by_key(|x| x.1);
println!("\n--- Top Senders ---");
for (k, v) in top_senders.iter().rev() {
println!("{:<10}: {v}", k);
}
println!("");
let mut top_receivers: Vec<(String, usize)> = self.dests.drain().collect();
top_receivers.sort_unstable_by_key(|x| x.1);
println!("\n--- Top Receivers ---");
for (k, v) in top_receivers.iter().rev() {
println!("{:<10}: {v}", k);
}
}
}
trait Indexable {
const MAX: usize;
fn get_index(&self) -> usize;
}
impl Indexable for FrameContent {
const MAX: usize = 11;
fn get_index(&self) -> usize {
match self {
FrameContent::Information(_) => 0,
FrameContent::ReceiveReady(_) => 1,
FrameContent::ReceiveNotReady(_) => 2,
FrameContent::Reject(_) => 3,
FrameContent::SetAsynchronousBalancedMode(_) => 4,
FrameContent::Disconnect(_) => 5,
FrameContent::DisconnectedMode(_) => 6,
FrameContent::UnnumberedAcknowledge(_) => 7,
FrameContent::FrameReject(_) => 8,
FrameContent::UnnumberedInformation(_) => 9,
FrameContent::UnknownContent(_) => 10,
}
}
}