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()
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
TCP commands in DFU update mode. The only way to exit it is by
power-cycling.
TCP commands in DFU mode. To exit it, submit a DFU leave request
or power-cycle the Thermostat.
"""
async with self._command_lock:
self._writer.write("dfu\n".encode("utf-8"))

View File

@ -30,19 +30,6 @@ class PIDAutoTuner(QObject):
def get_state(self, ch):
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):
self.autotuners[ch].setParam(
self.target_temp[ch],

View File

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

View File

@ -94,7 +94,7 @@ class CtrlPanel(QObject):
)
self.params[i].child(
"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.report_update.connect(self.update_report)
@ -285,6 +285,19 @@ class CtrlPanel(QObject):
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)
async def load_settings(self, ch):
await self.thermostat.load_cfg(ch)

View File

@ -15,9 +15,7 @@ import asyncio
import logging
import argparse
from PyQt6 import QtWidgets, QtGui, uic
from PyQt6.QtCore import QSignalBlocker, pyqtSlot
import pyqtgraph as pg
from functools import partial
from PyQt6.QtCore import pyqtSlot
import importlib.resources
@ -71,20 +69,19 @@ class MainWindow(QtWidgets.QMainWindow):
self.autotuners.autotune_state_changed.connect(self.pid_autotune_handler)
# 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():
for ch in range(self.NUM_CHANNELS):
if self.autotuners.get_state(ch) != PIDAutotuneState.STATE_OFF:
await self.autotuners.stop_pid_from_running(ch)
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
def get_ctrl_panel_config(args):
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.zero_limits_warning.set_limits_warning
)
self.loading_spinner.hide()
self.report_apply_btn.clicked.connect(
lambda: self.thermostat.set_update_s(self.report_refresh_spin.value())
)
if args.connect:
if args.IP:
self.host_set_line.setText(args.IP)
if args.PORT:
self.port_set_spin.setValue(int(args.PORT))
self.connect_btn.click()
@asyncClose
async def closeEvent(self, _event):
try:
await self.thermostat.end_session()
except:
pass
@asyncSlot(ThermostatConnectionState)
async def _on_connection_changed(self, state):
@pyqtSlot(ThermostatConnectionState)
def _on_connection_changed(self, state):
self.graph_group.setEnabled(state == ThermostatConnectionState.CONNECTED)
self.report_group.setEnabled(state == ThermostatConnectionState.CONNECTED)
self.thermostat_settings.setEnabled(
state == ThermostatConnectionState.CONNECTED
)
self.report_group.setEnabled(state == ThermostatConnectionState.CONNECTED)
match state:
case ThermostatConnectionState.CONNECTED:
@ -166,16 +162,27 @@ class MainWindow(QtWidgets.QMainWindow):
self.status_lbl.setText("Disconnected")
self.report_box.setChecked(False)
@asyncSlot(int)
async def on_report_box_stateChanged(self, enabled):
await self.thermostat.set_report_mode(enabled)
@pyqtSlot(int, PIDAutotuneState)
def pid_autotune_handler(self, _ch, _state):
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
async def closeEvent(self, _event):
try:
await self.thermostat.end_session()
except:
pass
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()
@asyncSlot()
async def on_connect_btn_clicked(self):
@ -201,27 +208,9 @@ class MainWindow(QtWidgets.QMainWindow):
else:
await self.thermostat.end_session()
@asyncSlot(int, PIDAutotuneState)
async def pid_autotune_handler(self, _ch, _state):
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)
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()
@asyncSlot(int)
async def on_report_box_stateChanged(self, enabled):
await self.thermostat.set_report_mode(enabled)
async def coro_main():
@ -242,6 +231,13 @@ async def coro_main():
main_window = MainWindow(args)
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()