forked from zietzm/Helmholtz_Test_Bench
Added ability to log data when test stand is commanded
This commit is contained in:
+117
-22
@@ -108,6 +108,7 @@ class ManualMode(Frame):
|
||||
|
||||
row_counter = 0
|
||||
|
||||
# setup title text
|
||||
header = Label(self, text="Manual Input Mode", font=HEADER_FONT, pady=3)
|
||||
header.grid(row=row_counter, column=0)
|
||||
|
||||
@@ -187,12 +188,12 @@ class ManualMode(Frame):
|
||||
execute_button.grid(row=row_counter, column=0, padx=5)
|
||||
|
||||
# add button for quick power_down
|
||||
power_down_button = Button(self.buttons_frame, text="Power Down All", command=func.power_down_all,
|
||||
power_down_button = Button(self.buttons_frame, text="Power Down All", command=self.power_down,
|
||||
pady=5, padx=5, font=BIG_BUTTON_FONT)
|
||||
power_down_button.grid(row=row_counter, column=1, padx=5)
|
||||
|
||||
# add button for reinitialization
|
||||
reinit_button = Button(self.buttons_frame, text="Reinitialize", command=func.setup_all,
|
||||
reinit_button = Button(self.buttons_frame, text="Reinitialize", command=self.reinitialize,
|
||||
pady=5, padx=5, font=BIG_BUTTON_FONT)
|
||||
reinit_button.grid(row=row_counter, column=2, padx=5)
|
||||
|
||||
@@ -206,10 +207,11 @@ class ManualMode(Frame):
|
||||
self.compensate.trace_add('write', self.change_mode_callback) # call mode change function on checkbox change
|
||||
|
||||
def page_switch(self): # function that is called when switching to this page in the UI
|
||||
self.modes[self.input_mode.get()][2]() # update max values, e.g. calls update_max_fields function
|
||||
self.modes[self.input_mode.get()][2]() # update max values and units, e.g. calls update_max_fields function
|
||||
|
||||
# noinspection PyUnusedLocal
|
||||
def change_mode_callback(self, var, index, mode): # not sure what the parameters are for, but they are necessary
|
||||
# not sure what the parameters are for, but they are necessary
|
||||
def change_mode_callback(self, var, index, mode): # called input mode dropdown is changed
|
||||
self.unit.set(self.modes[self.input_mode.get()][1]) # change unit text
|
||||
self.modes[self.input_mode.get()][2]() # update max values, e.g. calls update_max_fields function
|
||||
|
||||
@@ -235,6 +237,22 @@ class ManualMode(Frame):
|
||||
val.set("(%0.2f to %0.2f A)" % (-g.AXES[i].max_amps, g.AXES[i].max_amps))
|
||||
i += 1
|
||||
|
||||
def reinitialize(self): # called on "Reinitialize!" button press
|
||||
func.setup_all() # reinitialize all PSUs and the Arduino
|
||||
|
||||
# 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
|
||||
if logger.event_logging: # data should be logged when test stand is commanded
|
||||
logger.log_datapoint() # log data
|
||||
|
||||
def power_down(self): # called on "power down" button press
|
||||
func.power_down_all() # power down outputs on all PSUs and the Arduino
|
||||
|
||||
# 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
|
||||
if logger.event_logging: # data should be logged when test stand is commanded
|
||||
logger.log_datapoint() # log data
|
||||
|
||||
def execute(self):
|
||||
function_to_call = self.modes[self.input_mode.get()][0] # get function of appropriate mode
|
||||
vector = np.array([0, 0, 0], dtype=float)
|
||||
@@ -245,6 +263,11 @@ class ManualMode(Frame):
|
||||
function_to_call(vector) # call function
|
||||
self.controller.StatusDisplay.update_labels() # update status display after change
|
||||
|
||||
# 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
|
||||
if logger.event_logging: # data should be logged when test stand is commanded
|
||||
logger.log_datapoint() # log data
|
||||
|
||||
def execute_field(self, vector):
|
||||
ui_print("field executing", vector)
|
||||
comp = self.compensate.get()
|
||||
@@ -538,6 +561,7 @@ class ExecuteCSVMode(Frame):
|
||||
row_counter = 0
|
||||
self.row_elements = [] # make list of elements in rows to calculate height available for plot
|
||||
|
||||
# setup heading
|
||||
header = Label(self, text="Execute CSV Mode", font=HEADER_FONT, pady=3)
|
||||
header.grid(row=row_counter, column=0, padx=100, sticky=W)
|
||||
self.row_elements.append(header)
|
||||
@@ -564,7 +588,7 @@ class ExecuteCSVMode(Frame):
|
||||
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,
|
||||
self.reinit_button = Button(self.top_buttons_frame, text="Reinitialize Devices", command=self.reinitialize,
|
||||
pady=5, padx=5, font=SMALL_BUTTON_FONT)
|
||||
self.reinit_button.grid(row=0, column=3, padx=5)
|
||||
|
||||
@@ -647,6 +671,19 @@ class ExecuteCSVMode(Frame):
|
||||
self.stop_button["state"] = "disabled"
|
||||
self.reinit_button["state"] = "normal"
|
||||
|
||||
# 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
|
||||
if logger.event_logging: # data should be logged when test stand is commanded
|
||||
logger.log_datapoint() # log data
|
||||
|
||||
def reinitialize(self): # called on "Reinitialize devices" button press
|
||||
func.setup_all() # reinitialize all PSUs and the Arduino
|
||||
|
||||
# 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
|
||||
if logger.event_logging: # data should be logged when test stand is commanded
|
||||
logger.log_datapoint() # log data
|
||||
|
||||
def display_plot(self):
|
||||
# calculate available height for plot (in pixels):
|
||||
height_others = 0
|
||||
@@ -665,7 +702,6 @@ class ExecuteCSVMode(Frame):
|
||||
class ConfigureLogging(Frame):
|
||||
# generate window to configure data logging to csv
|
||||
# ToDo: support logging of axis-independent info like Arduino status
|
||||
# ToDo (optional): implement logging of a datapoint every time new commands are sent to the test stand
|
||||
|
||||
def __init__(self, parent, controller):
|
||||
Frame.__init__(self, parent)
|
||||
@@ -674,18 +710,28 @@ class ConfigureLogging(Frame):
|
||||
|
||||
self.log_file = None # string containing path of log file
|
||||
self.regular_logging = False # True if data should be logged regularly
|
||||
self.event_logging = False # True if data should be logged every time a command is sent to the test stand
|
||||
# log_datapoint() has to be called wherever a command is sent to the test stand and data should be logged
|
||||
# it does not happen automatically whenever something is sent to the test stand
|
||||
# It is done mainly in the functions for UI buttons, but rather inconsistently ToDo(optional): make consistent
|
||||
|
||||
self.grid_rowconfigure(ALL, weight=1)
|
||||
self.grid_columnconfigure(ALL, weight=1)
|
||||
|
||||
row_counter = 0
|
||||
|
||||
# setup heading
|
||||
header = Label(self, text="Configure Data Logging", font=HEADER_FONT, pady=3)
|
||||
header.grid(row=row_counter, column=0, padx=100, sticky=W)
|
||||
|
||||
row_counter += 1
|
||||
|
||||
# Create and place buttons
|
||||
# Setup frame to house buttons:
|
||||
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, pady=10)
|
||||
self.top_buttons_frame.grid(row=row_counter, column=0, sticky=W, padx=20, pady=5)
|
||||
|
||||
self.stop_logging_button = Button(self.top_buttons_frame, text="Stop Logging", command=self.stop_logging,
|
||||
pady=5, padx=5, font=SMALL_BUTTON_FONT)
|
||||
@@ -718,6 +764,34 @@ class ConfigureLogging(Frame):
|
||||
|
||||
row_counter += 1
|
||||
|
||||
# create checkboxes and entries to set how often data should be logged
|
||||
self.settings_frame = Frame(self)
|
||||
self.settings_frame.grid_rowconfigure(ALL, weight=1)
|
||||
self.settings_frame.grid_columnconfigure(ALL, weight=1)
|
||||
self.settings_frame.grid(row=row_counter, column=0, sticky=W, padx=20)
|
||||
|
||||
self.regular_logging_var = BooleanVar(value=True) # create variable for the regular logging checkbox
|
||||
self.event_logging_var = BooleanVar(value=True) # create variable for the logging on command checkbox
|
||||
self.log_interval = DoubleVar(value=1) # create variable for logging interval entry field
|
||||
|
||||
# create checkboxes for regular and event logging:
|
||||
self.regular_logging_checkbox = Checkbutton(self.settings_frame, text="Log in regular intervals",
|
||||
variable=self.regular_logging_var, onvalue=True, offvalue=False)
|
||||
self.event_logging_checkbox = Checkbutton(self.settings_frame, text="Log whenever test stand is commanded",
|
||||
variable=self.event_logging_var, onvalue=True, offvalue=False)
|
||||
self.regular_logging_checkbox.grid(row=0, column=0, sticky=W)
|
||||
self.event_logging_checkbox.grid(row=1, column=0, sticky=W, columnspan=3)
|
||||
|
||||
# Set up entry field for setting logging interval
|
||||
# Add description label for logging interval entry:
|
||||
interval_label = Label(self.settings_frame, text=" Interval (s):")
|
||||
interval_label.grid(row=0, column=1, sticky=W)
|
||||
# Add entry field to set interval
|
||||
self.interval_entry = Entry(self.settings_frame, textvariable=self.log_interval)
|
||||
self.interval_entry.grid(row=0, column=2, sticky=W)
|
||||
|
||||
row_counter += 1
|
||||
|
||||
# Create checkboxes to select what data to log
|
||||
self.checkbox_frame = Frame(self)
|
||||
self.checkbox_frame.grid_rowconfigure(ALL, weight=1)
|
||||
@@ -748,26 +822,38 @@ class ConfigureLogging(Frame):
|
||||
def start_logging(self):
|
||||
ui_print("Started data logging.")
|
||||
self.update_choices() # update list with ticked checkboxes
|
||||
self.regular_logging = True
|
||||
self.regular_logging = self.regular_logging_var.get() # check if regular logging checkbox is ticked
|
||||
self.event_logging = self.event_logging_var.get() # check if event logging checkbox is ticked
|
||||
self.update_datapoint_count() # start regular update of label showing how many datapoints have been collected
|
||||
|
||||
if self.logged_datapoints.get() == 0: # no data has been logged so far
|
||||
# (if condition is here to keep timestamps consistent when repeatedly starting/stopping)
|
||||
log.zero_time = datetime.now() # set reference time for timestamps in log
|
||||
self.periodic_log(1000) # start periodic logging ToDo: get interval from entry field
|
||||
|
||||
# lock/unlock buttons and checkboxes:
|
||||
self.write_to_file_button["state"] = "disabled"
|
||||
self.clear_data_button["state"] = "normal"
|
||||
self.lock_checkboxes()
|
||||
self.stop_logging_button.tkraise() # switch button to stop
|
||||
error = False
|
||||
if self.regular_logging:
|
||||
try: # try to get log interval
|
||||
interval_ms = int(self.log_interval.get() * 1000)
|
||||
except TclError as e: # invalid entry for log interval
|
||||
messagebox.showwarning("Wrong entry format!", "Invalid entry for log interval:\n%s" % e)
|
||||
self.event_logging = False # don't start logging if there is a problem
|
||||
error = True
|
||||
else:
|
||||
self.periodic_log(interval_ms) # start periodic logging
|
||||
if (self.regular_logging or self.event_logging) and not error: # logging is active and no error during setup
|
||||
# lock/unlock buttons and checkboxes:
|
||||
self.write_to_file_button["state"] = "disabled"
|
||||
self.clear_data_button["state"] = "normal"
|
||||
self.lock_checkboxes()
|
||||
self.stop_logging_button.tkraise() # switch button to stop
|
||||
|
||||
def stop_logging(self):
|
||||
ui_print("Stopped data logging. Remember to save data to file!")
|
||||
self.regular_logging = False
|
||||
self.regular_logging = False # tell everything its time to stop logging
|
||||
self.event_logging = False # tell everything its time to stop logging
|
||||
self.write_to_file_button["state"] = "normal" # enable button
|
||||
self.unlock_checkboxes() # enable checkboxes
|
||||
self.start_logging_button.tkraise() # switch button to start
|
||||
self.start_logging_button.tkraise() # switch start/stop button to start
|
||||
|
||||
def write_to_file(self): # lets user select a file and writes logged data to it
|
||||
filepath = log.select_file() # select a file to write to
|
||||
@@ -788,6 +874,7 @@ class ConfigureLogging(Frame):
|
||||
self.write_to_file() # run write to file function to save data
|
||||
log.clear_logged_data() # delete the logged data
|
||||
log.unsaved_data = False # tell everything that there is no unsaved data remaining
|
||||
self.logged_datapoints.set(len(log.log_data)) # update the label showing how much data has been logged
|
||||
ui_print("Log data cleared.")
|
||||
|
||||
def update_choices(self):
|
||||
@@ -798,21 +885,29 @@ class ConfigureLogging(Frame):
|
||||
if self.checkbox_vars[key].get(): # box is ticked
|
||||
self.active_keys.append(key) # add corresponding item to the list
|
||||
|
||||
def lock_checkboxes(self):
|
||||
for checkbox in self.checkboxes:
|
||||
def lock_checkboxes(self): # lock all checkboxes, so they can not be modified while logging
|
||||
for checkbox in [*self.checkboxes, self.event_logging_checkbox, self.regular_logging_checkbox]:
|
||||
checkbox.config(state=DISABLED)
|
||||
self.interval_entry.config(state=DISABLED)
|
||||
|
||||
def unlock_checkboxes(self):
|
||||
for checkbox in self.checkboxes:
|
||||
for checkbox in [*self.checkboxes, self.event_logging_checkbox, self.regular_logging_checkbox]:
|
||||
checkbox.config(state=NORMAL)
|
||||
self.interval_entry.config(state=NORMAL)
|
||||
|
||||
def periodic_log(self, interval): # logs data in regular intervals (ms)
|
||||
if self.regular_logging: # logging in intervals is active
|
||||
log.log_datapoint(self.active_keys) # add datapoint with active keys to log data frame
|
||||
self.controller.after(interval, lambda: self.periodic_log(interval)) # call function again after interval
|
||||
self.log_datapoint()
|
||||
self.controller.after(interval, lambda: self.periodic_log(interval)) # call again after time interval
|
||||
|
||||
def log_datapoint(self): # log a single datapoint based on which checkboxes are ticked
|
||||
try:
|
||||
log.log_datapoint(self.active_keys) # add datapoint with active checkboxes to log data frame
|
||||
except Exception as e:
|
||||
messagebox.showerror("Error!", "Error while logging data: \n%s" % e)
|
||||
|
||||
def update_datapoint_count(self):
|
||||
if self.regular_logging: # logging is active
|
||||
if self.regular_logging or self.event_logging: # logging is active
|
||||
self.logged_datapoints.set(len(log.log_data)) # update label with number of rows in log_data
|
||||
self.controller.after(1000, self.update_datapoint_count) # call function again after 1 second
|
||||
|
||||
|
||||
+1
-10
@@ -260,16 +260,7 @@ def activate_all(): # enables remote control and output on all PSUs and channel
|
||||
g.Z_DEVICE.enable_all()
|
||||
|
||||
|
||||
def print_status_3():
|
||||
ui_print("X-Axis:")
|
||||
g.X_AXIS.print_status()
|
||||
ui_print("Y-Axis:")
|
||||
g.Y_AXIS.print_status()
|
||||
ui_print("Z-Axis:")
|
||||
g.Z_AXIS.print_status()
|
||||
|
||||
|
||||
def set_to_zero(device): # sets voltages and currents to 0
|
||||
def set_to_zero(device): # sets voltages and currents to 0 on all channels of a specific PSU
|
||||
device.voltage1 = 0
|
||||
device.current1 = 0
|
||||
device.voltage2 = 0
|
||||
|
||||
+6
-2
@@ -23,8 +23,6 @@ class ExecCSVThread(Thread):
|
||||
|
||||
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
|
||||
self.execute_sequence(self.array, 0.1, self.parent, self.controller) # run sequence
|
||||
# reset buttons on UI:
|
||||
if not g.exitFlag: # program window is open
|
||||
@@ -64,6 +62,12 @@ class ExecCSVThread(Thread):
|
||||
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
|
||||
|
||||
# log change to the log file if user has selected event logging in the Configure Logging window
|
||||
logger = controller.pages[ui.ConfigureLogging] # get object of logging configurator
|
||||
if logger.event_logging: # data should be logged when test stand is commanded
|
||||
logger.log_datapoint() # log data
|
||||
|
||||
i = i + 1 # next row
|
||||
|
||||
g.threadLock.release() # allow going back to main thread now
|
||||
|
||||
Reference in New Issue
Block a user