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