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
37//use tof_dataclasses::events::{
38//    //RBEvent,
39//    //TofEvent,
40//    TofHit,
41//    //TofEventHeader,
42//    //MasterTriggerEvent,
43//};
44//
45//use tof_dataclasses::errors::SerializationError;
46use gondola_core::prelude::*;
47
48use crate::colors::ColorTheme;
49use crate::widgets::{
50  //clean_data,
51  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  // pulse height
71  pub pha_histo       : Hist1D<Uniform<f32>>,
72  // pulse time
73  pub pta_histo       : Hist1D<Uniform<f32>>,
74  // pulse charge
75  pub pca_histo       : Hist1D<Uniform<f32>>,
76  // pulse height
77  pub phb_histo       : Hist1D<Uniform<f32>>,
78  // pulse time
79  pub ptb_histo       : Hist1D<Uniform<f32>>,
80  // pulse charge
81  pub pcb_histo       : Hist1D<Uniform<f32>>,
82  // pos acrross
83  pub pa_histo        : Hist1D<Uniform<f32>>,
84  // tzero           
85  pub t0_histo        : Hist1D<Uniform<f32>>,
86  pub edep_histo      : Hist1D<Uniform<f32>>,
87  // paddle id
88  pub pid_histo       : Hist1D<Uniform<f32>>,
89  pub view            : TofHitView,
90  pub paddle_selector : u8,
91  pub paddle_changed  : bool,
92  
93  // list for the rb selector
94  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    // FIXME - can/should this block or not?
160    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        // never filter pid histogram
170        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        // as usual, layout first
195        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        // histograms
222        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        // histograms
292        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        // position across paddle
303        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            //let selector =  1;
344            //if self.paddle_selector != selector {
345            //  self.paddle_changed = true;
346            //  self.paddle_selector = selector;
347            //} else {
348            //  self.paddle_changed = false;
349            //}
350          },
351          Some(pid) => {
352            // entry 0 is for all paddles
353            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}