liftof_tui/tabs/
tab_tofsummary.rs

1use std::collections::VecDeque;
2
3use crossbeam_channel::{
4  Sender,
5  Receiver
6};
7
8//extern crate ndhistogram;
9use ndhistogram::{
10  ndhistogram,
11  Histogram,
12  Hist1D,
13};
14
15use ndhistogram::axis::{
16  Uniform,
17};
18
19use ratatui::{
20  //symbols,
21  layout::{
22      Alignment,
23      Constraint,
24      Direction,
25      Layout,
26      Rect
27  },
28  style::{
29      //Color,
30      //Modifier,
31      Style
32  },
33  //text::Span,
34  //terminal::Frame,
35  Frame,
36  widgets::{
37      Block,
38      //Dataset,
39      //Axis,
40      //GraphType,
41      BorderType,
42      //Chart,
43      //BarChart,
44      Sparkline,
45      Borders,
46      Paragraph
47  },
48};
49
50//use tof_dataclasses::packets::TofPacket;
51use tof_dataclasses::events::TofEventSummary;
52use tof_dataclasses::errors::SerializationError;
53//use tof_dataclasses::serialization::Serialization;
54use tof_dataclasses::database::DsiJChPidMapping;
55
56use crate::colors::{
57    ColorTheme,
58};
59
60use crate::widgets::{
61    //clean_data,
62    prep_data,
63    create_labels,
64    histogram,
65    gauge,
66};
67
68#[derive(Debug, Clone)]
69pub struct TofSummaryTab {
70  pub ts_receiver     : Receiver<TofEventSummary>,
71  /// pass our events on when we are done with them!
72  pub ts_sender       : Sender<TofEventSummary>,
73  pub summary_queue   : VecDeque<TofEventSummary>,
74  pub queue_size      : usize,
75  pub n_trg_pdl_histo : Hist1D<Uniform<f32>>, 
76  pub theme           : ColorTheme,
77
78  // missing event analysis
79  pub event_id_test   : Vec<u32>,
80  pub evid_test_info  : String,
81  pub evid_test_len   : usize,
82  pub n_evid_test     : usize,
83  pub evid_test_chnks : VecDeque<u64>,
84
85  // missing HG hit analysis
86  pub miss_hg_hits    : Hist1D<Uniform<f32>>,
87  pub pid_map         : DsiJChPidMapping,
88}
89
90impl TofSummaryTab {
91  pub fn new(ts_receiver  : Receiver<TofEventSummary>,
92             ts_sender    : Sender<TofEventSummary>,
93             dsijchpidmap : &DsiJChPidMapping,
94             theme        : ColorTheme) -> Self {
95    
96    let bins          = Uniform::new(25, 0.0, 25.0).unwrap();
97    let mhg_bins      = Uniform::new(160, 0.0, 160.0).unwrap();
98    Self {
99      ts_receiver     : ts_receiver,
100      ts_sender       : ts_sender,
101      summary_queue   : VecDeque::<TofEventSummary>::new(),
102      queue_size      : 10000,
103      n_trg_pdl_histo : ndhistogram!(bins),
104      theme           : theme,
105      event_id_test   : Vec::<u32>::with_capacity(100000),
106      evid_test_info  : String::from("Missing event id analysis"),
107      evid_test_len   : 20000,
108      n_evid_test     : 0,
109      evid_test_chnks : VecDeque::<u64>::new(),
110      miss_hg_hits    : ndhistogram!(mhg_bins),
111      pid_map         : dsijchpidmap.clone(),
112    }
113  }
114
115  pub fn receive_packet(&mut self) -> Result<(), SerializationError> {
116    //let mut ts = TofEventSummary::new();
117    match self.ts_receiver.try_recv() {
118      Err(_err)  => {
119        trace!("Unable to receive new TofEventSummary!");
120      },
121      Ok(ts)    => {
122        // triggerd paddles histogram
123        self.n_trg_pdl_histo.fill(&(ts.n_trigger_paddles as f32));
124        // missing hg hits for paddles histogram
125        let missing_hg = ts.get_missing_paddles_hg(&self.pid_map);
126        for pid in &missing_hg {
127            self.miss_hg_hits.fill(&(*pid as f32));
128        }
129        if self.event_id_test.len() != self.evid_test_len {
130          self.event_id_test.push(ts.event_id);
131        } else {
132          //let mut miss_pos = Vec::<usize>::new();
133          let mut missing = 0usize;
134          let mut evid = self.event_id_test[0];
135          for _ in 0..self.event_id_test.len() {
136            if !self.event_id_test.contains(&evid) {
137              missing += 1;
138              //miss_pos.push(k);
139            }
140            evid += 1;
141          }
142          self.n_evid_test += 1;
143          self.evid_test_chnks.push_back(missing as u64);
144          if self.evid_test_chnks.len() > 100 {
145            self.evid_test_chnks.pop_front();
146          }
147          self.evid_test_info  = format!("Missing event ID search [{}]", self.n_evid_test);
148          self.evid_test_info += &(format!("\n-- in a chunk of {} event ids", self.evid_test_len)); 
149          self.evid_test_info += &(format!("\n-- we found {} event ids missing ({}%)", missing, 100.0*(missing as f64)/self.event_id_test.len() as f64));
150          self.evid_test_info += &(format!("\n-- -- previous: {:?}", self.evid_test_chnks));
151          self.event_id_test.clear();
152        }
153        self.summary_queue.push_back(ts.clone());
154        if self.summary_queue.len() > self.queue_size {
155          self.summary_queue.pop_front();
156        }
157        match self.ts_sender.send(ts) {
158          Ok(_)    => (),
159          Err(err) => error!("Unable to pass on TofEvent! {err}")
160        }
161      }
162    }
163    Ok(())
164  }
165  
166  pub fn render(&mut self, main_window : &Rect, frame : &mut Frame) {
167    let layout = Layout::default()
168      .direction(Direction::Horizontal)
169      .constraints(
170          [Constraint::Percentage(30),
171           Constraint::Percentage(70)].as_ref(),
172      )
173      .split(*main_window);
174   
175    let histo_view = Layout::default()
176      .direction(Direction::Vertical)
177      .constraints(
178          [Constraint::Percentage(33),
179           Constraint::Percentage(33),
180           Constraint::Percentage(34)].as_ref(),
181      )  
182      .split(layout[1]);
183
184    let evid_test_view = Layout::default()
185      .direction(Direction::Vertical)
186      .constraints(
187        [Constraint::Percentage(70),
188         Constraint::Percentage(30)].as_ref(),
189      )
190      .split(histo_view[2]);
191    
192    let evid_test_view_0 = Layout::default()
193      .direction(Direction::Horizontal)
194      .constraints(
195        [Constraint::Percentage(30),
196         Constraint::Percentage(70)].as_ref(),
197      )
198      .split(evid_test_view[0]);
199
200    let last_ts = self.summary_queue.back();
201    let view_string : String;
202    match last_ts {
203      Some(ts) => { 
204        view_string = ts.to_string();
205      }, 
206      None => {
207        view_string = String::from("TofEventSummary QUEUE EMPTY!");
208      }
209    }
210    let event_view = Paragraph::new(view_string)
211      .style(Style::default().fg(self.theme.fg0))
212      .alignment(Alignment::Left)
213      //.scroll((5, 10))
214      .block(
215        Block::default()
216          .borders(Borders::ALL)
217          .style(self.theme.style())
218          .title("Last TofEventSummary")
219          .border_type(BorderType::Rounded),
220      );
221    frame.render_widget(event_view, layout[0]);
222     
223    // histograms
224    let th_labels  = create_labels(&self.n_trg_pdl_histo);
225    let th_data    = prep_data(&self.n_trg_pdl_histo, &th_labels, 5, true); 
226    let th_chart   = histogram(th_data, String::from("N Trig Paddles"), 2, 0, &self.theme);
227    frame.render_widget(th_chart, histo_view[0]); 
228   
229    let mhg_labels = create_labels(&self.miss_hg_hits);
230    let mhg_data   = prep_data(&self.miss_hg_hits, &mhg_labels, 10, true);
231    let mhg_chart  = histogram(mhg_data, String::from("Missing HG hits"), 1, 0, &self.theme);
232    frame.render_widget(mhg_chart, histo_view[1]);
233
234    let evid_test_data = Paragraph::new(self.evid_test_info.clone())
235      .style(Style::default().fg(self.theme.fg0))
236      .alignment(Alignment::Left)
237      //.scroll((5, 10))
238      .block(
239        Block::default()
240          .borders(Borders::ALL)
241          .style(self.theme.style())
242          .title("Missing event ID test")
243          .border_type(BorderType::Rounded),
244      );
245    let mut spl_data  = Vec::<u64>::new();
246    spl_data.extend_from_slice(self.evid_test_chnks.make_contiguous());
247    // that the sparkline does something, it can't be zero. 
248    // There is no axis marker, so we just add 1 to every bin
249    for k in 0..spl_data.len() {
250      spl_data[k] += 1;
251    }
252    let sparkline = Sparkline::default()
253      .style(self.theme.style())
254      //.direction(RenderDirection::LeftToRight)
255      //.data(self.evid_test_chnks.make_contiguous())
256      .data(&spl_data)
257      .block(
258        Block::default()
259        .borders(Borders::ALL)
260        .style(self.theme.style())
261        .title("Missing event IDs in chunks")
262      );
263
264    frame.render_widget(evid_test_data, evid_test_view_0[0]);
265    frame.render_widget(sparkline, evid_test_view_0[1]);
266    let ratio = self.event_id_test.len() as f64 / self.evid_test_len as f64;
267    let test_gauge = gauge(String::from("Missing event ID check"), String::from("Gathering data"), ratio, &self.theme);
268    frame.render_widget(test_gauge, evid_test_view[1]);
269  }
270}