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) time.sleep(10) except Exception as e: log.exception(e) time.sleep(10) 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 __run_server(self): self.endpoints = self.load_endpoints() self._app = web.Application( debug=self.__config.get('debugMode', False)) ssl_context = None cert = None key = None if self.__config.get('SSL', False): if not self.__config.get('security'): if not os.path.exists('domain_srv.crt'): from thingsboard_gateway.connectors.rest.ssl_generator import SSLGenerator n = SSLGenerator(self.__config['host']) n.generate_certificate() cert = 'domain_srv.crt' key = 'domain_srv.key' else: try: cert = self.__config['security']['cert'] key = self.__config['security']['key'] except KeyError as e: log.exception(e) log.error('Provide certificate and key path!') ssl_context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) ssl_context.load_cert_chain(cert, key) self.load_handlers() web.run_app(self._app, host=self.__config['host'], port=self.__config['port'], handle_signals=False, ssl_context=ssl_context, reuse_port=self.__config['port'], reuse_address=self.__config['host'])
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) device = tuple( filter( lambda slave: slave.name == server_rpc_request[DEVICE_SECTION_PARAMETER], self.__slaves ) )[0] if isinstance(device.config[RPC_SECTION], dict): rpc_command_config = device.config[RPC_SECTION].get( server_rpc_request[DATA_PARAMETER][RPC_METHOD_PARAMETER]) if rpc_command_config is not None: self.__process_request(server_rpc_request, rpc_command_config) elif isinstance(device.config[RPC_SECTION], list): for rpc_command_config in device.config[RPC_SECTION]: if rpc_command_config[TAG_PARAMETER] == server_rpc_request[DATA_PARAMETER][ RPC_METHOD_PARAMETER]: self.__process_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 __scan_nodes_from_config(self): try: if self.__interest_nodes: for device_object in self.__interest_nodes: for current_device in device_object: try: device_configuration = device_object[ current_device] devices_info_array = self.__search_general_info( device_configuration) for device_info in devices_info_array: if device_info is not None and device_info.get( "deviceNode") is not None: self.__search_nodes_and_subscribe( device_info) self.__save_methods(device_info) self.__search_attribute_update_variables( device_info) else: log.error( "Device node is None, please check your configuration." ) log.debug( "Current device node is: %s", str( device_configuration.get( "deviceNodePattern"))) break except BrokenPipeError: log.debug("Broken Pipe. Connection lost.") except OSError: log.debug("Stop on scanning.") except Exception as e: log.exception(e) log.debug(self.__interest_nodes) except Exception as e: log.exception(e)
def __init__(self, config): super().__init__() self.loop = None self.stopped = False self.name = config['name'] self.device_type = config.get('deviceType', 'default') self.timeout = config.get('timeout', 10000) / 1000 self.show_map = config.get('showMap', False) self.__connector_type = config['connector_type'] self.daemon = True try: self.mac_address = self.validate_mac_address(config['MACAddress']) self.client = BleakClient(self.mac_address) except ValueError as e: self.client = None self.stopped = True log.error(e) self.poll_period = config.get('pollPeriod', 5000) / 1000 self.config = { 'extension': config.get('extension', DEFAULT_CONVERTER_CLASS_NAME), 'telemetry': config.get('telemetry', []), 'attributes': config.get('attributes', []), 'attributeUpdates': config.get('attributeUpdates', []), 'serverSideRpc': config.get('serverSideRpc', []) } self.callback = config['callback'] self.last_polled_time = 0 self.notifying_chars = [] self.__converter = None self.__load_converter() self.start()
async def __process_self(self): not_converted_data = {'telemetry': [], 'attributes': []} for section in ('telemetry', 'attributes'): for item in self.config[section]: char_id = item['characteristicUUID'] if item['method'] == 'read': try: data = await self.client.read_gatt_char(char_id) not_converted_data[section].append({ 'data': data, **item }) except BleakError as e: log.error(e) elif item[ 'method'] == 'notify' and char_id not in self.notifying_chars: try: self.__set_char_handle(item, char_id) self.notifying_chars.append(char_id) await self.notify(char_id) except BleakError: log.error(e) if len(not_converted_data['telemetry']) > 0 or len( not_converted_data['attributes']) > 0: data_for_converter = { 'deviceName': self.name, 'deviceType': self.device_type, 'converter': self.__converter, 'config': { 'attributes': self.config['attributes'], 'telemetry': self.config['telemetry'] }, 'data': not_converted_data } self.callback(data_for_converter)
def server_side_rpc_handler(self, content): try: if content.get("device") is not None: log.debug( "Modbus connector received rpc request for %s with content: %s", content["device"], content) if isinstance( self.__devices[content["device"]]["config"]["rpc"], dict): rpc_command_config = self.__devices[ content["device"]]["config"]["rpc"].get( content["data"]["method"]) if rpc_command_config is not None: self.__process_rpc_request(content, rpc_command_config) elif isinstance( self.__devices[content["device"]]["config"]["rpc"], list): for rpc_command_config in self.__devices[ content["device"]]["config"]["rpc"]: if rpc_command_config["tag"] == content["data"][ "method"]: self.__process_rpc_request(content, rpc_command_config) break else: log.error( "Received rpc request, but method %s not found in config for %s.", content["data"].get("method"), self.get_name()) self.__gateway.send_rpc_reply( content["device"], content["data"]["id"], {content["data"]["method"]: "METHOD NOT FOUND!"}) else: log.debug("Received RPC to connector: %r", content) except Exception as e: log.exception(e)
def send_data_to_bus(self, node_id, data, is_extended_id=False, is_fd=False, bitrate_switch=False, data_check=False): try: self.__bus.send( Message(arbitration_id=node_id, is_extended_id=is_extended_id, is_fd=is_fd, bitrate_switch=bitrate_switch, data=data, check=data_check)) return True except (ValueError, TypeError) as e: log.error("[%s] Wrong CAN message data: %s", self.get_name(), str(e)) except CanError as e: log.error("[%s] Failed to send CAN message: %s", self.get_name(), str(e)) self.__on_bus_error(e) return False
def load_handlers(self): data_handlers = { "basic": BasicDataHandler, "anonymous": AnonymousDataHandler, } handlers = [] for mapping in self.__config.get("mapping"): try: security_type = "anonymous" if mapping.get( "security") is None else mapping["security"]["type"].lower( ) if security_type != "anonymous": Users.add_user(mapping['endpoint'], mapping['security']['username'], mapping['security']['password']) for http_method in mapping['HTTPMethods']: handler = data_handlers[security_type]( self.collect_statistic_and_send, self.get_name(), self.endpoints[mapping["endpoint"]]) handlers.append( web.route(http_method, mapping['endpoint'], handler)) except Exception as e: log.error("Error on creating handlers - %s", str(e)) self._app.add_routes(handlers)
def __general_cb(self, iocb, callback_params=None, value=None): try: if callback_params is None: callback_params = self.requests_in_progress[iocb] if iocb.ioResponse: apdu = iocb.ioResponse if isinstance(apdu, SimpleAckPDU): log.debug("Write to %s - successfully.", str(apdu.pduSource)) else: log.debug("Received response: %r", apdu) elif iocb.ioError: log.exception(iocb.ioError) else: log.error("There are no data in response and no errors.") if isinstance(callback_params, dict) and callback_params.get("callback"): try: callback_params["callback"](iocb, callback_params) except TypeError: callback_params["callback"](iocb) except Exception as e: log.exception("During processing callback, exception has been raised:") log.exception(e) if self.requests_in_progress.get(iocb) is not None: del self.requests_in_progress[iocb]
def server_side_rpc_handler(self, config, content, callback=None, errback=None): rpc_command_config = config["config"]["rpc"].get( content["data"].get("method")) if rpc_command_config.get('bit') is not None: rpc_command_config["functionCode"] = 6 rpc_command_config["unitId"] = config["config"]["unitId"] if rpc_command_config is not None: rpc_command_config["payload"] = self._devices[ content["device"]]["downlink_converter"].convert( rpc_command_config, content) return self._function_to_device(rpc_command_config, rpc_command_config['unit_id'], callback=callback, errback=errback) else: log.error( "Received rpc request, but method %s not found in config for %s.", content["data"].get("method"), config["config"]['deviceName']) return None
def server_side_rpc_handler(self, content): log.debug( "Modbus connector received rpc request for %s with content: %s", self.get_name(), content) rpc_command_config = self.__devices[ content["device"]]["config"]["rpc"].get( content["data"].get("method")) if rpc_command_config.get('bit') is not None: rpc_command_config["functionCode"] = 6 rpc_command_config["unitId"] = self.__devices[ content["device"]]["config"]["unitId"] if rpc_command_config is not None: rpc_command_config["payload"] = self.__devices[ content["device"]]["downlink_converter"].convert( rpc_command_config, content) response = None try: response = self.__function_to_device( rpc_command_config, rpc_command_config["unitId"]) except Exception as e: log.exception(e) if response is not None: response = isinstance( response, (WriteMultipleRegistersResponse, WriteMultipleCoilsResponse, WriteSingleCoilResponse, WriteSingleRegisterResponse)) log.debug(response) self.__gateway.send_rpc_reply( content["device"], content["data"]["id"], {content["data"]["method"]: response}) else: log.error( "Received rpc request, but method %s not found in config for %s.", content["data"].get("method"), self.get_name())
def load_converters(self): devices_config = self.__config.get('devices') try: if devices_config is not None: print("Devices config part loaded ... ") for device_config in devices_config: if device_config.get('converter') is not None: converter = TBUtility.check_and_import( self.__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 __search_general_info(self, device): result = {"deviceName": None, "deviceType": None, "deviceNode": None} result["deviceNode"] = self.__search_node( self.__opcua_nodes["root"], TBUtility.get_value(device["deviceNodePattern"], get_tag=True)) if result["deviceNode"] is not None: 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: device_name_node = self.__search_node( self.__opcua_nodes["root"], name_expression) if device_name_node is not None: device_name_from_node = device_name_node.get_value() full_device_name = name_pattern_config.replace( "${" + name_expression + "}", device_name_from_node).replace(name_expression, device_name_from_node) else: log.error("Device name node not found with expression: %s", name_expression) return else: full_device_name = name_expression result["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: device_type_node = self.__search_node( self.__opcua_nodes["root"], device_type_expression) 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["deviceType"] = full_device_type log.debug("Device type: %s", full_device_type) else: result["deviceType"] = "default" return result else: log.error( "Device node not found with expression: %s", TBUtility.get_value(device["deviceNodePattern"], get_tag=True))
def __fill_interest_devices(self): if self.__config.get('devices') is None: log.error('Devices not found in configuration file. BLE Connector stopped.') self._connected = False return for interest_device in self.__config.get('devices'): keys_in_config = ['attributes', 'telemetry'] if interest_device.get('MACAddress') is not None: default_converter = BytesBLEUplinkConverter(interest_device) interest_uuid = {} for key_type in keys_in_config: for type_section in interest_device.get(key_type): if type_section.get("characteristicUUID") is not None: converter = None if type_section.get('converter') is not None: try: module = TBUtility.check_and_import(self.__connector_type, type_section['converter']) if module is not None: log.debug('Custom converter for device %s - found!', interest_device['MACAddress']) converter = module(interest_device) else: log.error("\n\nCannot find extension module for device %s .\nPlease check your configuration.\n", interest_device['MACAddress']) except Exception as e: log.exception(e) else: converter = default_converter if converter is not None: if interest_uuid.get(type_section["characteristicUUID"].upper()) is None: interest_uuid[type_section["characteristicUUID"].upper()] = [{'section_config': type_section, 'type': key_type, 'converter': converter}] else: interest_uuid[type_section["characteristicUUID"].upper()].append({'section_config': type_section, 'type': key_type, 'converter': converter}) else: log.error("No characteristicUUID found in configuration section for %s:\n%s\n", key_type, pformat(type_section)) if self.__devices_around.get(interest_device['MACAddress'].upper()) is None: self.__devices_around[interest_device['MACAddress'].upper()] = {} self.__devices_around[interest_device['MACAddress'].upper()]['device_config'] = interest_device self.__devices_around[interest_device['MACAddress'].upper()]['interest_uuid'] = interest_uuid else: log.error("Device address not found, please check your settings.")
def __process_rpc_request(self, content, rpc_command_config): if rpc_command_config is not None: log.debug(rpc_command_config) address = rpc_command_config["address"] response = None try: if content["data"].get("params") is not None: params = content["data"]["params"] if not isinstance(params, bool): log.error( "Received rpc request, but params is not bool for method %s!", content["data"].get("method")) self.__gateway.send_rpc_reply( content["device"], content["data"]["id"], { content["data"]["method"]: "Error, params is not bool!" }) return inversion = rpc_command_config["inversion"] result = (params != inversion) # params XOR inversion f = open("/sys/class/gpio/gpio" + str(address) + "/value", "w") if result: ret = f.write("1") else: ret = f.write("0") f.close() response = {"success": True} else: log.error( "Received rpc request, but no params found for method %s!", content["data"].get("method")) self.__gateway.send_rpc_reply( content["device"], content["data"]["id"], {content["data"]["method"]: "Error, no params found!"}) return except Exception as e: log.exception(e) response = e if content.get("id") or (content.get("data") is not None and content["data"].get("id")): if isinstance(response, Exception): log.error( "Received rpc request, but exception happened for method %s!", content["data"].get("method")) self.__gateway.send_rpc_reply( content["device"], content["data"]["id"], {content["data"]["method"]: str(response)}) else: self.__gateway.send_rpc_reply(content["device"], content["data"]["id"], response)
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") name_path = self._check_path(name_expression, device_node) device_name_node = [] self.__search_node(device_node, name_path, result=device_name_node) device_name_node = device_name_node[0] if device_name_node is not None: device_name_from_node = device_name_node.get_value() full_device_name = name_pattern_config.replace("${" + name_expression + "}", str(device_name_from_node)).replace( name_expression, str(device_name_from_node)) else: log.error("Device name node not found with expression: %s", name_expression) return None 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 __service_processing(self, device, characteristic_processing_conf): for service in self.__devices_around[device]['services']: characteristic_uuid_from_config = characteristic_processing_conf.get('characteristicUUID') if characteristic_uuid_from_config is None: log.error('Characteristic not found in config: %s', pformat(characteristic_processing_conf)) return None if self.__devices_around[device]['services'][service].get(characteristic_uuid_from_config) is None: continue characteristic = self.__devices_around[device]['services'][service][characteristic_uuid_from_config][ 'characteristic'] self.__check_and_reconnect(device) data = None if characteristic_processing_conf.get('method', '_').upper().split()[0] == "READ": if characteristic.supportsRead(): self.__check_and_reconnect(device) data = characteristic.read() log.debug(data) else: log.error('This characteristic doesn\'t support "READ" method.') if characteristic_processing_conf.get('method', '_').upper().split()[0] == "NOTIFY": self.__check_and_reconnect(device) descriptor = characteristic.getDescriptors(forUUID=0x2902)[0] handle = descriptor.handle if self.__notify_delegators.get(device) is None: self.__notify_delegators[device] = {} if self.__notify_delegators[device].get(handle) is None: self.__notify_delegators[device][handle] = {'function': self.__notify_handler, 'args': (self.__devices_around[device], handle, self.__notify_delegators[device].get(handle)), 'delegate': None } self.__notify_delegators[device][handle]['delegate'] = self.__notify_delegators[device][handle][ 'function'](*self.__notify_delegators[device][handle]['args']) data = self.__notify_delegators[device][handle]['delegate'].data else: self.__notify_delegators[device][handle]['args'] = (self.__devices_around[device], handle, self.__notify_delegators[device][handle]['delegate']) self.__notify_delegators[device][handle]['delegate'] = self.__notify_delegators[device][handle][ 'function'](*self.__notify_delegators[device][handle]['args']) data = self.__notify_delegators[device][handle]['delegate'].data if data is None: log.error('Cannot process characteristic: %s with config:\n%s', str(characteristic.uuid).upper(), pformat(characteristic_processing_conf)) else: log.debug('data: %s', data) return data
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 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 server_side_rpc_handler(self, content): log.debug(content) try: for device in self.__devices_around: if self.__devices_around[device]['device_config'].get( 'name') == content['device']: for requests in self.__devices_around[device][ 'device_config']["serverSideRpc"]: for service in self.__devices_around[device][ 'services']: if requests[ 'characteristicUUID'] in self.__devices_around[ device]['services'][service]: characteristic = self.__devices_around[device][ 'services'][service][ requests['characteristicUUID']][ 'characteristic'] if requests.get( 'methodProcessing' ) and requests['methodProcessing'].upper( ) in characteristic.propertiesToString(): if content['data']['method'] == requests[ 'methodRPC']: response = None if requests['methodProcessing'].upper( ) == 'WRITE': try: self.__check_and_reconnect( device) response = characteristic.write( content['data'].get( 'params', '').encode('UTF-8'), requests.get( 'withResponse', False)) except BTLEDisconnectError: self.__check_and_reconnect( device) response = characteristic.write( content['data'].get( 'params', '').encode('UTF-8'), requests.get( 'withResponse', False)) elif requests[ 'methodProcessing'].upper( ) == 'READ': try: self.__check_and_reconnect( device) response = characteristic.read( ) except BTLEDisconnectError: self.__check_and_reconnect( device) response = characteristic.read( ) elif requests[ 'methodProcessing'].upper( ) == 'NOTIFY': try: self.__check_and_reconnect( device) delegate = self.__notify_handler( self. __devices_around[device], characteristic.handle) response = delegate.data except BTLEDisconnectError: self.__check_and_reconnect( device) delegate = self.__notify_handler( self. __devices_around[device], characteristic.handle) response = delegate.data if response is not None: log.debug( 'Response from device: %s', response) if requests['withResponse']: response = 'success' self.__gateway.send_rpc_reply( content['device'], content['data']['id'], str(response)) else: log.error( 'Method for rpc request - not supported by characteristic or not found in the config.\nDevice: %s with data: %s and config: %s', device, content, self.__devices_around[device] ['device_config']["serverSideRpc"]) except Exception as e: log.exception(e)
def __get_services_and_chars(self): for device in self.__devices_around: try: if self.__devices_around.get( device) is not None and self.__devices_around[ device].get('scanned_device') is not None: log.debug('Connecting to device: %s', device) if self.__devices_around[device].get('peripheral') is None: address_type = self.__devices_around[device][ 'device_config'].get('addrType', "public") peripheral = Peripheral( self.__devices_around[device]['scanned_device'], address_type) self.__devices_around[device][ 'peripheral'] = peripheral else: peripheral = self.__devices_around[device][ 'peripheral'] try: log.info(peripheral.getState()) except BTLEInternalError: peripheral.connect( self.__devices_around[device]['scanned_device']) try: services = peripheral.getServices() except BTLEDisconnectError: self.__check_and_reconnect(device) services = peripheral.getServices() for service in services: if self.__devices_around[device].get( 'services') is None: log.debug( 'Building device %s map, it may take a time, please wait...', device) self.__devices_around[device]['services'] = {} service_uuid = str(service.uuid).upper() if self.__devices_around[device]['services'].get( service_uuid) is None: self.__devices_around[device]['services'][ service_uuid] = {} try: characteristics = service.getCharacteristics() except BTLEDisconnectError: self.__check_and_reconnect(device) characteristics = service.getCharacteristics() if self.__config.get('buildDevicesMap', False): for characteristic in characteristics: descriptors = [] self.__check_and_reconnect(device) try: descriptors = characteristic.getDescriptors( ) except BTLEDisconnectError: self.__check_and_reconnect(device) descriptors = characteristic.getDescriptors( ) except BTLEGattError as e: log.debug(e) except Exception as e: log.exception(e) characteristic_uuid = str( characteristic.uuid).upper() if self.__devices_around[device][ 'services'][service_uuid].get( characteristic_uuid) is None: self.__check_and_reconnect(device) self.__devices_around[device][ 'services'][service_uuid][ characteristic_uuid] = { 'characteristic': characteristic, 'handle': characteristic.handle, 'descriptors': {} } for descriptor in descriptors: log.debug(descriptor.handle) log.debug(str(descriptor.uuid)) log.debug(str(descriptor)) self.__devices_around[device][ 'services'][service_uuid][ characteristic_uuid][ 'descriptors'][ descriptor. handle] = descriptor else: for characteristic in characteristics: characteristic_uuid = str( characteristic.uuid).upper() self.__devices_around[device]['services'][ service_uuid][characteristic_uuid] = { 'characteristic': characteristic, 'handle': characteristic.handle } if self.__devices_around[device]['is_new_device']: log.debug('New device %s - processing.', device) self.__devices_around[device]['is_new_device'] = False self.__new_device_processing(device) for interest_char in self.__devices_around[device][ 'interest_uuid']: characteristics_configs_for_processing_by_methods = {} for configuration_section in self.__devices_around[ device]['interest_uuid'][interest_char]: characteristic_uuid_from_config = configuration_section[ 'section_config'].get("characteristicUUID") if characteristic_uuid_from_config is None: log.error( 'Characteristic not found in config: %s', pformat(configuration_section)) continue method = configuration_section[ 'section_config'].get('method') if method is None: log.error('Method not found in config: %s', pformat(configuration_section)) continue characteristics_configs_for_processing_by_methods[ method.upper()] = { "method": method, "characteristicUUID": characteristic_uuid_from_config } for method in characteristics_configs_for_processing_by_methods: data = self.__service_processing( device, characteristics_configs_for_processing_by_methods[ method]) for section in self.__devices_around[device][ 'interest_uuid'][interest_char]: converter = section['converter'] converted_data = converter.convert( section, data) self.statistics[ 'MessagesReceived'] = self.statistics[ 'MessagesReceived'] + 1 log.debug(data) log.debug(converted_data) self.__gateway.send_to_storage( self.get_name(), converted_data) self.statistics[ 'MessagesSent'] = self.statistics[ 'MessagesSent'] + 1 except BTLEDisconnectError: log.debug('Connection lost. Device %s', device) continue except Exception as e: log.exception(e)
def __process_devices(self): for device in self.__devices: current_time = time.time() device_responses = {"timeseries": {}, "attributes": {}, } to_send = {} try: for config_data in device_responses: if self.__devices[device]["config"].get(config_data) is not None: unit_id = self.__devices[device]["config"]["unitId"] if self.__devices[device]["next_"+config_data+"_check"] < current_time: self.__connect_to_current_master(device) # Reading data from device for interested_data in range(len(self.__devices[device]["config"][config_data])): current_data = self.__devices[device]["config"][config_data][interested_data] current_data["deviceName"] = device input_data = self.__function_to_device(current_data, unit_id) # if not isinstance(input_data, ReadRegistersResponseBase) and input_data.isError(): # log.exception(input_data) # continue device_responses[config_data][current_data["tag"]] = {"data_sent": current_data, "input_data": input_data} log.debug("Checking %s for device %s", config_data, device) self.__devices[device]["next_"+config_data+"_check"] = current_time + self.__devices[device]["config"][config_data+"PollPeriod"]/1000 log.debug(device_responses) converted_data = {} try: converted_data = self.__devices[device]["converter"].convert(config={"byteOrder": self.__devices[device]["config"].get("byteOrder", self.__byte_order), "wordOrder": self.__devices[device]["config"].get("wordOrder", self.__word_order)}, data=device_responses) except Exception as e: log.error(e) if converted_data and self.__devices[device]["config"].get("sendDataOnlyOnChange"): self.statistics['MessagesReceived'] += 1 to_send = {"deviceName": converted_data["deviceName"], "deviceType": converted_data["deviceType"]} if to_send.get("telemetry") is None: to_send["telemetry"] = [] if to_send.get("attributes") is None: to_send["attributes"] = [] for telemetry_dict in converted_data["telemetry"]: for key, value in telemetry_dict.items(): if self.__devices[device]["last_telemetry"].get(key) is None or \ self.__devices[device]["last_telemetry"][key] != value: self.__devices[device]["last_telemetry"][key] = value to_send["telemetry"].append({key: value}) for attribute_dict in converted_data["attributes"]: for key, value in attribute_dict.items(): if self.__devices[device]["last_attributes"].get(key) is None or \ self.__devices[device]["last_attributes"][key] != value: self.__devices[device]["last_attributes"][key] = value to_send["attributes"].append({key: value}) # to_send["telemetry"] = converted_data["telemetry"] # if converted_data["attributes"] != self.__devices[device]["attributes"]: # self.__devices[device]["last_attributes"] = converted_data["attributes"] # to_send["attributes"] = converted_data["attributes"] if not to_send.get("attributes") and not to_send.get("telemetry"): # self.__gateway.send_to_storage(self.get_name(), to_send) # self.statistics['MessagesSent'] += 1 log.debug("Data has not been changed.") elif converted_data and self.__devices[device]["config"].get("sendDataOnlyOnChange") is None or not self.__devices[device]["config"].get("sendDataOnlyOnChange"): self.statistics['MessagesReceived'] += 1 to_send = {"deviceName": converted_data["deviceName"], "deviceType": converted_data["deviceType"]} # if converted_data["telemetry"] != self.__devices[device]["telemetry"]: self.__devices[device]["last_telemetry"] = converted_data["telemetry"] to_send["telemetry"] = converted_data["telemetry"] # if converted_data["attributes"] != self.__devices[device]["attributes"]: self.__devices[device]["last_attributes"] = converted_data["attributes"] to_send["attributes"] = converted_data["attributes"] # self.__gateway.send_to_storage(self.get_name(), to_send) # self.statistics['MessagesSent'] += 1 if to_send.get("attributes") or to_send.get("telemetry"): self.__gateway.send_to_storage(self.get_name(), to_send) self.statistics['MessagesSent'] += 1 except ConnectionException: time.sleep(5) log.error("Connection lost! Reconnecting...") except Exception as e: log.exception(e)
def run(self): while not self.__connected: try: self.client.connect() try: self.client.load_type_definitions() except Exception as e: log.debug(e) log.debug("Error on loading type definitions.") log.debug(self.client.get_namespace_array()[-1]) log.debug( self.client.get_namespace_index( self.client.get_namespace_array()[-1])) except ConnectionRefusedError: log.error( "Connection refused on connection to OPC-UA server with url %s", self.__server_conf.get("url")) time.sleep(10) except OSError: log.error( "Connection refused on connection to OPC-UA server with url %s", self.__server_conf.get("url")) time.sleep(10) except Exception as e: log.debug("error on connection to OPC-UA server.") log.error(e) time.sleep(10) else: self.__connected = True log.info("OPC-UA connector %s connected to server %s", self.get_name(), self.__server_conf.get("url")) self.__initialize_client() while not self.__stopped: try: time.sleep(.1) self.__check_connection() if not self.__connected and not self.__stopped: self.client.connect() self.__initialize_client() log.info("Reconnected to the OPC-UA server - %s", self.__server_conf.get("url")) elif not self.__stopped: if self.__server_conf.get( "disableSubscriptions", False ) and time.time( ) * 1000 - self.__previous_scan_time > self.__server_conf.get( "scanPeriodInMillis", 60000): self.scan_nodes_from_config() self.__previous_scan_time = time.time() * 1000 # giusguerrini, 2020-09-24: Fix: flush event set and send all data to platform, # so data_to_send doesn't grow indefinitely in case of more than one value change # per cycle, and platform doesn't lose events. # NOTE: possible performance improvement: use a map to store only one event per # variable to reduce frequency of messages to platform. while self.data_to_send: self.__gateway.send_to_storage(self.get_name(), self.data_to_send.pop()) if self.__stopped: self.close() break except (KeyboardInterrupt, SystemExit): self.close() raise except FuturesTimeoutError: self.__check_connection() except Exception as e: log.error( "Connection failed on connection to OPC-UA server with url %s", self.__server_conf.get("url")) log.exception(e) self.client = Client( self.__opcua_url, timeout=self.__server_conf.get("timeoutInMillis", 4000) / 1000) self._subscribed = {} self.__available_object_resources = {} time.sleep(10)
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})
def __search_nodes_and_subscribe(self, device_info): information_types = { "attributes": "attributes", "timeseries": "telemetry" } for information_type in information_types: for information in device_info["configuration"][information_type]: information_key = information["key"] config_path = TBUtility.get_value(information["path"], get_tag=True) information_path = self._check_path(config_path, device_info["deviceNode"]) information["path"] = '${%s}' % information_path information_nodes = [] self.__search_node(device_info["deviceNode"], information_path, result=information_nodes) for information_node in information_nodes: if information_node is not None: information_value = information_node.get_value() log.debug( "Node for %s \"%s\" with path: %s - FOUND! Current values is: %s", information_type, information_key, information_path, str(information_value)) if device_info.get("uplink_converter") is None: configuration = { **device_info["configuration"], "deviceName": device_info["deviceName"], "deviceType": device_info["deviceType"] } if device_info["configuration"].get( 'converter') is None: converter = OpcUaUplinkConverter(configuration) else: converter = TBUtility.check_and_import( self._connector_type, configuration) device_info["uplink_converter"] = converter else: converter = device_info["uplink_converter"] self.subscribed[information_node] = { "converter": converter, "path": information_path, "config_path": config_path } if not device_info.get( information_types[information_type]): device_info[ information_types[information_type]] = [] converted_data = converter.convert( (config_path, information_path), information_value) self.statistics['MessagesReceived'] += 1 self.data_to_send.append(converted_data) self.statistics['MessagesSent'] += 1 if self.__sub is None: self.__sub = self.client.create_subscription( self.__server_conf.get( "subCheckPeriodInMillis", 500), self.__sub_handler) if self.__sub: self.__sub.subscribe_data_change(information_node) log.debug("Added subscription to node: %s", str(information_node)) log.debug("Data to ThingsBoard: %s", converted_data) else: log.error( "Node for %s \"%s\" with path %s - NOT FOUND!", information_type, information_key, information_path)
def __init__(self, gateway, config, connector_type): self._connector_type = connector_type self.statistics = {'MessagesReceived': 0, 'MessagesSent': 0} super().__init__() self.__gateway = gateway self.__server_conf = config.get("server") self.__interest_nodes = [] self.__available_object_resources = {} self.__show_map = self.__server_conf.get("showMap", False) self.__previous_scan_time = 0 for mapping in self.__server_conf["mapping"]: if mapping.get("deviceNodePattern") is not None: self.__interest_nodes.append( {mapping["deviceNodePattern"]: mapping}) else: log.error( "deviceNodePattern in mapping: %s - not found, add property deviceNodePattern to processing this mapping", dumps(mapping)) if "opc.tcp" not in self.__server_conf.get("url"): opcua_url = "opc.tcp://" + self.__server_conf.get("url") else: opcua_url = self.__server_conf.get("url") self.client = Client( opcua_url, timeout=self.__server_conf.get("timeoutInMillis", 4000) / 1000) if self.__server_conf["identity"]["type"] == "cert.PEM": try: ca_cert = self.__server_conf["identity"].get("caCert") private_key = self.__server_conf["identity"].get("privateKey") cert = self.__server_conf["identity"].get("cert") security_mode = self.__server_conf["identity"].get( "mode", "SignAndEncrypt") policy = self.__server_conf["security"] if cert is None or private_key is None: log.exception( "Error in ssl configuration - cert or privateKey parameter not found" ) raise security_string = policy + ',' + security_mode + ',' + cert + ',' + private_key if ca_cert is not None: security_string = security_string + ',' + ca_cert self.client.set_security_string(security_string) except Exception as e: log.exception(e) if self.__server_conf["identity"].get("username"): self.client.set_user( self.__server_conf["identity"].get("username")) if self.__server_conf["identity"].get("password"): self.client.set_password( self.__server_conf["identity"].get("password")) self.setName( self.__server_conf.get( "name", 'OPC-UA ' + ''.join(choice(ascii_lowercase) for _ in range(5))) + " Connector") self.__opcua_nodes = {} self._subscribed = {} self.data_to_send = [] self.__sub_handler = SubHandler(self) self.__stopped = False self.__connected = False self.daemon = True
def run(self): while not self.__connected: try: self.__connected = self.client.connect() try: self.client.load_type_definitions() except Exception as e: log.debug(e) log.debug("Error on loading type definitions.") log.debug(self.client.get_namespace_array()[-1]) log.debug( self.client.get_namespace_index( self.client.get_namespace_array()[-1])) except ConnectionRefusedError: log.error( "Connection refused on connection to OPC-UA server with url %s", self.__server_conf.get("url")) time.sleep(10) except OSError: log.error( "Connection refused on connection to OPC-UA server with url %s", self.__server_conf.get("url")) time.sleep(10) except Exception as e: log.debug("error on connection to OPC-UA server.") log.error(e) time.sleep(10) else: self.__connected = True log.info("OPC-UA connector %s connected to server %s", self.get_name(), self.__server_conf.get("url")) self.__opcua_nodes["root"] = self.client.get_objects_node() self.__opcua_nodes["objects"] = self.client.get_objects_node() if not self.__server_conf.get("disableSubscriptions", False): self.__sub = self.client.create_subscription( self.__server_conf.get("subCheckPeriodInMillis", 500), self.__sub_handler) else: self.__sub = False self.__scan_nodes_from_config() self.__previous_scan_time = time.time() * 1000 log.debug('Subscriptions: %s', self.subscribed) log.debug("Available methods: %s", self.__available_object_resources) while not self.__stopped: try: time.sleep(.1) self.__check_connection() if not self.__connected and not self.__stopped: self.client.connect() elif not self.__stopped: if self.__server_conf.get( "disableSubscriptions", False ) and time.time( ) * 1000 - self.__previous_scan_time > self.__server_conf.get( "scanPeriodInMillis", 60000): self.__scan_nodes_from_config() self.__previous_scan_time = time.time() * 1000 if self.data_to_send: self.__gateway.send_to_storage(self.get_name(), self.data_to_send.pop()) if self.__stopped: self.close() break except (KeyboardInterrupt, SystemExit): self.close() raise except ConnectionRefusedError: log.error( "Connection refused on connection to OPC-UA server with url %s", self.__server_conf.get("url")) time.sleep(10) except Exception as e: self.close() log.exception(e)
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('\\\\.', '.') for child_node in current_node.get_children(): new_node_class = child_node.get_node_class() #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)) if child_node_parent_class == ua.NodeClass.View and new_parent is not None: parent_path = '\\.'.join( char.split(":")[1] for char in child_node.get_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)
async def connect_to_device(self): try: log.info('Trying to connect to %s with %s MAC address', self.name, self.mac_address) await self.client.connect(timeout=self.timeout) except Exception as e: log.error(e)