Compare commits

..

13 Commits

Author SHA1 Message Date
3f32c9a4a2 aioclient: More accurate DFU docstring 2024-08-29 13:23:50 +08:00
89986fd810 No need for async as well 2024-08-29 12:51:18 +08:00
f8900d295d Order 2024-08-29 12:12:05 +08:00
f0772a8072 No need for async 2024-08-29 12:11:49 +08:00
b9ba1e2d9f Remove extra imports 2024-08-29 12:02:56 +08:00
4997db0b4c blank line 2024-08-29 11:48:39 +08:00
65e3f6395b report group 2024-08-29 11:47:39 +08:00
daa3192d41 end_session in thermostat itself afterall 2024-08-29 11:40:57 +08:00
a6be838445 Reorder MainWindow 2024-08-29 11:26:11 +08:00
89076b2d13 Actually pid_auto_tune_request belongs to ctrl_panel 2024-08-29 11:09:33 +08:00
78b3f8e419 fixup! fixup! Rearrange MainWindow.__init__ 2024-08-29 10:57:45 +08:00
3925551071 Move command line host:port setting handling
To main
2024-08-29 10:36:18 +08:00
2fd852e171 finish IP -> HOST 2024-08-29 10:35:41 +08:00
5 changed files with 69 additions and 69 deletions

View File

@ -243,11 +243,11 @@ class AsyncioClient:
await self.disconnect() await self.disconnect()
async def dfu(self): async def dfu(self):
"""Put the Thermostat in DFU update mode """Put the Thermostat in DFU mode
The client is disconnected as the Thermostat stops responding to The client is disconnected as the Thermostat stops responding to
TCP commands in DFU update mode. The only way to exit it is by TCP commands in DFU mode. To exit it, submit a DFU leave request
power-cycling. or power-cycle the Thermostat.
""" """
async with self._command_lock: async with self._command_lock:
self._writer.write("dfu\n".encode("utf-8")) self._writer.write("dfu\n".encode("utf-8"))

View File

@ -30,19 +30,6 @@ class PIDAutoTuner(QObject):
def get_state(self, ch): def get_state(self, ch):
return self.autotuners[ch].state() return self.autotuners[ch].state()
@asyncSlot()
async def pid_auto_tune_request(self, ch):
match self.get_state(ch):
case PIDAutotuneState.STATE_OFF | PIDAutotuneState.STATE_FAILED:
self.load_params_and_set_ready(ch)
case (
PIDAutotuneState.STATE_READY
| PIDAutotuneState.STATE_RELAY_STEP_UP
| PIDAutotuneState.STATE_RELAY_STEP_DOWN
):
await self.stop_pid_from_running(ch)
def load_params_and_set_ready(self, ch): def load_params_and_set_ready(self, ch):
self.autotuners[ch].setParam( self.autotuners[ch].setParam(
self.target_temp[ch], self.target_temp[ch],

View File

@ -54,11 +54,15 @@ class Thermostat(QObject, metaclass=PropertyMeta):
"Encountered an error while polling for information from Thermostat.", "Encountered an error while polling for information from Thermostat.",
exc_info=True, exc_info=True,
) )
self.connection_error.emit() self.handle_connection_error()
return return
self._update_params_task = asyncio.create_task(self.update_params()) self._update_params_task = asyncio.create_task(self.update_params())
await asyncio.sleep(self._update_s) await asyncio.sleep(self._update_s)
def handle_connection_error(self):
self.end_session()
self.connection_error.emit()
async def get_hw_rev(self): async def get_hw_rev(self):
return await self._client.hw_rev() return await self._client.hw_rev()

View File

@ -94,7 +94,7 @@ class CtrlPanel(QObject):
) )
self.params[i].child( self.params[i].child(
"PID Config", "PID Auto Tune", "Run" "PID Config", "PID Auto Tune", "Run"
).sigActivated.connect(partial(self.autotuners.pid_auto_tune_request, i)) ).sigActivated.connect(partial(self.pid_auto_tune_request, i))
self.thermostat.pid_update.connect(self.update_pid) self.thermostat.pid_update.connect(self.update_pid)
self.thermostat.report_update.connect(self.update_report) self.thermostat.report_update.connect(self.update_report)
@ -285,6 +285,19 @@ class CtrlPanel(QObject):
f"Channel {ch} PID Autotune has failed.", f"Channel {ch} PID Autotune has failed.",
) )
@asyncSlot()
async def pid_auto_tune_request(self, ch):
match self.autotuners.get_state(ch):
case PIDAutotuneState.STATE_OFF | PIDAutotuneState.STATE_FAILED:
self.autotuners.load_params_and_set_ready(ch)
case (
PIDAutotuneState.STATE_READY
| PIDAutotuneState.STATE_RELAY_STEP_UP
| PIDAutotuneState.STATE_RELAY_STEP_DOWN
):
await self.autotuners.stop_pid_from_running(ch)
@asyncSlot(int) @asyncSlot(int)
async def load_settings(self, ch): async def load_settings(self, ch):
await self.thermostat.load_cfg(ch) await self.thermostat.load_cfg(ch)

View File

