def on_attributes_update(self, content): try: for device in self.__devices: if content["device"] == device["deviceName"]: for attribute_request_config in device[ "attributeUpdateRequests"]: for attribute, value in content["data"]: if search( attribute, attribute_request_config["attributeFilter"] ): common_parameters = self.__get_common_parameters( device) result = self.__process_methods( attribute_request_config["method"], common_parameters, { **attribute_request_config, "value": value }) log.debug( "Received attribute update request for device \"%s\" with attribute \"%s\" and value \"%s\"", content["device"], attribute) log.debug(result) log.debug(content) except Exception as e: log.exception(e)
def server_side_rpc_handler(self, content): try: for device in self.__devices: if content["device"] == device["deviceName"]: for rpc_request_config in device["serverSideRpcRequests"]: if search(content["data"]["method"], rpc_request_config["requestFilter"]): common_parameters = self.__get_common_parameters( device) result = self.__process_methods( rpc_request_config["method"], common_parameters, { **rpc_request_config, "value": content["data"]["params"] }) log.debug( "Received RPC request for device \"%s\" with command \"%s\" and value \"%s\"", content["device"], content["data"]["method"]) log.debug(result) log.debug(content) self.__gateway.send_rpc_reply( device=content["device"], req_id=content["data"]["id"], content=result) except Exception as e: log.exception(e) self.__gateway.send_rpc_reply(device=content["device"], req_id=content["data"]["id"], success_sent=False)
def __search_attribute_update_variables(self, device_info): try: if device_info["configuration"].get("attributes_updates", []): node = device_info["deviceNode"] device_name = device_info["deviceName"] if self.__available_object_resources.get(device_name) is None: self.__available_object_resources[device_name] = {} if self.__available_object_resources[device_name].get( "variables") is None: self.__available_object_resources[device_name][ "variables"] = [] for attribute_update in device_info["configuration"][ "attributes_updates"]: attribute_path = self._check_path( attribute_update["attributeOnDevice"], node) attribute_nodes = [] self.__search_node(node, attribute_path, result=attribute_nodes) for attribute_node in attribute_nodes: if attribute_node is not None: self.__available_object_resources[device_name][ "variables"].append({ attribute_update["attributeOnThingsBoard"]: attribute_node }) else: log.error( "Attribute update node with path \"%s\" - NOT FOUND!", attribute_path) except Exception as e: log.exception(e)
def __process_data(self, device): common_parameters = self.__get_common_parameters(device) converted_data = {} for datatype in self.__datatypes: for datatype_config in device[datatype]: try: response = None method = datatype_config.get("method") if method is None: log.error("Method not found in configuration: %r", datatype_config) continue else: method = method.lower() if method not in self.__methods: log.error("Unknown method: %s, configuration is: %r", method, datatype_config) response = self.__process_methods(method, common_parameters, datatype_config) converted_data.update( **device["uplink_converter"].convert(( datatype, datatype_config), response)) except SNMPTimeoutException: log.error( "Timeout exception on connection to device \"%s\" with ip: \"%s\"", device["deviceName"], device["ip"]) return except Exception as e: log.exception(e) if isinstance(converted_data, dict) and (converted_data.get("attributes") or converted_data.get("telemetry")): self.collect_statistic_and_send(self.get_name(), converted_data)
def __connect_to_devices(self): # Function for opening connection and connecting to devices for device in self.__devices: try: # Start error handler connection_start = time.time() if self.__devices[device].get("serial") is None \ or self.__devices[device]["serial"] is None \ or not self.__devices[device]["serial"].isOpen(): # Connect only if serial not available earlier or it is closed. self.__devices[device]["serial"] = None while self.__devices[device]["serial"] is None or not self.__devices[device]["serial"].isOpen(): # Try connect # connection to serial port with parameters from configuration file or default self.__devices[device]["serial"] = serial.Serial(port=self.__config.get('port', '/dev/ttyUSB0'), baudrate=self.__config.get('baudrate', 9600), bytesize=self.__config.get('bytesize', serial.EIGHTBITS), parity=self.__config.get('parity', serial.PARITY_NONE), stopbits=self.__config.get('stopbits', serial.STOPBITS_ONE), timeout=self.__config.get('timeout', 1), xonxoff=self.__config.get('xonxoff', False), rtscts=self.__config.get('rtscts', False), write_timeout=self.__config.get('write_timeout', None), dsrdtr=self.__config.get('dsrdtr', False), inter_byte_timeout=self.__config.get('inter_byte_timeout', None), exclusive=self.__config.get('exclusive', None)) time.sleep(.1) if time.time() - connection_start > 10: # Break connection try if it setting up for 10 seconds log.error("Connection refused per timeout for device %s", self.__devices[device]["device_config"].get("name")) break except serial.serialutil.SerialException: log.error("Port %s for device %s - not found", self.__config.get('port', '/dev/ttyUSB0'), device) except Exception as e: log.exception(e) else: # if no exception handled - add device and change connection state self.__gateway.add_device(self.__devices[device]["device_config"]["name"], {"connector": self}, self.__devices[device]["device_config"]["type"]) self.__connected = True
def on_attributes_update(self, content): if self.__attribute_updates: for attribute_update in self.__attribute_updates: if match(attribute_update["deviceNameFilter"], content["device"]): for attribute_key in content["data"]: if match(attribute_update["attributeFilter"], attribute_key): try: topic = attribute_update["topicExpression"]\ .replace("${deviceName}", str(content["device"]))\ .replace("${attributeKey}", str(attribute_key))\ .replace("${attributeValue}", str(content["data"][attribute_key])) except KeyError as e: log.exception("Cannot form topic, key %s - not found", e) raise e try: data = attribute_update["valueExpression"]\ .replace("${attributeKey}", str(attribute_key))\ .replace("${attributeValue}", str(content["data"][attribute_key])) except KeyError as e: log.exception("Cannot form topic, key %s - not found", e) raise e self._client.publish(topic, data).wait_for_publish() self.__log.debug("Attribute Update data: %s for device %s to topic: %s", data, content["device"], topic) else: self.__log.error("Cannot find attributeName by filter in message with data: %s", content) else: self.__log.error("Cannot find deviceName by filter in message with data: %s", content) else: self.__log.error("Attribute updates config not found.")
def on_attributes_update(self, content): try: log.debug('Recieved Attribute Update Request: %r', str(content)) for device in self.__devices: if device["deviceName"] == content["device"]: for request in device["attribute_updates"]: if request["config"].get("requestType") is not None: for attribute in content["data"]: if attribute == request["key"]: request["iocb"][1]["config"].update({ "propertyValue": content["data"][attribute] }) kwargs = request["iocb"][1] iocb = request["iocb"][0](device, **kwargs) self.__request_functions[ request["config"]["requestType"]](iocb) return else: log.error( "\"requestType\" not found in request configuration for key %s device: %s", request.get("key", "[KEY IS EMPTY]"), device["deviceName"]) except Exception as e: log.exception(e)
def on_attributes_update(self, content): try: for attribute_request in self.__attribute_updates: if fullmatch(attribute_request["deviceNameFilter"], content["device"]) and \ fullmatch(attribute_request["attributeFilter"], list(content["data"].keys())[0]): converted_data = attribute_request[ "downlink_converter"].convert(attribute_request, content) response_queue = Queue(1) request_dict = { "config": { **attribute_request, **converted_data }, "request": regular_request } with self._app.test_request_context(): attribute_update_request_thread = Thread( target=self.__send_request, args=(request_dict, response_queue, log), daemon=True, name="Attribute request to %s" % (converted_data["url"])) attribute_update_request_thread.start() attribute_update_request_thread.join() if not response_queue.empty(): response = response_queue.get_nowait() log.debug(response) del response_queue except Exception as e: log.exception(e)
def __function_to_device(self, config, unit_id): function_code = config.get(FUNCTION_CODE_PARAMETER) result = None if function_code in (1, 2, 3, 4): result = self.__available_functions[function_code]( address=config[ADDRESS_PARAMETER], count=config.get( OBJECTS_COUNT_PARAMETER, config.get("registersCount", config.get("registerCount", 1))), unit=unit_id) elif function_code in (5, 6): result = self.__available_functions[function_code]( address=config[ADDRESS_PARAMETER], value=config[PAYLOAD_PARAMETER], unit=unit_id) elif function_code in (15, 16): result = self.__available_functions[function_code]( address=config[ADDRESS_PARAMETER], values=config[PAYLOAD_PARAMETER], unit=unit_id) else: log.error("Unknown Modbus function with code: %i", function_code) log.debug("With result %s", str(result)) if "Exception" in str(result): log.exception(result) return result
def __fill_requests(self): log.debug(self.__config["mapping"]) for endpoint in self.__config["mapping"]: try: log.debug(endpoint) converter = None if endpoint["converter"]["type"] == "custom": module = TBModuleLoader.import_module( self.__connector_type, endpoint["converter"]["extension"]) if module is not None: log.debug('Custom converter for url %s - found!', endpoint["url"]) converter = module(endpoint) else: log.error( "\n\nCannot find extension module for %s url.\nPlease check your configuration.\n", endpoint["url"]) else: converter = JsonRequestUplinkConverter(endpoint) self.__requests_in_progress.append({ "config": endpoint, "converter": converter, "next_time": time(), "request": request }) except Exception as e: log.exception(e)
def close(self): self.__stopped = True try: self._client.disconnect() except Exception as e: log.exception(e) self._client.loop_stop() self.__log.info('%s has been stopped.', self.get_name())
def do_write_property(self, device, callback=None): try: iocb = device if isinstance(device, IOCB) else self.form_iocb( device, request_type="writeProperty") deferred(self.request_io, iocb) self.requests_in_progress.update({iocb: {"callback": callback}}) iocb.add_callback(self.__general_cb) except Exception as error: log.exception("exception: %r", error)
def close(self): self.__stopped = True for device in self.__devices_around: try: if self.__devices_around[device].get('peripheral') is not None: self.__devices_around[device]['peripheral'].disconnect() except Exception as e: log.exception(e) raise e
def __bacnet_device_mapping_response_cb(self, iocb, callback_params): converter = callback_params["config"]["uplink_converter"] mapping_type = callback_params["mapping_type"] config = callback_params["config"] converted_data = {} try: converted_data = converter.convert( (mapping_type, config), iocb.ioResponse if iocb.ioResponse else iocb.ioError) except Exception as e: log.exception(e) self.__gateway.send_to_storage(self.name, converted_data)
def server_side_rpc_handler(self, content): try: rpc_method = content["data"].get("method") for method in self.__available_object_resources[ content["device"]]['methods']: if rpc_method is not None and method.get( rpc_method) is not None: arguments_from_config = method["arguments"] arguments = content["data"].get( "params") if content["data"].get( "params") is not None else arguments_from_config try: if isinstance(arguments, list): result = method["node"].call_method( method[rpc_method], *arguments) elif arguments is not None: try: result = method["node"].call_method( method[rpc_method], arguments) except ua.UaStatusCodeError as e: if "BadTypeMismatch" in str(e) and isinstance( arguments, int): result = method["node"].call_method( method[rpc_method], float(arguments)) else: result = method["node"].call_method( method[rpc_method]) self.__gateway.send_rpc_reply( content["device"], content["data"]["id"], { content["data"]["method"]: result, "code": 200 }) log.debug("method %s result is: %s", method[rpc_method], result) except Exception as e: log.exception(e) self.__gateway.send_rpc_reply(content["device"], content["data"]["id"], { "error": str(e), "code": 500 }) else: log.error("Method %s not found for device %s", rpc_method, content["device"]) self.__gateway.send_rpc_reply( content["device"], content["data"]["id"], { "error": "%s - Method not found" % (rpc_method), "code": 404 }) except Exception as e: log.exception(e)
def __fill_converters(self): try: for device in self.__devices: device["uplink_converter"] = TBModuleLoader.import_module( "snmp", device.get('converter', self._default_converters["uplink"]))(device) device["downlink_converter"] = TBModuleLoader.import_module( "snmp", device.get('converter', self._default_converters["downlink"]))(device) except Exception as e: log.exception(e)
def run(self): self._connected = True try: self._app.run(host=self.__config["host"], port=self.__config["port"]) while not self.__stopped: if self.__stopped: break else: sleep(.1) except Exception as e: log.exception(e)
def __init__(self, connector, configuration): try: self.__config = configuration self.__connector = connector assert self.__config is not None assert self.__config.get("general") is not None self.requests_in_progress = {} self.discovered_devices = {} self.__device = TBBACnetDevice(self.__config["general"]) super().__init__(self.__device, self.__config["general"]["address"]) except Exception as e: log.exception(e)
def server_side_rpc_handler(self, server_rpc_request): try: if server_rpc_request.get(DEVICE_SECTION_PARAMETER) is not None: log.debug( "Modbus connector received rpc request for %s with server_rpc_request: %s", server_rpc_request[DEVICE_SECTION_PARAMETER], server_rpc_request) if isinstance( self.__devices[ server_rpc_request[DEVICE_SECTION_PARAMETER]] [CONFIG_SECTION_PARAMETER][RPC_SECTION], dict): rpc_command_config = self.__devices[ server_rpc_request[DEVICE_SECTION_PARAMETER]][ CONFIG_SECTION_PARAMETER][RPC_SECTION].get( server_rpc_request[DATA_PARAMETER] [RPC_METHOD_PARAMETER]) if rpc_command_config is not None: self.__process_rpc_request(server_rpc_request, rpc_command_config) elif isinstance( self.__devices[ server_rpc_request[DEVICE_SECTION_PARAMETER]] [CONFIG_SECTION_PARAMETER][RPC_SECTION], list): for rpc_command_config in self.__devices[ server_rpc_request[DEVICE_SECTION_PARAMETER]][ CONFIG_SECTION_PARAMETER][RPC_SECTION]: if rpc_command_config[ TAG_PARAMETER] == server_rpc_request[ DATA_PARAMETER][RPC_METHOD_PARAMETER]: self.__process_rpc_request(server_rpc_request, rpc_command_config) break else: log.error( "Received rpc request, but method %s not found in config for %s.", server_rpc_request[DATA_PARAMETER].get( RPC_METHOD_PARAMETER), self.get_name()) self.__gateway.send_rpc_reply( server_rpc_request[DEVICE_SECTION_PARAMETER], server_rpc_request[DATA_PARAMETER][RPC_ID_PARAMETER], { server_rpc_request[DATA_PARAMETER][RPC_METHOD_PARAMETER]: "METHOD NOT FOUND!" }) else: log.debug("Received RPC to connector: %r", server_rpc_request) except Exception as e: log.exception(e)
def __load_converters(self, connector_type): # Function for search a converter and save it. devices_config = self.__config.get('devices') try: if devices_config is not None: for device_config in devices_config: if device_config.get('converter') is not None: converter = TBModuleLoader.import_module(connector_type, device_config['converter']) self.__devices[device_config['name']] = {'converter': converter(device_config), 'device_config': device_config} else: log.error('Converter configuration for the custom connector %s -- not found, please check your configuration file.', self.get_name()) else: log.error('Section "devices" in the configuration not found. A custom connector %s has being stopped.', self.get_name()) self.close() except Exception as e: log.exception(e)
def __load_converters(self, device): datatypes = [ "attributes", "telemetry", "attribute_updates", "server_side_rpc" ] for datatype in datatypes: for datatype_config in device.get(datatype, []): try: for converter_type in self.default_converters: converter_object = self.default_converters[ converter_type] if datatype_config.get( "class" ) is None else TBUtility.check_and_import( self.__connector_type, device.get("class")) datatype_config[converter_type] = converter_object( device) except Exception as e: log.exception(e)
def __on_mapping_response_cb(self, iocb: IOCB): try: if self.requests_in_progress.get(iocb) is not None: log.debug(iocb) log.debug(self.requests_in_progress[iocb]) if iocb.ioResponse: apdu = iocb.ioResponse value = self.__property_value_from_apdu(apdu) callback_params = self.requests_in_progress[iocb] if callback_params.get("callback") is not None: self.__general_cb(iocb, callback_params, value) elif iocb.ioError: log.exception(iocb.ioError) except Exception as e: log.exception(e) if self.requests_in_progress.get(iocb) is not None: del self.requests_in_progress[iocb]
def form_iocb(device, config=None, request_type="readProperty"): config = config if config is not None else device address = device["address"] if isinstance( device["address"], Address) else Address(device["address"]) object_id = ObjectIdentifier(config["objectId"]) property_id = config.get("propertyId") value = config.get("propertyValue") property_index = config.get("propertyIndex") priority = config.get("priority") vendor = device.get("vendor", config.get("vendorId", 0)) request = None iocb = None if request_type == "readProperty": try: request = ReadPropertyRequest(objectIdentifier=object_id, propertyIdentifier=property_id) request.pduDestination = address if property_index is not None: request.propertyArrayIndex = int(property_index) iocb = IOCB(request) except Exception as e: log.exception(e) elif request_type == "writeProperty": datatype = get_datatype(object_id.value[0], property_id, vendor) if (isinstance(value, str) and value.lower() == 'null') or value is None: value = Null() request = WritePropertyRequest(objectIdentifier=object_id, propertyIdentifier=property_id) request.pduDestination = address request.propertyValue = Any() try: value = datatype(value) request.propertyValue = Any(value) except AttributeError as e: log.debug(e) except Exception as error: log.exception("WriteProperty cast error: %r", error) if property_index is not None: request.propertyArrayIndex = property_index if priority is not None: request.priority = priority iocb = IOCB(request) else: log.error("Request type is not found or not implemented") return iocb
def on_attributes_update(self, content): log.debug(content) try: for server_variables in self.__available_object_resources[ content["device"]]['variables']: for attribute in content["data"]: for variable in server_variables: if attribute == variable: try: server_variables[variable].set_value( content["data"][variable]) except Exception: server_variables[variable].set_attribute( ua.AttributeIds.Value, ua.DataValue(content["data"][variable])) except Exception as e: log.exception(e)
def server_side_rpc_handler(self, content): try: log.debug('Recieved RPC Request: %r', str(content)) for device in self.__devices: if device["deviceName"] == content["device"]: method_found = False for request in device["server_side_rpc"]: if request["config"].get("requestType") is not None: if content["data"]["method"] == request["method"]: method_found = True kwargs = request["iocb"][1] timeout = time() * 1000 + request[ "config"].get("requestTimeout", 200) if content["data"].get("params") is not None: kwargs["config"].update({ "propertyValue": content["data"]["params"] }) iocb = request["iocb"][0](device, **kwargs) self.__request_functions[ request["config"]["requestType"]]( device=iocb, callback=self.__rpc_response_cb) self.rpc_requests_in_progress[iocb] = { "content": content, "uplink_converter": request["uplink_converter"] } # self.__gateway.register_rpc_request_timeout(content, # timeout, # iocb, # self.__rpc_cancel_processing) else: log.error( "\"requestType\" not found in request configuration for key %s device: %s", request.get("key", "[KEY IS EMPTY]"), device["deviceName"]) if not method_found: log.error("RPC method %s not found in configuration", content["data"]["method"]) self.__gateway.send_rpc_reply(content["device"], content["data"]["id"], success_sent=False) except Exception as e: log.exception(e)
def on_attributes_update(self, content): # Function used for processing attribute update requests from ThingsBoard log.debug(content) if self.__devices.get(content["device"]) is not None: device_config = self.__devices[content["device"]].get("device_config") if device_config is not None and device_config.get("attributeUpdates") is not None: requests = device_config["attributeUpdates"] for request in requests: attribute = request.get("attributeOnThingsBoard") log.debug(attribute) if attribute is not None and attribute in content["data"]: try: value = content["data"][attribute] str_to_send = str(request["stringToDevice"].replace("${" + attribute + "}", str(value))).encode("UTF-8") self.__devices[content["device"]]["serial"].write(str_to_send) log.debug("Attribute update request to device %s : %s", content["device"], str_to_send) time.sleep(.01) except Exception as e: log.exception(e)
def server_side_rpc_handler(self, content): try: for rpc_request in self.__rpc_requests: if fullmatch(rpc_request["deviceNameFilter"], content["device"]) and \ fullmatch(rpc_request["methodFilter"], content["data"]["method"]): converted_data = rpc_request["downlink_converter"].convert( rpc_request, content) response_queue = Queue(1) request_dict = { "config": { **rpc_request, **converted_data }, "request": regular_request } request_dict["converter"] = request_dict["config"].get( "uplink_converter") with self._app.test_request_context(): from flask import request as flask_request rpc_request_thread = Thread( target=self.__send_request, args=(request_dict, response_queue, log, flask_request), daemon=True, name="RPC request to %s" % (converted_data["url"])) rpc_request_thread.start() rpc_request_thread.join() if not response_queue.empty(): response = response_queue.get_nowait() log.debug(response) self.__gateway.send_rpc_reply( device=content["device"], req_id=content["data"]["id"], content=response[2]) else: self.__gateway.send_rpc_reply( device=content["device"], req_id=content["data"]["id"], success_sent=True) del response_queue except Exception as e: log.exception(e)
def do_whois(self, device=None): try: device = {} if device is None else device request = WhoIsRequest() address = device.get("address") low_limit = device.get("idLowLimit") high_limit = device.get("idHighLimit") if address is not None: request.pduDestination = Address(address) else: request.pduDestination = GlobalBroadcast() if low_limit is not None and high_limit is not None: request.deviceInstanceRangeLowLimit = int(low_limit) request.deviceInstanceRangeHighLimit = int(high_limit) iocb = IOCB(request) deferred(self.request_io, iocb) except Exception as e: log.exception(e)
def datachange_notification(self, node, val, data): try: log.debug( "Python: New data change event on node %s, with val: %s and data %s", node, val, str(data)) subscription = self.connector.subscribed[node] converted_data = subscription["converter"].convert( (subscription["config_path"], subscription["path"]), val) self.connector.statistics[ 'MessagesReceived'] = self.connector.statistics[ 'MessagesReceived'] + 1 self.connector.data_to_send.append(converted_data) self.connector.statistics[ 'MessagesSent'] = self.connector.statistics['MessagesSent'] + 1 log.debug("[SUBSCRIPTION] Data to ThingsBoard: %s", converted_data) except KeyError: self.connector.scan_nodes_from_config() except Exception as e: log.exception(e)
def run(self): self._connected = True try: while not self.__stopped: current_time = time() * 1000 for device in self.__devices: try: if device.get("previous_poll_time", 0) + device.get( "pollPeriod", 10000) < current_time: self.__process_data(device) device["previous_poll_time"] = current_time except Exception as e: log.exception(e) if self.__stopped: break else: sleep(.01) except Exception as e: log.exception(e)