1#![allow(non_snake_case)]
10
11use log::error;
12#[cfg(all(target_os = "linux", not(test)))]
13use log::info;
14
15use std::sync::{Arc, Mutex};
16
17#[cfg(feature = "debug_sensor_manager")]
18use log::debug;
19
20#[cfg(all(target_os = "linux", not(test)))]
21use nix::unistd::gettid;
22
23use spin_sleep::SpinSleeper;
24
25use crate::sensors::atlas_scientific::AtlasScientificResultData;
26use crate::sensors::dht::DhtResult;
27use crate::sensors::ds18b20::Ds18b20Result;
28use crate::sensors::sensor_manager_channels::SensorManagerChannels;
29use crate::sensors::sensor_manager_config::SensorManagerConfig;
30use crate::simulator::get_resp_sim::GetResponseFromSimulatorTrait;
31use crate::simulator::tcp_communication_error::TcpCommunicationError;
32use crate::utilities::acknowledge_signal_handler::AcknowledgeSignalHandlerTrait;
33use crate::utilities::channel_content::{AquariumSignal, InternalCommand};
34use crate::utilities::check_mutex_access_duration::CheckMutexAccessDurationTrait;
35use crate::utilities::logger::log_error_chain;
36use crate::utilities::proc_ext_req::ProcessExternalRequestTrait;
37use crate::utilities::wait_for_termination::WaitForTerminationTrait;
38use std::time::{Duration, Instant};
39use thiserror::Error;
40
41mod sensor_manager_constants {
42 pub const SOURCE_WATER_TEMPERATURE_ATLAS_SCIENTIFIC: &str = "atlas_scientific";
43
44 pub const SOURCE_WATER_TEMPERATURE_DS18B20: &str = "ds18b20";
45
46 pub const SOURCE_AMBIENT_TEMPERATURE_DHT: &str = "dht";
47
48 pub const SOURCE_AMBIENT_TEMPERATURE_DS18B20: &str = "ds18b20";
49
50 pub const SIMULATOR_MEASUREMENT_INTERVAL_MILLIS: u64 = 500;
51
52 pub const HW_MEASUREMENT_INTERVAL_MILLIS: u64 = 500;
54
55 pub const MAX_MUTEX_ACCESS_DURATION_MILLIS: u64 = 10;
57}
58
59#[derive(Clone)]
64pub struct SensorManagerMutexes {
65 pub mutex_ds18b20_ambient_temperature: Arc<Mutex<Ds18b20Result>>,
67
68 pub mutex_ds18b20_water_temperature: Arc<Mutex<Ds18b20Result>>,
70
71 pub mutex_dht: Arc<Mutex<DhtResult>>,
73
74 pub mutex_atlas_scientific_temperature: Arc<Mutex<AtlasScientificResultData>>,
76
77 pub mutex_atlas_scientific_ph: Arc<Mutex<AtlasScientificResultData>>,
79
80 pub mutex_atlas_scientific_conductivity: Arc<Mutex<AtlasScientificResultData>>,
82
83 pub mutex_sensor_manager_signals: Arc<Mutex<SensorManagerSignals>>,
85}
86
87pub struct SensorManagerSignals {
90 pub water_temperature: f32,
92
93 pub ambient_temperature: f32,
95
96 pub ambient_humidity: f32,
98
99 pub ph: f32,
101
102 pub conductivity: f32,
104
105 pub instant_last_recording: Instant,
107}
108
109impl SensorManagerSignals {
110 pub fn new(config: &SensorManagerConfig) -> SensorManagerSignals {
111 SensorManagerSignals {
112 water_temperature: config.replacement_value_water_temperature,
113 ambient_temperature: config.replacement_value_ambient_temperature,
114 ambient_humidity: config.replacement_value_ambient_humidity,
115 ph: config.replacement_value_ph,
116 conductivity: config.replacement_value_conductivity,
117 instant_last_recording: Instant::now(),
118 }
119 }
120}
121
122#[derive(Error, Debug)]
123pub enum SensorManagerError {
124 #[error("[{0}]: invalid configuration of ambient temperature. source_ambient_temperature may only have values ({1}, {2}) configured value is {3}")]
126 InvalidAmbientTemperatureConfiguration(String, String, String, String),
127
128 #[error("[{0}]: invalid configuration of water temperature. source_ambient_temperature may only have values ({1}, {2}) configured value is {3}")]
130 InvalidWaterTemperatureConfiguration(String, String, String, String),
131
132 #[error("[{location}] Communication with simulator for {signal} failed")]
134 SimulatorCommunicationError {
135 location: String,
136 signal: AquariumSignal,
137
138 #[source]
139 source: TcpCommunicationError,
140 },
141}
142
143#[cfg_attr(doc, aquamarine::aquamarine)]
148pub struct SensorManager {
157 config: SensorManagerConfig,
159
160 ambient_temperature: f32,
162
163 ambient_humidity: f32,
165
166 water_temperature: f32,
168
169 conductivity: f32,
171
172 pH: f32,
174
175 pub lock_error_channel_receive_termination: bool,
177
178 pub lock_warn_inapplicable_command_signal_handler: bool,
180
181 pub instant_last_measurement: Instant,
183
184 pub measurement_interval: Duration,
186
187 lock_error_dht_mutex: bool,
189
190 lock_error_atlas_scientific_temperature_mutex: bool,
192
193 lock_error_atlas_scientific_ph_mutex: bool,
195
196 lock_error_atlas_scientific_conductivity_mutex: bool,
198
199 lock_error_ds18b20_water_temperature_mutex: bool,
201
202 lock_error_ds18b20_ambient_temperature_mutex: bool,
204
205 pub lock_warn_max_mutex_access_duration: bool,
207
208 #[cfg(test)]
210 pub mutex_access_duration_exceeded: bool,
211
212 pub max_mutex_access_duration: Duration,
214}
215
216impl ProcessExternalRequestTrait for SensorManager {}
217
218impl GetResponseFromSimulatorTrait for SensorManager {}
219
220impl SensorManager {
221 pub fn new(config: SensorManagerConfig) -> Result<SensorManager, SensorManagerError> {
237 if config.source_ambient_temperature
239 != sensor_manager_constants::SOURCE_AMBIENT_TEMPERATURE_DHT
240 && config.source_ambient_temperature
241 != sensor_manager_constants::SOURCE_AMBIENT_TEMPERATURE_DS18B20
242 {
243 return Err(SensorManagerError::InvalidAmbientTemperatureConfiguration(
244 module_path!().to_string(),
245 sensor_manager_constants::SOURCE_AMBIENT_TEMPERATURE_DHT.to_string(),
246 sensor_manager_constants::SOURCE_AMBIENT_TEMPERATURE_DS18B20.to_string(),
247 config.source_ambient_temperature,
248 ));
249 }
250
251 if config.source_water_temperature
253 != sensor_manager_constants::SOURCE_WATER_TEMPERATURE_ATLAS_SCIENTIFIC
254 && config.source_water_temperature
255 != sensor_manager_constants::SOURCE_WATER_TEMPERATURE_DS18B20
256 {
257 return Err(SensorManagerError::InvalidWaterTemperatureConfiguration(
258 module_path!().to_string(),
259 sensor_manager_constants::SOURCE_WATER_TEMPERATURE_ATLAS_SCIENTIFIC.to_string(),
260 sensor_manager_constants::SOURCE_WATER_TEMPERATURE_DS18B20.to_string(),
261 config.source_water_temperature,
262 ));
263 }
264
265 let replacement_value_ambient_temperature = config.replacement_value_ambient_temperature;
266 let replacement_value_ambient_humidity = config.replacement_value_ambient_humidity;
267
268 let measurement_interval = if config.use_simulator {
270 Duration::from_millis(sensor_manager_constants::SIMULATOR_MEASUREMENT_INTERVAL_MILLIS)
271 } else {
272 Duration::from_millis(sensor_manager_constants::HW_MEASUREMENT_INTERVAL_MILLIS)
273 };
274
275 Ok(SensorManager {
276 config,
277 ambient_temperature: replacement_value_ambient_temperature,
278 ambient_humidity: replacement_value_ambient_humidity,
279 water_temperature: 0.0,
280 lock_error_channel_receive_termination: false,
281 lock_warn_inapplicable_command_signal_handler: false,
282 lock_error_dht_mutex: false,
283 conductivity: 0.0,
284 pH: 0.0,
285 instant_last_measurement: Instant::now(),
286 measurement_interval,
287 lock_error_atlas_scientific_temperature_mutex: false,
288 lock_error_atlas_scientific_ph_mutex: false,
289 lock_error_atlas_scientific_conductivity_mutex: false,
290 lock_error_ds18b20_water_temperature_mutex: false,
291 lock_error_ds18b20_ambient_temperature_mutex: false,
292 lock_warn_max_mutex_access_duration: false,
293 #[cfg(test)]
294 mutex_access_duration_exceeded: false,
295 max_mutex_access_duration: Duration::from_millis(
296 sensor_manager_constants::MAX_MUTEX_ACCESS_DURATION_MILLIS,
297 ),
298 })
299 }
300
301 fn communicate_with_simulator(
326 &mut self,
327 sensor_manager_channels: &mut SensorManagerChannels,
328 ) -> Result<(), SensorManagerError> {
329 let tx_sensor_manager_to_simulator = sensor_manager_channels
331 .tx_sensor_manager_to_tcp_opt
332 .as_mut()
333 .unwrap();
334 let rx_sensor_manager_from_simulator = sensor_manager_channels
335 .rx_sensor_manager_from_tcp_opt
336 .as_mut()
337 .unwrap();
338
339 self.ambient_temperature = Self::get_response_from_simulator(
340 module_path!().to_string(),
341 tx_sensor_manager_to_simulator,
342 rx_sensor_manager_from_simulator,
343 InternalCommand::RequestSignal(AquariumSignal::AmbientTemperature),
344 )
345 .map_err(|e| SensorManagerError::SimulatorCommunicationError {
346 location: module_path!().to_string(),
347 signal: AquariumSignal::AmbientTemperature,
348 source: e,
349 })?;
350 self.ambient_humidity = Self::get_response_from_simulator(
351 module_path!().to_string(),
352 tx_sensor_manager_to_simulator,
353 rx_sensor_manager_from_simulator,
354 InternalCommand::RequestSignal(AquariumSignal::AmbientHumidity),
355 )
356 .map_err(|e| SensorManagerError::SimulatorCommunicationError {
357 location: module_path!().to_string(),
358 signal: AquariumSignal::AmbientHumidity,
359 source: e,
360 })?;
361 self.water_temperature = Self::get_response_from_simulator(
362 module_path!().to_string(),
363 tx_sensor_manager_to_simulator,
364 rx_sensor_manager_from_simulator,
365 InternalCommand::RequestSignal(AquariumSignal::WaterTemperature),
366 )
367 .map_err(|e| SensorManagerError::SimulatorCommunicationError {
368 location: module_path!().to_string(),
369 signal: AquariumSignal::WaterTemperature,
370 source: e,
371 })?;
372 self.conductivity = Self::get_response_from_simulator(
373 module_path!().to_string(),
374 tx_sensor_manager_to_simulator,
375 rx_sensor_manager_from_simulator,
376 InternalCommand::RequestSignal(AquariumSignal::Conductivity),
377 )
378 .map_err(|e| SensorManagerError::SimulatorCommunicationError {
379 location: module_path!().to_string(),
380 signal: AquariumSignal::Conductivity,
381 source: e,
382 })?;
383 self.pH = Self::get_response_from_simulator(
384 module_path!().to_string(),
385 tx_sensor_manager_to_simulator,
386 rx_sensor_manager_from_simulator,
387 InternalCommand::RequestSignal(AquariumSignal::pH),
388 )
389 .map_err(|e| SensorManagerError::SimulatorCommunicationError {
390 location: module_path!().to_string(),
391 signal: AquariumSignal::pH,
392 source: e,
393 })?;
394
395 Ok(())
396 }
397
398 pub fn execute(
422 &mut self,
423 sensor_manager_channels: &mut SensorManagerChannels,
424 mutexes: SensorManagerMutexes,
425 ) {
426 #[cfg(all(target_os = "linux", not(test)))]
427 info!(target: module_path!(), "Thread started with TID: {}", gettid());
428
429 if (self.config.use_simulator)
430 && (sensor_manager_channels
431 .tx_sensor_manager_to_tcp_opt
432 .is_none()
433 | sensor_manager_channels
434 .rx_sensor_manager_from_tcp_opt
435 .is_none())
436 {
437 panic!(
438 "{}: configured to run with simulator, but no channel to TCP thread provided.",
439 module_path!()
440 );
441 }
442
443 let sleep_duration_hundred_millis = Duration::from_millis(100);
444 let sleep_duration_one_millis = Duration::from_millis(1);
445 let spin_sleeper = SpinSleeper::default();
446 let mut mutex_poisoned = false;
447
448 self.ambient_temperature = self.config.replacement_value_ambient_temperature;
450 self.ambient_humidity = self.config.replacement_value_ambient_humidity;
451
452 loop {
453 let (
454 quit_command_received, _,
456 _,
457 ) = self.process_external_request(
458 &mut sensor_manager_channels.rx_sensor_manager_from_signal_handler,
459 None,
460 );
461
462 if quit_command_received {
463 break;
465 }
466
467 let current_instant = Instant::now();
468 if current_instant.duration_since(self.instant_last_measurement)
469 > self.measurement_interval
470 {
471 self.instant_last_measurement = current_instant;
472 if self.config.use_simulator {
473 let result = self.communicate_with_simulator(sensor_manager_channels);
474 if result.is_err() {
475 log_error_chain(
476 module_path!(),
477 "error when communicating with simulator",
478 result.err().unwrap(),
479 );
480 }
481 } else {
482 if self.config.source_water_temperature
484 == sensor_manager_constants::SOURCE_WATER_TEMPERATURE_DS18B20
485 {
486 match mutexes.mutex_ds18b20_water_temperature.lock() {
487 Ok(ds18b20_water_temperature_result) => {
488 match &*ds18b20_water_temperature_result {
489 Ok(ds18b20_water_temperature_result_data) => {
490 self.water_temperature =
491 ds18b20_water_temperature_result_data.value;
492 }
493 Err(_) => {
494 }
496 }
497 }
498 Err(e) => {
499 if !self.lock_error_ds18b20_water_temperature_mutex {
500 self.lock_error_ds18b20_water_temperature_mutex = true;
501 error!(target: module_path!(), "error when trying to lock Ds18b20 water temperature mutex: {e}");
502 }
503 }
504 }
505 }
506 if self.config.source_ambient_temperature
507 == sensor_manager_constants::SOURCE_WATER_TEMPERATURE_DS18B20
508 {
509 match mutexes.mutex_ds18b20_ambient_temperature.lock() {
510 Ok(ds18b20_ambient_temperature_result) => {
511 match &*ds18b20_ambient_temperature_result {
512 Ok(ds18b20_ambient_temperature_result_data) => {
513 self.ambient_temperature =
514 ds18b20_ambient_temperature_result_data.value;
515 }
516 Err(_) => {
517 }
519 }
520 }
521 Err(e) => {
522 if !self.lock_error_ds18b20_ambient_temperature_mutex {
523 self.lock_error_ds18b20_ambient_temperature_mutex = true;
524 error!(target: module_path!(), "error when trying to lock Ds18b20 ambient temperature mutex: {e}");
525 }
526 }
527 }
528 }
529 {
531 match mutexes.mutex_dht.lock() {
532 Ok(dht_result) => {
533 match *dht_result {
534 Ok(tuple) => {
535 if self.config.source_ambient_temperature ==
536 sensor_manager_constants::SOURCE_AMBIENT_TEMPERATURE_DHT {
537 self.ambient_temperature = tuple.0;
538 }
539 self.ambient_humidity = tuple.1;
540 }
541 Err(_) => {
542 }
544 }
545 }
546 Err(e) => {
547 if !self.lock_error_dht_mutex {
548 self.lock_error_dht_mutex = true;
549 error!(target: module_path!(), "error when trying to lock Dht mutex: {e}");
550 }
551 }
552 }
553 }
554
555 {
557 match mutexes.mutex_atlas_scientific_temperature.lock() {
558 Ok(atlas_scientific_temperature) => {
559 if self.config.source_water_temperature == sensor_manager_constants::SOURCE_WATER_TEMPERATURE_ATLAS_SCIENTIFIC {
560 self.water_temperature = atlas_scientific_temperature.value;
561 }
562 }
563 Err(e) => {
564 if !self.lock_error_atlas_scientific_temperature_mutex {
565 self.lock_error_atlas_scientific_temperature_mutex = true;
566 error!(target: module_path!(), "error when trying to lock mutex: {e}");
567 }
568 }
569 }
570 }
571
572 {
573 match mutexes.mutex_atlas_scientific_ph.lock() {
574 Ok(atlas_scientific_ph) => {
575 self.pH = atlas_scientific_ph.value;
576 }
577 Err(e) => {
578 if !self.lock_error_atlas_scientific_ph_mutex {
579 self.lock_error_atlas_scientific_ph_mutex = true;
580 error!(target: module_path!(), "error when trying to lock mutex: {e}");
581 }
582 }
583 }
584 }
585
586 {
587 match mutexes.mutex_atlas_scientific_conductivity.lock() {
588 Ok(atlas_scientific_conductivity) => {
589 self.conductivity = atlas_scientific_conductivity.value;
590 }
591 Err(e) => {
592 if !self.lock_error_atlas_scientific_conductivity_mutex {
593 self.lock_error_atlas_scientific_conductivity_mutex = true;
594 error!(target: module_path!(), "error when trying to lock mutex: {e}");
595 }
596 }
597 }
598 }
599 }
600
601 if !mutex_poisoned {
602 let instant_before_locking_mutex = Instant::now();
603 let mut instant_after_locking_mutex = Instant::now(); {
607 match mutexes.mutex_sensor_manager_signals.lock() {
608 Ok(mut sensor_manager_signals) => {
609 instant_after_locking_mutex = Instant::now();
610 sensor_manager_signals.ambient_humidity = self.ambient_humidity;
611 sensor_manager_signals.ambient_temperature =
612 self.ambient_temperature;
613 sensor_manager_signals.water_temperature = self.water_temperature;
614 sensor_manager_signals.conductivity = self.conductivity;
615 sensor_manager_signals.ph = self.pH;
616 sensor_manager_signals.instant_last_recording = Instant::now();
617 }
618 Err(e) => {
619 log_error_chain(module_path!(), "error when trying to lock mutex for writing sensor manager data", e);
620 mutex_poisoned = true;
621 }
622 }
623 }
624
625 self.check_mutex_access_duration(
627 None,
628 instant_after_locking_mutex,
629 instant_before_locking_mutex,
630 );
631 }
632 }
633 spin_sleeper.sleep(sleep_duration_hundred_millis);
634 }
635
636 sensor_manager_channels.acknowledge_signal_handler();
637
638 self.wait_for_termination(
642 &mut sensor_manager_channels.rx_sensor_manager_from_signal_handler,
643 sleep_duration_one_millis,
644 module_path!(),
645 );
646 }
647}
648
649#[cfg(test)]
650pub mod tests {
651 use crate::launch::channels::Channels;
652 use crate::sensors::atlas_scientific::AtlasScientificResultData;
653 use crate::sensors::ds18b20::Ds18b20ResultData;
654 use crate::sensors::sensor_manager::{
655 SensorManager, SensorManagerChannels, SensorManagerError, SensorManagerMutexes,
656 SensorManagerSignals,
657 };
658 use crate::utilities::channel_content::InternalCommand;
659 use crate::utilities::config::{read_config_file, ConfigData};
660 use crate::utilities::signal_handler_channels::SignalHandlerChannels;
661 use spin_sleep::SpinSleeper;
662 use std::sync::{Arc, Mutex};
663 use std::thread::scope;
664 use std::time::Duration;
665
666 #[test]
667 pub fn test_sensor_manager_error_water_temperature_source_is_invalid() {
668 let mut config: ConfigData =
669 read_config_file("/config/aquarium_control_test_generic.toml".to_string()).unwrap();
670 config.sensor_manager.source_water_temperature = "invalid".to_string();
671 let test_result = SensorManager::new(config.sensor_manager);
672 assert!(matches!(
673 test_result,
674 Err(SensorManagerError::InvalidWaterTemperatureConfiguration(
675 _,
676 _,
677 _,
678 _
679 ))
680 ));
681 }
682
683 #[test]
684 pub fn test_sensor_manager_error_ambient_temperature_source_is_invalid() {
685 let mut config: ConfigData =
686 read_config_file("/config/aquarium_control_test_generic.toml".to_string()).unwrap();
687 config.sensor_manager.source_ambient_temperature = "invalid".to_string();
688 let test_result = SensorManager::new(config.sensor_manager);
689 assert!(matches!(
690 test_result,
691 Err(SensorManagerError::InvalidAmbientTemperatureConfiguration(
692 _,
693 _,
694 _,
695 _
696 ))
697 ));
698 }
699
700 fn mock_signal_handler_for_sensor_manager(
701 mut signal_handler_channels: SignalHandlerChannels,
702 mutex_sensor_manager_opt: Option<Arc<Mutex<SensorManagerSignals>>>,
703 ) {
704 let spin_sleeper = SpinSleeper::default();
705 if mutex_sensor_manager_opt.is_none() {
707 let sleep_duration_8_seconds = Duration::from_millis(8000);
708 spin_sleeper.sleep(sleep_duration_8_seconds);
709 } else {
710 let mutex_sensor_manager = mutex_sensor_manager_opt.unwrap();
711 let sleep_duration_1_second = Duration::from_millis(1000);
712 for i in 0..8 {
713 if i % 2 > 0 {
714 {
715 let _unused = mutex_sensor_manager.lock().unwrap();
717 spin_sleeper.sleep(sleep_duration_1_second);
718 }
719 } else {
720 spin_sleeper.sleep(sleep_duration_1_second);
721 }
722 }
723 }
724 let _ = signal_handler_channels.send_to_sensor_manager(InternalCommand::Quit);
725 signal_handler_channels
726 .receive_from_sensor_manager()
727 .unwrap();
728 let _ = signal_handler_channels.send_to_sensor_manager(InternalCommand::Terminate);
729 }
730
731 fn execute_and_assert_sensor_manager(
732 mut sensor_manager_channels: SensorManagerChannels,
733 mut sensor_manager: SensorManager,
734 mutexes: SensorManagerMutexes,
735 target_value_water_temperature: f32,
736 target_value_ambient_temperature: f32,
737 reference_mutex_access_time_exceeded: bool,
738 ) {
739 let mutexes_clone_for_asserts = mutexes.clone();
740
741 sensor_manager.execute(&mut sensor_manager_channels, mutexes);
743
744 match mutexes_clone_for_asserts
745 .mutex_sensor_manager_signals
746 .lock()
747 {
748 Ok(mutex_sensor_manager_signals_after_run) => {
749 assert_eq!(
750 mutex_sensor_manager_signals_after_run.water_temperature,
751 target_value_water_temperature
752 );
753 assert_eq!(
754 mutex_sensor_manager_signals_after_run.ambient_temperature,
755 target_value_ambient_temperature
756 );
757 }
758 Err(e) => {
759 panic!("Could not lock mutex: {e:?}")
760 }
761 };
762
763 assert_eq!(
764 sensor_manager.mutex_access_duration_exceeded,
765 reference_mutex_access_time_exceeded
766 );
767 }
768
769 #[test]
770 pub fn test_sensor_manager_interfaces_using_ds18b20_as_input_without_mutex_blocked() {
773 let channels = Channels::new_for_test();
774
775 let mut config: ConfigData =
776 read_config_file("/config/aquarium_control_test_generic.toml".to_string()).unwrap();
777 config.sensor_manager.use_simulator = false;
778 config.sensor_manager.source_ambient_temperature = "ds18b20".to_string();
779 config.sensor_manager.source_water_temperature = "ds18b20".to_string();
780
781 let target_value_water_temperature =
782 config.sensor_manager.replacement_value_water_temperature;
783 let target_value_ambient_temperature =
784 config.sensor_manager.replacement_value_ambient_temperature;
785
786 let mutex_ds18b20_water_temperature = Arc::new(Mutex::new(Ok(Ds18b20ResultData::new(
787 target_value_water_temperature,
788 ))));
789 let mutex_ds18b20_ambient_temperature = Arc::new(Mutex::new(Ok(Ds18b20ResultData::new(
790 target_value_ambient_temperature,
791 ))));
792 let mutex_atlas_scientific_temperature = Arc::new(Mutex::new(
793 AtlasScientificResultData::new(target_value_water_temperature + 1.0),
794 ));
795 let mutex_atlas_scientific_conductivity = Arc::new(Mutex::new(
796 AtlasScientificResultData::new(config.sensor_manager.replacement_value_conductivity),
797 ));
798 let mutex_atlas_scientific_ph = Arc::new(Mutex::new(AtlasScientificResultData::new(
799 config.sensor_manager.replacement_value_ph,
800 )));
801 let mutex_sensor_manager_signals = Arc::new(Mutex::new(SensorManagerSignals::new(
802 &config.sensor_manager,
803 )));
804 let dht_result = Ok((target_value_ambient_temperature + 1.0, 2.0));
805 let mutex_dht = Arc::new(Mutex::new(dht_result));
806
807 let sensor_manager = SensorManager::new(config.sensor_manager).unwrap();
808
809 let mutexes = SensorManagerMutexes {
810 mutex_ds18b20_ambient_temperature,
811 mutex_ds18b20_water_temperature,
812 mutex_dht,
813 mutex_atlas_scientific_temperature,
814 mutex_atlas_scientific_ph,
815 mutex_atlas_scientific_conductivity,
816 mutex_sensor_manager_signals,
817 };
818
819 scope(|scope| {
820 scope.spawn(move || {
821 mock_signal_handler_for_sensor_manager(channels.signal_handler, None);
822 });
823
824 scope.spawn(move || {
825 execute_and_assert_sensor_manager(
826 channels.sensor_manager,
827 sensor_manager,
828 mutexes,
829 target_value_water_temperature,
830 target_value_ambient_temperature,
831 false, );
833 });
834 });
835 }
836
837 #[test]
838 pub fn test_sensor_manager_interfaces_using_atlas_scientific_and_dht_as_input() {
841 let channels = Channels::new_for_test();
842
843 let mut config: ConfigData =
844 read_config_file("/config/aquarium_control_test_generic.toml".to_string()).unwrap();
845 config.sensor_manager.use_simulator = false;
846 config.sensor_manager.source_ambient_temperature = "dht".to_string();
847 config.sensor_manager.source_water_temperature = "atlas_scientific".to_string();
848
849 let target_value_water_temperature =
850 config.sensor_manager.replacement_value_water_temperature;
851 let target_value_ambient_temperature =
852 config.sensor_manager.replacement_value_ambient_temperature;
853
854 let mutex_ds18b20_water_temperature = Arc::new(Mutex::new(Ok(Ds18b20ResultData::new(
855 target_value_water_temperature + 1.0,
856 ))));
857 let mutex_ds18b20_ambient_temperature = Arc::new(Mutex::new(Ok(Ds18b20ResultData::new(
858 target_value_ambient_temperature + 1.0,
859 ))));
860 let mutex_atlas_scientific_temperature = Arc::new(Mutex::new(
861 AtlasScientificResultData::new(target_value_water_temperature),
862 ));
863 let mutex_atlas_scientific_conductivity = Arc::new(Mutex::new(
864 AtlasScientificResultData::new(config.sensor_manager.replacement_value_conductivity),
865 ));
866 let mutex_atlas_scientific_ph = Arc::new(Mutex::new(AtlasScientificResultData::new(
867 config.sensor_manager.replacement_value_ph,
868 )));
869 let mutex_sensor_manager_signals = Arc::new(Mutex::new(SensorManagerSignals::new(
870 &config.sensor_manager,
871 )));
872 let dht_result = Ok((target_value_ambient_temperature, 2.0));
873 let mutex_dht = Arc::new(Mutex::new(dht_result));
874
875 let sensor_manager = SensorManager::new(config.sensor_manager).unwrap();
876
877 let mutexes = SensorManagerMutexes {
878 mutex_ds18b20_ambient_temperature,
879 mutex_ds18b20_water_temperature,
880 mutex_dht,
881 mutex_atlas_scientific_temperature,
882 mutex_atlas_scientific_ph,
883 mutex_atlas_scientific_conductivity,
884 mutex_sensor_manager_signals,
885 };
886
887 scope(|scope| {
888 scope.spawn(move || {
889 mock_signal_handler_for_sensor_manager(channels.signal_handler, None);
890 });
891
892 scope.spawn(move || {
893 execute_and_assert_sensor_manager(
894 channels.sensor_manager,
895 sensor_manager,
896 mutexes,
897 target_value_water_temperature,
898 target_value_ambient_temperature,
899 false, );
901 });
902 });
903 }
904
905 #[test]
906 pub fn test_sensor_manager_interfaces_using_ds18b20_as_input_with_mutex_blocked() {
909 let channels = Channels::new_for_test();
910
911 let mut config: ConfigData =
912 read_config_file("/config/aquarium_control_test_generic.toml".to_string()).unwrap();
913 config.sensor_manager.use_simulator = false;
914 config.sensor_manager.source_ambient_temperature = "ds18b20".to_string();
915 config.sensor_manager.source_water_temperature = "ds18b20".to_string();
916
917 let target_value_water_temperature =
918 config.sensor_manager.replacement_value_water_temperature;
919 let target_value_ambient_temperature =
920 config.sensor_manager.replacement_value_ambient_temperature;
921
922 let mutex_ds18b20_water_temperature = Arc::new(Mutex::new(Ok(Ds18b20ResultData::new(
923 target_value_water_temperature,
924 ))));
925 let mutex_ds18b20_ambient_temperature = Arc::new(Mutex::new(Ok(Ds18b20ResultData::new(
926 target_value_ambient_temperature,
927 ))));
928 let mutex_atlas_scientific_temperature = Arc::new(Mutex::new(
929 AtlasScientificResultData::new(target_value_water_temperature + 1.0),
930 ));
931 let mutex_atlas_scientific_conductivity = Arc::new(Mutex::new(
932 AtlasScientificResultData::new(config.sensor_manager.replacement_value_conductivity),
933 ));
934 let mutex_atlas_scientific_ph = Arc::new(Mutex::new(AtlasScientificResultData::new(
935 config.sensor_manager.replacement_value_ph,
936 )));
937 let mutex_sensor_manager_signals = Arc::new(Mutex::new(SensorManagerSignals::new(
938 &config.sensor_manager,
939 )));
940 let mutex_sensor_manager_signals_clone_for_test_environment =
941 mutex_sensor_manager_signals.clone();
942
943 let dht_result = Ok((target_value_ambient_temperature + 1.0, 2.0));
944 let mutex_dht = Arc::new(Mutex::new(dht_result));
945
946 let sensor_manager = SensorManager::new(config.sensor_manager).unwrap();
947
948 let mutexes = SensorManagerMutexes {
949 mutex_ds18b20_ambient_temperature,
950 mutex_ds18b20_water_temperature,
951 mutex_dht,
952 mutex_atlas_scientific_temperature,
953 mutex_atlas_scientific_ph,
954 mutex_atlas_scientific_conductivity,
955 mutex_sensor_manager_signals,
956 };
957
958 scope(|scope| {
959 scope.spawn(move || {
960 mock_signal_handler_for_sensor_manager(
961 channels.signal_handler,
962 Some(mutex_sensor_manager_signals_clone_for_test_environment),
963 );
964 });
965
966 scope.spawn(move || {
967 execute_and_assert_sensor_manager(
968 channels.sensor_manager,
969 sensor_manager,
970 mutexes,
971 target_value_water_temperature,
972 target_value_ambient_temperature,
973 true, );
975 });
976 });
977 }
978}