liftof_tui/tabs/
tab_tofhit.rs

1use std::collections::{
2    //HashMap,
3    VecDeque,
4};
5
6use crossbeam_channel::{
7    Receiver,
8    //Sender,
9};
10
11use ratatui::prelude::*;
12use ratatui::widgets::{
13    Block,
14    BorderType,
15    Borders,
16    Paragraph,
17    //BarChart,
18    List,
19    ListItem,
20    ListState,
21};
22
23//use ratatui::terminal::Frame;
24use 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    //RBEvent,
39    //TofEvent,
40    TofHit,
41    //TofEventHeader,
42    //MasterTriggerEvent,
43};
44
45use tof_dataclasses::errors::SerializationError;
46
47use crate::colors::ColorTheme;
48use crate::widgets::{
49    //clean_data,
50    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  // pulse height
70  pub pha_histo       : Hist1D<Uniform<f32>>,
71  // pulse time
72  pub pta_histo       : Hist1D<Uniform<f32>>,
73  // pulse charge
74  pub pca_histo       : Hist1D<Uniform<f32>>,
75  // pulse height
76  pub phb_histo       : Hist1D<Uniform<f32>>,
77  // pulse time
78  pub ptb_histo       : Hist1D<Uniform<f32>>,
79  // pulse charge
80  pub pcb_histo       : Hist1D<Uniform<f32>>,
81  // pos acrross
82  pub pa_histo        : Hist1D<Uniform<f32>>,
83  // tzero           
84  pub t0_histo        : Hist1D<Uniform<f32>>,
85  pub edep_histo      : Hist1D<Uniform<f32>>,
86  // paddle id
87  pub pid_histo       : Hist1D<Uniform<f32>>,
88  pub view            : TofHitView,
89  pub paddle_selector : u8,
90  pub paddle_changed  : bool,
91  
92  // list for the rb selector
93  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    // FIXME - can/should this block or not?
159    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        // never filter pid histogram
169        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        // as usual, layout first
194        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        // histograms
221        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        // histograms
291        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        // position across paddle
302        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            //let selector =  1;
343            //if self.paddle_selector != selector {
344            //  self.paddle_changed = true;
345            //  self.paddle_selector = selector;
346            //} else {
347            //  self.paddle_changed = false;
348            //}
349          },
350          Some(pid) => {
351            // entry 0 is for all paddles
352            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}