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.warning( "[%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(data, attr_config, data_check=True) if done: log.debug("[%s] Updated '%s' attribute for '%s' device", self.get_name(), attr_name, content["device"]) else: log.error( "[%s] Failed to update '%s' attribute for '%s' device", self.get_name(), attr_name, content["device"])
def __process_message(self, message): if message.arbitration_id not in self.__nodes: # Too lot log messages in case of high message generation frequency log.debug("[%s] Ignoring CAN message. Unknown arbitration_id %d", self.get_name(), message.arbitration_id) return cmd_conf = self.__commands[message.arbitration_id] if cmd_conf is not None: cmd_id = int.from_bytes( message.data[cmd_conf["start"]:cmd_conf["start"] + cmd_conf["length"]], cmd_conf["byteorder"]) else: cmd_id = self.NO_CMD_ID if cmd_id not in self.__nodes[message.arbitration_id]: log.debug("[%s] Ignoring CAN message. Unknown cmd_id %d", self.get_name(), cmd_id) return log.debug("[%s] Processing CAN message (id=%d,cmd_id=%s): %s", self.get_name(), message.arbitration_id, cmd_id, message) parsing_conf = self.__nodes[message.arbitration_id][cmd_id] data = self.__converters[parsing_conf["deviceName"]]["uplink"].convert( parsing_conf["configs"], message.data) if data is None or not data.get("attributes", []) and not data.get( "telemetry", []): log.warning( "[%s] Failed to process CAN message (id=%d,cmd_id=%s): data conversion failure", self.get_name(), message.arbitration_id, cmd_id) return self.__check_and_send(parsing_conf, data)
def __process_row(self, row): try: data = self.row_to_dict(row) 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.warning("[%s] Failed to process database row: %s", self.get_name(), str(e))
def __init_iterator(self): save_iterator = self.DEFAULT_SAVE_ITERATOR if "persistent" not in self.__config["polling"]["iterator"]: self.__config["polling"]["iterator"]["persistent"] = save_iterator else: save_iterator = self.__config["polling"]["iterator"]["persistent"] 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.warning("[%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 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()) 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() 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.warning("[%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() self.__stopped = False log.info("[%s] Stopped", self.get_name())
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.warning( "[%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 __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.get_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.warning("[%s] Failed to resolve iterator file name: %s", self.get_name(), str(e)) return bool(self.__iterator_file_name)
def __parse_rpc_config(self): if "serverSideRpc" not in self.__config: self.__config["serverSideRpc"] = {} 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 else: log.warning( "[%s] Wrong RPC config format. Expected str or dict, get %s", self.get_name(), type(rpc_config)) self.__config["serverSideRpc"]["methods"] = reformatted_config
def __parse_command_config(self, config): if config is None: log.warning("[%s] Wrong command configuration: no data", self.get_name()) return if isinstance(config, str): cmd_matches = re.search(self.CMD_REGEX, config) if not cmd_matches: log.warning( "[%s] Wrong command configuration: '%s' doesn't match pattern", self.get_name(), config) return return { "start": int(cmd_matches.group(1)), "length": int(cmd_matches.group(2)), "byteorder": cmd_matches.group(3) if cmd_matches.group(3) else self.DEFAULT_BYTEORDER, "value": int(cmd_matches.group(4)) } elif isinstance(config, dict): try: return { "start": int(config["start"]), "length": int(config["length"]), "byteorder": config["byteorder"] if config.get("byteorder", "") else self.DEFAULT_BYTEORDER, "value": int(config["value"]) } except (KeyError, ValueError) as e: log.warning("[%s] Wrong command configuration: %s", self.get_name(), str(e)) return log.warning("[%s] Wrong command configuration: unknown type", self.get_name()) return
def __search_node(self, current_node, fullpath, search_method=False, result=None): if result is None: result = [] try: if regex.match(r"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.warning("NODE NOT FOUND - using configuration %s", fullpath) else: log.debug("Found in %s", node) result.append(node) else: fullpath_pattern = regex.compile(fullpath) full1 = fullpath.replace('\\\\.', '.') #current_node_path = '\\.'.join(char.split(":")[1] for char in current_node.get_path(200000, True)) current_node_path = self.get_node_path(current_node) # we are allways the parent child_node_parent_class = current_node.get_node_class() new_parent = current_node for child_node in current_node.get_children(): new_node_class = child_node.get_node_class() # this will not change you can do it outside th loop # basis Description of node.get_parent() function, sometime child_node.get_parent() return None #new_parent = child_node.get_parent() #if (new_parent is None): # child_node_parent_class = current_node.get_node_class() #else: # child_node_parent_class = child_node.get_parent().get_node_class() #current_node_path = '\\.'.join(char.split(":")[1] for char in current_node.get_path(200000, True)) #new_node_path = '\\\\.'.join(char.split(":")[1] for char in child_node.get_path(200000, True)) new_node_path = self.get_node_path(child_node) if child_node_parent_class == ua.NodeClass.View and new_parent is not None: parent_path = self.get_node_path(new_parent) #parent_path = '\\.'.join(char.split(":")[1] for char in new_parent.get_path(200000, True)) fullpath = fullpath.replace(current_node_path, parent_path) nnp1 = new_node_path.replace('\\\\.', '.') nnp2 = new_node_path.replace('\\\\', '\\') if self.__show_map: log.debug("SHOW MAP: Current node path: %s", new_node_path) regex_fullmatch = regex.fullmatch(fullpath_pattern, nnp1) or \ nnp2 == full1 or \ nnp2 == fullpath or \ nnp1 == full1 if regex_fullmatch: if self.__show_map: log.debug( "SHOW MAP: Current node path: %s - NODE FOUND", nnp2) result.append(child_node) else: regex_search = fullpath_pattern.fullmatch(nnp1, partial=True) or \ nnp2 in full1 or \ nnp1 in full1 if 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(child_node, fullpath, result=result) elif new_node_class == ua.NodeClass.Variable: log.debug("Found in %s", new_node_path) result.append(child_node) elif new_node_class == ua.NodeClass.Method and search_method: log.debug("Found in %s", new_node_path) result.append(child_node) except CancelledError: log.error( "Request during search has been canceled by the OPC-UA server." ) except BrokenPipeError: log.error("Broken Pipe. Connection lost.") except OSError: log.debug("Stop on scanning.") except Exception as e: log.exception(e)
def __parse_value_config(self, config): if config is None: log.warning("[%s] Wrong value configuration: no data", self.get_name()) return if isinstance(config, str): value_matches = re.search(self.VALUE_REGEX, config) if not value_matches: log.warning( "[%s] Wrong value configuration: '%s' doesn't match pattern", self.get_name(), config) return value_config = { "start": int(value_matches.group(1)), "length": int(value_matches.group(2)), "byteorder": value_matches.group(3) if value_matches.group(3) else self.DEFAULT_BYTEORDER, "type": value_matches.group(4) } if value_config["type"][0] == "i" or value_config["type"][0] == "l": value_config["signed"] = value_matches.group(5) == "signed" if value_matches.group(5) \ else self.DEFAULT_SIGNED_FLAG elif value_config["type"][0] == "s" or value_config["type"][ 0] == "r": value_config["encoding"] = value_matches.group( 5) if value_matches.group(5) else self.DEFAULT_ENCODING return value_config elif isinstance(config, dict): try: value_config = { "start": int(config["start"]), "length": int(config["length"]), "byteorder": config["byteorder"] if config.get("byteorder", "") else self.DEFAULT_BYTEORDER, "type": config["type"] } if value_config["type"][0] == "i" or value_config["type"][ 0] == "l": value_config["signed"] = config.get( "signed", self.DEFAULT_SIGNED_FLAG) elif value_config["type"][0] == "s": value_config[ "encoding"] = config["encoding"] if config.get( "encoding", "") else self.DEFAULT_ENCODING return value_config except (KeyError, ValueError) as e: log.warning("[%s] Wrong value configuration: %s", self.get_name(), str(e)) return log.warning("[%s] Wrong value configuration: unknown type", self.get_name()) return
def __parse_config(self, config): self.__reconnect_count = 0 self.__reconnect_conf = { "enabled": config.get("reconnect", self.DEFAULT_RECONNECT_STATE), "period": config.get("reconnectPeriod", self.DEFAULT_RECONNECT_PERIOD), "maxCount": config.get("reconnectCount", None) } self.__bus_conf = { "interface": config.get("interface", "socketcan"), "channel": config.get("channel", "vcan0"), "backend": config.get("backend", {}) } for device_config in config.get("devices"): is_device_config_valid = False device_name = device_config["name"] device_type = device_config.get("type", self.__connector_type) strict_eval = device_config.get("strictEval", self.DEFAULT_STRICT_EVAL_FLAG) self.__devices[device_name] = {} self.__devices[device_name][ "enableUnknownRpc"] = device_config.get( "enableUnknownRpc", self.DEFAULT_ENABLE_UNKNOWN_RPC) self.__devices[device_name]["overrideRpcConfig"] = True if self.__devices[device_name]["enableUnknownRpc"] \ else device_config.get("overrideRpcConfig", self.DEFAULT_OVERRIDE_RPC_PARAMS) self.__converters[device_name] = {} if not strict_eval: log.info( "[%s] Data converters for '%s' device will use non-strict eval", self.get_name(), device_name) if "serverSideRpc" in device_config and device_config[ "serverSideRpc"]: is_device_config_valid = True self.__rpc_calls[device_name] = {} self.__converters[device_name][ "downlink"] = self.__get_converter( device_config.get("converters"), False) for rpc_config in device_config["serverSideRpc"]: rpc_config["strictEval"] = strict_eval self.__rpc_calls[device_name][ rpc_config["method"]] = rpc_config if "attributeUpdates" in device_config and device_config[ "attributeUpdates"]: is_device_config_valid = True self.__shared_attributes[device_name] = {} if "downlink" not in self.__converters[device_name]: self.__converters[device_name][ "downlink"] = self.__get_converter( device_config.get("converters"), False) for attribute_config in device_config["attributeUpdates"]: attribute_config["strictEval"] = strict_eval attribute_name = attribute_config.get( "attributeOnThingsBoard") or attribute_config.get( "attribute") self.__shared_attributes[device_name][ attribute_name] = attribute_config for config_key in ["timeseries", "attributes"]: if config_key not in device_config or not device_config[ config_key]: continue is_device_config_valid = True is_ts = (config_key[0] == "t") tb_item = "telemetry" if is_ts else "attributes" self.__devices[device_name][tb_item] = {} if "uplink" not in self.__converters[device_name]: self.__converters[device_name][ "uplink"] = self.__get_converter( device_config.get("converters"), True) for msg_config in device_config[config_key]: tb_key = msg_config["key"] msg_config["strictEval"] = strict_eval msg_config["is_ts"] = is_ts node_id = msg_config.get("nodeId", self.UNKNOWN_ARBITRATION_ID) if node_id == self.UNKNOWN_ARBITRATION_ID: log.warning( "[%s] Ignore '%s' %s configuration: no arbitration id", self.get_name(), tb_key, config_key) continue value_config = self.__parse_value_config( msg_config.get("value")) if value_config is not None: msg_config.update(value_config) else: log.warning( "[%s] Ignore '%s' %s configuration: no value configuration", self.get_name(), tb_key, config_key, ) continue if msg_config.get("command", "") and node_id not in self.__commands: cmd_config = self.__parse_command_config( msg_config["command"]) if cmd_config is None: log.warning( "[%s] Ignore '%s' %s configuration: wrong command configuration", self.get_name(), tb_key, config_key, ) continue cmd_id = cmd_config["value"] self.__commands[node_id] = cmd_config else: cmd_id = self.NO_CMD_ID self.__commands[node_id] = None if node_id not in self.__nodes: self.__nodes[node_id] = {} if cmd_id not in self.__nodes[node_id]: self.__nodes[node_id][cmd_id] = { "deviceName": device_name, "deviceType": device_type, "sendOnChange": device_config.get("sendDataOnlyOnChange", self.DEFAULT_SEND_IF_CHANGED), "configs": [] } self.__nodes[node_id][cmd_id]["configs"].append(msg_config) self.__devices[device_name][tb_item][tb_key] = None if "polling" in msg_config: try: polling_config = msg_config.get("polling") polling_config["key"] = tb_key # Just for logging polling_config["type"] = polling_config.get( "type", "always") polling_config["period"] = polling_config.get( "period", self.DEFAULT_POLL_PERIOD) polling_config["nodeId"] = node_id polling_config["isExtendedId"] = msg_config.get( "isExtendedId", self.DEFAULT_EXTENDED_ID_FLAG) polling_config["isFd"] = msg_config.get( "isFd", self.DEFAULT_FD_FLAG) polling_config["bitrateSwitch"] = msg_config.get( "bitrateSwitch", self.DEFAULT_BITRATE_SWITCH_FLAG) # Create CAN message object to validate its data can_msg = Message( arbitration_id=polling_config["nodeId"], is_extended_id=polling_config["isExtendedId"], is_fd=polling_config["isFd"], bitrate_switch=polling_config["bitrateSwitch"], data=bytearray.fromhex( polling_config["dataInHex"]), check=True) self.__polling_messages.append(polling_config) except (ValueError, TypeError) as e: log.warning( "[%s] Ignore '%s' %s polling configuration, wrong CAN data: %s", self.get_name(), tb_key, config_key, str(e)) continue if is_device_config_valid: log.debug("[%s] Done parsing of '%s' device configuration", self.get_name(), device_name) self.__gateway.add_device(device_name, {"connector": self}) else: log.warning( "[%s] Ignore '%s' device configuration, because it doesn't have attributes," "attributeUpdates,timeseries or serverSideRpc", self.get_name(), device_name)
def __on_bus_error(self, e): log.warning( "[%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.warning( "[%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 not None: done = self.send_data_to_bus(data, conversion_config, data_check=True) if done: log.debug( "[%s] Processed '%s' RPC request (id=%s) for '%s' device", self.get_name(), content["data"]["method"], content["data"]["id"], content["device"]) else: 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: done = False 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"]) if conversion_config.get("response", self.DEFAULT_RPC_RESPONSE_SEND_FLAG): self.__gateway.send_rpc_reply(content["device"], content["data"]["id"], {"success": done})
def server_side_rpc_handler(self, content): done = False try: if not self.is_connected(): log.warning( "[%s] Cannot process RPC request: not connected to database", self.get_name()) raise Exception("no connection") is_rpc_unknown = False rpc_config = self.__config["serverSideRpc"]["methods"].get( content["data"]["method"]) if rpc_config is None: if not self.__config["serverSideRpc"]["enableUnknownRpc"]: log.warning("[%s] Ignore unknown RPC request '%s' (id=%s)", self.get_name(), content["data"]["method"], content["data"]["id"]) raise Exception("unknown RPC request") else: is_rpc_unknown = True rpc_config = content["data"].get("params", {}) sql_params = rpc_config.get("args", []) query = rpc_config.get("query", "") else: if self.__config["serverSideRpc"]["overrideRpcConfig"]: rpc_config = { **rpc_config, **content["data"].get("params", {}) } # The params attribute is obsolete but leave for backward configuration compatibility sql_params = rpc_config.get("args") or rpc_config.get( "params", []) query = rpc_config.get("query", "") log.debug( "[%s] Processing %s '%s' RPC request (id=%s) for '%s' device: params=%s, query=%s", self.get_name(), "unknown" if is_rpc_unknown else "", content["data"]["method"], content["data"]["id"], content["device"], sql_params, query) if self.__rpc_cursor is None: self.__rpc_cursor = self.__connection.cursor() if query: if sql_params: self.__rpc_cursor.execute(query, sql_params) else: self.__rpc_cursor.execute(query) else: 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.warning( "[%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: if done and rpc_config.get("result", self.DEFAULT_PROCESS_RPC_RESULT): response = self.row_to_dict(self.__rpc_cursor.fetchone()) self.__gateway.send_rpc_reply(content["device"], content["data"]["id"], response) else: self.__gateway.send_rpc_reply(content["device"], content["data"]["id"], {"success": done})