コード例 #1
0
    def __init__(self, config_file=None):
        if config_file is None:
            config_file = path.dirname(path.dirname(
                path.abspath(__file__))) + '/config/tb_gateway.yaml'
        with open(config_file) as config:
            config = safe_load(config)
            self.__config_dir = path.dirname(path.abspath(config_file)) + '/'
            logging.config.fileConfig(self.__config_dir + "logs.conf")
            global log
            log = logging.getLogger('service')
            self.available_connectors = {}
            self.__connector_incoming_messages = {}
            self.__connected_devices = {}
            self.__saved_devices = {}
            self.__events = []
            self.__rpc_requests_in_progress = {}
            self.__connected_devices_file = "connected_devices.json"
            self.tb_client = TBClient(config["thingsboard"])
            self.main_handler = logging.handlers.MemoryHandler(1000)
            self.remote_handler = TBLoggerHandler(self)
            self.main_handler.setTarget(self.remote_handler)
            self.__default_connectors = {
                "mqtt": "MqttConnector",
                "modbus": "ModbusConnector",
                "opcua": "OpcUaConnector",
                "ble": "BLEConnector",
            }
            self.__implemented_connectors = {}
            self.__event_storage_types = {
                "memory": MemoryEventStorage,
                "file": FileEventStorage,
            }
            self.__load_connectors(config)
            self.__connect_with_connectors()
            self.__load_persistent_devices()
            self.__published_events = Queue(0)
            self.__send_thread = Thread(target=self.__read_data_from_storage,
                                        daemon=True,
                                        name="Send data to Thingsboard Thread")
            self.__event_storage = self.__event_storage_types[
                config["storage"]["type"]](config["storage"])
            self.tb_client.connect()
            self.tb_client.client.gw_set_server_side_rpc_request_handler(
                self.__rpc_request_handler)
            self.tb_client.client.set_server_side_rpc_request_handler(
                self.__rpc_request_handler)
            self.tb_client.client.subscribe_to_all_attributes(
                self.__attribute_update_callback)
            self.tb_client.client.gw_subscribe_to_all_attributes(
                self.__attribute_update_callback)
            self.__send_thread.start()

            try:
                gateway_statistic_send = 0
                while True:
                    cur_time = time.time()
                    if self.__rpc_requests_in_progress and self.tb_client.is_connected(
                    ):
                        for rpc_in_progress in self.__rpc_requests_in_progress:
                            if cur_time >= self.__rpc_requests_in_progress[
                                    rpc_in_progress][1]:
                                self.__rpc_requests_in_progress[
                                    rpc_in_progress][2](rpc_in_progress)
                                self.cancel_rpc_request(rpc_in_progress)
                        time.sleep(0.1)
                    else:
                        time.sleep(1)

                    if cur_time - gateway_statistic_send > 60.0 and self.tb_client.is_connected(
                    ):
                        summary_messages = {
                            "eventsProduced": 0,
                            "eventsSent": 0
                        }
                        telemetry = {}
                        for connector in self.available_connectors:
                            if self.available_connectors[
                                    connector].is_connected():
                                connector_camel_case = connector[0].lower(
                                ) + connector[1:].replace(' ', '')
                                telemetry[(connector_camel_case + ' EventsProduced').replace(' ', '')] = \
                                self.available_connectors[connector].statistics['MessagesReceived']
                                telemetry[(connector_camel_case + ' EventsSent').replace(' ', '')] = \
                                self.available_connectors[connector].statistics['MessagesSent']
                                self.tb_client.client.send_telemetry(telemetry)
                                summary_messages[
                                    'eventsProduced'] += telemetry[str(
                                        connector_camel_case +
                                        ' EventsProduced').replace(' ', '')]
                                summary_messages['eventsSent'] += telemetry[
                                    str(connector_camel_case +
                                        ' EventsSent').replace(' ', '')]
                        self.tb_client.client.send_telemetry(summary_messages)
                        gateway_statistic_send = time.time()
            except Exception as e:
                log.exception(e)
                for device in self.__connected_devices:
                    log.debug("Close connection for device %s", device)
                    try:
                        current_connector = self.__connected_devices[
                            device].get("connector")
                        if current_connector is not None:
                            current_connector.close()
                            log.debug("Connector %s closed connection.",
                                      current_connector.get_name())
                    except Exception as e:
                        log.error(e)
