Compare commits

...

15 Commits

13 changed files with 2663 additions and 91 deletions

View File

@ -53,6 +53,33 @@
dontFixup = true;
auditable = false;
};
pglive = pkgs.python3Packages.buildPythonPackage rec {
pname = "pglive";
version = "0.7.2";
format = "pyproject";
src = pkgs.fetchPypi {
inherit pname version;
hash = "sha256-jqj8X6H1N5mJQ4OrY5ANqRB0YJByqg/bNneEALWmH1A=";
};
buildInputs = [ pkgs.python3Packages.poetry-core ];
propagatedBuildInputs = with pkgs.python3Packages; [ pyqtgraph numpy ];
};
kirdy_gui = pkgs.python3Packages.buildPythonPackage {
pname = "kirdy_gui";
version = "0.0.0";
format = "pyproject";
src = "${self}/pykirdy";
nativeBuildInputs = [ pkgs.qt6.wrapQtAppsHook ];
propagatedBuildInputs = [ pkgs.qt6.qtbase ] ++ (with pkgs.python3Packages; [ pyqtgraph pyqt6 qasync pglive ]);
dontWrapQtApps = true;
postFixup = ''
wrapQtApp "$out/bin/tec_qt"
'';
};
in {
packages.x86_64-linux = {
inherit kirdy;
@ -67,12 +94,12 @@
buildInputs = with pkgs; [
rust openocd dfu-util glibc
] ++ (with python3Packages; [
numpy matplotlib pyqtgraph
numpy matplotlib pyqtgraph setuptools pyqt6 qasync pglive
]);
shellHook=
''
export QT_PLUGIN_PATH=${pkgs.qt5.qtbase}/${pkgs.qt5.qtbase.dev.qtPluginPrefix}
export QML2_IMPORT_PATH=${pkgs.qt5.qtbase}/${pkgs.qt5.qtbase.dev.qtQmlPrefix}
export QT_PLUGIN_PATH=${pkgs.qt6.qtbase}/${pkgs.qt6.qtbase.dev.qtPluginPrefix}
export QML2_IMPORT_PATH=${pkgs.qt6.qtbase}/${pkgs.qt6.qtbase.dev.qtQmlPrefix}
'';
};
defaultPackage.x86_64-linux = kirdy;

View File

@ -21,6 +21,9 @@ class InvalidDataType(Exception):
class NoAckRecv(Exception):
pass
class StoppedConnecting(Exception):
pass
Filter_Config = {
"Sinc5Sinc1With50hz60HzRejection": [
"sinc5sinc1postfilter",
@ -84,15 +87,15 @@ Filter_Config = {
}
class Device:
def __init__(self, send_cmd_handler, send_raw_cmd_handler, read_response):
def __init__(self, send_cmd_handler, send_raw_cmd_handler, read_response, cmd_lock):
self._send_cmd = send_cmd_handler
self._send_raw_cmd = send_raw_cmd_handler
self._read_response = read_response
self._cmd_lock = cmd_lock
async def set_ip_settings(self, addr=[192, 168, 1, 128], port=1337, prefix_len=24, gateway=[192, 168, 1, 1]):
"""
After calling this fn, the user needs to issue the SaveFlashSettings cmd and then issue a
Hard Reset/Power Cycle Kirdy for the new network settings to be effective.
After calling this fn, the ip settings are immediately saved into flash and will be effective on next reboot.
"""
if not(isinstance(addr, list) and isinstance(gateway, list)):
raise InvalidDataType
@ -129,6 +132,7 @@ class Device:
Example of yielded data::
{
'ts': 227657, # Relative Timestamp (ms)
'msg_type': 'Report' # Indicate it is a 'Report' json object
'laser': {
'pwr_on': False, # Laser Power is On (True/False)
'pwr_excursion': False, # Was Laser experienced an Overpowered Condition? (True/False)
@ -144,44 +148,42 @@ class Device:
'status': 'Off', # (To be revised)
'over_temp_alarm': False # Was Laser Diode experienced an Overtemperature condition (True/False)
},
'temperature': 298.18344, # Temperature Readings (Degree Celsius)
'temperature': 25.03344, # Temperature Readings (Degree Celsius)
'i_set': 0.0, # Tec Current Set by User/PID Controller(A)
'tec_i': 0.0024998188, # Tec Current Readings (A)
'tec_v': -0.00399971 # Tec Voltage Readings (V)
}
}
"""
response = await self._send_cmd(TARGET_DEVICE, "GetStatusReport")
if response["msg_type"] != "Acknowledge":
return response
return await self._read_response()
return await self._send_cmd(TARGET_DEVICE, "GetStatusReport", msg_type="Report")
async def get_settings_summary(self):
"""
Get the current settings of laser and thermostat in a json object
{
'msg_type': 'Settings', # Indicate it is a 'Settings' json object
'laser': {
default_pwr_on': False, # Power On Laser Diode at Startup
ld_drive_current': { # Laser Diode Output Current(A)
'default_pwr_on': False, # Power On Laser Diode at Startup
'ld_drive_current': { # Laser Diode Output Current(A)
'value': 0.0, # Value Set
'max': 0.3 # Max Value Settable
,
ld_drive_current_limit': { # Laser Diode Software Current Limit(A)
'ld_drive_current_limit': { # Laser Diode Software Current Limit(A)
'value': 0.3, # Value Set
'max': 0.3 # Max Value Settable
,
pd_responsitivity': { # Laser Diode Software Current Limit(A)
'pd_mon_params': { # Laser Diode Software Current Limit(A)
'responsitivity': None, # Value Set
'i_dark': 0.0 # Max Value Settable
,
ld_pwr_limit': 0.0 # Laser Diode Power Limit(W)
'ld_pwr_limit': 0.0 # Laser Diode Power Limit(W)
'ld_terms_short: False # Is Laser Diode Terminals short? (True/False)
},
'thermostat': {
'default_pwr_on': True, # Power on Thermostat at Startup
'pid_engaged': True, # True: PID Control Mode | False Constant Current Mode
'temperature_setpoint': 298.15, # Temperature Setpoint (Degree Celsius)
'temperature_setpoint': 25.0, # Temperature Setpoint (Degree Celsius)
'tec_settings': {
'i_set': { # Current TEC Current Set by PID Controller/User
'value': 0.04330516, # Value Set
@ -227,11 +229,7 @@ class Device:
}
}
"""
response = await self._send_cmd(TARGET_DEVICE, "GetSettingsSummary")
if response["msg_type"] != "Acknowledge":
return response
return await self._read_response()
return await self._send_cmd(TARGET_DEVICE, "GetSettingsSummary", msg_type="Settings")
async def dfu(self):
"""
@ -242,13 +240,13 @@ class Device:
async def save_current_settings_to_flash(self):
"""
Save the current device configurations into flash.
Save the current laser diode and thermostat configurations into flash.
"""
return await self._send_cmd(TARGET_DEVICE, "SaveFlashSettings")
async def load_current_settings_to_flash(self):
async def load_current_settings_from_flash(self):
"""
Restore stored settings in flash
Restore the laser diode and thermostat settings from flash
"""
return await self._send_cmd(TARGET_DEVICE, "LoadFlashSettings")
@ -260,8 +258,9 @@ class Device:
return await self._send_cmd(TARGET_DEVICE, "HardReset")
class Laser:
def __init__(self, send_cmd_handler):
def __init__(self, send_cmd_handler, cmd_lock):
self._send_cmd = send_cmd_handler
self._cmd_lock = cmd_lock
async def set_power_on(self, on):
"""
@ -303,16 +302,19 @@ class Laser:
- i_limit: A
"""
return await self._send_cmd(TARGET_LD, "SetISoftLimit", i_limit)
async def config_pd_mon(self, responsitivity, dark_current):
async def set_pd_mon_responsitivity(self, responsitivity):
"""
Configure the photodiode monitor parameters
Configure the photodiode monitor responsitivity parameter
- responsitivity: A/W
- dark current: A
"""
response = await self._send_cmd(TARGET_LD, "SetPdResponsitivity", responsitivity)
if response["msg_type"] != "Acknowledge":
return response
return await self._send_cmd(TARGET_LD, "SetPdResponsitivity", responsitivity)
async def set_pd_mon_dark_current(self, dark_current):
"""
Configure the photodiode monitor responsitivity parameter
- dark_current: A/W
"""
return await self._send_cmd(TARGET_LD, "SetPdDarkCurrent", dark_current)
async def set_ld_pwr_limit(self, pwr_limit):
@ -320,7 +322,7 @@ class Laser:
Set power limit for the power excursion monitor
If the calculated power with the params of pd_mon > pwr_limit,
overpower protection is triggered.
- pwr_limit: mW
- pwr_limit: W
"""
return await self._send_cmd(TARGET_LD, "SetLdPwrLimit", pwr_limit)
@ -331,10 +333,10 @@ class Laser:
return await self._send_cmd(TARGET_LD, "ClearAlarm")
class Thermostat:
def __init__(self, send_cmd_handler, send_raw_cmd_handler):
def __init__(self, send_cmd_handler, send_raw_cmd_handler, cmd_lock):
self._send_cmd = send_cmd_handler
self._send_raw_cmd = send_raw_cmd_handler
self._cmd_lock = cmd_lock
async def set_power_on(self, on):
"""
Power up or power down thermostat
@ -519,9 +521,9 @@ class Kirdy:
self._cmd_lock = asyncio.Lock()
self._report_mode_on = False
self.timeout = None
self.device = Device(self._send_cmd_handler, self._send_raw_cmd_handler, self._read_response)
self.laser = Laser(self._send_cmd_handler)
self.thermostat = Thermostat(self._send_cmd_handler, self._send_raw_cmd_handler)
self.device = Device(self._send_cmd_handler, self._send_raw_cmd_handler, self._read_response, self._cmd_lock)
self.laser = Laser(self._send_cmd_handler, self._cmd_lock)
self.thermostat = Thermostat(self._send_cmd_handler, self._send_raw_cmd_handler, self._cmd_lock)
self._cmd_list = {
TARGET_DEVICE: {
@ -603,11 +605,6 @@ class Kirdy:
"""Returns True if client is connected"""
return self._writer is not None
async def _cmd(self, *cmd):
async with self._cmd_lock:
# protect the read-write process from being cancelled midway
line = await asyncio.shield(self._read_write(cmd))
async def end_session(self):
"""End session to Kirdy if connected, cancel connection if connecting"""
if self._connecting_task is not None:
@ -643,21 +640,37 @@ class Kirdy:
pass
return { "msg_type": "Internal No json object found in response" }
async def _send_raw_cmd_handler(self, cmd, lock=True, msg_type="Acknowledge"):
if lock:
async with self._cmd_lock:
return await asyncio.shield(self._send_raw_cmd(cmd, msg_type))
else:
return await asyncio.shield(self._send_raw_cmd(cmd, msg_type))
# If the cmd involves a cmd specific data type,
# checking is done separately within the functions being called
async def _send_raw_cmd_handler(self, cmd):
async def _send_raw_cmd(self, cmd, msg_type):
retry = 0
while retry < 10:
self._writer.write(bytes(json.dumps(cmd), "UTF-8"))
await self._writer.drain()
response = await self._read_response()
if response["msg_type"] == "Acknowledge":
return response
retry += 1
await asyncio.sleep(0.1)
try:
if response["msg_type"] == msg_type:
return response
except:
retry += 1
await asyncio.sleep(0.1)
raise NoAckRecv
async def _send_cmd_handler(self, target, cmd, data=None):
async def _send_cmd_handler(self, target, cmd, data=None, msg_type="Acknowledge", lock=True):
if lock:
async with self._cmd_lock:
return await asyncio.shield(self._send_cmd(target, cmd, data, msg_type))
else:
return await asyncio.shield(self._send_cmd(target, cmd, data, msg_type))
async def _send_cmd(self, target, cmd, data, msg_type):
cmd_dict = {}
if not(target in self._cmd_list.keys()) or not(cmd in self._cmd_list[target].keys()):
@ -685,13 +698,15 @@ class Kirdy:
self._writer.write(bytes(json.dumps(cmd_dict), "UTF-8"))
await self._writer.drain()
response = await self._read_response()
if response["msg_type"] == "Acknowledge":
return response
retry += 1
await asyncio.sleep(0.1)
try:
if response["msg_type"] == msg_type:
return response
except:
retry += 1
await asyncio.sleep(0.1)
raise NoAckRecv
async def report_mode(self, report_interval = 0.0, buffer_size = 16384):
async def report_mode(self, report_interval = 0.0, buffer_size=16384):
"""
Start reporting device status in json object. Optional report_interval can be added to discard unwanted samples.
Only the latest status report received within the buffer is returned.

835
pykirdy/kirdy_qt.py Normal file
View File

@ -0,0 +1,835 @@
from PyQt6 import QtWidgets, QtGui, QtCore
from PyQt6.QtCore import pyqtSignal, QObject, QSignalBlocker, pyqtSlot
import pyqtgraph.parametertree.parameterTypes as pTypes
from pyqtgraph.parametertree import Parameter, ParameterTree, ParameterItem, registerParameterType
import pyqtgraph as pg
pg.setConfigOptions(antialias=True)
from pyqtgraph import mkPen
from pglive.sources.live_axis_range import LiveAxisRange
from pglive.sources.data_connector import DataConnector
from pglive.kwargs import Axis, LeadingLine
from pglive.sources.live_plot import LiveLinePlot
from pglive.sources.live_plot_widget import LivePlotWidget
from pglive.sources.live_axis import LiveAxis
import sys
import argparse
import logging
import asyncio
from driver.kirdy_async import Kirdy, StoppedConnecting
import qasync
from qasync import asyncSlot, asyncClose
from collections import deque
from datetime import datetime, timezone, timedelta
from time import time
from typing import Any, Optional, List
from ui.ui_net_settings_form import Ui_net_settings_form
from ui.ui_kirdy_qt import Ui_MainWindow
from dateutil import tz
import math
def get_argparser():
parser = argparse.ArgumentParser(description="ARTIQ master")
parser.add_argument("--connect", default=None, action="store_true",
help="Automatically connect to the specified Thermostat in IP:port format")
parser.add_argument('IP', metavar="ip", default=None, nargs='?')
parser.add_argument('PORT', metavar="port", default=None, nargs='?')
parser.add_argument("-l", "--log", dest="logLevel", choices=['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'],
help="Set the logging level")
return parser
class KirdyDataWatcher(QObject):
"""
This class provides various signals for Mainwindow to update various kinds of GUI objects
"""
connection_error_sig = pyqtSignal()
setting_update_sig = pyqtSignal(dict)
report_update_sig = pyqtSignal(dict)
def __init__(self, parent, kirdy, update_s):
self._update_s = update_s
self._kirdy = kirdy
self._watch_task = None
self._report_mode_task = None
self._poll_for_report = True
super().__init__(parent)
async def signal_emitter(self):
try:
settings_summary = await self._kirdy.device.get_settings_summary()
self.setting_update_sig.emit(settings_summary)
if self._poll_for_report:
status_report = await self._kirdy.device.get_status_report()
self.report_update_sig.emit(status_report)
# TODO: Identify the possible types of error that is connection related
except:
logging.error("Client connection error, disconnecting", exc_info=True)
self._kirdy.stop_report_mode()
self.connection_error_sig.emit()
async def run(self):
while True:
asyncio.ensure_future(self.signal_emitter())
await asyncio.sleep(self._update_s)
def start_watching(self):
self._watch_task = asyncio.create_task(self.run())
def stop_watching(self):
if self._watch_task is not None:
self._watch_task.cancel()
self._watch_task = None
async def set_report_mode(self, enabled: bool):
self._poll_for_report = not enabled
if enabled:
self._report_mode_task = asyncio.create_task(self.report_mode())
else:
self._kirdy.stop_report_mode()
if self._report_mode_task is not None:
await self._report_mode_task
self._report_mode_task = None
async def report_mode(self):
try:
async for status_report in self._kirdy.report_mode():
self.report_update_sig.emit(status_report)
except:
logging.error("Client connection error, disconnecting", exc_info=True)
self._kirdy.stop_report_mode()
self.connection_error_sig.emit()
@pyqtSlot(float)
def set_update_s(self, update_s):
self._update_s = update_s
class Graphs:
def __init__(self, ld_i_set_graph, pd_mon_pwr_graph, tec_i_graph, tec_temp_graph, max_samples=1000):
self.graphs = [ld_i_set_graph, pd_mon_pwr_graph, tec_i_graph, tec_temp_graph]
self.connectors = []
self._pd_mon_pwr_plot = LiveLinePlot(pen=pg.mkPen('r'))
self._ld_i_set_plot = LiveLinePlot(name="Set", pen=pg.mkPen('r'))
self._tec_temp_plot = LiveLinePlot(pen=pg.mkPen('r'))
self._tec_setpoint_plot = LiveLinePlot(pen=pg.mkPen('r'))
self._tec_i_target_plot = LiveLinePlot(name="Target", pen=pg.mkPen('r'))
self._tec_i_measure_plot = LiveLinePlot(name="Measure", pen=pg.mkPen('g'))
self._temp_setpoint_line = tec_temp_graph.getPlotItem().addLine(label='{value} °C', pen=pg.mkPen('g'))
# Render the temperature setpoint line on top of the temperature being plotted
self._temp_setpoint_line.setZValue(10)
self._temp_setpoint_line.setVisible(False)
def tickStrings(values: List, scale: float, spacing: float) -> List:
return [datetime.fromtimestamp(value/1000, tz=timezone.utc).strftime("%H:%M:%S") for value in values]
for graph in ld_i_set_graph, pd_mon_pwr_graph, tec_i_graph, tec_temp_graph:
time_axis = LiveAxis('bottom', text="Time since Kirdy Reset (Hr:Min:Sec)", tick_angle=-45, units="")
# Display the relative ts in custom %H:%M:%S format without local timezone
time_axis.tickStrings = tickStrings
# Prevent scaling prefix being added to the back fo axis label
time_axis.autoSIPrefix = False
time_axis.showLabel()
graph.setAxisItems({'bottom': time_axis})
graph.add_crosshair(pg.mkPen(color='red', width=1), {'color': 'green'})
#TODO: x_range should not be updated on every tick
graph.x_range_controller = LiveAxisRange(roll_on_tick=17, offset_left=4900)
graph.x_range_controller.crop_left_offset_to_data = True
# Enable linking of axes in the graph widget's context menu
graph.register(graph.getPlotItem().titleLabel.text) # Slight hack getting the title
self.max_samples = max_samples
ld_i_set_axis = LiveAxis('left', text="Current", units="A")
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)
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.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.connectors += [self.pd_mon_pwr_connector]
tec_temp_axis = LiveAxis('left', text="Temperature", units="°C")
tec_temp_axis.showLabel()
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.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.connectors += [self.tec_i_target_connector, self.tec_i_measure_connector]
def set_max_samples(self, max_samples):
self.max_samples = max_samples
for connector in self.connectors:
with connector.data_lock:
connector.max_points = self.max_samples
connector.x = deque(maxlen=int(connector.max_points))
connector.y = deque(maxlen=int(connector.max_points))
def plot_append(self, report):
try:
ld_i_set = report['laser']['ld_i_set']
pd_pwr = report['laser']['pd_pwr']
tec_i_set = report['thermostat']['i_set']
tec_i_measure = report['thermostat']['tec_i']
tec_temp = report['thermostat']['temperature']
ts = report['ts']
self.ld_i_set_connector.cb_append_data_point(ld_i_set, ts)
self.pd_mon_pwr_connector.cb_append_data_point(pd_pwr, ts)
if tec_temp is not None:
self.tec_temp_connector.cb_append_data_point(tec_temp, ts)
if self._temp_setpoint_line.isVisible():
self.tec_setpoint_connector.cb_append_data_point(self._temp_setpoint_line.value(), ts)
else:
self.tec_setpoint_connector.cb_append_data_point(tec_temp, ts)
if tec_i_measure is not None:
self.tec_i_measure_connector.cb_append_data_point(tec_i_measure, ts)
self.tec_i_target_connector.cb_append_data_point(tec_i_set, ts)
except Exception as e:
logging.error(f"Graph Value cannot be updated. Error:{e}. Data:{report}")
def clear_data_pts(self):
for connector in self.connectors:
connector.clear()
connector.resume()
def set_temp_setpoint_line(self, temp=None, visible=None):
if visible is not None:
self._temp_setpoint_line.setVisible(visible)
if temp is not None:
self._temp_setpoint_line.setValue(temp)
# PyQtGraph normally does not update this text when the line
# is not visible, so make sure that the temperature label
# gets updated always, and doesn't stay at an old value.
self._temp_setpoint_line.label.setText(f"{temp} °C", color='g')
class MutexParameter(pTypes.ListParameter):
"""
Mutually exclusive parameter where only one of its children is visible at a time, list selectable.
The ordering of the list items determines which children will be visible.
"""
def __init__(self, **opts):
super().__init__(**opts)
self.sigValueChanged.connect(self.show_chosen_child)
self.sigValueChanged.emit(self, self.opts['value'])
def _get_param_from_value(self, value):
if isinstance(self.opts['limits'], dict):
values_list = list(self.opts['limits'].values())
else:
values_list = self.opts['limits']
return self.children()[values_list.index(value)]
@pyqtSlot(object, object)
def show_chosen_child(self, value):
for param in self.children():
param.hide()
child_to_show = self._get_param_from_value(value.value())
child_to_show.show()
if child_to_show.opts.get('triggerOnShow', None):
child_to_show.sigValueChanged.emit(child_to_show, child_to_show.value())
registerParameterType('mutex', MutexParameter)
class NetSettingsForm(QtWidgets.QDialog, Ui_net_settings_form):
def __init__(self):
super().__init__()
self.setupUi(self)
def get_net_settings(self):
try:
ip_addr = [self.addr_in_0.text(), self.addr_in_1.text(), self.addr_in_2.text(), self.addr_in_3.text()]
for addr in ip_addr:
_ = int(addr)
return {
"ip_addr": ip_addr,
"port": int(self.port_in.text())
}
except ValueError:
return None
class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
"""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': 'Power', 'type': 'color', 'value': 'w', 'readonly': True},
{'name': 'Alarm', 'type': 'color', 'value': 'w', 'readonly': True},
]
LASER_DIODE_PARAMETERS = [
{'name': 'Readings', 'expanded': True, 'type': 'group', 'children': [
{'name': 'LD Current Set', 'type': 'float', 'suffix': 'A', 'siPrefix': True, 'readonly': True},
{'name': 'PD Current', 'type': 'float', 'suffix': 'A', 'siPrefix': True, 'readonly': True},
{'name': 'PD Power', 'type': 'float', 'suffix': 'W', 'siPrefix': True, 'readonly': True},
{'name': 'LF Mod Impedance', 'type': 'list', 'limits': ['Is50Ohm', 'Not50Ohm'], 'readonly': True}
]},
{'name': 'Output Config', 'expanded': True, 'type': 'group', 'children': [
{'name': 'LD Current Set', 'type': 'float', 'value': 0, 'step': 1, 'decimals': 6, 'limits': (0, 1),
'suffix': 'A', 'siPrefix': True, 'target': 'laser', 'action': 'set_i'},
{'name': 'LD Current Set Soft Limit', 'type': 'float', 'value': 0, 'step': 1, 'decimals': 6, 'limits': (0, 1),
'suffix': 'A', 'siPrefix': True, 'target': 'laser', 'action': 'set_i_soft_limit'},
{'name': 'LD Power Limit', 'type': 'float', 'value': 0, 'step': 1, 'decimals': 6, 'limits': (0, 0.3),
'suffix': 'W', 'siPrefix': True, 'target': 'laser', 'action': 'set_ld_pwr_limit'},
{'name': 'LD Terminals Short', 'type': 'bool', 'value': False, 'target': 'laser', 'action': 'set_ld_terms_short'},
{'name': 'Default Power On', 'type': 'bool', 'value': False, 'target': 'laser', 'action': 'set_default_pwr_on'},
]},
{'name': 'Photodiode Monitor Config', 'expanded': False, 'type': 'group', 'children': [
{'name': 'Responsitivity', 'type': 'float', 'value': 0, 'step': 1, 'decimals': 6, 'limits': (0, 3000),
'suffix': 'A/W', 'siPrefix': True, 'target': 'laser', 'action': 'set_pd_mon_responsitivity'},
{'name': 'Dark Current', 'type': 'float', 'value': 0, 'step': 1, 'decimals': 6, 'limits': (0, 3000),
'suffix': 'A', 'siPrefix': True, 'target': 'laser', 'action': 'set_pd_mon_dark_current'},
]},
]
THERMOSTAT_STATUS = [
{'name': 'Power', 'type': 'color', 'value': 'w', 'readonly': True},
{'name': 'Alarm', 'type': 'color', 'value': 'w', 'readonly': True},
]
THERMOSTAT_PARAMETERS = [
{'name': 'Readings', 'expanded': True, 'type': 'group', 'children': [
{'name': 'Temperature', 'type': 'float', 'format': '{value:.4f} °C', 'readonly': True},
{'name': 'Current through TEC', 'type': 'float', 'suffix': 'A', 'siPrefix': True, 'decimals': 6, 'readonly': True},
]},
{'name': 'Output Config', 'expanded': True, 'type': 'group', 'children': [
{'name': 'Control Method', 'type': 'mutex', 'limits': ['Constant Current', 'Temperature PID'],
'target_action_pair': [['thermostat', 'set_constant_current_control_mode'], ['thermostat', 'set_pid_control_mode']], 'children': [
{'name': 'Set Current', 'type': 'float', 'value': 0, 'step': 0.001, 'limits': (-1, 1), 'triggerOnShow': True,
'decimals': 6, 'suffix': 'A', 'siPrefix': True, 'target': 'thermostat', 'action': 'set_tec_i_out'},
{'name': 'Set Temperature', 'type': 'float', 'value': 25, 'step': 0.1, 'limits': (-273, 300),
'format': '{value:.4f} °C', 'target': 'thermostat', 'action': 'set_temperature_setpoint'},
]},
{'name': 'Limits', 'expanded': False, 'type': 'group', 'children': [
{'name': 'Max Cooling Current', 'type': 'float', 'value': 0, 'step': 0.001, 'decimals': 6, 'limits': (0, 1),
'suffix': 'A', 'siPrefix': True, 'target': 'thermostat', 'action': 'set_tec_max_cooling_i'},
{'name': 'Max Heating Current', 'type': 'float', 'value': 0, 'step': 0.001, 'decimals': 6, 'limits': (0, 1),
'suffix': 'A', 'siPrefix': True, 'target': 'thermostat', 'action': 'set_tec_max_heating_i'},
{'name': 'Max Voltage Difference', 'type': 'float', 'value': 0, 'step': 0.1, 'limits': (0, 5),
'suffix': 'V', 'siPrefix': True, 'target': 'thermostat', 'action': 'set_tec_max_v'},
]},
{'name': 'Default Power On', 'type': 'bool', 'value': False, 'target': 'thermostat', 'action': 'set_default_pwr_on'},
]},
# TODO Temperature ADC Filter Settings
{'name': 'Temperature Monitor Config', 'expanded': False, 'type': 'group', 'children': [
{'name': 'Upper Limit', 'type': 'float', 'value': 0, 'step': 1, 'decimals': 6, 'limits': (-273, 300),
'suffix': '°C', 'target': 'thermostat', 'action': 'set_temp_mon_upper_limit'},
{'name': 'Lower Limit', 'type': 'float', 'value': 0, 'step': 1, 'decimals': 6, 'limits': (-273, 300),
'suffix': '°C', 'target': 'thermostat', 'action': 'set_temp_mon_upper_limit'},
]},
{'name': 'Thermistor Settings','expanded': False, 'type': 'group', 'children': [
{'name': 'T₀', 'type': 'float', 'value': 0, 'step': 1, 'decimals': 6, 'limits': (-273, 300),
'suffix': '°C', 'target': 'thermostat', 'action': 'set_sh_t0'},
{'name': 'R₀', 'type': 'float', 'value': 0, 'step': 1, 'decimals': 6,
'suffix': 'Ω', 'siPrefix': True, 'target': 'thermostat', 'action': 'set_sh_r0'},
{'name': 'B', 'type': 'float', 'value': 3950, 'step': 1, 'suffix': 'K', 'decimals': 4, 'target': 'thermostat', 'action': 'set_sh_beta'},
]},
{'name': 'PID Config', 'expanded': False, 'type': 'group', 'children': [
{'name': 'Kp', 'type': 'float', 'step': 0.1, 'suffix': '', 'target': 'thermostat', 'action': 'set_pid_kp'},
{'name': 'Ki', 'type': 'float', 'step': 0.1, 'suffix': 'Hz', 'target': 'thermostat', 'action': 'set_pid_ki'},
{'name': 'Kd', 'type': 'float', 'step': 0.1, 'suffix': 's', 'target': 'thermostat', 'action': 'set_pid_kd'},
{'name': "PID Output Clamping", 'expanded': True, 'type': 'group', 'children': [
{'name': 'Minimum', 'type': 'float', 'step': 100, 'limits': (-1, 1), 'decimals': 6, 'suffix': 'A', 'target': 'thermostat', 'action': 'set_pid_output_min'},
{'name': 'Maximum', 'type': 'float', 'step': 100, 'limits': (-1, 1), 'decimals': 6, 'suffix': 'A', 'target': 'thermostat', 'action': 'set_pid_output_max'},
]},
# TODO PID AutoTune
]},
]
def __init__(self, args):
super().__init__()
self.kirdy = Kirdy()
self.setupUi(self)
self.ip_addr = self.DEFAULT_IP_ADDR
self.port = self.DEFAULT_PORT
self.net_settings_form = NetSettingsForm()
self.net_settings_form.accepted.connect(self.start_connecting)
self.max_samples = self.DEFAULT_MAX_SAMPLES
self._set_up_kirdy_menu()
self._set_up_ctrl_btns()
self._set_up_plot_menu()
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()
self.tec_i_graph.setTitle("TEC Current")
self.tec_temp_graph.setTitle("TEC Temperature")
self.ld_i_set_graph.setTitle("LD Current Set")
self.pd_mon_pwr_graph.setTitle("PD Mon Power")
self.connect_btn.clicked.connect(self.show_net_settings_form)
self.report_box.stateChanged.connect(self.on_report_box_stateChanged)
self.kirdy_data_watcher = KirdyDataWatcher(self, self.kirdy, self.report_refresh_spin.value())
self.kirdy_data_watcher.connection_error_sig.connect(self.bail)
# TODO: Identify the usable range of set_update_s
self.report_apply_btn.clicked.connect(
lambda: self.kirdy_data_watcher.set_update_s(self.report_refresh_spin.value())
)
self.kirdy_data_watcher.setting_update_sig.connect(self.update_ld_ctrl_panel_settings)
self.kirdy_data_watcher.setting_update_sig.connect(self.update_thermostat_ctrl_panel_settings)
self.kirdy_data_watcher.report_update_sig.connect(self.update_ld_ctrl_panel_readings)
self.kirdy_data_watcher.report_update_sig.connect(self.update_thermostat_ctrl_panel_readings)
self.graphs = Graphs(self.ld_i_set_graph, self.pd_mon_pwr_graph, self.tec_i_graph, self.tec_temp_graph, max_samples=self.max_samples)
self.kirdy_data_watcher.report_update_sig.connect(self.graphs.plot_append)
self.save_flash_btn.clicked.connect(
lambda: self.save_ld_thermostat_settings_to_flash()
)
self.load_flash_btn.clicked.connect(
lambda: self.load_ld_thermostat_settings_from_flash()
)
self.loading_spinner.hide()
def show_net_settings_form(self):
self.net_settings_form.addr_in_0.setText(self.ip_addr[0])
self.net_settings_form.addr_in_1.setText(self.ip_addr[1])
self.net_settings_form.addr_in_2.setText(self.ip_addr[2])
self.net_settings_form.addr_in_3.setText(self.ip_addr[3])
self.net_settings_form.port_in.setText(str(self.port))
self.net_settings_form.show()
def _set_up_ctrl_btns(self):
@asyncSlot(bool)
async def ld_pwr_on(_):
await self.kirdy.laser.set_power_on(True)
self.ld_pwr_on_btn.clicked.connect(ld_pwr_on)
@asyncSlot(bool)
async def ld_pwr_off(_):
await self.kirdy.laser.set_power_on(False)
self.ld_pwr_off_btn.clicked.connect(ld_pwr_off)
@asyncSlot(bool)
async def ld_clear_alarm(_):
await self.kirdy.laser.clear_alarm()
self.ld_clear_alarm_btn.clicked.connect(ld_clear_alarm)
@asyncSlot(bool)
async def tec_pwr_on(_):
await self.kirdy.thermostat.set_power_on(True)
self.tec_pwr_on_btn.clicked.connect(tec_pwr_on)
@asyncSlot(bool)
async def tec_pwr_off(_):
await self.kirdy.thermostat.set_power_on(False)
self.tec_pwr_off_btn.clicked.connect(tec_pwr_off)
@asyncSlot(bool)
async def tec_clear_alarm(_):
await self.kirdy.thermostat.clear_alarm()
self.tec_clear_alarm_btn.clicked.connect(tec_clear_alarm)
# TODO: Create a Diag box with QtDesigner instead of setting up here
def _set_up_kirdy_menu(self):
self.kirdy_menu = QtWidgets.QMenu()
self.kirdy_menu.setTitle('Kirdy Settings')
@asyncSlot(int)
async def on_report_box_stateChanged(self, enabled):
await self.kirdy_data_watcher.set_report_mode(enabled)
@asyncSlot(bool)
async def reset_kirdy(_):
await self.kirdy.device.hard_reset()
await self._on_connection_changed(False)
await asyncio.sleep(0.1) # Wait for the reset to start
# TODO: Attempt to reconnect after resetting
self.actionReset.triggered.connect(reset_kirdy)
self.kirdy_menu.addAction(self.actionReset)
@asyncSlot(bool)
async def dfu_mode(_):
await self._on_connection_changed(False)
await self.kirdy.device.dfu()
self.actionEnter_DFU_Mode.triggered.connect(dfu_mode)
self.kirdy_menu.addAction(self.actionEnter_DFU_Mode)
# TODO: Add a form for user to set ip settings in multiple text boxes
@asyncSlot(bool)
async def network_settings(_):
ask_network = QtWidgets.QInputDialog(self)
ask_network.setWindowTitle("Network Settings")
ask_network.setLabelText("Set the kirdy's IPv4 address, port, prefix length and gateway")
ask_network.setTextValue("192.168.1.128 1337 24 192.168.1.1")
@pyqtSlot(str)
def set_ipv4(ipv4_settings):
sure = QtWidgets.QMessageBox(self)
sure.setWindowTitle("Set network?")
sure.setText(f"Setting this as network and disconnecting:<br>{ipv4_settings}")
@asyncSlot(object)
async def really_set(button):
addr, port, prefix_len, gateway = ipv4_settings.split()
addr = list(map(int, addr.split(".")))
gateway = list(map(int, gateway.split(".")))
await self.kirdy.device.set_ip_settings(addr, int(port), int(prefix_len), gateway)
# TODO: Add a dialogue box and ask if the user wanna reboot Kirdy immediately
await self.kirdy.device.hard_reset()
await self._on_connection_changed(False)
sure.buttonClicked.connect(really_set)
sure.show()
ask_network.textValueSelected.connect(set_ipv4)
ask_network.show()
self.actionNetwork_Settings.triggered.connect(network_settings)
self.kirdy_menu.addAction(self.actionNetwork_Settings)
@asyncSlot(bool)
async def load(_):
await self.kirdy.device.load_current_settings_to_flash()
loaded = QtWidgets.QMessageBox(self)
loaded.setWindowTitle("Config loaded")
loaded.setText(f"All channel configs have been loaded from flash.")
loaded.setIcon(QtWidgets.QMessageBox.Icon.Information)
loaded.show()
self.actionLoad_all_configs.triggered.connect(load)
self.kirdy_menu.addAction(self.actionLoad_all_configs)
@asyncSlot(bool)
async def save(_):
await self.kirdy.device.save_current_settings_to_flash()
saved = QtWidgets.QMessageBox(self)
saved.setWindowTitle("Config saved")
saved.setText(f"All channel configs have been saved to flash.")
saved.setIcon(QtWidgets.QMessageBox.Icon.Information)
saved.show()
self.actionSave_all_configs.triggered.connect(save)
self.kirdy_menu.addAction(self.actionSave_all_configs)
def about_kirdy():
# TODO: Replace the hardware revision placeholder
QtWidgets.QMessageBox.about(
self,
"About Kirdy",
f"""
<h1>Sinara 1550 Kirdy v"major rev"."minor rev"</h1>
"""
)
self.actionAbout_Kirdy.triggered.connect(about_kirdy)
self.kirdy_menu.addAction(self.actionAbout_Kirdy)
self.kirdy_settings.setMenu(self.kirdy_menu)
def _set_up_plot_menu(self):
self.plot_menu = QtWidgets.QMenu()
self.plot_menu.setTitle("Plot Settings")
clear = QtGui.QAction("Clear graphs", self.plot_menu)
clear.triggered.connect(self.clear_graphs)
self.plot_menu.addAction(clear)
self.plot_menu.clear = clear
self.samples_spinbox = QtWidgets.QSpinBox()
self.samples_spinbox.setRange(2, 100000)
self.samples_spinbox.setSuffix(' samples')
self.samples_spinbox.setValue(self.max_samples)
self.samples_spinbox.valueChanged.connect(self.set_max_samples)
limit_samples = QtWidgets.QWidgetAction(self.plot_menu)
limit_samples.setDefaultWidget(self.samples_spinbox)
self.plot_menu.addAction(limit_samples)
self.plot_menu.limit_samples = limit_samples
self.plot_settings.setMenu(self.plot_menu)
def _set_param_tree(self):
status = self.ld_status
status.setHeaderHidden(True)
status.setParameters(self.params[0], showTop=False)
tree = self.ld_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)
async def _on_connection_changed(self, result):
def ctrl_panel_setEnable(result):
self.ld_status.setEnabled(result)
self.ld_tree.setEnabled(result)
self.ld_pwr_on_btn.setEnabled(result)
self.ld_pwr_off_btn.setEnabled(result)
self.ld_clear_alarm_btn.setEnabled(result)
self.tec_status.setEnabled(result)
self.tec_tree.setEnabled(result)
self.tec_pwr_on_btn.setEnabled(result)
self.tec_pwr_off_btn.setEnabled(result)
self.tec_clear_alarm_btn.setEnabled(result)
ctrl_panel_setEnable(result)
def graph_group_setEnable(result):
self.ld_i_set_graph.setEnabled(result)
self.pd_mon_pwr_graph.setEnabled(result)
self.tec_i_graph.setEnabled(result)
self.tec_temp_graph.setEnabled(result)
graph_group_setEnable(result)
self.kirdy_settings.setEnabled(result)
self.report_refresh_spin.setEnabled(result)
self.report_group.setEnabled(result)
self.report_refresh_spin.setEnabled(result)
self.report_box.setEnabled(result)
self.report_apply_btn.setEnabled(result)
self.save_flash_btn.setEnabled(result)
self.load_flash_btn.setEnabled(result)
# TODO: Use QStateMachine to manage connections
self.connect_btn.clicked.disconnect()
if result:
self.connect_btn.setText("Disconnect")
self.connect_btn.clicked.connect(self.bail)
else:
self.connect_btn.setText("Connect")
self.connect_btn.clicked.connect(self.show_net_settings_form)
if result:
# TODO: self.hw_rev_data = await self.kirdy.hw_rev()
self._status()
self.kirdy_data_watcher.start_watching()
else:
pass
self.status_lbl.setText("Disconnected")
self.clear_graphs()
self.report_box.setChecked(False)
await self.kirdy_data_watcher.set_report_mode(False)
self.kirdy_data_watcher.stop_watching()
self.status_lbl.setText("Disconnected")
def _status(self):
# TODO: Get rev no from Kirdy and then add revision into the text
host = f"{self.ip_addr[0]}.{self.ip_addr[1]}.{self.ip_addr[2]}.{self.ip_addr[3]}"
port = self.port
self.status_lbl.setText(f"Connected to Kirdy @ {host}:{port}")
def clear_graphs(self):
self.graphs.clear_data_pts()
def update_network_settings(self):
net_settings = self.net_settings_form.get_net_settings()
if net_settings is None:
return False;
self.ip_addr = net_settings["ip_addr"]
self.port = net_settings["port"]
return True
@asyncSlot()
async def save_ld_thermostat_settings_to_flash(self):
await self.kirdy.device.save_current_settings_to_flash()
@asyncSlot()
async def load_ld_thermostat_settings_from_flash(self):
await self.kirdy.device.load_current_settings_from_flash()
@asyncSlot(dict)
async def graphs_update(self, report):
self.graphs.plot_append(report)
@asyncSlot(dict)
async 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').setValue(settings["ld_drive_current"]['value'])
self.params[1].child('Output Config', 'LD Current Set Soft Limit').setValue(settings["ld_drive_current_limit"]['value'])
self.params[1].child('Output Config', 'LD Power Limit').setValue(settings["ld_pwr_limit"])
self.params[1].child('Output Config', 'LD Terminals Short').setValue(settings["ld_terms_short"])
self.params[1].child('Output Config', 'Default Power On').setValue(settings["default_pwr_on"])
if settings["pd_mon_params"]["responsitivity"] is not None:
self.params[1].child('Photodiode Monitor Config', 'Responsitivity').setValue(settings["pd_mon_params"]["responsitivity"])
else:
self.params[1].child('Photodiode Monitor Config', 'Responsitivity').setValue(0)
self.params[1].child('Photodiode Monitor Config', 'Dark Current').setValue(settings["pd_mon_params"]["i_dark"])
except Exception as e:
logging.error(f"Params tree cannot be updated. Error:{e}. Data:{settings}")
@asyncSlot(dict)
async def update_ld_ctrl_panel_readings(self, report):
try:
report = report['laser']
with QSignalBlocker(self.params[0]):
self.params[0].child('Power').setValue('g' if report['pwr_on'] else 'w')
self.params[0].child('Alarm').setValue('r' if report['pwr_excursion'] else 'w')
with QSignalBlocker(self.params[1]):
self.params[1].child('Readings', 'LD Current Set').setValue(report["ld_i_set"])
self.params[1].child('Readings', 'PD Current').setValue(report["pd_i"])
if report["pd_pwr"] is not None:
self.params[1].child('Readings', 'PD Power').setValue(report["pd_pwr"])
else:
self.params[1].child('Readings', 'PD Power').setValue(0)
self.params[1].child('Readings', 'LF Mod Impedance').setValue(report["term_status"])
except Exception as e:
logging.error(f"Params tree cannot be updated. Error:{e}. Data:{report}")
@asyncSlot(dict)
async 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').setValue("Temperature PID" if settings["pid_engaged"] else "Constant Current")
self.params[3].child('Output Config', 'Control Method', 'Set Current').setValue(settings["tec_settings"]['i_set']['value'])
self.params[3].child('Output Config', 'Control Method', 'Set Temperature').setValue(float(settings["temperature_setpoint"]))
self.params[3].child('Output Config', 'Limits', 'Max Cooling Current').setValue(settings["tec_settings"]['max_i_pos']['value'])
self.params[3].child('Output Config', 'Limits', 'Max Heating Current').setValue(settings["tec_settings"]['max_i_neg']['value'])
self.params[3].child('Output Config', 'Limits', 'Max Voltage Difference').setValue(settings["tec_settings"]['max_v']['value'])
self.params[3].child('Output Config', 'Default Power On').setValue(settings["default_pwr_on"])
# TODO: Update the Temperature ADC Settings here as well
self.params[3].child('Temperature Monitor Config', 'Upper Limit').setValue(settings["temp_mon_settings"]['upper_limit'])
self.params[3].child('Temperature Monitor Config', 'Lower Limit').setValue(settings["temp_mon_settings"]['lower_limit'])
self.params[3].child('PID Config', 'Kp').setValue(settings["pid_params"]['kp'])
self.params[3].child('PID Config', 'Ki').setValue(settings["pid_params"]['ki'])
self.params[3].child('PID Config', 'Kd').setValue(settings["pid_params"]['kd'])
self.params[3].child('PID Config', 'PID Output Clamping', 'Minimum').setValue(settings["pid_params"]['output_min'])
self.params[3].child('PID Config', 'PID Output Clamping', 'Maximum').setValue(settings["pid_params"]['output_max'])
self.params[3].child('Thermistor Settings', 'T₀').setValue(settings["thermistor_params"]['t0'])
self.params[3].child('Thermistor Settings', 'R₀').setValue(settings["thermistor_params"]['r0'])
self.params[3].child('Thermistor Settings', 'B').setValue(settings["thermistor_params"]['b'])
self.graphs.set_temp_setpoint_line(temp=round(settings["temperature_setpoint"], 6))
self.graphs.set_temp_setpoint_line(visible=settings['pid_engaged'])
except Exception as e:
logging.error(f"Params tree cannot be updated. Error:{e}. Data:{settings}")
@asyncSlot(dict)
async def update_thermostat_ctrl_panel_readings(self, report):
try:
report = report['thermostat']
with QSignalBlocker(self.params[2]):
self.params[2].child('Power').setValue('g' if report['pwr_on'] else 'w')
self.params[2].child('Alarm').setValue('r' if report['temp_mon_status']['over_temp_alarm'] else 'w')
with QSignalBlocker(self.params[3]):
self.params[3].child('Readings', 'Temperature').setValue(report["temperature"])
self.params[3].child('Readings', 'Current through TEC').setValue(report["tec_i"])
except Exception as e:
logging.error(f"Params tree cannot be updated. Error:{e}. Data:{report}")
@pyqtSlot(int)
def set_max_samples(self, samples: int):
self.graphs.set_max_samples(samples)
@asyncSlot(int)
async def on_report_box_stateChanged(self, enabled):
await self.kirdy_data_watcher.set_report_mode(enabled)
@asyncSlot()
async def start_connecting(self):
if not self.update_network_settings():
self.status_lbl.setText("Invalid IP Settings Input")
return
host = f"{self.ip_addr[0]}.{self.ip_addr[1]}.{self.ip_addr[2]}.{self.ip_addr[3]}"
port = self.port
try:
if not (self.kirdy.connecting() or self.kirdy.connected()):
self.status_lbl.setText("Connecting...")
await self.kirdy.start_session(host=host, port=port, timeout=0.1)
await self._on_connection_changed(True)
else:
await self.bail()
except (OSError, TimeoutError) as e:
logging.error(f"Failed communicating to {host}:{port}: {e}")
await self.bail()
self.status_lbl.setText(f"Cannot connect to Kirdy@ {host}:{port}")
@asyncSlot()
async def bail(self):
await self._on_connection_changed(False)
await self.kirdy.end_session()
@asyncSlot(object, object)
async def send_command(self, param, changes):
for inner_param, change, data in changes:
if change == 'value':
""" cmd translation from mutex type parameter """
if inner_param.opts.get('target_action_pair', None) is not None:
target, action = inner_param.opts['target_action_pair'][inner_param.opts['limits'].index(data)]
cmd = getattr(getattr(self.kirdy, target), action)
await cmd()
continue
""" cmd translation from non-mutex type parameter"""
if inner_param.opts.get("target", None) is not None:
if inner_param.opts.get("action", None) is not None:
cmd = getattr(getattr(self.kirdy, inner_param.opts["target"]), inner_param.opts["action"])
await cmd(data)
continue
async def coro_main():
args = get_argparser().parse_args()
if args.logLevel:
logging.basicConfig(level=getattr(logging, args.logLevel))
app_quit_event = asyncio.Event()
app = QtWidgets.QApplication.instance()
app.aboutToQuit.connect(app_quit_event.set)
main_window = MainWindow(args)
main_window.show()
await app_quit_event.wait()
def main():
qasync.run(coro_main())
if __name__ == '__main__':
main()

732
pykirdy/ui/kirdy_qt.ui Normal file
View File

@ -0,0 +1,732 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1280</width>
<height>720</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>1280</width>
<height>720</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>3840</width>
<height>2160</height>
</size>
</property>
<property name="windowTitle">
<string>Kirdy Control Panel</string>
</property>
<property name="windowIcon">
<iconset theme="application-x-executable"/>
</property>
<widget class="QWidget" name="main_widget">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>1</horstretch>
<verstretch>1</verstretch>
</sizepolicy>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<property name="leftMargin">
<number>3</number>
</property>
<property name="topMargin">
<number>3</number>
</property>
<property name="rightMargin">
<number>3</number>
</property>
<property name="bottomMargin">
<number>3</number>
</property>
<property name="spacing">
<number>3</number>
</property>
<item row="0" column="0">
<layout class="QVBoxLayout" name="main_layout">
<property name="spacing">
<number>0</number>
</property>
<item>
<layout class="QHBoxLayout" name="horizontalLayout" stretch="1,2">
<property name="spacing">
<number>0</number>
</property>
<property name="sizeConstraint">
<enum>QLayout::SetDefaultConstraint</enum>
</property>
<item>
<layout class="QVBoxLayout" name="ctrl_vertical_layout" stretch="1,1,10,1,1,1,10,1">
<property name="spacing">
<number>0</number>
</property>
<property name="sizeConstraint">
<enum>QLayout::SetMaximumSize</enum>
</property>
<item>
<widget class="QLabel" name="ld_section_label">
<property name="font">
<font>
<pointsize>14</pointsize>
<bold>true</bold>
</font>
</property>
<property name="text">
<string> Laser Diode</string>
</property>
<property name="wordWrap">
<bool>false</bool>
</property>
<property name="margin">
<number>0</number>
</property>
</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>55</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="ParameterTree" name="ld_tree" native="true">
<property name="enabled">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="ld_btns_layout">
<property name="spacing">
<number>30</number>
</property>
<property name="leftMargin">
<number>10</number>
</property>
<property name="rightMargin">
<number>10</number>
</property>
<item>
<widget class="QPushButton" name="ld_pwr_on_btn">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>POWER ON</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="ld_pwr_off_btn">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>POWER OFF</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="ld_clear_alarm_btn">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>CLEAR ALARM</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QLabel" name="tec_section_label">
<property name="font">
<font>
<pointsize>14</pointsize>
<bold>true</bold>
</font>
</property>
<property name="text">
<string> Thermostat</string>
</property>
<property name="wordWrap">
<bool>false</bool>
</property>
<property name="margin">
<number>0</number>
</property>
</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>55</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="ParameterTree" name="tec_tree" native="true">
<property name="enabled">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="tec_btns_layout">
<property name="spacing">
<number>30</number>
</property>
<property name="leftMargin">
<number>10</number>
</property>
<property name="rightMargin">
<number>10</number>
</property>
<item>
<widget class="QPushButton" name="tec_pwr_on_btn">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>POWER ON</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="tec_pwr_off_btn">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>POWER OFF</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="tec_clear_alarm_btn">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>CLEAR ALAM</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
<layout class="QGridLayout" name="graphgroup">
<property name="sizeConstraint">
<enum>QLayout::SetNoConstraint</enum>
</property>
<property name="spacing">
<number>0</number>
</property>
<item row="1" column="0">
<widget class="LivePlotWidget" name="tec_temp_graph" native="true">
<property name="enabled">
<bool>false</bool>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="LivePlotWidget" name="tec_i_graph" native="true">
<property name="enabled">
<bool>false</bool>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="LivePlotWidget" name="pd_mon_pwr_graph" native="true">
<property name="enabled">
<bool>false</bool>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="LivePlotWidget" name="ld_i_set_graph" native="true">
<property name="enabled">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
<widget class="QFrame" name="bottom_settings_group">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>40</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>40</height>
</size>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="spacing">
<number>3</number>
</property>
<property name="leftMargin">
<number>3</number>
</property>
<property name="topMargin">
<number>3</number>
</property>
<property name="rightMargin">
<number>3</number>
</property>
<property name="bottomMargin">
<number>3</number>
</property>
<item>
<layout class="QHBoxLayout" name="settings_layout">
<item>
<widget class="QPushButton" name="connect_btn">
<property name="text">
<string>Connect</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="status_lbl">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>360</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>360</width>
<height>16777215</height>
</size>
</property>
<property name="baseSize">
<size>
<width>120</width>
<height>50</height>
</size>
</property>
<property name="text">
<string>Disconnected</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="kirdy_settings">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string notr="true">⚙</string>
</property>
<property name="popupMode">
<enum>QToolButton::InstantPopup</enum>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="plot_settings">
<property name="toolTip">
<string>Plot Settings</string>
</property>
<property name="text">
<string>📉</string>
</property>
<property name="popupMode">
<enum>QToolButton::InstantPopup</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="limits_warning">
<property name="toolTipDuration">
<number>1000000000</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="background_task_lbl">
<property name="text">
<string>Ready.</string>
</property>
</widget>
</item>
<item>
<widget class="QtWaitingSpinner" name="loading_spinner" native="true"/>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<property name="spacing">
<number>10</number>
</property>
<property name="leftMargin">
<number>10</number>
</property>
<property name="rightMargin">
<number>10</number>
</property>
<item>
<widget class="QPushButton" name="save_flash_btn">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Save Settings to Flash</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="load_flash_btn">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Load Settings from Flash</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QWidget" name="report_group" native="true">
<property name="enabled">
<bool>false</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>40</width>
<height>0</height>
</size>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<layout class="QHBoxLayout" name="report_layout" stretch="0,1,1,1">
<property name="spacing">
<number>6</number>
</property>
<property name="sizeConstraint">
<enum>QLayout::SetDefaultConstraint</enum>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="report_lbl">
<property name="text">
<string>Poll every: </string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QDoubleSpinBox" name="report_refresh_spin">
<property name="enabled">
<bool>false</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>70</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>70</width>
<height>16777215</height>
</size>
</property>
<property name="baseSize">
<size>
<width>70</width>
<height>0</height>
</size>
</property>
<property name="suffix">
<string> s</string>
</property>
<property name="decimals">
<number>1</number>
</property>
<property name="minimum">
<double>0.100000000000000</double>
</property>
<property name="singleStep">
<double>0.100000000000000</double>
</property>
<property name="stepType">
<enum>QAbstractSpinBox::AdaptiveDecimalStepType</enum>
</property>
<property name="value">
<double>1.000000000000000</double>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="report_box">
<property name="enabled">
<bool>false</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>80</width>
<height>16777215</height>
</size>
</property>
<property name="baseSize">
<size>
<width>80</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Report</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="report_apply_btn">
<property name="enabled">
<bool>false</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>80</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>80</width>
<height>16777215</height>
</size>
</property>
<property name="baseSize">
<size>
<width>80</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Apply</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<action name="actionReset">
<property name="text">
<string>Reset</string>
</property>
<property name="toolTip">
<string>Reset the Kirdy</string>
</property>
<property name="menuRole">
<enum>QAction::NoRole</enum>
</property>
</action>
<action name="actionEnter_DFU_Mode">
<property name="text">
<string>Enter DFU Mode</string>
</property>
<property name="toolTip">
<string>Reset kirdy and enter USB device firmware update (DFU) mode</string>
</property>
<property name="menuRole">
<enum>QAction::NoRole</enum>
</property>
</action>
<action name="actionNetwork_Settings">
<property name="text">
<string>Network Settings</string>
</property>
<property name="toolTip">
<string>Configure IPv4 address, netmask length, and optional default gateway</string>
</property>
<property name="menuRole">
<enum>QAction::NoRole</enum>
</property>
</action>
<action name="actionAbout_Kirdy">
<property name="text">
<string>About Kirdy</string>
</property>
<property name="toolTip">
<string>Show Kirdy hardware revision, and settings related to i</string>
</property>
<property name="menuRole">
<enum>QAction::NoRole</enum>
</property>
</action>
<action name="actionLoad_all_configs">
<property name="text">
<string>Load all channel configs from flash</string>
</property>
<property name="toolTip">
<string>Restore configuration for all channels from flash</string>
</property>
<property name="menuRole">
<enum>QAction::NoRole</enum>
</property>
</action>
<action name="actionSave_all_configs">
<property name="text">
<string>Save all channel configs to flash</string>
</property>
<property name="toolTip">
<string>Save configuration for all channels to flash</string>
</property>
<property name="menuRole">
<enum>QAction::NoRole</enum>
</property>
</action>
</widget>
<customwidgets>
<customwidget>
<class>ParameterTree</class>
<extends>QWidget</extends>
<header>pyqtgraph.parametertree</header>
<container>1</container>
</customwidget>
<customwidget>
<class>LivePlotWidget</class>
<extends>QWidget</extends>
<header>pglive.sources.live_plot_widget</header>
<container>1</container>
</customwidget>
<customwidget>
<class>QtWaitingSpinner</class>
<extends>QWidget</extends>
<header>waitingspinnerwidget</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View File

@ -0,0 +1,412 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>net_settings_form</class>
<widget class="QDialog" name="net_settings_form">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>415</width>
<height>170</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>415</width>
<height>170</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>415</width>
<height>170</height>
</size>
</property>
<property name="windowTitle">
<string>Network Properties</string>
</property>
<widget class="QWidget" name="horizontalLayoutWidget">
<property name="geometry">
<rect>
<x>20</x>
<y>40</y>
<width>371</width>
<height>41</height>
</rect>
</property>
<layout class="QHBoxLayout" name="ip_addr_layout">
<item>
<widget class="QLabel" name="ip_addr_label">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>120</width>
<height>16777215</height>
</size>
</property>
<property name="font">
<font>
<pointsize>12</pointsize>
</font>
</property>
<property name="layoutDirection">
<enum>Qt::LeftToRight</enum>
</property>
<property name="text">
<string>IP Address:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="addr_in_0">
<property name="maximumSize">
<size>
<width>50</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>192</string>
</property>
<property name="maxLength">
<number>3</number>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="dot_0_label">
<property name="maximumSize">
<size>
<width>10</width>
<height>16777215</height>
</size>
</property>
<property name="font">
<font>
<pointsize>12</pointsize>
<bold>false</bold>
<kerning>true</kerning>
</font>
</property>
<property name="text">
<string>.</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="addr_in_1">
<property name="maximumSize">
<size>
<width>50</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>168</string>
</property>
<property name="maxLength">
<number>3</number>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="dot_1_label">
<property name="maximumSize">
<size>
<width>10</width>
<height>16777215</height>
</size>
</property>
<property name="font">
<font>
<pointsize>12</pointsize>
<bold>false</bold>
<kerning>true</kerning>
</font>
</property>
<property name="text">
<string>.</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="addr_in_2">
<property name="maximumSize">
<size>
<width>50</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>1</string>
</property>
<property name="maxLength">
<number>3</number>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="dot_2_label">
<property name="maximumSize">
<size>
<width>10</width>
<height>16777215</height>
</size>
</property>
<property name="font">
<font>
<pointsize>12</pointsize>
<bold>false</bold>
<kerning>true</kerning>
</font>
</property>
<property name="text">
<string>.</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="addr_in_3">
<property name="maximumSize">
<size>
<width>50</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>128</string>
</property>
<property name="maxLength">
<number>3</number>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="horizontalLayoutWidget_2">
<property name="geometry">
<rect>
<x>20</x>
<y>80</y>
<width>371</width>
<height>41</height>
</rect>
</property>
<layout class="QHBoxLayout" name="port_layout" stretch="3,0,6">
<item>
<widget class="QLabel" name="port_no_label">
<property name="maximumSize">
<size>
<width>97</width>
<height>16777215</height>
</size>
</property>
<property name="font">
<font>
<pointsize>12</pointsize>
</font>
</property>
<property name="text">
<string>Port:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="port_in">
<property name="maximumSize">
<size>
<width>50</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>1337</string>
</property>
<property name="maxLength">
<number>5</number>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<spacer name="port_layout_spacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Preferred</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>50</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<widget class="QWidget" name="horizontalLayoutWidget_3">
<property name="geometry">
<rect>
<x>20</x>
<y>-1</y>
<width>371</width>
<height>41</height>
</rect>
</property>
<layout class="QHBoxLayout" name="form_title_layout">
<item>
<widget class="QLabel" name="form_title_label">
<property name="maximumSize">
<size>
<width>16777215</width>
<height>20</height>
</size>
</property>
<property name="font">
<font>
<pointsize>14</pointsize>
<bold>false</bold>
<underline>false</underline>
<strikeout>false</strikeout>
</font>
</property>
<property name="text">
<string>Network Properties</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="horizontalLayoutWidget_4">
<property name="geometry">
<rect>
<x>20</x>
<y>120</y>
<width>371</width>
<height>47</height>
</rect>
</property>
<layout class="QHBoxLayout" name="buttons_layout">
<property name="topMargin">
<number>0</number>
</property>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="connect_btn">
<property name="text">
<string>Connect</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="cancel_btn">
<property name="text">
<string>Cancel</string>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
<tabstops>
<tabstop>addr_in_0</tabstop>
<tabstop>addr_in_1</tabstop>
<tabstop>addr_in_2</tabstop>
<tabstop>addr_in_3</tabstop>
<tabstop>port_in</tabstop>
<tabstop>connect_btn</tabstop>
<tabstop>cancel_btn</tabstop>
</tabstops>
<resources/>
<connections>
<connection>
<sender>cancel_btn</sender>
<signal>clicked()</signal>
<receiver>net_settings_form</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>340</x>
<y>140</y>
</hint>
<hint type="destinationlabel">
<x>364</x>
<y>96</y>
</hint>
</hints>
</connection>
<connection>
<sender>connect_btn</sender>
<signal>clicked()</signal>
<receiver>net_settings_form</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>258</x>
<y>147</y>
</hint>
<hint type="destinationlabel">
<x>237</x>
<y>99</y>
</hint>
</hints>
</connection>
</connections>
</ui>

348
pykirdy/ui/ui_kirdy_qt.py Normal file
View File

@ -0,0 +1,348 @@
# Form implementation generated from reading ui file 'kirdy_qt.ui'
#
# Created by: PyQt6 UI code generator 6.6.0
#
# WARNING: Any manual changes made to this file will be lost when pyuic6 is
# run again. Do not edit this file unless you know what you are doing.
from PyQt6 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(1280, 720)
MainWindow.setMinimumSize(QtCore.QSize(1280, 720))
MainWindow.setMaximumSize(QtCore.QSize(3840, 2160))
icon = QtGui.QIcon.fromTheme("application-x-executable")
MainWindow.setWindowIcon(icon)
self.main_widget = QtWidgets.QWidget(parent=MainWindow)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Expanding)
sizePolicy.setHorizontalStretch(1)
sizePolicy.setVerticalStretch(1)
sizePolicy.setHeightForWidth(self.main_widget.sizePolicy().hasHeightForWidth())
self.main_widget.setSizePolicy(sizePolicy)
self.main_widget.setObjectName("main_widget")
self.gridLayout_2 = QtWidgets.QGridLayout(self.main_widget)
self.gridLayout_2.setContentsMargins(3, 3, 3, 3)
self.gridLayout_2.setSpacing(3)
self.gridLayout_2.setObjectName("gridLayout_2")
self.main_layout = QtWidgets.QVBoxLayout()
self.main_layout.setSpacing(0)
self.main_layout.setObjectName("main_layout")
self.horizontalLayout = QtWidgets.QHBoxLayout()
self.horizontalLayout.setSizeConstraint(QtWidgets.QLayout.SizeConstraint.SetDefaultConstraint)
self.horizontalLayout.setSpacing(0)
self.horizontalLayout.setObjectName("horizontalLayout")
self.ctrl_vertical_layout = QtWidgets.QVBoxLayout()
self.ctrl_vertical_layout.setSizeConstraint(QtWidgets.QLayout.SizeConstraint.SetMaximumSize)
self.ctrl_vertical_layout.setSpacing(0)
self.ctrl_vertical_layout.setObjectName("ctrl_vertical_layout")
self.ld_section_label = QtWidgets.QLabel(parent=self.main_widget)
font = QtGui.QFont()
font.setPointSize(14)
font.setBold(True)
self.ld_section_label.setFont(font)
self.ld_section_label.setWordWrap(False)
self.ld_section_label.setObjectName("ld_section_label")
self.ctrl_vertical_layout.addWidget(self.ld_section_label)
self.ld_status = ParameterTree(parent=self.main_widget)
self.ld_status.setEnabled(False)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Preferred, QtWidgets.QSizePolicy.Policy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.ld_status.sizePolicy().hasHeightForWidth())
self.ld_status.setSizePolicy(sizePolicy)
self.ld_status.setMaximumSize(QtCore.QSize(16777215, 55))
self.ld_status.setObjectName("ld_status")
self.ctrl_vertical_layout.addWidget(self.ld_status)
self.ld_tree = ParameterTree(parent=self.main_widget)
self.ld_tree.setEnabled(False)
self.ld_tree.setObjectName("ld_tree")
self.ctrl_vertical_layout.addWidget(self.ld_tree)
self.ld_btns_layout = QtWidgets.QHBoxLayout()
self.ld_btns_layout.setContentsMargins(10, -1, 10, -1)
self.ld_btns_layout.setSpacing(30)
self.ld_btns_layout.setObjectName("ld_btns_layout")
self.ld_pwr_on_btn = QtWidgets.QPushButton(parent=self.main_widget)
self.ld_pwr_on_btn.setEnabled(False)
self.ld_pwr_on_btn.setObjectName("ld_pwr_on_btn")
self.ld_btns_layout.addWidget(self.ld_pwr_on_btn)
self.ld_pwr_off_btn = QtWidgets.QPushButton(parent=self.main_widget)
self.ld_pwr_off_btn.setEnabled(False)
self.ld_pwr_off_btn.setObjectName("ld_pwr_off_btn")
self.ld_btns_layout.addWidget(self.ld_pwr_off_btn)
self.ld_clear_alarm_btn = QtWidgets.QPushButton(parent=self.main_widget)
self.ld_clear_alarm_btn.setEnabled(False)
self.ld_clear_alarm_btn.setObjectName("ld_clear_alarm_btn")
self.ld_btns_layout.addWidget(self.ld_clear_alarm_btn)
self.ctrl_vertical_layout.addLayout(self.ld_btns_layout)
self.tec_section_label = QtWidgets.QLabel(parent=self.main_widget)
font = QtGui.QFont()
font.setPointSize(14)
font.setBold(True)
self.tec_section_label.setFont(font)
self.tec_section_label.setWordWrap(False)
self.tec_section_label.setObjectName("tec_section_label")
self.ctrl_vertical_layout.addWidget(self.tec_section_label)
self.tec_status = ParameterTree(parent=self.main_widget)
self.tec_status.setEnabled(False)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Preferred, QtWidgets.QSizePolicy.Policy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.tec_status.sizePolicy().hasHeightForWidth())
self.tec_status.setSizePolicy(sizePolicy)
self.tec_status.setMaximumSize(QtCore.QSize(16777215, 55))
self.tec_status.setObjectName("tec_status")
self.ctrl_vertical_layout.addWidget(self.tec_status)
self.tec_tree = ParameterTree(parent=self.main_widget)
self.tec_tree.setEnabled(False)
self.tec_tree.setObjectName("tec_tree")
self.ctrl_vertical_layout.addWidget(self.tec_tree)
self.tec_btns_layout = QtWidgets.QHBoxLayout()
self.tec_btns_layout.setContentsMargins(10, -1, 10, -1)
self.tec_btns_layout.setSpacing(30)
self.tec_btns_layout.setObjectName("tec_btns_layout")
self.tec_pwr_on_btn = QtWidgets.QPushButton(parent=self.main_widget)
self.tec_pwr_on_btn.setEnabled(False)
self.tec_pwr_on_btn.setObjectName("tec_pwr_on_btn")
self.tec_btns_layout.addWidget(self.tec_pwr_on_btn)
self.tec_pwr_off_btn = QtWidgets.QPushButton(parent=self.main_widget)
self.tec_pwr_off_btn.setEnabled(False)
self.tec_pwr_off_btn.setObjectName("tec_pwr_off_btn")
self.tec_btns_layout.addWidget(self.tec_pwr_off_btn)
self.tec_clear_alarm_btn = QtWidgets.QPushButton(parent=self.main_widget)
self.tec_clear_alarm_btn.setEnabled(False)
self.tec_clear_alarm_btn.setObjectName("tec_clear_alarm_btn")
self.tec_btns_layout.addWidget(self.tec_clear_alarm_btn)
self.ctrl_vertical_layout.addLayout(self.tec_btns_layout)
self.ctrl_vertical_layout.setStretch(0, 1)
self.ctrl_vertical_layout.setStretch(1, 1)
self.ctrl_vertical_layout.setStretch(2, 10)
self.ctrl_vertical_layout.setStretch(3, 1)
self.ctrl_vertical_layout.setStretch(4, 1)
self.ctrl_vertical_layout.setStretch(5, 1)
self.ctrl_vertical_layout.setStretch(6, 10)
self.ctrl_vertical_layout.setStretch(7, 1)
self.horizontalLayout.addLayout(self.ctrl_vertical_layout)
self.graphgroup = QtWidgets.QGridLayout()
self.graphgroup.setSizeConstraint(QtWidgets.QLayout.SizeConstraint.SetNoConstraint)
self.graphgroup.setSpacing(0)
self.graphgroup.setObjectName("graphgroup")
self.tec_temp_graph = LivePlotWidget(parent=self.main_widget)
self.tec_temp_graph.setEnabled(False)
self.tec_temp_graph.setObjectName("tec_temp_graph")
self.graphgroup.addWidget(self.tec_temp_graph, 1, 0, 1, 1)
self.tec_i_graph = LivePlotWidget(parent=self.main_widget)
self.tec_i_graph.setEnabled(False)
self.tec_i_graph.setObjectName("tec_i_graph")
self.graphgroup.addWidget(self.tec_i_graph, 1, 2, 1, 1)
self.pd_mon_pwr_graph = LivePlotWidget(parent=self.main_widget)
self.pd_mon_pwr_graph.setEnabled(False)
self.pd_mon_pwr_graph.setObjectName("pd_mon_pwr_graph")
self.graphgroup.addWidget(self.pd_mon_pwr_graph, 0, 0, 1, 1)
self.ld_i_set_graph = LivePlotWidget(parent=self.main_widget)
self.ld_i_set_graph.setEnabled(False)
self.ld_i_set_graph.setObjectName("ld_i_set_graph")
self.graphgroup.addWidget(self.ld_i_set_graph, 0, 2, 1, 1)
self.horizontalLayout.addLayout(self.graphgroup)
self.horizontalLayout.setStretch(0, 1)
self.horizontalLayout.setStretch(1, 2)
self.main_layout.addLayout(self.horizontalLayout)
self.bottom_settings_group = QtWidgets.QFrame(parent=self.main_widget)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.bottom_settings_group.sizePolicy().hasHeightForWidth())
self.bottom_settings_group.setSizePolicy(sizePolicy)
self.bottom_settings_group.setMinimumSize(QtCore.QSize(0, 40))
self.bottom_settings_group.setMaximumSize(QtCore.QSize(16777215, 40))
self.bottom_settings_group.setFrameShape(QtWidgets.QFrame.Shape.StyledPanel)
self.bottom_settings_group.setFrameShadow(QtWidgets.QFrame.Shadow.Raised)
self.bottom_settings_group.setObjectName("bottom_settings_group")
self.horizontalLayout_2 = QtWidgets.QHBoxLayout(self.bottom_settings_group)
self.horizontalLayout_2.setContentsMargins(3, 3, 3, 3)
self.horizontalLayout_2.setSpacing(3)
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
self.settings_layout = QtWidgets.QHBoxLayout()
self.settings_layout.setObjectName("settings_layout")
self.connect_btn = QtWidgets.QPushButton(parent=self.bottom_settings_group)
self.connect_btn.setObjectName("connect_btn")
self.settings_layout.addWidget(self.connect_btn)
self.status_lbl = QtWidgets.QLabel(parent=self.bottom_settings_group)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Fixed, QtWidgets.QSizePolicy.Policy.Expanding)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.status_lbl.sizePolicy().hasHeightForWidth())
self.status_lbl.setSizePolicy(sizePolicy)
self.status_lbl.setMinimumSize(QtCore.QSize(360, 0))
self.status_lbl.setMaximumSize(QtCore.QSize(360, 16777215))
self.status_lbl.setBaseSize(QtCore.QSize(120, 50))
self.status_lbl.setObjectName("status_lbl")
self.settings_layout.addWidget(self.status_lbl)
self.kirdy_settings = QtWidgets.QToolButton(parent=self.bottom_settings_group)
self.kirdy_settings.setEnabled(False)
self.kirdy_settings.setText("")
self.kirdy_settings.setPopupMode(QtWidgets.QToolButton.ToolButtonPopupMode.InstantPopup)
self.kirdy_settings.setObjectName("kirdy_settings")
self.settings_layout.addWidget(self.kirdy_settings)
self.plot_settings = QtWidgets.QToolButton(parent=self.bottom_settings_group)
self.plot_settings.setPopupMode(QtWidgets.QToolButton.ToolButtonPopupMode.InstantPopup)
self.plot_settings.setObjectName("plot_settings")
self.settings_layout.addWidget(self.plot_settings)
self.limits_warning = QtWidgets.QLabel(parent=self.bottom_settings_group)
self.limits_warning.setToolTipDuration(1000000000)
self.limits_warning.setObjectName("limits_warning")
self.settings_layout.addWidget(self.limits_warning)
self.background_task_lbl = QtWidgets.QLabel(parent=self.bottom_settings_group)
self.background_task_lbl.setObjectName("background_task_lbl")
self.settings_layout.addWidget(self.background_task_lbl)
self.loading_spinner = QtWaitingSpinner(parent=self.bottom_settings_group)
self.loading_spinner.setObjectName("loading_spinner")
self.settings_layout.addWidget(self.loading_spinner)
spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum)
self.settings_layout.addItem(spacerItem)
self.horizontalLayout_3 = QtWidgets.QHBoxLayout()
self.horizontalLayout_3.setContentsMargins(10, -1, 10, -1)
self.horizontalLayout_3.setSpacing(10)
self.horizontalLayout_3.setObjectName("horizontalLayout_3")
self.save_flash_btn = QtWidgets.QPushButton(parent=self.bottom_settings_group)
self.save_flash_btn.setEnabled(False)
self.save_flash_btn.setObjectName("save_flash_btn")
self.horizontalLayout_3.addWidget(self.save_flash_btn)
self.load_flash_btn = QtWidgets.QPushButton(parent=self.bottom_settings_group)
self.load_flash_btn.setEnabled(False)
self.load_flash_btn.setObjectName("load_flash_btn")
self.horizontalLayout_3.addWidget(self.load_flash_btn)
self.settings_layout.addLayout(self.horizontalLayout_3)
self.report_group = QtWidgets.QWidget(parent=self.bottom_settings_group)
self.report_group.setEnabled(False)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Expanding)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.report_group.sizePolicy().hasHeightForWidth())
self.report_group.setSizePolicy(sizePolicy)
self.report_group.setMinimumSize(QtCore.QSize(40, 0))
self.report_group.setObjectName("report_group")
self.horizontalLayout_4 = QtWidgets.QHBoxLayout(self.report_group)
self.horizontalLayout_4.setContentsMargins(0, 0, 0, 0)
self.horizontalLayout_4.setSpacing(0)
self.horizontalLayout_4.setObjectName("horizontalLayout_4")
self.report_layout = QtWidgets.QHBoxLayout()
self.report_layout.setSizeConstraint(QtWidgets.QLayout.SizeConstraint.SetDefaultConstraint)
self.report_layout.setContentsMargins(0, -1, -1, -1)
self.report_layout.setSpacing(6)
self.report_layout.setObjectName("report_layout")
self.report_lbl = QtWidgets.QLabel(parent=self.report_group)
self.report_lbl.setAlignment(QtCore.Qt.AlignmentFlag.AlignRight|QtCore.Qt.AlignmentFlag.AlignTrailing|QtCore.Qt.AlignmentFlag.AlignVCenter)
self.report_lbl.setObjectName("report_lbl")
self.report_layout.addWidget(self.report_lbl)
self.report_refresh_spin = QtWidgets.QDoubleSpinBox(parent=self.report_group)
self.report_refresh_spin.setEnabled(False)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Fixed, QtWidgets.QSizePolicy.Policy.Expanding)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.report_refresh_spin.sizePolicy().hasHeightForWidth())
self.report_refresh_spin.setSizePolicy(sizePolicy)
self.report_refresh_spin.setMinimumSize(QtCore.QSize(70, 0))
self.report_refresh_spin.setMaximumSize(QtCore.QSize(70, 16777215))
self.report_refresh_spin.setBaseSize(QtCore.QSize(70, 0))
self.report_refresh_spin.setDecimals(1)
self.report_refresh_spin.setMinimum(0.1)
self.report_refresh_spin.setSingleStep(0.1)
self.report_refresh_spin.setStepType(QtWidgets.QAbstractSpinBox.StepType.AdaptiveDecimalStepType)
self.report_refresh_spin.setProperty("value", 1.0)
self.report_refresh_spin.setObjectName("report_refresh_spin")
self.report_layout.addWidget(self.report_refresh_spin)
self.report_box = QtWidgets.QCheckBox(parent=self.report_group)
self.report_box.setEnabled(False)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Fixed, QtWidgets.QSizePolicy.Policy.Expanding)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.report_box.sizePolicy().hasHeightForWidth())
self.report_box.setSizePolicy(sizePolicy)
self.report_box.setMaximumSize(QtCore.QSize(80, 16777215))
self.report_box.setBaseSize(QtCore.QSize(80, 0))
self.report_box.setObjectName("report_box")
self.report_layout.addWidget(self.report_box)
self.report_apply_btn = QtWidgets.QPushButton(parent=self.report_group)
self.report_apply_btn.setEnabled(False)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Fixed, QtWidgets.QSizePolicy.Policy.Expanding)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.report_apply_btn.sizePolicy().hasHeightForWidth())
self.report_apply_btn.setSizePolicy(sizePolicy)
self.report_apply_btn.setMinimumSize(QtCore.QSize(80, 0))
self.report_apply_btn.setMaximumSize(QtCore.QSize(80, 16777215))
self.report_apply_btn.setBaseSize(QtCore.QSize(80, 0))
self.report_apply_btn.setObjectName("report_apply_btn")
self.report_layout.addWidget(self.report_apply_btn)
self.report_layout.setStretch(1, 1)
self.report_layout.setStretch(2, 1)
self.report_layout.setStretch(3, 1)
self.horizontalLayout_4.addLayout(self.report_layout)
self.settings_layout.addWidget(self.report_group)
self.horizontalLayout_2.addLayout(self.settings_layout)
self.main_layout.addWidget(self.bottom_settings_group)
self.gridLayout_2.addLayout(self.main_layout, 0, 0, 1, 1)
MainWindow.setCentralWidget(self.main_widget)
self.actionReset = QtGui.QAction(parent=MainWindow)
self.actionReset.setMenuRole(QtGui.QAction.MenuRole.NoRole)
self.actionReset.setObjectName("actionReset")
self.actionEnter_DFU_Mode = QtGui.QAction(parent=MainWindow)
self.actionEnter_DFU_Mode.setMenuRole(QtGui.QAction.MenuRole.NoRole)
self.actionEnter_DFU_Mode.setObjectName("actionEnter_DFU_Mode")
self.actionNetwork_Settings = QtGui.QAction(parent=MainWindow)
self.actionNetwork_Settings.setMenuRole(QtGui.QAction.MenuRole.NoRole)
self.actionNetwork_Settings.setObjectName("actionNetwork_Settings")
self.actionAbout_Kirdy = QtGui.QAction(parent=MainWindow)
self.actionAbout_Kirdy.setMenuRole(QtGui.QAction.MenuRole.NoRole)
self.actionAbout_Kirdy.setObjectName("actionAbout_Kirdy")
self.actionLoad_all_configs = QtGui.QAction(parent=MainWindow)
self.actionLoad_all_configs.setMenuRole(QtGui.QAction.MenuRole.NoRole)
self.actionLoad_all_configs.setObjectName("actionLoad_all_configs")
self.actionSave_all_configs = QtGui.QAction(parent=MainWindow)
self.actionSave_all_configs.setMenuRole(QtGui.QAction.MenuRole.NoRole)
self.actionSave_all_configs.setObjectName("actionSave_all_configs")
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "Kirdy Control Panel"))
self.ld_section_label.setText(_translate("MainWindow", " Laser Diode"))
self.ld_pwr_on_btn.setText(_translate("MainWindow", "POWER ON"))
self.ld_pwr_off_btn.setText(_translate("MainWindow", "POWER OFF"))
self.ld_clear_alarm_btn.setText(_translate("MainWindow", "CLEAR ALARM"))
self.tec_section_label.setText(_translate("MainWindow", " Thermostat"))
self.tec_pwr_on_btn.setText(_translate("MainWindow", "POWER ON"))
self.tec_pwr_off_btn.setText(_translate("MainWindow", "POWER OFF"))
self.tec_clear_alarm_btn.setText(_translate("MainWindow", "CLEAR ALAM"))
self.connect_btn.setText(_translate("MainWindow", "Connect"))
self.status_lbl.setText(_translate("MainWindow", "Disconnected"))
self.plot_settings.setToolTip(_translate("MainWindow", "Plot Settings"))
self.plot_settings.setText(_translate("MainWindow", "📉"))
self.background_task_lbl.setText(_translate("MainWindow", "Ready."))
self.save_flash_btn.setText(_translate("MainWindow", "Save Settings to Flash"))
self.load_flash_btn.setText(_translate("MainWindow", "Load Settings from Flash"))
self.report_lbl.setText(_translate("MainWindow", "Poll every: "))
self.report_refresh_spin.setSuffix(_translate("MainWindow", " s"))
self.report_box.setText(_translate("MainWindow", "Report"))
self.report_apply_btn.setText(_translate("MainWindow", "Apply"))
self.actionReset.setText(_translate("MainWindow", "Reset"))
self.actionReset.setToolTip(_translate("MainWindow", "Reset the Kirdy"))
self.actionEnter_DFU_Mode.setText(_translate("MainWindow", "Enter DFU Mode"))
self.actionEnter_DFU_Mode.setToolTip(_translate("MainWindow", "Reset kirdy and enter USB device firmware update (DFU) mode"))
self.actionNetwork_Settings.setText(_translate("MainWindow", "Network Settings"))
self.actionNetwork_Settings.setToolTip(_translate("MainWindow", "Configure IPv4 address, netmask length, and optional default gateway"))
self.actionAbout_Kirdy.setText(_translate("MainWindow", "About Kirdy"))
self.actionAbout_Kirdy.setToolTip(_translate("MainWindow", "Show Kirdy hardware revision, and settings related to i"))
self.actionLoad_all_configs.setText(_translate("MainWindow", "Load all channel configs from flash"))
self.actionLoad_all_configs.setToolTip(_translate("MainWindow", "Restore configuration for all channels from flash"))
self.actionSave_all_configs.setText(_translate("MainWindow", "Save all channel configs to flash"))
self.actionSave_all_configs.setToolTip(_translate("MainWindow", "Save configuration for all channels to flash"))
from pglive.sources.live_plot_widget import LivePlotWidget
from pyqtgraph.parametertree import ParameterTree
from waitingspinnerwidget import QtWaitingSpinner

View File

@ -0,0 +1,179 @@
# Form implementation generated from reading ui file 'net_settings_form.ui'
#
# Created by: PyQt6 UI code generator 6.6.0
#
# WARNING: Any manual changes made to this file will be lost when pyuic6 is
# run again. Do not edit this file unless you know what you are doing.
from PyQt6 import QtCore, QtGui, QtWidgets
class Ui_net_settings_form(object):
def setupUi(self, net_settings_form):
net_settings_form.setObjectName("net_settings_form")
net_settings_form.resize(415, 170)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Fixed, QtWidgets.QSizePolicy.Policy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(net_settings_form.sizePolicy().hasHeightForWidth())
net_settings_form.setSizePolicy(sizePolicy)
net_settings_form.setMinimumSize(QtCore.QSize(415, 170))
net_settings_form.setMaximumSize(QtCore.QSize(415, 170))
self.horizontalLayoutWidget = QtWidgets.QWidget(parent=net_settings_form)
self.horizontalLayoutWidget.setGeometry(QtCore.QRect(20, 40, 371, 41))
self.horizontalLayoutWidget.setObjectName("horizontalLayoutWidget")
self.ip_addr_layout = QtWidgets.QHBoxLayout(self.horizontalLayoutWidget)
self.ip_addr_layout.setContentsMargins(0, 0, 0, 0)
self.ip_addr_layout.setObjectName("ip_addr_layout")
self.ip_addr_label = QtWidgets.QLabel(parent=self.horizontalLayoutWidget)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Preferred, QtWidgets.QSizePolicy.Policy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.ip_addr_label.sizePolicy().hasHeightForWidth())
self.ip_addr_label.setSizePolicy(sizePolicy)
self.ip_addr_label.setMaximumSize(QtCore.QSize(120, 16777215))
font = QtGui.QFont()
font.setPointSize(12)
self.ip_addr_label.setFont(font)
self.ip_addr_label.setLayoutDirection(QtCore.Qt.LayoutDirection.LeftToRight)
self.ip_addr_label.setAlignment(QtCore.Qt.AlignmentFlag.AlignRight|QtCore.Qt.AlignmentFlag.AlignTrailing|QtCore.Qt.AlignmentFlag.AlignVCenter)
self.ip_addr_label.setObjectName("ip_addr_label")
self.ip_addr_layout.addWidget(self.ip_addr_label)
self.addr_in_0 = QtWidgets.QLineEdit(parent=self.horizontalLayoutWidget)
self.addr_in_0.setMaximumSize(QtCore.QSize(50, 16777215))
self.addr_in_0.setMaxLength(3)
self.addr_in_0.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
self.addr_in_0.setObjectName("addr_in_0")
self.ip_addr_layout.addWidget(self.addr_in_0)
self.dot_0_label = QtWidgets.QLabel(parent=self.horizontalLayoutWidget)
self.dot_0_label.setMaximumSize(QtCore.QSize(10, 16777215))
font = QtGui.QFont()
font.setPointSize(12)
font.setBold(False)
font.setKerning(True)
self.dot_0_label.setFont(font)
self.dot_0_label.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
self.dot_0_label.setObjectName("dot_0_label")
self.ip_addr_layout.addWidget(self.dot_0_label)
self.addr_in_1 = QtWidgets.QLineEdit(parent=self.horizontalLayoutWidget)
self.addr_in_1.setMaximumSize(QtCore.QSize(50, 16777215))
self.addr_in_1.setMaxLength(3)
self.addr_in_1.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
self.addr_in_1.setObjectName("addr_in_1")
self.ip_addr_layout.addWidget(self.addr_in_1)
self.dot_1_label = QtWidgets.QLabel(parent=self.horizontalLayoutWidget)
self.dot_1_label.setMaximumSize(QtCore.QSize(10, 16777215))
font = QtGui.QFont()
font.setPointSize(12)
font.setBold(False)
font.setKerning(True)
self.dot_1_label.setFont(font)
self.dot_1_label.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
self.dot_1_label.setObjectName("dot_1_label")
self.ip_addr_layout.addWidget(self.dot_1_label)
self.addr_in_2 = QtWidgets.QLineEdit(parent=self.horizontalLayoutWidget)
self.addr_in_2.setMaximumSize(QtCore.QSize(50, 16777215))
self.addr_in_2.setMaxLength(3)
self.addr_in_2.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
self.addr_in_2.setObjectName("addr_in_2")
self.ip_addr_layout.addWidget(self.addr_in_2)
self.dot_2_label = QtWidgets.QLabel(parent=self.horizontalLayoutWidget)
self.dot_2_label.setMaximumSize(QtCore.QSize(10, 16777215))
font = QtGui.QFont()
font.setPointSize(12)
font.setBold(False)
font.setKerning(True)
self.dot_2_label.setFont(font)
self.dot_2_label.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
self.dot_2_label.setObjectName("dot_2_label")
self.ip_addr_layout.addWidget(self.dot_2_label)
self.addr_in_3 = QtWidgets.QLineEdit(parent=self.horizontalLayoutWidget)
self.addr_in_3.setMaximumSize(QtCore.QSize(50, 16777215))
self.addr_in_3.setMaxLength(3)
self.addr_in_3.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
self.addr_in_3.setObjectName("addr_in_3")
self.ip_addr_layout.addWidget(self.addr_in_3)
self.horizontalLayoutWidget_2 = QtWidgets.QWidget(parent=net_settings_form)
self.horizontalLayoutWidget_2.setGeometry(QtCore.QRect(20, 80, 371, 41))
self.horizontalLayoutWidget_2.setObjectName("horizontalLayoutWidget_2")
self.port_layout = QtWidgets.QHBoxLayout(self.horizontalLayoutWidget_2)
self.port_layout.setContentsMargins(0, 0, 0, 0)
self.port_layout.setObjectName("port_layout")
self.port_no_label = QtWidgets.QLabel(parent=self.horizontalLayoutWidget_2)
self.port_no_label.setMaximumSize(QtCore.QSize(97, 16777215))
font = QtGui.QFont()
font.setPointSize(12)
self.port_no_label.setFont(font)
self.port_no_label.setAlignment(QtCore.Qt.AlignmentFlag.AlignRight|QtCore.Qt.AlignmentFlag.AlignTrailing|QtCore.Qt.AlignmentFlag.AlignVCenter)
self.port_no_label.setObjectName("port_no_label")
self.port_layout.addWidget(self.port_no_label)
self.port_in = QtWidgets.QLineEdit(parent=self.horizontalLayoutWidget_2)
self.port_in.setMaximumSize(QtCore.QSize(50, 16777215))
self.port_in.setMaxLength(5)
self.port_in.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
self.port_in.setObjectName("port_in")
self.port_layout.addWidget(self.port_in)
spacerItem = QtWidgets.QSpacerItem(50, 20, QtWidgets.QSizePolicy.Policy.Preferred, QtWidgets.QSizePolicy.Policy.Minimum)
self.port_layout.addItem(spacerItem)
self.port_layout.setStretch(0, 3)
self.port_layout.setStretch(2, 6)
self.horizontalLayoutWidget_3 = QtWidgets.QWidget(parent=net_settings_form)
self.horizontalLayoutWidget_3.setGeometry(QtCore.QRect(20, -1, 371, 41))
self.horizontalLayoutWidget_3.setObjectName("horizontalLayoutWidget_3")
self.form_title_layout = QtWidgets.QHBoxLayout(self.horizontalLayoutWidget_3)
self.form_title_layout.setContentsMargins(0, 0, 0, 0)
self.form_title_layout.setObjectName("form_title_layout")
self.form_title_label = QtWidgets.QLabel(parent=self.horizontalLayoutWidget_3)
self.form_title_label.setMaximumSize(QtCore.QSize(16777215, 20))
font = QtGui.QFont()
font.setPointSize(14)
font.setBold(False)
font.setUnderline(False)
font.setStrikeOut(False)
self.form_title_label.setFont(font)
self.form_title_label.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
self.form_title_label.setObjectName("form_title_label")
self.form_title_layout.addWidget(self.form_title_label)
self.horizontalLayoutWidget_4 = QtWidgets.QWidget(parent=net_settings_form)
self.horizontalLayoutWidget_4.setGeometry(QtCore.QRect(20, 120, 371, 47))
self.horizontalLayoutWidget_4.setObjectName("horizontalLayoutWidget_4")
self.buttons_layout = QtWidgets.QHBoxLayout(self.horizontalLayoutWidget_4)
self.buttons_layout.setContentsMargins(0, 0, 0, 0)
self.buttons_layout.setObjectName("buttons_layout")
spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum)
self.buttons_layout.addItem(spacerItem1)
self.connect_btn = QtWidgets.QPushButton(parent=self.horizontalLayoutWidget_4)
self.connect_btn.setObjectName("connect_btn")
self.buttons_layout.addWidget(self.connect_btn)
self.cancel_btn = QtWidgets.QPushButton(parent=self.horizontalLayoutWidget_4)
self.cancel_btn.setObjectName("cancel_btn")
self.buttons_layout.addWidget(self.cancel_btn)
self.retranslateUi(net_settings_form)
self.cancel_btn.clicked.connect(net_settings_form.reject) # type: ignore
self.connect_btn.clicked.connect(net_settings_form.accept) # type: ignore
QtCore.QMetaObject.connectSlotsByName(net_settings_form)
net_settings_form.setTabOrder(self.addr_in_0, self.addr_in_1)
net_settings_form.setTabOrder(self.addr_in_1, self.addr_in_2)
net_settings_form.setTabOrder(self.addr_in_2, self.addr_in_3)
net_settings_form.setTabOrder(self.addr_in_3, self.port_in)
net_settings_form.setTabOrder(self.port_in, self.connect_btn)
net_settings_form.setTabOrder(self.connect_btn, self.cancel_btn)
def retranslateUi(self, net_settings_form):
_translate = QtCore.QCoreApplication.translate
net_settings_form.setWindowTitle(_translate("net_settings_form", "Network Properties"))
self.ip_addr_label.setText(_translate("net_settings_form", "IP Address:"))
self.addr_in_0.setText(_translate("net_settings_form", "192"))
self.dot_0_label.setText(_translate("net_settings_form", "."))
self.addr_in_1.setText(_translate("net_settings_form", "168"))
self.dot_1_label.setText(_translate("net_settings_form", "."))
self.addr_in_2.setText(_translate("net_settings_form", "1"))
self.dot_2_label.setText(_translate("net_settings_form", "."))
self.addr_in_3.setText(_translate("net_settings_form", "128"))
self.port_no_label.setText(_translate("net_settings_form", "Port:"))
self.port_in.setText(_translate("net_settings_form", "1337"))
self.form_title_label.setText(_translate("net_settings_form", "Network Properties"))
self.connect_btn.setText(_translate("net_settings_form", "Connect"))
self.cancel_btn.setText(_translate("net_settings_form", "Cancel"))

