many things

This commit is contained in:
Martin Zietz
2021-02-08 20:10:20 +01:00
parent f9d0f8c69b
commit f0161d849d
7 changed files with 169 additions and 32 deletions
BIN
View File
Binary file not shown.
+62 -18
View File
@@ -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)
print(output)
+36 -3
View File
@@ -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
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
+66 -9
View File
@@ -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
+1 -1
View File
@@ -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]
+2
View File
@@ -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
+2 -1
View File
@@ -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
pandas~=1.1.5
matplotlib~=3.3.2