@ -15,9 +15,7 @@ import asyncio
import logging import logging
import argparse import argparse
from PyQt6 import QtWidgets, QtGui, uic from PyQt6 import QtWidgets, QtGui, uic
from PyQt6.QtCore import QSignalBlocker, pyqtSlot from PyQt6.QtCore import pyqtSlot
import pyqtgraph as pg
from functools import partial
import importlib.resources import importlib.resources
@ -71,20 +69,19 @@ class MainWindow(QtWidgets.QMainWindow):
self.autotuners.autotune_state_changed.connect(self.pid_autotune_handler) self.autotuners.autotune_state_changed.connect(self.pid_autotune_handler)
# Handlers for disconnections # Handlers for disconnections
@asyncSlot()
async def handle_connection_error():
self.info_box.display_info_box(
"Connection Error", "Thermostat connection lost. Is it unplugged?"
)
await self.thermostat.end_session()
self.thermostat.connection_error.connect(handle_connection_error)
async def autotune_disconnect(): async def autotune_disconnect():
for ch in range(self.NUM_CHANNELS): for ch in range(self.NUM_CHANNELS):
if self.autotuners.get_state(ch) != PIDAutotuneState.STATE_OFF: if self.autotuners.get_state(ch) != PIDAutotuneState.STATE_OFF:
await self.autotuners.stop_pid_from_running(ch) await self.autotuners.stop_pid_from_running(ch)
self.thermostat.disconnect_cb = autotune_disconnect self.thermostat.disconnect_cb = autotune_disconnect
@pyqtSlot()
def handle_connection_error():
self.info_box.display_info_box(
"Connection Error", "Thermostat connection lost. Is it unplugged?"
)
self.thermostat.connection_error.connect(handle_connection_error)
# Control Panel # Control Panel
def get_ctrl_panel_config(args): def get_ctrl_panel_config(args):
with open(args.param_tree, "r", encoding="utf-8") as f: with open(args.param_tree, "r", encoding="utf-8") as f:
@ -126,27 +123,26 @@ class MainWindow(QtWidgets.QMainWindow):
self.ctrl_panel_view.set_zero_limits_warning_sig.connect( self.ctrl_panel_view.set_zero_limits_warning_sig.connect(
self.zero_limits_warning.set_limits_warning self.zero_limits_warning.set_limits_warning
) )
self.loading_spinner.hide() self.loading_spinner.hide()
self.report_apply_btn.clicked.connect( self.report_apply_btn.clicked.connect(
lambda: self.thermostat.set_update_s(self.report_refresh_spin.value()) lambda: self.thermostat.set_update_s(self.report_refresh_spin.value())
) )
if args.connect: @asyncClose
if args.IP: async def closeEvent(self, _event):
self.host_set_line.setText(args.IP) try:
if args.PORT: await self.thermostat.end_session()
self.port_set_spin.setValue(int(args.PORT)) except:
self.connect_btn.click() pass
@asyncSlot(ThermostatConnectionState) @pyqtSlot(ThermostatConnectionState)
async def _on_connection_changed(self, state): def _on_connection_changed(self, state):
self.graph_group.setEnabled(state == ThermostatConnectionState.CONNECTED) self.graph_group.setEnabled(state == ThermostatConnectionState.CONNECTED)
self.report_group.setEnabled(state == ThermostatConnectionState.CONNECTED)
self.thermostat_settings.setEnabled( self.thermostat_settings.setEnabled(
state == ThermostatConnectionState.CONNECTED state == ThermostatConnectionState.CONNECTED
) )
self.report_group.setEnabled(state == ThermostatConnectionState.CONNECTED)
match state: match state:
case ThermostatConnectionState.CONNECTED: case ThermostatConnectionState.CONNECTED:
@ -166,16 +162,27 @@ class MainWindow(QtWidgets.QMainWindow):
self.status_lbl.setText("Disconnected") self.status_lbl.setText("Disconnected")
self.report_box.setChecked(False) self.report_box.setChecked(False)
@asyncSlot(int) @pyqtSlot(int, PIDAutotuneState)
async def on_report_box_stateChanged(self, enabled): def pid_autotune_handler(self, _ch, _state):
await self.thermostat.set_report_mode(enabled) ch_tuning = []
for ch in range(self.NUM_CHANNELS):
if self.autotuners.get_state(ch) in {
PIDAutotuneState.STATE_READY,
PIDAutotuneState.STATE_RELAY_STEP_UP,
PIDAutotuneState.STATE_RELAY_STEP_DOWN,
}:
ch_tuning.append(ch)
@asyncClose if len(ch_tuning) == 0:
async def closeEvent(self, _event): self.background_task_lbl.setText("Ready.")
try: self.loading_spinner.hide()
await self.thermostat.end_session() self.loading_spinner.stop()
except: else:
pass self.background_task_lbl.setText(
"Autotuning channel {ch}...".format(ch=ch_tuning)
)
self.loading_spinner.start()
self.loading_spinner.show()
@asyncSlot() @asyncSlot()
async def on_connect_btn_clicked(self): async def on_connect_btn_clicked(self):
@ -201,27 +208,9 @@ class MainWindow(QtWidgets.QMainWindow):
else: else:
await self.thermostat.end_session() await self.thermostat.end_session()
@asyncSlot(int, PIDAutotuneState) @asyncSlot(int)
async def pid_autotune_handler(self, _ch, _state): async def on_report_box_stateChanged(self, enabled):
ch_tuning = [] await self.thermostat.set_report_mode(enabled)
for ch in range(self.NUM_CHANNELS):
if self.autotuners.get_state(ch) in {
PIDAutotuneState.STATE_READY,
PIDAutotuneState.STATE_RELAY_STEP_UP,
PIDAutotuneState.STATE_RELAY_STEP_DOWN,
}:
ch_tuning.append(ch)
if len(ch_tuning) == 0:
self.background_task_lbl.setText("Ready.")
self.loading_spinner.hide()
self.loading_spinner.stop()
else:
self.background_task_lbl.setText(
"Autotuning channel {ch}...".format(ch=ch_tuning)
)
self.loading_spinner.start()
self.loading_spinner.show()
async def coro_main(): async def coro_main():
@ -242,6 +231,13 @@ async def coro_main():
main_window = MainWindow(args) main_window = MainWindow(args)
main_window.show() main_window.show()
if args.connect:
if args.HOST:
main_window.conn_menu.host_set_line.setText(args.HOST)
if args.PORT:
main_window.conn_menu.port_set_spin.setValue(int(args.PORT))
main_window.connect_btn.click()
await app_quit_event.wait() await app_quit_event.wait()