Repository: GyulyVGC/sniffnet (language: rust)

Issue:
Show data agglomerates when filtering connections in Inspect page
### Is there an existing issue for this?

- [X] I have searched the existing issues.

### Describe the solution you'd like

![image](https://github.com/user-attachments/assets/5d368f4f-231e-4c8f-bdb2-25c169d76cdc)
当我选择一个主机展示时，增加该主机显示出站或者入站的数据汇总最好了，这样更方便我统计我代理的服务器消耗的流量数据

### Is your feature request related to a problem?

每次我想统计出站或者入站的数据，特别的麻烦，需要自己加减，要么去大约估值，这样得出的数据很不准确


Discussion / hints:

Fix patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 78c323b83..cf431fa37 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,7 @@ All Sniffnet releases with the relative changes are documented in this file.
 
 ## [UNRELEASED]
 - Identify and tag unassigned/reserved "bogon" IP addresses ([#678](https://github.com/GyulyVGC/sniffnet/pull/678) — fixes [#209](https://github.com/GyulyVGC/sniffnet/issues/209))
+- Show data agglomerates in _Inspect_ page table ([#684](https://github.com/GyulyVGC/sniffnet/pull/684) — fixes [#601](https://github.com/GyulyVGC/sniffnet/issues/601))
 
 ## [1.3.2] - 2025-01-06
 - Dropdown menus for network host filters ([#659](https://github.com/GyulyVGC/sniffnet/pull/659) — fixes [#354](https://github.com/GyulyVGC/sniffnet/issues/354))
diff --git a/src/gui/pages/inspect_page.rs b/src/gui/pages/inspect_page.rs
index db824d053..e36060c18 100644
--- a/src/gui/pages/inspect_page.rs
+++ b/src/gui/pages/inspect_page.rs
@@ -13,6 +13,7 @@ use iced::{alignment, Alignment, Font, Length, Padding, Pixels};
 
 use crate::gui::components::tab::get_pages_tabs;
 use crate::gui::components::types::my_modal::MyModal;
+use crate::gui::pages::overview_page::get_bars;
 use crate::gui::styles::button::ButtonType;
 use crate::gui::styles::container::ContainerType;
 use crate::gui::styles::scrollbar::ScrollbarType;
@@ -21,6 +22,7 @@ use crate::gui::styles::text::TextType;
 use crate::gui::styles::text_input::TextInputType;
 use crate::gui::types::message::Message;
 use crate::networking::types::address_port_pair::AddressPortPair;
+use crate::networking::types::byte_multiple::ByteMultiple;
 use crate::networking::types::host_data_states::HostStates;
 use crate::networking::types::info_address_port_pair::InfoAddressPortPair;
 use crate::networking::types::traffic_direction::TrafficDirection;
@@ -116,7 +118,8 @@ fn lazy_report<'a>(sniffer: &Sniffer) -> Column<'a, Message, StyleType> {
     } = sniffer.configs.lock().unwrap().settings;
     let font = style.get_extension().font;
 
-    let (search_results, results_number) = get_searched_entries(sniffer);
+    let (search_results, results_number, packets, in_bytes, out_bytes) =
+        get_searched_entries(sniffer);
 
     let mut ret_val = Column::new()
         .height(Length::Fill)
@@ -147,6 +150,8 @@ fn lazy_report<'a>(sniffer: &Sniffer) -> Column<'a, Message, StyleType> {
                 .width(Length::Fill),
             )
             .push(Rule::horizontal(5))
+            .push(get_agglomerates_row(font, packets, in_bytes, out_bytes))
+            .push(Rule::horizontal(5))
             .push(get_change_page_row(
                 font,
                 language,
@@ -547,6 +552,38 @@ fn get_button_change_page<'a>(increment: bool) -> Button<'a, Message, StyleType>
     .on_press(Message::UpdatePageNumber(increment))
 }
 
+fn get_agglomerates_row<'a>(
+    font: Font,
+    tot_packets: u128,
+    tot_in_bytes: u128,
+    tot_out_bytes: u128,
+) -> Row<'a, Message, StyleType> {
+    let tot_bytes = tot_in_bytes + tot_out_bytes;
+    let width = ReportCol::FILTER_COLUMNS_WIDTH;
+    #[allow(clippy::cast_precision_loss)]
+    let in_length = width * (tot_in_bytes as f32 / tot_bytes as f32);
+    let out_length = width - in_length;
+    let bars = get_bars(in_length, out_length);
+
+    let bytes_col = Column::new()
+        .align_x(Alignment::Center)
+        .width(ReportCol::Bytes.get_width())
+        .push(Text::new(ByteMultiple::formatted_string(tot_bytes)).font(font));
+
+    let packets_col = Column::new()
+        .align_x(Alignment::Center)
+        .width(ReportCol::Packets.get_width())
+        .push(Text::new(tot_packets.to_string()).font(font));
+
+    Row::new()
+        .padding([0, 2])
+        .height(40)
+        .align_y(Alignment::Center)
+        .push(bars)
+        .push(bytes_col)
+        .push(packets_col)
+}
+
 fn get_change_page_row<'a>(
     font: Font,
     language: Language,
diff --git a/src/gui/pages/overview_page.rs b/src/gui/pages/overview_page.rs
index 5dae518da..b4324ad41 100644
--- a/src/gui/pages/overview_page.rs
+++ b/src/gui/pages/overview_page.rs
@@ -736,7 +736,7 @@ fn get_bars_length(
     (in_len, out_len)
 }
 
-fn get_bars<'a>(in_len: f32, out_len: f32) -> Row<'a, Message, StyleType> {
+pub fn get_bars<'a>(in_len: f32, out_len: f32) -> Row<'a, Message, StyleType> {
     Row::new()
         .push(if in_len > 0.0 {
             Row::new()
diff --git a/src/report/get_report_entries.rs b/src/report/get_report_entries.rs
index 367a3363d..e1c1da5b4 100644
--- a/src/report/get_report_entries.rs
+++ b/src/report/get_report_entries.rs
@@ -7,14 +7,25 @@ use crate::networking::types::data_info::DataInfo;
 use crate::networking::types::data_info_host::DataInfoHost;
 use crate::networking::types::host::Host;
 use crate::networking::types::info_address_port_pair::InfoAddressPortPair;
+use crate::networking::types::traffic_direction::TrafficDirection;
 use crate::report::types::sort_type::SortType;
 use crate::{ChartType, InfoTraffic, ReportSortType, Service, Sniffer};
 
-/// Returns the elements which satisfy the search constraints and belong to the given page,
-/// and the total number of elements which satisfy the search constraints
+/// Return the elements that satisfy the search constraints and belong to the given page,
+/// and the total number of elements which satisfy the search constraints,
+/// with their packets, in-bytes, and out-bytes count
 pub fn get_searched_entries(
     sniffer: &Sniffer,
-) -> (Vec<(AddressPortPair, InfoAddressPortPair)>, usize) {
+) -> (
+    Vec<(AddressPortPair, InfoAddressPortPair)>,
+    usize,
+    u128,
+    u128,
+    u128,
+) {
+    let mut tot_packets = 0;
+    let mut tot_in_bytes = 0;
+    let mut tot_out_bytes = 0;
     let info_traffic_lock = sniffer.info_traffic.lock().unwrap();
     let mut all_results: Vec<(&AddressPortPair, &InfoAddressPortPair)> = info_traffic_lock
         .map
@@ -31,7 +42,17 @@ pub fn get_searched_entries(
                 .search
                 .match_entry(key, value, r_dns_host, is_favorite)
         })
+        .map(|(key, val)| {
+            tot_packets += val.transmitted_packets;
+            if val.traffic_direction == TrafficDirection::Outgoing {
+                tot_out_bytes += val.transmitted_bytes;
+            } else {
+                tot_in_bytes += val.transmitted_bytes;
+            }
+            (key, val)
+        })
         .collect();
+
     all_results.sort_by(|&(_, a), &(_, b)| match sniffer.report_sort_type {
         ReportSortType {
             byte_sort,
@@ -62,6 +83,9 @@ pub fn get_searched_entries(
             .map(|&(key, val)| (key.to_owned(), val.to_owned()))
             .collect(),
         all_results.len(),
+        tot_packets,
+        tot_in_bytes,
+        tot_out_bytes,
     )
 }
 
diff --git a/src/report/types/report_col.rs b/src/report/types/report_col.rs
index bbcf4c6b5..dd5e5fb40 100644
--- a/src/report/types/report_col.rs
+++ b/src/report/types/report_col.rs
@@ -41,6 +41,8 @@ impl ReportCol {
         ReportCol::Packets,
     ];
 
+    pub(crate) const FILTER_COLUMNS_WIDTH: f32 = 4.0 * SMALL_COL_WIDTH + 2.0 * LARGE_COL_WIDTH;
+
     pub(crate) fn get_title(&self, language: Language) -> String {
         match self {
             ReportCol::SrcIp | ReportCol::DstIp => address_translation(language).to_string(),


Test patch: