moved config handling to separate file

This commit is contained in:
Martin Zietz
2021-02-07 14:54:12 +01:00
parent 47b4e447b0
commit 08d4ca463d
5 changed files with 169 additions and 160 deletions
+28 -26
View File
@@ -10,6 +10,7 @@ from os.path import exists
import globals as g
import cage_func as func
import csv_threading as csv
import config_handling as config
NORM_FONT = ()
HEADER_FONT = ("Arial", 13, "bold")
@@ -328,7 +329,7 @@ class Configuration(Frame):
"Field to be compensated", "ambient_field", 1e6],
"Resistances:": [[DoubleVar() for _ in range(3)], "\u03A9",
"Resistance of coils + equipment", "resistance", 1],
"Max. Power:": [[DoubleVar() for _ in range(3)], "W", "Max. allowed power", "max_watts", 1],
"Max. Current:": [[DoubleVar() for _ in range(3)], "A", "Max. allowed current", "max_amps", 1],
"Max. Voltage:": [[DoubleVar() for _ in range(3)], "V",
"Max. allowed voltage, must not exceed 16V!", "max_volts", 1],
"Arduino Pins:": [[IntVar() for _ in range(3)], "-", "Should be 15, 16, 17", "relay_pin", 1]
@@ -387,7 +388,7 @@ class Configuration(Frame):
self.update_fields()
def restore_defaults(self): # restore all default settings
func.reset_config_to_default(g.CONFIG_FILE) # overwrite config file with default
config.reset_config_to_default(config.CONFIG_FILE) # overwrite config file with default
func.setup_all() # setup everything with the defaults
self.update_fields() # update fields in config window
@@ -398,8 +399,8 @@ class Configuration(Frame):
for key in self.entries.keys():
for i in [0, 1, 2]:
value = func.read_from_config(g.AXIS_NAMES[i], self.entries[key][3],
g.CONFIG_OBJECT) # get value from config file
value = config.read_from_config(g.AXIS_NAMES[i], self.entries[key][3],
config.CONFIG_OBJECT) # get value from config file
self.entries[key][0][i].set(value) # set initial value on variable
type_value = self.entries[key][0][i].get() # get value with correct data type
factor = self.entries[key][4] # get unit conversion factor
@@ -415,8 +416,8 @@ class Configuration(Frame):
def write_values(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())
config.edit_config("PORTS", "xy_port", self.XY_port.get())
config.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
@@ -438,7 +439,7 @@ class Configuration(Frame):
axis = g.AXIS_NAMES[i] # get axis name for error messages
if value_ok == 'OK':
func.edit_config(g.AXIS_NAMES[i], config_key, value) # write new value to config file
config.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
@@ -462,7 +463,7 @@ class Configuration(Frame):
# 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)
config.edit_config(g.AXIS_NAMES[i], config_key, value, True)
# if user chooses 'no' nothing happens, old value is kept
def implement(self): # executed on button press
@@ -471,14 +472,15 @@ class Configuration(Frame):
self.update_fields() # update entry fields to show new values
def load_config(self): # load configuration from some config file
directory = os.path.dirname(os.path.abspath(g.CONFIG_FILE)) # get directory of current config file
directory = os.path.dirname(os.path.abspath(config.CONFIG_FILE)) # get directory of current config file
# open file selection dialogue and save path of selected file
filename = filedialog.askopenfilename(initialdir=directory, title="Select Config File",
filetypes=(("Config File", "*.ini*"), ("All Files", "*.*")))
if exists(filename): # does the file exist?
g.CONFIG_FILE = filename # set global config file to the new file
g.CONFIG_OBJECT = func.get_config_from_file(filename) # load values from config file to config object
func.check_config(g.CONFIG_OBJECT) # check the values and display warnings if values are out of bounds
config.CONFIG_FILE = filename # set global config file to the new file
config.CONFIG_OBJECT = config.get_config_from_file(filename) # load from config file to config object
config.check_config(
config.CONFIG_OBJECT) # check the values and display warnings if values are out of bounds
func.setup_all() # 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
@@ -487,24 +489,23 @@ class Configuration(Frame):
func.ui_print("Selected file", filename, "does not exist, could not load config.")
def save_config_as(self): # save current configuration to a new config file
directory = os.path.dirname(os.path.abspath(g.CONFIG_FILE)) # get directory of current config file
directory = os.path.dirname(os.path.abspath(config.CONFIG_FILE)) # get directory of current config file
# open file selection dialogue and save path of selected file
filename = filedialog.asksaveasfilename(initialdir=directory, title="Save config to file",
filetypes=([("Config File", "*.ini*")]),
defaultextension=[("Config File", "*.ini*")])
if exists(filename): # does the file exist?
g.CONFIG_FILE = filename # set global config file to the new file
self.write_values() # write current entry field values to the config object
func.write_config_to_file(g.CONFIG_OBJECT) # write contents of config object to file
self.update_fields() # update entry fields to show values as they are in the config
elif filename == '': # this happens when file selection window is closed without selecting a file
if filename == '': # this happens when file selection window is closed without selecting a file
func.ui_print("No file selected, could not save config.")
else:
func.ui_print("Selected file", filename, "does not exist, could not save config.")
else: # a file name was entered
config.CONFIG_FILE = filename # set global config file to the new 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
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()
func.write_config_to_file(g.CONFIG_OBJECT)
config.write_config_to_file(config.CONFIG_OBJECT)
self.update_fields()
@@ -541,18 +542,19 @@ class ExecuteCSVMode(Frame):
# Create and place buttons
self.select_file_button = Button(self.file_select_frame, text="Select csv file...", command=self.load_csv,
pady=5, padx=5, font=SMALL_BUTTON_FONT)
pady=5, padx=5, font=SMALL_BUTTON_FONT)
self.select_file_button.grid(row=0, column=0, padx=5)
self.execute_button = Button(self.file_select_frame, text="Run Sequence", command=self.run_sequence,
pady=5, padx=5, font=SMALL_BUTTON_FONT, state="disabled")
pady=5, padx=5, font=SMALL_BUTTON_FONT, state="disabled")
self.execute_button.grid(row=0, column=1, padx=5)
self.stop_button = Button(self.file_select_frame, text="Stop Run", command=self.stop_run,
pady=5, padx=5, font=SMALL_BUTTON_FONT, state="disabled")
pady=5, padx=5, font=SMALL_BUTTON_FONT, state="disabled")
self.stop_button.grid(row=0, column=2, padx=5)
row_counter += 1
def page_switch(self): # every class in the UI needs this, even if it doesn't do anything
def page_switch(self): # function that is called when switching to this window
# every class in the UI needs this, even if it doesn't do anything
pass
def load_csv(self): # load in csv file to be executed
+15 -123
View File
@@ -1,16 +1,13 @@
from pyps2000b import PS2000B
from Arduino import Arduino
import globals as g
import pandas
import time
import numpy as np
import serial
import traceback
# noinspection PyPep8Naming
import User_Interface as ui
from tkinter import *
from tkinter import messagebox
from configparser import ConfigParser
from pyps2000b import PS2000B
from Arduino import Arduino
# noinspection PyPep8Naming
import config_handling as config
import globals as g
class Axis:
@@ -24,13 +21,13 @@ class Axis:
self.name = g.AXIS_NAMES[index]
self.port = g.PORTS[index]
self.resistance = float(read_from_config(self.name, "resistance", g.CONFIG_OBJECT))
self.max_watts = float(read_from_config(self.name, "max_watts", g.CONFIG_OBJECT))
self.max_amps = np.sqrt(self.max_watts / self.resistance)
self.max_volts = float(read_from_config(self.name, "max_volts", g.CONFIG_OBJECT))
self.resistance = float(config.read_from_config(self.name, "resistance", config.CONFIG_OBJECT))
self.max_watts = float(config.read_from_config(self.name, "max_watts", config.CONFIG_OBJECT))
self.max_amps = float(config.read_from_config(self.name, "max_amps", config.CONFIG_OBJECT))
self.max_volts = float(config.read_from_config(self.name, "max_volts", config.CONFIG_OBJECT))
self.coil_constant = float(read_from_config(self.name, "coil_const", g.CONFIG_OBJECT))
self.ambient_field = float(read_from_config(self.name, "ambient_field", g.CONFIG_OBJECT))
self.coil_constant = float(config.read_from_config(self.name, "coil_const", config.CONFIG_OBJECT))
self.ambient_field = float(config.read_from_config(self.name, "ambient_field", config.CONFIG_OBJECT))
max_field = self.max_amps * self.coil_constant # max field reachable in this axis
self.max_field = np.array([-max_field, max_field])
@@ -145,7 +142,7 @@ class ArduinoCtrl(Arduino):
self.connected = "Unknown"
self.pins = [0, 0, 0]
for i in range(3): # get correct pins from config file
self.pins[i] = int(read_from_config(g.AXIS_NAMES[i], "relay_pin", g.CONFIG_OBJECT))
self.pins[i] = int(config.read_from_config(g.AXIS_NAMES[i], "relay_pin", config.CONFIG_OBJECT))
ui_print("\nConnecting to Arduino...")
try:
Arduino.__init__(self) # search for connected arduino and connect
@@ -180,111 +177,6 @@ class ArduinoCtrl(Arduino):
self.digitalWrite(pin, "LOW")
def get_config_from_file(file=g.CONFIG_FILE):
config_object = ConfigParser() # initialize config parser
config_object.read(file) # open config file
return config_object # return config object, that contains all info from the file
def write_config_to_file(config_object): # write contents of config object to a config file
with open(g.CONFIG_FILE, 'w') as conf: # Write changes to config file
config_object.write(conf)
def read_from_config(section, key, config_object): # read specific value from config object
try:
section_obj = config_object[section] # get relevant section
value = section_obj[key] # get relevant value in the section
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, override=False): # edit specific value in config file
config_object = g.CONFIG_OBJECT
# ToDo: add check for data types, e.g. int for arduino ports
# Check if value to write is within acceptable limits (set in globals.py)
try:
value_ok = 'OK'
if section in g.AXIS_NAMES and not override: # only check numerical values and not if overridden by user
value_ok = value_in_limits(section, key, value) # check if value is ok, too high or too low
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_ok == 'HIGH':
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_ok == 'LOW':
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)
if value_ok == 'OK' or override: # value is within limits or user has overridden
try:
section_obj = config_object[section] # get relevant section
except KeyError:
ui_print("Could not find section", section, "in config file, creating new.")
config_object.add_section(section)
section_obj = config_object[section]
try:
section_obj[key] = str(value) # set relevant value in the section
except KeyError:
ui_print("Could not find key", key, "in config file, creating new.")
config_object.set(section, key, str(value))
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(config_object): # check all numeric values in the config and see if they are within safe limits
ui_print("Checking config file for values exceeding limits:")
i = 0
concerns = {} # initialize dictionary for found problems
problem_counter = 0
for axis in g.AXIS_NAMES:
concerns[axis] = [] # create dictionary entry for this axis
for key in g.default_arrays.keys(): # go over entries in this axis
value = float(read_from_config(axis, key, config_object)) # read value to check from config file
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: # value is not in safe limits
concerns[axis].append(key) # add this entry to the problem dictionary
problem_counter += 1
if len(concerns[axis]) == 0:
concerns[axis].append("No problems detected.")
ui_print(axis, ":", *concerns[axis]) # print out results for this axis
i += 1
if problem_counter > 0: # some values are not ok
# shop pup-up warning message:
messagebox.showwarning("Warning!", "Found values exceeding limits in config file. Check values "
"to ensure correct operation and avoid equipment damage!")
g.app.show_frame(ui.Configuration) # open configuration window so user can check values
def reset_config_to_default(file): # reset values in config object to defaults (stored in globals.py)
config = ConfigParser() # initialize global config object
g.CONFIG_OBJECT = config
i = 0
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") # add section for PSU serial ports
for key in g.default_ports.keys():
config.set("PORTS", key, str(g.default_ports[key]))
def ui_print(*content): # prints text to built in console
output = ""
for text in content:
@@ -331,8 +223,8 @@ def setup_all(): # main initialization function, creates device objects for all
g.AXES = []
g.XY_PORT = read_from_config("PORTS", "xy_port", g.CONFIG_OBJECT)
g.Z_PORT = read_from_config("PORTS", "z_port", g.CONFIG_OBJECT)
g.XY_PORT = config.read_from_config("PORTS", "xy_port", config.CONFIG_OBJECT)
g.Z_PORT = config.read_from_config("PORTS", "z_port", config.CONFIG_OBJECT)
g.PORTS = [g.XY_PORT, g.XY_PORT, g.Z_PORT]
ui_print("\nConnecting to XY Device on %s..." % g.XY_PORT)
+115
View File
@@ -0,0 +1,115 @@
from configparser import ConfigParser
from tkinter import messagebox
import globals as g
import cage_func as func
# noinspection PyPep8Naming
import User_Interface as ui
global CONFIG_FILE
global CONFIG_OBJECT
def get_config_from_file(file):
config_object = ConfigParser() # initialize config parser
config_object.read(file) # open config file
return config_object # return config object, that contains all info from the file
def write_config_to_file(config_object): # write contents of config object to a config file
with open(CONFIG_FILE, 'w') as conf: # Write changes to config file
config_object.write(conf)
def read_from_config(section, key, config_object): # read specific value from config object
try:
section_obj = config_object[section] # get relevant section
value = section_obj[key] # get relevant value in the section
return value
except KeyError as e:
func.ui_print("Error while reading config file:", e)
raise KeyError("Could not find key", key, "in config file.")
def edit_config(section, key, value, override=False): # edit specific value in config file
config_object = CONFIG_OBJECT
# ToDo: add check for data types, e.g. int for arduino ports
# Check if value to write is within acceptable limits (set in globals.py)
try:
value_ok = 'OK'
if section in g.AXIS_NAMES and not override: # only check numerical values and not if overridden by user
value_ok = func.value_in_limits(section, key, value) # check if value is ok, too high or too low
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_ok == 'HIGH':
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_ok == 'LOW':
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)
if value_ok == 'OK' or override: # value is within limits or user has overridden
try:
section_obj = config_object[section] # get relevant section
except KeyError:
func.ui_print("Could not find section", section, "in config file, creating new.")
config_object.add_section(section)
section_obj = config_object[section]
try:
section_obj[key] = str(value) # set relevant value in the section
except KeyError:
func.ui_print("Could not find key", key, "in config file, creating new.")
config_object.set(section, key, str(value))
except KeyError as e:
func.ui_print("Error while editing config file:", e)
raise KeyError("Could not find key", key, "in config file.")
def check_config(config_object): # check all numeric values in the config and see if they are within safe limits
func.ui_print("Checking config file for values exceeding limits:")
i = 0
concerns = {} # initialize dictionary for found problems
problem_counter = 0
for axis in g.AXIS_NAMES:
concerns[axis] = [] # create dictionary entry for this axis
for key in g.default_arrays.keys(): # go over entries in this axis
value = float(read_from_config(axis, key, config_object)) # read value to check from config file
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: # value is not in safe limits
concerns[axis].append(key) # add this entry to the problem dictionary
problem_counter += 1
if len(concerns[axis]) == 0:
concerns[axis].append("No problems detected.")
func.ui_print(axis, ":", *concerns[axis]) # print out results for this axis
i += 1
if problem_counter > 0: # some values are not ok
# shop pup-up warning message:
messagebox.showwarning("Warning!", "Found values exceeding limits in config file. Check values "
"to ensure correct operation and avoid equipment damage!")
g.app.show_frame(ui.Configuration) # open configuration window so user can check values
def reset_config_to_default(file): # reset values in config object to defaults (stored in globals.py)
config = ConfigParser() # initialize global config object
config.CONFIG_OBJECT = config
i = 0
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") # add section for PSU serial ports
for key in g.default_ports.keys():
config.set("PORTS", key, str(g.default_ports[key]))
+1 -3
View File
@@ -15,9 +15,6 @@ app = None
AXIS_NAMES = ["X-Axis", "Y-Axis", "Z-Axis"]
CONFIG_FILE = None
CONFIG_OBJECT = None
global XY_PORT
global Z_PORT
@@ -37,6 +34,7 @@ default_arrays = {
"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]
"max_amps": np.array([[4.5, 4.5, 4.5], [6, 6, 6], [0, 0, 0]], dtype=float), # max. allowed current (A)
"relay_pin": [[15, 16, 17], [15, 16, 17], [15, 16, 17]] # pins on the arduino for reversing [x,y,z] polarity
}
default_ports = {
+10 -8
View File
@@ -1,20 +1,22 @@
from os.path import exists
from User_Interface import HelmholtzGUI
import cage_func as func
import traceback
import globals as g
from os.path import exists
import config_handling as config
try: # start normal operations
g.CONFIG_FILE = 'config.ini'
config.CONFIG_FILE = 'config.ini'
# ToDo: remember what the last config file was
if not exists(g.CONFIG_FILE):
if not exists(config.CONFIG_FILE):
print("Config file not found, creating new from defaults.")
func.reset_config_to_default(g.CONFIG_FILE)
func.write_config_to_file(g.CONFIG_OBJECT)
config.reset_config_to_default(config.CONFIG_FILE)
config.write_config_to_file(config.CONFIG_OBJECT)
g.CONFIG_OBJECT = func.get_config_from_file(g.CONFIG_FILE)
print(g.CONFIG_OBJECT)
config.CONFIG_OBJECT = config.get_config_from_file(config.CONFIG_FILE)
print("Starting setup...")
func.setup_all() # initiate communication, set handles
@@ -23,7 +25,7 @@ try: # start normal operations
g.app = HelmholtzGUI()
func.ui_print("Program Initialized")
func.check_config(g.CONFIG_OBJECT) # check config for values exceeding limits
config.check_config(config.CONFIG_OBJECT) # check config 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_all() # initiate communication, set handles