Add stats for frame types and source and destination addresses
This commit is contained in:
15
src/main.rs
15
src/main.rs
@@ -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
85
src/stats.rs
Normal 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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user