1use std::path::PathBuf;
5
6use crate::prelude::*;
7
8#[derive(Debug, Copy, Clone, PartialEq, FromRepr, AsRefStr, EnumIter)]
9#[cfg_attr(feature = "pybindings", pyclass(eq, eq_int))]
10#[repr(u8)]
11pub enum TofCommandCode {
12 Unknown = 0u8,
13 Ping = 1u8,
15 Moni = 2u8,
17 Kill = 4u8, ResetConfigWDefault = 5u8,
21 SubmitConfig = 6u8,
23 SetDataPublisherConfig = 20u8,
25 SetLTBThresholds = 21u8,
27 SetMTConfig = 22u8,
29 SetTofRunConfig = 23u8,
31 SetTofRBConfig = 24u8,
33 SetAnalysisEngineConfig = 27u8,
35 SetPreampBias = 28u8,
37 SetTOFEventBuilderConfig = 29u8,
39 DataRunStop = 30u8,
41 DataRunStart = 31u8,
43 StartValidationRun = 32u8,
45 GetFullWaveforms = 41u8,
47 UnspoolEventCache = 44u8,
49 RBCalibration = 53u8,
51 RestartLiftofRBClients = 60u8,
53 Listen = 70u8,
55 Staging = 71u8,
57 Lock = 80u8,
59 Unlock = 81u8,
61 SendTofEvents = 90u8,
63 NoSendTofEvents = 91u8,
65 SendRBWaveforms = 92u8,
67 NoSendRBWaveforms = 93u8,
69 SetRBChannelMask = 99u8,
71 ShutdownRB = 100u8,
73 ChangeNextRunConfig = 101u8,
75 ShutdownRAT = 102u8,
77 ShutdownRATPair = 103u8,
80 ShutdownCPU = 104u8,
82 UploadConfig = 105u8,
84 UploadConfigDiff = 106u8,
86 RunScriptAlfa = 107u8,
88 RunScriptBravo = 108u8,
90 RunScriptCharlie = 109u8,
92 RunScriptWhiskey = 110u8,
94 RunScriptTango = 111u8,
96 RunScriptFoxtrott = 112u8,
98 RequestLiftofSettings = 113u8,
100}
101
102expand_and_test_enum!(TofCommandCode, test_tofcommandcode_repr);
103
104#[derive(Debug, Clone, PartialEq)]
113#[cfg_attr(feature="pybindings", pyclass)]
114pub struct TofCommand {
115 pub command_code : TofCommandCode,
116 pub payload : Vec<u8>,
117}
118
119impl TofCommand {
120 pub fn new() -> Self {
122 Self {
123 command_code : TofCommandCode::Unknown,
124 payload : Vec::<u8>::new(),
125 }
126 }
127
128 pub fn forge_changerunconfig(key_value : &Vec<String>) -> Self {
142 let mut cmd = TofCommand::new();
143 cmd.command_code = TofCommandCode::ChangeNextRunConfig;
144 if key_value.len() == 0 {
145 error!("Empty command!");
146 return cmd;
147 }
148 let mut payload_string = String::from("");
149 for k in 0..key_value.len() - 1 {
150 payload_string += &format!("{}::", key_value[k]);
151 }
152 payload_string += key_value.last().unwrap();
153 let mut payload = Vec::<u8>::new();
154 payload.extend_from_slice(payload_string.as_bytes());
155 cmd.payload = payload;
156 cmd
157 }
158
159 pub fn extract_changerunconfig(&self) -> Option<Vec<String>> {
162 if self.command_code != TofCommandCode::ChangeNextRunConfig {
163 error!("Unable to extract configuration file changes from {}", self);
164 return None;
165 }
166 let mut liftof_config = Vec::<String>::new();
167 match String::from_utf8(self.payload.clone()) {
168 Err(err) => {
169 error!("Unable to extract the String payload! {err}");
170 }
171 Ok(concat_string) => {
172 let foo = concat_string.split("::").collect::<Vec<&str>>().into_iter();
173 for k in foo {
174 liftof_config.push(String::from(k));
175 }
176 }
177 }
178 Some(liftof_config)
179 }
180}
181
182impl TofPackable for TofCommand {
183 const TOF_PACKET_TYPE : TofPacketType = TofPacketType::TofCommand;
184}
185
186impl Serialization for TofCommand {
187
188 const HEAD : u16 = 0xAAAA;
189 const TAIL : u16 = 0x5555;
190
191 fn from_bytestream(stream : &Vec<u8>,
192 pos : &mut usize)
193 -> Result<Self, SerializationError>{
194 let mut command = TofCommand::new();
195 if parse_u16(stream, pos) != Self::HEAD {
196 error!("The given position {} does not point to a valid header signature of {}", pos, Self::HEAD);
197 return Err(SerializationError::HeadInvalid {});
198 }
199 command.command_code = TofCommandCode::from(parse_u8(stream, pos));
200 let payload_size = parse_u8(stream, pos);
201 let payload = stream[*pos..*pos + payload_size as usize].to_vec();
202 command.payload = payload;
203 *pos += payload_size as usize;
204 let tail = parse_u16(stream, pos);
205 if tail != Self::TAIL {
206 error!("After parsing the event, we found an invalid tail signature {}", tail);
207 return Err(SerializationError::TailInvalid);
208 }
209 Ok(command)
210 }
211
212 fn to_bytestream(&self) -> Vec<u8> {
213 let mut stream = Vec::<u8>::with_capacity(9);
214 stream.extend_from_slice(&Self::HEAD.to_le_bytes());
215 stream.push(self.command_code as u8);
216 stream.push(self.payload.len() as u8);
217 stream.extend_from_slice(self.payload.as_slice());
218 stream.extend_from_slice(&Self::TAIL.to_le_bytes());
219 stream
220 }
221}
222
223impl Default for TofCommand {
224 fn default() -> Self {
225 Self::new()
226 }
227}
228
229#[cfg(feature = "random")]
230impl FromRandom for TofCommand {
231 fn from_random() -> Self {
232 let mut rng = rand::rng();
233 let command_code = TofCommandCode::from_random();
234 let payload_size = rng.random::<u8>();
235 let mut payload = Vec::<u8>::with_capacity(payload_size as usize);
236 for _ in 0..payload_size {
237 payload.push(rng.random::<u8>());
238 }
239 Self {
240 command_code : command_code,
241 payload : payload
242 }
243 }
244}
245
246impl fmt::Display for TofCommand {
247 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
248 let mut repr = String::from("<TofCommand");
250 repr += &(format!("\n cmd code : {}", self.command_code));
251 match self.command_code {
252 TofCommandCode::ShutdownRB
253 | TofCommandCode::ShutdownRAT
254 | TofCommandCode::ShutdownRATPair => {
255 repr += &(format!("\n Sending shutdown command to RBs {:?}>", self.payload));
256 }
257 _ => {
258 repr += ">";
259 }
260 }
261 write!(f, "{}", repr)
262 }
263}
264
265#[cfg(feature="pybindings")]
266#[pymethods]
267impl TofCommand {
268
269 #[getter]
270 fn get_command_code(&mut self) -> TofCommandCode {
271 self.command_code
272 }
273
274 #[setter]
275 fn set_command_code(&mut self, command_code : TofCommandCode) {
276 self.command_code = command_code;
277 }
278
279 fn wrap_n_pack(&self) -> Vec<u8> {
285 self.pack().to_bytestream()
286 }
287
288 fn get_cc_u8(&self) -> u8 {
292 self.command_code as u8
293 }
294
295 #[pyo3(name="to_bytestream")]
296 fn to_bytestream_py(&self) -> Vec<u8> {
297 self.to_bytestream()
298 }
299}
300
301#[cfg(feature="pybindings")]
302pythonize_packable!(TofCommand);
303
304#[cfg_attr(feature="pybindings", pyfunction)]
306pub fn get_rbratmap_hardcoded() -> HashMap<u8,u8> {
307 warn!("Using hardcoded rbratmap!");
308 let mapping = HashMap::<u8,u8>::from(
309 [(1, 10),
310 (2, 15),
311 (3, 1),
312 (4, 15),
313 (5, 20),
314 (6, 19),
315 (7, 17),
316 (8, 9),
317 (9, 13),
318 (11,10),
319 (13, 4),
320 (14, 2),
321 (15, 1),
322 (16, 8),
323 (17,17),
324 (18,13),
325 (19, 7),
326 (20, 7),
327 (21, 5),
328 (22,11),
329 (23, 5),
330 (24, 6),
331 (25, 8),
332 (26,11),
333 (27, 6),
334 (28,20),
335 (29, 3),
336 (30, 9),
337 (31, 3),
338 (32, 2),
339 (33,18),
340 (34,18),
341 (35, 4),
342 (36,19),
343 (39,12),
344 (40,12),
345 (41,14),
346 (42,14),
347 (44,16),
348 (46,16)]);
349 mapping
350}
351
352#[cfg_attr(feature="pybindings", pyfunction)]
354pub fn get_ratrbmap_hardcoded() -> HashMap<u8,(u8,u8)> {
355 warn!("Using hardcoded ratrb map!");
356 let mapping = HashMap::<u8,(u8,u8)>::from(
357 [(1, (3,15)),
358 (2, (32,14)),
359 (3, (31,29)),
360 (4, (35,13)),
361 (5, (23,21)),
362 (6, (27,24)),
363 (7, (20,19)),
364 (8, (16,25)),
365 (9, (8,30)),
366 (10,(1,11)),
367 (11,(26,22)),
368 (12,(39,40)),
369 (13,(9,18)),
370 (14,(41,42)),
371 (15,(2,4)),
372 (16,(46,44)),
373 (17,(7,17)),
374 (18,(33,34)),
375 (19,(36,6)),
376 (20,(28,5))]);
377 mapping
378}
379
380#[cfg_attr(feature="pybindings", pyfunction)]
385pub fn get_ratpdumap_hardcoded() -> HashMap<u8,HashMap::<u8, (u8,u8)>> {
386 warn!("Using hardcoded rat-pdu map!");
387 let mut mapping = HashMap::<u8,HashMap::<u8,(u8,u8)>>::new();
388 let mut ch_map = HashMap::<u8, (u8,u8)>::from([(3, (15,16)), (7, (8,9))]);
389 mapping.insert(0u8, ch_map.clone());
390 ch_map = HashMap::<u8, (u8, u8)>::from([(2, (2,17)), (3, (4,5)), (5, (13,14))]);
391 mapping.insert(1u8, ch_map.clone());
392 ch_map = HashMap::<u8, (u8, u8)>::from([(3, (12,20)), (4, (10,11)), (5, (8,9))]);
393 mapping.insert(2u8, ch_map.clone());
394 ch_map = HashMap::<u8, (u8, u8)>::from([(2, (6,7)), (3, (1,3))]);
395 mapping.insert(3u8, ch_map.clone());
396 mapping
397}
398
399#[cfg_attr(feature="pybindings", pyfunction)]
405pub fn shutdown_rb(rb : u8) -> Option<TofCommand> {
406 let code = TofCommandCode::ShutdownRB;
407 let mut cmd = TofCommand::new();
408 cmd.command_code = code;
409 cmd.payload = vec![rb];
410 Some(cmd)
411}
412
413#[cfg_attr(feature="pybindings", pyfunction)]
419pub fn shutdown_rat(rat : u8) -> Option<TofCommand> {
420 let code = TofCommandCode::ShutdownRAT;
421 let mut cmd = TofCommand::new();
422 cmd.command_code = code;
423 cmd.payload = Vec::<u8>::new();
424 let ratmap = get_ratrbmap_hardcoded();
425 match ratmap.get(&rat) {
426 None => {
427 error!("Don't know RBs in RAT {}", rat);
428 return None
429 }
430 Some(pair) => {
431 cmd.payload.push(pair.0);
432 cmd.payload.push(pair.1);
433 }
434 }
435 Some(cmd)
436}
437
438#[cfg_attr(feature="pybindings", pyfunction)]
447pub fn shutdown_ratpair(pdu : u8, pduchannel : u8) -> Option<TofCommand> {
448 let code = TofCommandCode::ShutdownRATPair;
449 let mut cmd = TofCommand::new();
450 cmd.command_code = code;
451 cmd.payload = Vec::<u8>::new();
452 let ratmap = get_ratrbmap_hardcoded();
453 let ratpdumap = get_ratpdumap_hardcoded();
454 match ratpdumap.get(&pdu) {
455 None => {
456 error!("Don't know that there is a RAT connected to PDU {}!", pdu);
457 return None;
458 }
459 Some(select_pdu) => {
460 match select_pdu.get(&pduchannel) {
461 None => {
462 error!("Don't know that there is a RAT connected to PDU {}, channel {}!", pdu, pduchannel);
463 return None;
464 }
465 Some(rats) => {
466 match ratmap.get(&rats.0) {
467 Some(rbs) => {
468 cmd.payload.push(rbs.0);
469 cmd.payload.push(rbs.1);
470 }
471 None => {
472 error!("RAT mapping incorrect!");
473 return None;
474 }
475 }
476 match ratmap.get(&rats.1) {
477 Some(rbs) => {
478 cmd.payload.push(rbs.0);
479 cmd.payload.push(rbs.1);
480 },
481 None => {
482 error!("RAT mapping incorrect!");
483 return None;
484 }
485 }
486 }
487 }
488 }
489 }
490 Some(cmd)
491}
492
493#[cfg_attr(feature="pybindings", pyfunction)]
496pub fn shutdown_all_rbs() -> Option<TofCommand> {
497 Some(TofCommand {
498 command_code : TofCommandCode::ShutdownRB,
499 payload : Vec::<u8>::new()
500 })
501}
502
503#[cfg_attr(feature="pybindings", pyfunction)]
506pub fn shutdown_tofcpu() -> Option<TofCommand> {
507 Some(TofCommand {
508 command_code : TofCommandCode::ShutdownCPU,
509 payload : Vec::<u8>::new()
510 })
511}
512
513#[cfg_attr(feature="pybindings", pyfunction)]
519pub fn restart_liftofrb(rbs : Vec<u8>) -> Option<TofCommand> {
520 Some(TofCommand {
524 command_code : TofCommandCode::RestartLiftofRBClients,
525 payload : rbs
526 })
527}
528
529#[cfg_attr(feature="pybindings", pyfunction)]
532pub fn restore_default_config() -> Option<TofCommand> {
533 Some(TofCommand {
534 command_code : TofCommandCode::ResetConfigWDefault,
535 payload : Vec::<u8>::new(),
536 })
537}
538
539#[cfg_attr(feature="pybindings", pyfunction)]
542pub fn start_run() -> Option<TofCommand> {
543 Some(TofCommand {
544 command_code : TofCommandCode::DataRunStart,
545 payload : Vec::<u8>::new(),
546 })
547}
548
549#[cfg_attr(feature="pybindings", pyfunction)]
551pub fn stop_run() -> Option<TofCommand> {
552 Some(TofCommand {
553 command_code : TofCommandCode::DataRunStop,
554 payload : Vec::<u8>::new(),
555 })
556}
557
558#[cfg_attr(feature="pybindings", pyfunction)]
560pub fn run_action_alfa() -> Option<TofCommand> {
561 Some(TofCommand {
562 command_code : TofCommandCode::RunScriptAlfa,
563 payload : Vec::<u8>::new(),
564 })
565}
566
567#[cfg_attr(feature="pybindings", pyfunction)]
569pub fn run_action_bravo() -> Option<TofCommand> {
570 Some(TofCommand {
571 command_code : TofCommandCode::RunScriptBravo,
572 payload : Vec::<u8>::new(),
573 })
574}
575
576#[cfg_attr(feature="pybindings", pyfunction)]
578pub fn run_action_charlie() -> Option<TofCommand> {
579 Some(TofCommand {
580 command_code : TofCommandCode::RunScriptCharlie,
581 payload : Vec::<u8>::new(),
582 })
583}
584
585#[cfg_attr(feature="pybindings", pyfunction)]
587pub fn run_action_whiskey() -> Option<TofCommand> {
588 Some(TofCommand {
589 command_code : TofCommandCode::RunScriptWhiskey,
590 payload : Vec::<u8>::new(),
591 })
592}
593
594#[cfg_attr(feature="pybindings", pyfunction)]
596pub fn run_action_tango() -> Option<TofCommand> {
597 Some(TofCommand {
598 command_code : TofCommandCode::RunScriptTango,
599 payload : Vec::<u8>::new(),
600 })
601}
602
603#[cfg_attr(feature="pybindings", pyfunction)]
605pub fn run_action_foxtrott() -> Option<TofCommand> {
606 Some(TofCommand {
607 command_code : TofCommandCode::RunScriptFoxtrott,
608 payload : Vec::<u8>::new(),
609 })
610}
611
612#[cfg_attr(feature="pybindings", pyfunction)]
614pub fn request_liftof_settings() -> Option<TofCommand> {
615 Some(TofCommand {
616 command_code : TofCommandCode::RequestLiftofSettings,
617 payload : Vec::<u8>::new(),
618 })
619}
620
621#[cfg_attr(feature="pybindings", pyfunction)]
623pub fn apply_settings_diff(default : String, modified : String) -> Option<TofCommand> {
624 let default_p = PathBuf::from(default);
625 let modified_p = PathBuf::from(modified);
626 let diff = create_compressed_diff(&default_p, &modified_p).unwrap();
627 if diff.len() > 240 {
628 panic!("Command payload is too long! Sorry, you have to split this up in multiple changes!");
629 }
630 Some(TofCommand {
631 command_code : TofCommandCode::UploadConfigDiff,
632 payload : diff,
633 })
634}
635
636#[cfg_attr(feature="pybindings", pyfunction)]
641pub fn enable_verification_run(enabled : bool) -> Option<TofCommand> {
642 Some(TofCommand {
643 command_code : TofCommandCode::StartValidationRun,
644 payload : vec![enabled as u8],
645 })
646}
647
648
649
650#[cfg_attr(feature="pybindings", pyfunction)]
659pub fn calibrate_rbs(pre_run_calibration : bool, send_packets : bool, save_events : bool) -> Option<TofCommand> {
660 let payload = vec![pre_run_calibration as u8, send_packets as u8, save_events as u8];
661 Some(TofCommand {
662 command_code : TofCommandCode::RBCalibration,
663 payload : payload,
664 })
665}
666
667#[cfg_attr(feature="pybindings", pyfunction)]
669pub fn change_triggerconfig(cfg : &TriggerConfig) -> Option<TofCommand> {
670 let payload = cfg.to_bytestream();
671 Some(TofCommand {
672 command_code : TofCommandCode::SetMTConfig,
673 payload : payload,
674 })
675}
676
677#[cfg_attr(feature="pybindings", pyfunction)]
679pub fn change_tofeventbuilderconfig(cfg : &TOFEventBuilderConfig) -> Option<TofCommand> {
680 let payload = cfg.to_bytestream();
681 Some(TofCommand {
682 command_code : TofCommandCode::SetTOFEventBuilderConfig,
683 payload : payload,
684 })
685}
686
687#[cfg_attr(feature="pybindings", pyfunction)]
689pub fn change_datapublisherconfig(cfg : &DataPublisherConfig) -> Option<TofCommand> {
690 let payload = cfg.to_bytestream();
691 Some(TofCommand {
692 command_code : TofCommandCode::SetDataPublisherConfig,
693 payload : payload,
694 })
695}
696
697#[cfg_attr(feature="pybindings", pyfunction)]
699pub fn change_tofrunconfig(cfg : &TofRunConfig) -> Option<TofCommand> {
700 let payload = cfg.to_bytestream();
701 Some(TofCommand {
702 command_code : TofCommandCode::SetTofRunConfig,
703 payload : payload,
704 })
705}
706
707#[cfg_attr(feature="pybindings", pyfunction)]
708pub fn change_tofrbconfig(cfg : &TofRBConfig) -> Option<TofCommand> {
709 let payload = cfg.to_bytestream();
710 Some(TofCommand {
711 command_code : TofCommandCode::SetTofRBConfig,
712 payload : payload,
713 })
714}
715
716