Implemetned better buffering and high frequency tracing

This commit is contained in:
Martino Ferrari
2026-02-23 12:00:14 +01:00
parent 253a4989f9
commit 6b1fc59fc0
4 changed files with 193 additions and 162 deletions

View File

@@ -80,6 +80,7 @@ enum InternalEvent {
TraceRequested(String),
ClearTrace(String),
UdpStats(u64),
UdpDropped(u32),
}
// --- App State ---
@@ -126,6 +127,7 @@ struct MarteDebugApp {
node_info: String,
udp_packets: u64,
udp_dropped: u64,
forcing_dialog: Option<ForcingDialog>,
@@ -198,6 +200,7 @@ impl MarteDebugApp {
selected_node: "".to_string(),
node_info: "".to_string(),
udp_packets: 0,
udp_dropped: 0,
forcing_dialog: None,
tx_cmd,
rx_events,
@@ -206,7 +209,6 @@ impl MarteDebugApp {
}
fn render_tree(&mut self, ui: &mut egui::Ui, item: &TreeItem, path: String) {
// Strip "Root" from paths to match server discovery
let current_path = if path.is_empty() {
if item.name == "Root" { "".to_string() } else { item.name.clone() }
} else {
@@ -387,6 +389,7 @@ fn tcp_log_worker(shared_config: Arc<Mutex<ConnectionConfig>>, tx_events: Sender
fn udp_worker(shared_config: Arc<Mutex<ConnectionConfig>>, id_to_meta: Arc<Mutex<HashMap<u32, SignalMetadata>>>, traced_data: Arc<Mutex<HashMap<String, TraceData>>>, tx_events: Sender<InternalEvent>) {
let mut current_version = 0;
let mut socket: Option<UdpSocket> = None;
let mut last_seq: Option<u32> = None;
loop {
let (ver, port) = {
@@ -403,6 +406,9 @@ fn udp_worker(shared_config: Arc<Mutex<ConnectionConfig>>, id_to_meta: Arc<Mutex
let _ = sock.set_reuse_address(true);
#[cfg(all(unix, not(target_os = "solaris"), not(target_os = "illumos")))]
let _ = sock.set_reuse_port(true);
// CRITICAL: Increase receive buffer to 10MB to avoid drops at 100kHz
let _ = sock.set_recv_buffer_size(10 * 1024 * 1024);
let addr = format!("0.0.0.0:{}", port_num).parse::<std::net::SocketAddr>().unwrap();
if sock.bind(&addr.into()).is_ok() {
@@ -417,6 +423,7 @@ fn udp_worker(shared_config: Arc<Mutex<ConnectionConfig>>, id_to_meta: Arc<Mutex
continue;
}
let _ = socket.as_ref().unwrap().set_read_timeout(Some(std::time::Duration::from_millis(500)));
last_seq = None;
}
let s = socket.as_ref().unwrap();
@@ -431,7 +438,7 @@ fn udp_worker(shared_config: Arc<Mutex<ConnectionConfig>>, id_to_meta: Arc<Mutex
if let Ok(n) = s.recv(&mut buf) {
total_packets += 1;
if (total_packets % 100) == 0 {
if (total_packets % 500) == 0 {
let _ = tx_events.send(InternalEvent::UdpStats(total_packets));
}
@@ -440,14 +447,27 @@ fn udp_worker(shared_config: Arc<Mutex<ConnectionConfig>>, id_to_meta: Arc<Mutex
let mut magic_buf = [0u8; 4]; magic_buf.copy_from_slice(&buf[0..4]);
if u32::from_le_bytes(magic_buf) != 0xDA7A57AD { continue; }
// Sequence check
let mut seq_buf = [0u8; 4]; seq_buf.copy_from_slice(&buf[4..8]);
let seq = u32::from_le_bytes(seq_buf);
if let Some(last) = last_seq {
if seq != last + 1 {
let dropped = if seq > last { seq - last - 1 } else { 0 };
if dropped > 0 {
let _ = tx_events.send(InternalEvent::UdpDropped(dropped));
}
}
}
last_seq = Some(seq);
let mut count_buf = [0u8; 4]; count_buf.copy_from_slice(&buf[16..20]);
let count = u32::from_le_bytes(count_buf);
let now = start_time.elapsed().as_secs_f64();
let mut offset = 20;
let mut local_updates: HashMap<String, Vec<[f64; 2]>> = HashMap::new();
let metas = id_to_meta.lock().unwrap();
let mut data_map = traced_data.lock().unwrap();
for _ in 0..count {
if offset + 8 > n { break; }
@@ -484,14 +504,26 @@ fn udp_worker(shared_config: Arc<Mutex<ConnectionConfig>>, id_to_meta: Arc<Mutex
};
for name in &meta.names {
if let Some(entry) = data_map.get_mut(name) {
entry.values.push_back([now, val]);
if entry.values.len() > 5000 { entry.values.pop_front(); }
}
local_updates.entry(name.clone()).or_default().push([now, val]);
}
}
offset += size as usize;
}
drop(metas);
if !local_updates.is_empty() {
let mut data_map = traced_data.lock().unwrap();
for (name, new_points) in local_updates {
if let Some(entry) = data_map.get_mut(&name) {
for point in new_points {
entry.values.push_back(point);
}
while entry.values.len() > 10000 {
entry.values.pop_front();
}
}
}
}
}
}
}
@@ -531,7 +563,7 @@ impl eframe::App for MarteDebugApp {
}
InternalEvent::TraceRequested(name) => {
let mut data_map = self.traced_signals.lock().unwrap();
data_map.entry(name).or_insert_with(|| TraceData { values: VecDeque::with_capacity(5000) });
data_map.entry(name).or_insert_with(|| TraceData { values: VecDeque::with_capacity(10000) });
}
InternalEvent::ClearTrace(name) => {
let mut data_map = self.traced_signals.lock().unwrap();
@@ -547,6 +579,9 @@ impl eframe::App for MarteDebugApp {
InternalEvent::UdpStats(count) => {
self.udp_packets = count;
}
InternalEvent::UdpDropped(dropped) => {
self.udp_dropped += dropped as u64;
}
InternalEvent::Connected => {
self.connected = true;
let _ = self.tx_cmd.send("TREE".to_string());
@@ -636,7 +671,7 @@ impl eframe::App for MarteDebugApp {
}
}
ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
ui.label(format!("UDP Packets: {}", self.udp_packets));
ui.label(format!("UDP: OK [{}] / DROPPED [{}]", self.udp_packets, self.udp_dropped));
});
});
});
@@ -797,7 +832,6 @@ impl eframe::App for MarteDebugApp {
ui.horizontal(|ui| {
ui.heading("Oscilloscope");
if ui.button("🔄 Reset View").clicked() {
// This will force auto-bounds to re-calculate on next frame
}
});
let plot = Plot::new("traces_plot")