1pub mod control;
18pub mod registers;
19
20use std::sync::{
21 Arc,
22 Mutex,
23};
24
25use std::time::{
26 Duration,
27 Instant
28};
29
30use std::thread;
31use crossbeam_channel::Sender;
32use serde_json::json;
33
34use tof_dataclasses::packets::TofPacket;
35use tof_dataclasses::monitoring::MtbMoniData;
36use tof_dataclasses::events::MasterTriggerEvent;
37use tof_dataclasses::events::master_trigger::TriggerType;
38use tof_dataclasses::version::ProtocolVersion;
39use tof_dataclasses::errors::{
40 MasterTriggerError
41};
42
43use tof_dataclasses::ipbus::IPBus;
44
45use tof_dataclasses::heartbeats::MTBHeartbeat;
46use tof_dataclasses::serialization::Packable;
47
48use crate::thread_control::ThreadControl;
49
50pub use crate::settings::MTBSettings;
52
53use control::*;
54use registers::*;
55
56fn remove_from_word(s: String, word: &str) -> String {
58 if let Some(index) = s.find(word) {
59 s[..index].to_string()
61 } else {
62 s
64 }
65}
66
67fn read_until_footer(bus : &mut IPBus)
71 -> Result<Vec<u32>, MasterTriggerError> {
72 let mut data = Vec::<u32>::new();
73 loop {
74 let val = bus.read(0x11)?;
75 if val != 0xAAAAAAAA {
76 data.push(val);
77 }
78 if (val == 0x55555555) || (val == 0xAAAAAAAA) {
79 break;
80 }
81 }
82 Ok(data)
83}
84
85
86pub fn get_event(bus : &mut IPBus)
99 -> Option<Result<MasterTriggerEvent, MasterTriggerError>> {
100 let mut mte = MasterTriggerEvent::new();
101 let n_daq_words_fixed = 9u32;
102 let mut data : Vec<u32>;
103 loop {
104 if EVQ_SIZE.get(bus).ok()? < 12 {
115 return None;
116 } else {
117 break;
118 }
119 }
120 match bus.read_multiple(0x11, n_daq_words_fixed as usize, false) {
122 Ok(_data) => {
123 data = _data;
124 }
125 Err(err) => {
126 return Some(Err(err.into()));
127 }
128 }
129 if data.len() < 9 {
130 error!("Got MTB data, but the package ends before we get LTB information!");
133 warn!("Resetting master trigger DAQ");
134 match reset_daq(bus) {Err(err) => error!("Can not reset DAQ, error {err}"),
136 Ok(_) => ()
137 }
138 return Some(Err(MasterTriggerError::DataTooShort));
139 }
140 let n_ltb = data[8].count_ones();
141 let odd = n_ltb % 2 != 0;
144 let n_daq_words_flex : usize;
145 let n_hit_words : usize;
146 if odd {
148 n_hit_words = (n_ltb as usize + 1)/2;
149 } else {
150 n_hit_words = n_ltb as usize/2;
151 }
152 n_daq_words_flex = n_hit_words + 2; let mut data_flex : Vec<u32>;
155 match bus.read_multiple(0x11, n_daq_words_flex, false) {
156 Ok(_data) => {
157 data_flex = _data;
158 }
159 Err(err) => {
160 return Some(Err(err.into()));
161 }
162 }
163 data.append(&mut data_flex);
164 if data[0] != 0xAAAAAAAA {
165 error!("Got MTB data, but the header is incorrect {:x}", data[0]);
166 return Some(Err(MasterTriggerError::PackageHeaderIncorrect));
167 }
168
169 let n_daq_words = n_daq_words_fixed as usize + n_daq_words_flex;
170 let foot_pos = n_daq_words_fixed as usize + n_daq_words_flex - 1;
171 if data.len() != foot_pos + 1{
172 error!("Somehow the MTB DATA are misaligned! {}, {}", data.len(), foot_pos);
173 return Some(Err(MasterTriggerError::DataTooShort));
174 }
175 if data[foot_pos] != 0x55555555 {
176 error!("Got MTB data, but the footer is incorrect {:x}", data[foot_pos]);
177 if data[foot_pos] == 0xAAAAAAAA {
178 error!("Found next header, the package is TOO LONG! Attempt to fix for this event, but the next is LOST!");
179 info!("If we want to fix this, this whole mechanism needs a refactor and needs to fetch more thatn a single event at a time!");
180 read_until_footer(bus).ok()?;
182 data.pop();
184 } else {
185 let mut rest = read_until_footer(bus).ok()?;
187 data.append(&mut rest);
188 if data.len() != n_daq_words as usize + 1 {
189 error!("We tried to recover the event, however, that failed! Expected size of the packet {}, actual size {}", n_daq_words, data.len());
190 println!("-------------- DEBUG -------------------");
193 println!("N LTBs {} ({})", data[8].count_ones(), data[8]);
194 for k in data {
195 println!("-- {:x} ({})", k,k);
196 }
197 println!("--------------------");
198 return Some(Err(MasterTriggerError::PackageFooterIncorrect));
199 } else {
200 info!("Event recoveered!");
201 }
202 }
203 }
204
205 mte.event_id = data[1];
207 mte.timestamp = data[2];
208 mte.tiu_timestamp = data[3];
209 mte.tiu_gps32 = data[4];
210 mte.tiu_gps16 = (data[5] & 0x0000ffff) as u16;
211 mte.trigger_source = ((data[5] & 0xffff0000) >> 16) as u16;
212 let rbmask = (data[7] as u64) << 32 | data[6] as u64;
214 mte.mtb_link_mask = rbmask;
215 mte.dsi_j_mask = data[8];
216 for k in 9..9 + n_hit_words {
217 let ltb_hits = data[k as usize];
218 let first = (ltb_hits & 0x0000ffff) as u16;
220 let second = ((ltb_hits & 0xffff0000) >> 16) as u16;
221 mte.channel_mask.push(first);
222 if k == ( 9 + n_hit_words) {
225 if !odd {
226 mte.channel_mask.push(second);
227 }
228 } else {
229 mte.channel_mask.push(second);
230 }
231 }
232 Some(Ok(mte))
233}
234
235pub fn get_mtbmonidata(bus : &mut IPBus)
239 -> Result<MtbMoniData, MasterTriggerError> {
240 let mut moni = MtbMoniData::new();
241 let data = bus.read_multiple(0x120, 4, true)?;
242 if data.len() < 4 {
243 return Err(MasterTriggerError::BrokenPackage);
244 }
245 let tiu_busy_len = TIU_BUSY_LENGTH.get(bus)?;
246 let tiu_aux_link = (TIU_USE_AUX_LINK.get(bus)? != 0) as u8;
247 let tiu_emu_mode = (TIU_EMULATION_MODE.get(bus)? != 0) as u8;
248 let aggr_tiu = TIU_LT_AND_RB_MULT.get(bus)?;
249 let tiu_link_bad = (aggr_tiu & 0x1) as u8;
250 let tiu_busy_stuck = ((aggr_tiu & 0x2) >> 1) as u8;
251 let tiu_busy_ign = ((aggr_tiu & 0x4) >> 2) as u8;
252 let mut tiu_status = 0u8;
253 tiu_status = tiu_status | (tiu_emu_mode);
254 tiu_status = tiu_status | (tiu_aux_link << 1);
255 tiu_status = tiu_status | ((tiu_link_bad as u8) << 2);
256 tiu_status = tiu_status | (tiu_busy_stuck << 3);
257 tiu_status = tiu_status | (tiu_busy_ign << 4);
258 let daq_queue_len = EVQ_NUM_EVENTS.get(bus)? as u16;
259 moni.tiu_status = tiu_status;
260 moni.tiu_busy_len = tiu_busy_len;
261 moni.daq_queue_len = daq_queue_len;
262 let first_word = 0x00000fff;
264 let second_word = 0x0fff0000;
265 moni.temp = ( data[2] & first_word ) as u16;
266 moni.vccint = ((data[2] & second_word ) >> 16) as u16;
267 moni.vccaux = ( data[3] & first_word ) as u16;
268 moni.vccbram = ((data[3] & second_word ) >> 16) as u16;
269
270 let rate = bus.read_multiple(0x17, 2, true)?;
271 let mask = 0x0000ffff;
275 moni.rate = (rate[0] & mask) as u16;
276 moni.lost_rate = (rate[1] & mask) as u16;
277 let rb_lost_rate = RB_LOST_TRIGGER_RATE.get(bus)?;
278 if rb_lost_rate > 255 {
279 moni.rb_lost_rate = 255;
280 } else {
281 moni.rb_lost_rate = rb_lost_rate as u8;
282 }
283 Ok(moni)
284}
285
286pub fn configure_mtb(bus : &mut IPBus,
303 settings : &MTBSettings) -> Result<(), MasterTriggerError> {
304 let trace_suppression = settings.trace_suppression;
305 match set_trace_suppression(bus, trace_suppression) {
306 Err(err) => error!("Unable to set trace suppression mode! {err}"),
307 Ok(_) => {
308 if trace_suppression {
309 println!("==> Setting MTB to trace suppression mode!");
310 } else {
311 println!("==> Setting MTB to ALL_RB_READOUT mode!");
312 warn!("Reading out all events from all RBs! Data might be very large!");
313 }
314 }
315 }
316
317 let tiu_ignore_busy = settings.tiu_ignore_busy;
318 match TIU_BUSY_IGNORE.set(bus, tiu_ignore_busy as u32) {
319 Err(err) => error!("Unable to change tiu busy ignore settint! {err}"),
320 Ok(_) => {
321 if tiu_ignore_busy {
322 warn!("Ignoring TIU since tiu_busy_ignore is set in the config file!");
323 println!("==> Ignroing TIU since tiu_busy_ignore is set in the config file!");
324 }
325 }
326 }
327
328 info!("Settting rb integration window!");
329 let int_wind = settings.rb_int_window;
330 match set_rb_int_window(bus, int_wind) {
331 Err(err) => error!("Unable to set rb integration window! {err}"),
332 Ok(_) => {
333 info!("rb integration window set to {}", int_wind);
334 }
335 }
336
337 match unset_all_triggers(bus) {
338 Err(err) => error!("Unable to undo previous trigger settings! {err}"),
339 Ok(_) => ()
340 }
341 match settings.trigger_type {
342 TriggerType::Poisson => {
343 match set_poisson_trigger(bus,settings.poisson_trigger_rate) {
344 Err(err) => error!("Unable to set the POISSON trigger! {err}"),
345 Ok(_) => ()
346 }
347 }
348 TriggerType::Any => {
349 match set_any_trigger(bus,settings.trigger_prescale) {
350 Err(err) => error!("Unable to set the ANY trigger! {err}"),
351 Ok(_) => ()
352 }
353 }
354 TriggerType::Track => {
355 match set_track_trigger(bus, settings.trigger_prescale) {
356 Err(err) => error!("Unable to set the TRACK trigger! {err}"),
357 Ok(_) => ()
358 }
359 }
360 TriggerType::TrackCentral => {
361 match set_central_track_trigger(bus, settings.trigger_prescale) {
362 Err(err) => error!("Unable to set the CENTRAL TRACK trigger! {err}"),
363 Ok(_) => ()
364 }
365 }
366 TriggerType::TrackUmbCentral => {
367 match set_track_umb_central_trigger(bus, settings.trigger_prescale) {
368 Err(err) => error!("Unable to set the TRACK UMB CENTRAL trigger! {err}"),
369 Ok(_) => ()
370 }
371 }
372 TriggerType::Gaps => {
373 match set_gaps_trigger(bus, settings.gaps_trigger_use_beta) {
374 Err(err) => error!("Unable to set the GAPS trigger! {err}"),
375 Ok(_) => ()
376 }
377 }
378 TriggerType::Gaps633 => {
379 match set_gaps633_trigger(bus, settings.gaps_trigger_use_beta) {
380 Err(err) => error!("Unable to set the GAPS trigger! {err}"),
381 Ok(_) => ()
382 }
383 }
384 TriggerType::Gaps422 => {
385 match set_gaps422_trigger(bus, settings.gaps_trigger_use_beta) {
386 Err(err) => error!("Unable to set the GAPS trigger! {err}"),
387 Ok(_) => ()
388 }
389 }
390 TriggerType::Gaps211 => {
391 match set_gaps211_trigger(bus, settings.gaps_trigger_use_beta) {
392 Err(err) => error!("Unable to set the GAPS trigger! {err}"),
393 Ok(_) => ()
394 }
395 }
396 TriggerType::UmbCube => {
397 match set_umbcube_trigger(bus) {
398 Err(err) => error!("Unable to set UmbCube trigger! {err}"),
399 Ok(_) => ()
400 }
401 }
402 TriggerType::UmbCubeZ => {
403 match set_umbcubez_trigger(bus) {
404 Err(err) => error!("Unable to set UmbCubeZ trigger! {err}"),
405 Ok(_) => ()
406 }
407 }
408 TriggerType::UmbCorCube => {
409 match set_umbcorcube_trigger(bus) {
410 Err(err) => error!("Unable to set UmbCorCube trigger! {err}"),
411 Ok(_) => ()
412 }
413 }
414 TriggerType::CorCubeSide => {
415 match set_corcubeside_trigger(bus) {
416 Err(err) => error!("Unable to set CorCubeSide trigger! {err}"),
417 Ok(_) => ()
418 }
419 }
420 TriggerType::Umb3Cube => {
421 match set_umb3cube_trigger(bus) {
422 Err(err) => error!("Unable to set Umb3Cube trigger! {err}"),
423 Ok(_) => ()
424 }
425 }
426 TriggerType::Unknown => {
427 println!("== ==> Not setting any trigger condition. You can set it through pico_hal.py");
428 warn!("Trigger condition undefined! Not setting anything!");
429 error!("Trigger conditions unknown!");
430 }
431 _ => {
432 error!("Trigger type {} not covered!", settings.trigger_type);
433 println!("= => Not setting any trigger condition. You can set it through pico_hal.py");
434 warn!("Trigger condition undefined! Not setting anything!");
435 error!("Trigger conditions unknown!");
436 }
437 }
438
439 if settings.use_combo_trigger {
443 let global_prescale = settings.global_trigger_prescale;
444 let prescale_val = (u32::MAX as f32 * global_prescale as f32).floor() as u32;
445 println!("=> Setting an additonal trigger - using combo mode. Using prescale of {}", prescale_val as f32 / u32::MAX as f32);
446 match settings.global_trigger_type {
448 TriggerType::Any => {
449 match ANY_TRIG_PRESCALE.set(bus, prescale_val) {
450 Ok(_) => (),
451 Err(err) => error!("Settting the prescale {} for the any trigger failed! {err}", prescale_val)
452 }
453 }
454 TriggerType::Track => {
455 match TRACK_TRIG_PRESCALE.set(bus, prescale_val) {
456 Ok(_) => (),
457 Err(err) => error!("Settting the prescale {} for the any trigger failed! {err}", prescale_val)
458 }
459 }
460 TriggerType::TrackCentral => {
461 match TRACK_CENTRAL_PRESCALE.set(bus, prescale_val) {
462 Ok(_) => (),
463 Err(err) => error!("Settting the prescale {} for the track central trigger failed! {err}", prescale_val)
464 }
465 }
466 TriggerType::TrackUmbCentral => {
467 match TRACK_UMB_CENTRAL_PRESCALE.set(bus, prescale_val) {
468 Ok(_) => (),
469 Err(err) => error!("Settting the prescale {} for the track umb central trigger failed! {err}", prescale_val)
470 }
471 }
472 _ => {
473 error!("Unable to set {} as a global trigger type!", settings.global_trigger_type);
474 }
475 }
476 }
477 Ok(())
478}
479
480pub fn master_trigger(mt_address : &str,
501 mt_sender : &Sender<MasterTriggerEvent>,
502 moni_sender : &Sender<TofPacket>,
503 thread_control : Arc<Mutex<ThreadControl>>,
504 verbose : bool) {
505 let mut bus : IPBus;
506 let mut heartbeat = MTBHeartbeat::new();
507 let mut mtb_timeout = Instant::now();
508 let mut moni_interval = Instant::now();
509 let mut tc_timer = Instant::now();
510
511 let mut settings : MTBSettings;
512 let mut cali_active : bool;
513 let mut holdoff : bool;
514 loop {
515 match thread_control.lock() {
516 Ok(tc) => {
517 settings = tc.liftof_settings.mtb_settings.clone();
518 cali_active = tc.calibration_active;
519 holdoff = tc.holdoff_mtb_thread;
520 }
521 Err(err) => {
522 error!("Can't acquire lock for ThreadControl! Unable to set calibration mode! {err}");
523 return;
524 }
525 }
526
527 if holdoff || cali_active {
528 thread::sleep(Duration::from_secs(5));
529 } else {
530 if !holdoff {
531 println!("=> Docking clamp released!");
532 }
533 break;
534 }
535 if moni_interval.elapsed().as_secs() > settings.mtb_moni_interval {
536 match IPBus::new(mt_address) {
537 Err(err) => {
538 debug!("Can't connect to MTB, will try again in 10 ms! {err}");
539 continue;
540 }
541 Ok(mut moni_bus) => {
542 match get_mtbmonidata(&mut moni_bus) {
543 Err(err) => {
544 error!("Can not get MtbMoniData! {err}");
545 },
546 Ok(moni) => {
547 let tp = moni.pack();
548 match moni_sender.send(tp) {
549 Err(err) => {
550 error!("Can not send MtbMoniData over channel! {err}");
551 },
552 Ok(_) => ()
553 }
554 }
555 }
556 }
557 }
558 moni_interval = Instant::now();
559 }
560 }
561 let mtb_timeout_sec = settings.mtb_timeout_sec;
562 let mtb_moni_interval = settings.mtb_moni_interval;
563
564 let mut last_event_id = 0u32;
566 let mut first = true;
567 let mut slack_cadence = 5; let mut evq_num_events = 0u64;
570 let mut n_iter_loop = 0u64;
571 let mut hb_timer = Instant::now();
572 let hb_interval = Duration::from_secs(settings.hb_send_interval as u64);
573 let mut n_non_recoverable = 0usize; let connection_timeout = Instant::now();
577 loop {
578 match IPBus::new(mt_address) {
579 Err(err) => {
580 debug!("Can't connect to MTB, will try again in 10 ms! {err}");
581 thread::sleep(Duration::from_millis(10));
582 }
583 Ok(_bus) => {
584 bus = _bus;
585 break
586 }
587 }
588 if connection_timeout.elapsed().as_secs() > 10 {
589 error!("Unable to connect to MTB after 10 seconds!");
590 match thread_control.lock() {
591 Ok(mut tc) => {
592 tc.thread_master_trg_active = false;
593 }
594 Err(err) => {
595 error!("Can't acquire lock for ThreadControl! Unable to set calibration mode! {err}");
596 },
597 }
598 return;
599 }
600 }
601
602 debug!("Resetting master trigger DAQ");
603 bus.pid = 0;
605 match bus.realign_packet_id() {
606 Err(err) => error!("Can not realign packet ID! {err}"),
607 Ok(_) => ()
608 }
609
610 match reset_daq(&mut bus) {Err(err) => error!("Can not reset DAQ! {err}"),
612 Ok(_) => ()
613 }
614
615 match EVENT_CNT_RESET.set(&mut bus, 1) {
616 Err(err) => error!("Unable to reset event counter! {err}"),
617 Ok(_) => println!("=> Event counter reset!")
618 }
619
620 match configure_mtb(&mut bus, &settings) {
621 Err(err) => error!("Configuring the MTB failed! {err}"),
622 Ok(()) => ()
623 }
624
625 let mut preload_cache = 1000; loop {
627 if tc_timer.elapsed().as_secs_f32() > 2.5 {
630 match thread_control.try_lock() {
631 Ok(mut tc) => {
632 if tc.stop_flag || tc.sigint_recvd {
633 tc.end_all_rb_threads = true;
634 break;
635 }
636
637 },
638 Err(err) => {
639 error!("Can't acquire lock for ThreadControl! Unable to set calibration mode! {err}");
640 },
641 }
642 tc_timer = Instant::now();
643 }
644 if mtb_timeout.elapsed().as_secs() > mtb_timeout_sec {
647 if mtb_timeout.elapsed().as_secs() > mtb_timeout_sec {
648 info!("reconnection timer elapsed");
649 } else {
650 info!("reconnection requested");
651 }
652 match IPBus::new(mt_address) {
653 Err(err) => {
654 error!("Can't connect to MTB! {err}");
655 continue; }
657 Ok(_bus) => {
658 bus = _bus;
659 debug!("Resetting master trigger DAQ");
661 bus.pid = 0;
663 match bus.realign_packet_id() {
664 Err(err) => error!("Can not realign packet ID! {err}"),
665 Ok(_) => ()
666 }
667 match reset_daq(&mut bus) {Err(err) => error!("Can not reset DAQ! {err}"),
669 Ok(_) => ()
670 }
671 }
672 }
673 mtb_timeout = Instant::now();
674 }
675 if moni_interval.elapsed().as_secs() > mtb_moni_interval || first {
676 if first {
677 first = false;
678 }
679 match get_mtbmonidata(&mut bus) {
680 Err(err) => {
681 error!("Can not get MtbMoniData! {err}");
682 },
683 Ok(_moni) => {
684 if settings.tofbot_webhook != String::from("") {
685 let url = &settings.tofbot_webhook;
686 let message = format!("\u{1F916}\u{1F680}\u{1F388} [LIFTOF (Bot)]\n rate - {}[Hz]\n {}", _moni.rate, settings);
687 let clean_message = remove_from_word(message, "tofbot_webhook");
688 let data = json!({
689 "text" : clean_message
690 });
691 match serde_json::to_string(&data) {
692 Ok(data_string) => {
693 if slack_cadence == 0 {
694 match ureq::post(url)
695 .set("Content-Type", "application/json")
696 .send_string(&data_string) {
697 Err(err) => {
698 error!("Unable to send {} to TofBot! {err}", data_string);
699 }
700 Ok(response) => {
701 match response.into_string() {
702 Err(err) => {
703 error!("Not able to read response! {err}");
704 }
705 Ok(body) => {
706 if verbose {
707 println!("[master_trigger] - TofBot responded with {}", body);
708 }
709 }
710 }
711 }
712 }
713 } else {
714 slack_cadence -= 1;
715 }
716 if slack_cadence == 0 {
717 slack_cadence = 5;
718 }
719 }
720 Err(err) => {
721 error!("Can not convert .json to string! {err}");
722 }
723 }
724 }
725 let tp = TofPacket::from(&_moni);
726 match moni_sender.send(tp) {
727 Err(err) => {
728 error!("Can not send MtbMoniData over channel! {err}");
729 },
730 Ok(_) => ()
731 }
732 }
733 }
734 moni_interval = Instant::now();
735 }
736
737 match get_event(&mut bus){ None => {
739 }
740 Some(Err(err)) => {
741 match err {
742 MasterTriggerError::PackageFooterIncorrect
743 | MasterTriggerError::PackageHeaderIncorrect
744 | MasterTriggerError::DataTooShort
745 | MasterTriggerError::BrokenPackage => {
746 if n_non_recoverable == 100 {
749 error!("We have seen {} non-recoverable events, let's reset the DAQ!", n_non_recoverable);
750 match reset_daq(&mut bus) {Err(err) => error!("Can not reset DAQ, error {err}"),
752 Ok(_) => ()
753 }
754 n_non_recoverable = 0;
755 }
756 n_non_recoverable += 1;
757 }
758 _ => ()
759 }
760 },
761 Some(Ok(_ev)) => {
762 if _ev.event_id == last_event_id {
763 error!("We got a duplicate event from the MTB!");
764 continue;
765 }
766 if _ev.event_id > last_event_id + 1 {
767 if last_event_id != 0 {
768 error!("We skipped {} events!", _ev.event_id - last_event_id);
769 heartbeat.n_ev_missed += (_ev.event_id - last_event_id) as u64;
770 }
771 }
772 last_event_id = _ev.event_id;
773 heartbeat.n_events += 1;
774 match mt_sender.send(_ev) {
775 Err(err) => {
776 error!("Can not send MasterTriggerEvent over channel! {err}");
777 heartbeat.n_ev_unsent += 1;
778 },
779 Ok(_) => ()
780 }
781 }
782 }
783
784 if preload_cache > 0 {
785 preload_cache -= 1;
786 continue;
787 }
788 if hb_timer.elapsed() >= hb_interval {
789 match EVQ_NUM_EVENTS.get(&mut bus) {
790 Err(err) => {
791 error!("Unable to query {}! {err}", EVQ_NUM_EVENTS);
792 }
793 Ok(num_ev) => {
794 evq_num_events += num_ev as u64;
795 heartbeat.evq_num_events_last = num_ev as u64;
796 n_iter_loop += 1;
797 heartbeat.evq_num_events_avg = (evq_num_events as u64)/(n_iter_loop as u64);
798 }
799 }
800
801 heartbeat.total_elapsed += hb_timer.elapsed().as_secs() as u64;
802 match TRIGGER_RATE.get(&mut bus) {
803 Ok(trate) => {
804 heartbeat.trate = trate as u64;
805 }
806 Err(err) => {
807 error!("Unable to query {}! {err}", TRIGGER_RATE);
808 }
809 }
810 match LOST_TRIGGER_RATE.get(&mut bus) {
811 Ok(lost_trate) => {
812 heartbeat.lost_trate = lost_trate as u64;
813 }
814 Err(err) => {
815 error!("Unable to query {}! {err}", LOST_TRIGGER_RATE);
816 }
817 }
818 match TRACK_TRIG_PRESCALE.get(&mut bus) {
819 Ok(ps) => {
820 heartbeat.prescale_track = (ps as f32) / (u32::MAX as f32) ;
821 }
822 Err(err) => {
823 error!("Unable to query {}! {err}", LOST_TRIGGER_RATE);
824 }
825 }
826 match GAPS_TRIG_PRESCALE.get(&mut bus) {
827 Ok(ps) => {
828 heartbeat.prescale_gaps = (ps as f32) / (u32::MAX as f32) ;
829 }
830 Err(err) => {
831 error!("Unable to query {}! {err}", LOST_TRIGGER_RATE);
832 }
833 }
834 heartbeat.version = ProtocolVersion::V1;
835 if verbose {
836 println!("{}", heartbeat);
837 }
838
839 let pack = heartbeat.pack();
840 match moni_sender.send(pack) {
841 Err(err) => {
842 error!("Can not send MTB Heartbeat over channel! {err}");
843 },
844 Ok(_) => ()
845 }
846 hb_timer = Instant::now();
847 }
848 }
849}