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