From 3e71b9a8af83529c9a2f79a0ccdf95e8d74ebf1d Mon Sep 17 00:00:00 2001
From: Martin Killenberg <martin.killenberg@desy.de>
Date: Mon, 30 Jan 2023 11:53:05 +0100
Subject: [PATCH] re-introduce measurement in a separate thread

---
 Python_script/MeasurementPlot.py | 31 +++++++++++++++++++++++++++----
 Python_script/prototype.py       | 17 +++++++++++++++--
 2 files changed, 42 insertions(+), 6 deletions(-)

diff --git a/Python_script/MeasurementPlot.py b/Python_script/MeasurementPlot.py
index e26c23e..5e72951 100644
--- a/Python_script/MeasurementPlot.py
+++ b/Python_script/MeasurementPlot.py
@@ -2,6 +2,7 @@ import pandas as pd
 import matplotlib.pyplot as plt
 import numpy as np
 import time
+import queue
 
 # Different exceptions can be thrown while plotting, depending on the backend.
 # We catch them all locally and raise our own exception instead
@@ -53,7 +54,28 @@ class MeasurementPlot:
         labels = [pc.get_label() for pc in all_path_collections]
         self.ax1[1].legend(all_path_collections, labels, loc='lower right')
 
-    def draw(self, data_frame, pdf_name=''):
+        self.data_queue = queue.Queue(10)
+
+    def stop(self):
+        self.data_queue.put((None, None, True))
+
+    def draw_in_other_thread(self, data_frame, pdf_name=''):
+        # A shallow copy is enough (although not strictly 100 % thread safe). Data is only appended and
+        # the data members are not altered.
+        frame_copy = data_frame.copy()
+        try:
+            self.data_queue.put_nowait((frame_copy, pdf_name, False))
+        except queue.Full:
+            pass
+
+    def drawing_loop(self):
+        while True:
+            data_frame, pdf_name, stop_thread = self.data_queue.get()
+            if stop_thread:
+                return
+            self.draw_in_this_thread(data_frame, pdf_name='')
+
+    def draw_in_this_thread(self, data_frame, pdf_name=''):
         timestamps = data_frame.TIMESTAMP
         minimum, maximum = self.get_extended_min_max(timestamps)
         self.ax1[0].set_xlim(minimum, maximum)
@@ -106,6 +128,7 @@ if __name__ == '__main__':
     plt.ion()
     measurements = []
 
+    #FIXME: The loop should run in a separate thread and use draw_in_other_thread
     for i in range(20):
         measurement = {
             'TIMESTAMP': i,
@@ -116,11 +139,11 @@ if __name__ == '__main__':
             'S21_MAGNITUDE': 0.3*i
         }
         measurements.append(measurement)
-        data_frame = pd.DataFrame(measurements)
-        m.draw(data_frame)
+        my_data_frame = pd.DataFrame(measurements)
+        m.draw_in_this_thread(my_data_frame)
         print(str(i))
         time.sleep(0.3)
 
     print('I am done. ')
     plt.ioff()
-    m.draw(data_frame, 'the.pdf')
+    m.draw_in_this_thread(my_data_frame, 'the.pdf')
diff --git a/Python_script/prototype.py b/Python_script/prototype.py
index 4050036..cc7a055 100755
--- a/Python_script/prototype.py
+++ b/Python_script/prototype.py
@@ -14,6 +14,7 @@ import json
 import MeasurementPlot
 import sys
 import analysis
+import threading
 
 TEMPERATURE_STABLE = 0x1
 HUMIDITY_STABLE = 0x2
@@ -137,7 +138,17 @@ class Measurements:
         plt.close()
         return sweep_values
 
+    # wrapper function which calls the impl in a separate thread and runs the
+    # plotting loop
     def perform_single_measurement(self, output, target_temp, target_hum, soaking_time, n_stable_reads):
+        measurement_thread = threading.Thread(target=self.perform_single_measurement_impl,
+                                              args=(output, target_temp, target_hum, soaking_time,
+                                                    n_stable_reads))
+        measurement_thread.start()
+        self.measurement_plot.drawing_loop()
+        measurement_thread.join()
+
+    def perform_single_measurement_impl(self, output, target_temp, target_hum, soaking_time, n_stable_reads):
         with open(output, mode='w', newline='') as csv_file:
             fieldnames = ['TIMESTAMP', 'TARGET_TEMPERATURE', 'READBACK_TEMPERATURE', 'TARGET_HUMIDITY',
                           'READBACK_HUMIDITY', 'RF_POWER', 'RF_FREQUENCY', 'DUT_IDENTIFIER', 'RUN_ID',
@@ -256,6 +267,8 @@ class Measurements:
 
                     writer.writerow(measurement)
 
+            self.measurement_plot.stop()
+
     def sleep_until(self, wakeup_time):
         remaining_sleep_time = wakeup_time - self.clock.time()
         if remaining_sleep_time > 0:
@@ -294,7 +307,7 @@ class Measurements:
         }
         self.data_collection.append(measurement)
         data_frame = pd.DataFrame(self.data_collection)
-        self.measurement_plot.draw(data_frame)
+        self.measurement_plot.draw_in_other_thread(data_frame)
 
     def current_milli_time(self):
         return int(round(self.clock.time() * 1000))
@@ -363,7 +376,7 @@ def plot_output(output_basename, measurements_appendices, show_blocking_plot, ti
     if show_blocking_plot:
         plt.ioff()
     plot = MeasurementPlot.MeasurementPlot(title)
-    plot.draw(combined_data_frame, output_basename + '_graph.pdf')
+    plot.draw_in_this_thread(combined_data_frame, output_basename + '_graph.pdf')
 
 def run_temperature_sweep_from_file(temperature_sweep_file, meas):
     with open(temperature_sweep_file) as file:
-- 
GitLab