Implemented separate TCP logger service

This commit is contained in:
Martino Ferrari
2026-02-21 22:30:16 +01:00
parent 817d7276b7
commit 87b9ccebfd
12 changed files with 626 additions and 224 deletions

View File

@@ -58,6 +58,15 @@ struct SignalMetadata {
sig_type: String,
}
#[derive(Clone)]
struct ConnectionConfig {
ip: String,
tcp_port: String,
udp_port: String,
log_port: String,
version: u64,
}
enum InternalEvent {
Log(LogEntry),
Discovery(Vec<Signal>),
@@ -65,6 +74,7 @@ enum InternalEvent {
CommandResponse(String),
NodeInfo(String),
Connected,
Disconnected,
InternalLog(String),
TraceRequested(String),
ClearTrace(String),
@@ -91,10 +101,9 @@ struct LogFilters {
}
struct MarteDebugApp {
#[allow(dead_code)]
connected: bool,
tcp_addr: String,
log_addr: String,
config: ConnectionConfig,
shared_config: Arc<Mutex<ConnectionConfig>>,
signals: Vec<Signal>,
app_tree: Option<TreeItem>,
@@ -131,34 +140,43 @@ impl MarteDebugApp {
let (tx_events, rx_events) = unbounded::<InternalEvent>();
let internal_tx = tx_events.clone();
let tcp_addr = "127.0.0.1:8080".to_string();
let log_addr = "127.0.0.1:8082".to_string();
let config = ConnectionConfig {
ip: "127.0.0.1".to_string(),
tcp_port: "8080".to_string(),
udp_port: "8081".to_string(),
log_port: "8082".to_string(),
version: 0,
};
let shared_config = Arc::new(Mutex::new(config.clone()));
let id_to_meta = Arc::new(Mutex::new(HashMap::new()));
let traced_signals = Arc::new(Mutex::new(HashMap::new()));
let id_to_meta_clone = id_to_meta.clone();
let traced_signals_clone = traced_signals.clone();
let shared_config_cmd = shared_config.clone();
let shared_config_log = shared_config.clone();
let shared_config_udp = shared_config.clone();
let tx_events_c = tx_events.clone();
thread::spawn(move || {
tcp_command_worker(tcp_addr, rx_cmd_internal, tx_events_c);
tcp_command_worker(shared_config_cmd, rx_cmd_internal, tx_events_c);
});
let tx_events_log = tx_events.clone();
thread::spawn(move || {
tcp_log_worker(log_addr, tx_events_log);
tcp_log_worker(shared_config_log, tx_events_log);
});
let tx_events_udp = tx_events.clone();
thread::spawn(move || {
udp_worker(8081, id_to_meta_clone, traced_signals_clone, tx_events_udp);
udp_worker(shared_config_udp, id_to_meta_clone, traced_signals_clone, tx_events_udp);
});
Self {
connected: false,
tcp_addr: "127.0.0.1:8080".to_string(),
log_addr: "127.0.0.1:8082".to_string(),
config,
shared_config,
signals: Vec::new(),
app_tree: None,
id_to_meta,
@@ -237,20 +255,36 @@ impl MarteDebugApp {
}
}
fn tcp_command_worker(addr: String, rx_cmd: Receiver<String>, tx_events: Sender<InternalEvent>) {
fn tcp_command_worker(shared_config: Arc<Mutex<ConnectionConfig>>, rx_cmd: Receiver<String>, tx_events: Sender<InternalEvent>) {
let mut current_version = 0;
let mut current_addr = String::new();
loop {
if let Ok(mut stream) = TcpStream::connect(&addr) {
// Check for config updates
{
let config = shared_config.lock().unwrap();
if config.version != current_version {
current_version = config.version;
current_addr = format!("{}:{}", config.ip, config.tcp_port);
}
}
if let Ok(mut stream) = TcpStream::connect(&current_addr) {
let _ = stream.set_nodelay(true);
let mut reader = BufReader::new(stream.try_clone().unwrap());
let _ = tx_events.send(InternalEvent::Connected);
let tx_events_inner = tx_events.clone();
let stop_flag = Arc::new(Mutex::new(false));
let stop_flag_reader = stop_flag.clone();
thread::spawn(move || {
let mut line = String::new();
let mut json_acc = String::new();
let mut in_json = false;
while reader.read_line(&mut line).is_ok() {
if *stop_flag_reader.lock().unwrap() { break; }
let trimmed = line.trim();
if trimmed.is_empty() { line.clear(); continue; }
@@ -291,21 +325,47 @@ fn tcp_command_worker(addr: String, rx_cmd: Receiver<String>, tx_events: Sender<
});
while let Ok(cmd) = rx_cmd.recv() {
// Check if config changed while connected
{
let config = shared_config.lock().unwrap();
if config.version != current_version {
*stop_flag.lock().unwrap() = true;
break; // Trigger reconnect
}
}
if stream.write_all(format!("{}\n", cmd).as_bytes()).is_err() {
break;
}
}
let _ = tx_events.send(InternalEvent::Disconnected);
}
thread::sleep(std::time::Duration::from_secs(2));
}
}
fn tcp_log_worker(addr: String, tx_events: Sender<InternalEvent>) {
fn tcp_log_worker(shared_config: Arc<Mutex<ConnectionConfig>>, tx_events: Sender<InternalEvent>) {
let mut current_version = 0;
let mut current_addr = String::new();
loop {
if let Ok(stream) = TcpStream::connect(&addr) {
{
let config = shared_config.lock().unwrap();
if config.version != current_version {
current_version = config.version;
current_addr = format!("{}:{}", config.ip, config.log_port);
}
}
if let Ok(stream) = TcpStream::connect(&current_addr) {
let mut reader = BufReader::new(stream);
let mut line = String::new();
while reader.read_line(&mut line).is_ok() {
// Check for config update
{
if shared_config.lock().unwrap().version != current_version {
break;
}
}
let trimmed = line.trim();
if trimmed.starts_with("LOG ") {
let parts: Vec<&str> = trimmed[4..].splitn(2, ' ').collect();
@@ -324,14 +384,39 @@ fn tcp_log_worker(addr: String, tx_events: Sender<InternalEvent>) {
}
}
fn udp_worker(port: u16, id_to_meta: Arc<Mutex<HashMap<u32, SignalMetadata>>>, traced_data: Arc<Mutex<HashMap<String, TraceData>>>, tx_events: Sender<InternalEvent>) {
if let Ok(socket) = UdpSocket::bind(format!("0.0.0.0:{}", port)) {
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;
loop {
let (ver, port) = {
let config = shared_config.lock().unwrap();
(config.version, config.udp_port.clone())
};
if ver != current_version || socket.is_none() {
current_version = ver;
socket = UdpSocket::bind(format!("0.0.0.0:{}", port)).ok();
if socket.is_none() {
let _ = tx_events.send(InternalEvent::InternalLog(format!("UDP Bind Error on port {}", port)));
thread::sleep(std::time::Duration::from_secs(5));
continue;
}
let _ = socket.as_ref().unwrap().set_read_timeout(Some(std::time::Duration::from_millis(500)));
}
let s = socket.as_ref().unwrap();
let mut buf = [0u8; 4096];
let start_time = std::time::Instant::now();
let mut total_packets = 0u64;
loop {
if let Ok(n) = socket.recv(&mut buf) {
// Check for config update
if shared_config.lock().unwrap().version != current_version {
break; // Re-bind
}
if let Ok(n) = s.recv(&mut buf) {
total_packets += 1;
if (total_packets % 100) == 0 {
let _ = tx_events.send(InternalEvent::UdpStats(total_packets));
@@ -450,9 +535,13 @@ impl eframe::App for MarteDebugApp {
self.udp_packets = count;
}
InternalEvent::Connected => {
self.connected = true;
let _ = self.tx_cmd.send("TREE".to_string());
let _ = self.tx_cmd.send("DISCOVER".to_string());
}
InternalEvent::Disconnected => {
self.connected = false;
}
}
}
@@ -484,6 +573,38 @@ impl eframe::App for MarteDebugApp {
ui.toggle_value(&mut self.show_bottom_panel, "📜 Logs");
ui.separator();
ui.heading("MARTe2 Debug Explorer");
ui.separator();
ui.menu_button("🔌 Connection", |ui| {
egui::Grid::new("conn_grid")
.num_columns(2)
.spacing([40.0, 4.0])
.show(ui, |ui| {
ui.label("Server IP:");
ui.text_edit_singleline(&mut self.config.ip);
ui.end_row();
ui.label("Control Port (TCP):");
ui.text_edit_singleline(&mut self.config.tcp_port);
ui.end_row();
ui.label("Telemetry Port (UDP):");
ui.text_edit_singleline(&mut self.config.udp_port);
ui.end_row();
ui.label("Log Port (TCP):");
ui.text_edit_singleline(&mut self.config.log_port);
ui.end_row();
});
ui.separator();
if ui.button("🔄 Apply & Reconnect").clicked() {
self.config.version += 1;
let mut shared = self.shared_config.lock().unwrap();
*shared = self.config.clone();
ui.close_menu();
}
});
let status_color = if self.connected { egui::Color32::GREEN } else { egui::Color32::RED };
ui.label(egui::RichText::new(if self.connected { "● Online" } else { "○ Offline" }).color(status_color));
ui.separator();
if ui.button("🔄 Refresh").clicked() {
let _ = self.tx_cmd.send("TREE".to_string());