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