Compare commits

..

No commits in common. "3f32c9a4a24f88d691d68ee078dd1063c178cbfe" and "425dee173b238b607eb82f036d4621ea9694a287" have entirely different histories.

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 mode """Put the Thermostat in DFU update 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 mode. To exit it, submit a DFU leave request TCP commands in DFU update mode. The only way to exit it is by
or power-cycle the Thermostat. power-cycling.
""" """
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,6 +30,19 @@ 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,15 +54,11 @@ 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.handle_connection_error() self.connection_error.emit()
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.pid_auto_tune_request, i)) ).sigActivated.connect(partial(self.autotuners.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,19 +285,6 @@ 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,7 +15,9 @@ 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 pyqtSlot from PyQt6.QtCore import QSignalBlocker, pyqtSlot
import pyqtgraph as pg
from functools import partial
import importlib.resources import importlib.resources
@ -69,19 +71,20 @@ 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:
@ -123,26 +126,27 @@ 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())
) )
@asyncClose if args.connect:
async def closeEvent(self, _event): if args.IP:
try: self.host_set_line.setText(args.IP)
await self.thermostat.end_session() if args.PORT:
except: self.port_set_spin.setValue(int(args.PORT))
pass self.connect_btn.click()
@pyqtSlot(ThermostatConnectionState) @asyncSlot(ThermostatConnectionState)
def _on_connection_changed(self, state): async 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:
@ -162,27 +166,16 @@ class MainWindow(QtWidgets.QMainWindow):
self.status_lbl.setText("Disconnected") self.status_lbl.setText("Disconnected")
self.report_box.setChecked(False) self.report_box.setChecked(False)
@pyqtSlot(int, PIDAutotuneState) @asyncSlot(int)
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: @asyncClose
self.background_task_lbl.setText("Ready.") async def closeEvent(self, _event):
self.loading_spinner.hide() try:
self.loading_spinner.stop() await self.thermostat.end_session()
else: except:
self.background_task_lbl.setText( pass
"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):
@ -208,9 +201,27 @@ class MainWindow(QtWidgets.QMainWindow):
else: else:
await self.thermostat.end_session() await self.thermostat.end_session()
@asyncSlot(int) @asyncSlot(int, PIDAutotuneState)
async def on_report_box_stateChanged(self, enabled): async 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)
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():
@ -231,13 +242,6 @@ 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()