From f2c33f69fa51edf0adda4397d3293eea7890eff5 Mon Sep 17 00:00:00 2001 From: Leon Teichroeb Date: Fri, 22 Oct 2021 20:38:51 +0200 Subject: [PATCH] Changed hardware initialization to be asynchronous. --- main.py | 9 ++++++--- src/helmholtz_cage_device.py | 22 ++++++++++++++-------- src/user_interface.py | 18 +++++++++--------- 3 files changed, 29 insertions(+), 20 deletions(-) diff --git a/main.py b/main.py index 8f8ee95..2809b56 100644 --- a/main.py +++ b/main.py @@ -3,6 +3,7 @@ # import packages: from os.path import exists import traceback +from threading import Thread from tkinter import messagebox # import other project files: @@ -39,6 +40,7 @@ def program_end(): # called on exception or when user closes application try: # start normal operations + print("Starting application") config.CONFIG_FILE = 'config.ini' # set the config file path # ToDo: remember what the last config file was @@ -49,16 +51,17 @@ try: # start normal operations config.CONFIG_OBJECT = config.get_config_from_file(config.CONFIG_FILE) # read configuration data from config file - print("Starting setup...") # initiate communication with devices and initialize all major program objects g.CAGE_DEVICE = HelmholtzCageDevice() # Mostly a data structure to hold field data broadcast by connected tcp client with HW access. g.MAGNETOMETER = MagnetometerProxy() - print("\nOpening User Interface...") - g.app = HelmholtzGUI() # initialize user interface g.exitFlag = False # tell all functions that the user interface is now running + + # Connect hardware to HelmholtzCageDevice adapter object + g.CAGE_DEVICE.reconnect_hardware_async() + # g.app.state('zoomed') # open UI in maximized window # g.app.StatusDisplay.continuous_label_update(g.app, 1000) # initiate regular Status Display updates (ms) # ToDo: label update is very slow, commented out to save performance but should be implemented diff --git a/src/helmholtz_cage_device.py b/src/helmholtz_cage_device.py index 1db51dc..42579cc 100644 --- a/src/helmholtz_cage_device.py +++ b/src/helmholtz_cage_device.py @@ -79,11 +79,6 @@ class HelmholtzCageDevice: # The axes talks to the HW objects (Arduino, PSU) referenced in this object self.axes.append(Axis(i, self)) - self.connect_hardware() - - # Zero and activate channels. This is a sort of "armed" state so that we can send commands later - self.idle() - # --- HW COMMUNICATION THREAD --- self._cmd_exec_thread = Thread(target=self._cmd_exec_thread_method) self._cmd_exec_thread.start() @@ -93,13 +88,15 @@ class HelmholtzCageDevice: # TODO: Move to proxy def reconnect_hardware(self): - self.shutdown() - self.connect_hardware() - self.idle() + with self.hardware_lock: + self.shutdown() + self.connect_hardware() # TODO: Move to proxy def connect_hardware(self): """Connects devices. Does not check if they are already connected: Remember to call shutdown first""" + time.sleep(3) + with self.hardware_lock: # All devices are usable if the object exists. None indicates # the device is not connected/not working properly @@ -141,6 +138,15 @@ class HelmholtzCageDevice: for axis in self.axes: axis.reload_config() + # Zero and activate channels. This is a sort of "armed" state so that we can send commands later + self.idle() + + def reconnect_hardware_async(self): + """Disconnects and reconnects devices in a non-blocking call. + Acquires hardware lock and blocks other cage operations.""" + connect_hardware_thread = Thread(target=self.reconnect_hardware) + connect_hardware_thread.start() + def idle(self): """ Zero and activate channels """ if self.psu1 is not None: diff --git a/src/user_interface.py b/src/user_interface.py index 46c557b..60a19e2 100644 --- a/src/user_interface.py +++ b/src/user_interface.py @@ -282,7 +282,7 @@ class ManualMode(Frame): def reinitialize(self): # called on "Reinitialize!" button press # reinitialize all PSUs and the Arduino - g.CAGE_DEVICE.reconnect_hardware() + g.CAGE_DEVICE.reconnect_hardware_async() # log change to the log file if user has selected event logging in the Configure Logging window logger = self.controller.pages[ConfigureLogging] # get object of logging configurator @@ -507,7 +507,7 @@ class ExecuteCSVMode(Frame): def reinitialize(self): # called on "Reinitialize devices" button press # reinitialize all PSUs and the Arduino - g.CAGE_DEVICE.reconnect_hardware() + g.CAGE_DEVICE.reconnect_hardware_async() # log change to the log file if user has selected event logging in the Configure Logging window logger = self.controller.pages[ConfigureLogging] # get object of logging configurator @@ -874,7 +874,7 @@ class CalibrateAmbientField(Frame): config.edit_config("Z-Axis", "ambient_field", self.ambient_field_result[2]) ui_print("Reinitializing devices...") - g.CAGE_DEVICE.reconnect_hardware() # setup everything with the defaults + g.CAGE_DEVICE.reconnect_hardware_async() # setup everything with the defaults def save_and_apply_coil_constants(self): if self.coil_constant_results is not None: @@ -884,7 +884,7 @@ class CalibrateAmbientField(Frame): config.edit_config("Z-Axis", "coil_const", self.coil_constant_results[2]) ui_print("Reinitializing devices...") - g.CAGE_DEVICE.reconnect_hardware() # setup everything with the defaults + g.CAGE_DEVICE.reconnect_hardware_async() # setup everything with the defaults class CalibrateMagnetometer(Frame): def __init__(self, parent, controller): @@ -1271,7 +1271,7 @@ class HardwareConfiguration(Frame): def restore_defaults(self): # restore default settings config.reset_config_to_default() # overwrite config file with default ui_print("\nReinitializing devices...") - g.CAGE_DEVICE.reconnect_hardware() # setup everything with the defaults + g.CAGE_DEVICE.reconnect_hardware_async() # setup everything with the defaults self.update_fields() # update fields in config window def update_fields(self): # set current values for all entry variables from config file @@ -1355,7 +1355,7 @@ class HardwareConfiguration(Frame): def implement(self): # "Update and Reinitialize" button, update config with new values and reinitialize devices self.write_values() # write current values from entry fields to config object ui_print("\nReinitializing devices...") - g.CAGE_DEVICE.reconnect_hardware() # reinitialize devices and program with new values + g.CAGE_DEVICE.reconnect_hardware_async() # reinitialize devices and program with new values self.update_fields() # update entry fields to show new values def load_config(self): # load configuration from some config file @@ -1369,7 +1369,7 @@ class HardwareConfiguration(Frame): config.check_config(config.CONFIG_OBJECT) # check and display warnings if values are out of bounds ui_print("\nReinitializing devices...") - g.CAGE_DEVICE.reconnect_hardware() # reinitialize devices and program with new values + g.CAGE_DEVICE.reconnect_hardware_async() # reinitialize devices and program with new values self.update_fields() # update entry fields to show new values elif filename == '': # this happens when file selection window is closed without selecting a file ui_print("No file selected, could not load config.") @@ -1390,14 +1390,14 @@ class HardwareConfiguration(Frame): self.write_values() # write current entry field values to the config object config.write_config_to_file(config.CONFIG_OBJECT) # write contents of config object to file ui_print("\nReinitializing devices...") - g.CAGE_DEVICE.reconnect_hardware() # reinitialize devices and program with new values + g.CAGE_DEVICE.reconnect_hardware_async() # reinitialize devices and program with new values self.update_fields() # update entry fields to show values as they are in the config def save_config(self): # same as save_config_as() but with the current config file self.write_values() # write current entry field values to the config object config.write_config_to_file(config.CONFIG_OBJECT) # write contents of config object to file ui_print("\nReinitializing devices...") - g.CAGE_DEVICE.reconnect_hardware() # reinitialize devices and program with new values + g.CAGE_DEVICE.reconnect_hardware_async() # reinitialize devices and program with new values self.update_fields() # update entry fields to show values as they are in the config