forked from M-Labs/thermostat
linuswck
9acff86547
- Bugs fix: 1. Params Tree user input will not get overwritten by incoming report thermostat_data_model. 2. PID Autotune Sampling Period is now set according to Thermostat sampling interval 3. PID Autotune won't get stuck in Fail State 4. Various types disconnection related Bugs 5. Number of Samples stored in the plot cannot be set 6. Limit the max settable output current to be 2000mA - Improvement: 1. Params Tree settings can be changed with external json 2. Use a Tab system to show a single channel of config instead of two 3. Expose PID Autotune lookback params 4. Icon is changed to Artiq logo - Restructure: 1. Restructure the code to follow Model-View-Delegate Design Pattern
73 lines
2.9 KiB
Python
73 lines
2.9 KiB
Python
from PyQt6.QtCore import QObject, pyqtSlot
|
|
from qasync import asyncSlot
|
|
from autotune import PIDAutotuneState, PIDAutotune
|
|
|
|
|
|
class PIDAutoTuner(QObject):
|
|
def __init__(self, parent, client, num_of_channel):
|
|
super().__init__()
|
|
|
|
self._client = client
|
|
self.autotuners = [PIDAutotune(25) for _ in range(num_of_channel)]
|
|
self.target_temp = [20.0 for _ in range(num_of_channel)]
|
|
self.test_current = [1.0 for _ in range(num_of_channel)]
|
|
self.temp_swing = [1.5 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)]
|
|
|
|
@pyqtSlot(list)
|
|
def update_sampling_interval(self, interval):
|
|
self.sampling_interval = interval
|
|
|
|
def set_params(self, params_name, ch, val):
|
|
getattr(self, params_name)[ch] = val
|
|
|
|
def get_state(self, ch):
|
|
return self.autotuners[ch].state()
|
|
|
|
def load_params_and_set_ready(self, ch):
|
|
self.autotuners[ch].setParam(
|
|
self.target_temp[ch],
|
|
self.test_current[ch] / 1000,
|
|
self.temp_swing[ch],
|
|
1 / self.sampling_interval[ch],
|
|
self.lookback[ch],
|
|
)
|
|
self.autotuners[ch].setReady()
|
|
|
|
async def stop_pid_from_running(self, ch):
|
|
self.autotuners[ch].setOff()
|
|
await self._client.set_param("pwm", ch, "i_set", 0)
|
|
|
|
@asyncSlot(list)
|
|
async def tick(self, report):
|
|
for channel_report in report:
|
|
# TODO: Skip when PID Autotune or emit error message if NTC is not connected
|
|
if channel_report["temperature"] is None:
|
|
continue
|
|
|
|
ch = channel_report["channel"]
|
|
match self.autotuners[ch].state():
|
|
case PIDAutotuneState.STATE_READY | PIDAutotuneState.STATE_RELAY_STEP_UP | PIDAutotuneState.STATE_RELAY_STEP_DOWN:
|
|
self.autotuners[ch].run(
|
|
channel_report["temperature"], channel_report["time"]
|
|
)
|
|
await self._client.set_param(
|
|
"pwm", ch, "i_set", self.autotuners[ch].output()
|
|
)
|
|
case PIDAutotuneState.STATE_SUCCEEDED:
|
|
kp, ki, kd = self.autotuners[ch].get_tec_pid()
|
|
self.autotuners[ch].setOff()
|
|
|
|
await self._client.set_param("pid", ch, "kp", kp)
|
|
await self._client.set_param("pid", ch, "ki", ki)
|
|
await self._client.set_param("pid", ch, "kd", kd)
|
|
await self._client.set_param("pwm", ch, "pid")
|
|
|
|
await self._client.set_param(
|
|
"pid", ch, "target", self.target_temp[ch]
|
|
)
|
|
case PIDAutotuneState.STATE_FAILED:
|
|
self.autotuners[ch].setOff()
|
|
await self._client.set_param("pwm", ch, "i_set", 0)
|