liftof_lib/
settings.rs

1//! Aggregate settings for the TOF system
2//!
3//! Control the settings for the C&C server
4//! as well as the liftof-clients on the RBs
5//!
6//! Different sections might represent different
7//! threads/aspects of the code
8//!
9
10//use std::fmt::format;
11use std::fs::File;
12use std::io::{
13    Write,
14    Read,
15};
16use std::fmt;
17use std::collections::HashMap;
18
19use tof_dataclasses::commands::config::{
20  BuildStrategy,
21  TriggerConfig,
22  TOFEventBuilderConfig, 
23  TofRunConfig,
24  TofRBConfig,
25  DataPublisherConfig,
26};
27use tof_dataclasses::events::master_trigger::TriggerType;
28
29//use tof_dataclasses::events::master_trigger::TriggerType;
30use tof_dataclasses::events::DataType;
31#[cfg(feature="database")]
32use tof_dataclasses::packets::TofPacket;
33use tof_dataclasses::commands::TofOperationMode;
34#[cfg(feature="database")]
35use tof_dataclasses::commands::TofCommandV2;
36#[cfg(feature="database")]
37use tof_dataclasses::commands::TofCommandCode;
38
39use tof_dataclasses::commands::config::RunConfig;
40#[cfg(feature="database")]
41use tof_dataclasses::database::RAT;
42#[cfg(feature="database")]
43use tof_dataclasses::commands::config::PreampBiasConfig;
44#[cfg(feature="database")]
45use tof_dataclasses::commands::config::LTBThresholdConfig;
46#[cfg(feature="database")]
47use tof_dataclasses::commands::config::RBChannelMaskConfig;
48
49use tof_dataclasses::serialization::{
50  parse_u8,
51  parse_u16,
52  parse_u32,
53  //parse_bool, 
54  Serialization,
55  SerializationError,
56};
57
58#[cfg(feature="database")]
59use tof_dataclasses::serialization::Packable;
60
61#[derive(Debug, Clone, PartialEq, serde::Deserialize, serde::Serialize)]
62pub enum ParameterSetStrategy {
63  ControlServer,
64  Board
65}
66
67//pub trait ToPacketString {
68//  pub fn to_packed_string(&self) { 
69//    match toml::to_string(&self) {
70//      Err(err) => {
71//        error!("Unable to serialize toml! {err}");
72//      }
73//      Ok(toml_string) => {
74//      }
75//    }
76//  }
77//}
78
79/// Configure the trigger
80#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
81pub struct MTBSettings {
82  /// Select the trigger type for this run
83  pub trigger_type           : TriggerType,
84  /// Select the prescale factor for a run. The
85  /// prescale factor is between 0 (no events)
86  /// and 1.0 (all events). E.g. 0.1 means allow 
87  /// only 10% of the events
88  /// THIS DOES NOT APPLY TO THE GAPS OR POISSON 
89  /// TRIGGER!
90  pub trigger_prescale               : f32,
91  /// Set to true if we want a combo trigger run
92  pub use_combo_trigger              : bool,
93  /// Set the global trigger type. This has to be less 
94  /// strict than the trigger type   
95  pub global_trigger_type            : TriggerType,
96  /// Set the gloabl trigger prescale
97  pub global_trigger_prescale        : f32,
98
99  /// in case trigger_type = "Poisson", set rate here
100  pub poisson_trigger_rate           : u32,
101  /// in case trigger_type = "Gaps", set if we want to use 
102  /// beta
103  pub gaps_trigger_use_beta     : bool,
104  /// In case we are running the fixed rate trigger, set the
105  /// desired rate here
106  /// not sure
107  //pub gaps_trigger_inner_thresh : u32,
108  ///// not sure
109  //pub gaps_trigger_outer_thresh : u32, 
110  ///// not sure
111  //pub gaps_trigger_total_thresh : u32, 
112  ///// not sure
113  //pub gaps_trigger_hit_thresh   : u32,
114  /// Enable trace suppression on the MTB. If enabled, 
115  /// only those RB which hits will read out waveforms.
116  /// In case it is disabled, ALL RBs will readout events
117  /// ALL the time. For this, we need also the eventbuilder
118  /// strategy "WaitForNBoards(40)"
119  pub trace_suppression  : bool,
120  /// The number of seconds we want to wait
121  /// without hearing from the MTB before
122  /// we attempt a reconnect
123  pub mtb_timeout_sec    : u64,
124  /// Time in seconds between housekkeping 
125  /// packets
126  pub mtb_moni_interval  : u64,
127  pub rb_int_window      : u8,
128  pub tiu_emulation_mode : bool,
129  pub tiu_ignore_busy    : bool,
130  pub tofbot_webhook     : String,
131  pub hb_send_interval   : u64,
132}
133
134impl MTBSettings {
135  pub fn new() -> Self {
136    Self {
137      trigger_type            : TriggerType::Unknown,
138      trigger_prescale        : 0.0,
139      poisson_trigger_rate    : 0,
140      gaps_trigger_use_beta   : true,
141      trace_suppression       : true,
142      mtb_timeout_sec         : 60,
143      mtb_moni_interval       : 30,
144      rb_int_window           : 1,
145      tiu_emulation_mode      : false,
146      tiu_ignore_busy         : false,
147      tofbot_webhook          : String::from(""),
148      hb_send_interval        : 30,
149      use_combo_trigger       : false,
150      global_trigger_type     : TriggerType::Unknown,
151      global_trigger_prescale : 1.0,
152    }
153  }
154
155  /// Emit a config, so that infomraiton can be transported
156  /// over the wire
157  pub fn emit_triggerconfig(&self) -> TriggerConfig {
158    let mut cfg = TriggerConfig::new();
159    // all fields should be active, since the settings file 
160    // contains all fields per definition. We can already 
161    // be future proof and just set all of them
162    cfg.active_fields          = u32::MAX;
163    cfg.gaps_trigger_use_beta  = Some(self.gaps_trigger_use_beta);
164    cfg.prescale               = Some(self.trigger_prescale);
165    cfg.trigger_type           = Some(self.trigger_type);
166    cfg.use_combo_trigger      = Some(self.use_combo_trigger);
167    cfg.combo_trigger_type     = Some(self.global_trigger_type);
168    cfg.combo_trigger_prescale = Some(self.global_trigger_prescale);
169    cfg.trace_suppression      = Some(self.trace_suppression);
170    cfg.mtb_moni_interval      = Some((self.mtb_moni_interval & 0xffff) as u16); 
171    cfg.tiu_ignore_busy        = Some(self.tiu_ignore_busy); 
172    cfg.hb_send_interval       = Some((self.hb_send_interval & 0xffff) as u16); 
173    cfg
174  }
175
176  /// Change seetings accordingly to config 
177  pub fn from_triggerconfig(&mut self, cfg : &TriggerConfig) {
178    if cfg.gaps_trigger_use_beta.is_some() {
179      self.gaps_trigger_use_beta   = cfg.gaps_trigger_use_beta.unwrap() ;
180    }
181    if cfg.prescale.is_some() {
182      self.trigger_prescale        = cfg.prescale.unwrap()              ;
183    }
184    if cfg.trigger_type.is_some() {
185      self.trigger_type            = cfg.trigger_type.unwrap()          ; 
186    }
187    if cfg.use_combo_trigger.is_some() {
188      self.use_combo_trigger       = cfg.use_combo_trigger.unwrap()     ;
189    }
190    if cfg.combo_trigger_type.is_some() {
191      self.global_trigger_type     = cfg.combo_trigger_type.unwrap()    ;
192    }
193    if cfg.combo_trigger_prescale.is_some() {
194      self.global_trigger_prescale = cfg.combo_trigger_prescale.unwrap();
195    }
196    if cfg.trace_suppression.is_some() {
197      self.trace_suppression       = cfg.trace_suppression.unwrap()     ;
198    }
199    if cfg.mtb_moni_interval.is_some() {
200      self.mtb_moni_interval       = cfg.mtb_moni_interval.unwrap() as u64;
201    }
202    if cfg.tiu_ignore_busy.is_some() {
203      self.tiu_ignore_busy         = cfg.tiu_ignore_busy.unwrap()       ;
204    }
205    if cfg.hb_send_interval.is_some() {
206      self.hb_send_interval        = cfg.hb_send_interval.unwrap()  as u64;
207    }
208  }
209}
210
211impl fmt::Display for MTBSettings {
212  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
213    let disp = toml::to_string(self).unwrap_or(
214      String::from("-- DESERIALIZATION ERROR! --"));
215    write!(f, "<MTBSettings :\n{}>", disp)
216  }
217}
218
219impl Default for MTBSettings {
220  fn default() -> Self {
221    Self::new()
222  }
223}
224
225#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
226pub struct PreampSettings {
227  /// actually apply the below settings
228  pub set_preamp_voltages    : bool,
229  /// liftof-cc will send commands to set the 
230  /// preamp bias voltages
231  pub set_strategy           : ParameterSetStrategy,
232  /// preamp biases (one set of 16 values per RAT
233  pub rat_preamp_biases      : HashMap<String, [f32;16]>
234}
235
236impl PreampSettings {
237  pub fn new() -> Self {
238    //let default_biases = HashMap::<u8, [f32;16]>::new();
239    let default_biases = HashMap::from([
240      (String::from("RAT01"), [58.0;16]),
241      (String::from("RAT02"), [58.0;16]),
242      (String::from("RAT03"), [58.0;16]),
243      (String::from("RAT04"), [58.0;16]),
244      (String::from("RAT05"), [58.0;16]),
245      (String::from("RAT06"), [58.0;16]),
246      (String::from("RAT07"), [58.0;16]),
247      (String::from("RAT08"), [58.0;16]),
248      (String::from("RAT09"), [58.0;16]),
249      (String::from("RAT10"), [58.0;16]),
250      (String::from("RAT11"), [58.0;16]),
251      (String::from("RAT12"), [58.0;16]),
252      (String::from("RAT13"), [58.0;16]),
253      (String::from("RAT14"), [58.0;16]),
254      (String::from("RAT15"), [58.0;16]),
255      (String::from("RAT16"), [58.0;16]),
256      (String::from("RAT17"), [58.0;16]),
257      (String::from("RAT18"), [58.0;16]),
258      (String::from("RAT19"), [58.0;16]),
259      (String::from("RAT20"), [58.0;16])]);
260
261    Self {
262      set_preamp_voltages    : false,
263      set_strategy           : ParameterSetStrategy::ControlServer,
264      rat_preamp_biases      : default_biases,
265    }
266  }
267
268  #[cfg(feature="database")]
269  pub fn emit_pb_settings_packets(&self, rats : &HashMap<u8,RAT>) -> Vec<TofPacket> {
270    let mut packets = Vec::<TofPacket>::new();
271    for k in rats.keys() {
272      let rat          = &rats[&k];
273      let rat_key      = format!("RAT{:2}", rat);
274      let mut cmd      = TofCommandV2::new();
275      cmd.command_code = TofCommandCode::SetPreampBias;
276      let mut payload  = PreampBiasConfig::new();
277      payload.rb_id    = rat.rb2_id as u8;
278      if *k as usize >= self.rat_preamp_biases.len() {
279        error!("RAT ID {k} larger than 20!");
280        continue;
281      }
282      payload.biases = self.rat_preamp_biases[&rat_key];
283      cmd.payload = payload.to_bytestream();
284      let tp = cmd.pack();
285      packets.push(tp);
286    }
287    packets
288  }
289}
290
291impl fmt::Display for PreampSettings {
292  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
293    let disp : String;
294    match toml::to_string(self) {
295      Err(err) => {
296        error!("Deserialization error! {err}");
297        disp = String::from("-- DESERIALIZATION ERROR! --");
298      }
299      Ok(_disp) => {
300        disp = _disp;
301      }
302    }
303    write!(f, "<PreampBiasSettings :\n{}>", disp)
304  }
305}
306
307impl Default for PreampSettings {
308  fn default() -> Self {
309    Self::new()
310  }
311}
312
313#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
314pub struct LTBThresholdSettings {
315  /// actually apply the below settings
316  pub set_ltb_thresholds    : bool,
317  /// liftof-cc will send commands to set the 
318  /// ltb thresholds
319  pub set_strategy          : ParameterSetStrategy,
320  /// ltb threshold voltages (one set of 3 values per RAT)
321  pub rat_ltb_thresholds    : HashMap<String, [f32;3]>
322}
323
324impl LTBThresholdSettings {
325  pub fn new() -> Self {
326    let default_thresholds = HashMap::from([
327      (String::from("RAT01"), [40.0,32.0,375.0]),
328      (String::from("RAT02"), [40.0,32.0,375.0]),
329      (String::from("RAT03"), [40.0,32.0,375.0]),
330      (String::from("RAT04"), [40.0,32.0,375.0]),
331      (String::from("RAT05"), [40.0,32.0,375.0]),
332      (String::from("RAT06"), [40.0,32.0,375.0]),
333      (String::from("RAT07"), [40.0,32.0,375.0]),
334      (String::from("RAT08"), [40.0,32.0,375.0]),
335      (String::from("RAT09"), [40.0,32.0,375.0]),
336      (String::from("RAT10"), [40.0,32.0,375.0]),
337      (String::from("RAT11"), [40.0,32.0,375.0]),
338      (String::from("RAT12"), [40.0,32.0,375.0]),
339      (String::from("RAT13"), [40.0,32.0,375.0]),
340      (String::from("RAT14"), [40.0,32.0,375.0]),
341      (String::from("RAT15"), [40.0,32.0,375.0]),
342      (String::from("RAT16"), [40.0,32.0,375.0]),
343      (String::from("RAT17"), [40.0,32.0,375.0]),
344      (String::from("RAT18"), [40.0,32.0,375.0]),
345      (String::from("RAT19"), [40.0,32.0,375.0]),
346      (String::from("RAT20"), [40.0,32.0,375.0])]);
347
348      Self {
349        set_ltb_thresholds    : false,
350        set_strategy          : ParameterSetStrategy::ControlServer,
351        rat_ltb_thresholds    : default_thresholds,
352      }
353  }
354
355  #[cfg(feature="database")]
356  pub fn emit_ltb_settings_packets(&self, rats : &HashMap<u8,RAT>) -> Vec<TofPacket> {
357    let mut packets = Vec::<TofPacket>::new();
358    for k in rats.keys() {
359      let rat          = &rats[&k];
360      let rat_key      = format!("RAT{:2}", rat);
361      let mut cmd      = TofCommandV2::new();
362      cmd.command_code = TofCommandCode::SetLTBThresholds;
363      let mut payload  = LTBThresholdConfig::new();
364      payload.rb_id    = rat.rb1_id as u8;
365      if *k as usize >= self.rat_ltb_thresholds.len() {
366        error!("RAT ID {k} larger than 20!");
367        continue;
368      }
369      payload.thresholds = self.rat_ltb_thresholds[&rat_key];
370      cmd.payload = payload.to_bytestream();
371      let tp = cmd.pack();
372      packets.push(tp);
373    }
374    packets
375  }
376}
377
378impl fmt::Display for LTBThresholdSettings {
379  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
380    let disp : String;
381    match toml::to_string(self) {
382      Err(err) => {
383        error!("Deserialization error! {err}");
384        disp = String::from("-- DESERIALIZATION ERROR! --");
385      }
386      Ok(_disp) => {
387        disp = _disp;
388      }
389    }
390    write!(f, "<LTBThresholdSettings :\n{}>", disp)
391  }
392}
393
394impl Default for LTBThresholdSettings {
395  fn default() -> Self {
396    Self::new()
397  }
398}
399
400#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
401pub struct CommandDispatcherSettings {
402  /// Log all commands into this file
403  /// Set to "/dev/null" to turn off.
404  /// The mode will be always "append", since we don't 
405  /// expect a lot of logging
406  pub cmd_log_path               : String,
407  /// The address of the liftof-command & control server
408  /// that is the ip address on the RBNetwork which the 
409  /// liftof-cc instance runs on 
410  /// This address will be used as "PUB" for the CommandDispather
411  /// This address has to be within the RB network
412  pub cc_server_address          : String,   
413  /// The address ("tcp://xx.xx.xx.xx:xxxxx") the tof computer should subscribe to 
414  /// to get commands from the flight computer. This address is within the 
415  /// flight network
416  pub fc_sub_address             : String,
417  /// Interval of time that will elapse from a cmd check to the other
418  pub cmd_listener_interval_sec  : u64,
419  /// Safety mechanism - is this is on, the command listener will deny 
420  /// every request. E.g. in staging mode to guarantee no tinkering
421  pub deny_all_requests          : bool
422}
423
424impl CommandDispatcherSettings {
425  pub fn new() -> Self {
426    Self {
427      cmd_log_path                   : String::from("/home/gaps/log"),
428      cc_server_address              : String::from("tcp://10.0.1.10:42000"),   
429      fc_sub_address                 : String::from("tcp://192.168.37.200:41662"),
430      cmd_listener_interval_sec      : 1,
431      deny_all_requests              : false
432    }
433  }
434}
435
436impl fmt::Display for CommandDispatcherSettings {
437  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
438    let disp = toml::to_string(self).unwrap_or(
439      String::from("-- DESERIALIZATION ERROR! --"));
440    write!(f, "<CommandDispatcherSettings :\n{}>", disp)
441  }
442}
443
444impl Default for CommandDispatcherSettings {
445  fn default() -> Self {
446    Self::new()
447  }
448}
449
450/// Readout strategy for RB (onboard) (RAM) memory buffers
451#[derive(Clone, Copy, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
452pub enum RBBufferStrategy {
453  /// Readout and switch the buffers every
454  /// x events
455  NEvents(u16),
456  /// Adapt to the RB rate and readout the buffers
457  /// so that we get switch them every X seconds.
458  /// (Argument is in seconds
459  AdaptToRate(u16),
460}
461
462impl fmt::Display for RBBufferStrategy {
463  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
464    let r = serde_json::to_string(self).unwrap_or(
465      String::from("N.A. - Invalid RBBufferStrategy (error)"));
466    write!(f, "<RBBufferStrategy: {}>", r)
467  }
468}
469
470
471/// Settings for the specific clients on the RBs (liftof-rb)
472#[derive(Debug, Copy, Clone, serde::Serialize, serde::Deserialize)]
473pub struct RBSettings {
474  /// Don't send events if they have issues. Requires
475  /// EventStatus::Perfect. This can not work in the
476  /// OperationMode RBHighThroughput
477  pub only_perfect_events           : bool,
478  /// Calculate the crc32 sum for each channel and set
479  /// the EventStatus flag accordingly.
480  pub calc_crc32                    : bool,
481  /// tof operation mode - either "StreamAny",
482  /// "RequestReply" or "RBHighThroughput"
483  pub tof_op_mode                   : TofOperationMode,
484  /// if different from 0, activate RB self trigger
485  /// in poisson mode
486  pub trigger_poisson_rate          : u32,
487  /// if different from 0, activate RB self trigger 
488  /// with fixed rate setting
489  pub trigger_fixed_rate            : u32,
490  ///// if different from 0, activate RB self trigger
491  ///// in poisson mode
492  //pub trigger_poisson_rate    : u32,
493  ///// if different from 0, activate RB self trigger 
494  ///// with fixed rate setting
495  //pub trigger_fixed_rate      : u32,
496  /// Either "Physics" or a calibration related 
497  /// data type, e.g. "VoltageCalibration".
498  /// <div class="warning">This might get deprecated in a future version!</div>
499  pub data_type                     : DataType,
500  /// This allows for different strategies on how to readout 
501  /// the RB buffers. The following refers to the NEvent strategy.
502  /// The value when the readout of the RB buffers is triggered.
503  /// This number is in size of full events, which correspond to 
504  /// 18530 bytes. Maximum buffer size is a bit more than 3000 
505  /// events. Smaller buffer allows for a more snappy reaction, 
506  /// but might require more CPU resources (on the board)
507  /// For RBBufferStrategy::AdaptToRate(k), readout (and switch) the buffers every
508  /// k seconds. The size of the buffer will be determined
509  /// automatically depending on the rate.
510  pub rb_buff_strategy               : RBBufferStrategy,
511  /// The general moni interval. Whenever this time in seconds has
512  /// passed, the RB will send a RBMoniData packet
513  pub rb_moni_interval               : f32,
514  /// Powerboard monitoring. Do it every multiple of rb_moni_interval
515  pub pb_moni_every_x                : f32,
516  /// Preamp monitoring. Do it every multiple of rb_moni_interval
517  pub pa_moni_every_x                : f32,
518  /// LTB monitoring. Do it every multiple of rb_moni_interval
519  pub ltb_moni_every_x               : f32,
520  /// Choose between drs deadtime or fpga 
521  pub drs_deadtime_instead_fpga_temp : bool
522}
523
524impl RBSettings {
525  pub fn new() -> Self {
526    Self {
527      only_perfect_events            : false,
528      calc_crc32                     : false,
529      tof_op_mode                    : TofOperationMode::Default,
530      trigger_fixed_rate             : 0,
531      trigger_poisson_rate           : 0,
532      data_type                      : DataType::Physics,
533      rb_buff_strategy               : RBBufferStrategy::AdaptToRate(5),
534      rb_moni_interval               : 0.0,
535      pb_moni_every_x                : 0.0,
536      pa_moni_every_x                : 0.0,
537      ltb_moni_every_x               : 0.0,
538      drs_deadtime_instead_fpga_temp : false
539    }
540  }
541
542  pub fn from_tofrbconfig(&mut self, cfg : &TofRBConfig) {
543    if cfg.rb_moni_interval.is_some() {
544      self.rb_moni_interval = cfg.rb_moni_interval.unwrap() as f32;              
545    }
546    if cfg.rb_moni_interval.is_some() {
547      self.pb_moni_every_x  = cfg.pb_moni_every_x.unwrap() as f32;              
548    }
549    if cfg.rb_moni_interval.is_some() {
550      self.pa_moni_every_x  = cfg.pa_moni_every_x.unwrap() as f32;              
551    }
552    if cfg.rb_moni_interval.is_some() {
553      self.ltb_moni_every_x = cfg.ltb_moni_every_x.unwrap() as f32;              
554    }
555    if cfg.rb_moni_interval.is_some() {
556      self.drs_deadtime_instead_fpga_temp = cfg.drs_deadtime_instead_fpga_temp.unwrap(); 
557    }
558  }
559
560  pub fn get_runconfig(&self) -> RunConfig {
561    // missing here - run id, nevents, nseconds,
562    //
563    let mut rcfg              = RunConfig::new();
564    rcfg.is_active            = true;
565    rcfg.tof_op_mode          = self.tof_op_mode.clone();
566    rcfg.trigger_fixed_rate   = self.trigger_fixed_rate;
567    rcfg.trigger_poisson_rate = self.trigger_poisson_rate;
568    rcfg.data_type            = self.data_type.clone();
569    let buffer_trip : u16;
570    match self.rb_buff_strategy {
571      RBBufferStrategy::NEvents(buff_size) => {
572        buffer_trip = buff_size;
573      },
574      RBBufferStrategy::AdaptToRate(_) => {
575        // For now, let's just set the initial value to
576        // 50 FIXME
577        buffer_trip = 50;
578        //match rate = get_trigger_rate() {
579        //  Err(err) {
580        //    error!("Unable to obtain trigger rate!");
581        //    buffer_trip = 50;
582        //  },
583        //  Ok(rate) => {
584        //    buffer_trip = rate*n_secs as u16;
585        //  }
586        //}
587      }
588    }
589    rcfg.rb_buff_size         = buffer_trip as u16;
590    rcfg
591  }
592}
593
594impl fmt::Display for RBSettings {
595  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
596    let disp = toml::to_string(self).unwrap_or(
597      String::from("-- DESERIALIZATION ERROR! --"));
598    write!(f, "<RBSettings :\n{}>", disp)
599  }
600}
601
602impl Default for RBSettings {
603  fn default() -> Self {
604    Self::new()
605  }
606}
607
608
609/// Settings to change the configuration of the analysis engine 
610/// (pulse extraction)
611#[derive(Debug, Copy, Clone, serde::Serialize, serde::Deserialize)]
612pub struct AnalysisEngineSettings {
613  /// pulse integration start
614  pub integration_start      : f32,
615  /// pulse integration window
616  pub integration_window     : f32, 
617  /// Pedestal threshold
618  pub pedestal_thresh        : f32,
619  /// Pedestal begin bin
620  pub pedestal_begin_bin     : usize,
621  /// Pedestal width (bins)
622  pub pedestal_win_bins      : usize,
623  /// Use a zscore algorithm to find the peaks instead
624  /// of Jeff's algorithm
625  pub use_zscore             : bool,
626  /// Peakfinding start time
627  pub find_pks_t_start       : f32,
628  /// Peakfinding window
629  pub find_pks_t_window      : f32,
630  /// Minimum peaksize (bins)
631  pub min_peak_size          : usize,
632  /// Threshold for peak recognition
633  pub find_pks_thresh        : f32,
634  /// Max allowed peaks
635  pub max_peaks              : usize,
636  /// Timing CFG fraction
637  pub cfd_fraction           : f32
638}
639
640impl AnalysisEngineSettings {
641  pub fn new() -> Self {
642    Self {
643      integration_start         : 270.0,
644      integration_window        : 70.0, 
645      pedestal_thresh           : 10.0,
646      pedestal_begin_bin        : 10,
647      pedestal_win_bins         : 50,
648      use_zscore                : false,
649      find_pks_t_start          : 270.0,
650      find_pks_t_window         : 70.0,
651      min_peak_size             : 3,
652      find_pks_thresh           : 10.0,
653      max_peaks                 : 5,
654      cfd_fraction              : 0.2
655    }
656  }
657}
658
659impl fmt::Display for AnalysisEngineSettings {
660  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
661    let disp = toml::to_string(self).unwrap_or(
662      String::from("-- DESERIALIZATION ERROR! --"));
663    write!(f, "<AnalysisEngineSettings :\n{}>", disp)
664  }
665}
666
667impl Default for AnalysisEngineSettings {
668  fn default() -> Self {
669    Self::new()
670  }
671}
672
673/// Settings to change the configuration of the TOF Eventbuilder
674#[derive(Debug, Copy, Clone, serde::Serialize, serde::Deserialize)]
675pub struct TofEventBuilderSettings {
676  pub cachesize           : u32,
677  pub n_mte_per_loop      : u32,
678  pub n_rbe_per_loop      : u32,
679  /// The timeout parameter for the TofEvent. If not
680  /// complete after this time, send it onwards anyway
681  pub te_timeout_sec      : u32,
682  /// try to sort the events by id (uses more resources)
683  pub sort_events         : bool,
684  pub build_strategy      : BuildStrategy,
685  pub greediness          : u8,
686  pub wait_nrb            : u8,
687  pub hb_send_interval    : u16,
688  /// Allows to restrict saving the event to disk
689  /// based on the interesting event parameters
690  /// (These are minimum values)
691  pub only_save_interesting : bool,
692  pub thr_n_hits_umb        : u8,
693  pub thr_n_hits_cbe        : u8,
694  pub thr_n_hits_cor        : u8,
695  pub thr_tot_edep_umb      : f32,
696  pub thr_tot_edep_cbe      : f32,
697  pub thr_tot_edep_cor      : f32,
698}
699
700impl TofEventBuilderSettings {
701  pub fn new() -> TofEventBuilderSettings {
702    TofEventBuilderSettings {
703      cachesize             : 100000,
704      n_mte_per_loop        : 1,
705      n_rbe_per_loop        : 40,
706      te_timeout_sec        : 30,
707      sort_events           : false,
708      build_strategy        : BuildStrategy::Adaptive,
709      greediness            : 3,
710      wait_nrb              : 40,
711      hb_send_interval      : 30,
712      only_save_interesting : false,
713      thr_n_hits_umb        : 0,
714      thr_n_hits_cbe        : 0,
715      thr_n_hits_cor        : 0,
716      thr_tot_edep_umb      : 0.0,
717      thr_tot_edep_cbe      : 0.0,
718      thr_tot_edep_cor      : 0.0,
719    }
720  }
721
722  pub fn from_tofeventbuilderconfig(&mut self, cfg : &TOFEventBuilderConfig) {
723    if cfg.cachesize.is_some() {
724      self.cachesize = cfg.cachesize.unwrap();
725    }
726    if cfg.n_mte_per_loop.is_some() {
727      self.n_mte_per_loop = cfg.n_mte_per_loop.unwrap();
728    }
729    if cfg.n_rbe_per_loop.is_some() {
730      self.n_rbe_per_loop = cfg.n_rbe_per_loop.unwrap();
731    }
732    if cfg.te_timeout_sec.is_some() {
733      self.te_timeout_sec = cfg.te_timeout_sec.unwrap();
734    }
735    if cfg.sort_events.is_some() {
736      self.sort_events = cfg.sort_events.unwrap();
737    }
738    if cfg.build_strategy.is_some() {
739      self.build_strategy = cfg.build_strategy.unwrap();
740    }
741    if cfg.greediness.is_some() {
742      self.greediness = cfg.greediness.unwrap();
743    }
744    if cfg.wait_nrb.is_some() {
745      self.wait_nrb = cfg.wait_nrb.unwrap();
746    }
747    if cfg.hb_send_interval.is_some() {
748      self.hb_send_interval = cfg.hb_send_interval.unwrap();
749    }
750    if cfg.only_save_interesting.is_some() {
751      self.only_save_interesting = cfg.only_save_interesting.unwrap();
752    }
753    if cfg.thr_n_hits_umb.is_some() { 
754      self.thr_n_hits_umb = cfg.thr_n_hits_umb.unwrap();
755    }
756    if cfg.thr_n_hits_cbe.is_some() {      
757      self.thr_n_hits_cbe = cfg.thr_n_hits_cbe.unwrap();
758    }
759    if cfg.thr_n_hits_cor.is_some()   {
760      self.thr_n_hits_cor = cfg.thr_n_hits_cor.unwrap();
761    }
762    if cfg.thr_tot_edep_umb.is_some() {    
763      self.thr_tot_edep_umb = cfg.thr_tot_edep_umb.unwrap();
764    }
765    if cfg.thr_tot_edep_cbe.is_some() {    
766      self.thr_tot_edep_cbe = cfg.thr_tot_edep_cbe.unwrap();
767    }
768    if cfg.thr_tot_edep_cor.is_some() {    
769      self.thr_tot_edep_cor = cfg.thr_tot_edep_cor.unwrap();
770    }
771  }
772}
773
774impl fmt::Display for TofEventBuilderSettings {
775  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
776    let disp = toml::to_string(self).unwrap_or(
777      String::from("-- DESERIALIZATION ERROR! --"));
778    write!(f, "<TofEventBuilderSettings :\n{}>", disp)
779  }
780}
781
782impl Default for TofEventBuilderSettings {
783  fn default() -> Self {
784    Self::new()
785  }
786}
787
788/// Configure data storage and packet publishing
789#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
790pub struct DataPublisherSettings {
791  /// location to store data on TOF computer
792  pub data_dir                  : String,
793  /// The data written on disk gets divided into 
794  /// files of a fixed size. 
795  pub mbytes_per_file           : usize,
796  /// The address the flight computer should subscribe 
797  /// to to get tof packets
798  pub fc_pub_address            : String,
799  /// Mark a certain fraction of events as to be discarded, 
800  /// that is not to be stored on disk
801  /// 1 = Throw away all events, 0 = throw away no events
802  pub discard_event_fraction    : f32,
803  ///// Don't save events which are non-interesting
804  //pub discard_non_interesting   : bool,
805  //pub filter_interesting_numb   : u8,
806  //pub filter_interesting_ncbe   : u8,
807  //pub filter_interesting_n
808  /// Send also MastertriggerPackets (this should be 
809  /// turned off in flight - only useful if 
810  /// send_flight_packets is true, otherwise
811  /// MTB events will get sent as part of TofEvents
812  pub send_mtb_event_packets    : bool,
813  /// switch off waveform sending (in case of we 
814  /// are sending flight packets)
815  pub send_rbwaveform_packets   : bool,
816  /// send only a fraction of all RBWaveform packets
817  /// 1 = all events, 1000 = every 1/1000 event
818  pub send_rbwf_every_x_event   : u32,
819  pub send_tof_summary_packets  : bool,
820  pub send_tof_event_packets    : bool,
821  /// Send the RBCalibration to ground
822  pub send_cali_packets         : bool,
823  pub hb_send_interval          : u16,
824}
825
826impl DataPublisherSettings {
827  pub fn new() -> Self {
828    Self {
829      data_dir                  : String::from(""),
830      mbytes_per_file           : 420,
831      fc_pub_address            : String::from(""),
832      discard_event_fraction    : 0.0,
833      send_mtb_event_packets    : false,
834      send_rbwaveform_packets   : false,
835      send_rbwf_every_x_event   : 1,
836      send_tof_summary_packets  : true,
837      send_tof_event_packets    : false,
838      send_cali_packets         : true,
839      hb_send_interval          : 30,
840    }
841  }
842  
843  pub fn from_datapublisherconfig(&mut self, cfg : &DataPublisherConfig) {
844    if cfg.mbytes_per_file.is_some() {
845      self.mbytes_per_file = cfg.mbytes_per_file.unwrap() as usize;
846    }
847    if cfg.discard_event_fraction.is_some() {
848      self.discard_event_fraction = cfg.discard_event_fraction.unwrap();
849    }
850    if cfg.send_mtb_event_packets.is_some() {
851      self.send_mtb_event_packets = cfg.send_mtb_event_packets.unwrap();
852    }
853    if cfg.send_rbwaveform_packets.is_some() {
854      self.send_rbwaveform_packets = cfg.send_rbwaveform_packets.unwrap();
855    }
856    if cfg.send_rbwf_every_x_event.is_some() {
857      self.send_rbwf_every_x_event = cfg.send_rbwf_every_x_event.unwrap();
858    }
859    if cfg.send_tof_summary_packets.is_some() {
860      self.send_tof_summary_packets = cfg.send_tof_summary_packets.unwrap();
861    }
862    if cfg.send_tof_event_packets.is_some() {
863      self.send_tof_event_packets = cfg.send_tof_event_packets.unwrap();
864    }
865    if cfg.hb_send_interval.is_some() {
866      self.hb_send_interval = cfg.hb_send_interval.unwrap();
867    }
868  }
869}
870
871impl fmt::Display for DataPublisherSettings {
872  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
873    let disp = toml::to_string(self).unwrap_or(
874      String::from("-- DESERIALIZATION ERROR! --"));
875    write!(f, "<DataPublisherSettings :\n{}>", disp)
876  }
877}
878
879impl Default for DataPublisherSettings {
880  fn default() -> Self {
881    Self::new()
882  }
883}
884
885
886#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
887pub struct LiftofSettings {
888  /// read run .toml files from this directory and 
889  /// automotically work through them 1by1
890  pub staging_dir                : String,
891  /// default location for RBCalibration files
892  pub calibration_dir            : String,
893  /// default location for the database
894  pub db_path                    : String,
895  /// Runtime in seconds
896  pub runtime_sec                : u64,
897  /// The UDP port to be used to get packets from the 
898  /// MTB
899  pub mtb_address                : String,
900  /// The interval (in seconds) to retrive CPUMoniData from 
901  /// the TOF CPU
902  pub cpu_moni_interval_sec      : u64,
903  /// In an intervall from 1-50, these RB simply do not exist
904  /// or might have never existed. Always ingore these
905  pub rb_ignorelist_always       : Vec<u8>,
906  /// ignore these specific RB for this run
907  pub rb_ignorelist_run          : Vec<u8>,
908  /// Should TofHits be generated?
909  pub run_analysis_engine        : bool,
910  /// Run a full RB calibration before run 
911  /// start?
912  pub pre_run_calibration        : bool,
913  /// Should the waveforms which go into te calibration 
914  /// be saved in the package?
915  pub save_cali_wf               : bool,
916  /// Do a verification run before each run? The purpose 
917  /// of the verification run is to generate a "DetectorStatus"
918  /// packet. If a verification run is desired, change this 
919  /// number to the number of seconds to do the verification 
920  /// run
921  pub verification_runtime_sec   : u32,
922  /// Settings to control the MTB
923  pub mtb_settings               : MTBSettings,
924  /// Settings for the TOF event builder
925  pub event_builder_settings     : TofEventBuilderSettings,
926  /// Settings for the analysis engine
927  pub analysis_engine_settings   : AnalysisEngineSettings,
928  /// Configure data publshing and saving on local disc
929  pub data_publisher_settings    : DataPublisherSettings,
930  /// Configure cmmand reception and sending
931  pub cmd_dispatcher_settings    : CommandDispatcherSettings,
932  /// Settings for the individual RBs
933  pub rb_settings                : RBSettings,
934  /// Mask individual channels (e.g. dead preamps) 
935  /// for the readout boards
936  pub rb_channel_mask            : ChannelMaskSettings,
937  /// Preamp configuration
938  pub preamp_settings            : PreampSettings,
939  /// LTB threshold configuration
940  pub ltb_settings               : LTBThresholdSettings
941}
942
943impl LiftofSettings {
944  pub fn new() -> Self {
945    LiftofSettings {
946      staging_dir               : String::from("/home/gaps/liftof-staging"),
947      calibration_dir           : String::from(""),
948      db_path                   : String::from("/home/gaps/config/gaps_flight.db"),
949      runtime_sec               : 0,
950      mtb_address               : String::from("10.0.1.10:50001"),
951      cpu_moni_interval_sec     : 60,
952      rb_ignorelist_always      : Vec::<u8>::new(),
953      rb_ignorelist_run         : Vec::<u8>::new(),
954      run_analysis_engine       : true,
955      pre_run_calibration       : false,
956      save_cali_wf              : false,
957      verification_runtime_sec  : 0, // no verification run per default
958      mtb_settings              : MTBSettings::new(),
959      event_builder_settings    : TofEventBuilderSettings::new(),
960      analysis_engine_settings  : AnalysisEngineSettings::new(),
961      data_publisher_settings   : DataPublisherSettings::new(),
962      cmd_dispatcher_settings   : CommandDispatcherSettings::new(),
963      rb_settings               : RBSettings::new(),
964      rb_channel_mask           : ChannelMaskSettings::new(),
965      preamp_settings           : PreampSettings::new(),
966      ltb_settings              : LTBThresholdSettings::new(),
967    }
968  }  
969
970  /// Change the settings according to the ones in the 
971  /// given config 
972  pub fn from_tofrunconfig(&mut self, cfg : &TofRunConfig) {
973    if cfg.runtime.is_some() {
974      self.runtime_sec = cfg.runtime.unwrap() as u64;
975    }
976  }
977
978  /// Change a value by giving the specific key as 
979  /// a string, the value then will be parsed 
980  /// accordingly
981  #[deprecated(since="0.10.0", note="This is a dev deadend and will be nuked!")]
982  pub fn set_by_key(&mut self, key : &str, value : String) {
983    match key {
984      "runtime_sec"               => {
985        if let Ok(val) = value.parse::<u64>() {
986          self.runtime_sec = val;
987        } else {
988          error!("Unable to parse {value}!");
989        }
990      }
991      "cpu_moni_interval_sec"     => {
992        if let Ok(val) = value.parse::<u64>() {
993          self.cpu_moni_interval_sec = val;
994        } else {
995          error!("Unable to parse {value}!");
996        }
997      }
998      "rb_ignorelist_run"         => {
999      }
1000      "run_analysis_engine"       => {
1001        if let Ok(val) = value.parse::<bool>() {
1002          self.run_analysis_engine = val;
1003        } else {
1004          error!("Unable to parse {value}!");
1005        }
1006      }
1007      "pre_run_calibration"       => {
1008        if let Ok(val) = value.parse::<bool>() {
1009          self.pre_run_calibration = val;
1010        } else {
1011          error!("Unable to parse {value}!");
1012        }
1013      }
1014      "save_cali_wf"              => {
1015        if let Ok(val) = value.parse::<bool>() {
1016          self.save_cali_wf = val;
1017        } else {
1018          error!("Unable to parse {value}!");
1019        }
1020      }
1021      "verification_runtime_sec"  => {
1022        if let Ok(val) = value.parse::<u32>() {
1023          self.verification_runtime_sec = val;
1024        } else {
1025          error!("Unable to parse {value}!");
1026        }
1027      }
1028      _ => error!("Set by key for {} is not implemented!", key)
1029    }
1030  }
1031
1032  /// Write the settings to a toml file
1033  pub fn to_toml(&self, mut filename : String) {
1034    if !filename.ends_with(".toml") {
1035      filename += ".toml";
1036    }
1037    info!("Will write to file {}!", filename);
1038    match File::create(&filename) {
1039      Err(err) => {
1040        error!("Unable to open file {}! {}", filename, err);
1041      }
1042      Ok(mut file) => {
1043        match toml::to_string_pretty(&self) {
1044          Err(err) => {
1045            error!("Unable to serialize toml! {err}");
1046          }
1047          Ok(toml_string) => {
1048            match file.write_all(toml_string.as_bytes()) {
1049              Err(err) => error!("Unable to write to file {}! {}", filename, err),
1050              Ok(_)    => debug!("Wrote settings to {}!", filename)
1051            }
1052          }
1053        }
1054      }
1055    }
1056  }
1057
1058  /// Write the settings to a json file
1059  pub fn to_json(&self, mut filename : String) {
1060    if !filename.ends_with(".json") {
1061      filename += ".json";
1062    }
1063    info!("Will write to file {}!", filename);
1064    match File::create(&filename) {
1065      Err(err) => {
1066        error!("Unable to open file {}! {}", filename, err);
1067      }
1068      Ok(file) => {
1069        match serde_json::to_writer_pretty(file, &self) {
1070          Err(err) => {
1071            error!("Unable to serialize json! {err}");
1072          }
1073          Ok(_) => debug!("Wrote settings to {}!", filename)
1074        }
1075      }
1076    }
1077  }
1078
1079  pub fn from_toml(filename : &str) -> Result<LiftofSettings, SerializationError> {
1080    match File::open(filename) {
1081      Err(err) => {
1082        error!("Unable to open {}! {}", filename, err);
1083        return Err(SerializationError::TomlDecodingError);
1084      }
1085      Ok(mut file) => {
1086        let mut toml_string = String::from("");
1087        match file.read_to_string(&mut toml_string) {
1088          Err(err) => {
1089            error!("Unable to read {}! {}", filename, err);
1090            return Err(SerializationError::TomlDecodingError);
1091          }
1092          Ok(_) => {
1093            match toml::from_str(&toml_string) {
1094              Err(err) => {
1095                error!("Can't interpret toml! {}", err);
1096                return Err(SerializationError::TomlDecodingError);
1097              }
1098              Ok(settings) => {
1099                return Ok(settings);
1100              }
1101            }
1102          }
1103        }
1104      }
1105    }
1106  }
1107}
1108
1109impl fmt::Display for LiftofSettings {
1110  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1111    let disp : String;
1112    match toml::to_string(self) {
1113      Err(err) => {
1114        println!("Deserialization error! {err}");
1115        disp = String::from("-- DESERIALIZATION ERROR! --");
1116      }
1117      Ok(_disp) => {
1118        disp = _disp;
1119      }
1120    }
1121    write!(f, "<LiftofSettings :\n{}>", disp)
1122  }
1123}
1124
1125impl Default for LiftofSettings {
1126  fn default() -> Self {
1127    Self::new()
1128  }
1129}
1130
1131/// Readoutboard configuration for a specific run
1132#[derive(Debug, Copy, Clone, PartialEq, serde::Deserialize, serde::Serialize)]
1133pub struct LiftofRBConfig {
1134  /// limit run time to number of seconds
1135  pub nseconds                : u32,
1136  /// tof operation mode - either "StreamAny",
1137  /// "RequestReply" or "RBHighThroughput"
1138  pub tof_op_mode             : TofOperationMode,
1139  /// if different from 0, activate RB self trigger
1140  /// in poisson mode
1141  pub trigger_poisson_rate    : u32,
1142  /// if different from 0, activate RB self trigger 
1143  /// with fixed rate setting
1144  pub trigger_fixed_rate      : u32,
1145  /// Either "Physics" or a calibration related 
1146  /// data type, e.g. "VoltageCalibration".
1147  /// <div class="warning">This might get deprecated in a future version!</div>
1148  pub data_type               : DataType,
1149  /// The value when the readout of the RB buffers is triggered.
1150  /// This number is in size of full events, which correspond to 
1151  /// 18530 bytes. Maximum buffer size is a bit more than 3000 
1152  /// events. Smaller buffer allows for a more snappy reaction, 
1153  /// but might require more CPU resources (on the board)
1154  pub rb_buff_size            : u16
1155}
1156
1157impl LiftofRBConfig {
1158
1159  pub fn new() -> Self {
1160    Self {
1161      nseconds                : 0,
1162      tof_op_mode             : TofOperationMode::Default,
1163      trigger_poisson_rate    : 0,
1164      trigger_fixed_rate      : 0,
1165      data_type               : DataType::Unknown, 
1166      rb_buff_size            : 0,
1167    }
1168  }
1169}
1170
1171impl Serialization for LiftofRBConfig {
1172  const HEAD               : u16   = 43690; //0xAAAA
1173  const TAIL               : u16   = 21845; //0x5555
1174  const SIZE               : usize = 24; // bytes including HEADER + FOOTER
1175  
1176  fn from_bytestream(bytestream : &Vec<u8>,
1177                     pos        : &mut usize)
1178    -> Result<Self, SerializationError> {
1179    let mut pars = Self::new();
1180    Self::verify_fixed(bytestream, pos)?;
1181    pars.nseconds                = parse_u32 (bytestream, pos);
1182    pars.tof_op_mode           
1183      = TofOperationMode::try_from(
1184          parse_u8(bytestream, pos))
1185      .unwrap_or_else(|_| TofOperationMode::Unknown);
1186    pars.trigger_poisson_rate    = parse_u32 (bytestream, pos);
1187    pars.trigger_fixed_rate      = parse_u32 (bytestream, pos);
1188    pars.data_type    
1189      = DataType::try_from(parse_u8(bytestream, pos))
1190      .unwrap_or_else(|_| DataType::Unknown);
1191    pars.rb_buff_size = parse_u16(bytestream, pos);
1192    *pos += 2; // for the tail 
1193    //_ = parse_u16(bytestream, pos);
1194    Ok(pars)
1195  }
1196  
1197  fn to_bytestream(&self) -> Vec<u8> {
1198    let mut stream = Vec::<u8>::with_capacity(Self::SIZE);
1199    stream.extend_from_slice(&Self::HEAD.to_le_bytes());
1200    stream.extend_from_slice(&self.  nseconds.to_le_bytes());
1201    stream.extend_from_slice(&(self.tof_op_mode as u8).to_le_bytes());
1202    stream.extend_from_slice(&self.trigger_poisson_rate.to_le_bytes());
1203    stream.extend_from_slice(&self.trigger_fixed_rate.to_le_bytes());
1204    stream.extend_from_slice(&(self.data_type as u8).to_le_bytes());
1205    stream.extend_from_slice(&self.rb_buff_size.to_le_bytes());
1206    stream.extend_from_slice(&Self::TAIL.to_le_bytes());
1207    stream
1208  }
1209}
1210
1211impl Default for LiftofRBConfig {
1212  fn default() -> Self {
1213    Self::new()
1214  }
1215}
1216
1217impl fmt::Display for LiftofRBConfig {
1218  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1219    write!(f, 
1220"<LiftofRBConfig -- is_active : true
1221    nseconds     : {}
1222    TOF op. mode : {}
1223    data type    : {}
1224    tr_poi_rate  : {}
1225    tr_fix_rate  : {}
1226    buff size    : {} [events]>",
1227      self.nseconds,
1228      self.tof_op_mode,
1229      self.data_type,
1230      self.trigger_poisson_rate,
1231      self.trigger_fixed_rate,
1232      self.rb_buff_size)
1233  }
1234}
1235
1236//#[cfg(feature = "random")]
1237//impl FromRandom for LiftofRBConfig {
1238//    
1239//  fn from_random() -> Self {
1240//    let mut cfg = Self::new();
1241//    let mut rng  = rand::thread_rng();
1242//    cfg.runid                   = rng.gen::<u32>();
1243//    cfg.is_active               = rng.gen::<bool>();
1244//    cfg.nevents                 = rng.gen::<u32>();
1245//    cfg.nseconds                = rng.gen::<u32>();
1246//    cfg.tof_op_mode             = TofOperationMode::from_random();
1247//    cfg.trigger_poisson_rate    = rng.gen::<u32>();
1248//    cfg.trigger_fixed_rate      = rng.gen::<u32>();
1249//    cfg.data_type               = DataType::from_random();
1250//    cfg.rb_buff_size            = rng.gen::<u16>();
1251//    cfg
1252//  }
1253//}
1254//
1255//#[cfg(feature = "random")]
1256//#[test]
1257//fn serialization_runconfig() {
1258//  for k in 0..100 {
1259//    let cfg  = LiftofRBConfig::from_random();
1260//    let test = LiftofRBConfig::from_bytestream(&cfg.to_bytestream(), &mut 0).unwrap();
1261//    assert_eq!(cfg, test);
1262//
1263//    let cfg_json = serde_json::to_string(&cfg).unwrap();
1264//    let test_json 
1265//      = serde_json::from_str::<LiftofRBConfig>(&cfg_json).unwrap();
1266//    assert_eq!(cfg, test_json);
1267//  }
1268//}
1269// #[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
1270// pub struct ChannelMaskSettings {
1271//   /// actually apply the below settings
1272//   pub set_channel_mask   : bool,
1273//   /// liftof-cc will send commands to set the 
1274//   /// preamp bias voltages
1275//   pub set_strategy           : ParameterSetStrategy,
1276//   /// channels to mask (one set of 18 values per RAT)
1277//   pub rat_channel_mask     : HashMap<String, [bool;18]>
1278// }
1279
1280/// Ignore RB channnels
1281///
1282/// The values in these arrays correspond to 
1283/// (physical) channels 1-9
1284#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
1285pub struct ChannelMaskSettings {
1286  /// actually apply the below settings
1287  pub set_channel_mask   : bool,
1288  /// The set strat defines who should acutally set
1289  /// the parameters. Will that be done by each board
1290  /// independently (ParameterSetStrategy::Board) or
1291  /// will a command be sent by liftof-cc 
1292  /// (ParameterSetStrategy::ControlServer)
1293  pub set_strategy           : ParameterSetStrategy,
1294  /// channels to mask (one set of 9 values per RB)
1295  /// "true" means the channel is enabled, "false", 
1296  /// disabled
1297  pub rb_channel_mask     : HashMap<String, [bool;9]>
1298}
1299
1300impl ChannelMaskSettings {
1301  pub fn new() -> Self {
1302    let mut default_thresholds = HashMap::<String, [bool; 9]>::new();
1303    for k in 1..51 {
1304      let key = format!("RB{k:02}");
1305      default_thresholds.insert(key, [true;9]);
1306    }
1307//    let default_thresholds = HashMap::from([
1308//      (String::from("RAT01"), [false; 9]),
1309//      (String::from("RAT02"), [false; 9]),
1310//      (String::from("RAT03"), [false; 9]),
1311//      (String::from("RAT04"), [false; 9]),
1312//      (String::from("RAT05"), [false; 9]),
1313//      (String::from("RAT06"), [false; 9]),
1314//      (String::from("RAT07"), [false; 9]),
1315//      (String::from("RAT08"), [false; 9]),
1316//      (String::from("RAT09"), [false; 9]),
1317//      (String::from("RAT10"), [false; 9]),
1318//      (String::from("RAT11"), [false; 9]),
1319//      (String::from("RAT12"), [false; 9]),
1320//      (String::from("RAT13"), [false; 9]),
1321//      (String::from("RAT14"), [false; 9]),
1322//      (String::from("RAT15"), [false; 9]),
1323//      (String::from("RAT16"), [false; 9]),
1324//      (String::from("RAT17"), [false; 9]),
1325//      (String::from("RAT18"), [false; 9]),
1326//      (String::from("RAT19"), [false; 9]),
1327//      (String::from("RAT20"), [false; 9])]);
1328
1329      Self {
1330        set_channel_mask    : false,
1331        set_strategy          : ParameterSetStrategy::ControlServer,
1332        rb_channel_mask    : default_thresholds,
1333      }
1334  }
1335
1336  #[cfg(feature="database")]
1337  pub fn emit_ch_mask_packets(&self, rbs : &HashMap<u8,RAT>) -> Vec<TofPacket> {
1338    let mut packets = Vec::<TofPacket>::new();
1339    for k in rbs.keys() {
1340      let rb          = &rbs[&k];
1341      let rb_key      = format!("RB{:2}", rb);
1342      let mut cmd      = TofCommandV2::new();
1343      cmd.command_code = TofCommandCode::SetRBChannelMask;
1344      let mut payload  = RBChannelMaskConfig::new();
1345      payload.rb_id    = rb.rb2_id as u8;
1346      if *k as usize >= self.rb_channel_mask.len() {
1347        error!("RB ID {k} larger than 46!");
1348        continue;
1349      }
1350      payload.channels = self.rb_channel_mask[&rb_key];
1351      cmd.payload = payload.to_bytestream();
1352      let tp = cmd.pack();
1353      packets.push(tp);
1354    }
1355    packets
1356  }
1357}
1358impl fmt::Display for ChannelMaskSettings {
1359  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1360    let disp : String;
1361    match toml::to_string(self) {
1362      Err(err) => {
1363        error!("Deserialization error! {err}");
1364        disp = String::from("-- DESERIALIZATION ERROR! --");
1365      }
1366      Ok(_disp) => {
1367        disp = _disp;
1368      }
1369    }
1370    write!(f, "<RBChannelMaskConfig :\n{}>", disp)
1371  }
1372}
1373
1374impl Default for ChannelMaskSettings {
1375  fn default() -> Self {
1376    Self::new()
1377  }
1378}
1379
1380#[cfg(feature="random")]
1381#[test]
1382fn mtb_config() {
1383
1384  use tof_dataclasses::FromRandom;
1385  for _ in 0..100 {
1386    let cfg  = TriggerConfig::from_random();
1387    let mut settings = MTBSettings::new();
1388    settings.from_triggerconfig(&cfg);
1389    let test = settings.emit_triggerconfig();
1390    if cfg.gaps_trigger_use_beta.is_some() {
1391      assert_eq!(cfg.gaps_trigger_use_beta, test.gaps_trigger_use_beta);
1392    }
1393    if cfg.prescale.is_some() {
1394      assert_eq!(cfg.prescale, test.prescale);
1395    }
1396    if cfg.trigger_type.is_some() {
1397      assert_eq!(cfg.trigger_type, test.trigger_type);
1398    }
1399    if cfg.use_combo_trigger.is_some() {
1400      assert_eq!(cfg.use_combo_trigger, test.use_combo_trigger);
1401    }
1402    if cfg.combo_trigger_type.is_some() {
1403      assert_eq!(cfg.combo_trigger_type, test.combo_trigger_type);
1404    }
1405    if cfg.combo_trigger_prescale.is_some() {
1406      assert_eq!(cfg.combo_trigger_prescale, test.combo_trigger_prescale);
1407    }
1408    if cfg.trace_suppression.is_some() {
1409      assert_eq!(cfg.trace_suppression, test.trace_suppression);
1410    }
1411    if cfg.mtb_moni_interval.is_some() {
1412      assert_eq!(cfg.mtb_moni_interval, test.mtb_moni_interval);
1413    }
1414    if cfg.tiu_ignore_busy.is_some() {
1415      assert_eq!(cfg.tiu_ignore_busy, test.tiu_ignore_busy);
1416    }
1417    if cfg.hb_send_interval.is_some() {
1418      assert_eq!(cfg.hb_send_interval, test.hb_send_interval);
1419    }
1420  }
1421}
1422
1423#[test]
1424fn write_config_file() {
1425  let settings = LiftofSettings::new();
1426  //println!("{}", settings);
1427  settings.to_toml(String::from("liftof-config-test.toml"));
1428}
1429
1430#[test]
1431fn read_config_file() {
1432  let _settings = LiftofSettings::from_toml("liftof-config-test.toml");
1433}
1434
1435