liftof_tui/tabs/
tab_commands.rs1use ratatui::prelude::*;
6use ratatui::widgets::{
7 Block,
8 BorderType,
9 Borders,
10 List,
13 ListItem,
14 ListState,
15};
16
17use std::collections::VecDeque;
18use crossbeam_channel::{
19 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 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 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 }
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 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 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 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 _ => () }
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 let selector = cmd_id;
236 if self.cmdl_selector != selector {
237 self.cmdl_selector = selector;
240 } else {
241 }
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}