Files
brewpi/brewpi.py
T
2020-12-13 16:31:20 +01:00

312 lines
8.5 KiB
Python

import asyncio
from time import sleep
import abc
from ws.server.ws_server_multi_user import WsServerMultiUser
from components import ATemperatureSensor
from components.sensor import TempSensorFactory
from components.pid import TempController
from components.plant import Pot, APlant
from components.actor.heater import Heater, AHeater
from ws.message import MsgIo, MessageDispatcher
from utils.value import Value, ChangedFloat, ChangedInteger
DT_CTRL = 0.1
DT_PARAMS = 1.0
class ATask:
def __init__(self, interval):
self.interval = interval
@abc.abstractmethod
def on_process(self):
pass
class TempSensorTask(ATask):
def __init__(self, sensor: ATemperatureSensor, interval, msg_handler: MsgIo):
ATask.__init__(self, interval)
self.msg_handler = msg_handler
msg_handler.set_recv_handler(self.recv)
self.sensor = sensor
def on_temp_changed(self, value):
asyncio.create_task(self.send({'Temp': value}))
async def recv(self, data):
print(data)
async def send(self, data):
await self.msg_handler.send(data)
async def on_process(self):
print("{}: Started with interval {} s".format(self.msg_handler.get_key(), self.interval))
temp = ChangedFloat(self.on_temp_changed, prec=1)
self.sensor.set_on_changed("temp", temp.set)
while True:
self.sensor.process()
await asyncio.sleep(self.interval)
class HeaterTask(ATask):
def __init__(self, heater: AHeater, interval, msg_handler: MsgIo):
ATask.__init__(self, interval)
self.heater = heater
self.interval = interval
self.msg_handler = msg_handler
msg_handler.set_recv_handler(self.recv)
self.power_soll = 0
self.pulse_counter = 0
def actor(self, y):
self.power_soll = max(0, 250 + self.heater.get_power_max() * y)
def on_changed_active(self, is_activated):
asyncio.create_task(self.send({'Activate': int(is_activated)}))
def on_changed_power(self, power):
asyncio.create_task(self.send({'Power': power}))
async def recv(self, data):
print(data)
for pair in data.items():
if 'Power' in pair[0]:
self.power_soll = pair[1]
if 'Activate' in pair[0]:
self.heater.activate(bool(pair[1]))
async def send(self, data):
await self.msg_handler.send(data)
async def on_process(self):
print("{}: Started with interval {} s".format(self.msg_handler.get_key(), self.interval))
await self.send({'Capabilities': {'Power': {'Min': 0, 'Max': self.heater.get_power_max()}}})
self.heater.set_on_changed('is_active', ChangedInteger(self.on_changed_active).set)
self.heater.set_on_changed('power_eff', ChangedInteger(self.on_changed_power).set)
self.heater.activate(False)
pulse_period_s = 1
pulse_period_count = pulse_period_s/self.interval
while True:
if self.power_soll < self.heater.get_power_min():
self.heater.setPower(self.heater.get_power_min())
self.heater.activate(False)
else:
self.heater.setPower(self.power_soll)
self.heater.activate(True)
on_count = pulse_period_count
if self.heater.get_power_min() > 0:
on_count = pulse_period_count*min(1, self.power_soll/self.heater.get_power_min())
self.pulse_counter += 1
if self.pulse_counter >= pulse_period_count:
self.pulse_counter = 0
if self.pulse_counter < on_count:
self.heater.activate(True)
else:
self.heater.activate(False)
self.heater.process()
await asyncio.sleep(self.interval)
class PotTask(ATask):
def __init__(self, pot: APlant, interval, msg_handler: MsgIo):
ATask.__init__(self, interval)
self.msg_handler = msg_handler
msg_handler.set_recv_handler(self.recv)
self.plant = pot
def on_changed_power(self, value):
asyncio.create_task(self.send({'Power': value}))
def on_changed_temp(self, value):
asyncio.create_task(self.send({'Temp': value}))
async def recv(self, data):
print(data)
for key in data:
print(key)
async def send(self, data):
await self.msg_handler.send(data)
async def on_process(self):
print("{}: Started with interval {} s".format(self.msg_handler.get_key(), self.interval))
self.plant.set_on_changed('power_actual', ChangedInteger(self.on_changed_power).set)
self.plant.set_on_changed('temp', ChangedFloat(self.on_changed_temp, prec=1).set)
while True:
self.plant.process()
await asyncio.sleep(self.interval)
class TcTask(ATask):
def __init__(self, tc: TempController, interval, msg_handler: MsgIo):
ATask.__init__(self, interval)
self.tc = tc
self.msg_handler = msg_handler
msg_handler.set_recv_handler(self.recv)
def on_state_changed(self, value):
print ("State change to {}".format(value))
asyncio.create_task(self.send({'State': str(value)}))
def on_temp_soll_changed(self, value):
print ("Temp soll change to {}".format(value))
asyncio.create_task(self.send({'Soll': {'Temp': value}}))
def on_rate_soll_curr_changed(self, value):
print ("Rate soll change to {}".format(value))
asyncio.create_task(self.send({'Soll': {'Rate': {'Current': value}}}))
def on_rate_soll_set_changed(self, value):
print ("Rate soll change to {}".format(value))
asyncio.create_task(self.send({'Soll': {'Rate': {'Set': value}}}))
def on_temp_ist_changed(self, value):
print ("Temp ist change to {}".format(value))
asyncio.create_task(self.send({'Ist': {'Temp': value}}))
def on_rate_ist_changed(self, value):
print ("Rate ist change to {}".format(value))
asyncio.create_task(self.send({'Ist': {'Rate': value}}))
async def recv(self, msg):
print(msg)
for key in msg.keys():
if 'Soll' in key:
submsg = msg['Soll']
for subkey in submsg.keys():
if 'Temp' in subkey:
self.tc.set_theta_soll(submsg['Temp'])
if 'Rate' in subkey:
self.tc.set_heatrate_soll(submsg['Rate'])
async def send(self, data):
await self.msg_handler.send(data)
async def on_process(self):
print("{}: Started with interval {} s".format(self.msg_handler.get_key(), self.interval))
self.tc.set_on_changed('state', self.on_state_changed)
self.tc.set_on_changed('theta_ist', ChangedFloat(self.on_temp_ist_changed, prec=1).set)
self.tc.set_on_changed('heatrate_ist', ChangedFloat(self.on_rate_ist_changed, prec=1).set)
self.tc.set_on_changed('theta_soll_set', ChangedFloat(self.on_temp_soll_changed, prec=1).set)
self.tc.set_on_changed('heatrate_soll', ChangedFloat(self.on_rate_soll_curr_changed, prec=1).set)
self.tc.set_on_changed('heatrate_soll_set', ChangedFloat(self.on_rate_soll_set_changed, prec=1).set)
self.tc.set_theta_soll(20.0)
self.tc.set_heatrate_soll(1.0)
while True:
self.tc.process()
await asyncio.sleep(self.interval)
class TaskManager:
def __init__(self):
self.tasks = []
def add(self, task: ATask):
self.tasks.append(task)
def start(self):
funcs = []
for task in self.tasks:
funcs.append(task.on_process())
return asyncio.gather(*funcs)
async def asleep(time):
await asyncio.sleep(time)
if __name__ == '__main__':
if 0:
taskmgr = PeriodicUpdaterThreaded()
taskmgr.start("localhost", 8765)
bg_thread = Thread(target=taskmgr.loop.run_forever)
bg_thread.setDaemon(True)
bg_thread.start()
else:
dispatcher = MessageDispatcher()
server = WsServerMultiUser(listener=dispatcher)
taskmgr = TaskManager()
# Sensor
sensor = TempSensorFactory.create("sim")
taskmgr.add(TempSensorTask(sensor, DT_CTRL, dispatcher.msgio_get("Sensor")))
# Plant
pot_params = {
"dt" : DT_PARAMS,
"theta" : 20,
"C" : 4190,
"M" : 20,
"L" : 0.05,
"Td" : 12,
"kn" : 0.2
}
pot = Pot(pot_params, 20)
pot.set_on_changed("temp", ChangedFloat(sensor.set_fake_temp, prec=3).set)
taskmgr.add(PotTask(pot, DT_CTRL, dispatcher.msgio_get("Pot")))
# Heater
heater = Heater()
heater.set_on_changed("power_eff", ChangedFloat(pot.setPower, prec=0).set)
heater_task = HeaterTask(heater, DT_CTRL, dispatcher.msgio_get("Heater"))
taskmgr.add(heater_task)
# Temperature Controller
tc_params = {
"Hold": {
"dt": DT_PARAMS,
"kp": 1.0,
"ki": 0.0,
"kd": 0.0,
"kt": 0.0
},
"Heat": {
"dt": DT_PARAMS,
"kp": 0.1,
"ki": 0.01,
"kd": 0.0,
"kt": 1.0
}
}
# Kalman Filter
kalman_params = {
"dt" : DT_PARAMS,
"var_P" : 1,
"var_Q" : 0.0001,
"var_R" : 1
}
tc = TempController(tc_params, kalman_params)
tc.set_on_changed("y", heater_task.actor)
tc_task = TcTask(tc, DT_CTRL, dispatcher.msgio_get("TempCtrl"))
taskmgr.add(tc_task)
sensor.set_on_changed("temp", ChangedFloat(tc.set_theta_ist, prec=2).set)
# Message dispatcher
h_dispatcher = taskmgr.start()
h_server = server.listen("0.0.0.0", 8765)
asyncio.gather(h_dispatcher, h_server)
server.run_forever()
while True:
print("Hallo")
sleep(1)