FreeDATA/modem/config.py

225 lines
7.4 KiB
Python
Raw Permalink Normal View History

2022-09-20 09:34:28 +00:00
import configparser
import structlog
import json
2022-09-20 09:34:28 +00:00
class CONFIG:
"""
CONFIG class for handling with config files
"""
config_types = {
'NETWORK': {
2024-03-04 14:50:08 +00:00
'modemaddress': str,
'modemport': int,
},
'STATION': {
'mycall': str,
'mygrid': str,
2023-11-18 11:57:36 +00:00
'myssid': int,
'ssid_list': list,
'enable_explorer': bool,
'enable_stats': bool,
},
'AUDIO': {
'input_device': str,
'output_device': str,
'rx_audio_level': int,
'tx_audio_level': int,
},
'RADIO': {
2023-11-18 11:57:36 +00:00
'control': str,
'serial_port': str,
'model_id': int,
'serial_speed': int,
'data_bits': int,
'stop_bits': int,
'serial_handshake': str,
'ptt_port': str,
'ptt_type': str,
'serial_dcd': str,
'serial_dtr': str,
},
'RIGCTLD': {
'ip': str,
'port': int,
'path': str,
'command': str,
'arguments': str,
},
'TCI': {
'tci_ip': str,
'tci_port': int,
},
'MESH': {
'enable_protocol': bool,
},
'MODEM': {
'enable_hmac': bool,
'enable_morse_identifier': bool,
'maximum_bandwidth': int,
'respond_to_cq': bool,
2024-03-25 18:29:30 +00:00
'tx_delay': int,
'enable_socket_interface': bool,
},
2024-03-16 09:29:13 +00:00
'SOCKET_INTERFACE': {
'enable' : bool,
'host' : str,
'cmd_port' : int,
'data_port' : int,
},
2024-02-18 20:08:14 +00:00
'MESSAGES': {
'enable_auto_repeat': bool,
}
}
2024-02-17 19:45:51 +00:00
default_values = {
list: '[]',
bool: 'False',
int: '0',
str: '',
}
2022-12-10 12:34:26 +00:00
def __init__(self, configfile: str):
2022-09-20 09:34:28 +00:00
# set up logger
2024-02-17 19:42:07 +00:00
self.log = structlog.get_logger(type(self).__name__)
2022-09-20 09:34:28 +00:00
# init configparser
self.parser = configparser.ConfigParser(inline_comment_prefixes="#", allow_no_value=True)
2022-12-10 12:34:26 +00:00
try:
self.config_name = configfile
except Exception:
self.config_name = "config.ini"
2022-09-20 09:34:28 +00:00
2023-11-14 18:26:11 +00:00
self.log.info("[CFG] config init", file=self.config_name)
2022-09-20 09:34:28 +00:00
2023-11-14 18:26:11 +00:00
# check if config file exists
2022-09-20 09:34:28 +00:00
self.config_exists()
2024-02-17 19:42:07 +00:00
# validate config structure
self.validate_config()
2022-09-20 09:34:28 +00:00
def config_exists(self):
"""
check if config file exists
"""
try:
return bool(self.parser.read(self.config_name, None))
2022-09-20 09:34:28 +00:00
except Exception as configerror:
self.log.error("[CFG] logfile init error", e=configerror)
return False
# Validates config data
2024-02-17 19:42:07 +00:00
def validate_data(self, data):
for section in data:
for setting in data[section]:
if not isinstance(data[section][setting], self.config_types[section][setting]):
2023-11-15 16:06:51 +00:00
message = (f"{section}.{setting} must be {self.config_types[section][setting]}."
f" '{data[section][setting]}' {type(data[section][setting])} given.")
raise ValueError(message)
2023-11-14 18:26:11 +00:00
2024-02-17 19:42:07 +00:00
def validate_config(self):
"""
Updates the configuration file to match exactly what is defined in self.config_types.
It removes sections and settings not defined there and adds missing sections and settings.
"""
existing_sections = self.parser.sections()
# Remove sections and settings not defined in self.config_types
for section in existing_sections:
if section not in self.config_types:
self.parser.remove_section(section)
self.log.info(f"[CFG] Removing undefined section: {section}")
continue
existing_settings = self.parser.options(section)
for setting in existing_settings:
if setting not in self.config_types[section]:
self.parser.remove_option(section, setting)
self.log.info(f"[CFG] Removing undefined setting: {section}.{setting}")
# Add missing sections and settings from self.config_types
for section, settings in self.config_types.items():
if section not in existing_sections:
self.parser.add_section(section)
self.log.info(f"[CFG] Adding missing section: {section}")
for setting, value_type in settings.items():
if not self.parser.has_option(section, setting):
2024-02-17 19:45:51 +00:00
default_value = self.default_values.get(value_type, None)
2024-02-17 19:42:07 +00:00
self.parser.set(section, setting, str(default_value))
self.log.info(f"[CFG] Adding missing setting: {section}.{setting}")
2024-02-18 19:31:01 +00:00
return self.write_to_file()
2024-02-17 19:42:07 +00:00
2023-11-14 18:26:11 +00:00
# Handle special setting data type conversion
# is_writing means data from a dict being writen to the config file
# if False, it means the opposite direction
def handle_setting(self, section, setting, value, is_writing = False):
2024-01-05 14:10:46 +00:00
try:
if self.config_types[section][setting] == list:
if (is_writing):
return json.dumps(value)
else:
return json.loads(value)
2024-01-05 14:10:46 +00:00
elif self.config_types[section][setting] == bool and not is_writing:
return self.parser.getboolean(section, setting)
2024-01-05 14:10:46 +00:00
elif self.config_types[section][setting] == int and not is_writing:
return self.parser.getint(section, setting)
else:
return value
except KeyError as key:
self.log.error("[CFG] key error in logfile, please check 'config.ini.example' for help", key=key)
# Sets and writes config data from a dict containing data settings
def write(self, data):
# Validate config data before writing
2024-02-17 19:42:07 +00:00
self.validate_data(data)
for section in data:
# init section if it doesn't exist yet
if not section.upper() in self.parser.keys():
self.parser[section] = {}
for setting in data[section]:
2023-11-06 19:52:33 +00:00
new_value = self.handle_setting(
section, setting, data[section][setting], True)
2024-02-18 19:31:01 +00:00
try:
self.parser[section][setting] = str(new_value)
except Exception as e:
self.log.error("[CFG] error setting config key", e=e)
return self.write_to_file()
2024-02-17 19:42:07 +00:00
def write_to_file(self):
# Write config data to file
try:
with open(self.config_name, 'w') as configfile:
self.parser.write(configfile)
return self.read()
except Exception as conferror:
self.log.error("[CFG] reading logfile", e=conferror)
return False
2023-11-06 14:36:11 +00:00
def read(self):
2022-09-20 09:34:28 +00:00
"""
read config file
"""
2024-02-11 07:48:49 +00:00
#self.log.info("[CFG] reading...")
2023-11-06 14:36:11 +00:00
if not self.config_exists():
return False
# at first just copy the config as read from file
result = {s:dict(self.parser.items(s)) for s in self.parser.sections()}
# handle the special settings
for section in result:
for setting in result[section]:
result[section][setting] = self.handle_setting(
2023-11-14 18:26:11 +00:00
section, setting, result[section][setting], False)
2022-09-20 09:34:28 +00:00
return result