From f0161d849dee24a2287c81be57f5e81ae62649b4 Mon Sep 17 00:00:00 2001 From: Martin Zietz Date: Mon, 8 Feb 2021 20:10:20 +0100 Subject: [PATCH] many things --- Test cases.xlsx | Bin 12889 -> 18028 bytes User_Interface.py | 80 +++++++++++++++++++++++++++++++++++----------- cage_func.py | 39 ++++++++++++++++++++-- csv_threading.py | 75 +++++++++++++++++++++++++++++++++++++------ globals.py | 2 +- main.py | 2 ++ requirements.txt | 3 +- 7 files changed, 169 insertions(+), 32 deletions(-) diff --git a/Test cases.xlsx b/Test cases.xlsx index 88dda9cf58ce409302f45180b78cc44aadcc8315..da0c57af67d80b2605f18655ffaa09d840b61233 100644 GIT binary patch delta 8940 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 diff --git a/User_Interface.py b/User_Interface.py index 36577f0..226ef4e 100644 --- a/User_Interface.py +++ b/User_Interface.py @@ -3,6 +3,8 @@ from tkinter import ttk from tkinter import messagebox from tkinter import filedialog +from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg + import numpy as np import os from os.path import exists @@ -29,8 +31,8 @@ class HelmholtzGUI(Tk): self.Menu = TopMenu(self) # displays menu bar at the top - mainArea = Frame(self, padx=20, pady=20) - mainArea.pack(side="top", fill="both", expand=False) + mainArea = Frame(self, padx=10, pady=10) + mainArea.pack(side="top", fill="both", expand=True) mainArea.grid_rowconfigure(0, weight=1) mainArea.grid_columnconfigure(0, weight=1) @@ -44,7 +46,6 @@ class HelmholtzGUI(Tk): status_frame = Frame(self) status_frame.pack(side="bottom", fill="x", expand=False) - status_frame.grid_rowconfigure(ALL, weight=1) status_frame.grid_columnconfigure(1, weight=1) self.StatusDisplay = StatusDisplay(status_frame, self) @@ -514,7 +515,6 @@ class Configuration(Frame): class ExecuteCSVMode(Frame): # generate configuration window to set program constants # ToDo (optional): Generate graph to show sequence to be executed - # ToDo: add button for reinit def __init__(self, parent, controller): Frame.__init__(self, parent) @@ -537,24 +537,57 @@ class ExecuteCSVMode(Frame): # Setup buttons # Setup frame to house buttons: - self.file_select_frame = Frame(self) - self.file_select_frame.grid_rowconfigure(ALL, weight=1) - self.file_select_frame.grid_columnconfigure(ALL, weight=1) - self.file_select_frame.grid(row=row_counter, column=0, sticky=W, padx=20) + self.top_buttons_frame = Frame(self) + self.top_buttons_frame.grid_rowconfigure(ALL, weight=1) + self.top_buttons_frame.grid_columnconfigure(ALL, weight=1) + self.top_buttons_frame.grid(row=row_counter, column=0, sticky=W, padx=20) # Create and place buttons - self.select_file_button = Button(self.file_select_frame, text="Select csv file...", command=self.load_csv, + self.select_file_button = Button(self.top_buttons_frame, text="Select csv file...", command=self.load_csv, pady=5, padx=5, font=SMALL_BUTTON_FONT) self.select_file_button.grid(row=0, column=0, padx=5) - self.execute_button = Button(self.file_select_frame, text="Run Sequence", command=self.run_sequence, + self.execute_button = Button(self.top_buttons_frame, text="Run Sequence", command=self.run_sequence, pady=5, padx=5, font=SMALL_BUTTON_FONT, state="disabled") self.execute_button.grid(row=0, column=1, padx=5) - self.stop_button = Button(self.file_select_frame, text="Stop Run", command=self.stop_run, + self.stop_button = Button(self.top_buttons_frame, text="Stop Run", command=self.stop_run, pady=5, padx=5, font=SMALL_BUTTON_FONT, state="disabled") self.stop_button.grid(row=0, column=2, padx=5) + # add button for reinitialization + self.reinit_button = Button(self.top_buttons_frame, text="Reinitialize Devices", command=func.setup_all, + pady=5, padx=5, font=SMALL_BUTTON_FONT) + self.reinit_button.grid(row=0, column=3, padx=5) row_counter += 1 + # setup testing checkboxes + self.checkbox_frame = Frame(self) + self.checkbox_frame.grid_rowconfigure(ALL, weight=1) + self.checkbox_frame.grid_columnconfigure(ALL, weight=1) + self.checkbox_frame.grid(row=row_counter, column=0, sticky=W, padx=20) + + checkbox_label = Label(self.checkbox_frame, text="Disable device connection checks:") + checkbox_label.grid(row=0, column=0, sticky=W, padx=3) + self.xy_override = BooleanVar(value=False) + self.z_override = BooleanVar(value=False) + self.arduino_override = BooleanVar(value=False) + xy_checkbox = Checkbutton(self.checkbox_frame, text="XY PSU", + variable=self.xy_override, onvalue=True, offvalue=False) + xy_checkbox.grid(row=0, column=1, padx=3) + z_checkbox = Checkbutton(self.checkbox_frame, text="Z PSU", + variable=self.z_override, onvalue=True, offvalue=False) + z_checkbox.grid(row=0, column=2, padx=3) + arduino_checkbox = Checkbutton(self.checkbox_frame, text="Arduino", + variable=self.arduino_override, onvalue=True, offvalue=False) + arduino_checkbox.grid(row=0, column=3, padx=3) + + row_counter += 1 + + # make frame for plot of csv values + self.plotFrame = Frame(self) + self.plotFrame.grid_rowconfigure(0, weight=1) + self.plotFrame.grid_columnconfigure(0, weight=1) + self.plotFrame.grid(row=row_counter, column=0, sticky="nsw", padx=10, pady=10) + def page_switch(self): # function that is called when switching to this window # every class in the UI needs this, even if it doesn't do anything pass @@ -570,9 +603,11 @@ class ExecuteCSVMode(Frame): self.sequence_array = csv.read_csv_to_array(filename) except BaseException as e: ui_print("Error while opening file:", e) - # ToDo: make error a popup + messagebox.showerror("Error!", "Error while opening file: \n%s" % e) + + csv.check_array(self.sequence_array) + self.display_plot() - # ToDo: check for excessive values self.execute_button["state"] = "normal" # activate run button elif filename == '': # this happens when file selection window is closed without selecting a file ui_print("No file selected, could not load.") @@ -584,6 +619,7 @@ class ExecuteCSVMode(Frame): self.select_file_button["state"] = "disabled" self.execute_button["state"] = "disabled" self.stop_button["state"] = "normal" + self.reinit_button["state"] = "disabled" # g.threadLock = threading.Lock() # create separate thread to run sequence execution in: @@ -592,11 +628,18 @@ class ExecuteCSVMode(Frame): csv_thread.start() # start thread def stop_run(self): - g.running = False + g.running = False # this will cause the csv loop to end # (de)activate buttons as needed: self.select_file_button["state"] = "normal" self.execute_button["state"] = "normal" self.stop_button["state"] = "disabled" + self.reinit_button["state"] = "normal" + + def display_plot(self): # ToDo: comments + figure = csv.plot_field_sequence(self.sequence_array) + plotCanvas = FigureCanvasTkAgg(figure, self.plotFrame) + plotCanvas.draw() + plotCanvas.get_tk_widget().grid(row=0, column=0, sticky="nesw") class StatusDisplay(Frame): @@ -645,11 +688,12 @@ class StatusDisplay(Frame): col = col + 1 # rowCounter = rowCounter + self.rowNo # increase row counter to place future stuff below this - self.continuous_label_update(controller, 500) # initiate regular value updates (ms) + self.update_labels() def continuous_label_update(self, controller, interval): # update display values in regular intervals self.update_labels() - if g.app is not None: + if g.app is not None: # app ist still running + # ToDo (optional): prevent call after program close controller.after(interval, lambda: self.continuous_label_update(controller, interval)) def update_labels(self): @@ -697,9 +741,9 @@ def ui_print(*content): # prints text to built in console output = "" for text in content: output = " ".join((output, str(text))) # append content - if g.app is not None: + if g.app is not None: # if main window is still open output = "".join(("\n", output)) # begin new line each time g.app.OutputConsole.console.insert(END, output) # print to console g.app.OutputConsole.console.see(END) # scroll to bottom else: # if window is not open, do normal print - print(output) \ No newline at end of file + print(output) diff --git a/cage_func.py b/cage_func.py index 300be0d..0d07be8 100644 --- a/cage_func.py +++ b/cage_func.py @@ -70,7 +70,9 @@ class Axis: self.current = self.device.get_current(self.channel) self.current_setpoint = self.device.get_current_setpoint(self.channel) except (serial.serialutil.SerialException, IndexError): - # ui_print("Connection Error with %s PSU on %s" % (self.name, self.port)) + if self.connected == "Connected": + ui_print("Connection Error with %s PSU on %s" % (self.name, self.port)) + messagebox.showerror("PSU Error", "Connection Error with %s PSU on %s" % (self.name, self.port)) self.connected = "Connection Error" self.output_active = "Unknown" self.remote_ctrl_active = "Unknown" @@ -94,6 +96,7 @@ class Axis: g.ARDUINO.digitalWrite(self.ardPin, "LOW") except Exception as e: ui_print("Error while powering down %s: %s" % (self.name, e)) + messagebox.showerror("PSU Error!", "Error while powering down %s: \n%s" % (self.name, e)) def set_signed_current(self, value): # sets current with correct polarity on this axis device = self.device @@ -167,6 +170,7 @@ class ArduinoCtrl(Arduino): axis.polarity_switched = "False" except Exception as e: ui_print("Error with Arduino:", e) + messagebox.showerror("Error with Arduino!", "Connection Error with Arduino: \n%s" % e) for axis in g.AXES: axis.polarity_switched = "Unknown" self.connected = "Connection Error" @@ -280,7 +284,7 @@ def power_down_all(): # temporary, set all outputs to 0 but keep connections en def shut_down_all(): # shutdown at program end or on error, set outputs to 0 and disable connections # ToDo: remove checks if connected or make them only for printing ui_print("\nAttempting to safely shut down all devices. Check equipment to confirm.") - message = "Tried to safely shut down all devices. Check equipment to confirm." + message = "Tried to shut down all devices. Check equipment to confirm." if g.XY_DEVICE is not None: try: set_to_zero(g.XY_DEVICE) @@ -356,4 +360,33 @@ def set_current_vec(vector): # sets needed currents on each axis for given vect axis.set_signed_current(vector[i]) except ValueError as e: ui_print(e) - i += 1 \ No newline at end of file + i += 1 + + +def devices_ok(xy_off=False, z_off=False, arduino_off=False): + # ToDo: comments + try: + if not xy_off: + if g.XY_DEVICE is not None: + g.X_AXIS.update_status_info() + if g.X_AXIS.connected != "Connected": + return False + else: + return False + if not z_off: + if g.Z_DEVICE is not None: + g.Z_AXIS.update_status_info() + if g.Z_AXIS.connected != "Connected": + return False + else: + return False + + if not arduino_off: + g.ARDUINO.update_status_info() + if g.ARDUINO.connected != "Connected": + return False + except Exception as e: + messagebox.showerror("Error!", "Error while checking devices: \n%s" % e) + return False + else: + return True diff --git a/csv_threading.py b/csv_threading.py index 16fcb7f..f284d36 100644 --- a/csv_threading.py +++ b/csv_threading.py @@ -1,12 +1,17 @@ -import User_Interface as ui -import cage_func as func import time import pandas from threading import * +from tkinter import messagebox + +import matplotlib.pyplot as plt + +import User_Interface as ui +import cage_func as func import globals as g class ExecCSVThread(Thread): + # ToDo: handling for disconnected devices def __init__(self, threadID, array, parent, controller): Thread.__init__(self) @@ -19,28 +24,36 @@ class ExecCSVThread(Thread): ui.ui_print("Starting Sequence Execution...") # g.threadLock.acquire() # Get lock to synchronize threads # ToDo: add locking/synchronization? Works without so far but might be more robust - execute_sequence(self.array, 0.1, self.controller) # run sequence + execute_sequence(self.array, 0.1, self.parent, self.controller) # run sequence self.parent.running = False # sequence finished --> no longer running # reset buttons on UI: self.parent.select_file_button["state"] = "normal" self.parent.execute_button["state"] = "normal" self.parent.stop_button["state"] = "disabled" + self.parent.reinit_button["state"] = "normal" -def execute_sequence(array, delay, controller): # runs through array containing times and desired field vectors +def execute_sequence(array, delay, parent, controller): # runs through array containing times and desired field vectors # array format: [time (s), xField (T), yField (T), zField (T)] # decimal commas # all times in seconds func.power_down_all() # sets outputs to 0 before starting t_zero = time.time() # set reference time for start of run + + # Check if everything is properly connected: + all_connected = func.devices_ok(parent.xy_override, parent.z_override, parent.arduino_override) + # True or False depending on devices status, checks for some devices may be overridden by user + i = 0 - while i < len(array) and g.running: # while array is not finished and user has not cancelled + while i < len(array) and g.running and all_connected: + # while array is not finished, user has not cancelled and devices are connected + t = time.time() - t_zero # get relative time if t >= array[i, 0]: # time for this row has come field_vec = array[i, 1:4] # extract desired field vector ui.ui_print("%f s: t = %0.2f s, target field vector = " - % (time.time()-t_zero, array[i, 0]), field_vec * 1e6, "\u03BCT") - func.set_field_simple(field_vec) # send field vector to test stand # ToDo!: reset to set_field() + % (time.time() - t_zero, array[i, 0]), field_vec * 1e6, "\u03BCT") + func.set_field(field_vec) # send field vector to test stand ui.ui_print(time.time() - t_zero) controller.StatusDisplay.update_labels() # update status display after change i = i + 1 # next row @@ -49,10 +62,17 @@ def execute_sequence(array, delay, controller): # runs through array containing pass else: # sleep to give other threads time to run time.sleep(delay) - if g.running: # sequence ended without interruption + + # check again if everything is connected before starting next loop run: + all_connected = func.devices_ok(parent.xy_override, parent.z_override, parent.arduino_override) + + if g.running and all_connected: # sequence ended without interruption ui.ui_print("Sequence executed, powering down channels.") - else: # interrupted by user + elif all_connected: # interrupted by user ui.ui_print("Sequence cancelled, powering down channels.") + elif g.running: # interrupted by device error + ui.ui_print("Error with at least one device, sequence aborted.") + messagebox.showinfo("Device Error!", "Error with at least one device, sequence aborted.") func.power_down_all() # set currents and voltages to 0, set arduino pins to low @@ -63,3 +83,40 @@ def read_csv_to_array(filepath): file = pandas.read_csv(filepath, sep=';', decimal=',', header=0) # read csv file array = file.to_numpy() # convert csv to array return array + + +def check_array(array): + # ToDo: message formatting, pop up warning + # ToDo: comments + concerns = [] + for row in array: + i = 1 + for axis in g.AXES: + value = row[i] + if value > axis.max_comp_field[1]: + concerns.append(row) + elif value < axis.max_comp_field[0]: + concerns.append(row) + i += 1 + ui.ui_print("Checked csv, found %i concerns." % len(concerns)) + if len(concerns) > 0: + ui.ui_print(concerns) + + +def plot_field_sequence(array): # ToDo: comments + # ToDo: make pretty + figure = plt.Figure(figsize=(8, 10), dpi=100) + + # noinspection PyTypeChecker,SpellCheckingInspection + axes = figure.subplots(3, sharex=True, sharey=True, gridspec_kw={'hspace': 0.4}) + + figure.suptitle("Magnetic Field Sequence") + t = array[:, 0] + + for i in [0, 1, 2]: + data = array[:, i + 1] * 1e6 + plot = axes[i] + plot.plot(t, data) + plot.set_title(g.AXIS_NAMES[i]) + + return figure diff --git a/globals.py b/globals.py index f33f60a..ffe1ec8 100644 --- a/globals.py +++ b/globals.py @@ -30,7 +30,7 @@ running = False # ToDo: put this into a config file default_arrays = { "coil_const": np.array([[38.6, 38.45, 37.9], [50, 50, 50], [0, 0, 0]]) * 1e-6, # Coil constants [x,y,z] [T/A] - "ambient_field": np.array([[30, 30, 30], [200, 200, 200], [0, 0, 0]]) * 1e-6, # background magnetic field [T] + "ambient_field": np.array([[30, 30, 30], [200, 200, 200], [-200, -200, -200]]) * 1e-6, # background magnetic field [T] "resistance": np.array([[1.7, 1.7, 1.7], [5, 5, 5], [1, 1, 1]], dtype=float), # resistance of circuits [Ohm] "max_watts": np.array([[15, 15, 15], [50, 50, 50], [0, 0, 0]], dtype=float), # max. allowed power for circuits [W] "max_volts": np.array([[14, 14, 14], [16, 16, 16], [0, 0, 0]], dtype=float), # max. allowed voltage, limited to 16V by used diodes! [V] diff --git a/main.py b/main.py index 7edfb79..30cea3b 100644 --- a/main.py +++ b/main.py @@ -26,6 +26,8 @@ try: # start normal operations print("\nOpening User Interface...") g.app = HelmholtzGUI() + g.app.state('zoomed') # open maximized + g.app.StatusDisplay.continuous_label_update(g.app, 500) # initiate regular Status Display updates (ms) ui_print("Program Initialized") config.check_config(config.CONFIG_OBJECT) # check config for values exceeding limits diff --git a/requirements.txt b/requirements.txt index 6409c7c..088bdfa 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,5 @@ numpy==1.19.3 # bug in numpy 1.19.4, 1.19.3 used as workaround pyserial~=3.5 future~=0.18.2 -pandas \ No newline at end of file +pandas~=1.1.5 +matplotlib~=3.3.2 \ No newline at end of file