コード例 #2
0
class TBGatewayService:
    def __init__(self, config_file=None):
        self.__lock = RLock()
        if config_file is None:
            config_file = path.dirname(path.dirname(path.abspath(__file__))) + '/config/tb_gateway.yaml'.replace('/', path.sep)
        with open(config_file) as general_config:
            config = safe_load(general_config)
        self._config_dir = path.dirname(path.abspath(config_file)) + path.sep
        logging.config.fileConfig(self._config_dir + "logs.conf")
        global log
        log = logging.getLogger('service')
        log.info("Gateway starting...")
        self.available_connectors = {}
        self.__connector_incoming_messages = {}
        self.__connected_devices = {}
        self.__saved_devices = {}
        self.__events = []
        self.name = ''.join(choice(ascii_lowercase) for _ in range(64))
        self.__rpc_requests_in_progress = {}
        self.__connected_devices_file = "connected_devices.json"
        self.tb_client = TBClient(config["thingsboard"])
        self.tb_client.connect()
        self.subscribe_to_required_topics()
        self.counter = 0
        global main_handler
        self.main_handler = main_handler
        self.remote_handler = TBLoggerHandler(self)
        self.main_handler.setTarget(self.remote_handler)
        self._default_connectors = {
            "mqtt": "MqttConnector",
            "modbus": "ModbusConnector",
            "opcua": "OpcUaConnector",
            "ble": "BLEConnector",
            "request": "RequestConnector",
            "can": "CanConnector"
        }
        self._implemented_connectors = {}
        self._event_storage_types = {
            "memory": MemoryEventStorage,
            "file": FileEventStorage,
        }
        self.__gateway_rpc_methods = {
            "ping": self.__rpc_ping,
            "stats": self.__form_statistics,
            "devices": self.__rpc_devices,
        }
        self.__sheduled_rpc_calls = []
        self.__self_rpc_sheduled_methods_functions = {
            "restart": {"function": execv, "arguments": (executable, [executable.split(pathsep)[-1]] + argv)},
            "reboot": {"function": system, "arguments": ("reboot 0",)},
            }
        self._event_storage = self._event_storage_types[config["storage"]["type"]](config["storage"])
        self.connectors_configs = {}
        self._load_connectors(config)
        self._connect_with_connectors()
        self.__remote_configurator = None
        self.__request_config_after_connect = False
        if config["thingsboard"].get("remoteConfiguration"):
            try:
                self.__remote_configurator = RemoteConfigurator(self, config)
            except Exception as e:
                log.exception(e)
        if self.__remote_configurator is not None:
            self.__remote_configurator.send_current_configuration()
        self.__load_persistent_devices()
        self._published_events = Queue(-1)
        self._send_thread = Thread(target=self.__read_data_from_storage, daemon=True,
                                   name="Send data to Thingsboard Thread")
        self._send_thread.start()
        log.info("Gateway started.")

        try:
            gateway_statistic_send = 0
            while True:
                cur_time = time()*1000
                if self.__sheduled_rpc_calls:
                    for rpc_call_index in range(len(self.__sheduled_rpc_calls)):
                        rpc_call = self.__sheduled_rpc_calls[rpc_call_index]
                        if cur_time > rpc_call[0]:
                            rpc_call = self.__sheduled_rpc_calls.pop(rpc_call_index)
                            result = None
                            try:
                                result = rpc_call[1]["function"](*rpc_call[1]["arguments"])
                            except Exception as e:
                                log.exception(e)
                            if result == 256:
                                log.warning("Error on RPC command: 256. Permission denied.")
                if self.__rpc_requests_in_progress and self.tb_client.is_connected():
                    for rpc_in_progress, data in self.__rpc_requests_in_progress.items():
                        if cur_time >= data[1]:
                            data[2](rpc_in_progress)
                            self.cancel_rpc_request(rpc_in_progress)
                            self.__rpc_requests_in_progress[rpc_in_progress] = "del"
                    new_rpc_request_in_progress = {key: value for key, value in self.__rpc_requests_in_progress.items() if value != 'del'}
                    self.__rpc_requests_in_progress = new_rpc_request_in_progress
                else:
                    try:
                        sleep(.1)
                    except Exception as e:
                        log.exception(e)
                        break
                if not self.__request_config_after_connect and \
                        self.tb_client.is_connected() and not self.tb_client.client.get_subscriptions_in_progress():
                    self.__request_config_after_connect = True
                    self.__check_shared_attributes()

                if cur_time - gateway_statistic_send > 5000.0 and self.tb_client.is_connected():
                    summary_messages = self.__form_statistics()
                    # with self.__lock:
                    self.tb_client.client.send_telemetry(summary_messages)
                    gateway_statistic_send = time()*1000
                    # self.__check_shared_attributes()
        except KeyboardInterrupt:
            log.info("Stopping...")
            self.__close_connectors()
            log.info("The gateway has been stopped.")
            self.tb_client.stop()
        except Exception as e:
            log.exception(e)
            self.__close_connectors()
            log.info("The gateway has been stopped.")
            self.tb_client.stop()

    def __close_connectors(self):
        for current_connector in self.available_connectors:
            try:
                self.available_connectors[current_connector].close()
                log.debug("Connector %s closed connection.", current_connector)
            except Exception as e:
                log.exception(e)

    def __stop_gateway(self):
        pass

    def _attributes_parse(self, content, *args):
        try:
            log.debug("Received data: %s", content)
            log.debug(args)
            if content is not None:
                shared_attributes = content.get("shared")
                client_attributes = content.get("client")
                new_configuration = shared_attributes.get("configuration") if shared_attributes is not None and shared_attributes.get("configuration") is not None else content.get("configuration")
                if new_configuration is not None and self.__remote_configurator is not None:
                    try:
                        confirmed = self.__remote_configurator.process_configuration(new_configuration)
                        # if confirmed:
                            # self._send_thread = Thread(target=self.__read_data_from_storage, daemon=True,
                            #                            name="Send data to Thingsboard Thread")
                            # self._send_thread.start()
                        self.__remote_configurator.send_current_configuration()
                    except Exception as e:
                        log.exception(e)
                remote_logging_level = shared_attributes.get('RemoteLoggingLevel') if shared_attributes is not None else content.get("RemoteLoggingLevel")
                if remote_logging_level == 'NONE':
                    self.remote_handler.deactivate()
                    log.info('Remote logging has being deactivated.')
                elif remote_logging_level is not None:
                    if self.remote_handler.current_log_level != remote_logging_level or not self.remote_handler.activated:
                        self.main_handler.setLevel(remote_logging_level)
                        self.remote_handler.activate(remote_logging_level)
                        log.info('Remote logging has being updated. Current logging level is: %s ', remote_logging_level)
                if shared_attributes is not None:
                    log.debug("Shared attributes received (%s).", ", ".join([attr for attr in shared_attributes.keys()]))
                if client_attributes is not None:
                    log.debug("Client attributes received (%s).", ", ".join([attr for attr in client_attributes.keys()]))
        except Exception as e:
            log.exception(e)

    def get_config_path(self):
        return self._config_dir

    def subscribe_to_required_topics(self):
        self.tb_client.client.gw_set_server_side_rpc_request_handler(self._rpc_request_handler)
        self.tb_client.client.set_server_side_rpc_request_handler(self._rpc_request_handler)
        self.tb_client.client.subscribe_to_all_attributes(self._attribute_update_callback)
        self.tb_client.client.gw_subscribe_to_all_attributes(self._attribute_update_callback)

    def __check_shared_attributes(self):
        self.tb_client.client.request_attributes(callback=self._attributes_parse)

    def _load_connectors(self, main_config):
        self.connectors_configs = {}
        if not main_config.get("connectors"):
            raise Exception("Configuration for connectors not found, check your config file.")
        for connector in main_config['connectors']:
            try:
                connector_class = TBUtility.check_and_import(connector["type"], self._default_connectors.get(connector["type"], connector.get("class")))
                self._implemented_connectors[connector["type"]] = connector_class
                with open(self._config_dir + connector['configuration'], 'r') as conf_file:
                    connector_conf = load(conf_file)
                    if not self.connectors_configs.get(connector['type']):
                        self.connectors_configs[connector['type']] = []
                    connector_conf["name"] = connector["name"]
                    self.connectors_configs[connector['type']].append({"name": connector["name"], "config": {connector['configuration']: connector_conf}})
            except Exception as e:
                log.error("Error on loading connector:")
                log.exception(e)

    def _connect_with_connectors(self):
        for connector_type in self.connectors_configs:
            for connector_config in self.connectors_configs[connector_type]:
                for config in connector_config["config"]:
                    connector = None
                    try:
                        connector = self._implemented_connectors[connector_type](self, connector_config["config"][config],
                                                                                 connector_type)
                        connector.setName(connector_config["name"])
                        self.available_connectors[connector.get_name()] = connector
                        connector.open()
                    except Exception as e:
                        log.exception(e)
                        if connector is not None:
                            connector.close()

    def send_to_storage(self, connector_name, data):
        if not connector_name == self.name:
            if not TBUtility.validate_converted_data(data):
                log.error("Data from %s connector is invalid.", connector_name)
                return None
            if data["deviceName"] not in self.get_devices():
                self.add_device(data["deviceName"],
                                {"connector": self.available_connectors[connector_name]}, wait_for_publish=True, device_type=data["deviceType"])
            if not self.__connector_incoming_messages.get(connector_name):
                self.__connector_incoming_messages[connector_name] = 0
            else:
                self.__connector_incoming_messages[connector_name] += 1

        telemetry = {}
        telemetry_with_ts = []
        for item in data["telemetry"]:
            if item.get("ts") is None:
                telemetry = {**telemetry, **item}
            else:
                telemetry_with_ts.append({"ts": item["ts"], "values": {**item["values"]}})
        if telemetry_with_ts:
            data["telemetry"] = telemetry_with_ts
        else:
            data["telemetry"] = {"ts": int(time() * 1000), "values": telemetry}

        json_data = dumps(data)
        save_result = self._event_storage.put(json_data)
        if not save_result:
            log.error('Data from the device "%s" cannot be saved, connector name is %s.',
                      data["deviceName"],
                      connector_name)

    def check_size(self, size, devices_data_in_event_pack):
        if size >= 48000:
            self.__send_data(devices_data_in_event_pack)
            size = 0
        return size

    def __read_data_from_storage(self):
        devices_data_in_event_pack = {}
        log.debug("Send data Thread has been started successfully.")
        while True:
            try:
                if self.tb_client.is_connected():
                    size = getsizeof(devices_data_in_event_pack)
                    events = []
                    if self.__remote_configurator is None or not self.__remote_configurator.in_process:
                        events = self._event_storage.get_event_pack()
                    if events:
                        for event in events:
                            self.counter += 1
                            try:
                                current_event = loads(event)
                            except Exception as e:
                                log.exception(e)
                                continue
                            if not devices_data_in_event_pack.get(current_event["deviceName"]):
                                devices_data_in_event_pack[current_event["deviceName"]] = {"telemetry": [],
                                                                                           "attributes": {}}
                            if current_event.get("telemetry"):
                                if isinstance(current_event["telemetry"], list):
                                    for item in current_event["telemetry"]:
                                        size += getsizeof(item)
                                        size = self.check_size(size, devices_data_in_event_pack)
                                        devices_data_in_event_pack[current_event["deviceName"]]["telemetry"].append(item)
                                else:
                                    if not self.tb_client.is_connected():
                                        break
                                    size += getsizeof(current_event["telemetry"])
                                    size = self.check_size(size, devices_data_in_event_pack)
                                    devices_data_in_event_pack[current_event["deviceName"]]["telemetry"].append(current_event["telemetry"])
                            if current_event.get("attributes"):
                                if isinstance(current_event["attributes"], list):
                                    for item in current_event["attributes"]:
                                        if not self.tb_client.is_connected():
                                            break
                                        size += getsizeof(item)
                                        size = self.check_size(size, devices_data_in_event_pack)
                                        devices_data_in_event_pack[current_event["deviceName"]]["attributes"].update(item.items())
                                else:
                                    if not self.tb_client.is_connected():
                                        break
                                    size += getsizeof(current_event["attributes"].items())
                                    size = self.check_size(size, devices_data_in_event_pack)
                                    devices_data_in_event_pack[current_event["deviceName"]]["attributes"].update(
                                        current_event["attributes"].items())
                        if devices_data_in_event_pack:
                            if not self.tb_client.is_connected():
                                break
                            self.__send_data(devices_data_in_event_pack)
                        if self.tb_client.is_connected() and (self.__remote_configurator is None or not self.__remote_configurator.in_process):
                            success = True
                            while not self._published_events.empty():
                                if (self.__remote_configurator is not None and self.__remote_configurator.in_process) or not self.tb_client.is_connected() or self._published_events.empty():
                                    success = False
                                    break
                                event = self._published_events.get(False, 10)
                                try:
                                    if self.tb_client.is_connected() and (self.__remote_configurator is None or not self.__remote_configurator.in_process):
                                        success = event.get() == event.TB_ERR_SUCCESS
                                    else:
                                        break
                                except Exception as e:
                                    log.exception(e)
                                    success = False
                            if success:
                                self._event_storage.event_pack_processing_done()
                                del devices_data_in_event_pack
                                devices_data_in_event_pack = {}
                        else:
                            continue
                    else:
                        sleep(.01)
                else:
                    sleep(.1)
            except Exception as e:
                log.exception(e)
                sleep(1)

    def __send_data(self, devices_data_in_event_pack):
        try:
            for device in devices_data_in_event_pack:
                if devices_data_in_event_pack[device].get("attributes"):
                    if device == self.name:
                        self._published_events.put(self.tb_client.client.send_attributes(devices_data_in_event_pack[device]["attributes"]))
                    else:
                        self._published_events.put(self.tb_client.client.gw_send_attributes(device, devices_data_in_event_pack[device]["attributes"]))
                if devices_data_in_event_pack[device].get("telemetry"):
                    if device == self.name:
                        self._published_events.put(self.tb_client.client.send_telemetry(devices_data_in_event_pack[device]["telemetry"]))
                    else:
                        self._published_events.put(self.tb_client.client.gw_send_telemetry(device, devices_data_in_event_pack[device]["telemetry"]))
                devices_data_in_event_pack[device] = {"telemetry": [], "attributes": {}}
        except Exception as e:
            log.exception(e)

    def _rpc_request_handler(self, request_id, content):
        try:
            device = content.get("device")
            if device is not None:
                connector_name = self.get_devices()[device].get("connector")
                if connector_name is not None:
                    connector_name.server_side_rpc_handler(content)
                else:
                    log.error("Received RPC request but connector for the device %s not found. Request data: \n %s",
                              content["device"],
                              dumps(content))
            else:
                try:
                    method_split = content["method"].split('_')
                    module = None
                    if len(method_split) > 0:
                        module = method_split[0]
                    if module is not None:
                        result = None
                        if self.connectors_configs.get(module):
                            log.debug("Connector \"%s\" for RPC request \"%s\" found", module, content["method"])
                            for connector_name in self.available_connectors:
                                if self.available_connectors[connector_name]._connector_type == module:
                                    log.debug("Sending command RPC %s to connector %s", content["method"], connector_name)
                                    result = self.available_connectors[connector_name].server_side_rpc_handler(content)
                        elif module == 'gateway':
                            result = self.__rpc_gateway_processing(request_id, content)
                        else:
                            log.error("Connector \"%s\" not found", module)
                            result = {"error": "%s - connector not found in available connectors." % module, "code": 404}
                        if result is None:
                            self.send_rpc_reply(None, request_id, success_sent=False)
                        else:
                            self.send_rpc_reply(None, request_id, dumps(result))
                except Exception as e:
                    self.send_rpc_reply(None, request_id, "{\"error\":\"%s\", \"code\": 500}" % str(e))
                    log.exception(e)
        except Exception as e:
            log.exception(e)

    def __rpc_gateway_processing(self, request_id, content):
        log.info("Received RPC request to the gateway, id: %s, method: %s", str(request_id), content["method"])
        arguments = content.get('params')
        method_to_call = content["method"].replace("gateway_", "")
        result = None
        if isinstance(arguments, list):
            result = self.__gateway_rpc_methods[method_to_call](*arguments)
        elif method_to_call in self.__self_rpc_sheduled_methods_functions:
            seconds_to_restart = arguments*1000 if arguments else 0
            self.__sheduled_rpc_calls.append([time()*1000 + seconds_to_restart, self.__self_rpc_sheduled_methods_functions[method_to_call]])
            log.info("Gateway %s sheduled in %i seconds", method_to_call, seconds_to_restart/1000)
            result = {"success": True}
        elif arguments is not None:
            result = self.__gateway_rpc_methods[method_to_call]()
        else:
            result = self.__gateway_rpc_methods[method_to_call]()
        return result

    def __rpc_ping(self, *args):
        return {"code": 200, "resp": "pong"}

    def __rpc_devices(self, *args):
        data_to_send = {}
        for device in self.__connected_devices:
            if self.__connected_devices[device]["connector"] is not None:
                data_to_send[device] = self.__connected_devices[device]["connector"].get_name()
        return {"code": 200, "resp": data_to_send}

    def rpc_with_reply_processing(self, topic, content):
        req_id = self.__rpc_requests_in_progress[topic][0]["data"]["id"]
        device = self.__rpc_requests_in_progress[topic][0]["device"]
        self.send_rpc_reply(device, req_id, content)
        self.cancel_rpc_request(topic)

    def send_rpc_reply(self, device=None, req_id=None, content=None, success_sent=None, wait_for_publish=None):
        try:
            rpc_response = {"success": False}
            if success_sent is not None:
                if success_sent:
                    rpc_response["success"] = True
            if device is not None and success_sent is not None:
                self.tb_client.client.gw_send_rpc_reply(device, req_id, dumps(rpc_response))
            elif device is not None and req_id is not None and content is not None:
                self.tb_client.client.gw_send_rpc_reply(device, req_id, content)
            elif device is None and success_sent is not None:
                self.tb_client.client.send_rpc_reply(req_id, dumps(rpc_response), quality_of_service=1, wait_for_publish=wait_for_publish)
            elif device is None and content is not None:
                self.tb_client.client.send_rpc_reply(req_id, content, quality_of_service=1, wait_for_publish=wait_for_publish)
        except Exception as e:
            log.exception(e)

    def register_rpc_request_timeout(self, content, timeout, topic, cancel_method):
        self.__rpc_requests_in_progress[topic] = (content, timeout, cancel_method)

    def cancel_rpc_request(self, rpc_request):
        content = self.__rpc_requests_in_progress[rpc_request][0]
        self.send_rpc_reply(device=content["device"], req_id=content["data"]["id"], success_sent=False)

    def _attribute_update_callback(self, content, *args):
        log.debug("Attribute request received with content: \"%s\"", content)
        log.debug(args)
        if content.get('device') is not None:
            try:
                self.__connected_devices[content["device"]]["connector"].on_attributes_update(content)
            except Exception as e:
                log.exception(e)
        else:
            self._attributes_parse(content)

    def __form_statistics(self):
        summary_messages = {"eventsProduced": 0, "eventsSent": 0}
        telemetry = {}
        for connector in self.available_connectors:
            connector_camel_case = connector.lower().replace(' ', '')
            telemetry[(connector_camel_case + ' EventsProduced').replace(' ', '')] = \
                self.available_connectors[connector].statistics['MessagesReceived']
            self.available_connectors[connector].statistics['MessagesReceived'] = 0
            telemetry[(connector_camel_case + ' EventsSent').replace(' ', '')] = \
                self.available_connectors[connector].statistics['MessagesSent']
            self.available_connectors[connector].statistics['MessagesSent'] = 0
            summary_messages['eventsProduced'] += telemetry[
                str(connector_camel_case + ' EventsProduced').replace(' ', '')]
            summary_messages['eventsSent'] += telemetry[
                str(connector_camel_case + ' EventsSent').replace(' ', '')]
            summary_messages.update(**telemetry)
        return summary_messages

    def add_device(self, device_name, content, wait_for_publish=False, device_type=None):
        if device_name not in self.__saved_devices:
            self.__connected_devices[device_name] = content
            self.__saved_devices[device_name] = content
            device_type = device_type if device_type is not None else 'default'
            if wait_for_publish:
                self.tb_client.client.gw_connect_device(device_name, device_type).wait_for_publish()
            else:
                self.tb_client.client.gw_connect_device(device_name, device_type)
            self.__save_persistent_devices()

    def update_device(self, device_name, event, content):
        if event == 'connector' and self.__connected_devices[device_name].get(event) != content:
            self.__save_persistent_devices()
        self.__connected_devices[device_name][event] = content

    def del_device(self, device_name):
        del self.__connected_devices[device_name]
        self.tb_client.client.gw_disconnect_device(device_name)
        self.__save_persistent_devices()

    def get_devices(self):
        return self.__connected_devices

    def __load_persistent_devices(self):
        devices = {}
        if self.__connected_devices_file in listdir(self._config_dir) and \
                path.getsize(self._config_dir + self.__connected_devices_file) > 0:
            try:
                with open(self._config_dir + self.__connected_devices_file) as devices_file:
                    devices = load(devices_file)
            except Exception as e:
                log.exception(e)
        else:
            connected_devices_file = open(self._config_dir + self.__connected_devices_file, 'w')
            connected_devices_file.close()

        if devices is not None:
            log.debug("Loaded devices:\n %s", devices)
            for device_name in devices:
                try:
                    if self.available_connectors.get(devices[device_name]):
                        self.__connected_devices[device_name] = {
                            "connector": self.available_connectors[devices[device_name]]}
                    else:
                        log.warning("Device %s connector not found, maybe it had been disabled.", device_name)
                except Exception as e:
                    log.exception(e)
                    continue
        else:
            log.debug("No device found in connected device file.")
            self.__connected_devices = {} if self.__connected_devices is None else self.__connected_devices

    def __save_persistent_devices(self):
        with open(self._config_dir + self.__connected_devices_file, 'w') as config_file:
            try:
                data_to_save = {}
                for device in self.__connected_devices:
                    if self.__connected_devices[device]["connector"] is not None:
                        data_to_save[device] = self.__connected_devices[device]["connector"].get_name()
                config_file.write(dumps(data_to_save, indent=2, sort_keys=True))
            except Exception as e:
                log.exception(e)
        log.debug("Saved connected devices.")
コード例 #3
0
class TBGatewayService:
    def __init__(self, config_file=None):
        if config_file is None:
            config_file = path.dirname(path.dirname(
                path.abspath(__file__))) + '/config/tb_gateway.yaml'
        with open(config_file) as config:
            config = safe_load(config)
            self.__config_dir = path.dirname(path.abspath(config_file)) + '/'
            logging.config.fileConfig(self.__config_dir + "logs.conf")
            global log
            log = logging.getLogger('service')
            self.available_connectors = {}
            self.__connector_incoming_messages = {}
            self.__connected_devices = {}
            self.__saved_devices = {}
            self.__events = []
            self.__rpc_requests_in_progress = {}
            self.__connected_devices_file = "connected_devices.json"
            self.tb_client = TBClient(config["thingsboard"])
            self.main_handler = logging.handlers.MemoryHandler(1000)
            self.remote_handler = TBLoggerHandler(self)
            self.main_handler.setTarget(self.remote_handler)
            self.__default_connectors = {
                "mqtt": "MqttConnector",
                "modbus": "ModbusConnector",
                "opcua": "OpcUaConnector",
                "ble": "BLEConnector",
            }
            self.__implemented_connectors = {}
            self.__event_storage_types = {
                "memory": MemoryEventStorage,
                "file": FileEventStorage,
            }
            self.__load_connectors(config)
            self.__connect_with_connectors()
            self.__load_persistent_devices()
            self.__published_events = Queue(0)
            self.__send_thread = Thread(target=self.__read_data_from_storage,
                                        daemon=True,
                                        name="Send data to Thingsboard Thread")
            self.__event_storage = self.__event_storage_types[
                config["storage"]["type"]](config["storage"])
            self.tb_client.connect()
            self.tb_client.client.gw_set_server_side_rpc_request_handler(
                self.__rpc_request_handler)
            self.tb_client.client.set_server_side_rpc_request_handler(
                self.__rpc_request_handler)
            self.tb_client.client.subscribe_to_all_attributes(
                self.__attribute_update_callback)
            self.tb_client.client.gw_subscribe_to_all_attributes(
                self.__attribute_update_callback)
            self.__send_thread.start()

            try:
                gateway_statistic_send = 0
                while True:
                    cur_time = time.time()
                    if self.__rpc_requests_in_progress and self.tb_client.is_connected(
                    ):
                        for rpc_in_progress in self.__rpc_requests_in_progress:
                            if cur_time >= self.__rpc_requests_in_progress[
                                    rpc_in_progress][1]:
                                self.__rpc_requests_in_progress[
                                    rpc_in_progress][2](rpc_in_progress)
                                self.cancel_rpc_request(rpc_in_progress)
                        time.sleep(0.1)
                    else:
                        time.sleep(1)

                    if cur_time - gateway_statistic_send > 60.0 and self.tb_client.is_connected(
                    ):
                        summary_messages = {
                            "eventsProduced": 0,
                            "eventsSent": 0
                        }
                        telemetry = {}
                        for connector in self.available_connectors:
                            if self.available_connectors[
                                    connector].is_connected():
                                connector_camel_case = connector[0].lower(
                                ) + connector[1:].replace(' ', '')
                                telemetry[(connector_camel_case + ' EventsProduced').replace(' ', '')] = \
                                self.available_connectors[connector].statistics['MessagesReceived']
                                telemetry[(connector_camel_case + ' EventsSent').replace(' ', '')] = \
                                self.available_connectors[connector].statistics['MessagesSent']
                                self.tb_client.client.send_telemetry(telemetry)
                                summary_messages[
                                    'eventsProduced'] += telemetry[str(
                                        connector_camel_case +
                                        ' EventsProduced').replace(' ', '')]
                                summary_messages['eventsSent'] += telemetry[
                                    str(connector_camel_case +
                                        ' EventsSent').replace(' ', '')]
                        self.tb_client.client.send_telemetry(summary_messages)
                        gateway_statistic_send = time.time()
            except Exception as e:
                log.exception(e)
                for device in self.__connected_devices:
                    log.debug("Close connection for device %s", device)
                    try:
                        current_connector = self.__connected_devices[
                            device].get("connector")
                        if current_connector is not None:
                            current_connector.close()
                            log.debug("Connector %s closed connection.",
                                      current_connector.get_name())
                    except Exception as e:
                        log.error(e)

    def get_config_path(self):
        return self.__config_dir

    def __load_connectors(self, config):
        self._connectors_configs = {}
        if not config.get("connectors"):
            raise Exception(
                "Configuration for connectors not found, check your config file."
            )
        for connector in config['connectors']:
            try:
                if connector.get('class') is not None:
                    try:
                        connector_class = TBUtility.check_and_import(
                            connector['type'], connector['class'])
                        self.__implemented_connectors[
                            connector['type']] = connector_class
                    except Exception as e:
                        log.error(
                            "Exception when loading the custom connector:")
                        log.exception(e)
                elif connector.get("type") is not None and connector[
                        "type"] in self.__default_connectors:
                    try:
                        connector_class = TBUtility.check_and_import(
                            connector["type"],
                            self.__default_connectors[connector["type"]],
                            default=True)
                        self.__implemented_connectors[
                            connector["type"]] = connector_class
                    except Exception as e:
                        log.error("Error on loading default connector:")
                        log.exception(e)
                else:
                    log.error("Connector with config %s - not found",
                              safe_dump(connector))

                with open(self.__config_dir + connector['configuration'],
                          'r') as conf_file:
                    connector_conf = load(conf_file)
                    if not self._connectors_configs.get(connector['type']):
                        self._connectors_configs[connector['type']] = []
                    self._connectors_configs[connector['type']].append(
                        {connector['configuration']: connector_conf})
            except Exception as e:
                log.error(e)

    def __connect_with_connectors(self):
        for connector_type in self._connectors_configs:
            for connector_config in self._connectors_configs[connector_type]:
                for config_file in connector_config:
                    connector = None
                    try:
                        connector = self.__implemented_connectors[
                            connector_type](self,
                                            connector_config[config_file],
                                            connector_type)
                        self.available_connectors[
                            connector.get_name()] = connector
                        connector.open()
                    except Exception as e:
                        log.exception(e)
                        if connector is not None:
                            connector.close()

    def __send_statistic(self):
        self.tb_client.client.gw_send_telemetry()

    def send_to_storage(self, connector_name, data):
        self._send_to_storage(connector_name, data)

    def _send_to_storage(self, connector_name, data):
        if not TBUtility.validate_converted_data(data):
            log.error("Data from %s connector is invalid.", connector_name)
            return
        if data["deviceName"] not in self.get_devices():
            self.add_device(
                data["deviceName"],
                {"connector": self.available_connectors[connector_name]},
                wait_for_publish=True)
        if not self.__connector_incoming_messages.get(connector_name):
            self.__connector_incoming_messages[connector_name] = 0
        else:
            self.__connector_incoming_messages[connector_name] += 1

        telemetry = {}
        for item in data["telemetry"]:
            telemetry = {**telemetry, **item}
        data["telemetry"] = {
            "ts": int(time.time() * 1000),
            "values": telemetry
        }

        json_data = dumps(data)
        save_result = self.__event_storage.put(json_data)
        if not save_result:
            log.error(
                'Data from device "%s" cannot be saved, connector name is %s.',
                data["deviceName"], connector_name)

    def __read_data_from_storage(self):
        devices_data_in_event_pack = {}
        while True:
            try:
                if self.tb_client.is_connected():
                    size = getsizeof(devices_data_in_event_pack)
                    events = self.__event_storage.get_event_pack()
                    if events:
                        for event in events:
                            try:
                                current_event = loads(event)
                            except Exception as e:
                                log.exception(e)
                                continue
                            if not devices_data_in_event_pack.get(
                                    current_event["deviceName"]):
                                devices_data_in_event_pack[
                                    current_event["deviceName"]] = {
                                        "telemetry": [],
                                        "attributes": {}
                                    }

                            if current_event.get("telemetry"):
                                if type(current_event["telemetry"]) == list:
                                    for item in current_event["telemetry"]:
                                        size += getsizeof(item)
                                        if size >= 48000:
                                            if not self.tb_client.is_connected(
                                            ):
                                                break
                                            self.__send_data(
                                                devices_data_in_event_pack)
                                            size = 0
                                        devices_data_in_event_pack[
                                            current_event["deviceName"]][
                                                "telemetry"].append(item)
                                else:
                                    if not self.tb_client.is_connected(): break
                                    size += getsizeof(
                                        current_event["telemetry"])
                                    if size >= 48000:
                                        self.__send_data(
                                            devices_data_in_event_pack)
                                        size = 0
                                    devices_data_in_event_pack[current_event[
                                        "deviceName"]]["telemetry"].append(
                                            current_event["telemetry"])
                            if current_event.get("attributes"):
                                if type(current_event["attributes"]) == list:
                                    for item in current_event["attributes"]:
                                        if not self.tb_client.is_connected():
                                            break
                                        size += getsizeof(item)
                                        if size >= 48000:
                                            self.__send_data(
                                                devices_data_in_event_pack)
                                            size = 0
                                        devices_data_in_event_pack[
                                            current_event["deviceName"]][
                                                "attributes"].update(
                                                    item.items())
                                else:
                                    if not self.tb_client.is_connected(): break
                                    size += getsizeof(
                                        current_event["attributes"].items())
                                    if size >= 48000:
                                        self.__send_data(
                                            devices_data_in_event_pack)
                                        size = 0
                                    devices_data_in_event_pack[current_event[
                                        "deviceName"]]["attributes"].update(
                                            current_event["attributes"].items(
                                            ))
                        if devices_data_in_event_pack:
                            if not self.tb_client.is_connected(): break
                            self.__send_data(devices_data_in_event_pack)

                        if self.tb_client.is_connected():
                            success = True
                            while not self.__published_events.empty():
                                event = self.__published_events.get()
                                try:
                                    success = event.get(
                                    ) == event.TB_ERR_SUCCESS
                                except Exception as e:
                                    log.exception(e)
                                    success = False
                            if success:
                                self.__event_storage.event_pack_processing_done(
                                )
                                del devices_data_in_event_pack
                                devices_data_in_event_pack = {}
                        else:
                            break
                    else:
                        time.sleep(.01)
                else:
                    time.sleep(.1)
            except Exception as e:
                log.exception(e)
                time.sleep(1)

    def __send_data(self, devices_data_in_event_pack):
        for device in devices_data_in_event_pack:
            self.__published_events.put(
                self.tb_client.client.gw_send_attributes(
                    device, devices_data_in_event_pack[device]["attributes"]))
            self.__published_events.put(
                self.tb_client.client.gw_send_telemetry(
                    device, devices_data_in_event_pack[device]["telemetry"]))
            devices_data_in_event_pack[device] = {
                "telemetry": [],
                "attributes": {}
            }

    def __rpc_request_handler(self, _, content):
        try:
            device = content.get("device")
            if device is not None:
                connector = self.get_devices()[device].get("connector")
                if connector is not None:
                    connector.server_side_rpc_handler(content)
                else:
                    log.error(
                        "Received RPC request but connector for device %s not found. Request data: \n %s",
                        content["device"], dumps(content))
            else:
                log.debug("RPC request with no device param.")
                log.debug(content)
        except Exception as e:
            log.exception(e)

    def rpc_with_reply_processing(self, topic, content):
        req_id = self.__rpc_requests_in_progress[topic][0]["data"]["id"]
        device = self.__rpc_requests_in_progress[topic][0]["device"]
        self.send_rpc_reply(device, req_id, content)
        self.cancel_rpc_request(topic)

    def send_rpc_reply(self, device, req_id, content):
        self.tb_client.client.gw_send_rpc_reply(device, req_id, content)

    def register_rpc_request_timeout(self, content, timeout, topic,
                                     cancel_method):
        self.__rpc_requests_in_progress[topic] = (content, timeout,
                                                  cancel_method)

    def cancel_rpc_request(self, rpc_request):
        del self.__rpc_requests_in_progress[rpc_request]

    def __attribute_update_callback(self, content, *args):
        if content.get('device') is not None:
            try:
                self.__connected_devices[content["device"]][
                    "connector"].on_attributes_update(content)
            except Exception as e:
                log.error(e)
        else:
            if content.get('RemoteLoggingLevel') == 'NONE':
                self.remote_handler.deactivate()
                log.info('Remote logging has being deactivated.')
            elif content.get('RemoteLoggingLevel') is not None:
                self.remote_handler.activate(content.get('RemoteLoggingLevel'))
                log.info('Remote logging has being activated.')
            else:
                log.debug('Attributes on the gateway has being updated!')
                log.debug(args)
                log.debug(content)

    def add_device(self, device_name, content, wait_for_publish=False):
        if device_name not in self.__saved_devices:
            self.__connected_devices[device_name] = content
            self.__saved_devices[device_name] = content
            if wait_for_publish:
                self.tb_client.client.gw_connect_device(
                    device_name).wait_for_publish()
            else:
                self.tb_client.client.gw_connect_device(device_name)
            self.__save_persistent_devices()

    def update_device(self, device_name, event, content):
        if event == 'connector' and self.__connected_devices[device_name].get(
                event) != content:
            self.__save_persistent_devices()
        self.__connected_devices[device_name][event] = content

    def del_device(self, device_name):
        del self.__connected_devices[device_name]
        self.tb_client.client.gw_disconnect_device(device_name)
        self.__save_persistent_devices()

    def get_devices(self):
        return self.__connected_devices

    def __load_persistent_devices(self):
        devices = {}
        if self.__connected_devices_file in listdir(self.__config_dir) and \
                path.getsize(self.__config_dir + self.__connected_devices_file) > 0:
            try:
                with open(self.__config_dir +
                          self.__connected_devices_file) as devices_file:
                    devices = load(devices_file)
            except Exception as e:
                log.exception(e)
        else:
            connected_devices_file = open(
                self.__config_dir + self.__connected_devices_file, 'w')
            connected_devices_file.close()

        if devices is not None:
            log.debug("Loaded devices:\n %s", devices)
            for device_name in devices:
                try:
                    if self.available_connectors.get(devices[device_name]):
                        self.__connected_devices[device_name] = {
                            "connector":
                            self.available_connectors[devices[device_name]]
                        }
                    else:
                        log.warning(
                            "Device %s connector not found, maybe it had been disabled.",
                            device_name)
                except Exception as e:
                    log.exception(e)
                    continue
        else:
            log.debug("No device found in connected device file.")
            self.__connected_devices = {} if self.__connected_devices is None else self.__connected_devices

    def __save_persistent_devices(self):
        with open(self.__config_dir + self.__connected_devices_file,
                  'w') as config_file:
            try:
                data_to_save = {}
                for device in self.__connected_devices:
                    if self.__connected_devices[device][
                            "connector"] is not None:
                        data_to_save[device] = self.__connected_devices[
                            device]["connector"].get_name()
                config_file.write(dumps(data_to_save, indent=2,
                                        sort_keys=True))
            except Exception as e:
                log.exception(e)
        log.debug("Saved connected devices.")
コード例 #4
0
    def __init__(self, config_file=None):
        self.stopped = False
        self.__lock = RLock()
        if config_file is None:
            config_file = path.dirname(path.dirname(path.abspath(__file__))) + '/config/tb_gateway.yaml'.replace('/', path.sep)
        with open(config_file) as general_config:
            config = safe_load(general_config)
        self._config_dir = path.dirname(path.abspath(config_file)) + path.sep
        logging_error = None
        try:
            logging.config.fileConfig(self._config_dir + "logs.conf", disable_existing_loggers=False)
        except Exception as e:
            logging_error = e
        global log
        log = logging.getLogger('service')
        log.info("Gateway starting...")
        self.__updater = TBUpdater()
        self.__updates_check_period_ms = 300000
        self.__updates_check_time = 0
        self.version = self.__updater.get_version()
        log.info("ThingsBoard IoT gateway version: %s", self.version["current_version"])
        self.available_connectors = {}
        self.__connector_incoming_messages = {}
        self.__connected_devices = {}
        self.__saved_devices = {}
        self.__events = []
        self.name = ''.join(choice(ascii_lowercase) for _ in range(64))
        self.__rpc_register_queue = Queue(-1)
        self.__rpc_requests_in_progress = {}
        self.__connected_devices_file = "connected_devices.json"
        self.tb_client = TBClient(config["thingsboard"])
        self.tb_client.connect()
        self.subscribe_to_required_topics()
        self.__subscribed_to_rpc_topics = True
        if logging_error is not None:
            self.tb_client.client.send_telemetry({"ts": time()*1000, "values": {"LOGS": "Logging loading exception, logs.conf is wrong: %s" % (str(logging_error), )}})
            TBLoggerHandler.set_default_handler()
        self.counter = 0
        self.__rpc_reply_sent = False
        global main_handler
        self.main_handler = main_handler
        self.remote_handler = TBLoggerHandler(self)
        self.main_handler.setTarget(self.remote_handler)
        self._default_connectors = DEFAULT_CONNECTORS
        self._implemented_connectors = {}
        self._event_storage_types = {
            "memory": MemoryEventStorage,
            "file": FileEventStorage,
        }
        self.__gateway_rpc_methods = {
            "ping": self.__rpc_ping,
            "stats": self.__form_statistics,
            "devices": self.__rpc_devices,
            "update": self.__rpc_update,
            "version": self.__rpc_version,
        }
        self.__sheduled_rpc_calls = []
        self.__self_rpc_sheduled_methods_functions = {
            "restart": {"function": execv, "arguments": (executable, [executable.split(pathsep)[-1]] + argv)},
            "reboot": {"function": system, "arguments": ("reboot 0",)},
            }
        self._event_storage = self._event_storage_types[config["storage"]["type"]](config["storage"])
        self.connectors_configs = {}
        self._load_connectors(config)
        self._connect_with_connectors()
        self.__remote_configurator = None
        self.__request_config_after_connect = False
        if config["thingsboard"].get("remoteConfiguration"):
            try:
                self.__remote_configurator = RemoteConfigurator(self, config)
            except Exception as e:
                log.exception(e)
        if self.__remote_configurator is not None:
            self.__remote_configurator.send_current_configuration()
        self.__load_persistent_devices()
        self._published_events = Queue(-1)
        self._send_thread = Thread(target=self.__read_data_from_storage, daemon=True,
                                   name="Send data to Thingsboard Thread")
        self._send_thread.start()
        log.info("Gateway started.")

        try:
            gateway_statistic_send = 0
            while not self.stopped:
                cur_time = time()*1000
                if not self.tb_client.is_connected() and self.__subscribed_to_rpc_topics:
                    self.__subscribed_to_rpc_topics = False
                if self.tb_client.is_connected() and not self.__subscribed_to_rpc_topics:
                    for device in self.__saved_devices:
                        self.add_device(device, {"connector": self.__saved_devices[device]["connector"]}, True, device_type=self.__saved_devices[device]["device_type"])
                    self.subscribe_to_required_topics()
                    self.__subscribed_to_rpc_topics = True
                if self.__sheduled_rpc_calls:
                    for rpc_call_index in range(len(self.__sheduled_rpc_calls)):
                        rpc_call = self.__sheduled_rpc_calls[rpc_call_index]
                        if cur_time > rpc_call[0]:
                            rpc_call = self.__sheduled_rpc_calls.pop(rpc_call_index)
                            result = None
                            try:
                                result = rpc_call[1]["function"](*rpc_call[1]["arguments"])
                            except Exception as e:
                                log.exception(e)
                            if result == 256:
                                log.warning("Error on RPC command: 256. Permission denied.")
                if (self.__rpc_requests_in_progress or not self.__rpc_register_queue.empty()) and self.tb_client.is_connected():
                    new_rpc_request_in_progress = {}
                    if self.__rpc_requests_in_progress:
                        for rpc_in_progress, data in self.__rpc_requests_in_progress.items():
                            if cur_time >= data[1]:
                                data[2](rpc_in_progress)
                                self.cancel_rpc_request(rpc_in_progress)
                                self.__rpc_requests_in_progress[rpc_in_progress] = "del"
                        new_rpc_request_in_progress = {key: value for key, value in self.__rpc_requests_in_progress.items() if value != 'del'}
                    if not self.__rpc_register_queue.empty():
                        rpc_request_from_queue = self.__rpc_register_queue.get(False)
                        topic = rpc_request_from_queue["topic"]
                        data = rpc_request_from_queue["data"]
                        new_rpc_request_in_progress[topic] = data
                    self.__rpc_requests_in_progress = new_rpc_request_in_progress
                else:
                    try:
                        sleep(.1)
                    except Exception as e:
                        log.exception(e)
                        break
                if not self.__request_config_after_connect and self.tb_client.is_connected() and not self.tb_client.client.get_subscriptions_in_progress():
                    self.__request_config_after_connect = True
                    self.__check_shared_attributes()

                if cur_time - gateway_statistic_send > 5000.0 and self.tb_client.is_connected():
                    summary_messages = self.__form_statistics()
                    # with self.__lock:
                    self.tb_client.client.send_telemetry(summary_messages)
                    gateway_statistic_send = time()*1000
                    # self.__check_shared_attributes()

                if cur_time - self.__updates_check_time >= self.__updates_check_period_ms:
                    self.__updates_check_time = time()*1000
                    self.version = self.__updater.get_version()
        except KeyboardInterrupt:
            self.__stop_gateway()
        except Exception as e:
            log.exception(e)
            self.__close_connectors()
            log.info("The gateway has been stopped.")
            self.tb_client.stop()
コード例 #5
0
    def __init__(self, config_file=None):
        self.__lock = RLock()
        if config_file is None:
            config_file = path.dirname(path.dirname(path.abspath(__file__))) + '/config/tb_gateway.yaml'.replace('/', path.sep)
        with open(config_file) as general_config:
            config = safe_load(general_config)
        self._config_dir = path.dirname(path.abspath(config_file)) + path.sep
        logging.config.fileConfig(self._config_dir + "logs.conf")
        global log
        log = logging.getLogger('service')
        log.info("Gateway starting...")
        self.available_connectors = {}
        self.__connector_incoming_messages = {}
        self.__connected_devices = {}
        self.__saved_devices = {}
        self.__events = []
        self.name = ''.join(choice(ascii_lowercase) for _ in range(64))
        self.__rpc_requests_in_progress = {}
        self.__connected_devices_file = "connected_devices.json"
        self.tb_client = TBClient(config["thingsboard"])
        self.tb_client.connect()
        self.subscribe_to_required_topics()
        self.counter = 0
        global main_handler
        self.main_handler = main_handler
        self.remote_handler = TBLoggerHandler(self)
        self.main_handler.setTarget(self.remote_handler)
        self._default_connectors = {
            "mqtt": "MqttConnector",
            "modbus": "ModbusConnector",
            "opcua": "OpcUaConnector",
            "ble": "BLEConnector",
            "request": "RequestConnector",
        }
        self._implemented_connectors = {}
        self._event_storage_types = {
            "memory": MemoryEventStorage,
            "file": FileEventStorage,
        }
        self.__gateway_rpc_methods = {
            "ping": self.__rpc_ping,
            "stats": self.__form_statistics,
            "devices": self.__rpc_devices,
        }
        self.__sheduled_rpc_calls = []
        self.__self_rpc_sheduled_methods_functions = {
            "restart": {"function": execv, "arguments": (executable, [executable.split(pathsep)[-1]] + argv)},
            "reboot": {"function": system, "arguments": ("reboot 0",)},
            }
        self._event_storage = self._event_storage_types[config["storage"]["type"]](config["storage"])
        self.connectors_configs = {}
        self._load_connectors(config)
        self._connect_with_connectors()
        self.__remote_configurator = None
        self.__request_config_after_connect = False
        if config["thingsboard"].get("remoteConfiguration"):
            try:
                self.__remote_configurator = RemoteConfigurator(self, config)
            except Exception as e:
                log.exception(e)
        if self.__remote_configurator is not None:
            self.__remote_configurator.send_current_configuration()
        self.__load_persistent_devices()
        self._published_events = Queue(-1)
        self._send_thread = Thread(target=self.__read_data_from_storage, daemon=True,
                                   name="Send data to Thingsboard Thread")
        self._send_thread.start()
        log.info("Gateway started.")

        try:
            gateway_statistic_send = 0
            while True:
                cur_time = time.time()*1000
                if self.__sheduled_rpc_calls:
                    for rpc_call_index in range(len(self.__sheduled_rpc_calls)):
                        rpc_call = self.__sheduled_rpc_calls[rpc_call_index]
                        if cur_time > rpc_call[0]:
                            rpc_call = self.__sheduled_rpc_calls.pop(rpc_call_index)
                            result = None
                            try:
                                result = rpc_call[1]["function"](*rpc_call[1]["arguments"])
                            except Exception as e:
                                log.exception(e)
                            if result == 256:
                                log.warning("Error on RPC command: 256. Permission denied.")
                if self.__rpc_requests_in_progress and self.tb_client.is_connected():
                    for rpc_in_progress, data in self.__rpc_requests_in_progress.items():
                        if cur_time >= data[1]:
                            data[2](rpc_in_progress)
                            self.cancel_rpc_request(rpc_in_progress)
                            self.__rpc_requests_in_progress[rpc_in_progress] = "del"
                    new_rpc_request_in_progress = {key: value for key, value in self.__rpc_requests_in_progress.items() if value != 'del'}
                    self.__rpc_requests_in_progress = new_rpc_request_in_progress
                else:
                    try:
                        time.sleep(1)
                    except Exception as e:
                        log.exception(e)
                        break
                if not self.__request_config_after_connect and \
                        self.tb_client.is_connected() and not self.tb_client.client.get_subscriptions_in_progress():
                    self.__request_config_after_connect = True
                    self.__check_shared_attributes()

                if cur_time - gateway_statistic_send > 5000.0 and self.tb_client.is_connected():
                    summary_messages = self.__form_statistics()
                    # with self.__lock:
                    self.tb_client.client.send_telemetry(summary_messages)
                    gateway_statistic_send = time.time()*1000
                    # self.__check_shared_attributes()
        except KeyboardInterrupt:
            log.info("Stopping...")
            self.__close_connectors()
            log.info("The gateway has been stopped.")
            self.tb_client.stop()
        except Exception as e:
            log.exception(e)
            self.__close_connectors()
            log.info("The gateway has been stopped.")
            self.tb_client.stop()
