From 54f2322c07cb6feb0b90c87a45716c236745e540 Mon Sep 17 00:00:00 2001 From: jens Date: Mon, 18 Oct 2021 17:57:40 +0200 Subject: [PATCH] - added generic tracer class - create tracer for TC - tracer task runs TC-Tracer --- brewpi.py | 14 +++++- components/pid/temp_controller_smith.py | 6 +-- results_tc.m | 45 +++++++++++++++++ tasks/tracer.py | 13 +++-- tracer.py | 67 +++++++++++++++++++++++++ 5 files changed, 138 insertions(+), 7 deletions(-) create mode 100644 results_tc.m create mode 100644 tracer.py diff --git a/brewpi.py b/brewpi.py index da076c4..4635472 100755 --- a/brewpi.py +++ b/brewpi.py @@ -9,6 +9,7 @@ from components.pid import PidFactory from components.plant import Pot from components.actor import HeaterFactory, StirrerFactory from tasks import TaskManager, TempSensorTask, HeaterTask, PotTask, TcTask, StirrerTask, TracerTask +from tracer import Tracer if __name__ == '__main__': config = json.load(open("config.json")) @@ -40,13 +41,24 @@ if __name__ == '__main__': tc_task = TcTask(tc, DT_TASK, dispatcher.msgio_get("TempCtrl")) taskmgr.add(tc_task) + tc_trace_vars = [ + {'variable': 'theta_ist_plant', 'name': 'theta_ist_plant', 'unit': ''}, + {'variable': 'dtheta_ist_plant', 'name': 'dtheta_ist_plant', 'unit': ''}, + {'variable': 'theta_ist_model', 'name': 'theta_ist_model', 'unit': ''}, + {'variable': 'dtheta_ist_model', 'name': 'dtheta_ist_model', 'unit': ''}, + {'variable': 'theta_ist_model_delay', 'name': 'theta_ist_model_delay', 'unit': ''}, + {'variable': 'dtheta_ist_model_delay', 'name': 'dtheta_ist_model_delay', 'unit': ''} + ] + + trace_tc = Tracer(tc, tc_trace_vars, name='tc_trace') + # Stirrer stirrer = StirrerFactory.create(config['Controller']['stirrer_name'], DT, config['Stirrer']) stirrer_task = StirrerTask(stirrer, DT_TASK, dispatcher.msgio_get("Stirrer")) taskmgr.add(stirrer_task) # Tracer - taskmgr.add(TracerTask(sensor, heater, tc, DT_TASK_TRACER, dispatcher.msgio_get("Tracer"))) + taskmgr.add(TracerTask(sensor, heater, tc, trace_tc, DT_TASK_TRACER, dispatcher.msgio_get("Tracer"))) # Assign data flow tc.set_on_changed("y", heater_task.actor) diff --git a/components/pid/temp_controller_smith.py b/components/pid/temp_controller_smith.py index ad41f45..e2ad709 100755 --- a/components/pid/temp_controller_smith.py +++ b/components/pid/temp_controller_smith.py @@ -100,9 +100,9 @@ class TempController(APid): self.heatrate_soll = self.heatrate_soll_set * self.pid_hold.get_y() - print ("Model : T_ist={:2.2f}, dT_ist={:2.2f}".format(theta_ist_model, heatrate_ist_model)) - print ("Model*z-1: T_ist={:2.2f}, dT_ist={:2.2f}".format(theta_ist_model_delay, dtheta_ist_model_delay)) - print ("Plant : T_ist={:2.2f}, dT_ist={:2.2f}".format(theta_ist_plant, heatrate_ist_plant)) +# print ("Model : T_ist={:2.2f}, dT_ist={:2.2f}".format(theta_ist_model, heatrate_ist_model)) +# print ("Model*z-1: T_ist={:2.2f}, dT_ist={:2.2f}".format(theta_ist_model_delay, dtheta_ist_model_delay)) +# print ("Plant : T_ist={:2.2f}, dT_ist={:2.2f}".format(theta_ist_plant, heatrate_ist_plant)) if 0: theta_err = self.theta_soll_set - (theta_ist_plant - theta_ist_model_delay + theta_ist_model) else: diff --git a/results_tc.m b/results_tc.m new file mode 100644 index 0000000..8d2ec37 --- /dev/null +++ b/results_tc.m @@ -0,0 +1,45 @@ +## Copyright (C) 2020 Jens +## +## This program is free software: you can redistribute it and/or modify it +## under the terms of the GNU General Public License as published by +## the Free Software Foundation, either version 3 of the License, or +## (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, but +## WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program. If not, see +## . + +## -*- texinfo -*- +## @deftypefn {} {@var{retval} =} results (@var{input1}, @var{input2}) +## +## @seealso{} +## @end deftypefn + +## Author: Jens +## Created: 2020-12-20 + +function retval = results_tc() + +load ('logs/tc_trace.mat') + +close all; + +figure(1) + +set(0, "defaultlinelinewidth", 1.5); +subplot(2,1,1) +plot(time, theta_ist_plant, time, theta_ist_model, theta_ist_model_delay); grid +title("Temperature [°C]") +legend("Plant","Model","Model*z") + +subplot(2,1,2) +plot(time, dtheta_ist_plant, time, dtheta_ist_model, dtheta_ist_model_delay); grid +title("Heatrate [°C/min]") +legend("Plant","Model","Model*z") + +endfunction diff --git a/tasks/tracer.py b/tasks/tracer.py index 5a6398d..8ef61bb 100644 --- a/tasks/tracer.py +++ b/tasks/tracer.py @@ -5,6 +5,7 @@ from ws.message import MsgIo from components import ATemperatureSensor from components import AHeater from components import APid +from tracer import Tracer import numpy as np import scipy.io @@ -12,7 +13,7 @@ import os class TracerTask(ATask): - def __init__(self, sensor: ATemperatureSensor, heater: AHeater, temp_ctrl: APid, interval, msg_handler: MsgIo, path= './logs'): + def __init__(self, sensor: ATemperatureSensor, heater: AHeater, temp_ctrl: APid, tracer: Tracer, interval, msg_handler: MsgIo, path= './logs'): ATask.__init__(self, interval) if not os.path.exists(path): os.makedirs(path) @@ -23,6 +24,7 @@ class TracerTask(ATask): self.sensor = sensor self.heater = heater self.temp_ctrl = temp_ctrl + self.tracer = tracer self.path = path async def recv(self, data): @@ -44,8 +46,13 @@ class TracerTask(ATask): _tc_dtemp_soll = np.empty(0) _tc_dtemp_commanded = np.empty(0) - filename = os.path.join(self.path, '') + "brewpi." + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".mat" + name = 'brewpi' + filename = os.path.join(self.path, '') + name + ".mat" + filename_full = os.path.join(self.path, '') + name + "." + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".mat" + while True: + self.tracer.process() + _timestamp = np.append(_timestamp, timestamp) _sensor_temp = np.append(_sensor_temp, self.sensor.temperature()) _heater_power = np.append(_heater_power, self.heater.get_power()) @@ -57,8 +64,8 @@ class TracerTask(ATask): data = {'time': _timestamp, 'sensor_temp': _sensor_temp, 'heater_power': _heater_power, 'tc_temp_ist': _tc_temp_ist, 'tc_dtemp_ist': _tc_dtemp_ist, 'tc_temp_soll': _tc_temp_soll, 'tc_dtemp_soll': _tc_dtemp_soll, 'tc_dtemp_commanded': _tc_dtemp_commanded} - scipy.io.savemat('brewpi.mat', data) scipy.io.savemat(filename, data) + scipy.io.savemat(filename_full, data) timestamp += 1 await asyncio.sleep(self.interval) diff --git a/tracer.py b/tracer.py new file mode 100644 index 0000000..58aad7a --- /dev/null +++ b/tracer.py @@ -0,0 +1,67 @@ +from typing import TypedDict +import numpy as np +import scipy.io +import os +import time + + +class Entry(TypedDict): + variable: str + name: str + unit: str + + +class Tracer: + def __init__(self, obj, var_list: Entry, name='default', path='./logs'): + if not os.path.exists(path): + os.makedirs(path) + self.obj = obj + self.var_list = var_list + self.timestamp = 0 + self.timestamp_data = np.empty(0) + self.filename_full = os.path.join(path, '') + name + "." + time.strftime("%Y%m%d%H%M%S", time.localtime()) + ".mat" + self.filename = os.path.join(path, '') + name + ".mat" + self.data_list = {} + for var in self.var_list: + self.data_list[var['name']] = np.empty(0) + + def process(self): + data = {} + self.timestamp_data = np.append(self.timestamp_data, self.timestamp) + data['time'] = self.timestamp_data + + for var in self.var_list: + key_name = var['name'] + key_variable = var['variable'] + self.data_list[key_name] = np.append(self.data_list[key_name], self.obj.__getattribute__(key_variable)) + data[key_name] = self.data_list[key_name] + + scipy.io.savemat(self.filename, data) + scipy.io.savemat(self.filename_full, data) + self.timestamp += 1 + + +class Test: + def __init__(self): + self.a = 0 + self.b = 0 + + def process(self): + self.a += 1 + self.b += 10 + + @staticmethod + def get_entry(): + a: Entry = [{'variable': 'a', 'name' : 'v_a', 'unit': 'u_a'}] + b: Entry = [{'variable': 'b', 'name' : 'v_b', 'unit': 'u_b'}] + return a + b + + +if __name__ == '__main__': + test = Test() + + dut = Tracer(test, Test.get_entry(), name='TracerTest') + + for i in range(0, 10): + dut.process() + test.process()