View File

@ -5,7 +5,7 @@ use uom::si::electric_current::ampere;
use uom::si::power::milliwatt;
use crate::laser_diode::ld_ctrl::{LdCtrl, Impedance};
use crate::laser_diode::ld_pwr_exc_protector::{LdPwrExcProtector, self};
use crate::laser_diode::pd_responsitivity;
use crate::laser_diode::pd_mon_params;
use crate::laser_diode::ld_current_out_ctrl_timer::LdCurrentOutCtrlTimer;
use core::marker::PhantomData;
use crate::device::sys_timer::sleep;
@ -54,8 +54,9 @@ struct Settings {
default_pwr_on: bool,
ld_drive_current: ElectricCurrent,
ld_drive_current_limit: ElectricCurrent,
pd_responsitivity: pd_responsitivity::Parameters,
pd_mon_params: pd_mon_params::Parameters,
ld_pwr_limit: Power,
ld_terms_short: bool,
}
impl Default for Settings {
@ -65,8 +66,9 @@ impl Default for Settings {
default_pwr_on: false,
ld_drive_current: ElectricCurrent::new::<milliampere>(0.0),
ld_drive_current_limit: ElectricCurrent::new::<milliampere>(0.0),
pd_responsitivity: pd_responsitivity::Parameters::default(),
pd_mon_params: pd_mon_params::Parameters::default(),
ld_pwr_limit: Power::new::<milliwatt>(0.0),
ld_terms_short: false,
}
}
}
@ -111,10 +113,12 @@ impl LdDrive{
pub fn ld_short(&mut self) {
self.ctrl.ld_short_enable();
self.settings.ld_terms_short = true;
}
pub fn ld_open(&mut self) {
self.ctrl.ld_short_disable();
self.settings.ld_terms_short = false;
}
pub fn power_up(&mut self){
@ -140,7 +144,7 @@ impl LdDrive{
}
pub fn get_pd_pwr(&mut self) -> Power {
self.settings.pd_responsitivity.get_ld_pwr_from_ld_i(LdPwrExcProtector::get_status().v * Settings::PD_MON_TRANSCONDUCTANCE)
self.settings.pd_mon_params.get_ld_pwr_from_ld_i(LdPwrExcProtector::get_status().v * Settings::PD_MON_TRANSCONDUCTANCE)
}
pub fn ld_set_i(&mut self, i: ElectricCurrent){
@ -170,18 +174,19 @@ impl LdDrive{
LdPwrExcProtector::clear_alarm_status();
}
pub fn set_pd_responsitivity(&mut self, responsitivity: pd_responsitivity::ResponsitivityUnit){
self.settings.pd_responsitivity.set(responsitivity);
pub fn set_pd_responsitivity(&mut self, responsitivity: pd_mon_params::ResponsitivityUnit){
self.settings.pd_mon_params.set(responsitivity);
}
pub fn set_pd_dark_current(&mut self, i_dark: ElectricCurrent){
self.settings.pd_responsitivity.set_i_dark(i_dark);
self.settings.pd_mon_params.set_i_dark(i_dark);
}
pub fn set_ld_power_limit(&mut self, pwr_limit: Power){
LdPwrExcProtector::set_trigger_threshold_v(self.settings.pd_responsitivity
LdPwrExcProtector::set_trigger_threshold_v(self.settings.pd_mon_params
.get_ld_i_from_ld_pwr(pwr_limit) / Settings::PD_MON_TRANSCONDUCTANCE
);
self.settings.ld_pwr_limit = pwr_limit;
}
pub fn set_pd_i_limit(&mut self, i: ElectricCurrent){
@ -218,8 +223,9 @@ impl LdDrive{
default_pwr_on: self.settings.default_pwr_on,
ld_drive_current: LdSettingsSummaryField { value: settings.ld_drive_current, max: Settings::LD_CURRENT_MAX},
ld_drive_current_limit: LdSettingsSummaryField { value: settings.ld_drive_current_limit, max: Settings::LD_CURRENT_MAX},
pd_responsitivity: settings.pd_responsitivity,
pd_mon_params: settings.pd_mon_params,
ld_pwr_limit: settings.ld_pwr_limit,
ld_terms_short: settings.ld_terms_short,
}
}
@ -227,9 +233,15 @@ impl LdDrive{
self.power_down();
self.settings.ld_drive_current = settings.ld_drive_current.value;
self.settings.ld_drive_current_limit = settings.ld_drive_current_limit.value;
self.settings.pd_responsitivity = settings.pd_responsitivity;
self.settings.pd_mon_params = settings.pd_mon_params;
self.settings.ld_pwr_limit = settings.ld_pwr_limit;
self.settings.default_pwr_on = settings.default_pwr_on;
if self.settings.ld_terms_short {
self.ld_short();
} else {
self.ld_open();
}
if settings.default_pwr_on {
self.power_up();
} else {
@ -243,8 +255,9 @@ pub struct LdSettingsSummary {
default_pwr_on: bool,
ld_drive_current: LdSettingsSummaryField<ElectricCurrent>,
ld_drive_current_limit: LdSettingsSummaryField<ElectricCurrent>,
pd_responsitivity: pd_responsitivity::Parameters,
pd_mon_params: pd_mon_params::Parameters,
ld_pwr_limit: Power,
ld_terms_short: bool,
}
#[derive(Deserialize, Serialize, Copy, Clone, Debug, Tree)]

View File

@ -1,6 +1,6 @@
pub mod ld_ctrl;
pub mod max5719;
pub mod laser_diode;
pub mod pd_responsitivity;
pub mod pd_mon_params;
pub mod ld_pwr_exc_protector;
pub mod ld_current_out_ctrl_timer;

View File

@ -42,7 +42,8 @@ pub enum State {
#[default]
LoadFlashSettings,
MainLoop,
SaveFlashSettings,
SaveLdThermostatSettings,
SaveDeviceSettings,
PrepareForHardReset,
HardReset,
}
@ -180,22 +181,12 @@ fn main() -> ! {
})
};
}
State::SaveFlashSettings => {
State::SaveLdThermostatSettings => {
// State Transition
state = State::MainLoop;
wd.feed();
let mut store_value_buf = [0u8; 1024];
match flash_store.write_value(CONFIG_KEY[0], &device_settings, &mut store_value_buf) {
Ok(()) => {
debug!("Device Settings is stored in flash");
}
Err(e) => {
debug!("Cannot Store Flash: {:?}", e);
}
}
wd.feed();
match flash_store.write_value(CONFIG_KEY[1], &laser.get_settings_summary(), &mut store_value_buf) {
Ok(()) => {
debug!("Laser Diode Settings is stored in flash");
@ -215,6 +206,21 @@ fn main() -> ! {
}
}
}
State::SaveDeviceSettings => {
// State Transition
state = State::MainLoop;
wd.feed();
let mut store_value_buf = [0u8; 1024];
match flash_store.write_value(CONFIG_KEY[0], &device_settings, &mut store_value_buf) {
Ok(()) => {
debug!("Device Settings is stored in flash");
}
Err(e) => {
debug!("Cannot Store Flash: {:?}", e);
}
}
}
State::PrepareForHardReset => {
// State Transition
state = State::HardReset;

View File

@ -5,12 +5,12 @@ use uom::si::{
electric_current::{ampere, ElectricCurrent},
electric_potential::{volt, ElectricPotential},
electrical_resistance::{ohm, ElectricalResistance},
power::{milliwatt, Power},
power::{watt, Power},
thermodynamic_temperature::{degree_celsius, ThermodynamicTemperature}
};
use crate::{laser_diode::{laser_diode::{
LdDrive, LdSettingsSummary, StatusReport as LdStatusReport},
pd_responsitivity::ResponsitivityUnit
pd_mon_params::ResponsitivityUnit
},
net::net,
thermostat::{ad7172::FilterType, thermostat::{StatusReport as TecStatusReport, TempAdcFilter}}
@ -26,6 +26,8 @@ use smoltcp::iface::SocketHandle;
pub enum ResponseEnum {
#[default]
Reserved,
Settings,
Report,
Acknowledge,
InvalidDatatype,
InvalidCmd,
@ -150,6 +152,7 @@ pub struct Cmd {
#[derive(Deserialize, Serialize, Copy, Clone, Debug, Tree)]
pub struct StatusReport {
ts: u32,
msg_type: ResponseEnum,
laser: LdStatusReport,
thermostat: TecStatusReport,
}
@ -161,6 +164,7 @@ pub struct StatusReportObj {
#[derive(Deserialize, Serialize, Copy, Clone, Debug, Tree)]
pub struct SettingsSummary {
msg_type: ResponseEnum,
laser: LdSettingsSummary,
thermostat: ThermostatSettingsSummary,
}
@ -188,6 +192,7 @@ pub fn send_response(buffer: &mut [u8], msg_type: ResponseEnum, msg: MsgType, so
pub fn send_settings_summary(buffer: &mut [u8], laser: &mut LdDrive, thermostat: &mut Thermostat, socket: &mut SocketHandle){
let settings_summary = SettingsSummaryObj {
json: SettingsSummary {
msg_type: ResponseEnum::Settings,
laser: laser.get_settings_summary(),
thermostat: thermostat.get_settings_summary(),
}
@ -203,6 +208,7 @@ pub fn send_status_report(buffer: &mut [u8], laser: &mut LdDrive, thermostat: &m
let status_report = StatusReportObj {
json: StatusReport {
ts: sys_timer::now(),
msg_type: ResponseEnum::Report,
laser: laser.get_status_report(),
thermostat: thermostat.get_status_report(),
}
@ -255,6 +261,7 @@ pub fn execute_cmd(buffer: &mut [u8], buffer_size: usize, socket: &mut SocketHan
Some(val) => {
send_response(buffer, ResponseEnum::Acknowledge, None, socket);
device_settings.ip_settings = val;
*state = State::SaveDeviceSettings;
}
None => {
send_response(buffer, ResponseEnum::InvalidDatatype, Some(ERR_MSG_MISSING_IP_SETTINGS), socket);
@ -280,16 +287,14 @@ pub fn execute_cmd(buffer: &mut [u8], buffer_size: usize, socket: &mut SocketHan
}
}
Some(DeviceCmd::GetStatusReport) => {
send_response(buffer, ResponseEnum::Acknowledge, None, socket);
send_status_report(buffer, laser, thermostat, socket);
}
Some(DeviceCmd::GetSettingsSummary) => {
send_response(buffer, ResponseEnum::Acknowledge, None, socket);
send_settings_summary(buffer, laser, thermostat, socket);
}
Some(DeviceCmd::SaveFlashSettings) => {
send_response(buffer, ResponseEnum::Acknowledge, None, socket);
*state = State::SaveFlashSettings;
*state = State::SaveLdThermostatSettings;
}
Some(DeviceCmd::LoadFlashSettings) => {
send_response(buffer, ResponseEnum::Acknowledge, None, socket);
@ -383,7 +388,7 @@ pub fn execute_cmd(buffer: &mut [u8], buffer_size: usize, socket: &mut SocketHan
match cmd.json.data_f32 {
Some(val) => {
send_response(buffer, ResponseEnum::Acknowledge, None, socket);
laser.set_ld_power_limit(Power::new::<milliwatt>(val))
laser.set_ld_power_limit(Power::new::<watt>(val))
}
None => {
send_response(buffer, ResponseEnum::InvalidDatatype, Some(ERR_MSG_MISSING_DATA_F32), socket);

View File

@ -126,7 +126,7 @@ pub struct Thermostat {
pub struct ThermostatSettingsSummary {
default_pwr_on: bool,
pid_engaged: bool,
temperature_setpoint: ThermodynamicTemperature,
temperature_setpoint: f32,
tec_settings: TecSettingSummary,
pid_params: PidParams,
temp_adc_settings: TempAdcFilter,
@ -484,7 +484,7 @@ impl Thermostat{
ThermostatSettingsSummary {
default_pwr_on: self.tec_settings.default_pwr_on,
pid_engaged: self.get_pid_engaged(),
temperature_setpoint: self.pid_ctrl_ch0.get_pid_setpoint(),
temperature_setpoint: self.pid_ctrl_ch0.get_pid_setpoint().get::<degree_celsius>(),
tec_settings: self.get_tec_settings(),
pid_params: self.get_pid_settings(),
temp_adc_settings: TempAdcFilter{
@ -533,11 +533,11 @@ impl Thermostat{
self.set_pid_engaged(settings.pid_engaged);
self.pid_ctrl_ch0.apply_pid_params(settings.pid_params);
self.set_temperature_setpoint(settings.temperature_setpoint);
self.set_temperature_setpoint(ThermodynamicTemperature::new::<degree_celsius>(settings.temperature_setpoint));
if !settings.pid_engaged {
self.set_i(settings.tec_settings.i_set.value);
}
self.set_default_pwr_on(settings.default_pwr_on);
if settings.default_pwr_on {
self.power_up();
} else {