feat: initial rusty-telemetry UDP listener
- Async UDP listener on configurable bind address (default 127.0.0.1:5005) - RUSTY_TELEMETRY_BIND env var for cross-machine deployment - Deserializes JSON telemetry frames with serde - deny_unknown_fields for schema drift detection - ReceiveStats tracker with packet rate and data rate - Locked stdout writes for efficient output - .gitignore for /target/ and .idea/
This commit is contained in:
commit
30543fd0ba
7 changed files with 797 additions and 0 deletions
78
src/main.rs
Normal file
78
src/main.rs
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
mod stats;
|
||||
mod telemetry;
|
||||
|
||||
use stats::ReceiveStats;
|
||||
use telemetry::TelemetryFrame;
|
||||
|
||||
use std::io::Write;
|
||||
|
||||
const DEFAULT_BIND_ADDR: &str = "127.0.0.1:5005";
|
||||
const BUFFER_SIZE: usize = 65535;
|
||||
|
||||
fn print_banner(bind_addr: &str) {
|
||||
println!("========================================");
|
||||
println!(" rusty-telemetry listener");
|
||||
println!(" Phase 1 - UDP JSON receiver");
|
||||
println!("========================================");
|
||||
println!(" Bind: {}", bind_addr);
|
||||
println!("========================================");
|
||||
println!();
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
let bind_addr =
|
||||
std::env::var("RUSTY_TELEMETRY_BIND").unwrap_or_else(|_| DEFAULT_BIND_ADDR.to_string());
|
||||
|
||||
print_banner(&bind_addr);
|
||||
|
||||
let socket = match tokio::net::UdpSocket::bind(&bind_addr).await {
|
||||
Ok(s) => {
|
||||
let local_addr = s.local_addr().unwrap();
|
||||
println!("[INFO] Listening on {}", local_addr);
|
||||
s
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("[ERROR] Failed to bind UDP socket: {}", e);
|
||||
std::process::exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
let mut buf = vec![0u8; BUFFER_SIZE];
|
||||
let mut stats = ReceiveStats::new();
|
||||
let stdout = std::io::stdout();
|
||||
|
||||
loop {
|
||||
match socket.recv_from(&mut buf).await {
|
||||
Ok((len, src)) => {
|
||||
stats.record(len);
|
||||
|
||||
let text = match std::str::from_utf8(&buf[..len]) {
|
||||
Ok(s) => s,
|
||||
Err(e) => {
|
||||
eprintln!("[WARN] Invalid UTF-8 from {}: {}", src, e);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
let frame: TelemetryFrame = match serde_json::from_str(text) {
|
||||
Ok(f) => f,
|
||||
Err(e) => {
|
||||
eprintln!("[WARN] JSON parse error from {}: {}", src, e);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
{
|
||||
let mut handle = stdout.lock();
|
||||
let _ = writeln!(handle, "{}", frame);
|
||||
let _ = writeln!(handle, " [{}] {}", src, stats);
|
||||
let _ = writeln!(handle);
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("[ERROR] Receive error: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue