def __init__(self, name: str, external_name: str, event_id: str, logic_func: Callable, parent: Thinker, tick=0.1, print_every_n=20, original_owner=''): super().__init__() self.logger = logging.getLogger(__name__ + '.' + self.__class__.__name__) self.name = name self.external_name = external_name self.id = event_id self.parent = parent self.original_owner = original_owner self.logic_func = logic_func self.print_every_n = print_every_n self.n = 0 # reserved for heartbeat events self.time = 0 self.tick = tick # in sec self.active = False self.paused = False self.counter_timeout = 0 if self.parent: info_msg(self, 'CREATED', extra=f' of {self.parent.name}') else: info_msg(self, 'CREATED')
def react_directed(self, msg: MessageExt): if self.parent.pyqtsignal_connected: # Convert MessageExt to MessageInt and emit it msg_int = msg.ext_to_int() self.parent.signal.emit(msg_int) msg_r = None if msg.com == MsgComExt.ALIVE.msg_name: if msg.sender_id in self.parent.connections: msg_r = None # MsgGenerator.are_you_alive_reply(device=self.parent_logger, msg_i=msg) elif msg.com == MsgComExt.DO_IT.msg_name: if not self.parent.add_to_executor(self.parent.execute_com, msg=msg): self.logger.error(f'Adding to executor {msg.info} failed') elif msg.com == MsgComExt.DONE_IT.msg_name: if msg.reply_to in self.forwarded_messages: initial_msg: MessageExt = self.forwarded_messages[msg.reply_to] msg_r = msg.copy(receiver_id=initial_msg.sender_id, reply_to=initial_msg.id, sender_id=self.parent.id, forwarded_from=msg.sender_id) del self.forwarded_messages[msg.reply_to] info_msg( self, 'INFO', f'Msg {initial_msg.id} com {initial_msg.com} is deleted from forwarded messages' ) else: pass # TODO: at this moment Server does not do DO_IT command for itself, it only forwards elif msg.com == MsgComExt.WELCOME_INFO_SERVER.msg_name: self.react_first_welcome(msg) self.msg_out(msg_r)
def internal_hb_logic(event: ThinkerEvent): thinker: Thinker = event.parent device = thinker.parent full_heartbeat = False if device.type is DeviceType.SERVER: full_heartbeat = True # Allows to send MsgComExt.HEARTBEAT_FULL only for Server info_msg(event, 'STARTED', extra=f' of {thinker.name}') while event.active: sleep(0.001) if not event.paused: event.n += 1 if full_heartbeat and event.n % 3: # TODO: every n minutes changes session_key for safety... msg_heartbeat = device.generate_msg( msg_com=MsgComExt.HEARTBEAT_FULL, event=event) else: msg_heartbeat = device.generate_msg( msg_com=MsgComExt.HEARTBEAT, event=event) if device.pyqtsignal_connected and device.type is DeviceType.SERVER: msg = device.generate_msg(msg_com=MsgComInt.HEARTBEAT, event=event) device.signal.emit(msg) thinker.add_task_out(msg_heartbeat) sleep(event.tick)
def stop(self): info_msg(self, 'STOPPING') self.active = False self.paused = True self.parent.device_status.messaging_on = self.active self.parent.device_status.messaging_paused = self.paused self._msg_out.clear()
def send_msg(self, msg: MessageExt): try: crypted = str(int(msg.crypted)).encode('utf-8') msg_bytes = self.encrypt_with_session_key(msg) if msg.receiver_id == '': self.sockets[PUB_Socket_Server].send_multipart( [msg_bytes, crypted]) else: if msg.receiver_id in self._frontendpool: self.sockets[FRONTEND_Server].send_multipart( [msg.receiver_id.encode('utf-8'), msg_bytes, crypted]) info_msg( self, 'INFO', f'Msg {msg.id}, com {msg.com} is send from frontend to {msg.receiver_id}.' ) elif msg.receiver_id in self._backendpool: self.sockets[BACKEND_Server].send_multipart( [msg.receiver_id.encode('utf-8'), msg_bytes, crypted]) info_msg( self, 'INFO', f'Msg {msg.id}, com {msg.com} is send from backend to {msg.receiver_id}.' ) else: error_logger( self, self.send_msg, f'ReceiverID {msg.receiver_id} is not present in Server pool.' ) except zmq.ZMQError as e: error_logger(self, self.send_msg, e)
def run(self): info_msg(self, 'STARTING') self.active = True self.paused = False self.parent.device_status.messaging_on = self.active self.parent.device_status.messaging_paused = self.paused # Start send loop here self._send_loop_logic(await_time=0.1 / 1000.)
def _start_messaging(self): """Start messaging part of Device""" info_msg(self, 'STARTING') self.thinker.start( ) # !!!Thinker must start before the messenger, always!!!! self.messenger.start() info_msg(self, 'STARTED') self.send_status_pyqt()
def react_internal(self, event: ThinkerEvent): if 'heartbeat' in event.name: if event.counter_timeout > self.timeout: info_msg( self, 'INFO', 'Service was away for too long...deleting info about service' ) self.remove_device_from_connections(event.original_owner) self.parent.send_status_pyqt()
def _check_size_limit(self): if self.size_limit is not None: while len(self) > self.size_limit: element = self.popitem(last=False) # Remove first element if self.dict_parent: info_msg( self.dict_parent, 'INFO', f'Limit size={self.size_limit} was exceeded for {self.name}, ' f'first element {element} was removed')
def run(self): self.active = True self.paused = self.parent.paused try: self.time = time() info_msg(self, 'STARTING', extra=f' of {self.parent.name}') self.logic_func(self) except Exception as e: error_logger(self, self.run, f'{self.name}. Error: {e}') finally: info_msg(self, 'STOPPED', extra=f' of {self.parent.name}')
def internal_info_logic(event: ThinkerEvent): # TODO: why I need it? thinker: Thinker = event.parent device = thinker.parent info_msg(event, 'STARTED', extra=f' of {thinker.name} with tick {event.tick}') while event.active: sleep(0.001) if not event.paused: sleep(event.tick)
def react_demand(self, msg: Message): data = msg.data cmd = data.com info_msg(self, 'REQUEST', extra=str(msg.short())) reply = True if msg.body.receiver_id != self.parent.id: if msg.body.receiver_id in self.parent.connections: msg_i = MsgGenerator.forward_msg(device=self.parent, msg_i=msg) else: msg_i = [MsgGenerator.available_services_reply(device=self.parent, msg_i=msg), MsgGenerator.error(device=self.parent, comments=f'service {data.info.service_id} is not available anymore', msg_i=msg)] else: if cmd == MsgGenerator.HELLO.mes_name: try: device_info: DeviceInfoMes = data.info connections = self.parent.connections if data.info.type not in ('service', 'client'): raise Exception(f'{self}:{device_info.type} is not known') if device_info.device_id not in connections: connections[device_info.device_id] = Connection(device_info=data.info) if 'publisher' in device_info.public_sockets: from communication.logic.logic_functions import external_hb_logic self.parent.messenger.subscribe_sub(address=device_info.public_sockets['publisher']) a = f'heartbeat:{data.info.name}' self.register_event(name=f'heartbeat:{data.info.name}', logic_func=external_hb_logic, event_id=f'heartbeat:{data.info.device_id}', original_owner=device_info.device_id, start_now=True) session_key = self.parent.messenger.gen_symmetric_key(device_info.device_id) session_key_encrypted = self.parent.messenger.encrypt_with_public(session_key, device_info.public_key) msg_i = MsgGenerator.welcome_info(device=self.parent, msg_i=msg, session_key=session_key_encrypted) self.parent.send_status_pyqt(com='status_server_info_full') else: msg_i = MsgGenerator.welcome_info(device=self.parent, msg_i=msg) except Exception as e: self.logger.error(e) msg_i = MsgGenerator.error(device=self.parent, comments=repr(e), msg_i=msg) elif cmd == MsgGenerator.AVAILABLE_SERVICES_DEMAND.mes_name: msg_i = MsgGenerator.available_services_reply(device=self.parent, msg_i=msg) elif cmd == MsgGenerator.POWER_ON_DEMAND.mes_name: # TODO: service must be realized instead # Server here always replied the same way to all services comments = """"I always say, that power is ON, I hope the user have turned on power already""" msg_i = MsgGenerator.power_on_reply(self.parent, msg_i=msg, flag=True, comments=comments)
def msg_out(self, msg_out: Union[MessageExt, List[MessageExt]]): if msg_out: if isinstance(msg_out, list): for msg in msg_out: self.msg_out(msg) elif isinstance(msg_out, MessageExt): info_msg(self, 'INFO', msg_out.short()) self.add_task_out(msg_out) else: error_logger( self, self.msg_out, f'Union[MessageExt, List[MessageExt]] was not passed to msg_out, but' f'{msg_out}')
def pending_demands(event: ThinkerEvent): thinker: Thinker = event.parent demands_waiting_reply: MsgDict = thinker.demands_waiting_reply info_msg(event, 'STARTED', extra=f' of {thinker.name} with tick {event.tick}') while event.active: sleep(0.001) if not event.paused and demands_waiting_reply: try: sleep(event.tick) for key, item in demands_waiting_reply.items(): pending: PendingReply = item if (time() - event.time) > event.tick and pending.attempt < 3: pending.attempt += 1 info_msg( event, 'INFO', f'Msg {pending.message.id}, com {pending.message.com} waits ' f'{pending.attempt}.') elif (time() - event.time) > event.tick and pending.attempt >= 3: try: msg = pending.message del demands_waiting_reply[key] info_msg(event, 'INFO', f'Reply timeout for msg: {msg.short()}') info_msg(event, 'INFO', f'Msg {msg.id} is deleted') except KeyError: error_logger( event, pending_demands, f'Cannot delete Msg {msg.id}, com {msg.com} from ' f'demand_waiting_reply') except ThinkerErrorReact as e: error_logger(event, pending_demands, e)
def run(self): super().run() try: info_msg(self, 'STARTED') self._receive_msgs() except zmq.error.ZMQError as e: # Bad kind of error! error_logger(self, self.run, e) self.stop() finally: for _, soc in self.sockets.items(): soc.close() self.context.destroy() self.active = False self.paused = True info_msg(self, 'STOPPED')
def _stop_messaging(self): """Stop messaging part of Device""" info_msg(self, 'STOPPING') stop_msg = self.generate_msg(msg_com=MsgComExt.SHUTDOWN, reason='normal_shutdown') self.thinker.msg_out(stop_msg) sleep(0.1) self.thinker.pause() self.messenger.pause() self.thinker.stop() self.messenger.stop() self.send_status_pyqt() self.device_status.messaging_paused = False self.device_status.messaging_on = False info_msg(self, 'STOPPED')
def react_reply(self, msg: Message): data = msg.data info_msg(self, 'REPLY_IN', extra=str(msg.short())) if msg.reply_to in self.demands_pending_answer: del self.demands_pending_answer[msg.reply_to] self.logger.info(f'react_reply: Msg {msg.reply_to} reply is obtained') if data.com == MsgGenerator.WELCOME_INFO.mes_name: if data.info.device_id in self.parent.connections: self.logger.info(f'Server {data.info.device_id} is active. Handshake was undertaken') connection: Connection = self.parent.connections[data.info.device_id] connection.device_info = data.info session_key = self.parent.messenger.decrypt_with_private(data.info.session_key) self.parent.messenger.fernet = self.parent.messenger.create_fernet(session_key)
def remove_device_from_connections(self, device_id): # TODO: the service_info is not deleted from _frontend sockets or backend sockets connections = self.parent.connections if device_id in connections: info_msg(self, 'INFO', f'Procedure to delete {device_id} is started') for key, event in list(self.events.items()): if event.original_owner == device_id: self.unregister_event(key) del self.parent.connections[device_id] info_msg(self, 'INFO', f'Device {device_id} is deleted') else: error_logger( self, self.remove_device_from_connections, f'remove_device_from_connections: Wrong device_id {device_id} is passed' )
def task_in_reaction(event: ThinkerEvent): thinker: Thinker = event.parent tasks_in: MsgDict = thinker.tasks_in info_msg(event, 'STARTED', extra=f' of {thinker.name} with tick {event.tick}') exclude_msgs = [ MsgComExt.HEARTBEAT.msg_name, MsgComExt.HEARTBEAT_FULL.msg_name ] while event.active: sleep( 0.001 ) # Any small interruption is necessary not to overuse processor time if not event.paused and tasks_in: try: msg: MessageExt = tasks_in.popitem(last=False)[1] thinker.msg_counter += 1 react = True if msg.com not in exclude_msgs: info_msg(event, 'INFO', f'Received: {msg.short()}') if msg.reply_to == '' and msg.receiver_id != '': # If message is not a reply, it must be a demand one thinker.add_demand_waiting_reply(msg) info_msg( event, 'INFO', f'Expect a reply to {msg.id} com={msg.com}. Adding to waiting list.' ) elif msg.reply_to != '': if msg.reply_to in thinker.demands_waiting_reply: # TODO: should it have else clause or not? msg_awaited: MessageExt = thinker.demands_waiting_reply[ msg.reply_to].message del thinker.demands_waiting_reply[msg.reply_to] info_msg( event, 'INFO', f'REPLY to Msg {msg.reply_to} {msg_awaited.com} is obtained.' ) else: react = False info_msg( event, 'INFO', f'Reply to msg {msg.reply_to} arrived too late.') if react: thinker.react_external(msg) except (ThinkerErrorReact, KeyError) as e: error_logger(event, task_in_reaction, f'{e}: {msg.short()}')
def react_reply(self, msg: Message): data = msg.data cmd = data.com info_msg(self, 'REPLY_IN', extra=str(msg.short())) reply = False if msg.body.receiver_id != self.parent.id: msg_i = MsgGenerator.forward_msg(device=self.parent, msg_i=msg) if msg.reply_to in self.demands_pending_answer: del self.demands_pending_answer[msg.reply_to] self.logger.info(f'react_reply: Msg: {msg.reply_to} reply is obtained and forwarded to intial demander') reply = True else: pass self.msg_out(reply, msg_i)
def react_forward(self, msg: MessageExt): if msg.receiver_id in self.parent.connections: info_msg( self, 'INFO', f'Msg id={msg.id}, com={msg.com} is forwarded to {msg.receiver_id}' ) msg_r = msg.copy(sender_id=self.parent.id, forwarded_from=msg.sender_id) self.add_to_forwarded(msg_forwarded=msg_r, msg_arrived=msg) else: msg_r = [ self.parent.generate_msg(msg_com=MsgComExt.AVAILABLE_SERVICES, receiver_id=msg.sender_id, reply_to=msg.id), self.parent.generate_msg( msg_com=MsgComExt.ERROR, comments=f'service {msg.receiver_id} is not available', receiver_id=msg.sender_id, reply_to=msg.id) ] self.msg_out(msg_r)
def task_out_reaction(event: ThinkerEvent): thinker: Thinker = event.parent tasks_out: MsgDict = thinker.tasks_out demand_waiting_reply: MsgDict = thinker.demands_waiting_reply info_msg(event, 'STARTED', extra=f' of {thinker.name} with tick {event.tick}') while event.active: sleep(0.001) if not event.paused and tasks_out: try: msg: MessageExt = tasks_out.popitem(last=False)[1] react = True if msg.receiver_id != '' and msg.reply_to == '': # If msg is not reply, than add to pending demand info_msg( event, 'INFO', f'Msg id={msg.id}, com {msg.com} is considered to get a reply' ) thinker.add_demand_waiting_reply(msg) elif msg.reply_to != '': if msg.reply_to in demand_waiting_reply: msg_awaited: MessageExt = thinker.demands_waiting_reply[ msg.reply_to].message del demand_waiting_reply[msg.reply_to] info_msg( event, 'INFO', f'Msg id={msg.reply_to} {msg_awaited.com} is deleted from ' f'demand_waiting_reply') if react: thinker.parent.messenger.add_msg_out(msg) except (ThinkerErrorReact, KeyError) as e: error_logger(event, task_out_reaction, f'{e}: {msg.short()}')
def send_msg(self, msg: MessageExt): try: crypted = str(int(msg.crypted)).encode('utf-8') msg_bytes = self.encrypt_with_session_key(msg) if msg.receiver_id != '': self.sockets[DEALER_Socket].send_multipart( [msg_bytes, crypted]) info_msg( self, 'INFO', f'Msg {msg.id}, msg_com {msg.com} is send to {msg.receiver_id}.' ) else: if self.pub_option: self.sockets[PUB_Socket].send_multipart( [msg_bytes, crypted]) else: info_msg( self, 'INFO', f'Publisher socket is not available for {self.name}.') except zmq.ZMQError as e: error_logger(self, self.send_msg, e)
def __init__(self, parent): from devices.devices import Device Thinker.n_instance += 1 self.logger = logging.getLogger(__name__ + '.' + self.__class__.__name__) self.name = f'{self.__class__.__name__}:{parent.name}:{Thinker.n_instance}' self.id = f'{self.name}:{unique_id(self.name)}' self.parent: Device = parent self.msg_counter = 0 self.events = Events_Dict() msg_dict_size_limit = 10000 self._tasks_in = MsgDict(name='tasks_in', size_limit=msg_dict_size_limit, dict_parent=self) self.tasks_in_test = MsgDict(name='tasks_in_test', size_limit=msg_dict_size_limit, dict_parent=self) self._tasks_out = MsgDict(name='tasks_out', size_limit=msg_dict_size_limit, dict_parent=self) self.tasks_out_test = MsgDict(name='tasks_out_test', size_limit=msg_dict_size_limit, dict_parent=self) self._demands_waiting_reply = MsgDict(name='demands_waiting_reply', size_limit=msg_dict_size_limit, dict_parent=self) # TODO: add slow thread to track after forwarded messages self._forwarded = MsgDict(name='forwarded_messages', size_limit=msg_dict_size_limit, dict_parent=self) self.paused = False info_msg(self, 'CREATING') try: self.timeout = int(self.parent.get_general_settings()['timeout']) pending_demands_tick = float( self.parent.get_general_settings()['pending_demands']) / 1000. except KeyError as e: error_logger(self, self.__init__, e) self.timeout = 10 pending_demands_tick = 0.2 try: from communication.logic.logic_functions import (task_in_reaction, task_out_reaction, pending_demands) self.register_event(name='task_in_reaction', logic_func=task_in_reaction, tick=None) self.register_event(name='task_out_reaction', logic_func=task_out_reaction, tick=None) self.register_event(name='demands_waiting_reply', logic_func=pending_demands, tick=pending_demands_tick) info_msg(self, 'CREATED') except (ThinkerEventError, ThinkerEventFuncError, TypeError) as e: error_logger(self, self.register_event, e) info_msg(self, 'NOT CREATED') raise ThinkerError(str(e))
def react_first_welcome(self, msg: MessageExt): msg_r = None try: messenger = self.parent.messenger info: WelcomeInfoServer = msg.info # Decrypt public_key of device crypted on device side with public key of Server info.session_key = messenger.decrypt_with_private(info.session_key) server_connection = self.connections[msg.sender_id] # TODO: Actually check AccessLevel and Permission using password checksum server_connection.session_key = info.session_key server_connection.access_level = AccessLevel.FULL server_connection.permission = Permission.GRANTED self.parent.send_status_pyqt() info_msg( self, 'INFO', f'Handshake with Server is accomplished. Session_key is obtained.' ) except Exception as e: # TODO: change Exception to something reasonable msg_r = self.parent.generate_msg(msg_com=MsgComExt.ERROR, comments=f'{e}', receiver_id=msg.sender_id, reply_to=msg.id) self.msg_out(msg_r)
def run(self): super().run() try: for adr in self.addresses[PUB_Socket_Server]: self.subscribe_sub(address=adr) msg = self._wait_server_hb() if self.active: self.connect() self.parent.thinker.react_heartbeat_full(msg) info_msg(self, 'STARTED') self._receive_msgs() except (zmq.error.ZMQError, MessengerError) as e: # Bad type of error error_logger(self, self.run, e) self.stop() finally: self.sockets[DEALER_Socket].close() if self.sockets[SUB_Socket]: self.sockets[SUB_Socket].close() if self.sockets[PUB_Socket]: self.sockets[PUB_Socket].close() self.context.destroy() self.active = False self.paused = True info_msg(self, 'STOPPED')
def __init__(self, name: str, addresses: Dict[str, str], parent: Device, pub_option: bool, **kwargs): """ :param name: user-friendly name :param addresses: :param parent: Device, messenger can function without parent_logger Device as well :param pub_option: tell weather there is a publisher socket :param kwargs: """ super().__init__() self._attempts_to_restart_sub = 1 # restart subscriber self._are_you_alive_send = False Messenger.n_instance += 1 self.logger = logging.getLogger( f'{__name__}.{self.__class__.__name__}') self.name = f'{self.__class__.__name__}:{Messenger.n_instance}:{name}:{get_local_ip()}' self.id: DeviceId = parent.id self.parent: Device = parent try: self._polling_time = int( self.parent.get_general_settings()['polling']) / 1000. except AttributeError: self._polling_time = 1 self.active = False self.paused = True self._msg_out = MsgDict(name=f'msg_out:{self.id}', size_limit=1000, dict_parent=self) # ZMQ sockets, communication info self.sockets = {} self.public_sockets = {} self.addresses = {} self.pub_option = pub_option self._gen_rsa_keys() try: info_msg(self, 'INITIALIZING') self._verify_addresses(addresses) self._create_sockets() info_msg(self, 'INITIALIZED') except (WrongAddress, zmq.ZMQError) as e: info_msg(self, 'NOT INITIALIZED') raise MessengerError(str(e))
def _wait_server_hb(self) -> MessageExt: if FRONTEND_Server not in self.addresses or BACKEND_Server not in self.addresses: wait = True else: wait = False i = 0 msg_out = None while wait and self.active: if i > 100: info_msg( self, 'INFO', f'{self.name} could not connect to server, no sockets, ' f'try to restart {self.parent.name}') i = 0 i += 1 sockets = dict(self.poller.poll(100)) if self.sockets[SUB_Socket] in sockets: mes, crypted = self.sockets[SUB_Socket].recv_multipart() # TODO: decrypt message safely with try except msg: MessageExt = MessageExt.bytes_to_msg(mes) # TODO: first heartbeat could be received only from server! make it safe if msg.com == MsgComExt.HEARTBEAT_FULL.msg_name: try: info: HeartBeatFull = msg.info sockets = info.device_public_sockets if FRONTEND_Server in sockets and BACKEND_Server in sockets: #TODO: need to check if it true SERVER using password or certificate info_msg(self, 'INFO', f'{msg.short()}') info_msg( self, 'INFO', f'Info from Server is obtained for messenger operation.' ) self.addresses[FRONTEND_Server] = sockets[ BACKEND_Server] self.addresses[BACKEND_Server] = sockets[ BACKEND_Server] msg_out = msg break else: raise MessengerError( f'Not all sockets are sent to {self.name}') except (AttributeError, Exception ) as e: # IN case when short Heartbeat arrived pass return msg_out
def external_hb_logic(event: ThinkerEvent): """ This event function is designed to track after external events, e.g., heartbeat of a Server If event is timeout, than counter_timeout += 1 Every cycle event is being send to Thinker.react_internal, where Thinker decides what should be done, e.g., counter_timeout reached certain value Every event.print_every_n the information of event is printed out in console :param event: ThinkerEvent :return: None """ thinker: Thinker = event.parent info_msg(event, 'STARTED', extra=f' of {thinker.name} with tick {event.tick}') counter = 0 while event.active: sleep(0.001) if not event.paused: sleep(event.tick) if (time() - event.time) >= event.tick: event.counter_timeout += 1 else: event.counter_timeout = 0 if event.counter_timeout % 3 == 0 and event.counter_timeout != 0: info_msg(event, 'INFO', f'{event.name} timeout {event.counter_timeout}') counter += 1 event.parent.react_internal(event) if counter % event.print_every_n == 0: counter = 0 info_msg(event, 'INFO', extra=f'{event.name}: {event.n}') else: sleep(0.05) event.time = time()
def __init__(self, name: str, db_path: Path, cls_parts: Dict[str, Any], type: DeviceType, parent: QObject = None, db_command: str = '', logger_new=True, test=False, **kwargs): super().__init__() Device.n_instance += 1 self.available_public_functions_names = list( cmd.name for cmd in self.available_public_functions()) self.config = configurationSD(self) self.connections: Dict[DeviceId, Connection] = {} self.cls_parts: Dict[str, Union[ThinkerInter, MessengerInter, ExecutorInter]] = cls_parts self.device_status: DeviceStatus = DeviceStatus(*[False] * 5) self.db_path = db_path self.name: str = name self._main_executor = ThreadPoolExecutor(max_workers=100) self.parent: QObject = parent self.type: DeviceType = type self.test = test if logger_new: self.logger = initialize_logger(app_folder / 'LOG', file_name=__name__ + '.' + self.__class__.__name__) if test: self.logger.setLevel(logging.ERROR) else: self.logger = logging.getLogger(__name__ + '.' + self.__class__.__name__) if 'id' not in kwargs: self.id: DeviceId = f'{name}:{unique_id(name)}' else: self.id: DeviceId = kwargs['id'] try: assert len(self.cls_parts) == 2 for key, item in self.cls_parts.items(): assert key in ['Messenger', 'Thinker'] assert isclass(item) except AssertionError as e: self.logger.error(e) raise e try: pyqtslot: Callable = kwargs['pyqtslot'] self._connect_pyqtslot_signal(pyqtslot) except KeyError: self.pyqtsignal_connected = False self.logger.info(f'pyqtsignal is set to False') # config is set here try: db_conn = db_create_connection(self.db_path) res, comments = db_execute_select(db_conn, db_command) db_conn.close() self.config.add_config(self.name, config_text=res) from communication.messaging.messengers import Messenger from communication.logic.thinkers_logic import Thinker if 'pub_option' not in kwargs: kwargs['pub_option'] = True self.messenger: Messenger = self.cls_parts['Messenger']( name=self.name, addresses=self.get_addresses(), parent=self, pub_option=kwargs['pub_option']) self.thinker: Thinker = self.cls_parts['Thinker'](parent=self) except (sq3.Error, KeyError, MessengerError) as e: self.logger.error(e) raise DeviceError(str(e)) info_msg(self, 'CREATED')