liftof_tui/tabs/
tab_commands.rs

1//! Commands tab
2//! 
3//use chrono::Utc;
4
5use ratatui::prelude::*;
6use ratatui::widgets::{
7    Block,
8    BorderType,
9    Borders,
10    //Paragraph,
11    //BarChart,
12    List,
13    ListItem,
14    ListState,
15};
16
17use std::collections::VecDeque;
18use crossbeam_channel::{
19    //unbounded,
20    //Sender,
21    Receiver
22};
23
24use tof_dataclasses::packets::{
25  TofPacket,
26  PacketType
27};
28use tof_dataclasses::commands::{
29  TofCommandV2,
30  TofCommandCode,
31  TofResponse
32};
33
34use tof_dataclasses::serialization::{
35    Serialization,
36    SerializationError,
37    Packable
38};
39
40use crate::colors::ColorTheme;
41
42pub struct CommandTab<'a> {
43  pub resp_rc            : Receiver<TofPacket>,
44  pub theme              : ColorTheme,
45  pub cmd_sender         : zmq::Socket,
46  // list for the command selector
47  pub cmdl_state         : ListState,
48  pub cmdl_items         : Vec::<ListItem<'a>>,
49  pub cmdl_active        : bool,
50  pub cmdl_selector      : usize,
51  pub allow_commands     : bool,
52  pub active_cmd         : TofCommandV2,
53  pub commands           : Vec<TofCommandV2>,
54  pub queue_size         : usize,
55  pub tr_queue           : VecDeque<TofResponse>,
56}
57
58
59impl CommandTab<'_> {
60
61  pub fn new<'a>(resp_rc        : Receiver<TofPacket>,
62                 cmd_pub_addr   : String,
63                 theme          : ColorTheme,
64                 allow_commands : bool) -> CommandTab<'a> {  
65    let mut ping_cmd        = TofCommandV2::new();
66    ping_cmd.command_code   = TofCommandCode::Ping;
67    let mut start_cmd       = TofCommandV2::new();
68    start_cmd.command_code  = TofCommandCode::DataRunStart;
69    let mut stop_cmd        = TofCommandV2::new();
70    stop_cmd.command_code   = TofCommandCode::DataRunStop;
71    let mut cali_cmd        = TofCommandV2::new();
72    cali_cmd.command_code   = TofCommandCode::RBCalibration;
73    
74    let mut send_te         = TofCommandV2::new();
75    send_te.command_code    = TofCommandCode::SendTofEvents;
76    let mut no_send_te      = TofCommandV2::new();
77    no_send_te.command_code = TofCommandCode::NoSendTofEvents;
78    
79    let mut send_rw         = TofCommandV2::new();
80    send_rw.command_code    = TofCommandCode::SendRBWaveforms;
81    let mut no_send_rw      = TofCommandV2::new();
82    no_send_rw.command_code = TofCommandCode::NoSendRBWaveforms;
83
84    let mut kill_cmd        = TofCommandV2::new();
85    kill_cmd.command_code   = TofCommandCode::Kill;
86
87
88    let commands = vec![ping_cmd,
89                        start_cmd,
90                        stop_cmd,
91                        cali_cmd,
92                        send_te,
93                        no_send_te,
94                        send_rw,
95                        no_send_rw,
96                        kill_cmd];
97
98    let mut cmd_select_items = Vec::<ListItem>::new();
99    for k in &commands {
100      let this_item = format!("{:?}", k.command_code);
101      cmd_select_items.push(ListItem::new(Line::from(this_item)));
102    } 
103    let ctx = zmq::Context::new();
104    let cmd_sender = ctx.socket(zmq::PUB).expect("Can not create 0MQ PUB socket!"); 
105    if allow_commands {
106      cmd_sender.bind(&cmd_pub_addr).expect("Unable to bind to (PUB) socket!");
107    }
108    //thread::sleep(10*one_second);
109
110    CommandTab {
111      theme          ,
112      resp_rc        ,
113      cmd_sender     ,
114      cmdl_state     : ListState::default(),
115      cmdl_items     : cmd_select_items,
116      cmdl_active    : false,
117      cmdl_selector  : 0,
118      allow_commands : allow_commands,
119      active_cmd     : TofCommandV2::new(),
120      commands       : commands,
121      queue_size     : 1000,
122      tr_queue       : VecDeque::<TofResponse>::new(),
123    }
124  }
125  
126  pub fn next_cmd(&mut self) {
127    let i = match self.cmdl_state.selected() {
128      Some(i) => {
129        if i >= self.cmdl_items.len() - 1 {
130          self.cmdl_items.len() - 1
131        } else {
132          i + 1
133        }
134      }
135      None => 0,
136    };
137    self.cmdl_state.select(Some(i));
138    self.active_cmd = self.commands.get(i).unwrap().clone();
139    //info!("Selecting {}", i);
140  }
141
142  pub fn prev_cmd(&mut self) {
143    let i = match self.cmdl_state.selected() {
144      Some(i) => {
145        if i == 0 {
146          0 
147        } else {
148          i - 1
149        }
150      }
151      None => 0,
152    };
153    self.cmdl_state.select(Some(i));
154    self.active_cmd = self.commands.get(i).unwrap().clone();
155  }
156 
157  /// send the selected dommand
158  pub fn send_command(&self) {
159    if !self.allow_commands {
160      error!("To send commands, run program with --send-commands!");
161      return;
162    }
163    let payload = self.active_cmd.pack().to_bytestream();
164    match self.cmd_sender.send(&payload, 0) {
165      Err(err) => {
166        error!("Unable to send command! {err}");
167      },
168      Ok(_) => {
169        info!("TOF cmd {} sent!", self.active_cmd);
170      }
171    }  
172  }
173
174  /// Get the responses from the main program packet distributor
175  pub fn receive_packet(&mut self) -> Result<(), SerializationError> {
176    match self.resp_rc.try_recv() {
177      Err(_err)   => {
178        debug!("Unable to receive ACK TofPacket!");  
179      }
180      Ok(pack)    => {
181        //println!("Received {}", pack);
182        match pack.packet_type {
183          PacketType::TofResponse => {
184            let tr : TofResponse = pack.unpack()?;
185            self.tr_queue.push_back(tr);
186            if self.tr_queue.len() > self.queue_size {
187              self.tr_queue.pop_front(); 
188            }
189          }
190          _ => () // we don't care
191        }
192      }
193    }
194    Ok(())
195  }
196
197  pub fn render(&mut self, main_window : &Rect, frame : &mut Frame) {
198
199    let main_lo = Layout::default()
200      .direction(Direction::Horizontal)
201      .constraints(
202          [Constraint::Percentage(20), Constraint::Percentage(80)].as_ref(),
203      )
204      .split(*main_window);
205       let par_title_string = String::from("Select Command");
206       let (first, rest) = par_title_string.split_at(1);
207    
208    let par_title = Line::from(vec![
209         Span::styled(
210             first,
211             Style::default()
212                 .fg(self.theme.hc)
213                 .add_modifier(Modifier::UNDERLINED),
214         ),
215         Span::styled(rest, self.theme.style()),
216       ]);
217
218    let cmds = Block::default()
219      .borders(Borders::ALL)
220      .style(self.theme.style())
221      .title(par_title)
222      .border_type(BorderType::Plain);
223    
224    let cmd_select_list = List::new(self.cmdl_items.clone()).block(cmds)
225      .highlight_style(self.theme.highlight().add_modifier(Modifier::BOLD))
226      .highlight_symbol(">>")
227      .repeat_highlight_symbol(true);
228    
229    match self.cmdl_state.selected() {
230      None    => {
231        self.cmdl_selector = 0;
232      },
233      Some(cmd_id) => {
234        // entry 0 is for all paddles
235        let selector =  cmd_id;
236        if self.cmdl_selector != selector {
237          //self.paddle_changed = true;
238          //self.init_histos();
239          self.cmdl_selector = selector;
240        } else {
241          //self.paddle_changed = false;
242        }
243      },
244    }
245    if self.allow_commands {
246      frame.render_stateful_widget(cmd_select_list, main_lo[0], &mut self.cmdl_state );
247    }
248  }
249} // end impl
250