Add config file managing class

This commit is contained in:
CPD 2025-02-13 12:16:03 +01:00
parent 154235dbe5
commit 2631898c35
2 changed files with 79 additions and 34 deletions

View File

@ -53,26 +53,31 @@ from .led_script import LedScript
from .measurement import measure as _measure
from .utility.data import DataCollector
from .utility.data import plot_cpd_data as data_plot
from .utility.config_file import ConfigFile
from .utility import file_io
from .update_funcs import _Monitor, _update_print
config_path = path.expanduser("~/.config/cpdctrl.json")
# CONFIGURATION
_runtime_vars = {
"last-measurement": ""
}
# defaults, these may be overridden by a config file
settings = {
"datadir": path.expanduser("~/data"),
"name": "interactive-test",
"led": "unkown",
"led": "unknown",
"interval": 0.5,
"flush_after": 3000,
"use_buffer": False,
}
cfilename: str = "cpdctrl.yaml"
config_path: str = ""
config_file: ConfigFile = ConfigFile("")
test = False
# DEVICES
# global variable for the instrument/client returned by pyvisa/bleak
dev: VoltageMeasurementDevice|None = None
led: LedControlDevice|None = None
@ -81,7 +86,7 @@ t0 = 0
data = None
md = None
def monitor(script: str|int=0, interval: float|None=None, metadata:dict={}, flush_after: int|None=None, use_buffer: bool|None=None, max_measurements=None, max_points_shown=None):
def monitor(script: str|int=0, interval: float|None=None, metadata:dict={}, flush_after: int|None=None, use_buffer: bool|None=None, max_measurements=None, stop_on_script_end: bool=False, max_points_shown=None):
"""
Monitor the voltage with matplotlib.
- Opens a matplotlib window and takes measurements depending on settings["interval"]
@ -96,7 +101,7 @@ def monitor(script: str|int=0, interval: float|None=None, metadata:dict={}, flus
Parameters
----------
script : str|int
Path to a led script file or a constant value between 0 and 100 for the LED.
Path to a led script file, or a constant value between 0 and 100 for the LED.
interval : float|None
Time between measurements.
If None, the value is taken from the settings.
@ -106,12 +111,15 @@ def monitor(script: str|int=0, interval: float|None=None, metadata:dict={}, flus
flush_after : int|None
Flush the data to disk after <flush_after> readings
If None, the value is taken from the settings.
use_buffer : Bool
use_buffer : bool
If True, use the voltage measurement device's internal buffer for readings, which leads to more accurate timings.
If None, the value is taken from the settings.
max_points_shown : int|None
how many points should be shown at once. None means infinite
max_measurements : maximum number of measurements. None means infinite
max_measurements : int|None
maximum number of measurements. None means infinite
stop_on_script_end : bool, optional
Stop measurement when the script end is reached
"""
global _runtime_vars, data_collector, dev, led
global data, md
@ -141,6 +149,7 @@ def monitor(script: str|int=0, interval: float|None=None, metadata:dict={}, flus
flush_after,
use_buffer,
max_measurements,
stop_on_script_end,
False, # verbose
command_queue,
data_queue
@ -158,6 +167,7 @@ def monitor(script: str|int=0, interval: float|None=None, metadata:dict={}, flus
pass
command_queue.put("stop")
proc_measure.join()
print("Measurement stopped" + " "*50)
led_script.stop_updating() # stop watching for file updates (if enabled)
data_collector.save_csv(verbose=True)
data, metadata = data_collector.get_data()
@ -167,7 +177,6 @@ def monitor(script: str|int=0, interval: float|None=None, metadata:dict={}, flus
fig.savefig(fig_path)
# DATA
def data_load(dirname:str) -> tuple[np.ndarray, dict]:
"""
@ -186,8 +195,6 @@ def data_load(dirname:str) -> tuple[np.ndarray, dict]:
dirpath = path.join(settings["datadir"], dirname)
data, md = DataCollector.load_data(dirpath, verbose=True)
# data_plot imported
# SETTINGS
def set(setting, value):
global settings, config_path
@ -196,19 +203,20 @@ def set(setting, value):
print(f"set: setting '{setting}' currently holds a value of type '{type(settings[setting])}'")
return
settings[setting] = value
config_file.set(setting, value)
def name(s:str):
global settings
settings["name"] = s
def save_settings():
with open(config_path, "w") as file:
json.dump(settings, file, indent=4)
global settings
config_file.set_values(settings)
config_file.save()
def load_settings():
global settings, config_path
with open(config_path, "r") as file:
settings = json.load(file)
settings = config_file.get_values()
settings["datadir"] = path.expanduser(settings["datadir"]) # replace ~
def help(topic=None):
@ -226,23 +234,29 @@ Available topics:
Run 'help("topic")' to see more information on a topic""")
elif topic in [settings, "settings"]:
print("""Settings:
print(f"""Settings:
name: str - name of the measurement, determines filename
led: str - name/model of the LED that is being used
datadir: str - output directory for the csv files
interval: int - interval (inverse frequency) of the measurements, in seconds
beep: bool - wether the device should beep or not
beep: bool - whether the device should beep or not
Functions:
name("<name>") - short for set("name", "<name>")
set("setting", value) - set a setting to a value
save_settings() - store the settings as "cpdctrl.json" in the working directory
load_settings() - load settings from a file
The global variable 'config_path' determines the path used by save/load_settings. Use -c '<path>' to set another path.
The serach path is:
<working-dir>/cpdctrl.json
$XDG_CONFIG_HOME/cpdctrl.json
~/.config/cpdctrl.json
Upon startup, settings are loaded from the config file.
The global variable 'config_path' determines the path used by save/load_settings. Use -c '<path>' to set another path.
The search path is:
<working-dir>/{cfilename}
$XDG_CONFIG_HOME/{cfilename}
~/.config/cpdctrl/{cfilename}
The current file path is:
{config_path}
""")
elif topic == "imports":
print("""Imports:
@ -255,7 +269,7 @@ Functions:
def init():
global dev, led, settings, config_path
global dev, led, settings, config_path, config_file
print(r""" .___ __ .__
____ ______ __| _/_____/ |________| |
_/ ___\\____ \ / __ |/ ___\ __\_ __ \ |
@ -266,21 +280,19 @@ Interactive Shell for CPD measurements with Keithley 2700B
---
Enter 'help()' for a list of commands""")
from os import environ
if path.isfile("cpdctrl.json"):
config_path = "cpdctrl.json"
if path.isfile(cfilename):
config_path = cfilename
elif 'XDG_CONFIG_HOME' in environ.keys():
# and path.isfile(environ["XDG_CONFIG_HOME"] + "/cpdctrl.json"):
config_path = environ["XDG_CONFIG_HOME"] + "/cpdctrl.json"
config_path = path.join(environ["XDG_CONFIG_HOME"], "cpdctrl", cfilename)
else:
config_path = path.expanduser("~/.config/cpdctrl.json")
config_path = path.join(path.expanduser("~/.config/cpdctrl"), cfilename)
if args["config"]:
config_path = args["config"]
if not path.isdir(path.dirname(config_path)):
makedirs(path.dirname(config_path))
if path.isfile(config_path):
load_settings()
config_file = ConfigFile(config_path, init_values=settings)
load_settings()
if not path.isdir(settings["datadir"]):
makedirs(settings["datadir"])

View File

@ -0,0 +1,33 @@
from os import environ, makedirs, path
import yaml
class ConfigFile:
def __init__(self, filepath: str, init_values = None):
self.values = {}
if init_values:
self.values = init_values
self.filepath = filepath
if path.isfile(self.filepath):
with open(self.filepath, "r") as file:
self.values |= yaml.safe_load(file)
def save(self):
if not self.filepath: return
directory = path.dirname(self.filepath)
if not path.isdir(directory):
makedirs(directory)
with open(self.filepath, "w") as file:
yaml.dump(self.values, file)
def get(self, name: str, default=None):
if name in self.values: return self.values[name]
return default
def set(self, name: str, value):
self.values[name] = value
def get_values(self):
return self.values.copy()
def set_values(self, values):
self.values = values