def go(self) -> tuple: mapping = { 'device\\..*': self._device, 'group\\.(input|output|connection)': self._device, } if self.path_main == PATH_CORE: check = f'{self.path_type}.{self.path_subtype}' matched = False for match, goto in mapping.items(): if regex_match(match, check) is not None: matched = True goto() break if matched: return self.result else: log('No route matched!', level=5) else: log(f"Wrong api root-path supplied => should be '{PATH_CORE}'", level=4) return self.status
def get(self) -> dict: # { # object_condition_match: [instance_list], # object_condition_specialmatch: [instance_list], # object_condition_link: [instance_list], # group_condition: [instance_list] # } log(f'Building condition objects (all)', level=8) output = {} _config = { config.KEY_OBJECT_CONDITION_MATCH: ConditionMatch, config.KEY_OBJECT_CONDITION_MATCH_SPECIAL: ConditionMatchSpecial, config.KEY_OBJECT_CONDITION_LINK: ConditionLink, config.KEY_GROUP_CONDITION: ConditionGroup, } for key, factory in _config.items(): output[key] = factory( blueprint=blueprint_dict[key], supply_data=self.supply_data[key], ).get() return output
def get(config_dict: dict, system_tasks: bool = True) -> list: if system_tasks: timer_list = get_tasks() else: timer_list = [] for category, obj_data in config_dict.items(): for obj in obj_data: if isinstance(obj, TIMER_OBJECTS): if obj.enabled == 1: if isinstance(obj, GaInputDevice): if obj.timer is not None and obj.timer != obj.parent_instance.timer: timer_list.append(obj) elif isinstance(obj, (GaInputModel, GaConditionGroup)): timer_list.append(obj) else: log(f"Is disabled: \"{obj}\"", level=6) else: log(f"Is not allowed: \"{obj}\"", level=7) return timer_list
def get(self) -> list: # { # [instance_list] # } area_group_list = [] log(f'Building area objects', level=8) for data_dict in self.supply_data.values(): instance = self.blueprint( connection_group_list=data_dict[self.key_member][ config.SUPPLY_AR_KEY_MEMBER_CG], connection_obj_list=data_dict[self.key_member][ config.SUPPLY_AR_KEY_MEMBER_CO], input_group_list=data_dict[self.key_member][ config.SUPPLY_AR_KEY_MEMBER_IG], input_obj_list=data_dict[self.key_member][ config.SUPPLY_AR_KEY_MEMBER_IO], output_group_list=data_dict[self.key_member][ config.SUPPLY_AR_KEY_MEMBER_OG], output_obj_list=data_dict[self.key_member][ config.SUPPLY_AR_KEY_MEMBER_OO], nested_list=data_dict[self.key_member][ config.SUPPLY_GENERIC_KEY_MEMBER_NESTED], object_id=data_dict[config.DB_ALL_KEY_ID], name=data_dict[config.DB_ALL_KEY_NAME], description=data_dict[config.DB_ALL_KEY_DESCRIPTION], ) area_group_list.append(instance) return area_group_list
def get_device(self): output_list = [] log(f'Building device objects', level=8) for data_dict in self.supply_data.values(): if self.downlink_attribute in data_dict: instance = self.blueprint( setting_dict=data_dict[self.key_setting], object_id=data_dict[self.key_id], name=data_dict[self.key_name], description=data_dict[self.key_desc], downlink=data_dict[self.downlink_attribute], ) else: instance = self.blueprint( setting_dict=data_dict[self.key_setting], object_id=data_dict[self.key_id], name=data_dict[self.key_name], description=data_dict[self.key_desc], ) output_list.append(instance) return output_list
def get(self, query: [str, list]) -> list: if type(query) == str: query_list = [query] else: query_list = query log(f"Query to execute: \"{query_list}\"", level=7) data_list = [] try: for q in query_list: data = self._read_cache(q) if len(query_list) > 1: if type(data) is not None: data_list.append(data) else: data_list = data except self.SQL_EXCEPTION_TUPLE as error_msg: self._error(error_msg) log(f"Query output: \"{data_list}\"", level=7) return data_list # list of dicts
def stop(self) -> bool: log('Stopping all threads', level=6) for job in self.jobs: job.stop() log('All threads stopped. Exiting loop', level=3) return True
def stop_thread(self, description: str): log(f"Stopping thread for \"{description}\"", level=6) for job in self.jobs: if job.description == description: job.stop() self.jobs.remove(job) log(f"Thread {job.description} stopped.", level=4) del job break
def get(self) -> dict: # { # object_connection: [instance_list], # object_input: [instance_list], # group_connection: [instance_list], # object_output: [instance_list], # group_input: [instance_list], # group_output: [instance_list], # } log('Building device objects (all)', level=8) output_dict = {} device_config_list = [ {'bp': GenericDeviceFactory, 'ok': config.KEY_OBJECT_CONNECTION, 'gk': config.KEY_GROUP_CONNECTION}, {'bp': GenericDeviceFactory, 'ok': config.KEY_OBJECT_INPUT, 'gk': config.KEY_GROUP_INPUT}, {'bp': GenericDeviceFactory, 'ok': config.KEY_OBJECT_OUTPUT, 'gk': config.KEY_GROUP_OUTPUT}, ] update_states = { config.KEY_OBJECT_OUTPUT: {'attribute': 'state', 'query': DEVICE_TMPL['output']['state']['get'], 'value': 1, 'set_to': True}, } for device_config in device_config_list: output_dict[device_config['gk']] = device_config['bp']( blueprint=blueprint_dict[device_config['gk']], supply_data=self.supply_data[device_config['gk']], ).get_model() output_dict[device_config['ok']] = device_config['bp']( blueprint=blueprint_dict[device_config['ok']], supply_data=self.supply_data[device_config['ok']], ).get_device() if device_config['ok'] in update_states: update = update_states[device_config['ok']] attribute = update['attribute'] for device in output_dict[device_config['ok']]: result = self.database.get(update['query'] % device.object_id) if result is not None and len(result) > 0: db_value = result[0]['active'] if db_value == update['value']: log(f"Setting {attribute} of device {device.name} to value from db!", level=5) if 'set_to' in update: setattr(device, attribute, update['set_to']) else: setattr(device, attribute, db_value) del self.database return output_dict
def reload_thread(self, sleep_time: int, thread_data, description: str) -> None: log(f"Reloading thread for \"{description}\"", level=6) self.stop_thread(description=description) self.add_thread( sleep_time=sleep_time, thread_data=thread_data, description=description, ) self.start_thread(description=description)
def _error(self, msg: str): log(f"SQL connection error: \"{msg}\"", level=3) try: self.connection.rollback() except (UnboundLocalError, AttributeError): pass raise ConnectionError(censor(msg))
def _connect(self) -> bool: try: if self.connection_data_dict['server'] in ['127.0.0.1', 'localhost']: if self.connection_data_dict['user'] == 'root': try: # local logon as root # will only work if it is run with root privileges # should only be used in the setup and update _connection = mysql.connector.connect( unix_socket=self._unix_sock(), user=self.connection_data_dict['user'], ) log('Initiated sql connection via socket as root without password', level=8) except self.SQL_EXCEPTION_TUPLE: # if failed without password, try again with it _connection = mysql.connector.connect( unix_socket=self._unix_sock(), user=self.connection_data_dict['user'], passwd=self.connection_data_dict['secret'] ) log('Initiated sql connection via socket as root', level=8) else: # local logon for default users _connection = mysql.connector.connect( unix_socket=self._unix_sock(), user=self.connection_data_dict['user'], passwd=self.connection_data_dict['secret'], database=self.connection_data_dict['database'] ) log('Initiated sql connection to localhost', level=8) else: # remote logon _connection = mysql.connector.connect( host=self.connection_data_dict['server'], port=self.connection_data_dict['port'], user=self.connection_data_dict['user'], passwd=self.connection_data_dict['secret'], database=self.connection_data_dict['database'] ) log('Initiated sql connection to remote server', level=8) except self.SQL_EXCEPTION_TUPLE as error_msg: self._error(error_msg) try: self.connection = _connection self.cursor = _connection.cursor(buffered=True, dictionary=True) return True except UnboundLocalError: log('Connection instance could not be created.') raise ConnectionError('Connection instance not created')
def __init__(self, parsed: dict): self.path = parsed['path'].split(PACKAGE_PATH_SEPARATOR)[0].split('.') self.data = parsed['data'] self.result = None try: self.path_main = f'{self.path[0]}.{self.path[1]}' self.path_type = self.path[2] self.path_subtype = self.path[3] self.path_id = int(self.path[4]) except (TypeError, IndexError): log(f'Router did not get all expected arguments!', level=3) raise IndexError
def _check(self, attribute: str, instance) -> None: """ Will check if the link was successfully created. Else it will log an error. :param attribute: Instance attribute that should be checked :param instance: Instance to check :return: None """ to_check = getattr(instance, attribute) if not isinstance(to_check, self.LINK_TARGET_TYPES): log(f'Failed to create \"{attribute}\" link for object \"{instance}\"', level=4)
def __del__(self): try: self.cursor.close() except (UnboundLocalError, AttributeError, ReferenceError): pass try: self.connection.close() log('SQL connection closed', level=8) except (UnboundLocalError, AttributeError, ReferenceError): pass
def get(self): output_list = [] log(f'Building condition special-match objects', level=8) for data_dict in self.supply_data.values(): instance = self.blueprint( object_id=data_dict[config.DB_ALL_KEY_ID], name=data_dict[config.DB_ALL_KEY_NAME], description=data_dict[config.DB_ALL_KEY_DESCRIPTION], ) output_list.append(instance) return output_list
def get(self): output_list = [] log(f'Building task objects', level=8) for data_dict in self.supply_data.values(): instance = self.blueprint( setting_dict=data_dict[config.SUPPLY_KEY_SETTING_DICT], object_id=data_dict[config.DB_ALL_KEY_ID], name=data_dict[config.DB_ALL_KEY_NAME], description=data_dict[config.DB_ALL_KEY_DESCRIPTION], ) output_list.append(instance) return output_list
def get(self): output_list = [] log(f'Building condition link objects', level=8) for data_dict in self.supply_data.values(): instance = self.blueprint( condition_match_dict=data_dict[self.key_member][config.SUPPLY_CL_KEY_MEMBER_CM], condition_group_dict=data_dict[self.key_member][config.SUPPLY_CL_KEY_MEMBER_CG], setting_dict=data_dict[config.SUPPLY_KEY_SETTING_DICT], object_id=data_dict[config.DB_ALL_KEY_ID], name=data_dict[config.DB_ALL_KEY_NAME], ) output_list.append(instance) return output_list
def get(self) -> dict: # { # area_group: [instance_list], # } log(f'Building group objects (all)', level=8) output_dict = { self.key_area_group: AreaGroup( supply_data=self.supply_data[self.key_area_group], factory_dict=self.factory_data, blueprint=blueprint_dict[self.key_area_group], ).get() } return output_dict
def _log(self, output, level: int = 1): try: from core.utils.debug import fns_log, log if len(self.log_cache) > 0: for msg in self.log_cache: log(output=msg['output'], level=msg['level']) self.log_cache = [] fns_log(output=output, level=level) except Exception: self.log_cache.append({'level': level, 'output': output}) if level == 1: journal.send(output)
def get_model(self): output_list = [] log(f'Building device model objects', level=8) for data_dict in self.supply_data.values(): instance = self.blueprint( setting_dict=data_dict[self.key_setting], member_list=data_dict[config.SUPPLY_KEY_MEMBER_DICT][ config.SUPPLY_GENERIC_KEY_MEMBER], object_id=data_dict[self.key_id], name=data_dict[self.key_name], description=data_dict[self.key_desc], ) output_list.append(instance) return output_list
def put(self, command: [str, list]) -> bool: if type(command) == str: command_list = [command] else: command_list = command log(f"Query to execute: \"{command_list}\"", level=7) try: for cmd in command_list: self.cursor.execute(cmd) self.connection.commit() except self.SQL_EXCEPTION_TUPLE as error_msg: self._error(error_msg) return True
def _one_to_one_options(self, instances: list, oto_config: dict, type_key: str) -> None: """ Links two object instances if there is the option to choose one of X target types. Will check which target type is configured, find and link it [only object_id linking supported] Example: - condition match can be linked to one of [input object, input model, special match] - one will/must be linked :param instances: The currently processed object instances - only one type at a time :param oto_config: Link config sub-dict as configured in factory config file :param type_key: The type that is currently processed - needed to get the correct raw data from the supply data :return: None """ for instance in instances: _id = getattr(instance, config.CORE_ID_ATTRIBUTE) raw_instance_data = [ data_dict for data_dict in self.supply_data[type_key].values() if int(data_dict['id']) == _id ][0] for set_attr, options in oto_config.items(): for option in options: search_attr = option[config.LINK_KEY_SEARCH_ATTR] query_attr = config.CORE_ID_ATTRIBUTE search_key = option[config.LINK_KEY_SEARCH_KEY] if raw_instance_data[search_attr] is not None: to_compare = int(raw_instance_data[search_attr]) for target_instance in self.factory_data[search_key]: if getattr(target_instance, query_attr) == to_compare: setattr(instance, set_attr, target_instance) log(f'Object \"{instance}\" has the following link target for attribute \"{set_attr}\": \"{target_instance}\"', level=8) break break self._check(attribute=set_attr, instance=instance)
def stop(self) -> bool: log(f"Thread stopping {self.log_name}", level=6) self.state_stop.set() try: self.join(config.THREAD_JOIN_TIMEOUT) if self.is_alive(): log(f"Unable to join thread {self.log_name}", level=5) except RuntimeError: log(f"Got error stopping thread {self.log_name}", level=5) log(f"Stopped thread {self.log_name}", level=4) return True
def start(instance, settings: dict = None): if settings is None: settings = {} if isinstance(instance, (GaInputDevice, GaInputModel)): from core.device.input.main import Go Go(instance=instance, **settings).start() elif isinstance(instance, GaConditionGroup): from core.device.output.main import Go Go(instance=instance, **settings).start() elif isinstance(instance, GaTaskDevice): log('Core timers are not yet implemented', level=3) pass elif isinstance(instance, SystemTask): instance.execute(**settings) else: log(f"Service could not find a matching decision for instance: '{instance}'", level=5)
def add_thread(self, sleep_time: int, thread_data, description: str, once: bool = False, daemon: bool = True): log(f"Adding thread for \"{description}\" with interval \"{sleep_time}\"", level=7) self.thread_nr += 1 def decorator(function): if sleep_time == 0: sleep_time_new = config.THREAD_DEFAULT_SLEEP_TIME self.jobs.add( Workload( sleep=timedelta(seconds=sleep_time_new), execute=function, data=thread_data, loop_instance=self, once=True, description=description, daemon=daemon, name=f"Thread #{self.thread_nr}", )) else: self.jobs.add( Workload( sleep=timedelta(seconds=sleep_time), execute=function, data=thread_data, loop_instance=self, once=once, description=description, daemon=daemon, name=f"Thread #{self.thread_nr}", )) return function return decorator
def __init__(self): try: self.connection_data_dict = { 'server': config.AGENT.sql_server, 'port': config.AGENT.sql_port, 'user': config.AGENT.sql_user, 'secret': config.AGENT.sql_secret, 'database': config.AGENT.sql_database } log( f"DB connection config: " f"server => \"{self.connection_data_dict['server']}\" " f"port => \"{self.connection_data_dict['port']}\" " f"user => \"{self.connection_data_dict['user']}\" " f"database => \"{self.connection_data_dict['database']}\"", level=7) except IndexError as error_msg: self._error(msg=error_msg) self.link = None self._link()
def get(self): output_list = [] log(f'Building condition group objects', level=8) for data_dict in self.supply_data.values(): instance = self.blueprint( member_list=data_dict[self.key_member][ config.SUPPLY_CG_KEY_MEMBER_CL], output_object_list=data_dict[self.key_member][ config.SUPPLY_CG_KEY_MEMBER_OO], output_group_list=data_dict[self.key_member][ config.SUPPLY_CG_KEY_MEMBER_OG], area_group_list=data_dict[self.key_member][ config.SUPPLY_CG_KEY_MEMBER_AG], setting_dict=data_dict[config.SUPPLY_KEY_SETTING_DICT], object_id=data_dict[config.DB_ALL_KEY_ID], name=data_dict[config.DB_ALL_KEY_NAME], description=data_dict[config.DB_ALL_KEY_DESCRIPTION], ) output_list.append(instance) return output_list
def _one_to_one(self, instances: list, oto_config: dict) -> None: """ Links two object instances. Either links them via object_id [config=simple key:value pair] or some custom mapping [config=dict] :param instances: The currently processed object instances - only one type at a time :param oto_config: Link config sub-dict as configured in factory config file :return: None """ for key, link_config in oto_config.items(): if type(link_config) == dict: # this instance is a member of the targets member list search_key = link_config[config.LINK_KEY_SEARCH_KEY] search_attr = link_config[config.LINK_KEY_SEARCH_ATTR] set_attr = link_config[config.LINK_KEY_SET_ATTR] for instance in instances: for target_instance in self.factory_data[search_key]: if instance in getattr(target_instance, search_attr): log(f'Object \"{instance}\" has the following link target for attribute \"{set_attr}\": \"{target_instance}\"', level=8) setattr(instance, set_attr, target_instance) break self._check(attribute=set_attr, instance=instance) else: # simple id to id link search_key = link_config search_attr = config.CORE_ID_ATTRIBUTE set_attr = key for instance in instances: try: # get the raw id of the target object to_compare = int(getattr(instance, set_attr)) except TypeError: if set_attr != 'downlink': log(f'Unable to get value for attribute \"{set_attr}\" from object \"{instance}\"', level=5) continue for target_instance in self.factory_data[search_key]: if getattr(target_instance, search_attr) == to_compare: log(f'Object \"{instance}\" has the following link target for attribute \"{set_attr}\": \"{target_instance}\"', level=8) setattr(instance, set_attr, target_instance) break self._check(attribute=set_attr, instance=instance)
def _one_to_many(self, instances: list, otm_config: dict) -> None: """ Builds a member-list/dict to link to the current instance. It supports lists and numbered dicts (key=number,value=member) :param instances: The currently processed object instances - only one type at a time :param otm_config: Link config sub-dict as configured in factory config file :return: None """ for instance in instances: for member_attr, search_key in otm_config.items(): raw_member_data = getattr(instance, member_attr) possible_members = self.factory_data[search_key] if type(raw_member_data) == list: log(f'Building member list for instance \"{instance}\"', level=8) members = [] for member in raw_member_data: for possible_member in possible_members: if possible_member.object_id == member: members.append(possible_member) break else: # numbered members -> p.e. condition link log(f'Building numbered member dict for instance \"{instance}\"', level=8) members = {} for numbered_data, member in raw_member_data.items(): for possible_member in possible_members: if possible_member.object_id == member: members[numbered_data] = possible_member break log(f'Object \"{instance}\" has the following members for attribute \"{member_attr}\": \"{members}\"', level=8) setattr(instance, member_attr, members)