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

View File

@ -30,6 +30,19 @@ 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,15 +54,11 @@ class Thermostat(QObject, metaclass=PropertyMeta):
"Encountered an error while polling for information from Thermostat.",
exc_info=True,
)
self.handle_connection_error()
self.connection_error.emit()
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.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.report_update.connect(self.update_report)
@ -285,19 +285,6 @@ 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,7 +15,9 @@ import asyncio
import logging
import argparse
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
@ -69,19 +71,20 @@ 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:
@ -123,26 +126,27 @@ 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())
)
@asyncClose
async def closeEvent(self, _event):
try:
await self.thermostat.end_session()
except:
pass
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()
@pyqtSlot(ThermostatConnectionState)
def _on_connection_changed(self, state):
@asyncSlot(ThermostatConnectionState)
async 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:
@ -162,27 +166,16 @@ class MainWindow(QtWidgets.QMainWindow):
self.status_lbl.setText("Disconnected")
self.report_box.setChecked(False)
@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)
@asyncSlot(int)
async def on_report_box_stateChanged(self, enabled):
await self.thermostat.set_report_mode(enabled)
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()
@asyncClose
async def closeEvent(self, _event):
try:
await self.thermostat.end_session()
except:
pass
@asyncSlot()
async def on_connect_btn_clicked(self):
@ -208,9 +201,27 @@ class MainWindow(QtWidgets.QMainWindow):
else:
await self.thermostat.end_session()
@asyncSlot(int)
async def on_report_box_stateChanged(self, enabled):
await self.thermostat.set_report_mode(enabled)
@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()
async def coro_main():
@ -231,13 +242,6 @@ 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()