Compare commits

..

88 Commits

Author SHA1 Message Date
70627342f3 Real concurrently 2024-08-30 17:37:53 +08:00
ffa0783cff Don't create report task if not polling for it 2024-08-30 16:55:15 +08:00
ba6b24fe95 PIDAutotuner: Don't use separate sig for interval 2024-08-30 16:55:15 +08:00
2e0ecd9758 aioexample: Show polling while report mode on
WIP: handle KeyboardInterrupt
2024-08-30 16:55:15 +08:00
199aeb3609 aioclient: More accurate DFU docstring 2024-08-30 16:55:15 +08:00
1e81641169 No need for async as well 2024-08-30 16:55:15 +08:00
2c95bd9d09 No need for async 2024-08-30 16:55:15 +08:00
d83c4ed4f6 Remove extra imports 2024-08-30 16:55:15 +08:00
637d8d029a report group 2024-08-30 16:55:15 +08:00
671004703b end_session in thermostat itself afterall 2024-08-30 16:55:15 +08:00
10873933f4 Reorder MainWindow 2024-08-30 16:55:15 +08:00
ec1fffecd1 Rearrange MainWindow.__init__ 2024-08-30 16:55:15 +08:00
36c063d917 Thermostat: Add disconnect callback
For communicating with the autotuner before the client fully
disconnects

Also then there's no need for explicitly resetting autotune elements
2024-08-30 16:55:15 +08:00
65b9202cea Remove info_box_trigger 2024-08-30 16:55:15 +08:00
a72a0606f1 Remove connection errored 2024-08-30 16:55:15 +08:00
81f09a9f0f Simply test for connectivity for turning PID off 2024-08-30 16:55:15 +08:00
f768ea5d4f Fix pressing enter not working in conn menu 2024-08-30 16:55:15 +08:00
e849610658 Assign connection_errored as well 2024-08-30 16:55:15 +08:00
bbe9a60824 Back out 2024-08-30 16:55:15 +08:00
cd07cdeddd Put UI changes into conn_menu 2024-08-30 16:55:15 +08:00
fe3a22a599 ConnectionBtn + StatusLbl 2024-08-30 16:55:15 +08:00
2b35bf57f9 Clear Graph UI changes 2024-08-30 16:55:15 +08:00
8a83bbc1d1 Concentrate ThermostatCtrlMenu UI changes 2024-08-30 16:55:15 +08:00
82315d65b2 Descriptive name 2024-08-30 16:55:15 +08:00
dfe08546fe Refactor repeated stuff 2024-08-30 16:55:15 +08:00
c4309c5ba0 Split PID handler UI up 2024-08-30 16:55:15 +08:00
3387ad67f7 Fix hwrev getting 2024-08-30 16:55:15 +08:00
903f9f8767 hwrev updates 2024-08-30 15:53:05 +08:00
aadf8a2595 Fix info boxes for load/saving from flash 2024-08-30 15:53:05 +08:00
49561a2f5a Move reset request to thermostat control menu
We don't get auto reconnect anymore
2024-08-30 15:53:05 +08:00
b32b38d830 Move pid autotuning request to CtrlPanel
And update autotune UI only on state change instead of every single
report update
2024-08-30 15:53:05 +08:00
fcda46d1f3 Move channelGraph stuff inside LiveDataPlotter 2024-08-30 15:53:05 +08:00
89e9da4499 Move plot_options_menu stuff into menu 2024-08-30 15:53:05 +08:00
87ca0694cd Move autotune ticking connect 2024-08-30 15:53:05 +08:00
03233386d0 Get thermostat_ctrl_menu to subscribe to hwrev updates 2024-08-30 15:53:05 +08:00
242a2b2e69 Save/load info box content 2024-08-30 15:53:05 +08:00
b84af82722 Fix loading all channel settings would bring up 2 info boxes 2024-08-30 15:53:05 +08:00
7997ef2472 Add back asyncSlot to save_cfg_request 2024-08-30 15:53:05 +08:00
911b4c680f hw_rev 2024-08-30 15:53:05 +08:00
19677c2251 fan_update: tec_qt to thermostat_ctrl_menu 2024-08-30 15:53:05 +08:00
5f30f12875 Fix updating status label for hwrev = None 2024-08-30 15:53:05 +08:00
3283b126b8 Put some menu requests in menu itself 2024-08-30 15:53:05 +08:00
59ba8c692d Broadcast ConnectionState changes from Thermostat 2024-08-30 15:53:05 +08:00
e3ef9115a5 Move sigActivatedHandle to CtrlPanel 2024-08-30 15:53:05 +08:00
c5c311ae14 Get rid of timeout on readline 2024-08-30 15:53:05 +08:00
6699458939 Put send_command in CtrlPanel 2024-08-30 15:53:05 +08:00
8a4a963f9b Extra bail removed 2024-08-30 15:53:05 +08:00
2f10ad8056 return hwrev when start_session 2024-08-30 15:53:05 +08:00
3731282146 state str 2024-08-30 15:53:05 +08:00
50ac1c2078 params update concurrently 2024-08-30 15:53:05 +08:00
0eebc0a6df Stuff non-UI changes in Thermostat model 2024-08-30 15:53:05 +08:00
78ba799d56 Base Thermostat connectivity UI changes to state 2024-08-30 15:53:05 +08:00
f9c0f140fa Remove wait_for
OSError raised anyways
2024-08-30 15:53:05 +08:00
93d96401b9 Actually its OSError 2024-08-30 15:53:05 +08:00
881bf6ff6c {start,end}_session -> [dis]connect 2024-08-30 15:53:05 +08:00
7e0dd61f79 Simplify on_connect_btn_clicked
Raise if OSError
2024-08-30 15:53:05 +08:00
ed34eb9e03 Connecting task moved? 2024-08-30 15:53:05 +08:00
387d1226c5 AsyncIO version Client -> AsyncioClient 2024-08-30 15:53:05 +08:00
058e597ef0 Exclusively use the Thermostat object as a medium
All calls to the Thermostat should be forwarded by the medium.
2024-08-30 15:53:05 +08:00
80975f50b1 Integrate WrappedClient into Thermostat model 2024-08-30 15:53:05 +08:00
86afa19085 Should not stop cancelling read if timeout'd 2024-08-30 15:53:05 +08:00
4b6dfdf478 Fix Autotuner state for forceful disconnect 2024-08-30 15:53:05 +08:00
85cb2027a8 _ 2024-08-30 15:53:05 +08:00
4fedb14727 Make connection loss handling more elegant
Show an info box on connection lost informing the user that the
Thermostat was forcefully disconnected.
2024-08-30 15:53:05 +08:00
863920f922 ================gui_dev-fix_asyncio=============== 2024-08-30 15:53:05 +08:00
96b57f199d Use new style super() 2024-08-30 15:53:05 +08:00
f2da457860 Move command line host:port setting handling
To main
2024-08-30 15:40:31 +08:00
066c75fc21 aioclient: Add missing readline for saving
Saving all channels returns multiple JSON objects, read the extra {}.
2024-08-30 15:38:59 +08:00
60b05ddfeb import order rearrange 2024-08-30 15:38:59 +08:00
6a78a2069d This is bail 2024-08-30 15:38:59 +08:00
6eb5b7298e thermostat: Properly register task
Also Thermostat.task -> Thermostat._update_params_task
2024-08-30 15:38:58 +08:00
4a292c19db ip -> host 2024-08-30 15:38:58 +08:00
0b115b3394 Lazy evaluating for debug string command 2024-08-30 15:38:58 +08:00
2e904cb498 Add pytec runnables 2024-08-30 15:38:58 +08:00
d3f378f058 PYTHON shell 2024-08-30 15:38:58 +08:00
d0273cd41b Exactlier wording 2024-08-30 15:38:58 +08:00
0988ed24d6 unused 2024-08-30 15:38:58 +08:00
ab3b26a2a4 encoding 2024-08-30 15:38:58 +08:00
000b97398b Just catch asyncio.TimeoutError
Will just change to TimeoutError once we switch to Python 3.11 in the
flake.
2024-08-30 15:38:58 +08:00
e162bcf210 Remove exception too general 2024-08-30 15:38:58 +08:00
c3d7804898 Use asserts to check for connectivity 2024-08-30 15:38:58 +08:00
9c0977262c Add back the parent 2024-08-30 15:38:58 +08:00
017206bd34 Fix method call 2024-08-30 15:38:58 +08:00
f46bd2b50e README: Proofread 2024-08-30 15:38:58 +08:00
5b48fb2793 Swap order arounda bit more 2024-08-30 15:38:58 +08:00
358403e651 Formatting 2024-08-30 15:38:58 +08:00
4a48379923 Use qtextras 2024-08-30 15:38:58 +08:00
b766e949e8 flake update 2024-08-30 15:38:58 +08:00
5 changed files with 65 additions and 45 deletions

View File

@ -1,7 +1,19 @@
import asyncio import asyncio
from contextlib import suppress
from pytec.aioclient import AsyncioClient from pytec.aioclient import AsyncioClient
async def poll_for_info(tec):
while True:
print(tec.get_pwm())
print(tec.get_steinhart_hart())
print(tec.get_pid())
print(tec.get_postfilter())
print(tec.get_fan())
await asyncio.sleep(1)
async def main(): async def main():
tec = AsyncioClient() tec = AsyncioClient()
await tec.connect() # (host="192.168.1.26", port=23) await tec.connect() # (host="192.168.1.26", port=23)
@ -11,8 +23,14 @@ async def main():
print(await tec.get_pwm()) print(await tec.get_pwm())
print(await tec.get_postfilter()) print(await tec.get_postfilter())
print(await tec.get_steinhart_hart()) print(await tec.get_steinhart_hart())
polling_task = asyncio.create_task(poll_for_info(tec))
async for data in tec.report_mode(): async for data in tec.report_mode():
print(data) print(data)
polling_task.cancel()
with suppress(asyncio.CancelledError):
await polling_task
asyncio.run(main()) asyncio.run(main())

View File

@ -11,7 +11,6 @@ class PIDAutoTuner(QObject):
self._thermostat = thermostat self._thermostat = thermostat
self._thermostat.report_update.connect(self.tick) self._thermostat.report_update.connect(self.tick)
self._thermostat.interval_update.connect(self.update_sampling_interval)
self.autotuners = [PIDAutotune(25) for _ in range(num_of_channel)] self.autotuners = [PIDAutotune(25) for _ in range(num_of_channel)]
self.target_temp = [20.0 for _ in range(num_of_channel)] self.target_temp = [20.0 for _ in range(num_of_channel)]
@ -20,10 +19,6 @@ class PIDAutoTuner(QObject):
self.lookback = [3.0 for _ in range(num_of_channel)] self.lookback = [3.0 for _ in range(num_of_channel)]
self.sampling_interval = [1 / 16.67 for _ in range(num_of_channel)] self.sampling_interval = [1 / 16.67 for _ in range(num_of_channel)]
@pyqtSlot(list)
def update_sampling_interval(self, interval):
self.sampling_interval = interval
def set_params(self, params_name, ch, val): def set_params(self, params_name, ch, val):
getattr(self, params_name)[ch] = val getattr(self, params_name)[ch] = val
@ -50,11 +45,14 @@ class PIDAutoTuner(QObject):
@asyncSlot(list) @asyncSlot(list)
async def tick(self, report): async def tick(self, report):
for channel_report in report: for channel_report in report:
ch = channel_report["channel"]
self.sampling_interval[ch] = channel_report["interval"]
# TODO: Skip when PID Autotune or emit error message if NTC is not connected # TODO: Skip when PID Autotune or emit error message if NTC is not connected
if channel_report["temperature"] is None: if channel_report["temperature"] is None:
continue continue
ch = channel_report["channel"]
match self.autotuners[ch].state(): match self.autotuners[ch].state():
case ( case (
PIDAutotuneState.STATE_READY PIDAutotuneState.STATE_READY

View File

@ -20,7 +20,6 @@ class Thermostat(QObject, metaclass=PropertyMeta):
pid = Property(list) pid = Property(list)
pwm = Property(list) pwm = Property(list)
postfilter = Property(list) postfilter = Property(list)
interval = Property(list)
report = Property(list) report = Property(list)
connection_error = pyqtSignal() connection_error = pyqtSignal()
@ -54,36 +53,45 @@ 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() await 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): async def handle_connection_error(self):
self.end_session() await self.end_session()
self.connection_error.emit() 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()
async def update_params(self): async def update_params(self):
fan_task = asyncio.create_task(self._client.get_fan())
pwm_task = asyncio.create_task(self._client.get_pwm())
pid_task = asyncio.create_task(self._client.get_pid())
report_task = asyncio.create_task(self._client.report())
thermistor_task = asyncio.create_task(self._client.get_steinhart_hart())
postfilter_task = asyncio.create_task(self._client.get_postfilter())
self.fan = await fan_task
self.pwm = await pwm_task
if self._poll_for_report: if self._poll_for_report:
self.report = await report_task (
self.interval = [ self.fan,
self.report[i]["interval"] for i in range(len(self.report)) self.pwm,
] self.report,
self.pid = await pid_task self.pid,
self.thermistor = await thermistor_task self.thermistor,
self.postfilter = await postfilter_task self.postfilter,
) = await asyncio.gather(
self._client.get_fan(),
self._client.get_pwm(),
self._client.report(),
self._client.get_pid(),
self._client.get_steinhart_hart(),
self._client.get_postfilter(),
)
else:
self.fan, self.pwm, self.pid, self.thermistor, self.postfilter = (
await asyncio.gather(
self._client.get_fan(),
self._client.get_pwm(),
self._client.get_pid(),
self._client.get_steinhart_hart(),
self._client.get_postfilter(),
)
)
def connected(self): def connected(self):
return self._client.connected() return self._client.connected()
@ -110,9 +118,6 @@ class Thermostat(QObject, metaclass=PropertyMeta):
async def report_mode(self): async def report_mode(self):
async for report in self._client.report_mode(): async for report in self._client.report_mode():
self.report_update.emit(report) self.report_update.emit(report)
self.interval = [
self.report[i]["interval"] for i in range(len(self.report))
]
@asyncSlot() @asyncSlot()
async def end_session(self): async def end_session(self):

View File

@ -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)
@ -316,3 +303,17 @@ class CtrlPanel(QObject):
f"Channel {ch} settings has been saved to flash.\n" f"Channel {ch} settings has been saved to flash.\n"
"It will be loaded on Thermostat reset, or when settings are explicitly loaded.", "It will be loaded on Thermostat reset, or when settings are explicitly loaded.",
) )
@asyncSlot()
async def pid_auto_tune_request(self, ch=0):
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)

View File

@ -59,9 +59,7 @@ class MainWindow(QtWidgets.QMainWindow):
self.info_box = InfoBox() self.info_box = InfoBox()
# Models # Models
self.thermostat = Thermostat( self.thermostat = Thermostat(self, self.report_refresh_spin.value())
self, self.report_refresh_spin.value()
)
self._connecting_task = None self._connecting_task = None
self.thermostat.connection_state_changed.connect(self._on_connection_changed) self.thermostat.connection_state_changed.connect(self._on_connection_changed)