def __parse_rpc_config(self): if "enableUnknownRpc" not in self.__config["serverSideRpc"]: self.__config["serverSideRpc"]["enableUnknownRpc"] = self.DEFAULT_ENABLE_UNKNOWN_RPC log.info("[%s] Processing unknown RPC %s", self.get_name(), "enabled" if self.__config["serverSideRpc"]["enableUnknownRpc"] else "disabled") if "overrideRpcConfig" not in self.__config["serverSideRpc"]: self.__config["serverSideRpc"]["overrideRpcConfig"] = self.DEFAULT_OVERRIDE_RPC_PARAMS log.info("[%s] Overriding RPC config %s", self.get_name(), "enabled" if self.__config["serverSideRpc"]["overrideRpcConfig"] else "disabled") if "serverSideRpc" not in self.__config or not self.__config["serverSideRpc"].get("methods", []): self.__config["serverSideRpc"] = {"methods": {}} return reformatted_config = {} for rpc_config in self.__config["serverSideRpc"]["methods"]: if isinstance(rpc_config, str): reformatted_config[rpc_config] = [] elif isinstance(rpc_config, dict): reformatted_config[rpc_config["name"]] = rpc_config.get("params", []) else: log.warn("[%s] Wrong RPC config format. Expected str or dict, get %s", self.get_name(), type(rpc_config)) self.__config["serverSideRpc"]["methods"] = reformatted_config
def on_attributes_update(self, content): for attr_name, attr_value in content["data"].items(): attr_config = self.__shared_attributes.get(content["device"], {}).get(attr_name) if attr_config is None: log.warn( "[%s] No configuration for '%s' attribute, ignore its update", self.get_name(), attr_name) return log.debug( "[%s] Processing attribute update for '%s' device: attribute=%s,value=%s", self.get_name(), content["device"], attr_name, attr_value) # Converter expects dictionary as the second parameter so pack an attribute value to a dictionary data = self.__converters[content["device"]]["downlink"].convert( attr_config, {"value": attr_value}) if data is None: log.error( "[%s] Failed to update '%s' attribute for '%s' device: data conversion failure", self.get_name(), attr_name, content["device"]) return done = self.send_data_to_bus( attr_config.get("nodeId", self.UNKNOWN_ARBITRATION_ID), data, attr_config.get("isExtendedId", False), attr_config.get("isFd", False), attr_config.get("bitrateSwitch", False), True) if not done: log.error( "[%s] Failed to update '%s' attribute for '%s' device", self.get_name(), attr_name, content["device"]) else: log.debug("[%s] Updated '%s' attribute for '%s' device", self.get_name(), attr_name, content["device"])
def __init_iterator(self): save_iterator = self.__config["polling"]["iterator"].get("persistent", self.DEFAULT_SAVE_ITERATOR) log.info("[%s] Iterator saving %s", self.get_name(), "enabled" if save_iterator else "disabled") if save_iterator and self.__load_iterator_config(): log.info("[%s] Init iterator from file '%s': column=%s, start_value=%s", self.get_name(), self.__iterator_file_name, self.__iterator["name"], self.__iterator["value"]) return True self.__iterator = {"name": self.__config["polling"]["iterator"]["column"], "total": 0} if "value" in self.__config["polling"]["iterator"]: self.__iterator["value"] = self.__config["polling"]["iterator"]["value"] log.info("[%s] Init iterator from configuration: column=%s, start_value=%s", self.get_name(), self.__iterator["name"], self.__iterator["value"]) elif "query" in self.__config["polling"]["iterator"]: try: self.__iterator["value"] = \ self.__cursor.execute(self.__config["polling"]["iterator"]["query"]).fetchone()[0] log.info("[%s] Init iterator from database: column=%s, start_value=%s", self.get_name(), self.__iterator["name"], self.__iterator["value"]) except pyodbc.Warning as w: log.warn("[%s] Warning on init iterator from database: %s", self.get_name(), str(w)) except pyodbc.Error as e: log.error("[%s] Failed to init iterator from database: %s", self.get_name(), str(e)) else: log.error("[%s] Failed to init iterator: value/query param is absent", self.get_name()) return "value" in self.__iterator
def __init_connection(self): try: log.debug("[%s] Opening connection to database", self.get_name()) connection_config = self.__config["connection"] self.__connection = pyodbc.connect(connection_config["str"], **connection_config.get("attributes", {})) if connection_config.get("encoding", ""): log.info("[%s] Setting encoding to %s", self.get_name(), connection_config["encoding"]) self.__connection.setencoding(connection_config["encoding"]) decoding_config = connection_config.get("decoding") if decoding_config is not None: if isinstance(decoding_config, dict): if decoding_config.get("char", ""): log.info("[%s] Setting SQL_CHAR decoding to %s", self.get_name(), decoding_config["char"]) self.__connection.setdecoding(pyodbc.SQL_CHAR, decoding_config["char"]) if decoding_config.get("wchar", ""): log.info("[%s] Setting SQL_WCHAR decoding to %s", self.get_name(), decoding_config["wchar"]) self.__connection.setdecoding(pyodbc.SQL_WCHAR, decoding_config["wchar"]) if decoding_config.get("metadata", ""): log.info("[%s] Setting SQL_WMETADATA decoding to %s", self.get_name(), decoding_config["metadata"]) self.__connection.setdecoding(pyodbc.SQL_WMETADATA, decoding_config["metadata"]) else: log.warn("[%s] Unknown decoding configuration %s. Read data may be misdecoded", self.get_name(), decoding_config) self.__cursor = self.__connection.cursor() log.info("[%s] Connection to database opened, attributes %s", self.get_name(), connection_config.get("attributes", {})) except pyodbc.Error as e: log.error("[%s] Failed to connect to database: %s", self.get_name(), str(e)) self.__close() return self.is_connected()
def __search_node(self, current_node, fullpath, search_method=False, result=[]): try: if regex.match("ns=\d*;[isgb]=.*", fullpath, regex.IGNORECASE): if self.__show_map: log.debug("Looking for node with config") node = self.client.get_node(fullpath) if node is None: log.warn("NODE NOT FOUND - using configuration %s", fullpath) else: log.debug("Found in %s", node) result.append(node) else: fullpath_pattern = regex.compile(fullpath) for child_node in current_node.get_children(): new_node = self.client.get_node(child_node) new_node_path = '\\\\.'.join( char.split(":")[1] for char in new_node.get_path(200000, True)) if self.__show_map: log.debug("SHOW MAP: Current node path: %s", new_node_path) new_node_class = new_node.get_node_class() # regex_fullmatch = re.fullmatch(fullpath, new_node_path.replace('\\\\.', '.')) or new_node_path.replace('\\\\', '\\') == fullpath regex_fullmatch = regex.fullmatch(fullpath_pattern, new_node_path.replace('\\\\.', '.')) or \ new_node_path.replace('\\\\', '\\') == fullpath.replace('\\\\', '\\') or \ new_node_path.replace('\\\\', '\\') == fullpath regex_search = fullpath_pattern.fullmatch(new_node_path.replace('\\\\.', '.'), partial=True) or \ new_node_path.replace('\\\\', '\\') in fullpath.replace('\\\\', '\\') # regex_search = re.search(new_node_path, fullpath.replace('\\\\', '\\')) if regex_fullmatch: if self.__show_map: log.debug( "SHOW MAP: Current node path: %s - NODE FOUND", new_node_path.replace('\\\\', '\\')) result.append(new_node) elif regex_search: if self.__show_map: log.debug( "SHOW MAP: Current node path: %s - NODE FOUND", new_node_path) if new_node_class == ua.NodeClass.Object: if self.__show_map: log.debug("SHOW MAP: Search in %s", new_node_path) self.__search_node(new_node, fullpath, result=result) elif new_node_class == ua.NodeClass.Variable: log.debug("Found in %s", new_node_path) result.append(new_node) elif new_node_class == ua.NodeClass.Method and search_method: log.debug("Found in %s", new_node_path) result.append(new_node) except Exception as e: log.exception(e)
def __connect_to_current_master(self, device=None): connect_attempt_count = 5 connect_attempt_time_ms = 100 wait_after_failed_attempts_ms = 300000 if device is None: device = list(self.__devices.keys())[0] if self.__devices[device].get(MASTER_PARAMETER) is None: self.__devices[device][MASTER_PARAMETER], self.__devices[device][ AVAILABLE_FUNCTIONS_PARAMETER] = self.__configure_master( self.__devices[device][CONFIG_SECTION_PARAMETER]) if self.__devices[device][MASTER_PARAMETER] != self.__current_master: self.__current_master = self.__devices[device][MASTER_PARAMETER] self.__available_functions = self.__devices[device][ AVAILABLE_FUNCTIONS_PARAMETER] connect_attempt_count = self.__devices[device][ CONFIG_SECTION_PARAMETER].get(CONNECT_ATTEMPT_COUNT_PARAMETER, connect_attempt_count) if connect_attempt_count < 1: connect_attempt_count = 1 connect_attempt_time_ms = self.__devices[device][ CONFIG_SECTION_PARAMETER].get(CONNECT_ATTEMPT_TIME_MS_PARAMETER, connect_attempt_time_ms) if connect_attempt_time_ms < 500: connect_attempt_time_ms = 500 wait_after_failed_attempts_ms = self.__devices[device][ CONFIG_SECTION_PARAMETER].get( WAIT_AFTER_FAILED_ATTEMPTS_MS_PARAMETER, wait_after_failed_attempts_ms) if wait_after_failed_attempts_ms < 1000: wait_after_failed_attempts_ms = 1000 current_time = time.time() * 1000 if not self.__current_master.is_socket_open(): if self.__devices[device][CONNECTION_ATTEMPT_PARAMETER] >= connect_attempt_count and \ current_time - self.__devices[device][LAST_CONNECTION_ATTEMPT_TIME_PARAMETER] >= wait_after_failed_attempts_ms: self.__devices[device][CONNECTION_ATTEMPT_PARAMETER] = 0 while not self.__current_master.is_socket_open() \ and self.__devices[device][CONNECTION_ATTEMPT_PARAMETER] < connect_attempt_count \ and current_time - self.__devices[device].get(LAST_CONNECTION_ATTEMPT_TIME_PARAMETER, 0) >= connect_attempt_time_ms: self.__devices[device][ CONNECTION_ATTEMPT_PARAMETER] = self.__devices[device][ CONNECTION_ATTEMPT_PARAMETER] + 1 self.__devices[device][ LAST_CONNECTION_ATTEMPT_TIME_PARAMETER] = current_time log.debug("Modbus trying connect to %s", device) self.__current_master.connect() if self.__devices[device][ CONNECTION_ATTEMPT_PARAMETER] == connect_attempt_count: log.warn( "Maximum attempt count (%i) for device \"%s\" - encountered.", connect_attempt_count, device) # time.sleep(connect_attempt_time_ms / 1000) # if not self.__current_master.is_socket_open(): if self.__devices[device][ CONNECTION_ATTEMPT_PARAMETER] >= 0 and self.__current_master.is_socket_open( ): self.__devices[device][CONNECTION_ATTEMPT_PARAMETER] = 0 self.__devices[device][ LAST_CONNECTION_ATTEMPT_TIME_PARAMETER] = current_time log.debug("Modbus connected to device %s.", device)
def __connect_to_current_master(self, device=None): # TODO: write documentation connect_attempt_count = 5 connect_attempt_time_ms = 100 wait_after_failed_attempts_ms = 300000 if device.config.get('master') is None: device.config['master'], device.config[ 'available_functions'] = self.__configure_master(device.config) if connect_attempt_count < 1: connect_attempt_count = 1 connect_attempt_time_ms = device.config.get('connectAttemptTimeMs', connect_attempt_time_ms) if connect_attempt_time_ms < 500: connect_attempt_time_ms = 500 wait_after_failed_attempts_ms = device.config.get( 'waitAfterFailedAttemptsMs', wait_after_failed_attempts_ms) if wait_after_failed_attempts_ms < 1000: wait_after_failed_attempts_ms = 1000 current_time = time() * 1000 if not device.config['master'].is_socket_open(): if device.config[ 'connection_attempt'] >= connect_attempt_count and current_time - device.config[ 'last_connection_attempt_time'] >= wait_after_failed_attempts_ms: device.config['connection_attempt'] = 0 while not device.config['master'].is_socket_open() \ and device.config['connection_attempt'] < connect_attempt_count \ and current_time - device.config.get('last_connection_attempt_time', 0) >= connect_attempt_time_ms: device.config['connection_attempt'] = device.config[ 'connection_attempt'] + 1 device.config['last_connection_attempt_time'] = current_time log.debug("Modbus trying connect to %s", device) device.config['master'].connect() if device.config[ 'connection_attempt'] == connect_attempt_count: log.warn( "Maximum attempt count (%i) for device \"%s\" - encountered.", connect_attempt_count, device) if device.config['connection_attempt'] >= 0 and device.config[ 'master'].is_socket_open(): device.config['connection_attempt'] = 0 device.config['last_connection_attempt_time'] = current_time
def __resolve_iterator_file(self): file_name = "" try: # The algorithm of resolving iterator file name is described in # https://thingsboard.io/docs/iot-gateway/config/odbc/#subsection-iterator # Edit that description whether algorithm is changed. file_name += self.__connection.getinfo(pyodbc.SQL_DRIVER_NAME) file_name += self.__connection.getinfo(pyodbc.SQL_SERVER_NAME) file_name += self.__connection.getinfo(pyodbc.SQL_DATABASE_NAME) file_name += self.__config["polling"]["iterator"]["column"] self.__iterator_file_name = sha1(file_name.encode()).hexdigest() + ".json" log.debug("[%s] Iterator file name resolved to %s", self.get_name(), self.__iterator_file_name) except Exception as e: log.warn("[%s] Failed to resolve iterator file name: %s", self.get_name(), str(e)) return bool(self.__iterator_file_name)
def run(self): while not self.__stopped: # Initialization phase if not self.is_connected(): while not self.__stopped and \ not self.__init_connection() and \ self.__config["connection"].get("reconnect", self.DEFAULT_RECONNECT_STATE): reconnect_period = self.__config["connection"].get("reconnectPeriod", self.DEFAULT_RECONNECT_PERIOD) log.info("[%s] Will reconnect to database in %d second(s)", self.get_name(), reconnect_period) sleep(reconnect_period) if not self.is_connected(): log.error("[%s] Cannot connect to database so exit from main loop", self.get_name()) self.__stopped = True break if not self.__init_iterator(): log.error("[%s] Cannot init database iterator so exit from main loop", self.get_name()) break # Polling phase try: self.__poll() # self.server_side_rpc_handler({"device": "RPC test", # "data": { # "id": 777, # "method": "usp_NoParameters", # "params": [ 8, True, "Three" ] # }}) if not self.__stopped: polling_period = self.__config["polling"].get("period", self.DEFAULT_POLL_PERIOD) log.debug("[%s] Next polling iteration will be in %d second(s)", self.get_name(), polling_period) sleep(polling_period) except pyodbc.Warning as w: log.warn("[%s] Warning while polling database: %s", self.get_name(), str(w)) except pyodbc.Error as e: log.error("[%s] Error while polling database: %s", self.get_name(), str(e)) self.__close() self.__close() log.info("[%s] Stopped", self.get_name())
def __process_row(self, row): try: data = {} for column in self.__column_names: data[column] = getattr(row, column) to_send = {"attributes": {} if "attributes" not in self.__config["mapping"] else self.__converter.convert(self.__config["mapping"]["attributes"], data), "telemetry": {} if "timeseries" not in self.__config["mapping"] else self.__converter.convert(self.__config["mapping"]["timeseries"], data)} device_name = eval(self.__config["mapping"]["device"]["name"], globals(), data) if device_name not in self.__devices: self.__devices[device_name] = {"attributes": {}, "telemetry": {}} self.__gateway.add_device(device_name, {"connector": self}) self.__iterator["value"] = getattr(row, self.__iterator["name"]) self.__check_and_send(device_name, self.__config["mapping"]["device"].get("type", self.__connector_type), to_send) except Exception as e: log.warn("[%s] Failed to process database row: %s", self.get_name(), str(e))
def server_side_rpc_handler(self, content): done = False try: if not self.is_connected(): log.warn("[%s] Cannot process RPC request: not connected to database", self.get_name()) raise Exception("no connection") sql_params = self.__config["serverSideRpc"]["methods"].get(content["data"]["method"]) if sql_params is None: if not self.__config["serverSideRpc"]["enableUnknownRpc"]: log.warn("[%s] Ignore unknown RPC request '%s' (id=%s)", self.get_name(), content["data"]["method"], content["data"]["id"]) raise Exception("unknown RPC request") else: sql_params = content["data"].get("params", {}).get("args", []) elif self.__config["serverSideRpc"]["overrideRpcConfig"]: if content["data"].get("params", {}).get("args", []): sql_params = content["data"]["params"]["args"] log.debug("[%s] Processing '%s' RPC request (id=%s) for '%s' device: params=%s", self.get_name(), content["data"]["method"], content["data"]["id"], content["device"], content["data"].get("params")) if self.__rpc_cursor is None: self.__rpc_cursor = self.__connection.cursor() if sql_params: self.__rpc_cursor.execute("{{CALL {} ({})}}".format(content["data"]["method"], ("?," * len(sql_params))[0:-1]), sql_params) else: self.__rpc_cursor.execute("{{CALL {}}}".format(content["data"]["method"])) done = True log.debug("[%s] Processed '%s' RPC request (id=%s) for '%s' device", self.get_name(), content["data"]["method"], content["data"]["id"], content["device"]) except pyodbc.Warning as w: log.warn("[%s] Warning while processing '%s' RPC request (id=%s) for '%s' device: %s", self.get_name(), content["data"]["method"], content["data"]["id"], content["device"], str(w)) except Exception as e: log.error("[%s] Failed to process '%s' RPC request (id=%s) for '%s' device: %s", self.get_name(), content["data"]["method"], content["data"]["id"], content["device"], str(e)) finally: self.__gateway.send_rpc_reply(content["device"], content["data"]["id"], {"success": done})
def __on_bus_error(self, e): log.warn( "[%s] Notified about CAN bus error. Store it to later processing", self.get_name()) self.__bus_error = e
def server_side_rpc_handler(self, content): rpc_config = self.__rpc_calls.get(content["device"], {}).get(content["data"]["method"]) if rpc_config is None: if not self.__devices[content["device"]]["enableUnknownRpc"]: log.warn( "[%s] No configuration for '%s' RPC request (id=%s), ignore it", self.get_name(), content["data"]["method"], content["data"]["id"]) return else: rpc_config = {} log.debug( "[%s] Processing %s '%s' RPC request (id=%s) for '%s' device: params=%s", self.get_name(), "pre-configured" if rpc_config else "UNKNOWN", content["data"]["method"], content["data"]["id"], content["device"], content["data"].get("params")) if self.__devices[content["device"]]["overrideRpcConfig"]: if rpc_config: conversion_config = self.__merge_rpc_configs( content["data"].get("params", {}), rpc_config) log.debug( "[%s] RPC request (id=%s) params and connector config merged to conversion config %s", self.get_name(), content["data"]["id"], conversion_config) else: log.debug( "[%s] RPC request (id=%s) will use its params as conversion config", self.get_name(), content["data"]["id"]) conversion_config = content["data"].get("params", {}) else: conversion_config = rpc_config data = self.__converters[content["device"]]["downlink"].convert( conversion_config, content["data"].get("params", {})) if data is None: log.error( "[%s] Failed to process '%s' RPC request (id=%s) for '%s' device: data conversion failure", self.get_name(), content["data"]["method"], content["data"]["id"], content["device"]) return done = self.send_data_to_bus( conversion_config.get("nodeId", self.UNKNOWN_ARBITRATION_ID), data, conversion_config.get("isExtendedId"), conversion_config.get("isFd"), conversion_config.get("bitrateSwitch"), True) if not done: log.error( "[%s] Failed to process '%s' RPC request (id=%s) for '%s' device", self.get_name(), content["data"]["method"], content["data"]["id"], content["device"]) else: log.debug( "[%s] Processed '%s' RPC request (id=%s) for '%s' device", self.get_name(), content["data"]["method"], content["data"]["id"], content["device"]) if conversion_config.get("response", True): self.__gateway.send_rpc_reply(content["device"], content["data"]["id"], {"success": done})
def __search_general_info(self, device): result = [] match_devices = [] self.__search_node(self.__opcua_nodes["root"], TBUtility.get_value(device["deviceNodePattern"], get_tag=True), result=match_devices) for device_node in match_devices: if device_node is not None: result_device_dict = { "deviceName": None, "deviceType": None, "deviceNode": device_node, "configuration": deepcopy(device) } name_pattern_config = device["deviceNamePattern"] name_expression = TBUtility.get_value(name_pattern_config, get_tag=True) if "${" in name_pattern_config and "}" in name_pattern_config: log.debug("Looking for device name") device_name_from_node = "" if name_expression == "$DisplayName": device_name_from_node = device_node.get_display_name( ).Text elif name_expression == "$BrowseName": device_name_from_node = device_node.get_browse_name( ).Name elif name_expression == "$NodeId.Identifier": device_name_from_node = str( device_node.nodeid.Identifier) else: name_path = self._check_path(name_expression, device_node) device_name_node = [] self.__search_node(device_node, name_path, result=device_name_node) if len(device_name_node) == 0: log.warn( "Device name node - not found, skipping device..." ) continue device_name_node = device_name_node[0] if device_name_node is not None: device_name_from_node = device_name_node.get_value( ) if device_name_from_node == "": log.error( "Device name node not found with expression: %s", name_expression) return None full_device_name = name_pattern_config.replace( "${" + name_expression + "}", str(device_name_from_node)).replace( name_expression, str(device_name_from_node)) else: full_device_name = name_expression result_device_dict["deviceName"] = full_device_name log.debug("Device name: %s", full_device_name) if device.get("deviceTypePattern"): device_type_expression = TBUtility.get_value( device["deviceTypePattern"], get_tag=True) if "${" in device_type_expression and "}" in device_type_expression: type_path = self._check_path(device_type_expression, device_node) device_type_node = [] self.__search_node(device_node, type_path, result=device_type_node) device_type_node = device_type_node[0] if device_type_node is not None: device_type = device_type_node.get_value() full_device_type = device_type_expression.replace( "${" + device_type_expression + "}", device_type).replace(device_type_expression, device_type) else: log.error( "Device type node not found with expression: %s", device_type_expression) full_device_type = "default" else: full_device_type = device_type_expression result_device_dict["deviceType"] = full_device_type log.debug("Device type: %s", full_device_type) else: result_device_dict["deviceType"] = "default" result.append(result_device_dict) else: log.error( "Device node not found with expression: %s", TBUtility.get_value(device["deviceNodePattern"], get_tag=True)) return result
def __process_devices(self): for device in self.__devices: current_time = time.time() device_responses = { TIMESERIES_PARAMETER: {}, ATTRIBUTES_PARAMETER: {}, } to_send = {} try: for config_section in device_responses: if self.__devices[device][CONFIG_SECTION_PARAMETER].get( config_section) is not None: current_device_config = self.__devices[device][ CONFIG_SECTION_PARAMETER] unit_id = current_device_config[UNIT_ID_PARAMETER] if self.__devices[device][ NEXT_PREFIX + config_section + CHECK_POSTFIX] < current_time: self.__connect_to_current_master(device) if not self.__current_master.is_socket_open( ) or not len( current_device_config[config_section]): continue # Reading data from device for interested_data in range( len(current_device_config[config_section]) ): current_data = current_device_config[ config_section][interested_data] current_data[DEVICE_NAME_PARAMETER] = device input_data = self.__function_to_device( current_data, unit_id) device_responses[config_section][ current_data[TAG_PARAMETER]] = { "data_sent": current_data, "input_data": input_data } log.debug("Checking %s for device %s", config_section, device) self.__devices[device][ NEXT_PREFIX + config_section + CHECK_POSTFIX] = current_time + current_device_config[ config_section + POLL_PERIOD_POSTFIX] / 1000 log.debug(device_responses) converted_data = {} try: converted_data = self.__devices[device][ UPLINK_PREFIX + CONVERTER_PARAMETER].convert(config={ **current_device_config, BYTE_ORDER_PARAMETER: current_device_config.get( BYTE_ORDER_PARAMETER, self.__byte_order), WORD_ORDER_PARAMETER: current_device_config.get( WORD_ORDER_PARAMETER, self.__word_order) }, data= device_responses ) except Exception as e: log.error(e) if len( converted_data[ATTRIBUTES_PARAMETER] ) == 0 and len( converted_data[TELEMETRY_PARAMETER]) == 0: log.warn("Converted data is empty!") continue to_send = { DEVICE_NAME_PARAMETER: converted_data[DEVICE_NAME_PARAMETER], DEVICE_TYPE_PARAMETER: converted_data[DEVICE_TYPE_PARAMETER], TELEMETRY_PARAMETER: [], ATTRIBUTES_PARAMETER: [] } if current_device_config.get( SEND_DATA_ONLY_ON_CHANGE_PARAMETER): self.statistics[ STATISTIC_MESSAGE_RECEIVED_PARAMETER] += 1 for converted_data_section in CONVERTED_DATA_SECTIONS: for current_section_dict in converted_data[ converted_data_section]: for key, value in current_section_dict.items( ): if self.__devices[device][LAST_PREFIX + converted_data_section].get(key) is None or \ self.__devices[device][LAST_PREFIX + converted_data_section][key] != value: self.__devices[device][ LAST_PREFIX + converted_data_section][ key] = value to_send[ converted_data_section].append( {key: value}) if not to_send.get(ATTRIBUTES_PARAMETER ) and not to_send.get( TELEMETRY_PARAMETER): log.debug("Data has not been changed.") continue elif converted_data and current_device_config.get(SEND_DATA_ONLY_ON_CHANGE_PARAMETER) is None or \ not current_device_config.get(SEND_DATA_ONLY_ON_CHANGE_PARAMETER): self.statistics[ STATISTIC_MESSAGE_RECEIVED_PARAMETER] += 1 for converted_data_section in CONVERTED_DATA_SECTIONS: self.__devices[device][ LAST_PREFIX + converted_data_section] = converted_data[ converted_data_section] to_send[ converted_data_section] = converted_data[ converted_data_section] if to_send.get(ATTRIBUTES_PARAMETER) or to_send.get( TELEMETRY_PARAMETER): self.__gateway.send_to_storage(self.get_name(), to_send) self.statistics[STATISTIC_MESSAGE_SENT_PARAMETER] += 1 except ConnectionException: time.sleep(5) log.error("Connection lost! Reconnecting...") except Exception as e: log.exception(e)