Skip to content
GitLab
Explore
Sign in
Register
Primary navigation
Search or go to…
Project
Skyward Enhanced Ground Software
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
Avionics
Software Development
Skyward Enhanced Ground Software
Commits
f11e5fd2
Commit
f11e5fd2
authored
3 months ago
by
Camillo Nicoletti
Browse files
Options
Downloads
Patches
Plain Diff
Work-in-progress
parent
8d2cbdc1
Branches
Branches containing commit
No related tags found
No related merge requests found
Changes
1
Show whitespace changes
Inline
Side-by-side
Showing
1 changed file
src/ui/panes/messages_viewer.rs
+78
-164
78 additions, 164 deletions
src/ui/panes/messages_viewer.rs
with
78 additions
and
164 deletions
src/ui/panes/messages_viewer.rs
+
78
−
164
View file @
f11e5fd2
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
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment