forked from zietzm/Helmholtz_Test_Bench
more safe value checking
This commit is contained in:
+56
-11
@@ -1,5 +1,6 @@
|
||||
from tkinter import *
|
||||
from tkinter import ttk
|
||||
from tkinter import messagebox
|
||||
import globals as g
|
||||
import cage_func as func
|
||||
import numpy as np
|
||||
@@ -264,7 +265,7 @@ class Configuration(Frame):
|
||||
port_vars = [self.XY_port, self.Z_port]
|
||||
row = 0
|
||||
for text in entry_texts:
|
||||
field = ttk.Entry(port_frame, textvariable=port_vars[row])
|
||||
field = Entry(port_frame, textvariable=port_vars[row])
|
||||
field.grid(row=row, column=1, sticky=W)
|
||||
axis_label = Label(port_frame, text=text, padx=5, pady=10)
|
||||
axis_label.grid(row=row, column=0, sticky=W)
|
||||
@@ -293,7 +294,7 @@ class Configuration(Frame):
|
||||
"Arduino Pins:": [[IntVar() for _ in range(3)], "-", "Should be 15, 16, 17", "relay_pin", 1]
|
||||
}
|
||||
|
||||
self.update_fields() # set current values from config file
|
||||
self.fields = {}
|
||||
|
||||
# Fill in header (axis names):
|
||||
col = 1
|
||||
@@ -304,9 +305,11 @@ class Configuration(Frame):
|
||||
# generate table with entries, unit labels and descriptions:
|
||||
row = 1
|
||||
for key in self.entries.keys():
|
||||
self.fields[key] = []
|
||||
for axis in range(3): # generate entry fields
|
||||
field = ttk.Entry(value_frame, textvariable=self.entries[key][0][axis], width=10)
|
||||
field = Entry(value_frame, textvariable=self.entries[key][0][axis], width=10)
|
||||
field.grid(row=row, column=axis+1, sticky=W, padx=2)
|
||||
self.fields[key].append(field) # safe access to field for use elsewhere
|
||||
axis_label = Label(value_frame, text=key, padx=5, pady=5)
|
||||
axis_label.grid(row=row, column=0, sticky=W)
|
||||
unit_label = Label(value_frame, text=self.entries[key][1])
|
||||
@@ -317,6 +320,10 @@ class Configuration(Frame):
|
||||
|
||||
row_counter += 1
|
||||
|
||||
print(self.fields)
|
||||
|
||||
self.update_fields() # set current values from config file
|
||||
|
||||
Label(self, text="", pady=10).grid(row=row_counter, column=0) # add spacer
|
||||
row_counter += 1
|
||||
|
||||
@@ -359,23 +366,61 @@ class Configuration(Frame):
|
||||
factor = self.entries[key][4] # get unit conversion factor
|
||||
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: Warning messages if too high values are entered
|
||||
value_check = func.value_in_limits(g.AXIS_NAMES[i], self.entries[key][3], value)
|
||||
if value_check == 'OK':
|
||||
self.fields[key][i].config(background="White")
|
||||
else:
|
||||
self.fields[key][i].config(background="Red")
|
||||
|
||||
def implement(self): # update config file with user inputs into entry fields and reinitialize
|
||||
|
||||
# set serial ports for PSUs:
|
||||
func.edit_config("PORTS", "xy_port", self.XY_port.get())
|
||||
func.edit_config("PORTS", "z_port", self.Z_port.get())
|
||||
|
||||
# set numeric values for all axes
|
||||
for key in self.entries.keys(): # go through rows of entry table
|
||||
for i in [0, 1, 2]: # go through columns of entry table
|
||||
for i in [0, 1, 2]: # go through columns of entry table (axes)
|
||||
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:
|
||||
except TclError as e: # wrong format entered, e.g. text in number fields
|
||||
func.ui_print("Invalid entry for %s %s %s" % (g.AXIS_NAMES[i], key, e))
|
||||
|
||||
else: # format is ok
|
||||
factor = self.entries[key][4] # get unit conversion factor
|
||||
if factor not in [0, 1]: # prevent div/0 and conversion of int variables to float
|
||||
value = value / factor # do unit conversion
|
||||
|
||||
# Check if value is within safe limits
|
||||
config_key = self.entries[key][3] # handle by which value is indexed in config file
|
||||
value_ok = func.value_in_limits(g.AXIS_NAMES[i], config_key, value)
|
||||
unit = self.entries[key][1] # get unit string for error messages
|
||||
|
||||
if value_ok == 'OK':
|
||||
func.edit_config(g.AXIS_NAMES[i], config_key, value) # write new value to config file
|
||||
else: # value is not within limits
|
||||
if value_ok == 'HIGH':
|
||||
max_value = g.default_arrays[config_key][1][i] # get max value
|
||||
message = "Attempted to set too high value for {s} {k}\n" \
|
||||
"{v} {unit}, max. {mv} {unit} allowed. Excessive values may damage equipment!\n" \
|
||||
"Do you really want to use this value?"\
|
||||
.format(s=g.AXIS_NAMES[i], k=key, v=value*factor, mv=round(max_value*factor, 1), unit=unit)
|
||||
elif value_ok == 'LOW':
|
||||
min_value = g.default_arrays[config_key][2][i] # get min value
|
||||
message = "Attempted to set too low value for {s} {k}\n" \
|
||||
"{v} {unit}, min. {mv} {unit} allowed. Excessive values may damage equipment!\n" \
|
||||
"Do you really want to use this value?"\
|
||||
.format(s=g.AXIS_NAMES[i], k=key, v=value*factor, mv=round(min_value*factor, 1), unit=unit)
|
||||
else: message = "Unknown case, this should not happen."
|
||||
|
||||
# display pop-up message to ask user if he really wants the value
|
||||
answer = messagebox.askquestion("Value out of Bounds", message)
|
||||
# becomes 'yes' or 'no' depending on user choice
|
||||
if answer == 'yes': # user really wants the value
|
||||
# call function to write new value to config file with override=True
|
||||
func.edit_config(g.AXIS_NAMES[i], config_key, value, True)
|
||||
# if user chooses 'no' nothing happens, old value is kept
|
||||
|
||||
func.setup_axes() # reinitialize devices and program with new values
|
||||
self.update_fields() # update entry fields to show new values
|
||||
|
||||
|
||||
+73
-40
@@ -6,6 +6,7 @@ import time
|
||||
import numpy as np
|
||||
import serial
|
||||
import traceback
|
||||
import User_Interface as ui
|
||||
from tkinter import *
|
||||
from tkinter import messagebox
|
||||
from configparser import ConfigParser
|
||||
@@ -177,22 +178,23 @@ class ArduinoCtrl(Arduino):
|
||||
|
||||
def read_config(section, key): # read specific value from config file
|
||||
# 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) # 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
|
||||
|
||||
# Value checking: ToDo: decide if we want this
|
||||
'''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!")
|
||||
ui_print("\nWARNING: Too high value for", section, key, "read from config file:",
|
||||
value, "max.", max_value, "allowed. Excessive values may damage equipment!\n")
|
||||
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!")
|
||||
ui_print("\nWARNING: Too low value for", section, key, "read from config file:",
|
||||
value, "max.", max_value, "allowed. Excessive values may damage equipment!\n")'''
|
||||
return value
|
||||
except KeyError as e:
|
||||
ui_print("Error while reading config file:", e)
|
||||
@@ -201,64 +203,82 @@ def read_config(section, key): # read specific value from config file
|
||||
|
||||
def edit_config(section, key, value, override=False): # edit specific value in config file
|
||||
config_object = ConfigParser() # initialize config parser
|
||||
value_ok = False
|
||||
# Value checking:
|
||||
# ToDo: make pop-up warning messages that can be waived
|
||||
|
||||
# Check if value is within acceptable limits (set in globals.py)
|
||||
try:
|
||||
if section in g.AXIS_NAMES: # only check numerical values
|
||||
value_ok = True
|
||||
if section in g.AXIS_NAMES and not override: # only check numerical values and not if overridden by user
|
||||
value_ok = False
|
||||
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)] # get min value
|
||||
if value > max_value:
|
||||
message = "Attempted to write too high value for {s} {k} to config file:\n" \
|
||||
"{v}, max. {mv} allowed. Excessive values may damage equipment!\n" \
|
||||
"Do you really want to use this value?".format(s=section, k=key, v=value, mv=max_value)
|
||||
message = "Prevented writing too high value for {s} {k} to config file:\n" \
|
||||
"{v}, max. {mv} allowed. Erroneous values may damage equipment!" \
|
||||
.format(s=section, k=key, v=value, mv=max_value)
|
||||
raise ValueError(message)
|
||||
elif value < min_value:
|
||||
message = "Attempted to write too low value for {s} {k} to config file:\n" \
|
||||
"{v}, max. {mv} allowed. Excessive values may damage equipment!\n" \
|
||||
"Do you really want to use this value?".format(s=section, k=key, v=value, mv=min_value)
|
||||
message = "Prevented writing too low value for {s} {k} to config file:\n" \
|
||||
"{v}, max. {mv} allowed. Erroneous values may damage equipment!" \
|
||||
.format(s=section, k=key, v=value, mv=min_value)
|
||||
raise ValueError(message)
|
||||
else:
|
||||
value_ok = True
|
||||
except KeyError as e:
|
||||
ui_print("Error while editing config file:", e)
|
||||
raise KeyError("Could not find section", section, "in config file.")
|
||||
except ValueError as e: # value too high/low
|
||||
value_ok = False
|
||||
# display pop-up message to ask user if he really wants the value
|
||||
answer = messagebox.askquestion("Value out of bounds", e) # becomes 'yes' or 'no' depending on user choice
|
||||
if answer == 'yes': override = True
|
||||
else: override = False
|
||||
else: # no errors
|
||||
value_ok = True
|
||||
if value_ok or override: # value is ok or user has chosen to use it anyway
|
||||
try:
|
||||
|
||||
if value_ok or override:
|
||||
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.")
|
||||
|
||||
except KeyError as e:
|
||||
ui_print("Error while editing config file:", e)
|
||||
raise KeyError("Could not find key", key, "in config file.")
|
||||
|
||||
|
||||
def check_config():
|
||||
ui_print("Checking config file for values exceeding limits:")
|
||||
i = 0
|
||||
concerns = {}
|
||||
problem_counter = 0
|
||||
for axis in g.AXIS_NAMES:
|
||||
concerns[axis] = []
|
||||
for key in g.default_arrays.keys():
|
||||
value = float(read_config(axis, key))
|
||||
max_value = g.default_arrays[key][1][i] # get max value
|
||||
min_value = g.default_arrays[key][2][i] # get min value
|
||||
|
||||
if not min_value <= value <= max_value:
|
||||
concerns[axis].append(key)
|
||||
problem_counter += 1
|
||||
|
||||
if len(concerns[axis]) == 0:
|
||||
concerns[axis].append("No problems detected.")
|
||||
|
||||
ui_print(axis, ":", *concerns[axis])
|
||||
i += 1
|
||||
if problem_counter > 0:
|
||||
messagebox.showwarning("Warning!", "Found values exceeding limits in config file. Check values in "
|
||||
"configuration page to avoid equipment damage!")
|
||||
g.app.show_frame(ui.Configuration)
|
||||
|
||||
|
||||
def create_default_config(file): # create config file from default values (stored in globals.py)
|
||||
config = ConfigParser()
|
||||
config = ConfigParser() # initialize config object
|
||||
|
||||
i = 0
|
||||
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][0][i]))
|
||||
for axis_name in g.AXIS_NAMES: # go through axes
|
||||
config.add_section(axis_name) # add section for this axis
|
||||
for key in g.default_arrays.keys(): # go through dictionary with default values
|
||||
config.set(axis_name, key, str(g.default_arrays[key][0][i])) # set value
|
||||
i += 1
|
||||
|
||||
config.add_section("PORTS")
|
||||
config.add_section("PORTS") # add section for PSU serial ports
|
||||
for key in g.default_ports.keys():
|
||||
config.set("PORTS", key, str(g.default_ports[key]))
|
||||
|
||||
with open(file, 'w') as conf:
|
||||
with open(file, 'w') as conf: # write all we just did to the file
|
||||
config.write(conf)
|
||||
|
||||
|
||||
@@ -274,6 +294,19 @@ def ui_print(*content): # prints text to built in console
|
||||
print(output)
|
||||
|
||||
|
||||
def value_in_limits(axis, key, value): # Check if value is within safe limits (set in globals.py)
|
||||
# ToDo: replace checks everywhere with this
|
||||
max_value = g.default_arrays[key][1][g.AXIS_NAMES.index(axis)] # get max value
|
||||
min_value = g.default_arrays[key][2][g.AXIS_NAMES.index(axis)] # get min value
|
||||
|
||||
if float(value) > float(max_value):
|
||||
return 'HIGH'
|
||||
elif float(value) < float(min_value):
|
||||
return 'LOW'
|
||||
else:
|
||||
return 'OK'
|
||||
|
||||
|
||||
def setup_axes(): # creates device objects for all PSUs and sets their values
|
||||
# Connect to Arduino:
|
||||
try:
|
||||
|
||||
@@ -19,7 +19,9 @@ try: # start normal operations
|
||||
|
||||
g.app = HelmholtzGUI()
|
||||
func.ui_print("Program Initialized")
|
||||
func.ui_print("Starting setup...") # do it again, so it is printed in the UI console
|
||||
func.check_config() # check config file for values exceeding limits
|
||||
|
||||
func.ui_print("\nStarting setup...") # do it again, so it is printed in the UI console ToDo: do it only once
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user