@@ -23,6 +23,9 @@ use crate::acquisition::{
2323} ;
2424use crate :: processing:: nodes:: StreamingNodeRegistry ;
2525use crate :: processing:: { ProcessingConsumer , ProcessingGraph } ;
26+ use crate :: thermal_regulation:: {
27+ create_shared_thermal_state, SharedThermalState , ThermalRegulationSystemDaemon ,
28+ } ;
2629use crate :: utility:: PhotoacousticDataSource ;
2730use crate :: visualization:: server:: build_rocket;
2831use crate :: visualization:: shared_state:: SharedVisualizationState ;
@@ -80,6 +83,10 @@ pub struct Daemon {
8083 /// Shared configuration for dynamic configuration support
8184 /// This is the single source of truth for all configuration across the application
8285 config : Arc < RwLock < crate :: config:: Config > > ,
86+ /// Thermal regulation system daemon for PID temperature control
87+ thermal_regulation_daemon : Option < ThermalRegulationSystemDaemon > ,
88+ /// Shared thermal regulation state for historical data and monitoring
89+ thermal_regulation_state : SharedThermalState ,
8390}
8491
8592impl Default for Daemon {
@@ -120,6 +127,8 @@ impl Daemon {
120127 visualization_state : Arc :: new ( SharedVisualizationState :: new ( ) ) ,
121128 streaming_registry : Arc :: new ( StreamingNodeRegistry :: new ( ) ) ,
122129 config : Arc :: new ( RwLock :: new ( crate :: config:: Config :: default ( ) ) ) ,
130+ thermal_regulation_daemon : None ,
131+ thermal_regulation_state : create_shared_thermal_state ( ) ,
123132 }
124133 }
125134
@@ -202,6 +211,11 @@ impl Daemon {
202211 self . start_modbus_server ( ) . await ?;
203212 }
204213
214+ // Start thermal regulation system if enabled
215+ if self . config . read ( ) . unwrap ( ) . thermal_regulation . enabled {
216+ self . start_thermal_regulation_system ( ) . await ?;
217+ }
218+
205219 // Start computation task if enabled
206220 if true {
207221 self . start_photoacoustic_computation ( ) ?;
@@ -863,6 +877,167 @@ impl Daemon {
863877 Ok ( ( ) )
864878 }
865879
880+ /// Start the thermal regulation system daemon
881+ ///
882+ /// Initializes and starts the thermal regulation system with multiple independent
883+ /// PID controllers, each running in its own thread with individual sampling frequencies.
884+ /// The system provides precise temperature control for photoacoustic applications.
885+ ///
886+ /// This method creates a thermal regulation daemon that manages all configured
887+ /// thermal regulators according to the shared configuration. Each regulator
888+ /// operates independently with its own:
889+ /// - PID controller parameters (Kp, Ki, Kd)
890+ /// - Sampling frequency (sampling_frequency_hz)
891+ /// - Target temperature setpoint
892+ /// - Hardware driver (mock, native, or CP2112)
893+ ///
894+ /// The thermal regulation system maintains a shared state with historical data
895+ /// (up to 3600 data points per regulator) that can be accessed by the web interface
896+ /// and other system components.
897+ ///
898+ /// ### Returns
899+ ///
900+ /// * `Result<()>` - Success if the thermal regulation system started successfully
901+ ///
902+ /// ### Errors
903+ ///
904+ /// This function can fail if:
905+ /// * Hardware initialization fails
906+ /// * Configuration is invalid
907+ /// * Thread spawning fails
908+ /// * Driver creation fails
909+ async fn start_thermal_regulation_system ( & mut self ) -> Result < ( ) > {
910+ info ! ( "Starting thermal regulation system" ) ;
911+
912+ // Extract thermal regulation configuration
913+ let thermal_config = {
914+ let config = self . config . read ( ) . unwrap ( ) ;
915+ config. thermal_regulation . clone ( )
916+ } ;
917+
918+ if !thermal_config. enabled {
919+ info ! ( "Thermal regulation system is disabled in configuration" ) ;
920+ return Ok ( ( ) ) ;
921+ }
922+
923+ if thermal_config. regulators . is_empty ( ) {
924+ warn ! ( "No thermal regulators configured" ) ;
925+ return Ok ( ( ) ) ;
926+ }
927+
928+ // Create thermal regulation system daemon
929+ let mut thermal_daemon = ThermalRegulationSystemDaemon :: new (
930+ thermal_config,
931+ self . thermal_regulation_state . clone ( ) ,
932+ self . running . clone ( ) ,
933+ ) ;
934+
935+ // Start the thermal regulation system
936+ thermal_daemon. start ( ) . await ?;
937+
938+ info ! (
939+ "Thermal regulation system started successfully with {} regulators" ,
940+ thermal_daemon
941+ . get_shared_state( )
942+ . read( )
943+ . await
944+ . get_regulator_ids( )
945+ . len( )
946+ ) ;
947+
948+ // Store the daemon for later management
949+ self . thermal_regulation_daemon = Some ( thermal_daemon) ;
950+
951+ Ok ( ( ) )
952+ }
953+
954+ /// Get thermal regulation shared state for external access
955+ ///
956+ /// Returns a reference to the shared thermal regulation state that contains
957+ /// historical data, current status, and PID parameters for all thermal regulators.
958+ /// This can be used by the web interface and API endpoints to provide
959+ /// real-time monitoring and control capabilities.
960+ ///
961+ /// ### Returns
962+ ///
963+ /// Reference to the shared thermal regulation state
964+ pub fn get_thermal_regulation_state ( & self ) -> & SharedThermalState {
965+ & self . thermal_regulation_state
966+ }
967+
968+ /// Update PID parameters for a specific thermal regulator
969+ ///
970+ /// Allows dynamic updating of PID controller parameters for a specific regulator
971+ /// without requiring a system restart. This enables real-time tuning and
972+ /// optimization of thermal control performance.
973+ ///
974+ /// ### Arguments
975+ ///
976+ /// * `regulator_id` - Unique identifier of the thermal regulator
977+ /// * `kp` - Proportional gain
978+ /// * `ki` - Integral gain
979+ /// * `kd` - Derivative gain
980+ ///
981+ /// ### Returns
982+ ///
983+ /// * `Result<()>` - Success if parameters were updated successfully
984+ ///
985+ /// ### Errors
986+ ///
987+ /// This function can fail if:
988+ /// * The specified regulator is not found
989+ /// * The thermal regulation system is not running
990+ pub async fn update_thermal_regulator_pid_parameters (
991+ & mut self ,
992+ regulator_id : & str ,
993+ kp : f64 ,
994+ ki : f64 ,
995+ kd : f64 ,
996+ ) -> Result < ( ) > {
997+ if let Some ( ref mut thermal_daemon) = self . thermal_regulation_daemon {
998+ thermal_daemon
999+ . update_regulator_pid_parameters ( regulator_id, kp, ki, kd)
1000+ . await
1001+ } else {
1002+ Err ( anyhow:: anyhow!( "Thermal regulation system is not running" ) )
1003+ }
1004+ }
1005+
1006+ /// Update setpoint temperature for a specific thermal regulator
1007+ ///
1008+ /// Allows dynamic updating of the target temperature setpoint for a specific
1009+ /// regulator without requiring a system restart. This enables real-time control
1010+ /// of thermal regulation targets.
1011+ ///
1012+ /// ### Arguments
1013+ ///
1014+ /// * `regulator_id` - Unique identifier of the thermal regulator
1015+ /// * `setpoint_celsius` - New target temperature in degrees Celsius
1016+ ///
1017+ /// ### Returns
1018+ ///
1019+ /// * `Result<()>` - Success if setpoint was updated successfully
1020+ ///
1021+ /// ### Errors
1022+ ///
1023+ /// This function can fail if:
1024+ /// * The specified regulator is not found
1025+ /// * The thermal regulation system is not running
1026+ /// * The setpoint is outside safety limits
1027+ pub async fn update_thermal_regulator_setpoint (
1028+ & mut self ,
1029+ regulator_id : & str ,
1030+ setpoint_celsius : f64 ,
1031+ ) -> Result < ( ) > {
1032+ if let Some ( ref mut thermal_daemon) = self . thermal_regulation_daemon {
1033+ thermal_daemon
1034+ . update_regulator_setpoint ( regulator_id, setpoint_celsius)
1035+ . await
1036+ } else {
1037+ Err ( anyhow:: anyhow!( "Thermal regulation system is not running" ) )
1038+ }
1039+ }
1040+
8661041 /// Get the shared data source
8671042 ///
8681043 /// ### Returns
@@ -959,7 +1134,27 @@ impl Daemon {
9591134 /// Ok(())
9601135 /// }
9611136 /// ```
962- pub async fn join ( self ) -> Result < ( ) > {
1137+ pub async fn join ( mut self ) -> Result < ( ) > {
1138+ // Stop thermal regulation system if running
1139+ if let Some ( ref mut thermal_daemon) = self . thermal_regulation_daemon {
1140+ info ! ( "Stopping thermal regulation system" ) ;
1141+ if let Err ( e) = thermal_daemon. stop ( ) . await {
1142+ error ! ( "Failed to stop thermal regulation system: {}" , e) ;
1143+ }
1144+ }
1145+
1146+ // Stop other daemons
1147+ if let Some ( ref record_consumer) = self . record_consumer_daemon {
1148+ info ! ( "Stopping record consumer" ) ;
1149+ record_consumer. stop ( ) ;
1150+ }
1151+
1152+ if let Some ( ref processing_consumer) = self . processing_consumer_daemon {
1153+ info ! ( "Stopping processing consumer" ) ;
1154+ processing_consumer. stop ( ) . await ;
1155+ }
1156+
1157+ // Wait for all tasks to complete
9631158 for task in self . tasks {
9641159 match tokio:: time:: timeout ( Duration :: from_secs ( 5 ) , task) . await {
9651160 Ok ( result) => {
0 commit comments