implemented array support on client and server

This commit is contained in:
Martino Ferrari
2026-03-03 21:58:32 +01:00
parent a941563749
commit d3077e78ec
8 changed files with 208 additions and 83 deletions

View File

@@ -29,8 +29,14 @@ struct Signal {
id: u32,
#[serde(rename = "type")]
sig_type: String,
#[serde(default)]
dimensions: u8,
#[serde(default = "default_elements")]
elements: u32,
}
fn default_elements() -> u32 { 1 }
#[derive(Deserialize)]
struct DiscoverResponse {
#[serde(rename = "Signals")]
@@ -75,6 +81,8 @@ struct TraceData {
struct SignalMetadata {
names: Vec<String>,
sig_type: String,
dimensions: u8,
elements: u32,
}
#[derive(Clone)]
@@ -443,29 +451,57 @@ impl MarteDebugApp {
let _ = self.tx_cmd.send(format!("INFO {}", current_path));
}
if item.class.contains("Signal") {
let traceable = item.is_traceable.unwrap_or(false);
let forcable = item.is_forcable.unwrap_or(false);
let elements = item.elements.unwrap_or(1);
if elements > 1 {
let header = egui::CollapsingHeader::new(format!("{} [{}] ({} elems)", label, item.class, elements))
.id_salt(&current_path);
header.show(ui, |ui| {
for i in 0..elements {
let elem_path = format!("{}[{}]", current_path, i);
ui.horizontal(|ui| {
ui.label(format!("{}[{}]", item.name, i));
if ui.button("Trace").clicked() {
let _ = self.tx_cmd.send(format!("TRACE {} 1", current_path));
let _ = self.internal_tx.send(InternalEvent::TraceRequested(elem_path.clone(), false));
}
if item.class == "Signal" {
if ui.button("Monitor").clicked() {
self.monitoring_dialog = Some(MonitorDialog {
signal_path: current_path.clone(),
period_ms: "100".to_string(),
});
// Note: internal monitoring logic will handle individual elements via naming convention
let _ = self.internal_tx.send(InternalEvent::TraceRequested(elem_path.clone(), true));
}
}
});
}
});
} else {
let traceable = item.is_traceable.unwrap_or(false);
let forcable = item.is_forcable.unwrap_or(false);
if traceable && ui.button("Trace").clicked() {
let _ = self.tx_cmd.send(format!("TRACE {} 1", current_path));
let _ = self
.internal_tx
.send(InternalEvent::TraceRequested(current_path.clone(), false));
}
if item.class == "Signal" {
if ui.button("Monitor").clicked() {
self.monitoring_dialog = Some(MonitorDialog {
if traceable && ui.button("Trace").clicked() {
let _ = self.tx_cmd.send(format!("TRACE {} 1", current_path));
let _ = self
.internal_tx
.send(InternalEvent::TraceRequested(current_path.clone(), false));
}
if item.class == "Signal" {
if ui.button("Monitor").clicked() {
self.monitoring_dialog = Some(MonitorDialog {
signal_path: current_path.clone(),
period_ms: "100".to_string(),
});
}
}
if forcable && ui.button("⚡ Force").clicked() {
self.forcing_dialog = Some(ForcingDialog {
signal_path: current_path.clone(),
period_ms: "100".to_string(),
value: "".to_string(),
});
}
}
if forcable && ui.button("⚡ Force").clicked() {
self.forcing_dialog = Some(ForcingDialog {
signal_path: current_path.clone(),
value: "".to_string(),
});
}
}
});
}
@@ -841,50 +877,64 @@ fn udp_worker(
if let Some(meta) = metas.get(&id) {
let _ = tx_events.send(InternalEvent::TelemMatched(id));
let t = meta.sig_type.as_str();
let val = match size {
1 => {
if t.contains('u') {
data_slice[0] as f64
} else {
(data_slice[0] as i8) as f64
let type_size = if meta.elements > 0 { size / meta.elements } else { size };
for i in 0..meta.elements {
let elem_offset = (i * type_size) as usize;
if elem_offset + type_size as usize > data_slice.len() { break; }
let elem_data = &data_slice[elem_offset..elem_offset + type_size as usize];
let val = match type_size {
1 => {
if t.contains('u') {
elem_data[0] as f64
} else {
(elem_data[0] as i8) as f64
}
}
}
2 => {
let b = data_slice[0..2].try_into().unwrap();
if t.contains('u') {
u16::from_le_bytes(b) as f64
} else {
i16::from_le_bytes(b) as f64
2 => {
let b = elem_data[0..2].try_into().unwrap();
if t.contains('u') {
u16::from_le_bytes(b) as f64
} else {
i16::from_le_bytes(b) as f64
}
}
}
4 => {
let b = data_slice[0..4].try_into().unwrap();
if t.contains("float") {
f32::from_le_bytes(b) as f64
} else if t.contains('u') {
u32::from_le_bytes(b) as f64
} else {
i32::from_le_bytes(b) as f64
4 => {
let b = elem_data[0..4].try_into().unwrap();
if t.contains("float") {
f32::from_le_bytes(b) as f64
} else if t.contains('u') {
u32::from_le_bytes(b) as f64
} else {
i32::from_le_bytes(b) as f64
}
}
}
8 => {
let b = data_slice[0..8].try_into().unwrap();
if t.contains("float") {
f64::from_le_bytes(b)
} else if t.contains('u') {
u64::from_le_bytes(b) as f64
} else {
i64::from_le_bytes(b) as f64
8 => {
let b = elem_data[0..8].try_into().unwrap();
if t.contains("float") {
f64::from_le_bytes(b)
} else if t.contains('u') {
u64::from_le_bytes(b) as f64
} else {
i64::from_le_bytes(b) as f64
}
}
_ => 0.0,
};
for name in &meta.names {
let target_name = if meta.elements > 1 {
format!("{}[{}]", name, i)
} else {
name.clone()
};
local_updates
.entry(target_name.clone())
.or_default()
.push([ts_s, val]);
last_values.insert(target_name, val);
}
_ => 0.0,
};
for name in &meta.names {
local_updates
.entry(name.clone())
.or_default()
.push([ts_s, val]);
last_values.insert(name.clone(), val);
}
}
offset += size as usize;
@@ -933,6 +983,8 @@ impl eframe::App for MarteDebugApp {
let meta = metas.entry(s.id).or_insert_with(|| SignalMetadata {
names: Vec::new(),
sig_type: s.sig_type.clone(),
dimensions: s.dimensions,
elements: s.elements,
});
if !meta.names.contains(&s.name) {
meta.names.push(s.name.clone());
@@ -1108,9 +1160,36 @@ impl eframe::App for MarteDebugApp {
.tx_cmd
.send(format!("MONITOR SIGNAL {} {}", dialog.signal_path, period));
let _ = self.tx_cmd.send("DISCOVER".to_string());
let _ = self
.internal_tx
.send(InternalEvent::TraceRequested(dialog.signal_path.clone(), true));
// Check if it's an array signal to add all elements to view
let mut elements = 1;
if let Some(tree) = &self.app_tree {
// Helper to find item in tree
fn find_item<'a>(item: &'a TreeItem, target: &str, current: &str) -> Option<&'a TreeItem> {
let path = if current.is_empty() { item.name.clone() } else { format!("{}.{}", current, item.name) };
if path == target || (current.is_empty() && item.name == "Root" && target.is_empty()) { return Some(item); }
if let Some(children) = &item.children {
for child in children {
if let Some(found) = find_item(child, target, &path) { return Some(found); }
}
}
None
}
if let Some(found) = find_item(tree, &dialog.signal_path, "") {
elements = found.elements.unwrap_or(1);
}
}
if elements > 1 {
for i in 0..elements {
let elem_path = format!("{}[{}]", dialog.signal_path, i);
let _ = self.internal_tx.send(InternalEvent::TraceRequested(elem_path, true));
}
} else {
let _ = self
.internal_tx
.send(InternalEvent::TraceRequested(dialog.signal_path.clone(), true));
}
close = true;
}
if ui.button("Cancel").clicked() {