Exemplo n.º 1
0
    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 convert(self):
        if not self.__config.get('server'):
            return self.__config

        log.warning(
            'You are using old configuration structure for Modbus connector. It will be DEPRECATED in the future '
            'version! New config file "modbus_new.json" was generated in %s folder. Please, use it.',
            self.CONFIG_PATH)
        log.warning(
            'You have to manually connect the new generated config file to tb_gateway.yaml!'
        )

        slaves = []
        for device in self.__config['server'].get('devices', []):
            slave = {**device}

            for key in self.__keys:
                if not device.get(key):
                    slave[key] = self.__config['server'].get(key)

            slave['pollPeriod'] = slave['timeseriesPollPeriod']

            slaves.append(slave)

        result_dict = {
            'master': {
                'slaves': slaves
            },
            'slave': self.__config.get('slave')
        }
        self.__save_json_config_file(result_dict)

        return result_dict
Exemplo n.º 3
0
    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)
            }

            if to_send['telemetry'].get('ts'):
                to_send['ts'] = to_send['telemetry']['ts']
                del to_send['telemetry']['ts']

            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))
Exemplo n.º 4
0
    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 run(self):
        while not self.__master.connect():
            time.sleep(5)
            log.warning("Modbus trying reconnect to %s", self.__config.get("name"))
        log.info("Modbus connected.")
        self.__connected = True

        while True:
            time.sleep(.01)
            self.__process_devices()
            if self.__stopped:
                break
Exemplo n.º 6
0
 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)
             for child_node in current_node.get_children():
                 new_node = self.client.get_node(child_node)
                 new_node_path = '\\\\.'.join(char.split(":")[1] for char in new_node.get_path(200000, True))
                 if self.__show_map:
                     log.debug("SHOW MAP: Current node path: %s", new_node_path)
                 new_node_class = new_node.get_node_class()
                 regex_fullmatch = regex.fullmatch(fullpath_pattern, new_node_path.replace('\\\\.', '.')) or \
                                   new_node_path.replace('\\\\', '\\') == fullpath.replace('\\\\', '\\') or \
                                   new_node_path.replace('\\\\', '\\') == fullpath
                 regex_search = fullpath_pattern.fullmatch(new_node_path.replace('\\\\.', '.'), partial=True) or \
                                   new_node_path.replace('\\\\', '\\') in fullpath.replace('\\\\', '\\')
                 if regex_fullmatch:
                     if self.__show_map:
                         log.debug("SHOW MAP: Current node path: %s - NODE FOUND", new_node_path.replace('\\\\', '\\'))
                     result.append(new_node)
                 elif regex_search:
                     if self.__show_map:
                         log.debug("SHOW MAP: Current node path: %s - NODE FOUND", new_node_path)
                     if new_node_class == ua.NodeClass.Object:
                         if self.__show_map:
                             log.debug("SHOW MAP: Search in %s", new_node_path)
                         self.__search_node(new_node, fullpath, result=result)
                     elif new_node_class == ua.NodeClass.Variable:
                         log.debug("Found in %s", new_node_path)
                         result.append(new_node)
                     elif new_node_class == ua.NodeClass.Method and search_method:
                         log.debug("Found in %s", new_node_path)
                         result.append(new_node)
     except 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_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
Exemplo n.º 8
0
    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
Exemplo n.º 9
0
    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())
Exemplo n.º 10
0
    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()
Exemplo n.º 11
0
    def __resolve_iterator_file(self):
        file_name = ""
        try:
            # The algorithm of resolving iterator file name is described in
            # https://thingsboard.io/docs/iot-gateway/config/odbc/#subsection-iterator
            # Edit that description whether algorithm is changed.
            file_name += self.__connection.getinfo(pyodbc.SQL_DRIVER_NAME)
            file_name += self.__connection.getinfo(pyodbc.SQL_SERVER_NAME)
            file_name += self.__connection.getinfo(pyodbc.SQL_DATABASE_NAME)
            file_name += self.__config["polling"]["iterator"]["column"]

            self.__iterator_file_name = sha1(
                file_name.encode()).hexdigest() + ".json"
            log.debug("[%s] Iterator file name resolved to %s",
                      self.get_name(), self.__iterator_file_name)
        except Exception as e:
            log.warning("[%s] Failed to resolve iterator file name: %s",
                        self.get_name(), str(e))
        return bool(self.__iterator_file_name)
    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})
Exemplo n.º 13
0
    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

            return {
                "start":
                int(value_matches.group(1)),
                "length":
                int(value_matches.group(2)),
                "byteorder":
                value_matches.group(3) if value_matches.group(3) else "big",
                "type":
                value_matches.group(4),
                "encoding":
                value_matches.group(5) if value_matches.group(5) else "ascii"
            }
        elif isinstance(config, dict):
            try:
                return {
                    "start":
                    int(config["start"]),
                    "length":
                    int(config["length"]),
                    "byteorder":
                    config["byteorder"]
                    if config.get("byteorder", "") else "big",
                    "type":
                    config["type"],
                    "encoding":
                    config["encoding"]
                    if config.get("encoding", "") else "ascii"
                }
            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
Exemplo n.º 14
0
    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_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
Exemplo n.º 16
0
    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)
Exemplo n.º 17
0
 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
Exemplo n.º 18
0
 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)
Exemplo n.º 19
0
    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})