1use std::collections::{
2 VecDeque,
4};
5
6use crossbeam_channel::{
7 Receiver,
8 };
10
11use ratatui::prelude::*;
12use ratatui::widgets::{
13 Block,
14 BorderType,
15 Borders,
16 Paragraph,
17 List,
19 ListItem,
20 ListState,
21};
22
23use ratatui::Frame;
25use ratatui::layout::Rect;
26
27use ndhistogram::{
28 ndhistogram,
29 Histogram,
30 Hist1D,
31};
32
33use ndhistogram::axis::{
34 Uniform,
35};
36
37use tof_dataclasses::events::{
38 TofHit,
41 };
44
45use tof_dataclasses::errors::SerializationError;
46
47use crate::colors::ColorTheme;
48use crate::widgets::{
49 prep_data,
51 create_labels,
52 histogram,
53};
54
55#[derive(Debug, Clone)]
56pub enum TofHitView {
57 Hits,
58 Pulses,
59 Paddles,
60 SelectPaddle,
61}
62
63#[derive(Debug, Clone)]
64pub struct TofHitTab<'a> {
65 pub theme : ColorTheme,
66 pub th_recv : Receiver<TofHit>,
67 pub hit_queue : VecDeque<TofHit>,
68 pub queue_size : usize,
69 pub pha_histo : Hist1D<Uniform<f32>>,
71 pub pta_histo : Hist1D<Uniform<f32>>,
73 pub pca_histo : Hist1D<Uniform<f32>>,
75 pub phb_histo : Hist1D<Uniform<f32>>,
77 pub ptb_histo : Hist1D<Uniform<f32>>,
79 pub pcb_histo : Hist1D<Uniform<f32>>,
81 pub pa_histo : Hist1D<Uniform<f32>>,
83 pub t0_histo : Hist1D<Uniform<f32>>,
85 pub edep_histo : Hist1D<Uniform<f32>>,
86 pub pid_histo : Hist1D<Uniform<f32>>,
88 pub view : TofHitView,
89 pub paddle_selector : u8,
90 pub paddle_changed : bool,
91
92 pub pl_state : ListState,
94 pub pl_items : Vec::<ListItem<'a>>,
95 pub pl_active : bool,
96}
97
98impl TofHitTab<'_> {
99 pub fn new(th_recv : Receiver<TofHit>, theme : ColorTheme) -> TofHitTab<'static> {
100 let bins_ph = Uniform::new(50, 0.0, 200.0).unwrap();
101 let bins_pt = Uniform::new(50, 0.0, 400.0).unwrap();
102 let bins_pc = Uniform::new(30, 0.0, 30.0).unwrap();
103 let bins_pa = Uniform::new(90, 0.0, 1800.0).unwrap();
104 let bins_t0 = Uniform::new(50, 0.0, 200.0).unwrap();
105 let bins_pid = Uniform::new(160,1.0, 161.0).unwrap();
106 let bins_edep = Uniform::new(50,0.0,100.0).unwrap();
107 let mut paddle_select_items = Vec::<ListItem>::new();
108 let all_pdl = String::from(" All paddles");
109 paddle_select_items.push(ListItem::new(Line::from(all_pdl)));
110 for k in 1..161 {
111 let this_item = format!(" Paddle {:0>3}", k);
112 paddle_select_items.push(ListItem::new(Line::from(this_item)));
113 }
114 TofHitTab {
115 th_recv : th_recv,
116 hit_queue : VecDeque::<TofHit>::new(),
117 queue_size : 3000,
118 theme : theme,
119 pha_histo : ndhistogram!(bins_ph.clone()),
120 pta_histo : ndhistogram!(bins_pt.clone()),
121 pca_histo : ndhistogram!(bins_pc.clone()),
122 phb_histo : ndhistogram!(bins_ph),
123 ptb_histo : ndhistogram!(bins_pt),
124 pcb_histo : ndhistogram!(bins_pc),
125 pa_histo : ndhistogram!(bins_pa),
126 t0_histo : ndhistogram!(bins_t0),
127 edep_histo : ndhistogram!(bins_edep),
128 pid_histo : ndhistogram!(bins_pid),
129 view : TofHitView::Pulses,
130 paddle_selector : 0,
131 paddle_changed : false,
132
133 pl_state : ListState::default(),
134 pl_items : paddle_select_items,
135 pl_active : false,
136 }
137 }
138
139 pub fn init_histos(&mut self) {
140 let bins_ph = Uniform::new(50, 0.0, 200.0).unwrap();
141 let bins_pt = Uniform::new(50, 0.0, 400.0).unwrap();
142 let bins_pc = Uniform::new(30, 0.0, 30.0).unwrap();
143 let bins_pa = Uniform::new(90, 0.0, 1800.0).unwrap();
144 let bins_t0 = Uniform::new(50, 0.0, 200.0).unwrap();
145 let bins_edep = Uniform::new(50,0.0,100.0).unwrap();
146 self.pha_histo = ndhistogram!(bins_ph.clone());
147 self.pta_histo = ndhistogram!(bins_pt.clone());
148 self.pca_histo = ndhistogram!(bins_pc.clone());
149 self.phb_histo = ndhistogram!(bins_ph);
150 self.ptb_histo = ndhistogram!(bins_pt);
151 self.pcb_histo = ndhistogram!(bins_pc);
152 self.pa_histo = ndhistogram!(bins_pa);
153 self.t0_histo = ndhistogram!(bins_t0);
154 self.edep_histo = ndhistogram!(bins_edep);
155 }
156
157 pub fn receive_packet(&mut self) -> Result<(), SerializationError> {
158 match self.th_recv.try_recv() {
160 Err(_err) => {
161 return Ok(());
162 },
163 Ok(hit) => {
164 self.hit_queue.push_back(hit);
165 if self.hit_queue.len() > self.queue_size {
166 self.hit_queue.pop_front();
167 }
168 self.pid_histo.fill(&(hit.paddle_id as f32));
170 if self.paddle_selector != 0 {
171 if hit.paddle_id != self.paddle_selector {
172 return Ok(());
173 }
174 }
175 self.pha_histo.fill(&hit.get_peak_a());
176 self.phb_histo.fill(&hit.get_peak_b());
177 self.pta_histo.fill(&hit.get_time_a());
178 self.ptb_histo.fill(&hit.get_time_b());
179 self.pca_histo.fill(&hit.get_charge_a());
180 self.pcb_histo.fill(&hit.get_charge_b());
181 self.t0_histo.fill(&(hit.get_t0()));
182 self.pa_histo.fill(&(hit.get_pos()));
183 self.edep_histo.fill(&(hit.get_edep()));
184 return Ok(());
185 }
186 }
187 }
188
189 pub fn render(&mut self, main_window : &Rect, frame : &mut Frame) {
190
191 match self.view {
192 TofHitView::Pulses => {
193 let chunks = Layout::default()
195 .direction(Direction::Horizontal)
196 .constraints(
197 [Constraint::Percentage(50),
198 Constraint::Percentage(50)].as_ref(),
199 )
200 .split(*main_window);
201 let plots_a = Layout::default()
202 .direction(Direction::Vertical)
203 .constraints(
204 [Constraint::Percentage(33),
205 Constraint::Percentage(33),
206 Constraint::Percentage(34),
207 ].as_ref(),
208 )
209 .split(chunks[0]);
210 let plots_b = Layout::default()
211 .direction(Direction::Vertical)
212 .constraints(
213 [Constraint::Percentage(33),
214 Constraint::Percentage(33),
215 Constraint::Percentage(34),
216 ].as_ref(),
217 )
218 .split(chunks[1]);
219
220 let ph_labels = create_labels(&self.pha_histo);
222 let pha_data = prep_data(&self.pha_histo, &ph_labels, 5, false);
223 let pha_chart = histogram(pha_data, String::from("Pulse height SideA [mV]"), 2, 0, &self.theme);
224 frame.render_widget(pha_chart, plots_a[0]);
225 let phb_data = prep_data(&self.phb_histo, &ph_labels, 5, false);
226 let phb_chart = histogram(phb_data, String::from("Pulse height SideB [mV]"), 2, 0, &self.theme);
227 frame.render_widget(phb_chart, plots_b[0]);
228
229 let pt_labels = create_labels(&self.pta_histo);
230 let pta_data = prep_data(&self.pta_histo, &pt_labels, 5, false);
231 let pta_chart = histogram(pta_data, String::from("Pulse time SideA [a.u.]"), 2, 0, &self.theme);
232 frame.render_widget(pta_chart, plots_a[1]);
233
234 let ptb_data = prep_data(&self.ptb_histo, &pt_labels, 5, false);
235 let ptb_chart = histogram(ptb_data, String::from("Pulse time SideB [a.u.]"), 2, 0, &self.theme);
236 frame.render_widget(ptb_chart, plots_b[1]);
237
238 let pc_labels = create_labels(&self.pca_histo);
239 let pca_data = prep_data(&self.pca_histo, &pc_labels, 5, false);
240 let pca_chart = histogram(pca_data, String::from("Pulse charge SideA [mC]"), 2, 0, &self.theme);
241 frame.render_widget(pca_chart, plots_a[2]);
242
243 let pcb_data = prep_data(&self.pcb_histo, &pc_labels, 5, false);
244 let pcb_chart = histogram(pcb_data, String::from("Pulse charge SideB [mC]"), 2, 0, &self.theme);
245 frame.render_widget(pcb_chart, plots_b[2]);
246
247 },
248 TofHitView::Hits => {
249 let chunks = Layout::default()
250 .direction(Direction::Horizontal)
251 .constraints(
252 [Constraint::Percentage(60), Constraint::Percentage(40)].as_ref(),
253 )
254 .split(*main_window);
255 let mut hit_string = String::from("No HIT");
256 match self.hit_queue.back() {
257 None => (),
258 Some(hit) => {
259 hit_string = hit.to_string();
260 }
261 }
262 let hit_view = Paragraph::new(hit_string)
263 .style(self.theme.style())
264 .alignment(Alignment::Left)
265 .block(
266 Block::default()
267 .borders(Borders::ALL)
268 .border_type(BorderType::Rounded)
269 .title("Last TofHit")
270 );
271 frame.render_widget(hit_view, chunks[0]);
272 },
273 TofHitView::Paddles => {
274 let chunks = Layout::default()
275 .direction(Direction::Vertical)
276 .constraints(
277 [Constraint::Percentage(33),
278 Constraint::Percentage(33),
279 Constraint::Percentage(34)].as_ref(),
280 )
281 .split(*main_window);
282 let plots = Layout::default()
283 .direction(Direction::Horizontal)
284 .constraints(
285 [Constraint::Percentage(50),
286 Constraint::Percentage(50)].as_ref(),
287 )
288 .split(chunks[0]);
289
290 let t0_labels = create_labels(&self.t0_histo);
292 let t0_data = prep_data(&self.t0_histo, &t0_labels, 10, false);
293 let t0_chart = histogram(t0_data, String::from("Reco. T0"), 2, 0, &self.theme);
294 frame.render_widget(t0_chart, plots[0]);
295
296 let edep_labels = create_labels(&self.edep_histo);
297 let edep_data = prep_data(&self.edep_histo, &edep_labels, 5, false);
298 let edep_chart = histogram(edep_data, String::from("Reco. EDep"), 2, 0, &self.theme);
299 frame.render_widget(edep_chart, plots[1]);
300
301 let pa_labels = create_labels(&self.pa_histo);
303 let pa_data = prep_data(&self.pa_histo, &pa_labels, 20, false);
304 let pa_chart = histogram(pa_data, String::from("Position accross paddle"), 2, 0, &self.theme);
305 frame.render_widget(pa_chart, chunks[1]);
306
307 let pid_labels = create_labels(&self.pid_histo);
308 let pid_data = prep_data(&self.pid_histo, &pid_labels, 10, true);
309 let pid_chart = histogram(pid_data, String::from("Paddle ID"), 3, 0, &self.theme);
310 frame.render_widget(pid_chart, chunks[2]);
311 },
312 TofHitView::SelectPaddle => {
313 let list_chunks = Layout::default()
314 .direction(Direction::Horizontal)
315 .constraints(
316 [Constraint::Percentage(20), Constraint::Percentage(80)].as_ref(),
317 )
318 .split(*main_window);
319 let par_title_string = String::from("Select Paddle ID");
320 let (first, rest) = par_title_string.split_at(1);
321 let par_title = Line::from(vec![
322 Span::styled(
323 first,
324 Style::default()
325 .fg(self.theme.hc)
326 .add_modifier(Modifier::UNDERLINED),
327 ),
328 Span::styled(rest, self.theme.style()),
329 ]);
330 let paddles = Block::default()
331 .borders(Borders::ALL)
332 .style(self.theme.style())
333 .title(par_title)
334 .border_type(BorderType::Plain);
335 let paddle_select_list = List::new(self.pl_items.clone()).block(paddles)
336 .highlight_style(self.theme.highlight().add_modifier(Modifier::BOLD))
337 .highlight_symbol(">>")
338 .repeat_highlight_symbol(true);
339 match self.pl_state.selected() {
340 None => {
341 self.paddle_selector = 0;
342 },
350 Some(pid) => {
351 let selector = pid as u8;
353 if self.paddle_selector != selector {
354 self.paddle_changed = true;
355 self.init_histos();
356 self.paddle_selector = selector;
357 } else {
358 self.paddle_changed = false;
359 }
360 },
361 }
362 frame.render_stateful_widget(paddle_select_list, list_chunks[0], &mut self.pl_state );
363 }
364 }
365 }
366}