liftof_tui/tabs/
tab_rbs.rs

1//! ReadoutBoard Status tab
2//!
3//! Find connected ReadoutBoards and show their 
4//! details as well as the last waveforms
5//!
6
7use std::time::Instant;
8use std::fs;
9use std::sync::{
10    Arc,
11    Mutex,
12};
13
14use std::collections::{
15  VecDeque,
16  HashMap,
17};
18
19use ndhistogram::{
20  Histogram,
21  Hist1D,
22  ndhistogram,
23};
24
25use ndhistogram::axis::{
26  Uniform,
27};
28
29use ratatui::prelude::Stylize;
30use ratatui::{
31  //backend::CrosstermBackend,
32  //terminal::Frame,
33  Frame,
34  layout::{Alignment, Constraint, Direction, Layout, Rect},
35  style::{
36    Modifier,
37    Style
38  },
39  text::{
40    //Span,
41    Line
42  },
43  widgets::{
44    Block,
45    BorderType,
46    Borders,
47    List,
48    ListItem,
49    ListState,
50    Paragraph,
51    Row,
52    Table,
53    },
54};
55
56use crossbeam_channel::{
57  Receiver
58};
59
60
61use tof_dataclasses::packets::{
62  TofPacket,
63  PacketType
64};
65
66use tof_dataclasses::calibrations::RBCalibrations;
67use tof_dataclasses::errors::SerializationError;
68use tof_dataclasses::events::RBEvent;
69//use tof_dataclasses::serialization::Serialization;
70use tof_dataclasses::monitoring::{
71  RBMoniData,
72  LTBMoniData,
73  PAMoniData,
74  RBMoniDataSeries,
75  LTBMoniDataSeries,
76  PAMoniDataSeries,
77};
78
79use tof_dataclasses::series::MoniSeries;
80use tof_dataclasses::io::RBEventMemoryStreamer;
81use tof_dataclasses::database::ReadoutBoard;
82use tof_dataclasses::alerts::*;
83
84use crate::widgets::{
85  timeseries,
86  //histogram,
87};
88
89use crate::colors::{
90  ColorTheme,
91};
92
93#[derive(Debug, Copy, Clone)]
94pub enum RBLTBListFocus {
95  RBList,
96  LTBList,
97}
98
99#[derive(Debug, Copy, Clone, PartialEq)]
100pub enum RBTabView {
101  Info,
102  Waveform,
103  RBMoniData,
104  PAMoniData,
105  PBMoniData,
106  LTBMoniData,
107  GlobalRates,
108  SelectRB,
109}
110
111#[derive(Debug, Clone)]
112pub struct RBTab<'a>  {
113  pub tp_receiver        : Receiver<TofPacket>,
114  pub rb_receiver        : Receiver<RBEvent>,
115  pub rb_selector        : u8,
116  pub rb_changed         : bool,
117  pub rb_calibration     : RBCalibrations,
118  pub cali_loaded        : bool,
119  pub event_queue        : VecDeque<RBEvent>,
120  pub moni_queue         : RBMoniDataSeries,
121  pub global_rates       : HashMap<u8, String>,
122  pub ltb_moni_queue     : LTBMoniDataSeries,  
123  pub pa_show_biases     : bool,
124  pub pa_moni_queue      : PAMoniDataSeries,
125  pub met_queue          : VecDeque<f64>,
126  pub met_queue_moni     : HashMap<u8,VecDeque<f64>>,
127  pub met_queue_ltb_moni : HashMap<u8,VecDeque<f64>>,
128  pub met_queue_pa_moni  : HashMap<u8,VecDeque<f64>>,
129  /// Holds waveform data
130  pub ch_data            : Vec<Vec<(f64,f64)>>,
131  /// Holds the monitoring qunatities
132  pub fpgatmp_queue      : VecDeque<(f64,f64)>,
133  pub fpgatmp_fr_moni    : bool,
134
135  pub queue_size         : usize,
136  
137  pub n_events           : usize,
138  pub n_moni             : usize,
139  pub miss_evid          : usize,
140  pub last_evid          : u32,
141  pub nch_histo          : Hist1D<Uniform<f32>>,
142  timer                  : Instant,
143
144  pub theme              : ColorTheme,
145  pub view               : RBTabView,
146
147  pub rbs                : HashMap<u8, ReadoutBoard>,
148
149  pub list_focus         : RBLTBListFocus,
150  // list for the rb selector
151  pub rbl_state          : ListState,
152  pub rbl_items          : Vec::<ListItem<'a>>,
153  pub rbl_active         : bool,
154  // FIXME - get this from the DB!
155  pub rb_to_rat          : HashMap<u8,u8>,
156  // tie into the alert system
157  pub alerts             : Arc<Mutex<HashMap<&'a str,TofAlert<'a>>>>,
158  alerts_active          : bool,
159  moni_old_check         : HashMap<u8,Instant>,
160}
161
162impl RBTab<'_>  {
163
164  pub fn new<'a>(tp_receiver  : Receiver<TofPacket>,
165                 rb_receiver  : Receiver<RBEvent>,
166                 rbs          : HashMap<u8, ReadoutBoard>,
167                 alerts       : Arc<Mutex<HashMap<&'a str,TofAlert<'a>>>>,
168                 theme        : ColorTheme) -> RBTab<'a>  {
169    // check if the alerts are active
170    let mut alerts_active = false;
171    match alerts.lock() {
172      Ok(al) => {
173        if al.len() > 0 {
174          alerts_active = true;
175          info!("Found {} active alerts!", al.len());
176        }
177      }
178      Err(err) => {
179        error!("Unable to lock alert mutex! {err}");
180      }
181    }
182    let rb_non_exist = vec![10,12,34,37,38,43,45,47,48,49];
183    let mut rb_select_items = Vec::<ListItem>::new();
184    let mut global_rates    = HashMap::<u8,String>::new();
185    for k in 1..51 {
186      if rb_non_exist.contains(&k) {
187        continue;
188      }
189      global_rates.insert(k as u8, String::from("\u{203c} no data yet"));
190    }
191    let mut moni_old_check = HashMap::<u8, Instant>::new();
192    for k in 1..51 {
193      let this_item = format!("  RB{:0>2}", k);
194      rb_select_items.push(ListItem::new(Line::from(this_item)));
195      moni_old_check.insert(k, Instant::now());
196    }
197
198    let queue_size = 1000usize;
199    let mut ch_data    = Vec::<Vec::<(f64,f64)>>::with_capacity(1024);
200    for _channel in 0..9 {
201      let tmp_vec = vec![(0.0f64,0.0f64);1024];
202      //ch_data.push(Vec::<(f64,f64)>::new());
203      ch_data.push(tmp_vec);
204    }
205    let bins = Uniform::new(50,-0.5,49.5).unwrap();
206    let mut rbl_state    = ListState::default();
207    rbl_state.select(Some(1));
208    warn!("Using hardcoded values for the RB->RAT hashmap!");
209    // FIXME - get this from DB!
210    let rb_to_rat
211      = HashMap::<u8,u8>::from(
212      [(1, 10),
213       (2, 15),
214       (3,  1),
215       (4, 15),
216       (5, 20),
217       (6, 19),
218       (7, 17),
219       (8,  9),
220       (11,10),
221       (13, 4),
222       (14, 2),
223       (15, 1),
224       (16, 8),
225       (17,17),
226       (18,13),
227       (19, 7),
228       (20, 7),
229       (21, 5),
230       (22,11),
231       (23, 5),
232       (24, 6),
233       (25, 8),
234       (26,11),
235       (27, 6),
236       (28,20),
237       (29, 3),
238       (30, 9),
239       (31, 3),
240       (32, 2),
241       (33,18),
242       (34,18),
243       (35, 4),
244       (36,19),
245       (39,12),
246       (40,12),
247       (41,14),
248       (42,14),
249       (44,16),
250       (46,16)]);
251
252RBTab {
253      tp_receiver        : tp_receiver,
254      rb_receiver        : rb_receiver,
255      rb_selector        : 0,
256      rb_changed         : false,
257      rb_calibration     : RBCalibrations::new(0),
258      cali_loaded        : false,
259      event_queue        : VecDeque::<RBEvent>::with_capacity(queue_size),
260      //moni_queue         : VecDeque::<RBMoniData>::with_capacity(queue_size),
261      moni_queue         : RBMoniDataSeries::new(),
262      global_rates       : global_rates,
263      met_queue          : VecDeque::<f64>::with_capacity(queue_size),
264      met_queue_moni     : HashMap::<u8, VecDeque<f64>>::new(),
265      ltb_moni_queue     : LTBMoniDataSeries::new(),
266      met_queue_ltb_moni : HashMap::<u8, VecDeque<f64>>::new(),
267      pa_show_biases     : false,
268      pa_moni_queue      : PAMoniDataSeries::new(),
269      met_queue_pa_moni  : HashMap::<u8, VecDeque<f64>>::new(),
270      fpgatmp_queue      : VecDeque::<(f64,f64)>::with_capacity(queue_size),
271      fpgatmp_fr_moni    : true,
272
273      ch_data            : ch_data,
274
275      queue_size         : queue_size,
276      
277      n_events           : 0,
278      n_moni             : 0,
279      miss_evid          : 0,
280      last_evid          : 0,
281      nch_histo          : ndhistogram!(bins),
282      timer              : Instant::now(),
283  
284      theme              : theme,
285      view               : RBTabView::Waveform,
286   
287      rbs                : rbs,
288
289      list_focus         : RBLTBListFocus::RBList,
290
291      rbl_state          : rbl_state,
292      rbl_items          : rb_select_items,
293      rbl_active         : false,
294      
295      rb_to_rat          : rb_to_rat,
296      alerts             : alerts,
297      alerts_active      : alerts_active,
298      moni_old_check     : moni_old_check,
299    }
300  }
301  
302  pub fn receive_packet(&mut self) -> Result<(), SerializationError> {
303    let met    = self.timer.elapsed().as_secs_f64();
304    let mut ev = RBEvent::new();
305    let bins   = Uniform::new(50,-0.5,49.5).unwrap();
306    //info!("Receive packet!"); 
307    if self.rb_changed {
308      debug!("RB change detectod!");
309      // currently, only one RB at a time is supported
310      // FIXME
311      self.event_queue.clear();
312      self.met_queue.clear();
313      //self.met_queue_moni.clear();
314      self.fpgatmp_queue.clear();
315      self.nch_histo = ndhistogram!(bins);
316      // try to get a new calibration
317      match self.rbl_state.selected() {
318        None => {
319          self.cali_loaded = false;
320        }, 
321        Some(_rb_id) => {
322          let cali_path = format!("calibrations/RB{:02}.cali.tof.gaps", _rb_id + 1);
323          if fs::metadata(cali_path.clone()).is_ok() {
324            match RBCalibrations::from_file(cali_path.clone(), true) {
325              Err(err) => error!("Unable to load RBCalibration from file {}! {err}", cali_path),
326              Ok(cali) => {
327                self.rb_calibration = cali;
328                self.cali_loaded    = true;
329              }
330            } 
331          } else {
332            self.cali_loaded = false;
333          }
334        }
335      }
336      self.rb_changed = false;
337      info!("RB changed!");
338    }
339   
340    // check the age of the RBMoni data 
341    for k in self.moni_old_check.keys() {
342      let moni_age = self.moni_old_check.get(&k).unwrap().elapsed().as_secs();
343      // check if any of the alerts trigger, then notify global alerts
344      let alert_key_old  = format!("rb{:02}_hk_too_old", k);
345      match self.alerts.lock() {
346        Ok(mut al) => {
347          // we can work with mtb relevant alerts here
348          match al.get_mut(alert_key_old.as_str() ) {
349            None => (),
350            Some(rb_moni_old_alert) => {
351              rb_moni_old_alert.trigger(moni_age as f32);
352            }
353          }
354        },
355        Err(err)   =>  error!("Unable to lock global alerts! {err}"),
356      }
357    }
358
359    if !self.rb_receiver.is_empty() {
360      match self.rb_receiver.try_recv() {
361        Err(_) => (),
362        Ok(_ev)   => {
363          ev = _ev;
364        }
365      }
366    }
367    if !self.tp_receiver.is_empty() {
368      match self.tp_receiver.try_recv() {
369        Err(_err) => (),
370        Ok(pack)    => {
371          debug!("Got next packet {}!", pack);
372          match pack.packet_type {
373            PacketType::PAMoniData => {
374              info!("Received new PAMoniData!");
375              let moni : PAMoniData = pack.unpack()?;
376              self.pa_moni_queue.add(moni);
377              if !self.met_queue_pa_moni.contains_key(&moni.board_id) {
378                self.met_queue_pa_moni.insert(moni.board_id, VecDeque::<f64>::with_capacity(1000));
379              } else {
380                self.met_queue_pa_moni.get_mut(&moni.board_id).unwrap().push_back(met);
381                if self.met_queue_pa_moni.get(&moni.board_id).unwrap().len() > self.queue_size {
382                  self.met_queue_pa_moni.get_mut(&moni.board_id).unwrap().pop_front();
383                }
384              }
385              return Ok(());
386            }
387            PacketType::LTBMoniData => {
388              trace!("Received new LTBMoniData!");
389              let moni : LTBMoniData = pack.unpack()?;
390              self.ltb_moni_queue.add(moni);
391              if !self.met_queue_ltb_moni.contains_key(&moni.board_id) {
392                self.met_queue_ltb_moni.insert(moni.board_id, VecDeque::<f64>::with_capacity(1000));
393              } else {
394                self.met_queue_ltb_moni.get_mut(&moni.board_id).unwrap().push_back(met);
395                if self.met_queue_ltb_moni.get(&moni.board_id).unwrap().len() > self.queue_size {
396                  self.met_queue_ltb_moni.get_mut(&moni.board_id).unwrap().pop_front();
397                }
398              }
399              return Ok(());
400            },
401            PacketType::RBMoniData   => {
402              trace!("Received new RBMoniData!");
403              let moni : RBMoniData = pack.unpack()?;
404              // check in with alert system
405              *self.moni_old_check.get_mut(&moni.board_id).unwrap() = Instant::now();
406              if self.alerts_active {
407                // FIXME - unify and get rid of all this goddam hardcoding in several 
408                //         places!
409                //let rb_list = vec![1,2,3,4,5,6,7,8,9,11,13,14,15,16,17,18,19,
410                //                   20,21,22,23,24,25,26,27,28,29,30,31,32,33,
411                //                   34,35,36,39,40,41,44,46];
412                let alert_key_rate = format!("rb{:02}_rate_zero", moni.board_id);
413                let alert_key_temp = format!("rb{:02}_temp", moni.board_id);
414                //let alert_key_old  = format!("rb{:02}_hk_too_old", moni.board_id);
415                //let moni_age = self.moni_old_check.get(&moni.board_id).unwrap().elapsed().as_secs();
416                match self.alerts.lock() {
417                  Ok(mut al) => {
418                    // we can work with mtb relevant alerts here
419                    al.get_mut(alert_key_rate.as_str()).unwrap().trigger(moni.rate as f32);
420                    al.get_mut(alert_key_temp.as_str()).unwrap().trigger(moni.tmp_drs);
421                    //al.get_mut(alert_key_old.as_str()).unwrap() .trigger(moni_age as f32);
422                  },
423                  Err(err)   =>  error!("Unable to lock global alerts! {err}"),
424                }
425              }
426
427              self.moni_queue.add(moni);
428              self.n_moni += 1;
429              // capture the rate for the global rate window
430              self.global_rates.insert(moni.board_id, format!("{}",moni.rate));
431                
432              if !self.met_queue_moni.contains_key(&moni.board_id) {
433                // FIXME - make the 1000 (which is queue size) a member
434                self.met_queue_moni.insert(moni.board_id, VecDeque::<f64>::with_capacity(1000));
435              } else {
436                self.met_queue_moni.get_mut(&moni.board_id).unwrap().push_back(met);
437                if self.met_queue_moni.get(&moni.board_id).unwrap().len() > self.queue_size {
438                  self.met_queue_moni.get_mut(&moni.board_id).unwrap().pop_front();
439                }
440              }
441              return Ok(());
442            },
443            PacketType::RBEvent => {
444              ev = pack.unpack()?;
445            },
446            PacketType::RBEventMemoryView => {
447              let mut streamer = RBEventMemoryStreamer::new();
448              //println!("{:?}",&pack.payload[0..10]);
449              streamer.add(&pack.payload, pack.payload.len());
450              match streamer.get_event_at_pos_unchecked(None) {
451                None => {
452                  error!("Not able to obtain RBEvent from RBEventMemoryView packet!");
453                  return Ok(());
454                }
455                Some(_ev) => {
456                  ev = _ev;
457                }
458              }
459            },
460            _ => (),
461          }
462        }
463      }
464    }
465   
466    if ev.header.event_id != 0 && self.rb_selector == ev.header.rb_id {
467      for ch in ev.header.get_channels() {
468        if self.cali_loaded {
469          let mut nanos = vec![0f32;1024];
470          let mut volts = vec![0f32;1024];
471          self.rb_calibration.nanoseconds(ch as usize + 1, ev.header.stop_cell as usize, 
472                                          &mut nanos);
473          self.rb_calibration.voltages(ch as usize + 1, ev.header.stop_cell as usize, 
474                                       &ev.adc[ch as usize], &mut volts);
475          //let 
476          for k in 0..nanos.len() {
477            let vals = (nanos[k] as f64, volts[k] as f64);
478            self.ch_data[ch as usize][k] = vals;
479          }
480        } else {
481          for k in 0..ev.adc[ch as usize].len() {
482            let vals = (k as f64, ev.adc[ch as usize][k] as f64);
483            self.ch_data[ch as usize][k] = vals;
484          }
485          //println!("{:?}", self.ch_data[ch as usize]);
486        }
487      }
488
489      self.nch_histo.fill(&(ev.header.get_nchan() as f32));
490      self.n_events += 1;
491      if self.last_evid != 0 {
492        if ev.header.event_id - self.last_evid != 1 {
493          self.miss_evid += (ev.header.event_id - self.last_evid) as usize;
494        }
495      }
496      self.last_evid = ev.header.event_id;
497      self.fpgatmp_queue.push_back((met, ev.header.get_fpga_temp() as f64));
498      self.fpgatmp_fr_moni = false; // choose this as source
499      if self.fpgatmp_queue.len() > self.queue_size {
500        self.fpgatmp_queue.pop_front();
501      }
502      self.event_queue.push_back(ev);
503      if self.event_queue.len() > self.queue_size {
504        self.event_queue.pop_front();
505      }
506      self.met_queue.push_back(met);
507      if self.met_queue.len() > self.queue_size {
508        self.met_queue.pop_front();
509      }
510    }
511    Ok(())
512  }
513  
514  pub fn next_rb(&mut self) {
515    let i = match self.rbl_state.selected() {
516      Some(i) => {
517        if i >= self.rbl_items.len() - 1 {
518          self.rbl_items.len() - 1
519        } else {
520          i + 1
521        }
522      }
523      None => 0,
524    };
525    self.rbl_state.select(Some(i));
526    //info!("Selecting {}", i);
527  }
528
529  pub fn previous_rb(&mut self) {
530    let i = match self.rbl_state.selected() {
531      Some(i) => {
532        if i == 0 {
533          0 
534        } else {
535          i - 1
536        }
537      }
538      None => 0,
539    };
540    self.rbl_state.select(Some(i));
541  }
542
543  pub fn unselect_rbl(&mut self) {
544    self.rbl_state.select(None);
545  }
546  
547
548  pub fn render(&mut self, main_window : &Rect, frame : &mut Frame) {
549    match self.view {
550      RBTabView::SelectRB => {
551        let main_lo = Layout::default()
552          .direction(Direction::Horizontal)
553          .constraints(
554              [Constraint::Percentage(10),
555               //Constraint::Percentage(20),
556               Constraint::Percentage(90)].as_ref(),
557          )
558          .split(*main_window);
559        let rbs = Block::default()
560          .borders(Borders::ALL)
561          .style(self.theme.style())
562          .title("Select ReadoutBoard (RB)")
563          .border_type(BorderType::Plain);
564        let rb_select_list = List::new(self.rbl_items.clone()).block(rbs)
565          .highlight_style(self.theme.highlight().add_modifier(Modifier::BOLD))
566          .highlight_symbol(">>")
567          .repeat_highlight_symbol(true);
568        match self.list_focus {
569          RBLTBListFocus::RBList => {
570            match self.rbl_state.selected() {
571              None    => {
572                let selector =  1;
573                if self.rb_selector != selector {
574                  self.rb_changed = true;
575                  self.rb_selector = selector;
576                } else {
577                  self.rb_changed = false;
578                }
579              },
580              Some(_rbid) => {
581                let selector =  _rbid as u8 + 1;
582                if self.rb_selector != selector {
583                  self.rb_changed = true;
584                  self.rb_selector = selector;
585                } else {
586                  self.rb_changed = false;
587                }
588              },
589            }
590          },
591          _ => ()
592        }
593        let view_string : String;
594        match self.rbs.get(&self.rb_selector) {
595          Some(_rb) => {
596            view_string = format!("{}", _rb.to_summary_str());
597          }
598          None => {
599            view_string = format!("No information for RB {} in DB \n or DB not available!", self.rb_selector);
600          }
601        }
602        let rb_view = Paragraph::new(view_string)
603          .style(self.theme.style())
604          .alignment(Alignment::Left)
605          //.scroll((5, 10))
606          .block(
607          Block::default()
608            .borders(Borders::ALL)
609            .style(self.theme.style())
610            .title("RB")
611            .border_type(BorderType::Rounded),
612        );
613
614        frame.render_stateful_widget(rb_select_list,  main_lo[0], &mut self.rbl_state );
615        frame.render_widget(rb_view, main_lo[1]);
616      },
617      RBTabView::Waveform => {
618        // set up general layout
619        let status_chunks = Layout::default()
620          .direction(Direction::Horizontal)
621          .constraints(
622              [Constraint::Percentage(30), Constraint::Percentage(70)].as_ref(),
623          )
624          .split(*main_window);
625
626        let detail_and_ch9_chunks = Layout::default()
627          .direction(Direction::Vertical)
628          .constraints(
629              [Constraint::Percentage(25),
630               Constraint::Percentage(75)].as_ref(),
631          )
632          .split(status_chunks[0]);
633
634        let wf_chunks = Layout::default()
635          .direction(Direction::Horizontal)
636          .constraints(
637              [Constraint::Percentage(50),
638               Constraint::Percentage(50)].as_ref(),
639          )
640          .split(status_chunks[1]);
641
642        let mut ch_chunks = Layout::default()
643          .direction(Direction::Vertical)
644          .constraints(
645              [Constraint::Percentage(25),
646               Constraint::Percentage(25),
647               Constraint::Percentage(26),
648               Constraint::Percentage(25)].as_ref(),
649          )
650          .split(wf_chunks[0]).to_vec();
651
652        let mut ch_chunks_2 = Layout::default()
653          .direction(Direction::Vertical)
654          .constraints(
655              [Constraint::Percentage(25),
656               Constraint::Percentage(25),
657               Constraint::Percentage(26),
658               Constraint::Percentage(25)].as_ref(),
659          )
660          .split(wf_chunks[1]).to_vec();
661
662        ch_chunks.append(&mut ch_chunks_2);
663        // the waveform plots
664        for ch in 0..9 {
665          let label          = format!("Ch{}", ch + 1);
666          let ch_tc_theme    = self.theme.clone();
667          let mut ch_ts_data = VecDeque::from(self.ch_data[ch].clone());
668          let ch_ts = timeseries(&mut ch_ts_data,
669                                 label.clone(),
670                                 label.clone(),
671                                 &ch_tc_theme  );
672          // render it!
673          if ch == 8 {
674            //frame.render_widget(chart, detail_and_ch9_chunks[0]);
675            frame.render_widget(ch_ts,detail_and_ch9_chunks[0]);
676          } else {
677            frame.render_widget(ch_ts,ch_chunks[ch]);
678            //frame.render_widget(chart, ch_chunks[ch]);
679          }
680        //charts.push(chart);
681        } // end loop over channels
682
683        let last_event = self.event_queue.back();
684        let view_string : String;
685        match last_event {
686          Some(event) => { 
687            view_string = event.to_string();
688          }, 
689          None => {
690            view_string = String::from("EVT QUEUE EMPTY!");
691          }
692        }
693        let event_view = Paragraph::new(view_string)
694          .style(self.theme.style())
695          .alignment(Alignment::Left)
696          //.scroll((5, 10))
697          .block(
698          Block::default()
699            .borders(Borders::ALL)
700            .style(self.theme.style())
701            .title("Last RBEvent")
702            .border_type(BorderType::Rounded),
703        );
704        frame.render_widget(event_view, detail_and_ch9_chunks[1]);
705      } // end Waveform
706      RBTabView::RBMoniData => {
707        // Have 4 columns a 4 plots each. We use col(0,0) for the RBMoniData string,
708        // so this leaves us with 13 plots
709        let columns = Layout::default()
710          .direction(Direction::Horizontal)
711          .constraints(
712              [Constraint::Percentage(25),
713               Constraint::Percentage(25),
714               Constraint::Percentage(25),
715               Constraint::Percentage(25),
716              ].as_ref(),
717          )
718          .split(*main_window);
719        let col0 = Layout::default()
720          .direction(Direction::Vertical)
721          .constraints(
722              [Constraint::Percentage(75),
723               Constraint::Percentage(25)].as_ref()
724          )
725          .split(columns[0]);
726        let col1 = Layout::default()
727          .direction(Direction::Vertical)
728          .constraints(
729              [Constraint::Percentage(25),
730               Constraint::Percentage(25),
731               Constraint::Percentage(25),
732               Constraint::Percentage(25),
733              ].as_ref(),
734          )
735          .split(columns[1]);
736        let col2 = Layout::default()
737          .direction(Direction::Vertical)
738          .constraints(
739              [Constraint::Percentage(25),
740               Constraint::Percentage(25),
741               Constraint::Percentage(25),
742               Constraint::Percentage(25),
743              ].as_ref(),
744          )
745          .split(columns[2]);
746        let col3 = Layout::default()
747          .direction(Direction::Vertical)
748          .constraints(
749              [Constraint::Percentage(25),
750               Constraint::Percentage(25),
751               Constraint::Percentage(25),
752               Constraint::Percentage(25),
753              ].as_ref(),
754          )
755          .split(columns[3]);
756
757        let last_moni = self.moni_queue.get_last_moni(self.rb_selector);
758        let view_string : String;
759        match last_moni {
760          Some(_moni) => { 
761            view_string = _moni.to_string();
762          }, 
763          None => {
764            view_string = format!("No RBMoniData for board {} avaiable", self.rb_selector);
765          }
766        }
767        
768        let moni_view = Paragraph::new(view_string)
769          .style(self.theme.style())
770          .alignment(Alignment::Left)
771          //.scroll((5, 10))
772          .block(
773          Block::default()
774            .borders(Borders::ALL)
775            .style(self.theme.style())
776            .title("Last RBMoniData")
777            .border_type(BorderType::Rounded),
778        );
779        frame.render_widget(moni_view, col0[0]);
780       
781        let rate_ds_name   = String::from("Rate");
782        let rate_ds_title  = String::from("RB Rate [Hz]");
783        let rate_data      = self.moni_queue.get_var_for_board("rate", &self.rb_selector);
784        let mut rate_ts    = VecDeque::<(f64, f64)>::new(); 
785        match rate_data {
786          None => {
787            error!("No rate data available for board {}", self.rb_selector);
788          },
789          Some(rdata) => {
790            if rdata.len() != 0 {
791              for (k, time) in self.met_queue_moni.get(&self.rb_selector).unwrap().iter().enumerate() {
792                rate_ts.push_back((*time, rdata[k] as f64));
793              }
794            }
795          }
796        }
797        let rate_tc = timeseries(&mut rate_ts,
798                                 rate_ds_name,
799                                 rate_ds_title,
800                                 &self.theme);
801        frame.render_widget(rate_tc, col0[1]);
802
803        // ambience
804        let mag_tot_ds_name   = String::from("Magnetic Field");
805        let mag_tot_ds_title  = String::from("Tot mag field [Gauss]");
806        let mag_tot_data      = self.moni_queue.get_var_for_board("mag_tot", &self.rb_selector);
807        let mut mag_tot_ts    = VecDeque::<(f64, f64)>::new(); 
808        match mag_tot_data  {
809          None => {
810            error!("No mag_tot data available for board {}", self.rb_selector);
811          },
812          Some(data) => {
813            if data.len() != 0 {
814              for (k, time) in self.met_queue_moni.get(&self.rb_selector).unwrap().iter().enumerate() {
815                mag_tot_ts.push_back((*time, data[k] as f64));
816              }
817            }
818          }
819        }
820        let mag_tot_tc = timeseries(&mut mag_tot_ts,
821                                 mag_tot_ds_name,
822                                 mag_tot_ds_title,
823                                 &self.theme);
824        frame.render_widget(mag_tot_tc, col1[0]);
825
826        let pres_ds_name   = String::from("Atmospheric pressure");
827        let pres_ds_title  = String::from("Atmospheric pressure [hPa]");
828        let pres_data      = self.moni_queue.get_var_for_board("pressure", &self.rb_selector);
829        let mut pres_ts    = VecDeque::<(f64, f64)>::new(); 
830        match pres_data {
831          None => {
832            error!("No atmos pressure data available for board {}", self.rb_selector);
833          },
834          Some(data) => {
835            if data.len() != 0 {
836              for (k, time) in self.met_queue_moni.get(&self.rb_selector).unwrap().iter().enumerate() {
837                pres_ts.push_back((*time, data[k] as f64));
838              }
839            }
840          }
841        }
842        
843        let pres_tc = timeseries(&mut pres_ts,
844                                 pres_ds_name,
845                                 pres_ds_title,
846                                 &self.theme);
847        frame.render_widget(pres_tc, col1[1]);
848        
849        let humi_ds_name   = String::from("Ambient humidity");
850        let humi_ds_title  = String::from("Humidity [%]");
851        let humi_data      = self.moni_queue.get_var_for_board("humidity", &self.rb_selector);
852        let mut humi_ts    = VecDeque::<(f64, f64)>::new(); 
853        match humi_data {
854          None => {
855            error!("No humidity data available for board {}", self.rb_selector);
856          },
857          Some(data) => {
858            if data.len() != 0 {
859              for (k, time) in self.met_queue_moni.get(&self.rb_selector).unwrap().iter().enumerate() {
860                humi_ts.push_back((*time, data[k] as f64));
861              }
862            }
863          }
864        }
865        let humi_tc = timeseries(&mut humi_ts,
866                                 humi_ds_name,
867                                 humi_ds_title,
868                                 &self.theme);
869        frame.render_widget(humi_tc, col1[2]);
870
871        // Temperatures (one is missing because of display constraints)
872        let fpga_ds_name   = String::from("FPGA (DRS) Temperature");
873        let fpga_ds_title  = String::from("DRS Temp [\u{00B0}C]");
874        // in case we are receiving RBEvents, these have the FPGA temperature as well
875        // since this is more fine-grained (once every event) we will use that instead
876        if !self.fpgatmp_fr_moni {
877          let fpga_tc = timeseries(&mut self.fpgatmp_queue,
878                                   fpga_ds_name,
879                                   fpga_ds_title,
880                                   &self.theme  );
881          frame.render_widget(fpga_tc, col1[3]);
882        } else {
883          // we will get it from the RBMoniData
884          let fpga_data      = self.moni_queue.get_var_for_board("tmp_drs", &self.rb_selector);
885          let mut fpga_ts    = VecDeque::<(f64, f64)>::new(); 
886          match fpga_data {
887            None => {
888              error!("No DRS4 temperature data available for board {}", self.rb_selector);
889            },
890            Some(data) => {
891              if data.len() != 0 {
892                for (k, time) in self.met_queue_moni.get(&self.rb_selector).unwrap().iter().enumerate() {
893                  fpga_ts.push_back((*time, data[k] as f64));
894                }
895              }
896            }
897          }
898          let fpga_tc = timeseries(&mut fpga_ts,
899                                   fpga_ds_name,
900                                   fpga_ds_title,
901                                   &self.theme);
902          frame.render_widget(fpga_tc, col1[3]);
903        }
904
905        let tmp_clk_ds_name   = String::from("CLK Temperature");
906        let tmp_clk_ds_title  = String::from("CLK Temp. [\u{00B0}C]");
907        let tmp_clk_data      = self.moni_queue.get_var_for_board("tmp_clk", &self.rb_selector);
908        let mut tmp_clk_ts    = VecDeque::<(f64, f64)>::new(); 
909        match tmp_clk_data {
910          None => {
911            error!("No CLK temperature data available for board {}", self.rb_selector);
912          },
913          Some(data) => {
914            if data.len() != 0 {
915              for (k, time) in self.met_queue_moni.get(&self.rb_selector).unwrap().iter().enumerate() {
916                tmp_clk_ts.push_back((*time, data[k] as f64));
917              }
918            }
919          }
920        }
921        let tmp_clk_tc = timeseries(&mut tmp_clk_ts,
922                                    tmp_clk_ds_name,
923                                    tmp_clk_ds_title,
924                                    &self.theme);
925        frame.render_widget(tmp_clk_tc, col2[0]);
926        
927        let tmp_adc_ds_name   = String::from("ADC Temperature");
928        let tmp_adc_ds_title  = String::from("ADC Temp. [\u{00B0}C]");
929        let tmp_adc_data      = self.moni_queue.get_var_for_board("tmp_adc", &self.rb_selector);
930        let mut tmp_adc_ts    = VecDeque::<(f64, f64)>::new(); 
931        match tmp_adc_data {
932          None => {
933            error!("No ADC temperature data available for board {}", self.rb_selector);
934          },
935          Some(data) => {
936            if data.len() != 0 {
937              for (k, time) in self.met_queue_moni.get(&self.rb_selector).unwrap().iter().enumerate() {
938                tmp_adc_ts.push_back((*time, data[k] as f64));
939              }
940            }
941          }
942        }
943        let tmp_adc_tc = timeseries(&mut tmp_adc_ts,
944                                    tmp_adc_ds_name,
945                                    tmp_adc_ds_title,
946                                    &self.theme);
947        frame.render_widget(tmp_adc_tc, col2[1]);
948        
949        let tmp_zynq_ds_name   = String::from("ZYNQ Temperature");
950        let tmp_zynq_ds_title  = String::from("ZYNQ Temp. [\u{00B0}C]");
951        let tmp_zynq_data      = self.moni_queue.get_var_for_board("tmp_zynq", &self.rb_selector);
952        let mut tmp_zynq_ts    = VecDeque::<(f64, f64)>::new(); 
953        match tmp_zynq_data {
954          None => {
955            error!("No ZYNQ temperature data available for board {}", self.rb_selector);
956          },
957          Some(data) => {
958            if data.len() != 0 {
959              for (k, time) in self.met_queue_moni.get(&self.rb_selector).unwrap().iter().enumerate() {
960                tmp_zynq_ts.push_back((*time, data[k] as f64));
961              }
962            }
963          }
964        }
965        let tmp_zynq_tc = timeseries(&mut tmp_zynq_ts,
966                                     tmp_zynq_ds_name,
967                                     tmp_zynq_ds_title,
968                                     &self.theme);
969        frame.render_widget(tmp_zynq_tc, col2[2]);
970        
971        let tmp_bm280_name   = String::from("BM280 Temperature");
972        let tmp_bm280_title  = String::from("BM280 Temp. [\u{00B0}C]");
973        let tmp_bm280_data      = self.moni_queue.get_var_for_board("tmp_bm280", &self.rb_selector);
974        let mut tmp_bm280_ts    = VecDeque::<(f64, f64)>::new(); 
975        match tmp_bm280_data {
976          None => {
977            error!("No BM280 temperature data available for board {}", self.rb_selector);
978          },
979          Some(data) => {
980            if data.len() != 0 {
981              for (k, time) in self.met_queue_moni.get(&self.rb_selector).unwrap().iter().enumerate() {
982                tmp_bm280_ts.push_back((*time, data[k] as f64));
983              }
984            }
985          }
986        }
987        let tmp_bm280_tc = timeseries(&mut tmp_bm280_ts,
988                                      tmp_bm280_name,
989                                      tmp_bm280_title,
990                                      &self.theme);
991        frame.render_widget(tmp_bm280_tc, col2[3]);
992       
993        // Currents 
994        let drs_c_name   = String::from("DRS Current");
995        let drs_c_title  = String::from("DRS Curr. [mA]");
996        let drs_c_data   = self.moni_queue.get_var_for_board("drs_dvdd_current", &self.rb_selector);
997        let mut drs_c_ts = VecDeque::<(f64, f64)>::new(); 
998        match drs_c_data {
999          None => {
1000            error!("No DRS4 current data available for board {}", self.rb_selector);
1001          },
1002          Some(data) => {
1003            if data.len() != 0 {
1004              for (k, time) in self.met_queue_moni.get(&self.rb_selector).unwrap().iter().enumerate() {
1005                drs_c_ts.push_back((*time, data[k] as f64));
1006              }
1007            }
1008          }
1009        }
1010        let drs_c_tc = timeseries(&mut drs_c_ts,
1011                                  drs_c_name,
1012                                  drs_c_title,
1013                                  &self.theme);
1014        frame.render_widget(drs_c_tc, col3[0]);
1015
1016        let zynq_c_name   = String::from("Zynq Current");
1017        let zynq_c_title  = String::from("Zynq Curr. [mA]");
1018        let zynq_c_data   = self.moni_queue.get_var_for_board("zynq_current", &self.rb_selector);
1019        let mut zynq_c_ts = VecDeque::<(f64, f64)>::new(); 
1020        match zynq_c_data {
1021          None => {
1022            error!("No ZYNQ current data available for board {}", self.rb_selector);
1023          },
1024          Some(data) => {
1025            if data.len() != 0 {
1026              for (k, time) in self.met_queue_moni.get(&self.rb_selector).unwrap().iter().enumerate() {
1027                zynq_c_ts.push_back((*time, data[k] as f64));
1028              }
1029            }
1030          }
1031        }
1032        let zynq_c_tc = timeseries(&mut zynq_c_ts,
1033                                   zynq_c_name,
1034                                   zynq_c_title,
1035                                   &self.theme);
1036        frame.render_widget(zynq_c_tc, col3[1]);
1037        
1038        let p3v3_c_name   = String::from("P3V3 Current");
1039        let p3v3_c_title  = String::from("P3V3 Curr. [mA]");
1040        let p3v3_c_data   = self.moni_queue.get_var_for_board("p3v3_current", &self.rb_selector);
1041        let mut p3v3_c_ts = VecDeque::<(f64, f64)>::new(); 
1042        match p3v3_c_data {
1043          None => {
1044            error!("No P3V3 current data available for board {}", self.rb_selector);
1045          },
1046          Some(data) => {
1047            if data.len() != 0 {
1048              for (k, time) in self.met_queue_moni.get(&self.rb_selector).unwrap().iter().enumerate() {
1049                p3v3_c_ts.push_back((*time, data[k] as f64));
1050              }
1051            }
1052          }
1053        }
1054        let p3v3_c_tc = timeseries(&mut p3v3_c_ts,
1055                                   p3v3_c_name,
1056                                   p3v3_c_title,
1057                                   &self.theme);
1058        frame.render_widget(p3v3_c_tc, col3[2]);
1059        
1060        let p3v5_c_name   = String::from("P3V5 Current");
1061        let p3v5_c_title  = String::from("P3V5 Curr. [mA]");
1062        let p3v5_c_data   = self.moni_queue.get_var_for_board("p3v5_current", &self.rb_selector);
1063        let mut p3v5_c_ts = VecDeque::<(f64, f64)>::new(); 
1064        match p3v5_c_data {
1065          None => {
1066            error!("No P3V5 current data available for board {}", self.rb_selector);
1067          },
1068          Some(data) => {
1069            if data.len() != 0 {
1070              for (k, time) in self.met_queue_moni.get(&self.rb_selector).unwrap().iter().enumerate() {
1071                p3v5_c_ts.push_back((*time, data[k] as f64));
1072              }
1073            }
1074          }
1075        }
1076        let p3v5_c_tc = timeseries(&mut p3v5_c_ts,
1077                                   p3v5_c_name,
1078                                   p3v5_c_title,
1079                                   &self.theme);
1080        frame.render_widget(p3v5_c_tc, col3[3]);
1081      },
1082      RBTabView::LTBMoniData => {
1083        let columns = Layout::default()
1084            .direction(Direction::Horizontal)
1085            .constraints(
1086                [Constraint::Percentage(30),
1087                 Constraint::Percentage(70)].as_ref(),
1088            )
1089            .split(*main_window);
1090        let rows = Layout::default()
1091            .direction(Direction::Vertical)
1092            .constraints(
1093                [Constraint::Percentage(30),
1094                 Constraint::Percentage(30),
1095                 Constraint::Percentage(40)].as_ref(),
1096            )
1097            .split(columns[1]);
1098        
1099        let last_ltb_moni    = self.ltb_moni_queue.get_last_moni(self.rb_selector);
1100        let mut ltb_moni_str = format!("No data for board {}!", self.rb_selector);
1101        let mut ltb_thr_str  = format!("No data for board {}!", self.rb_selector);
1102        match last_ltb_moni {
1103          None => (),
1104          Some(mon) => {
1105            ltb_moni_str = format!("{}", mon);
1106            ltb_thr_str  = format!("  Hit  : {:.3} [mV]", mon.thresh[0]);
1107            ltb_thr_str += &(format!("\n  Beta : {:.3} [mV]", mon.thresh[1]));
1108            ltb_thr_str += &(format!("\n  Veto : {:.3} [mV]", mon.thresh[2]));
1109          }
1110        }
1111        let moni_view = Paragraph::new(ltb_moni_str)
1112          .style(self.theme.style())
1113          .alignment(Alignment::Left)
1114          //.scroll((5, 10))
1115          .block(
1116          Block::default()
1117            .borders(Borders::ALL)
1118            .style(self.theme.style())
1119            .title("Last LTBMoniData")
1120            .border_type(BorderType::Rounded),
1121        );
1122        frame.render_widget(moni_view, columns[0]);
1123        let thr_view = Paragraph::new(ltb_thr_str)
1124          .style(self.theme.style())
1125          .alignment(Alignment::Left)
1126          //.scroll((5, 10))
1127          .block(
1128          Block::default()
1129            .borders(Borders::ALL)
1130            .style(self.theme.style())
1131            .title("Thresholds")
1132            .border_type(BorderType::Rounded),
1133        );
1134        frame.render_widget(thr_view, rows[2]);
1135
1136        let tt_name   = String::from("Trenz Temp");
1137        let tt_title  = String::from("Trenz Temp [\u{00B0}C]");
1138        let tt_data   = self.ltb_moni_queue.get_var_for_board("trenz_temp", &self.rb_selector);
1139        let mut tt_ts = VecDeque::<(f64, f64)>::new(); 
1140        match tt_data {
1141          None => {
1142            error!("No trenz temp data available for board {}", self.rb_selector);
1143          },
1144          Some(data) => {
1145            if data.len() != 0 {
1146              for (k, time) in self.met_queue_ltb_moni.get(&self.rb_selector).unwrap().iter().enumerate() {
1147                tt_ts.push_back((*time, data[k] as f64));
1148              }
1149            }
1150          }
1151        }
1152        let tt_tc = timeseries(&mut tt_ts,
1153                               tt_name,
1154                               tt_title,
1155                               &self.theme);
1156        frame.render_widget(tt_tc, rows[0]);
1157
1158        let lt_name   = String::from("LTB Temperature");
1159        let lt_title  = String::from("LTB Temp. [\u{00B0}C]");
1160        let lt_data      = self.ltb_moni_queue.get_var_for_board("ltb_temp", &self.rb_selector);
1161        let mut lt_ts    = VecDeque::<(f64, f64)>::new(); 
1162        match lt_data {
1163          None => {
1164            error!("No LTB temp data available for board {}", self.rb_selector);
1165          },
1166          Some(data) => {
1167            if data.len() != 0 {
1168              for (k, time) in self.met_queue_ltb_moni.get(&self.rb_selector).unwrap().iter().enumerate() {
1169                lt_ts.push_back((*time, data[k] as f64));
1170              }
1171            }
1172          }
1173        }
1174        let lt_tc = timeseries(&mut lt_ts,
1175                               lt_name,
1176                               lt_title,
1177                               &self.theme);
1178        frame.render_widget(lt_tc, rows[1]);
1179
1180      }
1181      RBTabView::PAMoniData => {
1182        let rows = Layout::default()
1183            .direction(Direction::Vertical)
1184            .constraints(
1185                [Constraint::Percentage(8),
1186                 Constraint::Percentage(92)].as_ref(),
1187            )
1188            .split(*main_window);
1189          let columns = Layout::default()
1190              .direction(Direction::Horizontal)
1191              .constraints(
1192                  [Constraint::Percentage(25),
1193                   Constraint::Percentage(25),
1194                   Constraint::Percentage(25),
1195                   Constraint::Percentage(25)].as_ref(),
1196              )
1197              .split(rows[1]);
1198          let col0 = Layout::default()
1199              .direction(Direction::Vertical)
1200              .constraints(
1201                  [Constraint::Percentage(24),
1202                   Constraint::Percentage(24),
1203                   Constraint::Percentage(24),
1204                   Constraint::Percentage(24)].as_ref(),
1205              )
1206              .split(columns[0]);
1207          let col1 = Layout::default()
1208              .direction(Direction::Vertical)
1209              .constraints(
1210                  [Constraint::Percentage(24),
1211                   Constraint::Percentage(24),
1212                   Constraint::Percentage(24),
1213                   Constraint::Percentage(24)].as_ref(),
1214              )
1215              .split(columns[1]);
1216          let col2 = Layout::default()
1217              .direction(Direction::Vertical)
1218              .constraints(
1219                  [Constraint::Percentage(24),
1220                   Constraint::Percentage(24),
1221                   Constraint::Percentage(24),
1222                   Constraint::Percentage(24)].as_ref(),
1223              )
1224              .split(columns[2]);
1225          let col3 = Layout::default()
1226              .direction(Direction::Vertical)
1227              .constraints(
1228                  [Constraint::Percentage(24),
1229                   Constraint::Percentage(24),
1230                   Constraint::Percentage(24),
1231                   Constraint::Percentage(24)].as_ref(),
1232              )
1233              .split(columns[3]);
1234        // the preamps don't have their own board, the board id refers to the RB
1235        let pa_str  = format!("PreampMoniData for ReadoutBoard {}", self.rb_selector);
1236        let pa_view = Paragraph::new(pa_str)
1237          .style(self.theme.style())
1238          .alignment(Alignment::Left)
1239          .block(
1240          Block::default()
1241            .borders(Borders::ALL)
1242            .style(self.theme.style())
1243            //.title("Thresholds")
1244            .border_type(BorderType::Rounded),
1245        );
1246        frame.render_widget(pa_view, rows[0]);
1247        
1248        if self.pa_show_biases {
1249          //let mut moni_str  = String::from("No PAMoniData avaiable!");
1250          //match self.pa_moni_queue.get_last_moni(self.rb_selector) {
1251          //  None => (),
1252          //  Some(moni) => {
1253          //    moni_str = format!("{}", moni);
1254          //  }
1255          //}
1256          //let moni_view = Paragraph::new(moni_str)
1257          //  .style(self.theme.style())
1258          //  .alignment(Alignment::Left)
1259          //  .block(
1260          //  Block::default()
1261          //    .borders(Borders::ALL)
1262          //    .style(self.theme.style())
1263          //    //.title("Thresholds")
1264          //    .border_type(BorderType::Rounded),
1265          //);
1266          //frame.render_widget(moni_view, rows[1]);
1267          // the temperature plots
1268          for k in 0..16 {
1269            let identifier = format!("bias_{}", k+1);
1270            let name   = format!("Ch {} Bias Voltage", k+1);
1271            let title  = format!("Ch {} Bias [V]", k+1);
1272            let c_data = self.pa_moni_queue.get_var_for_board(&identifier, &self.rb_selector);
1273            let mut ts = VecDeque::<(f64, f64)>::new(); 
1274            match c_data {
1275              None => {
1276                error!("No {} data available for board {}", identifier, self.rb_selector);
1277              },
1278              Some(data) => {
1279                for (k, time) in self.met_queue_pa_moni.get(&self.rb_selector).unwrap().iter().enumerate() {
1280                  ts.push_back((*time, data[k] as f64));
1281                }
1282              }
1283            }
1284            let tc = timeseries(&mut ts,
1285                                name,
1286                                title,
1287                                &self.theme);
1288            if k < 4 {
1289              frame.render_widget(tc, col0[k]);
1290            } else if k < 8 {
1291              frame.render_widget(tc, col1[k-4]);
1292            } else if k < 12 {
1293              frame.render_widget(tc, col2[k-8]);
1294            } else if k < 16 {
1295              frame.render_widget(tc, col3[k-12]);
1296            }
1297          }
1298        } else {
1299          // the temperature plots
1300          for k in 0..16 {
1301            let identifier = format!("temps{}", k+1);
1302            let name   = format!("Ch {} Temperature", k+1);
1303            let title  = format!("Ch {} Temp [\u{00B0}C]", k+1);
1304            let c_data = self.pa_moni_queue.get_var_for_board(&identifier, &self.rb_selector);
1305            let mut ts = VecDeque::<(f64, f64)>::new(); 
1306            match c_data {
1307              None => {
1308                error!("No {} data available for board {}", identifier, self.rb_selector);
1309              },
1310              Some(data) => {
1311                if data.len() != 0 {
1312                  for (k, time) in self.met_queue_pa_moni.get(&self.rb_selector).unwrap().iter().enumerate() {
1313                    ts.push_back((*time, data[k] as f64));
1314                  }
1315                }
1316              }
1317            }
1318            let tc = timeseries(&mut ts,
1319                                name,
1320                                title,
1321                                &self.theme);
1322            if k < 4 {
1323              frame.render_widget(tc, col0[k]);
1324            } else if k < 8 {
1325              frame.render_widget(tc, col1[k-4]);
1326            } else if k < 12 {
1327              frame.render_widget(tc, col2[k-8]);
1328            } else if k < 16 {
1329              frame.render_widget(tc, col3[k-12]);
1330            }
1331          }
1332        }
1333      }
1334      RBTabView::PBMoniData => {
1335      }
1336      RBTabView::GlobalRates => {
1337        // this is just the simple example
1338        let mut rows = Vec::<Row>::new();
1339        // we have 40 rbs
1340        let mut rbids = Vec::from_iter(&mut self.global_rates.keys());
1341        rbids.sort();
1342        let mut ncol = 0;
1343        let mut this_row = Vec::<String>::new();
1344        for k in rbids {
1345          this_row.push(format!("RB {:02}", k));
1346          if self.global_rates[k] == String::from("0") {
1347            this_row.push(format!("\u{203c} {} Hz", self.global_rates[k]));
1348          } else {
1349            this_row.push(format!("\u{2714} {} Hz", self.global_rates[k]));
1350          }
1351          ncol += 2;
1352          if ncol == 8 {
1353            rows.push(Row::new(this_row.clone()));
1354            this_row = Vec::<String>::new();
1355            ncol = 0;
1356          }
1357          //let this_row = vec![format!("{}",k) , format!("{}", global_rates[k])];
1358          //rows.push(this_row);
1359        }
1360        // Columns widths are constrained in the same way as Layout...
1361        let widths = [
1362          Constraint::Percentage(12),
1363          Constraint::Percentage(12),
1364          Constraint::Percentage(12),
1365          Constraint::Percentage(12),
1366          Constraint::Percentage(12),
1367          Constraint::Percentage(12),
1368          Constraint::Percentage(12),
1369          Constraint::Percentage(16),
1370        ];
1371        let table = Table::new(rows, widths)
1372          // ...and they can be separated by a fixed spacing.
1373          .column_spacing(1)
1374          // You can set the style of the entire Table.
1375          .style(Style::new().blue())
1376          // It has an optional header, which is simply a Row always visible at the top.
1377          .header(
1378            Row::new(vec!["RBs", "Rate", "RBs", "Rate", "RBs", "Rate", "RBs", "Rate"])
1379              .style(self.theme.style())
1380              // To add space between the header and the rest of the rows, specify the margin
1381              .bottom_margin(1),
1382          )
1383          // It has an optional footer, which is simply a Row always visible at the bottom.
1384          //.footer(Row::new(vec!["Updated on Dec 28"]))
1385          // As any other widget, a Table can be wrapped in a Block.
1386          .block(
1387            Block::default()
1388              .borders(Borders::ALL)
1389              .style(self.theme.style())
1390              .title("RB Trigger rates (scalar, from RBMoniData)")
1391              .border_type(BorderType::Rounded),
1392            )
1393          // The selected row, column, cell and its content can also be styled.
1394          .row_highlight_style(self.theme.style())
1395          .column_highlight_style(self.theme.style())
1396          .cell_highlight_style(self.theme.style());
1397          // ...and potentially show a symbol in front of the selection.
1398          //.highlight_symbol(">>");
1399        frame.render_widget(table, *main_window); 
1400      }
1401      // This seems deprecated
1402      RBTabView::Info => {
1403        let main_view = Layout::default()
1404          .direction(Direction::Horizontal)
1405          .constraints(
1406              [Constraint::Percentage(30), Constraint::Percentage(70)].as_ref(),
1407          )
1408          .split(*main_window);
1409        let view_info = format!("Summary Statistics:
1410           N_Events                         : {}
1411           N_Moni                           : {}
1412           N EventID Missed                 : {}",
1413                              self.n_events,
1414                              self.n_moni,
1415                              self.miss_evid);
1416        let info_view = Paragraph::new(view_info)
1417        .style(self.theme.style())
1418        .alignment(Alignment::Left)
1419        .block(
1420          Block::default()
1421            .borders(Borders::ALL)
1422            .style(self.theme.style())
1423            .title("Overview")
1424            .border_type(BorderType::Rounded),
1425        );
1426
1427        // render everything
1428        frame.render_widget(info_view, main_view[0]); 
1429      }
1430    } //end match 
1431  }
1432}
1433