From 2a2a538fd80a23baa088540720be16e9ecdb177b Mon Sep 17 00:00:00 2001 From: Martin Zietz Date: Tue, 2 Feb 2021 17:56:43 +0100 Subject: [PATCH] added protection against excessive values --- User_Interface.py | 35 ++++++++++++++++++++-------- cage_func.py | 58 +++++++++++++++++++++++++++++++++++------------ globals.py | 20 ++++++++++------ main.py | 2 +- 4 files changed, 83 insertions(+), 32 deletions(-) diff --git a/User_Interface.py b/User_Interface.py index f85b305..1651e80 100644 --- a/User_Interface.py +++ b/User_Interface.py @@ -5,6 +5,7 @@ import cage_func as func import numpy as np NORM_FONT = () +HEADER_FONT = ("Arial", 13, "bold") SUB_HEADER_FONT = ("Arial", 9, "bold") BIG_BUTTON_FONT = ("Arial", 11, "bold") @@ -82,6 +83,11 @@ class ManualMode(Frame): row_counter = 0 + header = Label(self, text="Manual Input Mode", font=HEADER_FONT, pady=3) + header.grid(row=row_counter, column=0) + + row_counter += 1 + # Setup Dropdown Menu for input mode dropdown_frame = Frame(self) dropdown_frame.grid_rowconfigure(ALL, weight=1) @@ -241,6 +247,11 @@ class Configuration(Frame): row_counter = 0 + header = Label(self, text="Configuration Window", font=HEADER_FONT, pady=3) + header.grid(row=row_counter, column=0, padx=100, sticky=W) + + row_counter += 1 + # Serial port settings frame: port_frame = Frame(self) port_frame.grid_rowconfigure(ALL, weight=1) @@ -349,20 +360,24 @@ class Configuration(Frame): self.entries[key][0][i].set(round(type_value * factor, 3)) # set value with correct unit conversion def implement(self): # update config file with user inputs into entry fields and reinitialize - # ToDo: Error handling with warning messages to user if wrong data format or high/low value is entered + # ToDo: Warning messages if too high values are entered + func.edit_config("PORTS", "xy_port", self.XY_port.get()) func.edit_config("PORTS", "z_port", self.Z_port.get()) - for key in self.entries.keys(): - for i in [0, 1, 2]: - value = self.entries[key][0][i].get() - factor = self.entries[key][4] # get unit conversion factor - if factor not in [0, 1]: # prevent conversion of int variables to float representation - value = value / factor # implement unit conversion - print(key, value) - func.edit_config(g.AXIS_NAMES[i], self.entries[key][3], value) + for key in self.entries.keys(): # go through rows of entry table + for i in [0, 1, 2]: # go through columns of entry table + try: + value = self.entries[key][0][i].get() # get value from field + factor = self.entries[key][4] # get unit conversion factor + if factor not in [0, 1]: # prevent conversion of int variables to float and div/0 + value = value / factor # do unit conversion + func.edit_config(g.AXIS_NAMES[i], self.entries[key][3], value) # write new value to config file + except TclError as e: + func.ui_print("Invalid entry for %s %s %s" % (g.AXIS_NAMES[i], key, e)) - func.setup_axes() + func.setup_axes() # reinitialize devices and program with new values + self.update_fields() # update entry fields to show new values class StatusDisplay(Frame): diff --git a/cage_func.py b/cage_func.py index 19c930d..69a5a81 100644 --- a/cage_func.py +++ b/cage_func.py @@ -175,29 +175,59 @@ class ArduinoCtrl(Arduino): def read_config(section, key): # read specific value from config file - # ToDo (optional) better error handling - config_object = ConfigParser() + # ToDo (optional): better error handling + # ToDo: make pop-up error message for excessive values that can be waived + config_object = ConfigParser() # initialize config parser try: - config_object.read(g.CONFIG_FILE) - section_obj = config_object[section] - return section_obj[key] + config_object.read(g.CONFIG_FILE) # open config file + section_obj = config_object[section] # get relevant section + value = section_obj[key] # get relevant value in the section + # Value checking: + if section in g.AXIS_NAMES: # only check numerical values + max_value = g.default_arrays[key][1][g.AXIS_NAMES.index(section)] # get max value + min_value = g.default_arrays[key][2][g.AXIS_NAMES.index(section)] + if float(value) > float(max_value): + ui_print("WARNING: Too high value for", section, key, "read from config file:", + value, "max.", max_value, "allowed. Excessive values may damage equipment!") + elif float(value) < float(min_value): + ui_print("WARNING: Too low value for", section, key, "read from config file:", + value, "max.", max_value, "allowed. Excessive values may damage equipment!") + return value except KeyError as e: ui_print("Error while reading config file:", e) raise KeyError("Could not find key", key, "in config file.") def edit_config(section, key, value): # edit specific value in config file - config_object = ConfigParser() + config_object = ConfigParser() # initialize config parser + # Value checking: + # ToDo: make pop-up warning messages that can be waived try: - config_object.read(g.CONFIG_FILE) - section_obj = config_object[section] - section_obj[key] = str(value) - - with open(g.CONFIG_FILE, 'w') as conf: # Write changes back to file - config_object.write(conf) + if section in g.AXIS_NAMES: # only check numerical values + max_value = g.default_arrays[key][1][g.AXIS_NAMES.index(section)] # get max value + min_value = g.default_arrays[key][2][g.AXIS_NAMES.index(section)] + if value > max_value: + raise ValueError("Attempted to write too high value for", section, key, "to config file:", + value, ", max.", max_value, "allowed. Excessive values may damage equipment!") + elif value < min_value: + raise ValueError("Attempted to write too low value for", section, key, "to config file:", + value, ", max.", max_value, "allowed. Excessive values may damage equipment!") except KeyError as e: ui_print("Error while editing config file:", e) - raise KeyError("Could not find key", key, "in config file.") + raise KeyError("Could not find section", section, "in config file.") + except ValueError as e: # value too high/low + ui_print(e) + else: # no errors so far + try: + config_object.read(g.CONFIG_FILE) # open config file + section_obj = config_object[section] # get relevant section + section_obj[key] = str(value) # get relevant value in the section + + with open(g.CONFIG_FILE, 'w') as conf: # Write changes back to file + config_object.write(conf) + except KeyError as e: + ui_print("Error while editing config file:", e) + raise KeyError("Could not find key", key, "in config file.") def create_default_config(file): # create config file from default values (stored in globals.py) @@ -207,7 +237,7 @@ def create_default_config(file): # create config file from default values (stor for axis_name in g.AXIS_NAMES: config.add_section(axis_name) for key in g.default_arrays.keys(): - config.set(axis_name, key, str(g.default_arrays[key][i])) + config.set(axis_name, key, str(g.default_arrays[key][0][i])) i += 1 config.add_section("PORTS") diff --git a/globals.py b/globals.py index af65349..26033da 100644 --- a/globals.py +++ b/globals.py @@ -22,16 +22,22 @@ global Z_PORT global PORTS -# Default Constants: +# Default Constants and maximum/minimum values (warning messages will be generated if these are exceeded) +# format: [[default values], [maximum values], [minimum values]] +# ToDo: check actual maximum ratings +# ToDo: Add maximum current: 5A (BA Blessing page 30), remove max_watts (there for testing with resistors) default_arrays = { - "coil_const": np.array([38.6, 38.45, 37.9]) * 1e-6, # Coil constants [x,y,z] [T/A] - "ambient_field": np.array([30, 30, 30]) * 1e-6, # ambient magnetic field in measurement area [T] - "resistance": np.array([1.7, 1.7, 1.7], dtype=float), # resistance of [x,y,z] circuits [Ohm] - "max_watts": np.array([15, 15, 15], dtype=float), # max. allowed power for [x,y,z] circuits [W] - "max_volts": np.array([16, 16, 16], dtype=float), # max. allowed voltage, limited to 16V by used diodes! [V] - "relay_pin": [15, 16, 17] # pin on the arduino for reversing [x,y,z] polarity + "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] + "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] + "relay_pin": [[15, 16, 17], [15, 16, 17], [15, 16, 17]] # pins on the arduino for reversing [x,y,z] polarity } default_ports = { "xy_port": "COM1", # Serial port where PSU for X- and Y-Axes is connected "z_port": "COM2", # Serial port where PSU for Z-Axis is connected } +maximums = { + +} diff --git a/main.py b/main.py index f4a8002..b9fa5fc 100644 --- a/main.py +++ b/main.py @@ -19,7 +19,7 @@ try: # start normal operations g.app = HelmholtzGUI() func.ui_print("Program Initialized") - func.ui_print("Starting setup...") + func.ui_print("Starting setup...") # do it again, so it is printed in the UI console func.setup_axes() # initiate communication, set handles g.app.mainloop() g.app = None # reset to None so nothing tries to print in the UI output