Skip to content
Snippets Groups Projects
Commit f11e5fd2 authored by Camillo Nicoletti's avatar Camillo Nicoletti :computer:
Browse files

Work-in-progress

parent 8d2cbdc1
Branches
No related tags found
No related merge requests found
use egui::{vec2, Color32, Frame, Id, ScrollArea, Window, Response}; // Importa i moduli necessari da egui use egui::{ScrollArea, Window, Response}; // Importa i moduli necessari da egui
use serde::{Serialize, Deserialize}; // Importa i moduli per la serializzazione e deserializzazione use serde::{Serialize, Deserialize}; // Importa i moduli per la serializzazione e deserializzazione
use std::collections::HashSet; // Importa HashSet dalla libreria standard use std::collections::{HashSet, HashMap}; // Importa HashSet e HashMap dalla libreria standard
use crate::mavlink::{TimedMessage, extract_from_message}; // Importa moduli specifici dal crate mavlink use crate::mavlink::{TimedMessage, extract_from_message}; // Importa moduli specifici dal crate mavlink
use crate::mavlink::reflection::ReflectionContext; // Importa ReflectionContext dal crate mavlink use crate::mavlink::reflection::ReflectionContext; // Importa ReflectionContext dal crate mavlink
use crate::ui::panes::{PaneBehavior, PaneResponse}; // Importa i comportamenti e le risposte del pannello use crate::ui::panes::{PaneBehavior, PaneResponse}; // Importa i comportamenti e le risposte del pannello
use crate::mavlink::Message;
use crate::MAVLINK_PROFILE;
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct MessagesViewerPane { pub struct MessagesViewerPane {
...@@ -19,7 +22,9 @@ pub struct MessagesViewerPane { ...@@ -19,7 +22,9 @@ pub struct MessagesViewerPane {
#[serde(skip)] #[serde(skip)]
seen_message_types: HashSet<String>, // Tipi di messaggi visti seen_message_types: HashSet<String>, // Tipi di messaggi visti
#[serde(skip)] #[serde(skip)]
selected_message: Option<String>, // Messaggio selezionato field_map: HashMap<String, Option<String>>, // Mappa dei campi
#[serde(skip)]
selected_message: Option<u32>, // Messaggio selezionato
#[serde(skip)] #[serde(skip)]
selected_fields: HashSet<String>, // Campi selezionati selected_fields: HashSet<String>, // Campi selezionati
#[serde(skip)] #[serde(skip)]
...@@ -30,26 +35,21 @@ pub struct MessagesViewerPane { ...@@ -30,26 +35,21 @@ pub struct MessagesViewerPane {
impl Default for MessagesViewerPane { impl Default for MessagesViewerPane {
fn default() -> Self { fn default() -> Self {
Self { Self {
contains_pointer: false, // Imposta contains_pointer su false contains_pointer: false,
settings_visible: false, // Imposta settings_visible su false settings_visible: false,
items: vec![], // Inizializza items come un vettore vuoto items: vec![],
settings: MsgSources::default(), // Imposta settings con il valore predefinito di MsgSources settings: MsgSources::default(),
/* available_messages: ReflectionContext::new()
* Reflection permette di aggiornare in tempo reale quali sono i messaggi .sorted_messages()
* e i fields dei pacchetti che riceviamo, il tutto in maniera automatica.
*/
available_messages: ReflectionContext::new() //Questo carica il profilo MAVLink con tutti i messaggi disponibili.
.sorted_messages() //Questo restituisce un Vec<&str> con i nomi dei messaggi, come ["ATTITUDE", "GPS_RAW_INT", "SYS_STATUS"].
.iter() .iter()
.map(|&s| s.to_string()) .map(|&s| s.to_string())
.collect(), // Inizializza available_messages con i messaggi ordinati .collect(),
//.iter().map(|&s| s.to_string()).collect() seen_message_types: HashSet::new(),
//Trasforma ogni &str in String, perché il Vec finale deve contenere Vec<String>. sampling_frequency: 10.0,
seen_message_types: HashSet::new(), //Non permette duplicati. Se provi ad aggiungere due volte lo stesso elemento, lo tiene solo una volta. selected_message: None,
sampling_frequency: 10.0, // Imposta la frequenza di campionamento su 10.0 selected_fields: HashSet::new(),
selected_message: None, //Memorizza il nome del messaggio selezionato dall’utente. DEVO IMPOSTARLO A NONE IL PRIMO? message_log: Vec::new(),
selected_fields: HashSet::new(), //Memorizza i campi del messaggio selezionato che l’utente vuole visualizzare. field_map: HashMap::new(),
message_log: Vec::new(), // Inizializza message_log come un vettore vuoto
} }
} }
} }
...@@ -57,39 +57,25 @@ impl Default for MessagesViewerPane { ...@@ -57,39 +57,25 @@ impl Default for MessagesViewerPane {
impl PaneBehavior for MessagesViewerPane { impl PaneBehavior for MessagesViewerPane {
fn ui(&mut self, ui: &mut egui::Ui, _tile_id: egui_tiles::TileId) -> PaneResponse { fn ui(&mut self, ui: &mut egui::Ui, _tile_id: egui_tiles::TileId) -> PaneResponse {
let response = PaneResponse::default(); // Crea una risposta predefinita del pannello let response = PaneResponse::default(); // Crea una risposta predefinita del pannello
ui.heading("Messages Viewer"); // Aggiunge un'intestazione al pannello ui.heading("Messages Viewer");
if ui.button("Open Message Filter").clicked() { if ui.button("Open Message Filter").clicked() {
self.settings_visible = true; // Mostra le impostazioni se il pulsante è cliccato self.settings_visible = true; // Mostra le impostazioni se il pulsante è cliccato
} }
if self.settings_visible { if self.settings_visible {
// Controlla se le impostazioni sono visibili // Finestra di configurazione
Window::new("Messages Viewer Settings") Window::new("Messages Viewer Settings")
// Crea una nuova finestra con il titolo "Messages Viewer Settings"
.collapsible(false) .collapsible(false)
// Impedisce alla finestra di essere collassabile
.resizable(false) .resizable(false)
// Impedisce alla finestra di essere ridimensionabile
.open(&mut self.settings_visible) .open(&mut self.settings_visible)
// Imposta la visibilità della finestra in base a self.settings_visible
.show(ui.ctx(), |ui| { .show(ui.ctx(), |ui| {
// Mostra la finestra nel contesto dell'interfaccia utente
egui::ComboBox::new("message_kind", "Message Kind") egui::ComboBox::new("message_kind", "Message Kind")
// Crea una nuova combo box con l'etichetta "Message Kind"
.selected_text(self.selected_message.clone().unwrap_or("Select a message".to_string())) .selected_text(self.selected_message.clone().unwrap_or("Select a message".to_string()))
// Imposta il testo selezionato nella combo box, o "Select a message" se nessun messaggio è selezionato
.show_ui(ui, |ui| { .show_ui(ui, |ui| {
// Mostra la combo box nell'interfaccia utente for message_type in &self.available_messages {
for message_type in &self.available_messages { //available_messages si popola con ReflectionContext
// Itera su ciascun tipo di messaggio disponibile
if ui.selectable_label(self.selected_message.as_deref() == Some(message_type), message_type).clicked() { if ui.selectable_label(self.selected_message.as_deref() == Some(message_type), message_type).clicked() {
//Crea un’etichetta selezionabile (selectable_label) per ogni messaggio disponibile.
//Controlla se ci ha cliccato sopra (clicked()).
//Se clicca, cambia il messaggio selezionato.
self.selected_message = Some(message_type.clone()); self.selected_message = Some(message_type.clone());
// Imposta il messaggio selezionato
self.selected_fields.clear(); self.selected_fields.clear();
// Pulisce i campi selezionati quando si seleziona un nuovo messaggio
} }
} }
}); });
...@@ -97,206 +83,134 @@ impl PaneBehavior for MessagesViewerPane { ...@@ -97,206 +83,134 @@ impl PaneBehavior for MessagesViewerPane {
if let Some(ref selected_msg) = self.selected_message { if let Some(ref selected_msg) = self.selected_message {
ui.label("Select Fields:"); ui.label("Select Fields:");
ScrollArea::both() ScrollArea::both()
.auto_shrink([false, true]) // Impedisce la riduzione automatica in larghezza .auto_shrink([false, true])
.max_width(300.0) // Imposta una larghezza massima più ampia .max_width(300.0)
.max_height(100.0) .max_height(100.0)
.show(ui, |ui| { .show(ui, |ui| {
let mut select_all = false; let mut select_all = false;
let mut deselect_all = false; let mut deselect_all = false;
if let Ok(fields) = MAVLINK_PROFILE.get_fields_by_name(selected_msg) {
/*ReflectionContext*
* è una struttura o un oggetto che fornisce funzionalità di riflessione per i messaggi MAVLink.
* La riflessione è una tecnica che consente a un programma di ispezionare e manipolare la struttura
* dei dati a runtime. In questo contesto, ReflectionContext viene utilizzato per ottenere informazioni
* sui campi di un messaggio MAVLink specifico.
*/
if let Ok(fields) = ReflectionContext::new().get_fields_by_name(selected_msg) {
//restituisce i fields del messaggio selezionato
// Ottiene i campi del messaggio selezionato utilizzando ReflectionContext
if fields.len() > 1 { if fields.len() > 1 {
// Se il numero di campi è maggiore di 1
ui.horizontal(|ui| { ui.horizontal(|ui| {
// Dispone gli elementi orizzontalmente
ui.checkbox(&mut select_all, "Select All"); ui.checkbox(&mut select_all, "Select All");
// Crea una checkbox per selezionare tutti i campi
ui.checkbox(&mut deselect_all, "Deselect All"); ui.checkbox(&mut deselect_all, "Deselect All");
// Crea una checkbox per deselezionare tutti i campi
}); });
} }
} }
if select_all { if select_all {
if let Ok(fields) = ReflectionContext::new().get_fields_by_name(selected_msg) { if let Ok(fields) = MAVLINK_PROFILE.get_fields_by_name(selected_msg) {
for field in &fields { for field in &fields {
self.selected_fields.insert(field.to_string()); // Seleziona tutti i campi self.selected_fields.insert(field.to_string());
} }
} }
} }
if deselect_all { if deselect_all {
self.selected_fields.clear(); // Deseleziona tutti i campi self.selected_fields.clear();
} }
if let Ok(fields) = ReflectionContext::new().get_fields_by_name(selected_msg) { if let Ok(fields) = ReflectionContext::new().get_fields_by_name(selected_msg) {
// Ottiene i campi del messaggio selezionato utilizzando ReflectionContext
for field in fields { for field in fields {
// Itera su ciascun campo ottenuto
let mut selected = self.selected_fields.contains(field); let mut selected = self.selected_fields.contains(field);
// Verifica se il campo è già selezionato
let response: Response = ui.checkbox(&mut selected, field); let response: Response = ui.checkbox(&mut selected, field);
// Crea una checkbox per il campo e aggiorna lo stato di selezione
if response.clicked() { if response.clicked() {
// Se la checkbox è stata cliccata
if selected { if selected {
self.selected_fields.insert(field.to_string()); self.selected_fields.insert(field.to_string());
// Aggiunge il campo selezionato all'insieme dei campi selezionati
} else { } else {
self.selected_fields.remove(field); self.selected_fields.remove(field);
// Rimuove il campo deselezionato dall'insieme dei campi selezionati
}
}
if response.drag_started() {
ui.memory_mut(|mem| mem.set_dragged_id(Id::new(field)));
// Imposta l'ID del campo trascinato nella memoria di egui
}
if let Some(dragged) = ui.memory(|mem| mem.dragged_id()) {
// Verifica se c'è un campo trascinato
if dragged == Id::new(field) {
ui.label("Dragging...");
// Mostra un'etichetta durante il trascinamento del campo
} }
} }
} }
} }
}); });
} }
ui.add_space(7.0); // Aggiunge spazio tra le righe
ui.add_space(7.0);
ui.label("Sampling Frequency (Hz):"); ui.label("Sampling Frequency (Hz):");
ui.add(egui::Slider::new(&mut self.sampling_frequency, 1.0..=100.0).text("Hz")); // Aggiunge uno slider per la frequenza di campionamento ui.add(egui::Slider::new(&mut self.sampling_frequency, 1.0..=100.0).text("Hz"));
}); });
} }
egui::Grid::new("message_viewer").show(ui, |ui| {
if let Some(selected_msg) = &self.selected_message {
if let Ok(fields) = MAVLINK_PROFILE.get_fields_by_name(selected_msg) {
for field in fields {
// Usa field come &str per il controllo
if self.selected_fields.contains(field) {
let value = self.field_map
.get(field)
.and_then(|v| v.as_deref())
.unwrap_or("N/A");
// Drag and drop per riordinare i messaggi ui.label(field);
let mut from: Option<usize> = None; // Indice di partenza del trascinamento ui.label(value);
let mut to: Option<usize> = None; // Indice di destinazione del trascinamento ui.end_row();
}
let frame = Frame::default().fill(Color32::DARK_GRAY); // Crea un frame con sfondo grigio scuro
let (_, dropped_payload) = ui.dnd_drop_zone::<usize, ()>(frame, |ui| {
ui.set_min_size(vec2(150.0, 200.0)); // Imposta la dimensione minima della zona di drop
for (row_idx, item) in self.message_log.iter().enumerate() {
let item_id = Id::new(("drag_and_drop", row_idx)); // Crea un ID per l'elemento
let response = ui.dnd_drag_source(item_id, row_idx, |ui| {
ui.label(item); // Mostra l'etichetta dell'elemento
}).response;
if let (Some(pointer), Some(hovered_payload)) = (
ui.input(|i| i.pointer.interact_pos()), // Ottiene la posizione del puntatore
response.dnd_hover_payload::<usize>(), // Ottiene il payload del trascinamento
) {
let rect = response.rect; // Ottiene il rettangolo dell'elemento
let stroke = egui::Stroke::new(1.0, Color32::WHITE); // Crea una linea bianca
let insert_row_idx = if *hovered_payload == row_idx {
ui.painter().hline(rect.x_range(), rect.center().y, stroke); // Disegna una linea orizzontale al centro
row_idx
} else if pointer.y < rect.center().y {
ui.painter().hline(rect.x_range(), rect.top(), stroke); // Disegna una linea orizzontale in alto
row_idx
} else {
ui.painter().hline(rect.x_range(), rect.bottom(), stroke); // Disegna una linea orizzontale in basso
row_idx + 1
};
if let Some(dragged_payload) = response.dnd_release_payload() {
from = Some(*dragged_payload); // Imposta l'indice di partenza
to = Some(insert_row_idx); // Imposta l'indice di destinazione
} }
} }
} else {
ui.label("No message selected");
} }
}); });
if let Some(dragged_payload) = dropped_payload {
from = Some(*dragged_payload); // Imposta l'indice di partenza se c'è un payload rilasciato
to = Some(usize::MAX); // Imposta l'indice di destinazione al massimo valore
}
if let (Some(from), Some(mut to)) = (from, to) {
to -= (from < to) as usize; // Regola l'indice di destinazione
let item = self.message_log.remove(from); // Rimuove l'elemento dall'indice di partenza
to = to.min(self.message_log.len()); // Limita l'indice di destinazione alla lunghezza del registro
self.message_log.insert(to, item); // Inserisce l'elemento all'indice di destinazione
}
response // Restituisce la risposta del pannello response
} }
fn contains_pointer(&self) -> bool { fn contains_pointer(&self) -> bool {
self.contains_pointer // Restituisce se il puntatore è contenuto nel pannello self.contains_pointer
} }
fn update(&mut self, messages: &[TimedMessage]) {
//let message = messages.into_iter().last()
//if let Some (message) = message
fn update(&mut self, messages: &[TimedMessage]) {
for msg in messages { for msg in messages {
let msg_type = format!("{:?}", msg.message); // Formatta il tipo di messaggio come stringa if let Some(selected_msg) = &self.selected_message {
if self.seen_message_types.insert(msg_type.clone()) {
self.available_messages.push(msg_type.clone()); // Aggiunge il tipo di messaggio ai messaggi disponibili if let Ok(fields) = MAVLINK_PROFILE.get_fields(selected_msg) {
self.available_messages.sort(); // Ordina i messaggi disponibili
}
if self.selected_message.as_deref() == Some(&msg_type) {
let mut log_entry = format!("[{}] {}", msg.time.elapsed().as_secs(), msg_type); // Crea una voce di registro con il tempo trascorso e il tipo di messaggio
let fields: Vec<_> = self.selected_fields.iter().take(5).collect(); // Ottiene i primi 5 campi selezionati
for field in fields { for field in fields {
if let Ok(values) = extract_from_message::<_, String>(&msg.message, vec![field.to_string()]) { if self.selected_fields.contains(field) {
if let Ok(values) = extract_from_message(&msg.message, &[field]) {
if !values.is_empty() { if !values.is_empty() {
log_entry.push_str(&format!(" | {}: {:?}", field, values[0])); // Aggiunge i valori dei campi alla voce di registro let value: f64 = values[0];
self.field_map.insert(field.to_string(), Some(value.to_string()));
}
} }
} }
} }
self.message_log.push(log_entry); // Aggiunge la voce di registro al registro dei messaggi
if self.message_log.len() > 1000 {
self.message_log.remove(0); // Rimuove la voce più vecchia se il registro supera 1000 voci
} }
} }
} }
} }
fn get_message_subscription(&self) -> Option<u32> { fn get_message_subscription(&self) -> Option<u32> {
None // Non restituisce alcuna sottoscrizione ai messaggi None
} }
fn should_send_message_history(&self) -> bool { fn should_send_message_history(&self) -> bool {
false // Non invia la cronologia dei messaggi false
} }
} }
// La macro #[derive(Clone, Debug, Serialize, Deserialize)] genera automaticamente le implementazioni
// per i tratti Clone, Debug, Serialize e Deserialize per la struttura MsgSources.
// E la mia struttura di come sono fatti i dati dei messaggi
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub struct MsgSources { pub struct MsgSources {
msg_id: u32, // ID del messaggio msg_id: u32,
fields_with_checkbox: Vec<(String, bool)>, // Campi con checkbox fields_with_checkbox: Vec<(String, bool)>,
} }
// Implementazione del tratto Default per MsgSources
impl Default for MsgSources { impl Default for MsgSources {
fn default() -> Self { fn default() -> Self {
Self { Self {
//come default devo far vedere un messaggio in particolare ? msg_id: 0,
msg_id: 0, // Imposta l'ID del messaggio su 0 fields_with_checkbox: Vec::new(),
fields_with_checkbox: Vec::new(), // Inizializza fields_with_checkbox come un vettore vuoto
} }
} }
} }
// Implementazione del tratto PartialEq per MsgSources
impl PartialEq for MsgSources { impl PartialEq for MsgSources {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
self.msg_id == other.msg_id self.msg_id == other.msg_id && self.fields_with_checkbox == other.fields_with_checkbox
&& self.fields_with_checkbox == other.fields_with_checkbox // Confronta l'uguaglianza dei campi
} }
} }
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment