1
0
Fork 0

Compare commits

...

10 Commits

Author SHA1 Message Date
linuswck 4d8e16771e gui: Correct Pid Autotune output current limit 2024-10-29 12:59:15 +08:00
linuswck 4d332328c0 gui: Limit min PD Mon Pwr y axis range to 0-100uW 2024-10-23 12:24:57 +08:00
linuswck edb8099a26 Add pglive patch for setting y-axis min range span 2024-10-23 12:24:57 +08:00
linuswck 49dc8a9b96 gui: autotuner only set_i when relay state changes
- Previously, autotuner issues set_i cmd for each report obj received
- These change increase the maximum autotune-able PID update rate with GUI
2024-10-22 18:09:39 +08:00
linuswck a1b7538295 driver: Fix disconnection bug at high cmd rate
- Disconnection issue can be triggered by setting high polling rate
(500Hz) and then interactive with gui elements
2024-10-22 18:09:28 +08:00
linuswck 8f5a294a53 gui: Fix graphs' refresh rate to be 10Hz
- Reduce unnecessary CPU usage
2024-10-22 10:57:55 +08:00
linuswck 87b0e1bf67 gui: rm sIPrefix from PD Current readings 2024-10-21 11:15:15 +08:00
linuswck 35f66c3516 gui: Change LD, Thermostat Status Display Method
- Use combinations of label and stylesheet border to create visual cue for status
2024-10-18 17:33:54 +08:00
linuswck dd7e1bbbdd gui: Adjust y_axis limit range of graphs 2024-10-18 17:33:09 +08:00
linuswck a3b38fc6b7 gui: Unify the param tree unit formatting style 2024-10-18 17:33:01 +08:00
6 changed files with 243 additions and 177 deletions

View File

@ -62,6 +62,7 @@
inherit pname version;
hash = "sha256-jqj8X6H1N5mJQ4OrY5ANqRB0YJByqg/bNneEALWmH1A=";
};
patches = ./patches/0001-Add-option-for-setting-min-range-span-for-y-axis.patch;
buildInputs = [ pkgs.python3Packages.poetry-core ];
propagatedBuildInputs = with pkgs.python3Packages; [ pyqtgraph numpy ];
};

View File

@ -0,0 +1,61 @@
From 59a14e06320fd42d56cd7d953da337c0fe4357fa Mon Sep 17 00:00:00 2001
From: linuswck <linuswck@m-labs.hk>
Date: Tue, 22 Oct 2024 17:30:36 +0800
Subject: [PATCH] Add option for setting min range span for y-axis
---
pglive/sources/live_axis_range.py | 17 ++++++++++++++++-
1 file changed, 16 insertions(+), 1 deletion(-)
diff --git a/pglive/sources/live_axis_range.py b/pglive/sources/live_axis_range.py
index afbd806..9b62ba5 100644
--- a/pglive/sources/live_axis_range.py
+++ b/pglive/sources/live_axis_range.py
@@ -5,7 +5,7 @@ from typing import Optional, List, Tuple, Dict
class LiveAxisRange:
def __init__(self, roll_on_tick: int = 1, offset_left: float = 0., offset_right: float = 0., offset_top: float = 0.,
- offset_bottom: float = 0., fixed_range: Optional[List[float]] = None) -> None:
+ offset_bottom: float = 0., fixed_range: Optional[List[float]] = None, min_y_range_span: Optional[List[float]] = [None, None]) -> None:
self.roll_on_tick = roll_on_tick
self.offset_left = offset_left
self.offset_right = offset_right
@@ -21,6 +21,7 @@ class LiveAxisRange:
self.final_x_range = [0., 0.]
self.final_y_range = [0., 0.]
self.ignored_data_connectors: List[str] = []
+ self.min_y_range_span = min_y_range_span
def get_x_range(self, data_connector, tick: int) -> List[float]:
x, _ = data_connector.plot.getData()
@@ -116,6 +117,13 @@ class LiveAxisRange:
# therefore in that case we must set some range
final_range[0] -= 0.4
final_range[1] += 0.4
+
+ if self.min_y_range_span[0] is not None:
+ final_range[0] = min(self.min_y_range_span[0], final_range[0])
+
+ if self.min_y_range_span[1] is not None:
+ final_range[1] = max(self.min_y_range_span[1], final_range[1])
+
if self.final_y_range != final_range:
self.final_y_range = final_range
return self.final_y_range
@@ -139,6 +147,13 @@ class LiveAxisRange:
# therefore in that case we must set some range
final_range[0] -= 0.4
final_range[1] += 0.4
+
+ if self.min_y_range_span[0] is not None:
+ final_range[0] = min(self.min_y_range_span[0], final_range[0])
+
+ if self.min_y_range_span[1] is not None:
+ final_range[1] = max(self.min_y_range_span[1], final_range[1])
+
if self.final_y_range != final_range:
self.final_y_range = final_range
return self.final_y_range
--
2.44.1

View File

@ -879,7 +879,7 @@ class Kirdy:
else:
self._report_sig.emit(response)
else:
if self._msg_queue_get_report:
if self._msg_queue_get_report and response["msg_type"] == 'Report':
self._msg_queue_get_report = False
self._int_msg_queue.put_nowait_overwrite(response)
except asyncio.exceptions.CancelledError:

View File

@ -195,14 +195,17 @@ class Graphs:
ld_i_set_axis.showLabel()
ld_i_set_graph.setAxisItems({'left': ld_i_set_axis})
ld_i_set_graph.addItem(self._ld_i_set_plot)
self.ld_i_set_connector = DataConnector(self._ld_i_set_plot, max_points=self.max_samples)
ld_i_set_graph.y_range_controller = LiveAxisRange(fixed_range=[0.0, 0.4])
self.ld_i_set_connector = DataConnector(self._ld_i_set_plot, plot_rate=10.0, update_rate=10.0, max_points=self.max_samples)
self.connectors += [self.ld_i_set_connector]
pd_mon_pwr_axis = LiveAxis('left', text="Power", units="W")
pd_mon_pwr_axis.showLabel()
pd_mon_pwr_graph.y_range_controller = LiveAxisRange(min_y_range_span=[0.0, 100 / 1000 / 1000])
pd_mon_pwr_graph.setAxisItems({'left': pd_mon_pwr_axis})
pd_mon_pwr_graph.addItem(self._pd_mon_pwr_plot)
self.pd_mon_pwr_connector = DataConnector(self._pd_mon_pwr_plot, max_points=self.max_samples)
self.pd_mon_pwr_connector = DataConnector(self._pd_mon_pwr_plot, plot_rate=10.0, update_rate=10.0, max_points=self.max_samples)
self.connectors += [self.pd_mon_pwr_connector]
tec_temp_axis = LiveAxis('left', text="Temperature", units="")
@ -210,19 +213,18 @@ class Graphs:
tec_temp_graph.setAxisItems({'left': tec_temp_axis})
tec_temp_graph.addItem(self._tec_setpoint_plot)
tec_temp_graph.addItem(self._tec_temp_plot)
self.tec_setpoint_connector = DataConnector(self._tec_setpoint_plot, max_points=1)
self.tec_temp_connector = DataConnector(self._tec_temp_plot, max_points=self.max_samples)
self.tec_setpoint_connector = DataConnector(self._tec_setpoint_plot, plot_rate=10.0, update_rate=10.0, max_points=1)
self.tec_temp_connector = DataConnector(self._tec_temp_plot, plot_rate=10.0, update_rate=10.0, max_points=self.max_samples)
self.connectors += [self.tec_temp_connector, self.tec_setpoint_connector]
tec_i_axis = LiveAxis('left', text="Current", units="A")
tec_i_axis.showLabel()
tec_i_graph.setAxisItems({'left': tec_i_axis})
tec_i_graph.addLegend(brush=(50, 50, 200, 150))
tec_i_graph.y_range_controller = LiveAxisRange(fixed_range=[-1.0, 1.0])
tec_i_graph.addItem(self._tec_i_target_plot)
tec_i_graph.addItem(self._tec_i_measure_plot)
self.tec_i_target_connector = DataConnector(self._tec_i_target_plot, max_points=self.max_samples)
self.tec_i_measure_connector = DataConnector(self._tec_i_measure_plot, max_points=self.max_samples)
self.tec_i_target_connector = DataConnector(self._tec_i_target_plot, plot_rate=10.0, update_rate=10.0, max_points=self.max_samples)
self.tec_i_measure_connector = DataConnector(self._tec_i_measure_plot, plot_rate=10.0, update_rate=10.0, max_points=self.max_samples)
self.connectors += [self.tec_i_target_connector, self.tec_i_measure_connector]
def set_max_samples(self, max_samples):
@ -400,17 +402,11 @@ class MainWindow(QtWidgets.QMainWindow):
DEFAULT_IP_ADDR = '192.168.1.128'
DEFAULT_PORT = 1337
LASER_DIODE_STATUS = [
{'name': 'Status', 'title': 'Status: Power Off', 'expanded': True, 'type': 'group', 'children': [
{'name': 'Color', 'title': '', 'type': 'color', 'value': 'w', 'readonly': True, "compactHeight": False},
]}
]
LASER_DIODE_PARAMETERS = [
{'name': 'Readings', 'expanded': True, 'type': 'group', 'children': [
{'name': 'LD Current Set', 'type': 'float', 'suffix': 'A', 'siPrefix': True, 'readonly': True, "compactHeight": False},
{'name': 'PD Current', 'type': 'float', 'suffix': 'A', 'siPrefix': True, 'readonly': True, "compactHeight": False},
{'name': 'PD Power', 'type': 'float', 'suffix': 'W', 'siPrefix': True, 'readonly': True, "compactHeight": False},
{'name': 'LD Current Set', 'type': 'float', 'unit': 'mA', 'readonly': True, "compactHeight": False},
{'name': 'PD Current', 'type': 'float', 'unit': 'uA', 'readonly': True, "compactHeight": False},
{'name': 'PD Power', 'type': 'float', 'unit': 'mW', 'readonly': True, "compactHeight": False},
{'name': 'LF Mod Termination (50 Ohm)', 'type': 'list', 'limits': ['On', 'Off'], 'readonly': True, "compactHeight": False}
]},
{'name': 'Output Config', 'expanded': True, 'type': 'group', 'children': [
@ -430,16 +426,10 @@ class MainWindow(QtWidgets.QMainWindow):
]},
]
THERMOSTAT_STATUS = [
{'name': 'Status', 'title': 'Status: Power Off', 'expanded': True, 'type': 'group', 'children': [
{'name': 'Color', 'title': '', 'type': 'color', 'value': 'w', 'readonly': True, "compactHeight": False},
]}
]
THERMOSTAT_PARAMETERS = [
{'name': 'Readings', 'expanded': True, 'type': 'group', 'children': [
{'name': 'Temperature', 'type': 'float', 'format': '{value:.4f}', 'readonly': True, "compactHeight": False},
{'name': 'Current through TEC', 'type': 'float', 'suffix': 'A', 'siPrefix': True, 'decimals': 6, 'readonly': True, "compactHeight": False},
{'name': 'Temperature', 'type': 'float', 'unit': '', 'format': '{value:.4f}', 'readonly': True, "compactHeight": False},
{'name': 'Current through TEC', 'type': 'float', 'unit': 'mA', 'decimals': 6, 'readonly': True, "compactHeight": False},
]},
{'name': 'Output Config', 'expanded': True, 'type': 'group', 'children': [
{'name': 'Control Method', 'type': 'mutex', 'limits': ['Constant Current', 'Temperature PID'],
@ -484,14 +474,14 @@ class MainWindow(QtWidgets.QMainWindow):
{'name': 'Ki', 'type': 'float', 'step': 0.1, 'decimals': 16, 'unit': 'Hz', 'lock': False, 'target': 'thermostat', 'action': 'set_pid_ki', "compactHeight": False},
{'name': 'Kd', 'type': 'float', 'step': 0.1, 'decimals': 16, 'unit': 's', 'lock': False, 'target': 'thermostat', 'action': 'set_pid_kd', "compactHeight": False},
{'name': "PID Output Clamping", 'expanded': True, 'type': 'group', 'children': [
{'name': 'Minimum', 'type': 'float', 'step': 1, 'limits': (-1000, 1000), 'decimals': 6,
{'name': 'Minimum', 'type': 'float', 'step': 1, 'limits': (-3000, 3000), 'decimals': 6,
'unit': 'mA', 'lock': False, 'target': 'thermostat', 'action': 'set_pid_output_min', "compactHeight": False},
{'name': 'Maximum', 'type': 'float', 'step': 1, 'limits': (-1000, 1000), 'decimals': 6,
{'name': 'Maximum', 'type': 'float', 'step': 1, 'limits': (-3000, 3000), 'decimals': 6,
'unit': 'mA', 'lock': False, 'target': 'thermostat', 'action': 'set_pid_output_max', "compactHeight": False},
]},
{'name': 'PID Auto Tune', 'expanded': False, 'type': 'group', 'children': [
{'name': 'Target Temperature', 'type': 'float', 'value': 20.0, 'step': 0.1, 'unit': '', 'format': '{value:.4f}', "compactHeight": False},
{'name': 'Test Current', 'type': 'float', 'value': 1000, 'decimals': 6, 'step': 100, 'limits': (-1000, 1000), 'unit': 'mA', "compactHeight": False},
{'name': 'Test Current', 'type': 'float', 'value': 1000, 'decimals': 6, 'step': 100, 'limits': (-3000, 3000), 'unit': 'mA', "compactHeight": False},
{'name': 'Temperature Swing', 'type': 'float', 'value': 0.0, 'step': 0.0001, 'prefix': '±', 'unit': '', 'format': '{value:.4f}', "compactHeight": False},
{'name': 'Lookback', 'type': 'float', 'value': 5.0, 'step': 0.1, 'unit': 's', 'format': '{value:.4f}', "compactHeight": False},
{'name': 'Run', 'type': 'action', 'tip': 'Run'},
@ -559,15 +549,11 @@ class MainWindow(QtWidgets.QMainWindow):
else:
_traverse(param_tree)
_add_unit_to_title(self.LASER_DIODE_STATUS)
_add_unit_to_title(self.LASER_DIODE_PARAMETERS)
_add_unit_to_title(self.THERMOSTAT_STATUS)
_add_unit_to_title(self.THERMOSTAT_PARAMETERS)
self.params = [
Parameter.create(name=f"Laser Diode Status", type='group', value=0, children=self.LASER_DIODE_STATUS),
Parameter.create(name=f"Laser Diode Parameters", type='group', value=1, children=self.LASER_DIODE_PARAMETERS),
Parameter.create(name=f"Thermostat Status", type='group', value=2, children=self.THERMOSTAT_STATUS),
Parameter.create(name=f"Thermostat Parameters", type='group', value=3, children=self.THERMOSTAT_PARAMETERS),
]
self._set_param_tree()
@ -659,8 +645,8 @@ class MainWindow(QtWidgets.QMainWindow):
self.menu_action_update_net_settings.triggered.connect(show_update_net_settings_form)
def update_pd_mon_form_readings(self, ld_settings):
pwr_unit = self.params[1].child('Photodiode Monitor Config', 'LD Power Limit').opts.get("unit", None)
self.params[1].child('Photodiode Monitor Config', 'LD Power Limit').setOpts(limits= (0, siConvert(ld_settings["ld_pwr_limit"]["max"], pwr_unit)))
pwr_unit = self.params[0].child('Photodiode Monitor Config', 'LD Power Limit').opts.get("unit", None)
self.params[0].child('Photodiode Monitor Config', 'LD Power Limit').setOpts(limits= (0, siConvert(ld_settings["ld_pwr_limit"]["max"], pwr_unit)))
self.cfg_pd_mon_form.settable_pwr_range_display_lbl.setText(f" 0 - {siConvert(ld_settings['ld_pwr_limit']['max'], pwr_unit):.4f}")
self.cfg_pd_mon_form.cfg_pwr_limit_spinbox.setMaximum(siConvert(ld_settings['ld_pwr_limit']['max'], pwr_unit))
@ -792,19 +778,19 @@ class MainWindow(QtWidgets.QMainWindow):
self.kirdy.task_dispatcher(self.kirdy.laser.set_ld_pwr_limit(settings['laser']['ld_pwr_limit']['max']))
self.cfg_pd_mon_form.apply_pwr_limit_max_btn.clicked.connect(apply_ld_pwr_limit_max)
ld_pwr_limit_unit = self.params[1].child('Photodiode Monitor Config', 'LD Power Limit').opts["unit"]
ld_pwr_limit_unit = self.params[0].child('Photodiode Monitor Config', 'LD Power Limit').opts["unit"]
ld_pwr_limit_text = self.cfg_pd_mon_form.cfg_pwr_limit_lbl.text()
self.cfg_pd_mon_form.cfg_pwr_limit_lbl.setText(ld_pwr_limit_text.replace(":", f" ({ld_pwr_limit_unit}):"))
self.cfg_pd_mon_form.cfg_pwr_limit_spinbox.unit = ld_pwr_limit_unit
settable_pwr_limit_text = self.cfg_pd_mon_form.settable_pwr_range_lbl.text()
self.cfg_pd_mon_form.settable_pwr_range_lbl.setText(settable_pwr_limit_text.replace(":", f" ({ld_pwr_limit_unit}):"))
pd_responsitivity_unit = self.params[1].child('Photodiode Monitor Config', 'Responsitivity').opts["unit"]
pd_responsitivity_unit = self.params[0].child('Photodiode Monitor Config', 'Responsitivity').opts["unit"]
pd_responsitivity_text = self.cfg_pd_mon_form.cfg_responsitivity_lbl.text()
self.cfg_pd_mon_form.cfg_responsitivity_lbl.setText(pd_responsitivity_text.replace(":", f" ({pd_responsitivity_unit}):"))
self.cfg_pd_mon_form.cfg_responsitivity_spinbox.unit = pd_responsitivity_unit
pd_dark_current_unit = self.params[1].child('Photodiode Monitor Config', 'Dark Current').opts["unit"]
pd_dark_current_unit = self.params[0].child('Photodiode Monitor Config', 'Dark Current').opts["unit"]
pd_dark_current_text = self.cfg_pd_mon_form.cfg_dark_current_lbl.text()
self.cfg_pd_mon_form.cfg_dark_current_lbl.setText(pd_dark_current_text.replace(":", f" ({pd_dark_current_unit}):"))
self.cfg_pd_mon_form.cfg_dark_current_spinbox.unit = pd_dark_current_unit
@ -817,26 +803,23 @@ class MainWindow(QtWidgets.QMainWindow):
self.cfg_adc_filter_form.apply_btn.clicked.connect(apply_adc_filter_settings)
def _set_param_tree(self):
status = self.ld_status
status.setHeaderHidden(True)
status.setParameters(self.params[0], showTop=False)
self.ld_status.setStyleSheet("border: 3px solid #A1A1A1;") # Light Gray
self.tec_status.setStyleSheet("border: 3px solid #A1A1A1;") # Light Gray
tree = self.ld_tree
tree.setHeaderHidden(True)
tree.setParameters(self.params[0], showTop=False)
self.params[0].sigTreeStateChanged.connect(self.send_command)
tree = self.tec_tree
tree.setHeaderHidden(True)
tree.setParameters(self.params[1], showTop=False)
self.params[1].sigTreeStateChanged.connect(self.send_command)
status = self.tec_status
status.setHeaderHidden(True)
status.setParameters(self.params[2], showTop=False)
tree = self.tec_tree
tree.setHeaderHidden(True)
tree.setParameters(self.params[3], showTop=False)
self.params[3].sigTreeStateChanged.connect(self.send_command)
self.prev_autotuner_state = None
@asyncSlot()
async def autotune(param):
self.prev_autotuner_state = None
match self.autotuner.state():
case PIDAutotuneState.STATE_OFF:
settings = await self.kirdy.device.get_settings_summary()
@ -846,6 +829,7 @@ class MainWindow(QtWidgets.QMainWindow):
param.parent().child('Temperature Swing').value(),
1.0 / settings['thermostat']['temp_adc_settings']['rate'],
param.parent().child('Lookback').value())
print(param.parent().child('Lookback').value())
self.autotuner.setReady()
param.setOpts(title="Stop")
self.kirdy.task_dispatcher(self.kirdy.thermostat.set_constant_current_control_mode())
@ -861,20 +845,20 @@ class MainWindow(QtWidgets.QMainWindow):
self.background_task_lbl.setText("Ready.")
self.loading_spinner.stop()
self.loading_spinner.hide()
self.params[3].child('PID Config', 'PID Auto Tune', 'Run').sigActivated.connect(autotune)
self.params[1].child('PID Config', 'PID Auto Tune', 'Run').sigActivated.connect(autotune)
@pyqtSlot()
def show_pd_mon_cfg_form(param):
ld_pwr_limit = self.params[1].child('Photodiode Monitor Config', 'LD Power Limit').value()
pd_responsitivity = self.params[1].child('Photodiode Monitor Config', 'Responsitivity').value()
pd_dark_current = self.params[1].child('Photodiode Monitor Config', 'Dark Current').value()
ld_pwr_limit = self.params[0].child('Photodiode Monitor Config', 'LD Power Limit').value()
pd_responsitivity = self.params[0].child('Photodiode Monitor Config', 'Responsitivity').value()
pd_dark_current = self.params[0].child('Photodiode Monitor Config', 'Dark Current').value()
self.cfg_pd_mon_form.cfg_responsitivity_spinbox.setValue(pd_responsitivity)
self.cfg_pd_mon_form.cfg_pwr_limit_spinbox.setValue(ld_pwr_limit)
self.cfg_pd_mon_form.cfg_dark_current_spinbox.setValue(pd_dark_current)
self.cfg_pd_mon_form.show()
self.params[1].child('Photodiode Monitor Config', 'Configure Photodiode Monitor').sigActivated.connect(show_pd_mon_cfg_form)
self.params[0].child('Photodiode Monitor Config', 'Configure Photodiode Monitor').sigActivated.connect(show_pd_mon_cfg_form)
@asyncSlot()
async def show_adc_filter_cfg_form(param):
@ -891,7 +875,7 @@ class MainWindow(QtWidgets.QMainWindow):
self.cfg_adc_filter_form.filter_sampling_rate_cbox.setCurrentIndex(self.cfg_adc_filter_form.filter_sampling_rate_cbox.findText(filter_rate))
self.cfg_adc_filter_form.show()
self.params[3].child('Temperature ADC Filter Settings', 'Configure ADC Filter').sigActivated.connect(show_adc_filter_cfg_form)
self.params[1].child('Temperature ADC Filter Settings', 'Configure ADC Filter').sigActivated.connect(show_adc_filter_cfg_form)
@pyqtSlot(str)
def cmd_cannot_execute(self, kirdy_msg):
@ -899,40 +883,43 @@ class MainWindow(QtWidgets.QMainWindow):
self.info_box.setWindowTitle("Command fails to execute")
self.info_box.show()
@pyqtSlot(dict)
def autotune_tick(self, report):
match self.autotuner.state():
case PIDAutotuneState.STATE_READY | PIDAutotuneState.STATE_RELAY_STEP_UP | PIDAutotuneState.STATE_RELAY_STEP_DOWN:
self.autotuner.run(report['thermostat']['temperature'], report['ts']/1000)
self.kirdy.task_dispatcher(self.kirdy.thermostat.set_tec_i_out(self.autotuner.output()))
case PIDAutotuneState.STATE_SUCCEEDED:
kp, ki, kd = self.autotuner.get_tec_pid()
self.autotuner.setOff()
self.params[3].child('PID Config', 'PID Auto Tune', 'Run').setOpts(title="Run")
self.kirdy.task_dispatcher(self.kirdy.thermostat.set_pid_kp(kp))
self.kirdy.task_dispatcher(self.kirdy.thermostat.set_pid_ki(ki))
self.kirdy.task_dispatcher(self.kirdy.thermostat.set_pid_kd(kd))
self.kirdy.task_dispatcher(self.kirdy.thermostat.set_pid_control_mode())
self.kirdy.task_dispatcher(self.kirdy.thermostat.set_temperature_setpoint(self.params[3].child('PID Config', 'PID Auto Tune', 'Target Temperature').value()))
self.kirdy_handler.report_update_sig.disconnect(self.autotune_tick)
self.background_task_lbl.setText("Ready.")
self.loading_spinner.stop()
self.loading_spinner.hide()
self.info_box.setWindowTitle("PID AutoTune Success")
self.info_box.setText("PID Config has been loaded to Thermostat.\nRegulating temperature.")
self.info_box.show()
case PIDAutotuneState.STATE_FAILED:
self.autotuner.setOff()
self.params[3].child('PID Config', 'PID Auto Tune', 'Run').setOpts(title="Run")
self.kirdy.task_dispatcher(self.kirdy.thermostat.set_tec_i_out(0.0))
self.kirdy_handler.report_update_sig.disconnect(self.autotune_tick)
self.background_task_lbl.setText("Ready.")
self.loading_spinner.stop()
self.loading_spinner.hide()
self.info_box.setWindowTitle("PID Autotune Failed")
self.info_box.setText("PID Autotune is failed.")
self.info_box.show()
@asyncSlot(dict)
async def autotune_tick(self, report):
self.autotuner.run(report['thermostat']['temperature'], report['ts']/1000)
if self.prev_autotuner_state != self.autotuner.state():
match self.autotuner.state():
case PIDAutotuneState.STATE_READY | PIDAutotuneState.STATE_RELAY_STEP_UP | PIDAutotuneState.STATE_RELAY_STEP_DOWN:
await self.kirdy.thermostat.set_tec_i_out(self.autotuner.output())
self.prev_autotuner_state = self.autotuner.state()
case PIDAutotuneState.STATE_SUCCEEDED:
kp, ki, kd = self.autotuner.get_tec_pid()
self.autotuner.setOff()
self.params[1].child('PID Config', 'PID Auto Tune', 'Run').setOpts(title="Run")
self.kirdy.task_dispatcher(self.kirdy.thermostat.set_pid_kp(kp))
self.kirdy.task_dispatcher(self.kirdy.thermostat.set_pid_ki(ki))
self.kirdy.task_dispatcher(self.kirdy.thermostat.set_pid_kd(kd))
self.kirdy.task_dispatcher(self.kirdy.thermostat.set_pid_control_mode())
self.kirdy.task_dispatcher(self.kirdy.thermostat.set_temperature_setpoint(self.params[1].child('PID Config', 'PID Auto Tune', 'Target Temperature').value()))
self.kirdy_handler.report_update_sig.disconnect(self.autotune_tick)
self.background_task_lbl.setText("Ready.")
self.loading_spinner.stop()
self.loading_spinner.hide()
self.info_box.setWindowTitle("PID AutoTune Success")
self.info_box.setText("PID Config has been loaded to Thermostat.\nRegulating temperature.")
self.info_box.show()
self.prev_autotuner_state = None
case PIDAutotuneState.STATE_FAILED:
self.autotuner.setOff()
self.params[1].child('PID Config', 'PID Auto Tune', 'Run').setOpts(title="Run")
self.kirdy.task_dispatcher(self.kirdy.thermostat.set_tec_i_out(0.0))
self.kirdy_handler.report_update_sig.disconnect(self.autotune_tick)
self.background_task_lbl.setText("Ready.")
self.loading_spinner.stop()
self.loading_spinner.hide()
self.info_box.setWindowTitle("PID Autotune Failed")
self.info_box.setText("PID Autotune is failed.")
self.info_box.show()
self.prev_autotuner_state = None
@pyqtSlot(bool)
def _on_connection_changed(self, result):
@ -1003,17 +990,17 @@ class MainWindow(QtWidgets.QMainWindow):
def update_ld_ctrl_panel_settings(self, settings):
try:
settings = settings['laser']
with QSignalBlocker(self.params[1]):
self.params[1].child('Output Config', 'LD Current Set').setValuewithLock(settings["ld_drive_current"]['value'])
self.params[1].child('Output Config', 'LD Terminals Short').setValuewithLock(settings["ld_terms_short"])
self.params[1].child('Output Config', 'Default Power On').setValuewithLock(settings["default_pwr_on"])
self.params[1].child('Photodiode Monitor Config', 'LD Power Limit').setValuewithLock(settings["ld_pwr_limit"]["value"])
with QSignalBlocker(self.params[0]):
self.params[0].child('Output Config', 'LD Current Set').setValuewithLock(settings["ld_drive_current"]['value'])
self.params[0].child('Output Config', 'LD Terminals Short').setValuewithLock(settings["ld_terms_short"])
self.params[0].child('Output Config', 'Default Power On').setValuewithLock(settings["default_pwr_on"])
self.params[0].child('Photodiode Monitor Config', 'LD Power Limit').setValuewithLock(settings["ld_pwr_limit"]["value"])
self.update_pd_mon_form_readings(settings)
if settings["pd_mon_params"]["responsitivity"] is not None:
self.params[1].child('Photodiode Monitor Config', 'Responsitivity').setValuewithLock(settings["pd_mon_params"]["responsitivity"])
self.params[0].child('Photodiode Monitor Config', 'Responsitivity').setValuewithLock(settings["pd_mon_params"]["responsitivity"])
else:
self.params[1].child('Photodiode Monitor Config', 'Responsitivity').setValuewithLock(0)
self.params[1].child('Photodiode Monitor Config', 'Dark Current').setValuewithLock(settings["pd_mon_params"]["i_dark"])
self.params[0].child('Photodiode Monitor Config', 'Responsitivity').setValuewithLock(0)
self.params[0].child('Photodiode Monitor Config', 'Dark Current').setValuewithLock(settings["pd_mon_params"]["i_dark"])
except Exception as e:
logging.error(f"Params tree cannot be updated. Data:{settings}", exc_info=True)
@ -1023,20 +1010,24 @@ class MainWindow(QtWidgets.QMainWindow):
report = report['laser']
with QSignalBlocker(self.params[0]):
if report['pwr_excursion']:
self.params[0].child('Status', 'Color').setValuewithLock('r')
self.params[0].child('Status').setOpts(title='Status: OverPower Alarm')
self.ld_status.setStyleSheet("border: 3px solid red;")
self.ld_status.setText(' Status: OverPower Alarm')
else:
self.params[0].child('Status', 'Color').setValuewithLock('g' if report['pwr_on'] else 'w')
self.params[0].child('Status').setOpts(title='Status: Power On' if report['pwr_on'] else 'Status: Power Off')
if report['pwr_on']:
self.ld_status.setStyleSheet("border: 3px solid #44E62C;") # Light Green
self.ld_status.setText(' Status: Power On')
else:
self.ld_status.setStyleSheet("border: 3px solid #A1A1A1;") # Light Gray
self.ld_status.setText(' Status: Power Off')
with QSignalBlocker(self.params[1]):
self.params[1].child('Readings', 'LD Current Set').setValuewithLock(report["ld_i_set"])
self.params[1].child('Readings', 'PD Current').setValuewithLock(report["pd_i"])
with QSignalBlocker(self.params[0]):
self.params[0].child('Readings', 'LD Current Set').setValuewithLock(report["ld_i_set"])
self.params[0].child('Readings', 'PD Current').setValuewithLock(report["pd_i"])
if report["pd_pwr"] is not None:
self.params[1].child('Readings', 'PD Power').setValuewithLock(report["pd_pwr"])
self.params[0].child('Readings', 'PD Power').setValuewithLock(report["pd_pwr"])
else:
self.params[1].child('Readings', 'PD Power').setValuewithLock(0)
self.params[1].child('Readings', 'LF Mod Termination (50 Ohm)').setValuewithLock(report["term_50ohm"])
self.params[0].child('Readings', 'PD Power').setValuewithLock(0)
self.params[0].child('Readings', 'LF Mod Termination (50 Ohm)').setValuewithLock(report["term_50ohm"])
except Exception as e:
logging.error(f"Params tree cannot be updated. Data:{report}", exc_info=True)
@ -1044,29 +1035,29 @@ class MainWindow(QtWidgets.QMainWindow):
def update_thermostat_ctrl_panel_settings(self, settings):
try:
settings = settings['thermostat']
with QSignalBlocker(self.params[3]):
self.params[3].child('Output Config', 'Control Method').setValuewithLock("Temperature PID" if settings["pid_engaged"] else "Constant Current")
self.params[3].child('Output Config', 'Control Method', 'Set Current').setValuewithLock(settings["tec_settings"]['i_set']['value'])
self.params[3].child('Output Config', 'Control Method', 'Set Temperature').setValuewithLock(float(settings["temperature_setpoint"]))
self.params[3].child('Output Config', 'Limits', 'Max Cooling Current').setValuewithLock(settings["tec_settings"]['max_i_pos']['value'])
self.params[3].child('Output Config', 'Limits', 'Max Heating Current').setValuewithLock(settings["tec_settings"]['max_i_neg']['value'])
self.params[3].child('Output Config', 'Limits', 'Max Voltage Difference').setValuewithLock(settings["tec_settings"]['max_v']['value'])
self.params[3].child('Output Config', 'Default Power On').setValuewithLock(settings["default_pwr_on"])
with QSignalBlocker(self.params[1]):
self.params[1].child('Output Config', 'Control Method').setValuewithLock("Temperature PID" if settings["pid_engaged"] else "Constant Current")
self.params[1].child('Output Config', 'Control Method', 'Set Current').setValuewithLock(settings["tec_settings"]['i_set']['value'])
self.params[1].child('Output Config', 'Control Method', 'Set Temperature').setValuewithLock(float(settings["temperature_setpoint"]))
self.params[1].child('Output Config', 'Limits', 'Max Cooling Current').setValuewithLock(settings["tec_settings"]['max_i_pos']['value'])
self.params[1].child('Output Config', 'Limits', 'Max Heating Current').setValuewithLock(settings["tec_settings"]['max_i_neg']['value'])
self.params[1].child('Output Config', 'Limits', 'Max Voltage Difference').setValuewithLock(settings["tec_settings"]['max_v']['value'])
self.params[1].child('Output Config', 'Default Power On').setValuewithLock(settings["default_pwr_on"])
filter_type = settings['temp_adc_settings']['filter_type']
filter_rate = settings['temp_adc_settings'][getattr(getattr(FilterConfig, filter_type), "_odr_type")]
self.update_adc_filter_form_readings(filter_type, filter_rate)
self.params[3].child('Temperature ADC Filter Settings', 'Filter Type').setValue(filter_type)
self.params[3].child('Temperature ADC Filter Settings', 'Sampling Rate').setValue(settings['temp_adc_settings']['rate'])
self.params[3].child('Temperature Monitor Config', 'Upper Limit').setValuewithLock(settings["temp_mon_settings"]['upper_limit'])
self.params[3].child('Temperature Monitor Config', 'Lower Limit').setValuewithLock(settings["temp_mon_settings"]['lower_limit'])
self.params[3].child('PID Config', 'Kp').setValuewithLock(settings["pid_params"]['kp'])
self.params[3].child('PID Config', 'Ki').setValuewithLock(settings["pid_params"]['ki'])
self.params[3].child('PID Config', 'Kd').setValuewithLock(settings["pid_params"]['kd'])
self.params[3].child('PID Config', 'PID Output Clamping', 'Minimum').setValuewithLock(settings["pid_params"]['output_min'])
self.params[3].child('PID Config', 'PID Output Clamping', 'Maximum').setValuewithLock(settings["pid_params"]['output_max'])
self.params[3].child('Thermistor Settings', 'T₀').setValuewithLock(settings["thermistor_params"]['t0'])
self.params[3].child('Thermistor Settings', 'R₀').setValuewithLock(settings["thermistor_params"]['r0'])
self.params[3].child('Thermistor Settings', 'B').setValuewithLock(settings["thermistor_params"]['b'])
self.params[1].child('Temperature ADC Filter Settings', 'Filter Type').setValue(filter_type)
self.params[1].child('Temperature ADC Filter Settings', 'Sampling Rate').setValue(settings['temp_adc_settings']['rate'])
self.params[1].child('Temperature Monitor Config', 'Upper Limit').setValuewithLock(settings["temp_mon_settings"]['upper_limit'])
self.params[1].child('Temperature Monitor Config', 'Lower Limit').setValuewithLock(settings["temp_mon_settings"]['lower_limit'])
self.params[1].child('PID Config', 'Kp').setValuewithLock(settings["pid_params"]['kp'])
self.params[1].child('PID Config', 'Ki').setValuewithLock(settings["pid_params"]['ki'])
self.params[1].child('PID Config', 'Kd').setValuewithLock(settings["pid_params"]['kd'])
self.params[1].child('PID Config', 'PID Output Clamping', 'Minimum').setValuewithLock(settings["pid_params"]['output_min'])
self.params[1].child('PID Config', 'PID Output Clamping', 'Maximum').setValuewithLock(settings["pid_params"]['output_max'])
self.params[1].child('Thermistor Settings', 'T₀').setValuewithLock(settings["thermistor_params"]['t0'])
self.params[1].child('Thermistor Settings', 'R₀').setValuewithLock(settings["thermistor_params"]['r0'])
self.params[1].child('Thermistor Settings', 'B').setValuewithLock(settings["thermistor_params"]['b'])
self.graphs.set_temp_setpoint_line(temp=round(settings["temperature_setpoint"], 4))
self.graphs.set_temp_setpoint_line(visible=settings['pid_engaged'])
except Exception as e:
@ -1075,24 +1066,26 @@ class MainWindow(QtWidgets.QMainWindow):
@pyqtSlot(dict)
def update_thermostat_ctrl_panel_readings(self, report):
try:
report = report['thermostat']
with QSignalBlocker(self.params[2]):
if report['temp_mon_status']['over_temp_alarm']:
self.params[2].child('Status', 'Color').setValuewithLock('r')
self.params[2].child('Status').setOpts(title='Status: OverTemperature Alarm')
report = report['thermostat']
if report['temp_mon_status']['over_temp_alarm']:
self.tec_status.setStyleSheet("border: 3px solid red;")
self.tec_status.setText(' Status: OverTemperature Alarm')
else:
if report['pwr_on']:
self.tec_status.setStyleSheet("border: 3px solid #44E62C;") # Light Green
self.tec_status.setText(' Status: Power On')
else:
self.params[2].child('Status', 'Color').setValuewithLock('g' if report['pwr_on'] else 'w')
self.params[2].child('Status').setOpts(title='Status: Power On' if report['pwr_on'] else 'Status: Power Off')
self.tec_status.setStyleSheet("border: 3px solid #A1A1A1;") # Light Gray
self.tec_status.setText(' Status: Power Off')
with QSignalBlocker(self.params[3]):
with QSignalBlocker(self.params[1]):
if report["temperature"] == None:
self.params[3].child('Readings', 'Temperature').setValuewithLock(-273.15)
self.params[1].child('Readings', 'Temperature').setValuewithLock(-273.15)
else:
self.params[3].child('Readings', 'Temperature').setValuewithLock(report["temperature"])
self.params[3].child('Readings', 'Current through TEC').setValuewithLock(report["tec_i"])
self.params[1].child('Readings', 'Temperature').setValuewithLock(report["temperature"])
self.params[1].child('Readings', 'Current through TEC').setValuewithLock(report["tec_i"])
rate = 1 / (report['interval']['ms'] / 1e3 + report['interval']['us'] / 1e6)
self.params[3].child('Temperature ADC Filter Settings', 'Recorded Sampling Rate').setValue(rate)
self.params[1].child('Temperature ADC Filter Settings', 'Recorded Sampling Rate').setValue(rate)
self.cfg_adc_filter_form.recorded_sampling_rate_reading_lbl.setText(f"{rate:.2f}")
except Exception as e:
logging.error(f"Params tree cannot be updated. Data:{report}", exc_info=True)

View File

@ -78,10 +78,11 @@
<font>
<pointsize>14</pointsize>
<bold>true</bold>
<underline>false</underline>
</font>
</property>
<property name="text">
<string> Laser Diode</string>
<string> Laser Diode </string>
</property>
<property name="wordWrap">
<bool>false</bool>
@ -92,23 +93,27 @@
</widget>
</item>
<item>
<widget class="ParameterTree" name="ld_status" native="true">
<property name="enabled">
<bool>false</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>57</height>
</size>
</property>
</widget>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLabel" name="ld_status">
<property name="enabled">
<bool>false</bool>
</property>
<property name="font">
<font>
<pointsize>12</pointsize>
<bold>true</bold>
</font>
</property>
<property name="styleSheet">
<string notr="true"/>
</property>
<property name="text">
<string> Status: Power Off</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="ParameterTree" name="ld_tree" native="true">
@ -166,10 +171,11 @@
<font>
<pointsize>14</pointsize>
<bold>true</bold>
<underline>false</underline>
</font>
</property>
<property name="text">
<string> Thermostat</string>
<string> Thermostat </string>
</property>
<property name="wordWrap">
<bool>false</bool>
@ -180,23 +186,27 @@
</widget>
</item>
<item>
<widget class="ParameterTree" name="tec_status" native="true">
<property name="enabled">
<bool>false</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>57</height>
</size>
</property>
</widget>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<widget class="QLabel" name="tec_status">
<property name="enabled">
<bool>false</bool>
</property>
<property name="font">
<font>
<pointsize>12</pointsize>
<bold>true</bold>
</font>
</property>
<property name="styleSheet">
<string notr="true"/>
</property>
<property name="text">
<string> Status: Power Off</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="ParameterTree" name="tec_tree" native="true">

View File

@ -3,4 +3,5 @@ ParameterTree:disabled { color: gray }
QToolButton:disabled { color: gray }
QDoubleSpinBox:disabled { color: gray }
QCheckBox:disabled { color: gray }
QMenu:disabled { color: gray }
QMenu:disabled { color: gray }
QLabel:disabled { color: gray }