From ffc0513926e4487843053a3bf7a2f3e01b91a5bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Weber?= Date: Thu, 30 Jul 2026 14:09:45 +0200 Subject: [PATCH 1/2] boiler example --- .../daq_move_BSPiezoMirror.py | 19 ---- .../daq_move_plugins/daq_move_Boiler.py | 95 +++++++++++++++++++ .../plugins_0D/daq_0Dviewer_Boiler.py | 72 ++++++++++++++ .../hardware/boiler.py | 56 +++++++++++ .../models/PIDModelBeamSteering.py | 2 +- .../models/PIDModelBoiler.py | 78 +++++++++++++++ 6 files changed, 302 insertions(+), 20 deletions(-) create mode 100644 src/pymodaq_plugins_mockexamples/daq_move_plugins/daq_move_Boiler.py create mode 100644 src/pymodaq_plugins_mockexamples/daq_viewer_plugins/plugins_0D/daq_0Dviewer_Boiler.py create mode 100644 src/pymodaq_plugins_mockexamples/hardware/boiler.py create mode 100644 src/pymodaq_plugins_mockexamples/models/PIDModelBoiler.py diff --git a/src/pymodaq_plugins_mockexamples/daq_move_plugins/daq_move_BSPiezoMirror.py b/src/pymodaq_plugins_mockexamples/daq_move_plugins/daq_move_BSPiezoMirror.py index 1238ef7..5fb4ae9 100644 --- a/src/pymodaq_plugins_mockexamples/daq_move_plugins/daq_move_BSPiezoMirror.py +++ b/src/pymodaq_plugins_mockexamples/daq_move_plugins/daq_move_BSPiezoMirror.py @@ -40,26 +40,7 @@ def commit_settings(self, param): def ini_stage(self, controller=None): """ - Initialize the controller and stages (axes) with given parameters. - ============== ================================================ ========================================================================================== - **Parameters** **Type** **Description** - - *controller* instance of the specific controller object If defined this hardware will use it and will not initialize its own controller instance - ============== ================================================ ========================================================================================== - - Returns - ------- - Easydict - dictionnary containing keys: - * *info* : string displaying various info - * *controller*: instance of the controller object in order to control other axes without the need to init the same controller twice - * *stage*: instance of the stage (axis or whatever) object - * *initialized*: boolean indicating if initialization has been done corretly - - See Also - -------- - daq_utils.ThreadCommand """ self.ini_stage_init(controller, BeamSteering()) self.controller.tau = self.settings['tau'] / 1000 diff --git a/src/pymodaq_plugins_mockexamples/daq_move_plugins/daq_move_Boiler.py b/src/pymodaq_plugins_mockexamples/daq_move_plugins/daq_move_Boiler.py new file mode 100644 index 0000000..a8a8b0f --- /dev/null +++ b/src/pymodaq_plugins_mockexamples/daq_move_plugins/daq_move_Boiler.py @@ -0,0 +1,95 @@ +from pymodaq.control_modules.move_utility_classes import DAQ_Move_base, comon_parameters_fun, main, DataActuatorType + +from pymodaq.utils.daq_utils import ThreadCommand +from pymodaq.utils.data import DataActuator + +from pymodaq_plugins_mockexamples.hardware.boiler import BoilerController + + +class DAQ_Move_Boiler(DAQ_Move_base): + """ + Wrapper object to access the Mock fonctionnalities, similar wrapper for all controllers. + + =============== ============== + **Attributes** **Type** + *params* dictionnary + =============== ============== + """ + _controller_units = 'W' + is_multiaxes = True + stage_names = ['Heater'] + _epsilon = 0.01 + + params = [] + comon_parameters_fun(is_multiaxes, stage_names, epsilon=_epsilon) + data_actuator_type = DataActuatorType.DataActuator + + def ini_attributes(self): + self.controller: BoilerController = None + + + def get_actuator_value(self) -> DataActuator: + """ + + """ + pos = DataActuator(self._title, data=self.controller.check_position()) + pos = self.get_position_with_scaling(pos) + return pos + + def commit_settings(self, param): + pass + + def ini_stage(self, controller: BoilerController = None): + """ + + """ + if self.is_master: + self.controller = BoilerController() # any object that will control the stages + else: + self.controller = controller + + info = "Boiler controller initialized" + initialized = True + + return info, initialized + + def move_abs(self, position: DataActuator): + """ + Make the absolute move from the given position after thread command signal was received in DAQ_Move_main. + + =============== ========= ======================= + **Parameters** **Type** **Description** + + *position* float The absolute position + =============== ========= ======================= + + See Also + -------- + DAQ_Move_base.set_position_with_scaling, DAQ_Move_base.poll_moving + + """ + position = self.check_bound(position) + self.target_value = position + self.controller.move_abs(self.target_value.value(self.axis_unit)) + + def move_rel(self, position: DataActuator): + """ + + """ + position = self.check_bound(self.current_value + position) - self.current_value + self.target_value = position + self.current_value + position = self.set_position_with_scaling(self.target_value) + + self.controller.move_rel(position.value(self.axis_unit)) + + def stop_motion(self): + """ + Call the specific move_done function (depending on the hardware). + + See Also + -------- + move_done + """ + self.move_done() + +if __name__ == '__main__': + main(__file__) \ No newline at end of file diff --git a/src/pymodaq_plugins_mockexamples/daq_viewer_plugins/plugins_0D/daq_0Dviewer_Boiler.py b/src/pymodaq_plugins_mockexamples/daq_viewer_plugins/plugins_0D/daq_0Dviewer_Boiler.py new file mode 100644 index 0000000..ea7d79f --- /dev/null +++ b/src/pymodaq_plugins_mockexamples/daq_viewer_plugins/plugins_0D/daq_0Dviewer_Boiler.py @@ -0,0 +1,72 @@ +import numpy as np + + +from pymodaq.utils.data import DataFromPlugins + +from pymodaq.control_modules.viewer_utility_classes import DAQ_Viewer_base, comon_parameters, main +from pymodaq_data import DataToExport + +from pymodaq_plugins_mockexamples.hardware.boiler import BoilerController + + + +class DAQ_0DViewer_Boiler(DAQ_Viewer_base): + """ + """ + params = comon_parameters + [ + {'title:': 'Noise', 'name': 'noise', 'type': 'float', 'value': BoilerController._noise}, + {'title:': 'Ambiant temp', 'name': 'ambiant_temp', 'type': 'float', + 'value': BoilerController._ambiant_temperature} + ] + + + def ini_attributes(self): + self.controller: BoilerController = None + self.ind_data = 0 + + def commit_settings(self, param): + """ + """ + if param.name() == 'noise': + self.controller.noise = param.value() + elif param.name() == 'ambiant_temp': + self.controller.ambiant_temp = param.value() + + + def ini_detector(self, controller=None): + """ + """ + if self.is_master: + self.controller = BoilerController() + else: + self.controller = controller + + initialized = True + info = 'Controller ok' + return info, initialized + + def close(self): + """ + not implemented. + """ + pass + + def grab_data(self, Naverage=1, **kwargs): + """ + + + """ + temperature = self.controller.grab() + self.dte_signal.emit(DataToExport('Boiler', data=[ + DataFromPlugins(name='Boiler', data=[np.array([temperature])], + dim='Data0D', labels=['Temperature'])])) + + def stop(self): + """ + not implemented. + """ + return "" + + +if __name__ == '__main__': + main(__file__) diff --git a/src/pymodaq_plugins_mockexamples/hardware/boiler.py b/src/pymodaq_plugins_mockexamples/hardware/boiler.py new file mode 100644 index 0000000..ae09192 --- /dev/null +++ b/src/pymodaq_plugins_mockexamples/hardware/boiler.py @@ -0,0 +1,56 @@ +from time import perf_counter +from qtpy.QtCore import QObject +from numpy.random import random +import numpy as np +from pymodaq import Q_ + + +class BoilerController(QObject): + _current_temperature = 20. + _ambiant_temperature = 19. + _noise = 0.1 + + def __init__(self, ): + super().__init__() + self.startTimer(10) + self._current_power = 0. + self._ellapsed_time = Q_(0., 's') + self._tau = Q_(1, 's') + + def timerEvent(self, event): + dt = Q_(perf_counter(), 's') - self._ellapsed_time + self._ellapsed_time += dt + + self._current_temperature += 1 * self._current_power * dt.m_as('s') + self._noise * (random() - 0.5) + # some heat dissipation + self._current_temperature -= 0.2 * dt.m_as('s') + self._current_temperature = np.clip(self._current_temperature, self.ambiant_temp, None) + + def check_position(self): + return self._current_power + + def move_abs(self, value): + self._current_power = value + + @property + def ambiant_temp(self): + return self._ambiant_temperature + + @ambiant_temp.setter + def ambiant_temp(self, temperature): + self._ambiant_temperature = temperature + + + @property + def noise(self): + return self._noise + + @noise.setter + def noise(self, noise): + self._noise = noise + + def move_rel(self, value): + self._current_power += value + + def grab(self): + return self._current_temperature diff --git a/src/pymodaq_plugins_mockexamples/models/PIDModelBeamSteering.py b/src/pymodaq_plugins_mockexamples/models/PIDModelBeamSteering.py index 5a8d571..696b1be 100644 --- a/src/pymodaq_plugins_mockexamples/models/PIDModelBeamSteering.py +++ b/src/pymodaq_plugins_mockexamples/models/PIDModelBeamSteering.py @@ -74,7 +74,7 @@ def convert_input(self, measurements: DataToExport) -> DataToExport: ], ) - def convert_output(self, outputs: List[float], dt, stab=True) -> DataToActuators: + def convert_output(self, outputs: List[float], dt: float, stab=True) -> DataToActuators: """ Convert the output of the PID in units to be fed into the actuator Parameters diff --git a/src/pymodaq_plugins_mockexamples/models/PIDModelBoiler.py b/src/pymodaq_plugins_mockexamples/models/PIDModelBoiler.py new file mode 100644 index 0000000..86580cc --- /dev/null +++ b/src/pymodaq_plugins_mockexamples/models/PIDModelBoiler.py @@ -0,0 +1,78 @@ +import numpy as np + +from pymodaq.extensions.pid.utils import PIDModelGeneric +from pymodaq.utils.data import DataToActuators, DataActuator +from pymodaq_data import DataToExport + + + +class PIDModelBoiler(PIDModelGeneric): + + limits = dict(max=dict(state=False, value=10), + min=dict(state=False, value=0), ) + konstants = dict(kp=0.001, ki=0, kd=0.0000) + + actuators_name = ["Heater"] + detectors_name = ['Thermometer'] + + Nsetpoints = 1 + setpoint_ini = [20] + setpoints_names = ['Temperature'] + + + + def __init__(self, pid_controller): + super().__init__(pid_controller) + + def update_settings(self, param): + """ + Get a parameter instance whose value has been modified by a user on the UI + Parameters + ---------- + param: (Parameter) instance of Parameter object + """ + if param.name() == '': + pass + + def ini_model(self): + super().ini_model() + self.pid_controller.modules_manager.get_mod_from_name('Thermometer', 'det').\ + settings.child('main_settings', 'wait_time').setValue(0) + + def convert_input(self, measurements: DataToExport) -> DataToExport: + """ + Convert the measurements in the units to be fed to the PID (same dimensionality as the setpoint) + Parameters + ---------- + measurements: DataToExport + DataToExport object from which the model extract a value of the same units as the setpoint + + Returns + ------- + DataToExport: the converted input as 0D DataCalculated stored in a DataToExport + """ + + return DataToExport('output', data=[measurements.get_data_from_name('Thermometer')]) + + def convert_output(self, outputs: list[float], dt: float, stab=True) -> DataToActuators: + """ + Convert the output of the PID in units to be fed into the actuator + Parameters + ---------- + outputs: (list of float) output value from the PID from which the model extract a value of the same units as the actuator + dt: (float) elapsed time in seconds since last call + + Returns + ------- + DataToActuatorPID: the converted output as a DataToActuatorPID object (derived from DataToExport) + """ + out_put_to_actuator = DataToActuators('Boiler', + mode='abs', + data=[DataActuator(name=self.actuators_name[0], + data = [np.atleast_1d(outputs[0] / dt)])],) + + + return out_put_to_actuator + + + From 27c79ee98dba83a98aa0fec5de27700d8a963ecb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Weber?= Date: Thu, 30 Jul 2026 15:19:50 +0200 Subject: [PATCH 2/2] renamed as Heater --- ...daq_move_Boiler.py => daq_move_MockHeater.py} | 10 +++++----- ...ewer_Boiler.py => daq_0Dviewer_MockHeater.py} | 16 ++++++++-------- .../hardware/{boiler.py => heater.py} | 2 +- .../{PIDModelBoiler.py => PIDModelMockHeater.py} | 8 +++----- 4 files changed, 17 insertions(+), 19 deletions(-) rename src/pymodaq_plugins_mockexamples/daq_move_plugins/{daq_move_Boiler.py => daq_move_MockHeater.py} (90%) rename src/pymodaq_plugins_mockexamples/daq_viewer_plugins/plugins_0D/{daq_0Dviewer_Boiler.py => daq_0Dviewer_MockHeater.py} (76%) rename src/pymodaq_plugins_mockexamples/hardware/{boiler.py => heater.py} (97%) rename src/pymodaq_plugins_mockexamples/models/{PIDModelBoiler.py => PIDModelMockHeater.py} (90%) diff --git a/src/pymodaq_plugins_mockexamples/daq_move_plugins/daq_move_Boiler.py b/src/pymodaq_plugins_mockexamples/daq_move_plugins/daq_move_MockHeater.py similarity index 90% rename from src/pymodaq_plugins_mockexamples/daq_move_plugins/daq_move_Boiler.py rename to src/pymodaq_plugins_mockexamples/daq_move_plugins/daq_move_MockHeater.py index a8a8b0f..b9eef54 100644 --- a/src/pymodaq_plugins_mockexamples/daq_move_plugins/daq_move_Boiler.py +++ b/src/pymodaq_plugins_mockexamples/daq_move_plugins/daq_move_MockHeater.py @@ -3,10 +3,10 @@ from pymodaq.utils.daq_utils import ThreadCommand from pymodaq.utils.data import DataActuator -from pymodaq_plugins_mockexamples.hardware.boiler import BoilerController +from pymodaq_plugins_mockexamples.hardware.heater import HeaterController -class DAQ_Move_Boiler(DAQ_Move_base): +class DAQ_Move_MockHeater(DAQ_Move_base): """ Wrapper object to access the Mock fonctionnalities, similar wrapper for all controllers. @@ -24,7 +24,7 @@ class DAQ_Move_Boiler(DAQ_Move_base): data_actuator_type = DataActuatorType.DataActuator def ini_attributes(self): - self.controller: BoilerController = None + self.controller: HeaterController = None def get_actuator_value(self) -> DataActuator: @@ -38,12 +38,12 @@ def get_actuator_value(self) -> DataActuator: def commit_settings(self, param): pass - def ini_stage(self, controller: BoilerController = None): + def ini_stage(self, controller: HeaterController = None): """ """ if self.is_master: - self.controller = BoilerController() # any object that will control the stages + self.controller = HeaterController() # any object that will control the stages else: self.controller = controller diff --git a/src/pymodaq_plugins_mockexamples/daq_viewer_plugins/plugins_0D/daq_0Dviewer_Boiler.py b/src/pymodaq_plugins_mockexamples/daq_viewer_plugins/plugins_0D/daq_0Dviewer_MockHeater.py similarity index 76% rename from src/pymodaq_plugins_mockexamples/daq_viewer_plugins/plugins_0D/daq_0Dviewer_Boiler.py rename to src/pymodaq_plugins_mockexamples/daq_viewer_plugins/plugins_0D/daq_0Dviewer_MockHeater.py index ea7d79f..c0a3993 100644 --- a/src/pymodaq_plugins_mockexamples/daq_viewer_plugins/plugins_0D/daq_0Dviewer_Boiler.py +++ b/src/pymodaq_plugins_mockexamples/daq_viewer_plugins/plugins_0D/daq_0Dviewer_MockHeater.py @@ -6,22 +6,22 @@ from pymodaq.control_modules.viewer_utility_classes import DAQ_Viewer_base, comon_parameters, main from pymodaq_data import DataToExport -from pymodaq_plugins_mockexamples.hardware.boiler import BoilerController +from pymodaq_plugins_mockexamples.hardware.heater import HeaterController -class DAQ_0DViewer_Boiler(DAQ_Viewer_base): +class DAQ_0DViewer_MockHeater(DAQ_Viewer_base): """ """ params = comon_parameters + [ - {'title:': 'Noise', 'name': 'noise', 'type': 'float', 'value': BoilerController._noise}, + {'title:': 'Noise', 'name': 'noise', 'type': 'float', 'value': HeaterController._noise}, {'title:': 'Ambiant temp', 'name': 'ambiant_temp', 'type': 'float', - 'value': BoilerController._ambiant_temperature} + 'value': HeaterController._ambiant_temperature} ] def ini_attributes(self): - self.controller: BoilerController = None + self.controller: HeaterController = None self.ind_data = 0 def commit_settings(self, param): @@ -37,7 +37,7 @@ def ini_detector(self, controller=None): """ """ if self.is_master: - self.controller = BoilerController() + self.controller = HeaterController() else: self.controller = controller @@ -57,8 +57,8 @@ def grab_data(self, Naverage=1, **kwargs): """ temperature = self.controller.grab() - self.dte_signal.emit(DataToExport('Boiler', data=[ - DataFromPlugins(name='Boiler', data=[np.array([temperature])], + self.dte_signal.emit(DataToExport(self._title, data=[ + DataFromPlugins(name=self._title, data=[np.array([temperature])], dim='Data0D', labels=['Temperature'])])) def stop(self): diff --git a/src/pymodaq_plugins_mockexamples/hardware/boiler.py b/src/pymodaq_plugins_mockexamples/hardware/heater.py similarity index 97% rename from src/pymodaq_plugins_mockexamples/hardware/boiler.py rename to src/pymodaq_plugins_mockexamples/hardware/heater.py index ae09192..99d8cb6 100644 --- a/src/pymodaq_plugins_mockexamples/hardware/boiler.py +++ b/src/pymodaq_plugins_mockexamples/hardware/heater.py @@ -5,7 +5,7 @@ from pymodaq import Q_ -class BoilerController(QObject): +class HeaterController(QObject): _current_temperature = 20. _ambiant_temperature = 19. _noise = 0.1 diff --git a/src/pymodaq_plugins_mockexamples/models/PIDModelBoiler.py b/src/pymodaq_plugins_mockexamples/models/PIDModelMockHeater.py similarity index 90% rename from src/pymodaq_plugins_mockexamples/models/PIDModelBoiler.py rename to src/pymodaq_plugins_mockexamples/models/PIDModelMockHeater.py index 86580cc..6695805 100644 --- a/src/pymodaq_plugins_mockexamples/models/PIDModelBoiler.py +++ b/src/pymodaq_plugins_mockexamples/models/PIDModelMockHeater.py @@ -6,14 +6,14 @@ -class PIDModelBoiler(PIDModelGeneric): +class PIDModelMockHeater(PIDModelGeneric): limits = dict(max=dict(state=False, value=10), min=dict(state=False, value=0), ) konstants = dict(kp=0.001, ki=0, kd=0.0000) actuators_name = ["Heater"] - detectors_name = ['Thermometer'] + detectors_name = ['Temperature'] Nsetpoints = 1 setpoint_ini = [20] @@ -36,8 +36,6 @@ def update_settings(self, param): def ini_model(self): super().ini_model() - self.pid_controller.modules_manager.get_mod_from_name('Thermometer', 'det').\ - settings.child('main_settings', 'wait_time').setValue(0) def convert_input(self, measurements: DataToExport) -> DataToExport: """ @@ -52,7 +50,7 @@ def convert_input(self, measurements: DataToExport) -> DataToExport: DataToExport: the converted input as 0D DataCalculated stored in a DataToExport """ - return DataToExport('output', data=[measurements.get_data_from_name('Thermometer')]) + return DataToExport('output', data=[measurements.get_data_from_name(self.detectors_name[0])]) def convert_output(self, outputs: list[float], dt: float, stab=True) -> DataToActuators: """