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 gondola_core::prelude::*;
38
39use crate::colors::ColorTheme;
40use crate::widgets::{
41  //clean_data,
42  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  // pulse height
62  pub pha_histo       : Hist1D<Uniform<f32>>,
63  // pulse time
64  pub pta_histo       : Hist1D<Uniform<f32>>,
65  // pulse charge
66  pub pca_histo       : Hist1D<Uniform<f32>>,
67  // pulse height
68  pub phb_histo       : Hist1D<Uniform<f32>>,
69  // pulse time
70  pub ptb_histo       : Hist1D<Uniform<f32>>,
71  // pulse charge
72  pub pcb_histo       : Hist1D<Uniform<f32>>,
73  // pos acrross
74  pub pa_histo        : Hist1D<Uniform<f32>>,
75  // tzero           
76  pub t0_histo        : Hist1D<Uniform<f32>>,
77  pub edep_histo      : Hist1D<Uniform<f32>>,
78  // paddle id
79  pub pid_histo       : Hist1D<Uniform<f32>>,
80  pub view            : TofHitView,
81  pub paddle_selector : u8,
82  pub paddle_changed  : bool,
83  
84  // list for the rb selector
85  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    // FIXME - can/should this block or not?
151    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        // never filter pid histogram
161        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        // as usual, layout first
186        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        // histograms
213        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        // histograms
283        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        // position across paddle
294        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            //let selector =  1;
335            //if self.paddle_selector != selector {
336            //  self.paddle_changed = true;
337            //  self.paddle_selector = selector;
338            //} else {
339            //  self.paddle_changed = false;
340            //}
341          },
342          Some(pid) => {
343            // entry 0 is for all paddles
344            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}