コード例 #6
0
    def __init__(self, config_file=None):
        if config_file is None:
            config_file = path.dirname(path.dirname(
                path.abspath(__file__))) + '/config/tb_gateway.yaml'.replace(
                    '/', path.sep)
        with open(config_file) as general_config:
            config = safe_load(general_config)
        self._config_dir = path.dirname(path.abspath(config_file)) + path.sep
        logging.config.fileConfig(self._config_dir + "logs.conf")
        global log
        log = logging.getLogger('service')
        self.available_connectors = {}
        self.__connector_incoming_messages = {}
        self.__connected_devices = {}
        self.__saved_devices = {}
        self.__events = []
        self.name = ''.join(choice(ascii_lowercase) for _ in range(64))
        self.__rpc_requests_in_progress = {}
        self.__connected_devices_file = "connected_devices.json"
        self.tb_client = TBClient(config["thingsboard"])
        self.tb_client.connect()
        self.subscribe_to_required_topics()
        global main_handler
        self.main_handler = main_handler
        self.remote_handler = TBLoggerHandler(self)
        self.main_handler.setTarget(self.remote_handler)
        self._default_connectors = {
            "mqtt": "MqttConnector",
            "modbus": "ModbusConnector",
            "opcua": "OpcUaConnector",
            "ble": "BLEConnector",
        }
        self._implemented_connectors = {}
        self._event_storage_types = {
            "memory": MemoryEventStorage,
            "file": FileEventStorage,
        }
        self._event_storage = self._event_storage_types[
            config["storage"]["type"]](config["storage"])
        self.connectors_configs = {}
        self._load_connectors(config)
        self._connect_with_connectors()
        self.__remote_configurator = None
        self.__request_config_after_connect = False
        if config["thingsboard"].get("remoteConfiguration"):
            try:
                self.__remote_configurator = RemoteConfigurator(self, config)
            except Exception as e:
                log.exception(e)
        if self.__remote_configurator is not None:
            self.__remote_configurator.send_current_configuration()
        self.__load_persistent_devices()
        self.__published_events = Queue(0)
        self.__send_thread = Thread(target=self.__read_data_from_storage,
                                    daemon=True,
                                    name="Send data to Thingsboard Thread")
        self.__send_thread.start()

        try:
            gateway_statistic_send = 0
            while True:
                cur_time = time.time() * 1000
                if self.__rpc_requests_in_progress and self.tb_client.is_connected(
                ):
                    for rpc_in_progress, data in self.__rpc_requests_in_progress.items(
                    ):
                        if cur_time >= data[1]:
                            data[2](rpc_in_progress)
                            self.cancel_rpc_request(rpc_in_progress)
                            self.__rpc_requests_in_progress[
                                rpc_in_progress] = "del"
                    new_rpc_request_in_progress = {
                        key: value
                        for key, value in
                        self.__rpc_requests_in_progress.items()
                        if value != 'del'
                    }
                    self.__rpc_requests_in_progress = new_rpc_request_in_progress

                else:
                    try:
                        time.sleep(1)
                    except Exception as e:
                        log.exception(e)
                        break
                if not self.__request_config_after_connect and \
                        self.tb_client.is_connected() and not self.tb_client.client.get_subscriptions_in_progress():
                    self.__request_config_after_connect = True
                    self.__check_shared_attributes()

                if cur_time - gateway_statistic_send > 60000.0 and self.tb_client.is_connected(
                ):
                    summary_messages = self.__form_statistics()
                    self.tb_client.client.send_telemetry(summary_messages)
                    gateway_statistic_send = time.time() * 1000
                    # self.__check_shared_attributes()
        except KeyboardInterrupt:
            log.info("Stopping...")
            self.__close_connectors()
            log.info("The gateway has been stopped.")
            self.tb_client.stop()
        except Exception as e:
            log.exception(e)
            self.__close_connectors()
            log.info("The gateway has been stopped.")
            self.tb_client.stop()
