forked from zietzm/Helmholtz_Test_Bench
restructured program end, tried to fix threading issue on window close (not yet fixed)
This commit is contained in:
+21
-7
@@ -8,6 +8,7 @@ from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
|
||||
import numpy as np
|
||||
import os
|
||||
from os.path import exists
|
||||
import threading
|
||||
|
||||
import globals as g
|
||||
import cage_func as func
|
||||
@@ -519,8 +520,8 @@ class ExecuteCSVMode(Frame):
|
||||
self.parent = parent
|
||||
self.controller = controller # object on which mainloop() is running, usually main window
|
||||
# Functional init:
|
||||
self.csv_thread = None # the thread object for executing csv
|
||||
self.sequence_array = None # array containing the values from the csv file
|
||||
g.running = False # variable to turn thread execution on or off
|
||||
|
||||
# Build UI:
|
||||
self.grid_rowconfigure(ALL, weight=1)
|
||||
@@ -625,14 +626,13 @@ class ExecuteCSVMode(Frame):
|
||||
self.stop_button["state"] = "normal"
|
||||
self.reinit_button["state"] = "disabled"
|
||||
|
||||
# g.threadLock = threading.Lock()
|
||||
# g.threadLock = threading.Lock() # create thread locking object, used to ensure all devices switch at once later
|
||||
# create separate thread to run sequence execution in:
|
||||
g.running = True
|
||||
csv_thread = csv.ExecCSVThread("CSV_Thread", self.sequence_array, self, self.controller)
|
||||
csv_thread.start() # start thread
|
||||
self.csv_thread = csv.ExecCSVThread("CSV_Thread", self.sequence_array, self, self.controller)
|
||||
self.csv_thread.start() # start thread
|
||||
|
||||
def stop_run(self):
|
||||
g.running = False # this will cause the csv loop to end
|
||||
self.csv_thread.stop() # this will cause the csv loop to end
|
||||
# (de)activate buttons as needed:
|
||||
self.select_file_button["state"] = "normal"
|
||||
self.execute_button["state"] = "normal"
|
||||
@@ -654,6 +654,20 @@ class ExecuteCSVMode(Frame):
|
||||
plotCanvas.get_tk_widget().grid(row=0, column=0, sticky="nesw") # place canvas in UI
|
||||
|
||||
|
||||
class ConfigureLogging(Frame):
|
||||
# generate window to configure data logging
|
||||
|
||||
def __init__(self, parent, controller):
|
||||
Frame.__init__(self, parent)
|
||||
self.parent = parent
|
||||
self.controller = controller # object on which mainloop() is running, usually main window
|
||||
|
||||
self.grid_rowconfigure(ALL, weight=1)
|
||||
self.grid_columnconfigure(ALL, weight=1)
|
||||
|
||||
row_counter = 0
|
||||
|
||||
|
||||
class StatusDisplay(Frame):
|
||||
|
||||
def __init__(self, parent, controller):
|
||||
@@ -753,7 +767,7 @@ 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 main window is still open
|
||||
if not g.exitFlag:
|
||||
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
|
||||
|
||||
+1
-2
@@ -282,7 +282,6 @@ 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 shut down all devices. Check equipment to confirm."
|
||||
if g.XY_DEVICE is not None:
|
||||
@@ -332,7 +331,7 @@ def shut_down_all(): # shutdown at program end or on error, set outputs to 0 an
|
||||
ui_print("Serial connection to Arduino closed.")
|
||||
message += "\nSerial connection to Arduino closed."
|
||||
|
||||
messagebox.showinfo("Shutdown Status", message)
|
||||
messagebox.showinfo("Program ended", message)
|
||||
|
||||
|
||||
def set_field_simple(vector): # forms magnetic field as specified by vector, w/o cancelling ambient field
|
||||
|
||||
+50
-41
@@ -19,60 +19,69 @@ class ExecCSVThread(Thread):
|
||||
self.parent = parent # object from which this is called
|
||||
self.controller = controller # object on which mainloop() is running, usually main window
|
||||
|
||||
self.__stop_event = Event()
|
||||
|
||||
def run(self):
|
||||
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.parent, self.controller) # run sequence
|
||||
self.parent.running = False # sequence finished --> no longer running
|
||||
self.execute_sequence(self.array, 0.1, self.parent, self.controller) # run sequence
|
||||
# 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"
|
||||
if not g.exitFlag: # program window is open
|
||||
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 stop(self): # stop thread execution
|
||||
self.__stop_event.set()
|
||||
|
||||
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
|
||||
def stopped(self):
|
||||
return self.__stop_event.is_set()
|
||||
|
||||
# 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
|
||||
def execute_sequence(self, 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
|
||||
|
||||
i = 0
|
||||
while i < len(array) and g.running and all_connected:
|
||||
# while array is not finished, user has not cancelled and devices are connected
|
||||
# Check if everything is properly connected:
|
||||
all_connected = func.devices_ok(parent.xy_override.get(), parent.z_override.get(), parent.arduino_override.get())
|
||||
# True or False depending on devices status, checks for some devices may be overridden by user
|
||||
|
||||
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(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
|
||||
i = 0
|
||||
while i < len(array) and all_connected and not self.stopped() and not g.exitFlag:
|
||||
# while array is not finished, devices are connected, user has not cancelled and application is running
|
||||
|
||||
elif t >= array[i, 0] - delay - 0.02: # next change time is close, not enough time to sleep
|
||||
pass
|
||||
else: # sleep to give other threads time to run
|
||||
time.sleep(delay)
|
||||
t = time.time() - t_zero # get relative time
|
||||
if t >= array[i, 0]: # time for this row has come
|
||||
# g.threadLock.acquire(0)
|
||||
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(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
|
||||
# g.threadLock.release()
|
||||
|
||||
# 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)
|
||||
elif t >= array[i, 0] - delay - 0.02: # next change time is close, not enough time to sleep
|
||||
pass
|
||||
else: # sleep to give other threads time to run
|
||||
time.sleep(delay)
|
||||
|
||||
if g.running and all_connected: # sequence ended without interruption
|
||||
ui.ui_print("Sequence executed, powering down channels.")
|
||||
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
|
||||
# check again if everything is connected before starting next loop run:
|
||||
all_connected = func.devices_ok(parent.xy_override.get(), parent.z_override.get(), parent.arduino_override.get())
|
||||
|
||||
if not self.stopped() and not g.exitFlag and all_connected: # sequence ended without interruption
|
||||
ui.ui_print("Sequence executed, powering down channels.")
|
||||
elif all_connected: # interrupted by user
|
||||
ui.ui_print("Sequence cancelled, powering down channels.")
|
||||
elif not self.stopped() and not g.exitFlag: # 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
|
||||
|
||||
|
||||
def read_csv_to_array(filepath):
|
||||
|
||||
+2
-1
@@ -21,7 +21,8 @@ global Z_PORT
|
||||
global PORTS
|
||||
|
||||
global threadLock
|
||||
running = False
|
||||
|
||||
exitFlag = True # False when main window is open, false otherwise
|
||||
|
||||
# Default Constants and maximum/minimum values (warning messages will be generated if these are exceeded)
|
||||
# format: [[default values], [maximum values], [minimum values]]
|
||||
|
||||
@@ -5,10 +5,23 @@ from tkinter import messagebox
|
||||
import cage_func as func
|
||||
from User_Interface import HelmholtzGUI
|
||||
from User_Interface import ui_print
|
||||
import User_Interface as ui
|
||||
import globals as g
|
||||
import config_handling as config
|
||||
|
||||
|
||||
def program_end():
|
||||
g.exitFlag = True # tell everything else the application has been closed
|
||||
if g.app is not None:
|
||||
if g.app.pages[ui.ExecuteCSVMode].csv_thread is not None: # end possible csv execution thread
|
||||
g.app.pages[ui.ExecuteCSVMode].csv_thread.stop() # stop thread
|
||||
g.app.pages[ui.ExecuteCSVMode].csv_thread.join() # wait for thread to finish
|
||||
# g.app = None # reset to None so nothing tries to print in the UI output
|
||||
func.shut_down_all() # shut down devices
|
||||
if g.app is not None:
|
||||
g.app.destroy() # close application
|
||||
|
||||
|
||||
try: # start normal operations
|
||||
|
||||
config.CONFIG_FILE = 'config.ini'
|
||||
@@ -26,6 +39,7 @@ try: # start normal operations
|
||||
print("\nOpening User Interface...")
|
||||
|
||||
g.app = HelmholtzGUI()
|
||||
g.exitFlag = False
|
||||
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")
|
||||
@@ -33,19 +47,22 @@ try: # start normal operations
|
||||
|
||||
ui_print("\nStarting setup...") # do it again, so it is printed in the UI console ToDo: do it only once
|
||||
func.setup_all() # initiate communication, set handles
|
||||
|
||||
g.app.protocol("WM_DELETE_WINDOW", program_end) # call program end function if user closes the application
|
||||
|
||||
g.app.mainloop()
|
||||
g.app = None # reset to None so nothing tries to print in the UI output
|
||||
|
||||
|
||||
except BaseException as e: # if there is an error, print what happened
|
||||
except Exception as e: # if there is an error, print what happened
|
||||
print("\nAn error occurred, Shutting down.")
|
||||
# shop pup-up error message:
|
||||
message = "%s.\nSee python console traceback for more details. " \
|
||||
"\nShutting down devices, check equipment to confirm." % e
|
||||
messagebox.showerror("Error!", message)
|
||||
print(traceback.print_exc())
|
||||
program_end() # safely close everything and shut down devices
|
||||
|
||||
finally: # safely shut everything down at the end
|
||||
func.shut_down_all()
|
||||
# ToDo: rework window closing code https://bytes.com/topic/python/answers/431323-detect-tkinter-window-being-closed
|
||||
# https://stackoverflow.com/questions/14694408/runtimeerror-main-thread-is-not-in-main-loop
|
||||
|
||||
# ToDo: logging
|
||||
|
||||
Reference in New Issue
Block a user