1
0
forked from M-Labs/kirdy

Compare commits

...

12 Commits

Author SHA1 Message Date
85f81ee8e4 Change default tcp port 1337 -> 1550 2025-01-09 16:38:02 +08:00
5b00bd60c7 rm erase_flash_settings.bin 2025-01-09 10:52:20 +08:00
4d8e16771e gui: Correct Pid Autotune output current limit 2024-10-29 12:59:15 +08:00
4d332328c0 gui: Limit min PD Mon Pwr y axis range to 0-100uW 2024-10-23 12:24:57 +08:00
edb8099a26 Add pglive patch for setting y-axis min range span 2024-10-23 12:24:57 +08:00
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
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
8f5a294a53 gui: Fix graphs' refresh rate to be 10Hz
- Reduce unnecessary CPU usage
2024-10-22 10:57:55 +08:00
87b0e1bf67 gui: rm sIPrefix from PD Current readings 2024-10-21 11:15:15 +08:00
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
dd7e1bbbdd gui: Adjust y_axis limit range of graphs 2024-10-18 17:33:09 +08:00
a3b38fc6b7 gui: Unify the param tree unit formatting style 2024-10-18 17:33:01 +08:00
14 changed files with 253 additions and 193 deletions

View File

@ -70,16 +70,10 @@ openocd -f interface/stlink.cfg -f target/stm32f4x.cfg -c "program target/thumbv
```
## Erasing Flash Settings
The flash settings are stored in the last flash sector(ID: 11) of bank 0 of stm32f405. You can erase it with JTAG/SWD adapter or by putting the device in Dfu mode. You may find it useful if you have set network settings incorrectly.
The flash settings are stored in the last flash sector(ID: 11) of bank 0 of stm32f405. You can erase it with JTAG/SWD adapter. You may find it useful if you have set network settings incorrectly.
With JTAG/SWD adapter connected, issue the following command:
```shell
openocd -f interface/stlink.cfg -f target/stm32f4x.cfg -c "flash init; init; halt; flash erase_sector 0 11 last; reset; exit"
```
With STM32 in DFU Mode, connect the USB Type C cable and then issue the following command:
```
dfu-util -a 0 -s 0x080E0000:leave -D erase_flash_settings.bin
```

Binary file not shown.

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

@ -32,7 +32,7 @@ async def device_cfg(kirdy: Kirdy):
# Network Settings will be updated on next reboot.
await kirdy.device.set_ip_settings(
addr="192.168.1.128",
port=1337,
port=1550,
prefix_len=24,
gateway="192.168.1.1"
)

View File

@ -181,7 +181,7 @@ class Device:
self._send_cmd = send_cmd_handler
self._send_raw_cmd = send_raw_cmd_handler
async def set_ip_settings(self, addr="192.168.1.128", port=1337, prefix_len=24, gateway="192.168.1.1"):
async def set_ip_settings(self, addr="192.168.1.128", port=1550, prefix_len=24, gateway="192.168.1.1"):
"""
Upon command execution, the ip settings are saved into flash and are effective upon next reboot.
"""
@ -693,7 +693,7 @@ class Kirdy:
"""
self._err_msg_sig = sig
def start_session(self, host='192.168.1.128', port=1337):
def start_session(self, host='192.168.1.128', port=1550):
"""
Start Kirdy Connection Session.
In case of disconnection, all the queued tasks are cleared and the handler task retries TCP connection indefinitely.
@ -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):
@ -398,19 +400,13 @@ class MainWindow(QtWidgets.QMainWindow):
"""The maximum number of sample points to store."""
DEFAULT_MAX_SAMPLES = 1000
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},
]}
]
DEFAULT_PORT = 1550
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

@ -258,7 +258,7 @@
</size>
</property>
<property name="text">
<string>1337</string>
<string>1550</string>
</property>
<property name="maxLength">
<number>5</number>

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 }

View File

@ -156,6 +156,6 @@ class Ui_Conn_Settings_Form(object):
self.dot_2_label.setText(_translate("Conn_Settings_Form", "."))
self.addr_in_3.setText(_translate("Conn_Settings_Form", "128"))
self.port_no_label.setText(_translate("Conn_Settings_Form", "Port:"))
self.port_in.setText(_translate("Conn_Settings_Form", "1337"))
self.port_in.setText(_translate("Conn_Settings_Form", "1550"))
self.connect_btn.setText(_translate("Conn_Settings_Form", "Connect"))
self.cancel_btn.setText(_translate("Conn_Settings_Form", "Cancel"))

View File

@ -253,7 +253,7 @@ class Ui_Update_Network_Settings_Form(object):
self.prefix_len_lbl.setText(_translate("Update_Network_Settings_Form", "Prefix Length:"))
self.prefix_len_in.setText(_translate("Update_Network_Settings_Form", "24"))
self.port_no_lbl.setText(_translate("Update_Network_Settings_Form", "Port:"))
self.port_in.setText(_translate("Update_Network_Settings_Form", "1337"))
self.port_in.setText(_translate("Update_Network_Settings_Form", "1550"))
self.gateway_lbl.setText(_translate("Update_Network_Settings_Form", "Gateway:"))
self.gateway_in_0.setText(_translate("Update_Network_Settings_Form", "192"))
self.dot_0_lbl_2.setText(_translate("Update_Network_Settings_Form", "."))

View File

@ -349,7 +349,7 @@
</size>
</property>
<property name="text">
<string>1337</string>
<string>1550</string>
</property>
<property name="maxLength">
<number>5</number>

View File

@ -30,7 +30,7 @@ impl Default for IpSettings {
fn default() -> Self {
IpSettings {
addr: [192, 168, 1, 128],
port: 1337,
port: 1550,
prefix_len: 24,
gateway: [192, 168, 1, 1],
}