1use std::collections::VecDeque;
2
3use crossbeam_channel::{
4 Sender,
5 Receiver
6};
7
8use ndhistogram::{
10 ndhistogram,
11 Histogram,
12 Hist1D,
13};
14
15use ndhistogram::axis::{
16 Uniform,
17};
18
19use ratatui::{
20 layout::{
22 Alignment,
23 Constraint,
24 Direction,
25 Layout,
26 Rect
27 },
28 style::{
29 Style
32 },
33 Frame,
36 widgets::{
37 Block,
38 BorderType,
42 Sparkline,
45 Borders,
46 Paragraph
47 },
48};
49
50use tof_dataclasses::events::TofEventSummary;
52use tof_dataclasses::errors::SerializationError;
53use tof_dataclasses::database::DsiJChPidMapping;
55
56use crate::colors::{
57 ColorTheme,
58};
59
60use crate::widgets::{
61 prep_data,
63 create_labels,
64 histogram,
65 gauge,
66};
67
68#[derive(Debug, Clone)]
69pub struct TofSummaryTab {
70 pub ts_receiver : Receiver<TofEventSummary>,
71 pub ts_sender : Sender<TofEventSummary>,
73 pub summary_queue : VecDeque<TofEventSummary>,
74 pub queue_size : usize,
75 pub n_trg_pdl_histo : Hist1D<Uniform<f32>>,
76 pub theme : ColorTheme,
77
78 pub event_id_test : Vec<u32>,
80 pub evid_test_info : String,
81 pub evid_test_len : usize,
82 pub n_evid_test : usize,
83 pub evid_test_chnks : VecDeque<u64>,
84
85 pub miss_hg_hits : Hist1D<Uniform<f32>>,
87 pub pid_map : DsiJChPidMapping,
88}
89
90impl TofSummaryTab {
91 pub fn new(ts_receiver : Receiver<TofEventSummary>,
92 ts_sender : Sender<TofEventSummary>,
93 dsijchpidmap : &DsiJChPidMapping,
94 theme : ColorTheme) -> Self {
95
96 let bins = Uniform::new(25, 0.0, 25.0).unwrap();
97 let mhg_bins = Uniform::new(160, 0.0, 160.0).unwrap();
98 Self {
99 ts_receiver : ts_receiver,
100 ts_sender : ts_sender,
101 summary_queue : VecDeque::<TofEventSummary>::new(),
102 queue_size : 10000,
103 n_trg_pdl_histo : ndhistogram!(bins),
104 theme : theme,
105 event_id_test : Vec::<u32>::with_capacity(100000),
106 evid_test_info : String::from("Missing event id analysis"),
107 evid_test_len : 20000,
108 n_evid_test : 0,
109 evid_test_chnks : VecDeque::<u64>::new(),
110 miss_hg_hits : ndhistogram!(mhg_bins),
111 pid_map : dsijchpidmap.clone(),
112 }
113 }
114
115 pub fn receive_packet(&mut self) -> Result<(), SerializationError> {
116 match self.ts_receiver.try_recv() {
118 Err(_err) => {
119 trace!("Unable to receive new TofEventSummary!");
120 },
121 Ok(ts) => {
122 self.n_trg_pdl_histo.fill(&(ts.n_trigger_paddles as f32));
124 let missing_hg = ts.get_missing_paddles_hg(&self.pid_map);
126 for pid in &missing_hg {
127 self.miss_hg_hits.fill(&(*pid as f32));
128 }
129 if self.event_id_test.len() != self.evid_test_len {
130 self.event_id_test.push(ts.event_id);
131 } else {
132 let mut missing = 0usize;
134 let mut evid = self.event_id_test[0];
135 for _ in 0..self.event_id_test.len() {
136 if !self.event_id_test.contains(&evid) {
137 missing += 1;
138 }
140 evid += 1;
141 }
142 self.n_evid_test += 1;
143 self.evid_test_chnks.push_back(missing as u64);
144 if self.evid_test_chnks.len() > 100 {
145 self.evid_test_chnks.pop_front();
146 }
147 self.evid_test_info = format!("Missing event ID search [{}]", self.n_evid_test);
148 self.evid_test_info += &(format!("\n-- in a chunk of {} event ids", self.evid_test_len));
149 self.evid_test_info += &(format!("\n-- we found {} event ids missing ({}%)", missing, 100.0*(missing as f64)/self.event_id_test.len() as f64));
150 self.evid_test_info += &(format!("\n-- -- previous: {:?}", self.evid_test_chnks));
151 self.event_id_test.clear();
152 }
153 self.summary_queue.push_back(ts.clone());
154 if self.summary_queue.len() > self.queue_size {
155 self.summary_queue.pop_front();
156 }
157 match self.ts_sender.send(ts) {
158 Ok(_) => (),
159 Err(err) => error!("Unable to pass on TofEvent! {err}")
160 }
161 }
162 }
163 Ok(())
164 }
165
166 pub fn render(&mut self, main_window : &Rect, frame : &mut Frame) {
167 let layout = Layout::default()
168 .direction(Direction::Horizontal)
169 .constraints(
170 [Constraint::Percentage(30),
171 Constraint::Percentage(70)].as_ref(),
172 )
173 .split(*main_window);
174
175 let histo_view = Layout::default()
176 .direction(Direction::Vertical)
177 .constraints(
178 [Constraint::Percentage(33),
179 Constraint::Percentage(33),
180 Constraint::Percentage(34)].as_ref(),
181 )
182 .split(layout[1]);
183
184 let evid_test_view = Layout::default()
185 .direction(Direction::Vertical)
186 .constraints(
187 [Constraint::Percentage(70),
188 Constraint::Percentage(30)].as_ref(),
189 )
190 .split(histo_view[2]);
191
192 let evid_test_view_0 = Layout::default()
193 .direction(Direction::Horizontal)
194 .constraints(
195 [Constraint::Percentage(30),
196 Constraint::Percentage(70)].as_ref(),
197 )
198 .split(evid_test_view[0]);
199
200 let last_ts = self.summary_queue.back();
201 let view_string : String;
202 match last_ts {
203 Some(ts) => {
204 view_string = ts.to_string();
205 },
206 None => {
207 view_string = String::from("TofEventSummary QUEUE EMPTY!");
208 }
209 }
210 let event_view = Paragraph::new(view_string)
211 .style(Style::default().fg(self.theme.fg0))
212 .alignment(Alignment::Left)
213 .block(
215 Block::default()
216 .borders(Borders::ALL)
217 .style(self.theme.style())
218 .title("Last TofEventSummary")
219 .border_type(BorderType::Rounded),
220 );
221 frame.render_widget(event_view, layout[0]);
222
223 let th_labels = create_labels(&self.n_trg_pdl_histo);
225 let th_data = prep_data(&self.n_trg_pdl_histo, &th_labels, 5, true);
226 let th_chart = histogram(th_data, String::from("N Trig Paddles"), 2, 0, &self.theme);
227 frame.render_widget(th_chart, histo_view[0]);
228
229 let mhg_labels = create_labels(&self.miss_hg_hits);
230 let mhg_data = prep_data(&self.miss_hg_hits, &mhg_labels, 10, true);
231 let mhg_chart = histogram(mhg_data, String::from("Missing HG hits"), 1, 0, &self.theme);
232 frame.render_widget(mhg_chart, histo_view[1]);
233
234 let evid_test_data = Paragraph::new(self.evid_test_info.clone())
235 .style(Style::default().fg(self.theme.fg0))
236 .alignment(Alignment::Left)
237 .block(
239 Block::default()
240 .borders(Borders::ALL)
241 .style(self.theme.style())
242 .title("Missing event ID test")
243 .border_type(BorderType::Rounded),
244 );
245 let mut spl_data = Vec::<u64>::new();
246 spl_data.extend_from_slice(self.evid_test_chnks.make_contiguous());
247 for k in 0..spl_data.len() {
250 spl_data[k] += 1;
251 }
252 let sparkline = Sparkline::default()
253 .style(self.theme.style())
254 .data(&spl_data)
257 .block(
258 Block::default()
259 .borders(Borders::ALL)
260 .style(self.theme.style())
261 .title("Missing event IDs in chunks")
262 );
263
264 frame.render_widget(evid_test_data, evid_test_view_0[0]);
265 frame.render_widget(sparkline, evid_test_view_0[1]);
266 let ratio = self.event_id_test.len() as f64 / self.evid_test_len as f64;
267 let test_gauge = gauge(String::from("Missing event ID check"), String::from("Gathering data"), ratio, &self.theme);
268 frame.render_widget(test_gauge, evid_test_view[1]);
269 }
270}