liftof_tui/tabs/
tab_home.rs

1//! The front page of the app, including a view of received packets
2
3use std::collections::{
4  HashMap,
5  VecDeque,
6};
7
8use std::time::{
9  Instant,
10  //Duration
11};
12
13use std::sync::{
14  Arc,
15  Mutex,
16};
17use ratatui::prelude::*;
18
19//use ratatui::terminal::Frame;
20use ratatui::Frame;
21use ratatui::layout::Rect;
22use ratatui::widgets::{
23  Block,
24  BorderType,
25  Borders,
26  Paragraph,
27  Table,
28  Row,
29};
30
31//use liftof_lib::LIFTOF_LOGO_SHOW;
32use gondola_core::prelude::LIFTOF_LOGO_SHOW;
33
34use crate::colors::ColorTheme;
35
36
37#[derive(Debug, Clone)]
38pub struct HomeTab<'a> {
39  pub theme      : ColorTheme,
40  pub streamer   : Arc<Mutex<VecDeque<String>>>,
41  pub pack_stat  : Arc<Mutex<HashMap<&'a str, usize>>>,
42  pub stream     : String,
43  pub stream_max : usize, 
44  start_time     : Instant,
45}
46
47impl HomeTab<'_> {
48  pub fn new(theme     : ColorTheme,
49             streamer  : Arc<Mutex<VecDeque<String>>>,
50             pack_stat : Arc<Mutex<HashMap<&str,usize>>>) -> HomeTab {
51    HomeTab {
52      theme,
53      streamer, 
54      pack_stat,
55      stream     : String::from(""),
56      stream_max : 30,
57      start_time : Instant::now(),
58    }
59  }
60
61  pub fn render(&mut self, main_window : &Rect, frame : &mut Frame) {
62    let main_chunks = Layout::default()
63      .direction(Direction::Vertical)
64      .constraints(
65          [Constraint::Percentage(70),
66           Constraint::Percentage(30)].as_ref(),
67      )
68      .split(*main_window);
69    
70    let upper_chunks = Layout::default()
71        .direction(Direction::Horizontal)
72        .constraints(
73            [Constraint::Percentage(70),
74            Constraint::Percentage(30)].as_ref(),
75        )
76        .split(main_chunks[0]);
77 
78    let mut rows   = Vec::<Row>::new();
79    let mut sum_pack = 0;
80    let passed_time = self.start_time.elapsed().as_secs_f64();
81    match self.pack_stat.lock() {
82      Err(_err) => (),
83      Ok(mut _stat) =>  {
84        for k in _stat.keys() {
85          //stat_string_render += "  -- -- -- -- -- -- -- -- -- --\n";
86          if _stat[k] != 0 {
87            sum_pack += _stat[k];
88            if k.contains("Heart"){
89              rows.push(Row::new(vec![format!("  \u{1f493} {:.1}", _stat[k]),
90                                      format!("{:.1}", (_stat[k] as f64)/passed_time,),
91                                      format!("[{}]", k)]));
92            } else {
93              rows.push(Row::new(vec![format!("  \u{279f} {:.1}", _stat[k]),
94                                      format!("{:.1}", (_stat[k] as f64)/passed_time,),
95                                      format!("[{}]", k)]));
96            }
97          }
98        }
99      } 
100    }
101    rows.push(Row::new(vec!["  \u{FE4C}\u{FE4C}\u{FE4C}","\u{FE4C}\u{FE4C}","\u{FE4C}\u{FE4C}\u{FE4C}\u{FE4C}\u{FE4C}\u{FE4C}"])); 
102    rows.push(Row::new(vec![format!("  \u{279f}{}", sum_pack),
103                       format!("{:.1}/s", (sum_pack as f64)/passed_time),
104                       format!("[TOTAL]")]));
105    
106    let widths = [Constraint::Percentage(30),
107                  Constraint::Percentage(20),
108                  Constraint::Percentage(50)];
109    let table  = Table::new(rows, widths)
110      .column_spacing(1)
111      .header(
112        Row::new(vec!["  N", "\u{1f4e6}/s", "Type"])
113        .bottom_margin(1)
114        .top_margin(1)
115        .style(Style::new().add_modifier(Modifier::UNDERLINED))
116      )
117      .block(Block::new()
118             .title("Packet summary \u{1f4e6}")
119             .borders(Borders::ALL)
120             .border_type(BorderType::Rounded)
121             )
122      .style(self.theme.style());
123
124    let main_view = Paragraph::new(LIFTOF_LOGO_SHOW)
125    .style(self.theme.style())
126    .alignment(Alignment::Center)
127    .block(
128      Block::default()
129        .borders(Borders::NONE)
130    );
131    
132    match self.streamer.lock() {
133      Err(_err) => (),
134      Ok(mut _vecdeque) =>  {
135        self.stream = _vecdeque
136            .iter()
137            .cloned() // Clone each string to avoid moving ownership
138            .collect::<Vec<String>>()
139            .join("\n");
140        //if _vecdeque.len() > self.stream_max {
141        while _vecdeque.len() > self.stream_max {
142          _vecdeque.pop_front();
143        }
144      }, 
145    }
146    //let stream : String = String::from("");
147    let side_view = Paragraph::new(self.stream.clone())
148    .style(self.theme.style())
149    .alignment(Alignment::Left)
150    .block(
151      Block::default()
152        .borders(Borders::ALL)
153        .border_type(BorderType::Rounded)
154        .title("Stream")
155    );
156    frame.render_widget(main_view,       upper_chunks[0]);
157    frame.render_widget(side_view,       main_chunks[1]);
158    frame.render_widget(table, upper_chunks[1])
159  }
160}
161