diff --git a/brewpi.py b/brewpi.py index 095a256..26177f2 100644 --- a/brewpi.py +++ b/brewpi.py @@ -1,234 +1,17 @@ import asyncio -import abc import json 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.plant import Pot from components.actor import HeaterFactory -from ws.message import MsgIo, MessageDispatcher -from utils.value import ChangedFloat, ChangedInteger +from tasks import TaskManager +from ws.message import MessageDispatcher +from utils.value import ChangedFloat +from tasks import TempSensorTask, HeaterTask, PotTask, TcTask DT_CTRL = 0.1 - -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__': config = json.load(open("config.json")) dispatcher = MessageDispatcher() @@ -268,4 +51,3 @@ if __name__ == '__main__': h_server = server.listen("0.0.0.0", 8765) asyncio.gather(h_dispatcher, h_server) server.run_forever() - diff --git a/tasks/__init__.py b/tasks/__init__.py new file mode 100644 index 0000000..e48f498 --- /dev/null +++ b/tasks/__init__.py @@ -0,0 +1,5 @@ +from tasks.task import * +from tasks.tempsensor import * +from tasks.heater import * +from tasks.pot import * +from tasks.tempctrl import * diff --git a/tasks/heater.py b/tasks/heater.py new file mode 100644 index 0000000..8334ea2 --- /dev/null +++ b/tasks/heater.py @@ -0,0 +1,74 @@ +import asyncio +from tasks import ATask +from components import AHeater +from ws.message import MsgIo +from utils.value import ChangedInteger + + +class HeaterTask(ATask): + def __init__(self, heater_device: AHeater, interval, msg_handler: MsgIo): + ATask.__init__(self, interval) + + self.heater = heater_device + 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) + + diff --git a/tasks/pot.py b/tasks/pot.py new file mode 100644 index 0000000..2b8bdc3 --- /dev/null +++ b/tasks/pot.py @@ -0,0 +1,39 @@ +import asyncio +from tasks import ATask +from components.plant import APlant +from ws.message import MsgIo +from utils.value import ChangedInteger, ChangedFloat + + +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) + + diff --git a/tasks/task.py b/tasks/task.py new file mode 100644 index 0000000..e3bf2cd --- /dev/null +++ b/tasks/task.py @@ -0,0 +1,26 @@ +import abc +import asyncio + + +class ATask: + def __init__(self, interval): + self.interval = interval + + @abc.abstractmethod + def on_process(self): + pass + + +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) diff --git a/tasks/tempctrl.py b/tasks/tempctrl.py new file mode 100644 index 0000000..25859fb --- /dev/null +++ b/tasks/tempctrl.py @@ -0,0 +1,70 @@ +import asyncio +from tasks import ATask +from components.pid import TempController +from utils.value import ChangedFloat +from ws.message import MsgIo + + +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) + diff --git a/tasks/tempsensor.py b/tasks/tempsensor.py new file mode 100644 index 0000000..3823b45 --- /dev/null +++ b/tasks/tempsensor.py @@ -0,0 +1,31 @@ +import asyncio +from tasks import ATask +from components import ATemperatureSensor +from utils.value import ChangedFloat +from ws.message import MsgIo + + +class TempSensorTask(ATask): + def __init__(self, sensor_device: ATemperatureSensor, interval, msg_handler: MsgIo): + ATask.__init__(self, interval) + + self.msg_handler = msg_handler + msg_handler.set_recv_handler(self.recv) + self.sensor = sensor_device + + 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)