import asyncio from time import sleep import abc from ws.server.ws_server_multi_user import WsServerMultiUser from components import ATemperatureSensor, AHeater, AStirrer from components.sensor import TempSensorFactory from components.pid import TempController from components.plant import Pot, APlant from components.actor import HeaterFactory from ws.message import MsgIo, MessageDispatcher from utils.value import 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.set_power(self.heater.get_power_min()) self.heater.activate(False) else: self.heater.set_power(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 = HeaterFactory.create("sim") heater.set_on_changed("power_eff", ChangedFloat(pot.set_power, 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)