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