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
50use gondola_core::prelude::*;
51
52use crate::colors::{
53    ColorTheme,
54};
55
56use crate::widgets::{
57    //clean_data,
58    prep_data,
59    create_labels,
60    histogram,
61    gauge,
62};
63
64#[derive(Debug, Clone)]
65pub struct TofSummaryTab {
66  pub ts_receiver     : Receiver<TofPacket>,
67  /// pass our events on when we are done with them!
68  pub ts_sender       : Sender<TofEvent>,
69  pub rbe_sender      : Sender<RBEvent>,
70  pub mte_sender      : Sender<TofEvent>,
71  pub summary_queue   : VecDeque<TofEvent>,
72  pub queue_size      : usize,
73  pub n_trg_pdl_histo : Hist1D<Uniform<f32>>, 
74  pub theme           : ColorTheme,
75
76  // missing event analysis
77  pub event_id_test   : Vec<u32>,
78  pub evid_test_info  : String,
79  pub evid_test_len   : usize,
80  pub n_evid_test     : usize,
81  pub evid_test_chnks : VecDeque<u64>,
82
83  // missing HG hit analysis
84  pub miss_hg_hits    : Hist1D<Uniform<f32>>,
85  pub pid_map         : DsiJChPidMapping,
86}
87
88impl TofSummaryTab {
89  pub fn new(ts_receiver  : Receiver<TofPacket>,
90             ts_sender    : Sender<TofEvent>,
91             mte_sender   : Sender<TofEvent>,
92             rbe_sender   : Sender<RBEvent>,
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      mte_sender      : mte_sender,
102      rbe_sender      : rbe_sender,
103      summary_queue   : VecDeque::<TofEvent>::new(),
104      queue_size      : 10000,
105      n_trg_pdl_histo : ndhistogram!(bins),
106      theme           : theme,
107      event_id_test   : Vec::<u32>::with_capacity(100000),
108      evid_test_info  : String::from("Missing event id analysis"),
109      evid_test_len   : 20000,
110      n_evid_test     : 0,
111      evid_test_chnks : VecDeque::<u64>::new(),
112      miss_hg_hits    : ndhistogram!(mhg_bins),
113      pid_map         : dsijchpidmap.clone(),
114    }
115  }
116
117  pub fn receive_packet(&mut self) -> Result<(), SerializationError> {
118    //let mut ts = TofEvent::new();
119    match self.ts_receiver.try_recv() {
120      Err(_err)  => {
121        trace!("Unable to receive new TofEvent!");
122      },
123      Ok(tp)    => {
124        let ts = tp.unpack::<TofEvent>()?;
125        //let ts : TofEvent;
126        //match tp.unpack::<TofEvent>() {
127        //  Err(err) => {
128        //    error!("Can't unpack TofEvent! {}", err);
129
130        //  }
131        //}
132
133
134        // triggerd paddles histogram
135        self.n_trg_pdl_histo.fill(&(ts.n_trigger_paddles as f32));
136        // missing hg hits for paddles histogram
137        let missing_hg = ts.get_missing_paddles_hg(&self.pid_map);
138        for pid in &missing_hg {
139            self.miss_hg_hits.fill(&(*pid as f32));
140        }
141        if self.event_id_test.len() != self.evid_test_len {
142          self.event_id_test.push(ts.event_id);
143        } else {
144          //let mut miss_pos = Vec::<usize>::new();
145          let mut missing = 0usize;
146          let mut evid = self.event_id_test[0];
147          for _ in 0..self.event_id_test.len() {
148            if !self.event_id_test.contains(&evid) {
149              missing += 1;
150              //miss_pos.push(k);
151            }
152            evid += 1;
153          }
154          self.n_evid_test += 1;
155          self.evid_test_chnks.push_back(missing as u64);
156          if self.evid_test_chnks.len() > 100 {
157            self.evid_test_chnks.pop_front();
158          }
159          self.evid_test_info  = format!("Missing event ID search [{}]", self.n_evid_test);
160          self.evid_test_info += &(format!("\n-- in a chunk of {} event ids", self.evid_test_len)); 
161          self.evid_test_info += &(format!("\n-- we found {} event ids missing ({}%)", missing, 100.0*(missing as f64)/self.event_id_test.len() as f64));
162          self.evid_test_info += &(format!("\n-- -- previous: {:?}", self.evid_test_chnks));
163          self.event_id_test.clear();
164        }
165        // pass on RBEvents 
166        for rbev in &ts.rb_events {
167          match self.rbe_sender.send(rbev.clone()) {
168            Ok(_) => (),
169            Err(err) => error!("Unable to pass on RBEvent! {err}")
170          }
171        }
172        self.summary_queue.push_back(ts.clone());
173        if self.summary_queue.len() > self.queue_size {
174          self.summary_queue.pop_front();
175        }
176
177        match self.ts_sender.send(ts.clone()) {
178          Ok(_)    => (),
179          Err(err) => error!("Unable to pass on TofEvent! {err}")
180        }
181        match self.mte_sender.send(ts) {
182          Ok(_)    => (),
183          Err(err) => error!("Unable to pass on TofEvent! {err}")
184        }
185      }
186    }
187    Ok(())
188  }
189  
190  pub fn render(&mut self, main_window : &Rect, frame : &mut Frame) {
191    let layout = Layout::default()
192      .direction(Direction::Horizontal)
193      .constraints(
194          [Constraint::Percentage(30),
195           Constraint::Percentage(70)].as_ref(),
196      )
197      .split(*main_window);
198   
199    let histo_view = Layout::default()
200      .direction(Direction::Vertical)
201      .constraints(
202          [Constraint::Percentage(33),
203           Constraint::Percentage(33),
204           Constraint::Percentage(34)].as_ref(),
205      )  
206      .split(layout[1]);
207
208    let evid_test_view = Layout::default()
209      .direction(Direction::Vertical)
210      .constraints(
211        [Constraint::Percentage(70),
212         Constraint::Percentage(30)].as_ref(),
213      )
214      .split(histo_view[2]);
215    
216    let evid_test_view_0 = Layout::default()
217      .direction(Direction::Horizontal)
218      .constraints(
219        [Constraint::Percentage(30),
220         Constraint::Percentage(70)].as_ref(),
221      )
222      .split(evid_test_view[0]);
223
224    let last_ts = self.summary_queue.back();
225    let view_string : String;
226    match last_ts {
227      Some(ts) => { 
228        view_string = ts.to_string();
229      }, 
230      None => {
231        view_string = String::from("TofEvent QUEUE EMPTY!");
232      }
233    }
234    let event_view = Paragraph::new(view_string)
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("Last TofEvent")
243          .border_type(BorderType::Rounded),
244      );
245    frame.render_widget(event_view, layout[0]);
246     
247    // histograms
248    let th_labels  = create_labels(&self.n_trg_pdl_histo);
249    let th_data    = prep_data(&self.n_trg_pdl_histo, &th_labels, 5, true); 
250    let th_chart   = histogram(th_data, String::from("N Trig Paddles"), 2, 0, &self.theme);
251    frame.render_widget(th_chart, histo_view[0]); 
252   
253    let mhg_labels = create_labels(&self.miss_hg_hits);
254    let mhg_data   = prep_data(&self.miss_hg_hits, &mhg_labels, 10, true);
255    let mhg_chart  = histogram(mhg_data, String::from("Missing HG hits"), 1, 0, &self.theme);
256    frame.render_widget(mhg_chart, histo_view[1]);
257
258    let evid_test_data = Paragraph::new(self.evid_test_info.clone())
259      .style(Style::default().fg(self.theme.fg0))
260      .alignment(Alignment::Left)
261      //.scroll((5, 10))
262      .block(
263        Block::default()
264          .borders(Borders::ALL)
265          .style(self.theme.style())
266          .title("Missing event ID test")
267          .border_type(BorderType::Rounded),
268      );
269    let mut spl_data  = Vec::<u64>::new();
270    spl_data.extend_from_slice(self.evid_test_chnks.make_contiguous());
271    // that the sparkline does something, it can't be zero. 
272    // There is no axis marker, so we just add 1 to every bin
273    for k in 0..spl_data.len() {
274      spl_data[k] += 1;
275    }
276    let sparkline = Sparkline::default()
277      .style(self.theme.style())
278      //.direction(RenderDirection::LeftToRight)
279      //.data(self.evid_test_chnks.make_contiguous())
280      .data(&spl_data)
281      .block(
282        Block::default()
283        .borders(Borders::ALL)
284        .style(self.theme.style())
285        .title("Missing event IDs in chunks")
286      );
287
288    frame.render_widget(evid_test_data, evid_test_view_0[0]);
289    frame.render_widget(sparkline, evid_test_view_0[1]);
290    let ratio = self.event_id_test.len() as f64 / self.evid_test_len as f64;
291    let test_gauge = gauge(String::from("Missing event ID check"), String::from("Gathering data"), ratio, &self.theme);
292    frame.render_widget(test_gauge, evid_test_view[1]);
293  }
294}