コード例 #7
0
class TBGatewayService:
    def __init__(self, config_file=None):
        if config_file is None:
            config_file = path.dirname(path.dirname(
                path.abspath(__file__))) + '/config/tb_gateway.yaml'.replace(
                    '/', path.sep)
        with open(config_file) as general_config:
            config = safe_load(general_config)
        self._config_dir = path.dirname(path.abspath(config_file)) + path.sep
        logging.config.fileConfig(self._config_dir + "logs.conf")
        global log
        log = logging.getLogger('service')
        self.available_connectors = {}
        self.__connector_incoming_messages = {}
        self.__connected_devices = {}
        self.__saved_devices = {}
        self.__events = []
        self.name = ''.join(choice(ascii_lowercase) for _ in range(64))
        self.__rpc_requests_in_progress = {}
        self.__connected_devices_file = "connected_devices.json"
        self.tb_client = TBClient(config["thingsboard"])
        self.tb_client.connect()
        self.subscribe_to_required_topics()
        global main_handler
        self.main_handler = main_handler
        self.remote_handler = TBLoggerHandler(self)
        self.main_handler.setTarget(self.remote_handler)
        self._default_connectors = {
            "mqtt": "MqttConnector",
            "modbus": "ModbusConnector",
            "opcua": "OpcUaConnector",
            "ble": "BLEConnector",
        }
        self._implemented_connectors = {}
        self._event_storage_types = {
            "memory": MemoryEventStorage,
            "file": FileEventStorage,
        }
        self._event_storage = self._event_storage_types[
            config["storage"]["type"]](config["storage"])
        self.connectors_configs = {}
        self._load_connectors(config)
        self._connect_with_connectors()
        self.__remote_configurator = None
        self.__request_config_after_connect = False
        if config["thingsboard"].get("remoteConfiguration"):
            try:
                self.__remote_configurator = RemoteConfigurator(self, config)
            except Exception as e:
                log.exception(e)
        if self.__remote_configurator is not None:
            self.__remote_configurator.send_current_configuration()
        self.__load_persistent_devices()
        self.__published_events = Queue(0)
        self.__send_thread = Thread(target=self.__read_data_from_storage,
                                    daemon=True,
                                    name="Send data to Thingsboard Thread")
        self.__send_thread.start()

        try:
            gateway_statistic_send = 0
            while True:
                cur_time = time.time() * 1000
                if self.__rpc_requests_in_progress and self.tb_client.is_connected(
                ):
                    for rpc_in_progress, data in self.__rpc_requests_in_progress.items(
                    ):
                        if cur_time >= data[1]:
                            data[2](rpc_in_progress)
                            self.cancel_rpc_request(rpc_in_progress)
                            self.__rpc_requests_in_progress[
                                rpc_in_progress] = "del"
                    new_rpc_request_in_progress = {
                        key: value
                        for key, value in
                        self.__rpc_requests_in_progress.items()
                        if value != 'del'
                    }
                    self.__rpc_requests_in_progress = new_rpc_request_in_progress

                else:
                    try:
                        time.sleep(1)
                    except Exception as e:
                        log.exception(e)
                        break
                if not self.__request_config_after_connect and \
                        self.tb_client.is_connected() and not self.tb_client.client.get_subscriptions_in_progress():
                    self.__request_config_after_connect = True
                    self.__check_shared_attributes()

                if cur_time - gateway_statistic_send > 60000.0 and self.tb_client.is_connected(
                ):
                    summary_messages = self.__form_statistics()
                    self.tb_client.client.send_telemetry(summary_messages)
                    gateway_statistic_send = time.time() * 1000
                    # self.__check_shared_attributes()
        except KeyboardInterrupt:
            log.info("Stopping...")
            self.__close_connectors()
            log.info("The gateway has been stopped.")
            self.tb_client.stop()
        except Exception as e:
            log.exception(e)
            self.__close_connectors()
            log.info("The gateway has been stopped.")
            self.tb_client.stop()

    def __close_connectors(self):
        for current_connector in self.available_connectors:
            try:
                self.available_connectors[current_connector].close()
                log.debug("Connector %s closed connection.", current_connector)
            except Exception as e:
                log.exception(e)

    def __stop_gateway(self):
        pass

    def _attributes_parse(self, content, *args):
        try:
            log.debug("Received data: %s", content)
            log.debug(args)
            if content is not None:
                shared_attributes = content.get("shared")
                client_attributes = content.get("client")
                new_configuration = shared_attributes.get(
                    "configuration"
                ) if shared_attributes is not None and shared_attributes.get(
                    "configuration") is not None else content.get(
                        "configuration")
                if new_configuration is not None and self.__remote_configurator is not None:
                    try:
                        confirmed = self.__remote_configurator.process_configuration(
                            new_configuration)
                        if confirmed:
                            self.__send_thread = Thread(
                                target=self.__read_data_from_storage,
                                daemon=True,
                                name="Send data to Thingsboard Thread")
                            self.__send_thread.start()
                        self.__remote_configurator.send_current_configuration()
                    except Exception as e:
                        log.exception(e)
                remote_logging_level = shared_attributes.get(
                    'RemoteLoggingLevel'
                ) if shared_attributes is not None else content.get(
                    "RemoteLoggingLevel")
                if remote_logging_level == 'NONE':
                    self.remote_handler.deactivate()
                    log.info('Remote logging has being deactivated.')
                elif remote_logging_level is not None:
                    if self.remote_handler.current_log_level != remote_logging_level or not self.remote_handler.activated:
                        self.remote_handler.activate(remote_logging_level)
                        log.info(
                            'Remote logging has being updated. Current logging level is: %s ',
                            remote_logging_level)
                if shared_attributes is not None:
                    log.debug(
                        "Shared attributes received (%s).",
                        ", ".join([attr for attr in shared_attributes.keys()]))
                if client_attributes is not None:
                    log.debug(
                        "Client attributes received (%s).",
                        ", ".join([attr for attr in client_attributes.keys()]))
        except Exception as e:
            log.exception(e)

    def get_config_path(self):
        return self._config_dir

    def subscribe_to_required_topics(self):
        self.tb_client.client.gw_set_server_side_rpc_request_handler(
            self._rpc_request_handler)
        self.tb_client.client.set_server_side_rpc_request_handler(
            self._rpc_request_handler)
        self.tb_client.client.subscribe_to_all_attributes(
            self._attribute_update_callback)
        self.tb_client.client.gw_subscribe_to_all_attributes(
            self._attribute_update_callback)

    def __check_shared_attributes(self):
        self.tb_client.client.request_attributes(
            callback=self._attributes_parse)

    def _load_connectors(self, main_config):
        self.connectors_configs = {}
        if not main_config.get("connectors"):
            raise Exception(
                "Configuration for connectors not found, check your config file."
            )
        for connector in main_config['connectors']:
            try:
                connector_class = TBUtility.check_and_import(
                    connector["type"],
                    self._default_connectors.get(connector["type"],
                                                 connector.get("class")))
                self._implemented_connectors[
                    connector["type"]] = connector_class
                with open(self._config_dir + connector['configuration'],
                          'r') as conf_file:
                    connector_conf = load(conf_file)
                    if not self.connectors_configs.get(connector['type']):
                        self.connectors_configs[connector['type']] = []
                    self.connectors_configs[connector['type']].append({
                        "name":
                        connector["name"],
                        "config": {
                            connector['configuration']: connector_conf
                        }
                    })
            except Exception as e:
                log.error("Error on loading connector:")
                log.exception(e)

    def _connect_with_connectors(self):
        for connector_type in self.connectors_configs:
            for connector_config in self.connectors_configs[connector_type]:
                for config in connector_config["config"]:
                    connector = None
                    try:
                        connector = self._implemented_connectors[
                            connector_type](self,
                                            connector_config["config"][config],
                                            connector_type)
                        connector.setName(connector_config["name"])
                        self.available_connectors[
                            connector.get_name()] = connector
                        connector.open()
                    except Exception as e:
                        log.exception(e)
                        if connector is not None:
                            connector.close()

    def send_to_storage(self, connector_name, data):
        if not connector_name == self.name:
            if not TBUtility.validate_converted_data(data):
                log.error("Data from %s connector is invalid.", connector_name)
                return
            if data["deviceName"] not in self.get_devices():
                self.add_device(
                    data["deviceName"],
                    {"connector": self.available_connectors[connector_name]},
                    wait_for_publish=True)
            if not self.__connector_incoming_messages.get(connector_name):
                self.__connector_incoming_messages[connector_name] = 0
            else:
                self.__connector_incoming_messages[connector_name] += 1

        telemetry = {}
        telemetry_with_ts = []
        for item in data["telemetry"]:
            if item.get("ts") is None:
                telemetry = {**telemetry, **item}
            else:
                telemetry_with_ts.append({
                    "ts": item["ts"],
                    "values": {
                        **item["values"]
                    }
                })
        if telemetry_with_ts:
            data["telemetry"] = telemetry_with_ts
        else:
            data["telemetry"] = {
                "ts": int(time.time() * 1000),
                "values": telemetry
            }

        json_data = dumps(data)
        save_result = self._event_storage.put(json_data)
        if not save_result:
            log.error(
                'Data from the device "%s" cannot be saved, connector name is %s.',
                data["deviceName"], connector_name)

    def check_size(self, size, devices_data_in_event_pack):
        if size >= 48000:
            self.__send_data(devices_data_in_event_pack)
            size = 0
        return size

    def __read_data_from_storage(self):
        devices_data_in_event_pack = {}
        log.debug("Send data Thread has been started successfully.")
        while True:
            try:
                if self.tb_client.is_connected():
                    size = getsizeof(devices_data_in_event_pack)
                    events = self._event_storage.get_event_pack()
                    if events:
                        for event in events:
                            try:
                                current_event = loads(event)
                            except Exception as e:
                                log.exception(e)
                                continue
                            if not devices_data_in_event_pack.get(
                                    current_event["deviceName"]):
                                devices_data_in_event_pack[
                                    current_event["deviceName"]] = {
                                        "telemetry": [],
                                        "attributes": {}
                                    }
                            if current_event.get("telemetry"):
                                if type(current_event["telemetry"]) == list:
                                    for item in current_event["telemetry"]:
                                        size += getsizeof(item)
                                        size = self.check_size(
                                            size, devices_data_in_event_pack)
                                        devices_data_in_event_pack[
                                            current_event["deviceName"]][
                                                "telemetry"].append(item)
                                else:
                                    if not self.tb_client.is_connected(): break
                                    size += getsizeof(
                                        current_event["telemetry"])
                                    size = self.check_size(
                                        size, devices_data_in_event_pack)
                                    devices_data_in_event_pack[current_event[
                                        "deviceName"]]["telemetry"].append(
                                            current_event["telemetry"])
                            if current_event.get("attributes"):
                                if type(current_event["attributes"]) == list:
                                    for item in current_event["attributes"]:
                                        if not self.tb_client.is_connected():
                                            break
                                        size += getsizeof(item)
                                        size = self.check_size(
                                            size, devices_data_in_event_pack)
                                        devices_data_in_event_pack[
                                            current_event["deviceName"]][
                                                "attributes"].update(
                                                    item.items())
                                else:
                                    if not self.tb_client.is_connected(): break
                                    size += getsizeof(
                                        current_event["attributes"].items())
                                    size = self.check_size(
                                        size, devices_data_in_event_pack)
                                    devices_data_in_event_pack[current_event[
                                        "deviceName"]]["attributes"].update(
                                            current_event["attributes"].items(
                                            ))
                        if devices_data_in_event_pack:
                            if not self.tb_client.is_connected(): break
                            self.__send_data(devices_data_in_event_pack)
                        if self.tb_client.is_connected():
                            success = True
                            while not self.__published_events.empty():
                                event = self.__published_events.get()
                                try:
                                    success = event.get(
                                    ) == event.TB_ERR_SUCCESS
                                except Exception as e:
                                    log.exception(e)
                                    success = False
                            if success:
                                self._event_storage.event_pack_processing_done(
                                )
                                del devices_data_in_event_pack
                                devices_data_in_event_pack = {}
                        else:
                            continue
                    else:
                        time.sleep(.01)
                else:
                    time.sleep(.1)
            except Exception as e:
                log.exception(e)
                time.sleep(1)

    def __send_data(self, devices_data_in_event_pack):
        try:
            for device in devices_data_in_event_pack:
                if devices_data_in_event_pack[device].get("attributes"):
                    if device == self.name:
                        self.__published_events.put(
                            self.tb_client.client.send_attributes(
                                devices_data_in_event_pack[device]
                                ["attributes"]))
                    else:
                        self.__published_events.put(
                            self.tb_client.client.gw_send_attributes(
                                device, devices_data_in_event_pack[device]
                                ["attributes"]))
                if devices_data_in_event_pack[device].get("telemetry"):
                    if device == self.name:
                        self.__published_events.put(
                            self.tb_client.client.send_telemetry(
                                devices_data_in_event_pack[device]
                                ["telemetry"]))
                    else:
                        self.__published_events.put(
                            self.tb_client.client.gw_send_telemetry(
                                device, devices_data_in_event_pack[device]
                                ["telemetry"]))
                devices_data_in_event_pack[device] = {
                    "telemetry": [],
                    "attributes": {}
                }
        except Exception as e:
            log.exception(e)

    def _rpc_request_handler(self, _, content):
        try:
            device = content.get("device")
            if device is not None:
                connector = self.get_devices()[device].get("connector")
                if connector is not None:
                    connector.server_side_rpc_handler(content)
                else:
                    log.error(
                        "Received RPC request but connector for the device %s not found. Request data: \n %s",
                        content["device"], dumps(content))
            else:
                log.debug("RPC request with no device param.")
                log.debug(content)
        except Exception as e:
            log.exception(e)

    def rpc_with_reply_processing(self, topic, content):
        req_id = self.__rpc_requests_in_progress[topic][0]["data"]["id"]
        device = self.__rpc_requests_in_progress[topic][0]["device"]
        self.send_rpc_reply(device, req_id, content)
        self.cancel_rpc_request(topic)

    def send_rpc_reply(self,
                       device=None,
                       req_id=None,
                       content=None,
                       success_sent=None):
        if success_sent is not None:
            rpc_response = {"success": False}
            if success_sent:
                rpc_response["success"] = True
            self.tb_client.client.gw_send_rpc_reply(device, req_id,
                                                    rpc_response)
        elif device is not None and req_id is not None and content is not None:
            self.tb_client.client.gw_send_rpc_reply(device, req_id, content)

    def register_rpc_request_timeout(self, content, timeout, topic,
                                     cancel_method):
        self.__rpc_requests_in_progress[topic] = (content, timeout,
                                                  cancel_method)

    def cancel_rpc_request(self, rpc_request):
        content = self.__rpc_requests_in_progress[rpc_request][0]
        self.send_rpc_reply(device=content["device"],
                            req_id=content["data"]["id"],
                            success_sent=False)

    def _attribute_update_callback(self, content, *args):
        log.debug("Attribute request received with content: \"%s\"", content)
        log.debug(args)
        if content.get('device') is not None:
            try:
                self.__connected_devices[content["device"]][
                    "connector"].on_attributes_update(content)
            except Exception as e:
                log.exception(e)
        else:
            self._attributes_parse(content)

    def __form_statistics(self):
        summary_messages = {"eventsProduced": 0, "eventsSent": 0}
        telemetry = {}
        for connector in self.available_connectors:
            if self.available_connectors[connector].is_connected():
                connector_camel_case = connector[0].lower(
                ) + connector[1:].replace(' ', '')
                telemetry[(connector_camel_case + ' EventsProduced').replace(' ', '')] = \
                    self.available_connectors[connector].statistics['MessagesReceived']
                telemetry[(connector_camel_case + ' EventsSent').replace(' ', '')] = \
                    self.available_connectors[connector].statistics['MessagesSent']
                self.tb_client.client.send_telemetry(telemetry)
                summary_messages['eventsProduced'] += telemetry[str(
                    connector_camel_case + ' EventsProduced').replace(' ', '')]
                summary_messages['eventsSent'] += telemetry[str(
                    connector_camel_case + ' EventsSent').replace(' ', '')]
        return summary_messages

    def add_device(self, device_name, content, wait_for_publish=False):
        if device_name not in self.__saved_devices:
            self.__connected_devices[device_name] = content
            self.__saved_devices[device_name] = content
            if wait_for_publish:
                self.tb_client.client.gw_connect_device(
                    device_name).wait_for_publish()
            else:
                self.tb_client.client.gw_connect_device(device_name)
            self.__save_persistent_devices()

    def update_device(self, device_name, event, content):
        if event == 'connector' and self.__connected_devices[device_name].get(
                event) != content:
            self.__save_persistent_devices()
        self.__connected_devices[device_name][event] = content

    def del_device(self, device_name):
        del self.__connected_devices[device_name]
        self.tb_client.client.gw_disconnect_device(device_name)
        self.__save_persistent_devices()

    def get_devices(self):
        return self.__connected_devices

    def __load_persistent_devices(self):
        devices = {}
        if self.__connected_devices_file in listdir(self._config_dir) and \
                path.getsize(self._config_dir + self.__connected_devices_file) > 0:
            try:
                with open(self._config_dir +
                          self.__connected_devices_file) as devices_file:
                    devices = load(devices_file)
            except Exception as e:
                log.exception(e)
        else:
            connected_devices_file = open(
                self._config_dir + self.__connected_devices_file, 'w')
            connected_devices_file.close()

        if devices is not None:
            log.debug("Loaded devices:\n %s", devices)
            for device_name in devices:
                try:
                    if self.available_connectors.get(devices[device_name]):
                        self.__connected_devices[device_name] = {
                            "connector":
                            self.available_connectors[devices[device_name]]
                        }
                    else:
                        log.warning(
                            "Device %s connector not found, maybe it had been disabled.",
                            device_name)
                except Exception as e:
                    log.exception(e)
                    continue
        else:
            log.debug("No device found in connected device file.")
            self.__connected_devices = {} if self.__connected_devices is None else self.__connected_devices

    def __save_persistent_devices(self):
        with open(self._config_dir + self.__connected_devices_file,
                  'w') as config_file:
            try:
                data_to_save = {}
                for device in self.__connected_devices:
                    if self.__connected_devices[device][
                            "connector"] is not None:
                        data_to_save[device] = self.__connected_devices[
                            device]["connector"].get_name()
                config_file.write(dumps(data_to_save, indent=2,
                                        sort_keys=True))
            except Exception as e:
                log.exception(e)
        log.debug("Saved connected devices.")