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