liftof_tui/tabs/
tab_home.rs1use std::collections::{
4 HashMap,
5 VecDeque,
6};
7
8use std::time::{
9 Instant,
10 };
12
13use std::sync::{
14 Arc,
15 Mutex,
16};
17use ratatui::prelude::*;
18
19use ratatui::Frame;
21use ratatui::layout::Rect;
22use ratatui::widgets::{
23 Block,
24 BorderType,
25 Borders,
26 Paragraph,
27 Table,
28 Row,
29};
30
31use 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,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,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::Horizontal)
64 .constraints(
65 [Constraint::Percentage(60),
66 Constraint::Percentage(40)].as_ref(),
67 )
68 .split(*main_window);
69
70 let upper_chunks = Layout::default()
71 .direction(Direction::Vertical)
72 .constraints(
73 [Constraint::Percentage(75),
74 Constraint::Percentage(25)].as_ref(),
75 )
76 .split(main_chunks[0]);
77
78 let mut rows = Vec::<Row>::new();
79 let mut sum_pack = 0;
80 let mut sum_bytes = 0;
81 let passed_time = self.start_time.elapsed().as_secs_f64();
82 match self.pack_stat.lock() {
83 Err(_err) => (),
84 Ok(mut _stat) => {
85 for k in _stat.keys() {
86 if _stat[k].0 != 0 {
88 sum_pack += _stat[k].0;
89 sum_bytes += _stat[k].1;
90 if k.contains("HB"){ rows.push(Row::new(vec![format!(" \u{1f493} {:.1}", _stat[k].0),
92 format!("{:.1}", (_stat[k].0 as f64)/passed_time,),
93 format!("{:.1}", 0.008*(_stat[k].1 as f64)/passed_time,),
94 format!("[{}]", k)]));
95 } else {
96 rows.push(Row::new(vec![format!(" \u{279f} {:.1}", _stat[k].0),
97 format!("{:.1}", (_stat[k].0 as f64)/passed_time,),
98 format!("{:.1}", 0.008*(_stat[k].1 as f64)/passed_time,),
99 format!("[{}]", k)]));
100 }
101 }
102 }
103 }
104 }
105 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}"]));
106 rows.push(Row::new(vec![format!(" \u{279f}{}", sum_pack),
107 format!("{:.1}/s", (sum_pack as f64)/passed_time),
108 format!("{:.1}kBit/s", 0.008*&(sum_bytes as f64)/passed_time),
109 format!("[TOTAL]")]));
110
111 let widths = [Constraint::Percentage(30),
112 Constraint::Percentage(10),
113 Constraint::Percentage(30),
114 Constraint::Percentage(30)];
115 let table = Table::new(rows, widths)
116 .column_spacing(1)
117 .header(
118 Row::new(vec![" N", "\u{1f4e6}/s", "kBit/s", "Type"])
119 .bottom_margin(1)
120 .top_margin(1)
121 .style(Style::new().add_modifier(Modifier::UNDERLINED))
122 )
123 .block(Block::new()
124 .title("Packet summary \u{1f4e6}")
125 .borders(Borders::ALL)
126 .border_type(BorderType::Rounded)
127 )
128 .style(self.theme.style());
129
130 let main_view = Paragraph::new(LIFTOF_LOGO_SHOW)
131 .style(self.theme.style())
132 .alignment(Alignment::Center)
133 .block(
134 Block::default()
135 .borders(Borders::NONE)
136 );
137
138 match self.streamer.lock() {
139 Err(_err) => (),
140 Ok(mut _vecdeque) => {
141 self.stream = _vecdeque
142 .iter()
143 .cloned() .collect::<Vec<String>>()
145 .join("\n");
146 while _vecdeque.len() > self.stream_max {
148 _vecdeque.pop_front();
149 }
150 },
151 }
152 let side_view = Paragraph::new(self.stream.clone())
154 .style(self.theme.style())
155 .alignment(Alignment::Left)
156 .block(
157 Block::default()
158 .borders(Borders::ALL)
159 .border_type(BorderType::Rounded)
160 .title("Stream")
161 );
162 frame.render_widget(main_view, main_chunks[0]);
163 frame.render_widget(side_view, upper_chunks[1]);
164 frame.render_widget(table, main_chunks[1])
165 }
166}
167