From d74dda0bc53a2718ef3f17a2e52da664d22e96e5 Mon Sep 17 00:00:00 2001 From: DJ2LS Date: Tue, 14 Nov 2023 19:26:11 +0100 Subject: [PATCH] config fixes and adjustments --- modem/config.py | 151 +++++++++++++++++++++++++++------------ modem/data_handler.py | 3 +- modem/modem.py | 2 +- modem/server.py | 9 ++- modem/service_manager.py | 2 +- modem/state_manager.py | 9 ++- 6 files changed, 123 insertions(+), 53 deletions(-) diff --git a/modem/config.py b/modem/config.py index ac494f1c..29dedc43 100644 --- a/modem/config.py +++ b/modem/config.py @@ -17,13 +17,12 @@ class CONFIG: try: self.config_name = configfile - except Exception: self.config_name = "config.ini" - self.log.info("[CFG] logfile init", file=self.config_name) + self.log.info("[CFG] config init", file=self.config_name) - # check if log file exists + # check if config file exists self.config_exists() def config_exists(self): @@ -36,30 +35,110 @@ class CONFIG: self.log.error("[CFG] logfile init error", e=configerror) return False - def write_config(self, section: str, key: str, value): - """ - write values to config - """ - # Validates config data + def validate_network_settings(self, data): + if 'modemport' in data: + if not isinstance(data['modemport'], int): + raise ValueError("'modemport' in 'NETWORK' must be an integer.") + + def validate_station_settings(self, data): + for setting in ['mycall', 'mygrid']: + if setting in data and not data[setting]: + raise ValueError(f"'{setting}' in 'STATION' cannot be empty.") + if 'ssid_list' in data and not isinstance(data['ssid_list'], list): + raise ValueError("'ssid_list' in 'STATION' needs to be a list.") + + def validate_audio_settings(self, data): + for setting in ['input_device', 'output_device']: + if setting in data and not isinstance(data[setting], str): + raise ValueError(f"'{setting}' in 'AUDIO' must be a string.") + for setting in ['rx_audio_level', 'tx_audio_level']: + if setting in data and not isinstance(data[setting], int): + raise ValueError(f"'{setting}' in 'AUDIO' must be an integer.") + + def validate_radio_settings(self, data): + if 'radioport' in data and not (data['radioport'] is None or isinstance(data['radioport'], int)): + raise ValueError("'radioport' in 'RADIO' must be None or an integer.") + + def validate_tci_settings(self, data): + if 'tci_ip' in data and not isinstance(data['tci_ip'], str): + raise ValueError("'tci_ip' in 'TCI' must be a string.") + if 'tci_port' in data and not isinstance(data['tci_port'], int): + raise ValueError("'tci_port' in 'TCI' must be an integer.") + + def validate_modem_settings(self, data): + for setting in ['enable_fft', 'enable_fsk', 'enable_low_bandwidth_mode', 'respond_to_cq', 'enable_scatter']: + if setting in data and not isinstance(data[setting], bool): + raise ValueError(f"'{setting}' in 'MODEM' must be a boolean.") + for setting in ['tuning_range_fmax', 'tuning_range_fmin', 'rx_buffer_size', 'tx_delay']: + if setting in data and not isinstance(data[setting], int): + raise ValueError(f"'{setting}' in 'MODEM' must be an integer.") + + def validate_mesh_settings(self, data): + if 'enable_protocol' in data and not isinstance(data['enable_protocol'], bool): + raise ValueError("'enable_protocol' in 'MESH' must be a boolean.") + def validate(self, data): - for section in data: - for setting in data[section]: - if section == 'NETWORK': - if setting == 'modemport' and int(data[section][setting]) == 0: - raise Exception("'modemport' should be an integer") - if section == 'STATION': - if setting == 'mycall' and len(data[section][setting]) <= 0: - raise Exception("'%s' can't be empty" % setting) - if setting == 'mygrid' and len(data[section][setting]) <= 0: - raise Exception("'%s' can't be empty" % setting) - if setting == 'ssid_list' and not isinstance(data[section][setting], list): - raise Exception("'%s' needs to be a list" % setting) - # TODO finish this for all config settings! - - # Handle special setting data type conversion + for section, settings in data.items(): + if section == 'NETWORK': + self.validate_network_settings(settings) + elif section == 'STATION': + self.validate_station_settings(settings) + elif section == 'AUDIO': + self.validate_audio_settings(settings) + elif section == 'RADIO': + self.validate_radio_settings(settings) + elif section == 'TCI': + self.validate_tci_settings(settings) + elif section == 'MESH': + self.validate_mesh_settings(settings) + elif section == 'MODEM': + self.validate_modem_settings(settings) + else: + self.log.warning("wrong config", section=section) + + # converts values of settings from String to Value. + # For example 'False' (type String) will be converted to False (type Bool) + def convert_types(self, config): + for setting in config: + value = config[setting] + + if isinstance(value, dict): + # If the value is a dictionary, apply the function recursively + config[setting] = self.convert_types(value) + + elif isinstance(value, list): + # If the value is a list, iterate through the list + new_list = [] + for item in value: + # Apply the function to each dictionary item in the list + if isinstance(item, dict): + new_list.append(self.convert_types(item)) + else: + new_list.append(item) + config[setting] = new_list + + elif isinstance(value, str): + # Attempt to convert string values + if value.lstrip('-').isdigit(): + config[setting] = int(value) + else: + try: + # Try converting to a float + float_value = float(value) + # If it's actually an integer (like -50.0), convert it to an integer + config[setting] = int(float_value) if float_value.is_integer() else float_value + except ValueError: + # Convert to boolean if applicable + if value.lower() in ['true', 'false']: + config[setting] = value.lower() == 'true' + + return config + + # 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 + # TODO check if we can include this in function "convert_types" def handle_setting(self, section, setting, value, is_writing = False): if (section == 'STATION' and setting == 'ssid_list'): if (is_writing): @@ -71,7 +150,8 @@ class CONFIG: # Sets and writes config data from a dict containing data settings def write(self, data): - + # convert datatypes + data = self.convert_types(data) # Validate config data before writing self.validate(data) @@ -98,35 +178,18 @@ class CONFIG: """ read config file """ + self.log.info("[CFG] reading...") if not self.config_exists(): return False # at first just copy the config as read from file result = {s:dict(self.config.items(s)) for s in self.config.sections()} + result = self.convert_types(result) # handle the special settings (like 'ssid_list') for section in result: for setting in result[section]: result[section][setting] = self.handle_setting( - section, setting, result[section][setting], False) + section, setting, result[section][setting], False) return result - - def get(self, area, key, default): - """ - read from config and add if not exists - - """ - - for _ in range(2): - try: - parameter = ( - self.config[area][key] in ["True", "true", True] - if default in ["True", "true", True, "False", "false", False] - else self.config[area][key] - ) - except KeyError: - self.config[area][key] = str(default) - - self.log.info("[CFG] reading...", parameter=parameter, key=key) - return parameter diff --git a/modem/data_handler.py b/modem/data_handler.py index 165f1030..3ab349ff 100644 --- a/modem/data_handler.py +++ b/modem/data_handler.py @@ -285,7 +285,6 @@ class DATA: def worker_transmit(self) -> None: """Dispatch incoming UI instructions for transmitting operations""" while True: - print("ja?") data = self.data_queue_transmit.get() print(data) @@ -365,7 +364,7 @@ class DATA: self.log.error( "[Modem] worker_transmit: received invalid command:", data=data ) - print("jaaaa") + def worker_receive(self) -> None: """Queue received data for processing""" while True: diff --git a/modem/modem.py b/modem/modem.py index 32bc6b39..577bf684 100644 --- a/modem/modem.py +++ b/modem/modem.py @@ -63,6 +63,7 @@ class RF: def __init__(self, config, event_queue, fft_queue, service_queue, states) -> None: self.config = config + print(config) self.service_queue = service_queue self.states = states @@ -79,7 +80,6 @@ class RF: self.enable_fft = config['MODEM']['enable_fft'] self.enable_scatter = config['MODEM']['enable_scatter'] self.tx_delay = config['MODEM']['tx_delay'] - self.tuning_range_fmin = config['MODEM']['tuning_range_fmin'] self.tuning_range_fmax = config['MODEM']['tuning_range_fmax'] diff --git a/modem/server.py b/modem/server.py index b73001a3..32de18b8 100644 --- a/modem/server.py +++ b/modem/server.py @@ -40,7 +40,7 @@ set_config() # start modem app.state_queue = queue.Queue() # queue which holds latest states app.modem_events = queue.Queue() # queue which holds latest events -app.modem_fft = queue.Queue() # queue which holds lates fft data +app.modem_fft = queue.Queue() # queue which holds latest fft data app.modem_service = queue.Queue() # start / stop modem service # init state manager @@ -204,8 +204,11 @@ def sock_watchdog(sock, client_list, event_queue): try: sock.receive(timeout=1) except Exception as e: - print(e) - client_list.remove(sock) + print(f"client connection lost: {e}") + try: + client_list.remove(sock) + except Exception as err: + print(f"error removing client from list: {e} | {err}") break return diff --git a/modem/service_manager.py b/modem/service_manager.py index 6bee8796..b8c6f955 100644 --- a/modem/service_manager.py +++ b/modem/service_manager.py @@ -14,7 +14,7 @@ class SM: self.modem = False self.data_handler = False - self.config = app.config_manager.config + self.config = app.config_manager.read() self.modem_events = app.modem_events self.modem_fft = app.modem_fft self.modem_service = app.modem_service diff --git a/modem/state_manager.py b/modem/state_manager.py index 4eb830b1..dd0a069d 100644 --- a/modem/state_manager.py +++ b/modem/state_manager.py @@ -3,7 +3,7 @@ import ujson as json class STATES: def __init__(self, statequeue): self.statequeue = statequeue - + self.newstate = None self.channel_busy = False self.channel_busy_slot = [False, False, False, False, False] self.is_codec2_traffic = False @@ -16,7 +16,12 @@ class STATES: def set(self, key, value): setattr(self, key, value) - self.statequeue.put(self.getAsJSON()) + + # only process data if changed + new_state = self.getAsJSON() + if new_state != self.newstate: + self.statequeue.put(new_state) + self.newstate = new_state def getAsJSON(self): return json.dumps({