From a17630ad5748c2c9d8501c6ca56fcb20f08b0124 Mon Sep 17 00:00:00 2001 From: Jacob Date: Tue, 28 Sep 2021 23:03:18 +0200 Subject: [PATCH] Introduced logging, ABC, applied black --- Diagramm1.dia | Bin 0 -> 2910 bytes ExampleDriver.py | 47 ++++++++++ ExampleSensor.py | 65 ++++++++++++++ FemtoDLPVA100F.py | 212 ++++++++++++++++++++++++++++++++++++++++++++++ driver.py | 21 +++++ measurement.py | 9 +- model.py | 11 ++- plot_data.py | 45 +++++----- sensor.py | 74 ++-------------- view.py | 56 +++++++----- 10 files changed, 425 insertions(+), 115 deletions(-) create mode 100644 Diagramm1.dia create mode 100644 ExampleDriver.py create mode 100644 ExampleSensor.py create mode 100644 FemtoDLPVA100F.py create mode 100644 driver.py diff --git a/Diagramm1.dia b/Diagramm1.dia new file mode 100644 index 0000000000000000000000000000000000000000..47a6e3bb2b6f9d510745dfb96d9a45af4839eeb8 GIT binary patch literal 2910 zcmV-k3!(HMiwFP!000021MOYia@)EQzRy!|lv@SjuS6an=ZurgY0k8nw3D1`4Maf_ z8j4^+$c}o^C+l5btNA$DeHj_|gr}6nw>=A) zJ%hv{o6>lG+xzqD?{ECxZ*O1yI;Hp*`ESng(vVNYsrv110h*&2_MM~;X z-=CD(luBixc>ebFo%=d((&mF*T}2Ho1rbZk=QKXXk+O+$LDEx4q<&bgu3++y4qnz7M1s+&hmP<$SMOtJ;I}1eGA9Q-# z`)uxu>acUNCaDFFWR_g`*EJDV6^u>34; z-2gn1&&tJn*n(4U<+EhCN)k@ebiDmTsvU|+n;Q$MTeLwvzTRqfYTqhr0#vh0?SVEE zQk&!+I8OBUCPTq9xqipt0w1Z@t#WnEX7z8RG?vyf8Al|ZA8Xg0nKNpKA@F>@Z6H`1=zm1RpO7E4M%21qo^8TKbUYHf*{|8-af=k`j^po#*SYM>N8`8 zmo!@6_LeMWX@Wy(ex+iqY%ys{* zqVl> z?u^L6Y5#O*HVrCXQOhL4g6Ogt9_vr{gCe=F-^}^9+B*NXhn&nC*XjH#5QX7-H%@?@ zk=M6vH1c%%|84tK$N#q+6twC^)mrh8y{~m_4L(1?>58lM`vM&QN1tEh&i)_qOug0A z+9z=y1R&CsHvXzs6M-NA1OXrj06_o<0zeP|f&dT%fFJ+_0U!wQ@`C^sB#G5B+7>>h zVN{aP4m=W^BqW}Gzyg=h(N1tU&T+Hqxmr!&E(VmD(KMoBT?9ws)p9d=o)S9FhdFj3 z^>wMhL##>QIv|(yhAUg>;Eru9#uDc-H3mi{3ezjoPL3zgRugY~9R6lENp0)O$wT8!s1s=4hE33){ zs|u_tu&ThS0;}p=tI9@B5M0F zE`7GOT~)?c@mlj$Mo67tjD|YtDiT9Nc;1cUqM_3tqM_dNlG^pD4pl{J9lHzmNWG(z zx@V@G;%8nLG37GEljgb;cqhO+0p1DlPJnj;yc6J^0Ph5NC%`)a-boqnq&j77Tey?# zd9h0Kl(m1X$ckKH{#e1G4|7L5+)M;#|NCn8^gFEvZ_0}Taa zibAF+WSwi%At@1g;yLIL&>^5hK!-H5LqLas4gnnkCsthHi50`P3;=hCJg;vyzQd>% z1wer)j2q8FBfIbEy)e0Lm+CM8JY?Bj>D#IEexn0l)|V zMgTAZfDr(U0AK_FBLElyzzFzB7y+N9dmS-69p3>J0H^>jg9>ojh4T&;4lEp4IA?Ou zjCWL|uySDK{M78T!Askl8MV&N^s-FG#z*PYOV9LFdZvYj-i_mWK zL5YA80VUExiIDFUR0pUIP#ut*`uqqGe<86{bP@gc;mse`h6&-fWkO^pN1I;XZG0ahKb-sPi#(9-C#0I&QerRv)Ph6fM37q~7UXnk0~2)TV<>umuN!to@A=c&U20$*LO zW+GoJA+24v>d?{Ti^Una?YCiSano#M1zp@uaB2~MQuhgEb`J2<;LHFZr~yF@2x>r3 z1A-b5)PSG{1T`S20YU8w2x@~D8*sJE))7~g3ud}~%SDdPkgJ4oB}}05&y?@4!iJVg z5WC`-YoX{0!VV5{N2XP~T<|nut}u$>sEBpYI9(88t`Kv5<}ue%o0w~$?}pZDjci@l z9+-pdok9*8pQUh8 z3-_!^v0xA5((1eNTCfuBxM=FN?f_?MGeDc%mVRDI4nCKjZiLOzoHU}*W`IhIZ<62CeXZK4SbLeH>th5N#*)47`>{}!0 zuwSo9SR%ZFf*_0yG09z~*rkFbuEBFl=O4nWeT-wOw_>Mb>RtIbc|U zVFiX27* 4: + exp_a = 4 + + if (exp_a % 2) == 0: + self.femtoThread.lsbA.set() + else: + self.femtoThread.lsbA.clear() + + if (floor((exp_a - 1) / 2) - 1) == 0: + self.femtoThread.msbA.set() + else: + self.femtoThread.msbA.clear() + + LOGGER.debug("(%s) exponent a = %i" % (self.name, exp_a)) + + def set_exponent_b( + self, + exp_b=1, + ): + if exp_b < 1: + exp_b = 1 + if exp_b > 4: + exp_b = 4 + + if (exp_b % 2) == 0: + self.femtoThread.lsbB.set() + else: + self.femtoThread.lsbB.clear() + + if (floor((exp_b - 1) / 2) - 1) == 0: + self.femtoThread.msbB.set() + else: + self.femtoThread.msbB.clear() + + LOGGER.debug("(%s) exponent a = %i" % (self.name, exp_b)) diff --git a/driver.py b/driver.py new file mode 100644 index 0000000..bc49c35 --- /dev/null +++ b/driver.py @@ -0,0 +1,21 @@ +from abc import ABC, abstractmethod + + +class Driver(ABC): + """docstring for Model.""" + + @abstractmethod + def exit(self): + pass + + @abstractmethod + def set_config(self, config): + pass + + @abstractmethod + def get_status(self): + pass + + @abstractmethod + def get_config(self): + pass diff --git a/measurement.py b/measurement.py index 529fda6..b53309e 100644 --- a/measurement.py +++ b/measurement.py @@ -18,9 +18,13 @@ class Measurement: data: list of tuples: (timestamp, value) """ if sensor_name not in self.data_files: - self.data_files[sensor_name] = open(os.path.join(self.path, f"{sensor_name}_data.csv"), "w") + self.data_files[sensor_name] = open( + os.path.join(self.path, f"{sensor_name}_data.csv"), "w" + ) for timestamp, value in zip(data[0], data[1]): - self.data_files[sensor_name].write(f"{timestamp.isoformat()}, {timestamp.timestamp()}, {value}\n") + self.data_files[sensor_name].write( + f"{timestamp.isoformat()}, {timestamp.timestamp()}, {value}\n" + ) def save_version_text(self, comment): with open(os.path.join(self.path, "version.txt"), "w") as file: @@ -28,4 +32,3 @@ class Measurement: if comment: file.write(comment + "\n") file.write("More descriptions of relevant parameters\n") - diff --git a/model.py b/model.py index a33ecaa..9921382 100644 --- a/model.py +++ b/model.py @@ -8,9 +8,11 @@ Data: from datetime import datetime from threading import Event, Thread from time import sleep -from sensor import Sensor +from FemtoDLPVA100F import FemtoDLPVA100F from measurement import Measurement from plot_data import PlotData +from ExampleSensor import ExampleSensor + class DataGrabber(Thread): def __init__(self, sensors, drivers, plot_data): @@ -25,7 +27,7 @@ class DataGrabber(Thread): while not self.exit_request.is_set(): for name, sens in self.sensors.items(): dat = sens.get_data - self.plot_data.append_data(name,dat) + self.plot_data.append_data(name, dat) if self.measurement is not None: self.measurement.append_data(name, dat) sleep(1) @@ -34,7 +36,8 @@ class DataGrabber(Thread): class Model: def __init__(self): self.plot_data = PlotData() - self.sensors = {"temp": Sensor(), "keks": Sensor()} + a = ExampleSensor() + self.sensors = {"temp": a, "multi": ExampleSensor()} self.drivers = [] self.measurement = None self.data_grabber = DataGrabber(self.sensors, self.drivers, self.plot_data) @@ -74,4 +77,4 @@ class Model: -""" \ No newline at end of file +""" diff --git a/plot_data.py b/plot_data.py index 2bd43d7..710e52a 100644 --- a/plot_data.py +++ b/plot_data.py @@ -2,12 +2,12 @@ import numpy as np from datetime import datetime, timedelta -class PlotData(): +class PlotData: def __init__(self): - #resolution * timeout must be at least 2-3 times higher then the slowest refresh rate + # resolution * timeout must be at least 2-3 times higher then the slowest refresh rate self.resolution = 100 # ms - self.timeout = 100 #cycles + self.timeout = 100 # cycles self.data = {"time": np.array([])} self.start_time = datetime.utcnow() self.queues = {} @@ -22,48 +22,51 @@ class PlotData(): return self.data.get(key, np.full_like(self.data["time"], np.nan, dtype=np.double)) def append_data(self, sensor_name, data): - """ - - """ + """ """ if sensor_name not in self.data: - self.queues[sensor_name] = ([],[]) + self.queues[sensor_name] = ([], []) self.data[sensor_name] = np.full_like(self.data["time"], np.nan, dtype=np.double) for time, dat in zip(data[0], data[1]): - self.queues[sensor_name][0].append((time-self.start_time)/ timedelta(milliseconds=self.resolution)) + self.queues[sensor_name][0].append( + (time - self.start_time) / timedelta(milliseconds=self.resolution) + ) self.queues[sensor_name][1].append(dat) self.extend_timeline() self.sort_in_queue() self.drop_old() def extend_timeline(self): - #extend numpy arrays with passed time - #unknown values are filled with nans - end = (datetime.utcnow()-self.start_time)/timedelta(milliseconds=self.resolution) + # extend numpy arrays with passed time + # unknown values are filled with nans + end = (datetime.utcnow() - self.start_time) / timedelta(milliseconds=self.resolution) self.data["time"] = np.arange(0, end, 1).astype(np.double) for key in self.data: if key != "time": - pad_length = self.data["time"].size-self.data[key].size - self.data[key] = np.pad(self.data[key], (0,pad_length), mode="constant", constant_values=np.nan) + pad_length = self.data["time"].size - self.data[key].size + self.data[key] = np.pad( + self.data[key], (0, pad_length), mode="constant", constant_values=np.nan + ) def sort_in_queue(self): - #linear interpolation and adding it to the array + # linear interpolation and adding it to the array for key in self.queues: time = np.array(self.queues[key][0]) data = np.array(self.queues[key][1]) if time.size > 1: - inter = np.interp(self.data["time"], time, data,left=np.nan,right=np.nan) + inter = np.interp(self.data["time"], time, data, left=np.nan, right=np.nan) self.data[key] = np.where(np.isnan(inter), self.data[key], inter) - def drop_old(self): for key in self.queues: time = np.array(self.queues[key][0]) - old = self.data["time"][-1]-(self.timeout) - if time.size>2: - drop_index = (np.where(time < old)[0]) + old = self.data["time"][-1] - (self.timeout) + if time.size > 2: + drop_index = np.where(time < old)[0] if len(drop_index) is not 0: drop_index = drop_index[-1] - self.queues[key] = (self.queues[key][0][drop_index:],self.queues[key][1][drop_index:]) + self.queues[key] = ( + self.queues[key][0][drop_index:], + self.queues[key][1][drop_index:], + ) print(drop_index) - diff --git a/sensor.py b/sensor.py index 150187d..38a1259 100644 --- a/sensor.py +++ b/sensor.py @@ -1,77 +1,21 @@ -import numpy as np -from datetime import datetime -from threading import Thread, Event -from time import sleep, time -from queue import Queue -from random import random +from abc import ABC, abstractmethod -class SensorWorker(Thread): - """ Communicates with the measuring hardware. Here we only produce random data. """ - - def __init__(self, message_queue): - super().__init__(name="Measure") - self.message_queue = message_queue - self.produceData = Event() - self.exit_request = Event() - self.prev= 0 - - def run(self): - """ Worker method of a python Thread. Called when the Thread is started. """ - while not self.exit_request.is_set(): - if self.produceData.is_set(): - temp = self.prev + random() / 100.0 - self.prev = temp - ts = datetime.utcnow() - self.message_queue.put((ts, temp)) - else: - pass - sleep(1) - - -class Sensor(object): +class Sensor(ABC): """docstring for Model.""" - def __init__(self): - super(Sensor, self).__init__() - self.measureQueue = Queue() - self.measureThread = SensorWorker(self.measureQueue) - self.measureThread.start() - self.measureThread.produceData.set() - - + @abstractmethod def start_measuring(self): - self.measureThread.produceData.set() - print("I started meas") - + pass + @abstractmethod def stop_measuring(self): - self.measureThread.produceData.clear() - print("I stopped meas") + pass + @abstractmethod def exit(self): - self.stop_measuring() - self.measureThread.exit_request.set() + pass @property def get_data(self): - time = [] - val = [] - while not self.measureQueue.empty(): - t, data = self.measureQueue.get() - time.append(t) - val.append(data) - return time, val - - - - - # def clear(self): - # self.data = [] - # self.t = [] - # print("I cleared meas") - # - # def save_measuring(self, path="temp_temp.csv"): - # print("I saved meas") - # np.savetxt(path, np.array(self.data)) - + pass diff --git a/view.py b/view.py index bfaec80..bfeeca1 100644 --- a/view.py +++ b/view.py @@ -5,6 +5,7 @@ # import importlib.machinery +import logging from threading import Thread import wx from model import Model @@ -21,7 +22,10 @@ from matplotlib.backends.backend_wxagg import ( NavigationToolbar2WxAgg as NavigationToolbar, ) from matplotlib.figure import Figure -import numpy as np + + +logging.basicConfig(level=logging.DEBUG) +LOGGER = logging.getLogger() # begin wxGlade: dependencies # end wxGlade @@ -54,7 +58,7 @@ class PlotPanel(wx.Panel): self.ax = self.fig.add_subplot() self.fig.legend() dat = self.model.plot_data - (self.im,) = self.ax.plot(dat.get("keks"), dat.get("temp"), "-o",label="temp") + (self.im,) = self.ax.plot(dat.get("keks"), dat.get("temp"), "-o", label="temp") self.toolbar.update() # Not sure why this is needed - ADS def get_toolbar(self): @@ -113,7 +117,7 @@ class MyFrame(wx.Frame): sizer_status = wx.BoxSizer(wx.VERTICAL) grid_sizer_main.Add(sizer_status, (0, 0), (2, 1), wx.EXPAND, 0) - #TODO test + # TODO test label_1 = wx.StaticText(self.panel_1, wx.ID_ANY, "Text1") sizer_status.Add(label_1, 0, 0, 0) label_2 = wx.StaticText(self.panel_1, wx.ID_ANY, u"Für") @@ -135,7 +139,7 @@ class MyFrame(wx.Frame): grid_sizer_measurement = wx.GridBagSizer(0, 0) grid_sizer_3.Add(grid_sizer_measurement, (0, 0), (1, 1), wx.EXPAND, 0) - + self.b_start = wx.Button(self.panel_1, wx.ID_ANY, "Start") grid_sizer_measurement.Add(self.b_start, (0, 0), (1, 1), 0, 0) @@ -144,7 +148,6 @@ class MyFrame(wx.Frame): self.b_run = wx.Button(self.panel_1, wx.ID_ANY, "Run") grid_sizer_measurement.Add(self.b_run, (2, 0), (1, 1), 0, 0) - grid_sizer_5 = wx.GridBagSizer(0, 0) grid_sizer_3.Add(grid_sizer_5, (1, 0), (1, 1), wx.EXPAND, 0) @@ -181,9 +184,10 @@ class MyFrame(wx.Frame): self.Bind(wx.EVT_TIMER, self.update, self.timer) self.timer.Start(100, False) self.runner_thread = None + def on_close_window(self, event): self.model.exit() - print("Closing") + LOGGER.info("Closing") wx.Exit() def update(self, event): @@ -202,12 +206,12 @@ class MyFrame(wx.Frame): self.b_start.Enable() def on_b_start(self, event): # wxGlade: MyFrame. - print("Event handler 'on_b_start'") + LOGGER.info("Event handler 'on_b_start'") self.model.start_measuring() event.Skip() def on_b_save(self, event): # wxGlade: MyFrame. - print("Event handler 'on_b_save'") + LOGGER.info("Event handler 'on_b_save'") with wx.FileDialog( self, "Save csv file", @@ -224,32 +228,39 @@ class MyFrame(wx.Frame): event.Skip() def on_b_run(self, event): # wxGlade: MyFrame. - print("Event handler 'on_b_run'") - with wx.FileDialog(self, "Open PY file", wildcard="PY files (*.py)|*.py", - style=wx.FD_OPEN | wx.FD_FILE_MUST_EXIST) as fileDialog: + LOGGER.info("Event handler 'on_b_run'") + with wx.FileDialog( + self, + "Open PY file", + wildcard="PY files (*.py)|*.py", + style=wx.FD_OPEN | wx.FD_FILE_MUST_EXIST, + ) as fileDialog: - if fileDialog.ShowModal() == wx.ID_CANCEL: - return # the user changed their mind + if fileDialog.ShowModal() == wx.ID_CANCEL: + return # the user changed their mind - # Proceed loading the file chosen by the user - pathname = fileDialog.GetPath() - script = importlib.machinery.SourceFileLoader("ghf", pathname).load_module() - self.runner_thread = Thread(target=script.main, args=(self.model,)) - self.lock() - self.runner_thread.start() - event.Skip() + # Proceed loading the file chosen by the user + pathname = fileDialog.GetPath() + script = importlib.machinery.SourceFileLoader("ghf", pathname).load_module() + self.runner_thread = Thread(target=script.main, args=(self.model,)) + self.lock() + self.runner_thread.start() + event.Skip() def on_b_stop(self, event): # wxGlade: MyFrame. self.model.stop_measuring() - print("Event handler 'on_b_stop'") + LOGGER.info("Event handler 'on_b_stop'") event.Skip() def on_b_clear(self, event): # wxGlade: MyFrame. self.model.clear() - print("Event handler 'on_b_clear'") + LOGGER.info("Event handler 'on_b_clear'") event.Skip() + + # end of class MyFrame + class MyApp(wx.App): def OnInit(self): self.frame = MyFrame(None, wx.ID_ANY, "") @@ -257,6 +268,7 @@ class MyApp(wx.App): self.frame.Show() return True + # end of class MyApp if __name__ == "__main__":