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