plotter refactor
This commit is contained in:
@@ -0,0 +1,122 @@
|
||||
use can_dbc::Dbc;
|
||||
use eframe::egui;
|
||||
use egui::ScrollArea;
|
||||
use egui_ltreeview::{Action, TreeView};
|
||||
use libsvld::DecodeResult;
|
||||
|
||||
pub struct SigNode {
|
||||
pub id: u64,
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
pub struct MsgNode {
|
||||
pub id: u64,
|
||||
pub label: String,
|
||||
pub signals: Vec<SigNode>,
|
||||
}
|
||||
|
||||
const SIGNAL_FLAG: u64 = 1 << 63;
|
||||
|
||||
pub fn signal_node_id(msg_id: u32, sig_idx: usize) -> u64 {
|
||||
SIGNAL_FLAG | ((msg_id as u64) << 16) | (sig_idx as u64)
|
||||
}
|
||||
|
||||
pub fn decode_signal_node_id(id: u64) -> Option<(u32, usize)> {
|
||||
if id & SIGNAL_FLAG == 0 {
|
||||
return None;
|
||||
}
|
||||
let msg_id = ((id >> 16) & 0xFFFF_FFFF) as u32;
|
||||
let sig_idx = (id & 0xFFFF) as usize;
|
||||
Some((msg_id, sig_idx))
|
||||
}
|
||||
|
||||
pub fn build_tree(dbc: &Dbc, data: &DecodeResult) -> Vec<MsgNode> {
|
||||
let mut msgs: Vec<&libsvld::motec::MessageMetadata> = data.metadata.values().collect();
|
||||
msgs.sort_by_key(|m| m.id);
|
||||
msgs.into_iter()
|
||||
.map(|msg| {
|
||||
let name = dbc
|
||||
.messages
|
||||
.iter()
|
||||
.find(|x| x.id.raw() == msg.id)
|
||||
.map(|x| x.name.as_str())
|
||||
.unwrap_or("?");
|
||||
let label = format!("0x{:X} {}", msg.id, name);
|
||||
let signals = msg
|
||||
.signals
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(sig_idx, signal)| SigNode {
|
||||
id: signal_node_id(msg.id, sig_idx),
|
||||
name: signal.name.clone(),
|
||||
})
|
||||
.collect();
|
||||
MsgNode {
|
||||
id: msg.id as u64,
|
||||
label,
|
||||
signals,
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Render the side panel and return any signal IDs the user clicked to toggle.
|
||||
pub fn show_side_panel(
|
||||
ui: &mut egui::Ui,
|
||||
filter: &mut String,
|
||||
tree: &[MsgNode],
|
||||
) -> Vec<u64> {
|
||||
let mut toggles = Vec::new();
|
||||
egui::Panel::left(egui::Id::new("Signals"))
|
||||
.resizable(true)
|
||||
.show_inside(ui, |ui| {
|
||||
ui.add(
|
||||
egui::TextEdit::singleline(filter)
|
||||
.hint_text("Filter messages/signals")
|
||||
.desired_width(f32::INFINITY),
|
||||
);
|
||||
let needle = filter.trim().to_lowercase();
|
||||
ScrollArea::both()
|
||||
.scroll([true, true])
|
||||
.scroll_bar_visibility(
|
||||
egui::scroll_area::ScrollBarVisibility::VisibleWhenNeeded,
|
||||
)
|
||||
.show(ui, |ui| {
|
||||
let (_resp, actions) = TreeView::<u64>::new(
|
||||
ui.make_persistent_id("SignalsTree"),
|
||||
)
|
||||
.show(ui, |builder| {
|
||||
for msg in tree {
|
||||
let msg_hit = needle.is_empty()
|
||||
|| msg.label.to_lowercase().contains(&needle);
|
||||
let matching: Vec<&SigNode> = if msg_hit {
|
||||
msg.signals.iter().collect()
|
||||
} else {
|
||||
msg.signals
|
||||
.iter()
|
||||
.filter(|s| s.name.to_lowercase().contains(&needle))
|
||||
.collect()
|
||||
};
|
||||
if !msg_hit && matching.is_empty() {
|
||||
continue;
|
||||
}
|
||||
builder.dir(msg.id, msg.label.as_str());
|
||||
for sig in matching {
|
||||
builder.leaf(sig.id, sig.name.as_str());
|
||||
}
|
||||
builder.close_dir();
|
||||
}
|
||||
});
|
||||
for action in actions {
|
||||
if let Action::SetSelected(ids) = action {
|
||||
for id in ids {
|
||||
if id & SIGNAL_FLAG != 0 {
|
||||
toggles.push(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
toggles
|
||||
}
|
||||
Reference in New Issue
Block a user