liftof_tui/tabs/
tab_commands.rs1use ratatui::prelude::*;
6use ratatui::widgets::{
7 Block,
8 BorderType,
9 Borders,
10 Paragraph,
11 List,
13 ListItem,
14 ListState,
15};
16
17use std::collections::VecDeque;
18use crossbeam_channel::{
19 Receiver
22};
23
24use std::sync::Mutex;
25use gondola_core::prelude::*;
26
27use crate::colors::ColorTheme;
28
29#[derive(Debug, Copy, Clone, PartialEq)]
30pub enum CommandTabView {
31 General,
32 LatestConfig
33}
34
35
36pub struct CommandTab<'a> {
37 pub resp_rc : Receiver<TofPacket>,
38 pub theme : ColorTheme,
39 pub cmd_sender : zmq::Socket,
40 pub cmdl_state : ListState,
42 pub cmdl_items : Vec::<ListItem<'a>>,
43 pub cmdl_active : bool,
44 pub cmdl_selector : usize,
45 pub allow_commands : bool,
46 pub active_cmd : TofCommand,
47 pub commands : Vec<TofCommand>,
48 pub queue_size : usize,
49 pub tr_queue : VecDeque<TofResponse>,
50 pub latest_config : Arc<Mutex<String>>,
51 pub view : CommandTabView,
52}
53
54impl CommandTab<'_> {
55 pub fn new<'a>(resp_rc : Receiver<TofPacket>,
56 cmd_pub_addr : String,
57 theme : ColorTheme,
58 allow_commands : bool,
59 latest_config : Arc<Mutex<String>>) -> CommandTab<'a> {
60 let mut ping_cmd = TofCommand::new();
61 ping_cmd.command_code = TofCommandCode::Ping;
62 let mut start_cmd = TofCommand::new();
63 start_cmd.command_code = TofCommandCode::DataRunStart;
64 let mut stop_cmd = TofCommand::new();
65 stop_cmd.command_code = TofCommandCode::DataRunStop;
66 let mut cali_cmd = TofCommand::new();
67 cali_cmd.command_code = TofCommandCode::RBCalibration;
68
69 let mut send_te = TofCommand::new();
70 send_te.command_code = TofCommandCode::SendTofEvents;
71 let mut no_send_te = TofCommand::new();
72 no_send_te.command_code = TofCommandCode::NoSendTofEvents;
73
74 let mut send_rw = TofCommand::new();
75 send_rw.command_code = TofCommandCode::SendRBWaveforms;
76 let mut no_send_rw = TofCommand::new();
77 no_send_rw.command_code = TofCommandCode::NoSendRBWaveforms;
78
79 let mut kill_cmd = TofCommand::new();
80 kill_cmd.command_code = TofCommandCode::Kill;
81
82 let commands = vec![ping_cmd,
83 start_cmd,
84 stop_cmd,
85 cali_cmd,
86 send_te,
87 no_send_te,
88 send_rw,
89 no_send_rw,
90 kill_cmd];
91
92 let mut cmd_select_items = Vec::<ListItem>::new();
93 for k in &commands {
94 let this_item = format!("{:?}", k.command_code);
95 cmd_select_items.push(ListItem::new(Line::from(this_item)));
96 }
97 let ctx = zmq::Context::new();
98 let cmd_sender = ctx.socket(zmq::PUB).expect("Can not create 0MQ PUB socket!");
99 if allow_commands {
100 cmd_sender.bind(&cmd_pub_addr).expect("Unable to bind to (PUB) socket!");
101 }
102 CommandTab {
105 theme ,
106 resp_rc ,
107 cmd_sender ,
108 cmdl_state : ListState::default(),
109 cmdl_items : cmd_select_items,
110 cmdl_active : false,
111 cmdl_selector : 0,
112 allow_commands : allow_commands,
113 active_cmd : TofCommand::new(),
114 commands : commands,
115 queue_size : 1000,
116 tr_queue : VecDeque::<TofResponse>::new(),
117 latest_config : latest_config,
118 view : CommandTabView::General
119 }
120 }
121
122 pub fn next_cmd(&mut self) {
123 let i = match self.cmdl_state.selected() {
124 Some(i) => {
125 if i >= self.cmdl_items.len() - 1 {
126 self.cmdl_items.len() - 1
127 } else {
128 i + 1
129 }
130 }
131 None => 0,
132 };
133 self.cmdl_state.select(Some(i));
134 self.active_cmd = self.commands.get(i).unwrap().clone();
135 }
137
138 pub fn prev_cmd(&mut self) {
139 let i = match self.cmdl_state.selected() {
140 Some(i) => {
141 if i == 0 {
142 0
143 } else {
144 i - 1
145 }
146 }
147 None => 0,
148 };
149 self.cmdl_state.select(Some(i));
150 self.active_cmd = self.commands.get(i).unwrap().clone();
151 }
152
153 pub fn send_command(&self) {
155 if !self.allow_commands {
156 error!("To send commands, run program with --send-commands!");
157 return;
158 }
159 let payload = self.active_cmd.pack().to_bytestream();
160 match self.cmd_sender.send(&payload, 0) {
161 Err(err) => {
162 error!("Unable to send command! {err}");
163 },
164 Ok(_) => {
165 info!("TOF cmd {} sent!", self.active_cmd);
166 }
167 }
168 }
169
170 pub fn receive_packet(&mut self) -> Result<(), SerializationError> {
172 match self.resp_rc.try_recv() {
173 Err(_err) => {
174 debug!("Unable to receive ACK TofPacket!");
175 }
176 Ok(pack) => {
177 match pack.packet_type {
179 TofPacketType::TofResponse => {
180 let tr : TofResponse = pack.unpack()?;
181 self.tr_queue.push_back(tr);
182 if self.tr_queue.len() > self.queue_size {
183 self.tr_queue.pop_front();
184 }
185 }
186 _ => () }
188 }
189 }
190 Ok(())
191 }
192
193 pub fn render(&mut self, main_window : &Rect, frame : &mut Frame) {
194 match self.view {
195 CommandTabView::General => {
196 let main_lo = Layout::default()
197 .direction(Direction::Horizontal)
198 .constraints(
199 [Constraint::Percentage(20), Constraint::Percentage(80)].as_ref(),
200 )
201 .split(*main_window);
202 let par_title_string = String::from("Select Command");
203 let (first, rest) = par_title_string.split_at(1);
204
205 let par_title = Line::from(vec![
206 Span::styled(
207 first,
208 Style::default()
209 .fg(self.theme.hc)
210 .add_modifier(Modifier::UNDERLINED),
211 ),
212 Span::styled(rest, self.theme.style()),
213 ]);
214
215 let cmds = Block::default()
216 .borders(Borders::ALL)
217 .style(self.theme.style())
218 .title(par_title)
219 .border_type(BorderType::Plain);
220
221 let cmd_select_list = List::new(self.cmdl_items.clone()).block(cmds)
222 .highlight_style(self.theme.highlight().add_modifier(Modifier::BOLD))
223 .highlight_symbol(">>")
224 .repeat_highlight_symbol(true);
225
226 match self.cmdl_state.selected() {
227 None => {
228 self.cmdl_selector = 0;
229 },
230 Some(cmd_id) => {
231 let selector = cmd_id;
233 if self.cmdl_selector != selector {
234 self.cmdl_selector = selector;
237 } else {
238 }
240 },
241 }
242 if self.allow_commands {
243 frame.render_stateful_widget(cmd_select_list, main_lo[0], &mut self.cmdl_state );
244 }
245 }
246 CommandTabView::LatestConfig => {
247 let main_lo = Layout::default()
248 .direction(Direction::Horizontal)
249 .constraints(
250 [Constraint::Percentage(10),
251 Constraint::Percentage(90)].as_ref(),
253 )
254 .split(*main_window);
255 match self.latest_config.lock() {
258 Ok(latest_config_view) => {
259 let liftof_view = Paragraph::new(&*(*latest_config_view))
260 .style(self.theme.style())
261 .alignment(Alignment::Left)
262 .block(
264 Block::default()
265 .borders(Borders::ALL)
266 .style(self.theme.style())
267 .title("Latest config")
268 .border_type(BorderType::Rounded),
269 );
270 frame.render_widget(liftof_view, main_lo[1]);
271 }
272 Err(err) => {
273 error!("Can not lock {err}");
274 }
275 }
276 }
277 }
278 }
279}