# This file contains functions and variables related to reading and writing configuration files. # The configparser module is used for processing. Config files are of type .ini # import packages: from configparser import ConfigParser from tkinter import messagebox # import other project files: import globals as g import cage_func as func # noinspection PyPep8Naming import User_Interface as ui global CONFIG_FILE # string storing the path of the used config file global CONFIG_OBJECT # object of type ConfigParser(), storing all configuration information # CONFIG_OBJECT is what is mostly read/written by the program # CONFIG_FILE is only used to export/import to/from a file def get_config_from_file(file): # read a config file to a config_object 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 a specific value from a 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: # a section or key was used, that does not exist ui.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 a specific value in the CONFIG_OBJECT # section: Section of the config, e.g. "X-Axis" or "PORTS" # key: name of the value in the section, e.g. max_amps # value: new value to be written into the config # override: Bool to allow user to force writing a value into the config, even if it exceeds the safe limit global CONFIG_OBJECT # get the global config object to edit it # ToDo (optional): add check for data types, e.g. int for arduino ports # Check if value to write is within acceptable limits (set in dictionary in globals.py): try: value_ok = 'OK' if section in g.AXIS_NAMES and not override: # only check values in axis sections and not if check overridden value_ok = func.value_in_limits(section, key, value) # check if value is ok, too high or too low if value_ok == 'HIGH': # value is too high max_value = g.default_arrays[key][1][g.AXIS_NAMES.index(section)] # get max value for message printing 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) # return an error with the message attached elif value_ok == 'LOW': # value is too low min_value = g.default_arrays[key][2][g.AXIS_NAMES.index(section)] # get min value for message printing 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) # return an error with the message attached if value_ok == 'OK' or override: # value is within limits or user has overridden the checks try: section_obj = CONFIG_OBJECT[section] # get relevant section in the config except KeyError: # there is no such section ui.ui_print("Could not find section", section, "in config file, creating new.") CONFIG_OBJECT.add_section(section) # create the missing section section_obj = CONFIG_OBJECT[section] # get the object of the section try: section_obj[key] = str(value) # set value for correct entry in the section except KeyError: # there is no entry with this key ui.ui_print("Could not find key", key, "in config file, creating new.") CONFIG_OBJECT.set(section, key, str(value)) # create the entry and set the value except KeyError as e: # key for section or specific value does not exist in the dictionary for max/min values ui.ui_print("Error while editing config file:", e) raise KeyError("Could not find key", key, "in config file.") # return an error def check_config(config_object): # check all numeric values in the config and see if they are within safe limits ui.ui_print("Checking config file for values exceeding limits:") concerns = {} # initialize dictionary for found problems problem_counter = 0 # count the number of values that exceed limits i = 0 for axis in g.AXIS_NAMES: # go through all 3 axes 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: # no problems were found for this axis concerns[axis].append("No problems detected.") ui.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 %i value(s) exceeding limits in config file. Check values " "to ensure correct operation and avoid equipment damage!" % problem_counter) g.app.show_frame(ui.Configuration) # open configuration window so user can check values def reset_config_to_default(): # reset values in config object to defaults (set in globals.py) config = ConfigParser() # reinitialize empty config object global CONFIG_OBJECT # get the global config object CONFIG_OBJECT = config # reset it to the empty object 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 values i += 1 config.add_section("PORTS") # add section for PSU serial ports for key in g.default_ports.keys(): # go through dictionary of default serial ports config.set("PORTS", key, str(g.default_ports[key])) # set the value for each axis