Ejemplo n.º 1
0
    def start_uaprogramm(self,
                         name,
                         serverurl,
                         port,
                         uamethod="start_demoprogramm"):
        """
        ruft die UA Methode über den im Docker erzeugten Client auf

        """

        # Suche, ob der Container existiert
        clientname = name + "client"
        self._refresh_registry()
        container = self.registry.get(clientname)
        if container:

            connectstring = "opc.tcp://" + serverurl + ":" + port + "/freeopcua/server/"
            client = Client(connectstring)
            # client = Client("opc.tcp://admin@localhost:4840/freeopcua/server/")
            try:
                client.connect()
                client.load_type_definitions()

                # # Der Client verfügt über einige Methoden, um einen Proxy für UA-Knoten abzurufen,
                # die sich immer im Adressraum befinden sollten, z. B. Root oder Objects
                root = client.get_root_node()
                print("Root node is: ", root)
                objects = client.get_objects_node()
                print("Objects node is: ", objects)

                # Knotenobjekte verfügen über Methoden zum Lesen und Schreiben von Knotenattributen sowie zum Duchsuchen
                # des Adressraums
                print("Children of root are: ", root.get_children())

                # Der Adressraum  idx
                uri = "http://freeopcua.github.io"
                idx = client.get_namespace_index(uri)

                # Jetzt wird ein variabler Knoten über den Suchpfad abgerufen
                myvar = root.get_child([
                    "0:Objects", "{}:MyObject".format(idx),
                    "{}:MyVariable".format(idx)
                ])

                obj = root.get_child(["0:Objects", "{}:MyObject".format(idx)])
                # Aufrufen unserer übergebenen Methode
                res = obj.call_method(uamethod.format(idx))
            finally:
                client.disconnect()
Ejemplo n.º 2
0
def download_xst(subband: int,
                 integration_time_s: int,
                 url: str = 'localhost',
                 port: int = 50000):
    """
    Download cross correlation statistics

    Args:
        subband (int): Subband number
        integration_time_s (int): Integration time in seconds
        url (str): URL to connect to, defaults to 'localhost'
        port (str): Port to connect to, defaults to 50000

    Returns:
        Tuple[datetime.datetime, np.ndarray, int]: UTC time, visibilities (shape n_ant x n_ant),
                                                   RCU mode

    Raises:
        RuntimeError: if in mixed RCU mode
    """
    client = Client("opc.tcp://{}:{}/".format(url, port), timeout=1000)
    client.connect()
    client.load_type_definitions()
    objects = client.get_objects_node()
    idx = client.get_namespace_index(DEFAULT_URI)
    obj = client.get_root_node().get_child(
        ["0:Objects", "{}:StationMetrics".format(idx), "{}:RCU".format(idx)])

    obstime, visibilities_opc, rcu_modes = obj.call_method(
        "{}:record_cross".format(idx), subband, integration_time_s)

    client.close_session()
    client.close_secure_channel()

    rcu_modes_on = set([mode for mode in rcu_modes if mode != '0'])
    if len(rcu_modes_on) == 1:
        rcu_mode = int(rcu_modes_on.pop())
    elif len(rcu_modes_on) == 0:
        rcu_mode = 0
    else:
        raise RuntimeError(
            "Multiple nonzero RCU modes are used, that's not supported yet")

    assert (len(visibilities_opc) == 2)  # Real and complex part
    visibilities = np.array(visibilities_opc)[0] + 1j * np.array(
        visibilities_opc[1])

    return obstime, visibilities, rcu_mode
Ejemplo n.º 3
0
def uageneratestructs():
    parser = argparse.ArgumentParser(
        description=
        "Generate a Python module from the xml structure definition (.bsd)")
    add_common_args(parser, require_node=True)
    parser.add_argument(
        "-o",
        "--output",
        dest="output_path",
        required=True,
        type=str,
        default=None,
        help="The python file to be generated.",
    )
    args = parse_args(parser, requirenodeid=True)

    client = Client(args.url, timeout=args.timeout)
    client.set_security_string(args.security)
    client.connect()
    try:
        node = get_node(client, args)
        generators, _ = client.load_type_definitions([node])
        generators[0].save_to_file(args.output_path, True)
    finally:
        client.disconnect()
    sys.exit(0)
Ejemplo n.º 4
0
    def run(self):
        print('Thread running')
        url = "opc.tcp://10.8.0.14:4840"

        client = Client(url)

        client.connect()
        print("Client Connected")

        client.load_type_definitions()

        #while True:
        root = client.get_root_node()
        print("Root 노드: ", root)
        objects = client.get_objects_node()
        print("오브젝트: ", objects)

        print("Children of root are: ", objects.get_children())
Ejemplo n.º 5
0
def conexion_OPC():
    # Conexion con el servidor del controlador
    MPC = Client("opc.tcp://Labo6:16700/")    # Creacion objeto OPC-UA
    MPC.connect()                             # Conexion Controlador
    MPC.load_type_definitions()

    # Conexión con el servidor RTO
    RTO = Client("opc.tcp://Labo6:16701/")    # Creacion objeto OPC-UA
    RTO.connect()                             # Conexion Controlador
    RTO.load_type_definitions()
    # Conexion con los demas servidores
    SCADA = OpenOPC.client()                  # Creacion objeto OPC-DA
    PID1 = OpenOPC.client()                   # Creacion objeto OPC-DA
    PID2 = OpenOPC.client()                   # Creacion objeto OPC-DA

    SCADA.connect("LecturaOPC.1.0")
    PID1.connect("OPC.PID ISA (CreaOPC).1")
    PID2.connect("OPC.PID ISA 2 (CreaOPC).1")

    return [MPC, SCADA, PID1, PID2, RTO]
Ejemplo n.º 6
0
class opcUaClient():
    def __init__(self, url):
        self.session = Client(url)
        self.session.connect()
        self.session.load_type_definitions(
        )  # load definition of server specific structures/extension objects

    def getArrayNodesFromOpcServer(self, tagList, resultFile):
        localArrayNodes = []
        for x in tagList:
            tag = x.strip()
            try:
                var = self.session.get_node(tag)

                localArrayNodes.append(var)

            except Exception as e:
                errorMessage = tag + " ,\t Cannot read tag from source. Error message: " + str(
                    e)
                writeMessageInFile(resultFile, errorMessage)
        return localArrayNodes
Ejemplo n.º 7
0
def uageneratestructs():
    parser = argparse.ArgumentParser(description="Generate a Python module from the xml structure definition (.bsd)")
    add_common_args(parser, require_node=True)
    parser.add_argument("-o",
                        "--output",
                        dest="output_path",
                        required=True,
                        type=str,
                        default=None,
                        help="The python file to be generated.",
                        )
    args = parse_args(parser, requirenodeid=True)

    client = Client(args.url, timeout=args.timeout)
    _configure_client_with_args(client, args)
    client.connect()
    try:
        node = get_node(client, args)
        generators, _ = client.load_type_definitions([node])
        generators[0].save_to_file(args.output_path, True)
    finally:
        client.disconnect()
    sys.exit(0)
Ejemplo n.º 8
0
import sys
sys.path.insert(0, "..")
import time
import logging
from IPython import embed

from opcua import Client
from opcua import ua

if __name__ == "__main__":
    logging.basicConfig(level=logging.WARN)
    client = Client("opc.tcp://opcua.demo-this.com:51210/UA/SampleServer")
    try:
        client.connect()
        root = client.get_root_node()
        objects = client.get_objects_node()
        struct = client.get_node("ns=2;i=10239")
        before = struct.get_value()
        client.load_type_definitions(
        )  # scan server for custom structures and import them
        after = struct.get_value()

        embed()
    finally:
        client.disconnect()
Ejemplo n.º 9
0
from opcua import Client
import time
import opcua

url = "opc.tcp://192.168.56.1:4840"
client = Client(url)
client.connect()
print("Client connected")

client.load_type_definitions()

root = client.get_root_node()
print("Root node is: ", root)
objects = client.get_objects_node()
print("Objects node is: ", objects)

print("Children of root are: ", root.get_children())

uri = "OPCUA_SERVER"
idx = client.get_namespace_index(uri)

while True:
    try:
        Time = root.get_child(
            ["0:Objects", "{}:Parameters".format(idx), "{}:Time".format(idx)])
        Pressure = root.get_child([
            "0:Objects", "{}:Parameters".format(idx), "{}:Pressure".format(idx)
        ])
        Temp = root.get_child([
            "0:Objects", "{}:Parameters".format(idx),
            "{}:Temperature".format(idx)
Ejemplo n.º 10
0
class OpcUaClient(object):
    CONNECT_TIMEOUT = 15  # [sec]
    RETRY_DELAY = 10  # [sec]
    MAX_RETRIES = 3  # [-]

    class Decorators(object):
        @staticmethod
        def autoConnectingClient(wrappedMethod):
            def wrapper(obj, *args, **kwargs):
                for retry in range(OpcUaClient.MAX_RETRIES):
                    try:
                        return wrappedMethod(obj, *args, **kwargs)
                    except ua.uaerrors.BadNoMatch:
                        raise
                    except Exception:
                        pass
                    try:
                        obj._logger.warning(
                            '(Re)connecting to OPC-UA service.')
                        obj.reconnect()
                    except ConnectionRefusedError:
                        obj._logger.warning(
                            'Connection refused. Retry in 10s.'.format(
                                OpcUaClient.RETRY_DELAY))
                        time.sleep(OpcUaClient.RETRY_DELAY)
                else:  # So the exception is exposed.
                    obj.reconnect()
                    return wrappedMethod(obj, *args, **kwargs)

            return wrapper

    def __init__(self, serverUrl):
        self._logger = logging.getLogger(self.__class__.__name__)
        self._client = Client(serverUrl.geturl(), timeout=self.CONNECT_TIMEOUT)

    def __enter__(self):
        self.connect()
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        self.disconnect()
        self._client = None

    @property
    @Decorators.autoConnectingClient
    def sensorList(self):
        return self.objectsNode.get_children()

    @property
    @Decorators.autoConnectingClient
    def objectsNode(self):
        path = [ua.QualifiedName(name='Objects', namespaceidx=0)]
        return self._client.get_root_node().get_child(path)

    def connect(self):
        self._client.connect()
        self._client.load_type_definitions()

    def disconnect(self):
        try:
            self._client.disconnect()
        except Exception:
            pass

    def reconnect(self):
        self.disconnect()
        self.connect()

    @Decorators.autoConnectingClient
    def get_browse_name(self, uaNode):
        return uaNode.get_browse_name()

    @Decorators.autoConnectingClient
    def get_node_class(self, uaNode):
        return uaNode.get_node_class()

    @Decorators.autoConnectingClient
    def get_namespace_index(self, uri):
        return self._client.get_namespace_index(uri)

    @Decorators.autoConnectingClient
    def get_child(self, uaNode, path):
        return uaNode.get_child(path)

    @Decorators.autoConnectingClient
    def read_raw_history(self,
                         uaNode,
                         starttime=None,
                         endtime=None,
                         numvalues=0,
                         cont=None):
        details = ua.ReadRawModifiedDetails()
        details.IsReadModified = False
        details.StartTime = starttime or ua.get_win_epoch()
        details.EndTime = endtime or ua.get_win_epoch()
        details.NumValuesPerNode = numvalues
        details.ReturnBounds = True
        result = OpcUaClient._history_read(uaNode, details, cont)
        assert (result.StatusCode.is_good())
        return result.HistoryData.DataValues, result.ContinuationPoint

    @staticmethod
    def _history_read(uaNode, details, cont):
        valueid = ua.HistoryReadValueId()
        valueid.NodeId = uaNode.nodeid
        valueid.IndexRange = ''
        valueid.ContinuationPoint = cont

        params = ua.HistoryReadParameters()
        params.HistoryReadDetails = details
        params.TimestampsToReturn = ua.TimestampsToReturn.Both
        params.ReleaseContinuationPoints = False
        params.NodesToRead.append(valueid)
        result = uaNode.server.history_read(params)[0]
        return result
Ejemplo n.º 11
0
class OpcClient():
    """A simplified OpClient.

    This is a opc client that can be leveraged to
    subscribe to the OPC data change notifications.
    
    You should interact with the client using
    * add_notification_handler
    * subscribe_variable
    * unsubscribe_variable

    So typically:
    ```
    # setup client
    cl = OpcClient(...)
    # define handler
    def handler(update):
        # data update : {'node': <node>, 'timestamp': <datetime>, 'value': <value>, 'data': <data>}
        # do something with update here
    # add handler
    cl.add_notification_handler(handler)
    # subscribe to variable
    cl.subscribe_variable(cl.get_node("ns=3;i=2002"))

    time.sleep(100)
    cl.disconnect()
    ```

    *Note*
    Make sure to call disconnect before shutting down in
    order to ensure a clean close.

    See: https://github.com/FreeOpcUa/python-opcua

    Attributes
    ----------
    _address : str
        The connection string address
    _address_obfuscated : str
        The connection string address obfuscating a potnetial password
    _client : OPCLibClient
        The opc lib client
    _subscription : ua.Subscription
        The ua subscription handle that can be used
        to create new subscritions or unsubscribe
    _sub_handles : dict
        A dictionary mapping ua.Node variables to
        subscription handles to be able to unsubscribe
        from them again
    _handlers : list
        A list of handler functions
    """
    def __init__(self, host, port, path='/', username=None, password=None):
        """Create a new OpClient.

        This will create a new opc client that can be
        leveraged to subscribe to the OPC data change
        notifications.

        Parameters
        ----------
        host : str
            The host name of the opc server.
        port : int
            The port of the opc server.
        path : str
            The path to add to the server as base
        username : str|None
            A username to use for authorization.
        password : str|None
            A password to use for authorization.
        """

        self._client = None
        self._subscription = None
        self._sub_handles = {}
        self._handlers = []

        # setup authorization
        auth = ''
        if username is not None:
            auth = '{}:{}@'.format(username, password)

        # define address
        self._address = self._address_obfuscated = "opc.tcp://{}{}:{}{}".format(
            auth, host, port, path)
        if username is not None and password is not None:
            self._address_obfuscated = self._address.replace(password, '***')

        Logger.trace('Created OpcClient',
                     fields={'address': self._address_obfuscated})

        # setup client
        self.init_client()
        # setup subscriptions
        self.init_subscriptions()

    def init_client(self):
        """Initialize the client.

        This will connect to the client using
        the address. This is required to be
        called before anything else interacting
        with the opc server is done.
        """
        try:
            self._client = OPCLibClient(self._address)
            self._client.connect()
            self._client.load_type_definitions()
            self.namespace = self._client.get_namespace_array()
            Logger.trace('Connection established')
        except Exception as e:
            Logger.error('Failed connecting to address "{}"'.format(
                self._address_obfuscated))
            Logger.error('{}'.format(e))
            raise e

    def log_info(self):
        root = self._client.get_root_node()
        objects = self._client.get_objects_node()
        children = root.get_children()
        # print '{}:{}'.format(namespace[children[1].nodeid.NamespaceIndex], children[1].nodeid.Identifier)
        # print self._client.get_node("ns=0;i=86")
        Logger.debug("Retrieved Client Info",
                     fields={
                         'root': root,
                         'objects': objects,
                         'children': children,
                         'namespace': self.namespace
                     })

    def full_uri(self, node):
        """Resolve the full uri of a ua.Node.

        Parameters
        ----------
        node : ua.Node
            A node from the OPC interface.

        Returns
        -------
        str :
            The namespace prefixed full uri of the entity.
        """
        return '{}:{}'.format(self.namespace[node.nodeid.NamespaceIndex],
                              node.nodeid.Identifier)

    def get_by_uri(self, uri):
        """Resolve the full uri to a ua.Node.

        Parameters
        ----------
        uri : str
            The uri to the ua.Node.

        Returns
        -------
        ua.Node :
            The resolved node.
        """
        ns_match = None
        for ns in self.namespace:
            if '{}:'.format(ns) in uri:
                ns_match = ns
                break
        if ns_match is None:
            raise RuntimeError('Namespace of "{}" not available'.format(uri))
        ns_idx = self._client.get_namespace_index(ns_match)
        identifier = uri.replace('{}:'.format(ns_match), '')
        try:
            int(identifier)
            is_int = True
        except:
            is_int = False

        substituted = 'ns={};{}={}'.format(ns_idx, 'i' if is_int else 's',
                                           identifier)
        return self._client.get_node(substituted)

    def init_subscriptions(self):
        """Initialize the subscriptions.

        This will initialize a subscription handler
        and afterwards will be ready to subscribe to
        specific variables.
        """
        if self._client is None:
            raise RuntimeError('Client not initialized yet.')

        self._subscription = self._client.create_subscription(500, self)
        Logger.trace('Base Subscription established')

    def subscribe_variable(self, variable):
        """Subscribe to a OPC variable.

        This will subscribe the client to
        the given variable and any data change
        notification will be published.

        Parameters
        ----------
        variable : ua.Node
            The ua node, e.g. client.get_node(ua.NodeId(1002, 2)) or
            client.get_node("ns=3;i=2002")
        """
        if self._subscription is None:
            raise RuntimeError('Subscriptions not initialized yet.')

        if variable in self._sub_handles.keys():
            Logger.info('Already subscribed to "{}"'.format(variable))
            return
        self._sub_handles[variable] = self._subscription.subscribe_data_change(
            variable)

    def unsubscribe_variable(self, variable):
        """Unsubscribe from a OPC variable.

        This will unsubscribe the client from
        the given variable.

        Parameters
        ----------
        variable : ua.Node
            The ua node, e.g. client.get_node(ua.NodeId(1002, 2)) or
            client.get_node("ns=3;i=2002")
        """
        if self._subscription is None:
            raise RuntimeError('Subscriptions not initialized yet.')

        if variable not in self._sub_handles.keys():
            Logger.info('Not subscribed to "{}"'.format(variable))
            return
        self._subscription.unsubscribe(self._sub_handles[variable])
        del self._sub_handles[variable]

    def disconnect(self):
        """Disconnect the client.
        """
        if self._client is None:
            return
        try:
            self._client.disconnect()
        except Exception:
            pass

    def datachange_notification(self, node, value, data):
        """Receiver from the OPC client.

        This gets called by OPC client on subscription
        notification.

        Parameter
        ---------
        node : ua.Node
            The node from which we received the data.
        value : any
            The value notification
        data : any
            The data of the notification
        """
        timestamp = datetime.now()
        if hasattr(data, 'MonitoredItemNotification') and \
                hasattr(data.MonitoredItemNotification, 'SourceTimestamp'):
            timestamp = data.MonitoredItemNotification.SourceTimestamp
        Logger.debug('OpcClient: Received data change notification',
                     fields={
                         'node': node,
                         'value': value,
                         'data': data,
                         'timestamp': timestamp.isoformat()
                     })
        # send update to handlers
        update = {
            'node': node,
            'value': value,
            'data': data,
            'timestamp': timestamp
        }
        for hdl in self._handlers:
            hdl(update)

    def add_notification_handler(self, handler):
        """Add a handler function.

        This handler `def handler(update)` will be
        called upon reception of a notification from
        the OPC Client. The update will have the
        following data:
        ```{
            'node': <node>,
            'value': <value>,
            'data': <data>,
            'timestamp': <timestamp>
        }```
        """
        self._handlers.append(handler)
Ejemplo n.º 12
0
        print("Python: New data change event", node, val)

    def event_notification(self, event):
        print("Python: New event", event)


if __name__ == "__main__":
    logging.basicConfig(level=logging.WARN)
    #logger = logging.getLogger("KeepAlive")
    #logger.setLevel(logging.DEBUG)

    client = Client("opc.tcp://*****:*****@localhost:4840/freeopcua/server/") #connect using a user
    try:
        client.connect()
        client.load_type_definitions()  # load definition of server specific structures/extension objects

        # Client has a few methods to get proxy to UA nodes that should always be in address space such as Root or Objects
        root = client.get_root_node()
        print("Root node is: ", root)
        objects = client.get_objects_node()
        print("Objects node is: ", objects)

        # Node objects have methods to read and write node attributes as well as browse or populate address space
        print("Children of root are: ", root.get_children())

        # get a specific node knowing its node id
        #var = client.get_node(ua.NodeId(1002, 2))
        #var = client.get_node("ns=3;i=2002")
        #print(var)
        #var.get_data_value() # get value of node as a DataValue object
Ejemplo n.º 13
0
class OpcUaConnector(Thread, Connector):
    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"):
            self.__opcua_url = "opc.tcp://" + self.__server_conf.get("url")
        else:
            self.__opcua_url = self.__server_conf.get("url")
        self.client = Client(
            self.__opcua_url,
            timeout=self.__server_conf.get("timeoutInMillis", 4000) / 1000)
        if self.__server_conf["identity"].get("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 RuntimeError(
                        "Error in ssl configuration - cert or privateKey parameter not found"
                    )
                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.__sub = None
        self.__sub_handler = SubHandler(self)
        self.data_to_send = []
        self.__stopped = False
        self.__connected = False
        self.daemon = True

    def is_connected(self):
        return self.__connected

    def open(self):
        self.__stopped = False
        self.start()
        log.info("Starting OPC-UA Connector")

    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 __check_connection(self):
        try:
            node = self.client.get_root_node()
            node.get_children()
            if not self.__server_conf.get("disableSubscriptions", False) and (
                    not self.__connected or not self.subscribed):
                self.__sub = self.client.create_subscription(
                    self.__server_conf.get("subCheckPeriodInMillis", 500),
                    self.__sub_handler)
            self.__connected = True
        except ConnectionRefusedError:
            self.__connected = False
            self._subscribed = {}
            self.__available_object_resources = {}
            self.__sub = None
        except OSError:
            self.__connected = False
            self._subscribed = {}
            self.__available_object_resources = {}
            self.__sub = None
        except FuturesTimeoutError:
            self.__connected = False
            self._subscribed = {}
            self.__available_object_resources = {}
            self.__sub = None
        except AttributeError:
            self.__connected = False
            self._subscribed = {}
            self.__available_object_resources = {}
            self.__sub = None
        except Exception as e:
            self.__connected = False
            self._subscribed = {}
            self.__available_object_resources = {}
            self.__sub = None
            log.exception(e)

    def close(self):
        self.__stopped = True
        if self.__connected:
            self.client.disconnect()
        self.__connected = False
        log.info('%s has been stopped.', self.get_name())

    def get_name(self):
        return self.name

    def on_attributes_update(self, content):
        log.debug(content)
        try:
            for server_variables in self.__available_object_resources[
                    content["device"]]['variables']:
                for attribute in content["data"]:
                    for variable in server_variables:
                        if attribute == variable:
                            try:
                                server_variables[variable].set_value(
                                    content["data"][variable])
                            except Exception:
                                server_variables[variable].set_attribute(
                                    ua.AttributeIds.Value,
                                    ua.DataValue(content["data"][variable]))
        except Exception as e:
            log.exception(e)

    def server_side_rpc_handler(self, content):
        try:
            rpc_method = content["data"].get("method")
            for method in self.__available_object_resources[
                    content["device"]]['methods']:
                if rpc_method is not None and method.get(
                        rpc_method) is not None:
                    arguments_from_config = method["arguments"]
                    arguments = content["data"].get(
                        "params") if content["data"].get(
                            "params") is not None else arguments_from_config
                    try:
                        if isinstance(arguments, list):
                            result = method["node"].call_method(
                                method[rpc_method], *arguments)
                        elif arguments is not None:
                            try:
                                result = method["node"].call_method(
                                    method[rpc_method], arguments)
                            except ua.UaStatusCodeError as e:
                                if "BadTypeMismatch" in str(e) and isinstance(
                                        arguments, int):
                                    result = method["node"].call_method(
                                        method[rpc_method], float(arguments))
                        else:
                            result = method["node"].call_method(
                                method[rpc_method])

                        self.__gateway.send_rpc_reply(
                            content["device"], content["data"]["id"], {
                                content["data"]["method"]: result,
                                "code": 200
                            })
                        log.debug("method %s result is: %s",
                                  method[rpc_method], result)
                    except Exception as e:
                        log.exception(e)
                        self.__gateway.send_rpc_reply(content["device"],
                                                      content["data"]["id"], {
                                                          "error": str(e),
                                                          "code": 500
                                                      })
                else:
                    log.error("Method %s not found for device %s", rpc_method,
                              content["device"])
                    self.__gateway.send_rpc_reply(
                        content["device"], content["data"]["id"], {
                            "error": "%s - Method not found" % (rpc_method),
                            "code": 404
                        })
        except Exception as e:
            log.exception(e)

    def __initialize_client(self):
        self.__opcua_nodes["root"] = self.client.get_objects_node()
        self.__opcua_nodes["objects"] = self.client.get_objects_node()
        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)

    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 FuturesTimeoutError:
                            self.__check_connection()
                        except Exception as e:
                            log.exception(e)
                log.debug(self.__interest_nodes)
        except Exception as e:
            log.exception(e)

    def __search_nodes_and_subscribe(self, device_info):
        sub_nodes = []
        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:
                        try:
                            information_value = information_node.get_value()
                        except:
                            log.error("Err get_value: %s",
                                      str(information_node))
                            continue
                        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 = TBModuleLoader.import_module(
                                    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'] = self.statistics[
                            'MessagesReceived'] + 1
                        self.data_to_send.append(converted_data)
                        self.statistics['MessagesSent'] = self.statistics[
                            'MessagesSent'] + 1
                        log.debug("Data to ThingsBoard: %s", converted_data)
                        if not self.__server_conf.get("disableSubscriptions",
                                                      False):
                            sub_nodes.append(information_node)
                    else:
                        log.error(
                            "Node for %s \"%s\" with path %s - NOT FOUND!",
                            information_type, information_key,
                            information_path)
        if not self.__server_conf.get("disableSubscriptions", False):
            if self.__sub is None:
                self.__sub = self.client.create_subscription(
                    self.__server_conf.get("subCheckPeriodInMillis", 500),
                    self.__sub_handler)
            if sub_nodes:
                self.__sub.subscribe_data_change(sub_nodes)
                log.debug("Added subscription to nodes: %s", str(sub_nodes))

    def __save_methods(self, device_info):
        try:
            if self.__available_object_resources.get(
                    device_info["deviceName"]) is None:
                self.__available_object_resources[
                    device_info["deviceName"]] = {}
            if self.__available_object_resources[
                    device_info["deviceName"]].get("methods") is None:
                self.__available_object_resources[
                    device_info["deviceName"]]["methods"] = []
            if device_info["configuration"].get("rpc_methods", []):
                node = device_info["deviceNode"]
                for method_object in device_info["configuration"][
                        "rpc_methods"]:
                    method_node_path = self._check_path(
                        method_object["method"], node)
                    methods = []
                    self.__search_node(node,
                                       method_node_path,
                                       True,
                                       result=methods)
                    for method in methods:
                        if method is not None:
                            node_method_name = method.get_display_name().Text
                            self.__available_object_resources[
                                device_info["deviceName"]]["methods"].append({
                                    node_method_name:
                                    method,
                                    "node":
                                    node,
                                    "arguments":
                                    method_object.get("arguments")
                                })
                        else:
                            log.error(
                                "Node for method with path %s - NOT FOUND!",
                                method_node_path)
        except Exception as e:
            log.exception(e)

    def __search_attribute_update_variables(self, device_info):
        try:
            if device_info["configuration"].get("attributes_updates", []):
                node = device_info["deviceNode"]
                device_name = device_info["deviceName"]
                if self.__available_object_resources.get(device_name) is None:
                    self.__available_object_resources[device_name] = {}
                if self.__available_object_resources[device_name].get(
                        "variables") is None:
                    self.__available_object_resources[device_name][
                        "variables"] = []
                for attribute_update in device_info["configuration"][
                        "attributes_updates"]:
                    attribute_path = self._check_path(
                        attribute_update["attributeOnDevice"], node)
                    attribute_nodes = []
                    self.__search_node(node,
                                       attribute_path,
                                       result=attribute_nodes)
                    for attribute_node in attribute_nodes:
                        if attribute_node is not None:
                            self.__available_object_resources[device_name][
                                "variables"].append({
                                    attribute_update["attributeOnThingsBoard"]:
                                    attribute_node
                                })
                        else:
                            log.error(
                                "Attribute update node with path \"%s\" - NOT FOUND!",
                                attribute_path)
        except Exception as e:
            log.exception(e)

    def __search_general_info(self, device):
        result = []
        match_devices = []
        self.__search_node(self.__opcua_nodes["root"],
                           TBUtility.get_value(device["deviceNodePattern"],
                                               get_tag=True),
                           result=match_devices)
        for device_node in match_devices:
            if device_node is not None:
                result_device_dict = {
                    "deviceName": None,
                    "deviceType": None,
                    "deviceNode": device_node,
                    "configuration": deepcopy(device)
                }
                name_pattern_config = device["deviceNamePattern"]
                name_expression = TBUtility.get_value(name_pattern_config,
                                                      get_tag=True)
                if "${" in name_pattern_config and "}" in name_pattern_config:
                    log.debug("Looking for device name")
                    device_name_from_node = ""
                    if name_expression == "$DisplayName":
                        device_name_from_node = device_node.get_display_name(
                        ).Text
                    elif name_expression == "$BrowseName":
                        device_name_from_node = device_node.get_browse_name(
                        ).Name
                    elif name_expression == "$NodeId.Identifier":
                        device_name_from_node = str(
                            device_node.nodeid.Identifier)
                    else:
                        name_path = self._check_path(name_expression,
                                                     device_node)
                        device_name_node = []
                        self.__search_node(device_node,
                                           name_path,
                                           result=device_name_node)
                        device_name_node = device_name_node[0]
                        if device_name_node is not None:
                            device_name_from_node = device_name_node.get_value(
                            )
                    if device_name_from_node == "":
                        log.error(
                            "Device name node not found with expression: %s",
                            name_expression)
                        return None
                    full_device_name = name_pattern_config.replace(
                        "${" + name_expression + "}",
                        str(device_name_from_node)).replace(
                            name_expression, str(device_name_from_node))
                else:
                    full_device_name = name_expression
                result_device_dict["deviceName"] = full_device_name
                log.debug("Device name: %s", full_device_name)
                if device.get("deviceTypePattern"):
                    device_type_expression = TBUtility.get_value(
                        device["deviceTypePattern"], get_tag=True)
                    if "${" in device_type_expression and "}" in device_type_expression:
                        type_path = self._check_path(device_type_expression,
                                                     device_node)
                        device_type_node = []
                        self.__search_node(device_node,
                                           type_path,
                                           result=device_type_node)
                        device_type_node = device_type_node[0]
                        if device_type_node is not None:
                            device_type = device_type_node.get_value()
                            full_device_type = device_type_expression.replace(
                                "${" + device_type_expression + "}",
                                device_type).replace(device_type_expression,
                                                     device_type)
                        else:
                            log.error(
                                "Device type node not found with expression: %s",
                                device_type_expression)
                            full_device_type = "default"
                    else:
                        full_device_type = device_type_expression
                    result_device_dict["deviceType"] = full_device_type
                    log.debug("Device type: %s", full_device_type)
                else:
                    result_device_dict["deviceType"] = "default"
                result.append(result_device_dict)
            else:
                log.error(
                    "Device node not found with expression: %s",
                    TBUtility.get_value(device["deviceNodePattern"],
                                        get_tag=True))
        return result

    #
    # get fullpath of node as string
    #
    # this is verry slow
    # path = '\\.'.join(char.split(":")[1] for char in node.get_path(200000, True))
    # i think we don't need \\.
    #
    def get_node_path(self, node):
        ID = node.nodeid.Identifier
        if ID == 85:
            return 'Root.Objects'  # this is Root
        if type(ID) == int:
            ID = node.get_browse_name(
            ).Name  # for int Identifer we take browsename
        return 'Root.Objects.' + ID

    def __search_node(self,
                      current_node,
                      fullpath,
                      search_method=False,
                      result=None):
        if result is None:
            result = []
        try:
            if regex.match(r"ns=\d*;[isgb]=.*", fullpath, regex.IGNORECASE):
                if self.__show_map:
                    log.debug("Looking for node with config")
                node = self.client.get_node(fullpath)
                if node is None:
                    log.warning("NODE NOT FOUND - using configuration %s",
                                fullpath)
                else:
                    log.debug("Found in %s", node)
                    result.append(node)
            else:
                fullpath_pattern = regex.compile(fullpath)
                full1 = fullpath.replace('\\\\.', '.')
                #current_node_path = '\\.'.join(char.split(":")[1] for char in current_node.get_path(200000, True))
                current_node_path = self.get_node_path(current_node)
                # we are allways the parent
                child_node_parent_class = current_node.get_node_class()
                new_parent = current_node
                for child_node in current_node.get_children():
                    new_node_class = child_node.get_node_class()
                    # this will not change you can do it outside th loop
                    # basis Description of node.get_parent() function, sometime child_node.get_parent() return None
                    #new_parent = child_node.get_parent()
                    #if (new_parent is None):
                    #    child_node_parent_class = current_node.get_node_class()
                    #else:
                    #    child_node_parent_class = child_node.get_parent().get_node_class()
                    #current_node_path = '\\.'.join(char.split(":")[1] for char in current_node.get_path(200000, True))
                    #new_node_path = '\\\\.'.join(char.split(":")[1] for char in child_node.get_path(200000, True))
                    new_node_path = self.get_node_path(child_node)
                    if child_node_parent_class == ua.NodeClass.View and new_parent is not None:
                        parent_path = self.get_node_path(new_parent)
                        #parent_path = '\\.'.join(char.split(":")[1] for char in new_parent.get_path(200000, True))
                        fullpath = fullpath.replace(current_node_path,
                                                    parent_path)
                    nnp1 = new_node_path.replace('\\\\.', '.')
                    nnp2 = new_node_path.replace('\\\\', '\\')
                    if self.__show_map:
                        log.debug("SHOW MAP: Current node path: %s",
                                  new_node_path)
                    regex_fullmatch = regex.fullmatch(fullpath_pattern,  nnp1) or \
                                      nnp2 == full1 or \
                                      nnp2 == fullpath or \
                                      nnp1 == full1
                    if regex_fullmatch:
                        if self.__show_map:
                            log.debug(
                                "SHOW MAP: Current node path: %s - NODE FOUND",
                                nnp2)
                        result.append(child_node)
                    else:
                        regex_search = fullpath_pattern.fullmatch(nnp1, partial=True) or \
                                          nnp2 in full1 or \
                                          nnp1 in full1
                        if regex_search:
                            if self.__show_map:
                                log.debug(
                                    "SHOW MAP: Current node path: %s - NODE FOUND",
                                    new_node_path)
                            if new_node_class == ua.NodeClass.Object:
                                if self.__show_map:
                                    log.debug("SHOW MAP: Search in %s",
                                              new_node_path)
                                self.__search_node(child_node,
                                                   fullpath,
                                                   result=result)
                            elif new_node_class == ua.NodeClass.Variable:
                                log.debug("Found in %s", new_node_path)
                                result.append(child_node)
                            elif new_node_class == ua.NodeClass.Method and search_method:
                                log.debug("Found in %s", new_node_path)
                                result.append(child_node)
        except CancelledError:
            log.error(
                "Request during search has been canceled by the OPC-UA server."
            )
        except BrokenPipeError:
            log.error("Broken Pipe. Connection lost.")
        except OSError:
            log.debug("Stop on scanning.")
        except Exception as e:
            log.exception(e)

    def _check_path(self, config_path, node):
        if regex.match(r"ns=\d*;[isgb]=.*", config_path, regex.IGNORECASE):
            return config_path
        if re.search(r"^root", config_path.lower()) is None:
            node_path = self.get_node_path(node)
            #node_path = '\\\\.'.join(char.split(":")[1] for char in node.get_path(200000, True))
            if config_path[-3:] != '\\.':
                information_path = node_path + '\\\\.' + config_path.replace(
                    '\\', '\\\\')
            else:
                information_path = node_path + config_path.replace(
                    '\\', '\\\\')
        else:
            information_path = config_path
        result = information_path[:]
        return result

    @property
    def subscribed(self):
        return self._subscribed
Ejemplo n.º 14
0
class OpcUaConnector(Thread, Connector):
    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 = {}
        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 Default ' +
                ''.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 is_connected(self):
        return self.__connected

    def open(self):
        self.__stopped = False
        self.start()
        log.info("Starting OPC-UA Connector")

    def run(self):
        while not self.__connected:
            try:
                self.__connected = self.client.connect()
                self.client.load_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 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_root_node()
        self.__opcua_nodes["objects"] = self.client.get_objects_node()
        sub = self.client.create_subscription(
            self.__server_conf.get("scanPeriodInMillis", 500),
            self.__sub_handler)
        self.__search_name(self.__opcua_nodes["objects"], 2)
        self.__search_tags(self.__opcua_nodes["objects"], 2, sub)
        log.debug('Subscriptions: %s', self.subscribed)

        log.debug("Available methods: %s", self.__available_object_resources)
        while True:
            try:
                time.sleep(1)
                if self.data_to_send:
                    self.__gateway.send_to_storage(self.get_name(),
                                                   self.data_to_send.pop())
                if self.__stopped:
                    break
            except (KeyboardInterrupt, SystemExit):
                self.close()
                raise
            except Exception as e:
                self.close()
                log.exception(e)

    def close(self):
        self.__stopped = True
        self.client.disconnect()
        self.__connected = False
        log.info('%s has been stopped.', self.get_name())

    def get_name(self):
        return self.name

    def on_attributes_update(self, content):
        log.debug(content)
        try:
            for server_variables in self.__available_object_resources[
                    content["device"]]['variables']:
                for attribute in content["data"]:
                    for variable in server_variables:
                        if attribute == variable:
                            server_variables[variable].set_value(
                                content["data"][variable])
        except Exception as e:
            log.exception(e)

    def server_side_rpc_handler(self, content):
        try:
            for method in self.__available_object_resources[
                    content["device"]]['methods']:
                rpc_method = content["data"].get("method")
                if rpc_method is not None and method.get(
                        rpc_method) is not None:
                    arguments = content["data"].get("params")
                    if type(arguments) is list:
                        result = method["node"].call_method(
                            method[rpc_method], *arguments)
                    elif arguments is not None:
                        result = method["node"].call_method(
                            method[rpc_method], arguments)
                    else:
                        result = method["node"].call_method(method[rpc_method])

                    self.__gateway.send_rpc_reply(
                        content["device"], content["data"]["id"],
                        {content["data"]["method"]: result})

                    log.debug("method %s result is: %s", method[rpc_method],
                              result)
        except Exception as e:
            log.exception(e)

    def __search_name(self, node, recursion_level):
        try:
            for childId in node.get_children():
                ch = self.client.get_node(childId)
                current_var_path = '.'.join(
                    x.split(":")[1] for x in ch.get_path(20000, True))
                if self.__interest_nodes:
                    if ch.get_node_class() == ua.NodeClass.Object:
                        for interest_node in self.__interest_nodes:
                            for int_node in interest_node:
                                subrecursion_level = recursion_level
                                if subrecursion_level != recursion_level + len(
                                        interest_node[int_node]
                                    ["deviceNamePattern"].split("\\.")):
                                    if ch.get_display_name().Text in TBUtility.get_value(interest_node[int_node]["deviceNamePattern"], get_tag=True).split('.') or \
                                            re.search(TBUtility.get_value(interest_node[int_node]["deviceNodePattern"], get_tag=True), ch.get_display_name().Text):
                                        self.__search_name(
                                            ch, subrecursion_level + 1)
                                else:
                                    return
                    elif ch.get_node_class() == ua.NodeClass.Variable:
                        try:
                            for interest_node in self.__interest_nodes:
                                for int_node in interest_node:
                                    if interest_node[int_node].get(
                                            "deviceName") is None:
                                        try:
                                            name_pattern = TBUtility.get_value(
                                                interest_node[int_node]
                                                ["deviceNamePattern"],
                                                get_tag=True)
                                            log.debug(current_var_path)
                                            device_name_node = re.search(
                                                name_pattern.split('\\.')[-1],
                                                current_var_path)
                                            if device_name_node is not None:
                                                device_name = ch.get_value()
                                                if "${" + name_pattern + "}" in interest_node[
                                                        int_node][
                                                            "deviceNamePattern"]:
                                                    full_device_name = interest_node[
                                                        int_node][
                                                            "deviceNamePattern"].replace(
                                                                "${" +
                                                                name_pattern +
                                                                "}",
                                                                device_name)
                                                elif device_name in interest_node[
                                                        int_node][
                                                            "deviceNamePattern"]:
                                                    full_device_name = interest_node[
                                                        int_node][
                                                            "deviceNamePattern"].replace(
                                                                name_pattern,
                                                                device_name)
                                                else:
                                                    log.error(
                                                        "Name pattern not found."
                                                    )
                                                    break
                                                interest_node[int_node][
                                                    "deviceName"] = full_device_name
                                                if self.__available_object_resources.get(
                                                        full_device_name
                                                ) is None:
                                                    self.__available_object_resources[
                                                        full_device_name] = {
                                                            'methods': [],
                                                            'variables': []
                                                        }
                                                if not self.__gateway.get_devices(
                                                ).get(full_device_name):
                                                    self.__gateway.add_device(
                                                        full_device_name,
                                                        {"connector": None})
                                                self.__gateway.update_device(
                                                    full_device_name,
                                                    "connector", self)
                                            else:
                                                try:
                                                    if re.search(
                                                            int_node.split(
                                                                '\\.')
                                                        [recursion_level - 2],
                                                            ch.
                                                            get_display_name(
                                                            ).Text):
                                                        self.__search_name(
                                                            ch,
                                                            recursion_level +
                                                            1)
                                                except IndexError:
                                                    if re.search(
                                                            int_node.split(
                                                                '\\.')[-1],
                                                            ch.
                                                            get_display_name(
                                                            ).Text):
                                                        self.__search_name(
                                                            ch,
                                                            recursion_level +
                                                            1)

                                        except Exception as e:
                                            log.exception(e)
                                    else:
                                        break
                        except BadWaitingForInitialData:
                            pass
                elif not self.__interest_nodes:
                    log.error(
                        "Nodes in mapping not found, check your settings.")
        except Exception as e:
            log.exception(e)

    def __search_tags(self, node, recursion_level, sub=None):
        try:
            for childId in node.get_children():
                ch = self.client.get_node(childId)
                current_var_path = '.'.join(
                    x.split(":")[1] for x in ch.get_path(20000, True))
                if self.__interest_nodes:
                    if ch.get_node_class() == ua.NodeClass.Object:
                        for interest_node in self.__interest_nodes:
                            for int_node in interest_node:
                                try:
                                    name_to_check = int_node.split('\\.')[
                                        recursion_level -
                                        1] if '\\.' in int_node else int_node
                                    name_to_check = int_node.split(
                                        '.'
                                    )[recursion_level -
                                      1] if '.' in int_node else name_to_check
                                except IndexError:
                                    name_to_check = int_node.split(
                                        '\\.'
                                    )[-1] if '\\.' in int_node else int_node
                                    name_to_check = int_node.split(
                                        '.'
                                    )[-1] if '.' in int_node else name_to_check
                                if re.search(name_to_check,
                                             ch.get_display_name().Text):
                                    try:
                                        methods = ch.get_methods()
                                        for method in methods:
                                            self.__available_object_resources[
                                                interest_node[int_node]
                                                ["deviceName"]][
                                                    "methods"].append({
                                                        method.get_display_name(
                                                        ).Text:
                                                        method,
                                                        "node":
                                                        ch
                                                    })
                                    except Exception as e:
                                        log.exception(e)
                                for tag in interest_node[int_node][
                                        "timeseries"] + interest_node[
                                            int_node]["attributes"]:
                                    subrecursion_level = recursion_level
                                    if subrecursion_level != recursion_level + len(
                                            tag["path"].split("\\.")):
                                        self.__search_tags(
                                            ch, subrecursion_level + 1, sub)
                                    else:
                                        return
                                self.__search_tags(ch, recursion_level + 1,
                                                   sub)
                    elif ch.get_node_class() == ua.NodeClass.Variable:
                        try:
                            for interest_node in self.__interest_nodes:
                                for int_node in interest_node:
                                    if interest_node[int_node].get(
                                            "attributes_updates"):
                                        try:
                                            for attribute_update in interest_node[
                                                    int_node][
                                                        "attributes_updates"]:
                                                if attribute_update[
                                                        "attributeOnDevice"] == ch.get_display_name(
                                                        ).Text:
                                                    self.__available_object_resources[
                                                        interest_node[int_node]
                                                        ["deviceName"]][
                                                            'variables'].append({
                                                                attribute_update["attributeOnThingsBoard"]:
                                                                ch,
                                                            })
                                        except Exception as e:
                                            log.exception(e)
                                    name_to_check = int_node.split(
                                        '\\.'
                                    )[-1] if '\\.' in int_node else int_node
                                    name_to_check = int_node.split(
                                        '.'
                                    )[-1] if '.' in int_node else name_to_check
                                    if re.search(
                                            name_to_check.replace('$', ''),
                                            current_var_path):
                                        tags = []
                                        if interest_node[int_node].get(
                                                "attributes"):
                                            tags.extend(interest_node[int_node]
                                                        ["attributes"])
                                        if interest_node[int_node].get(
                                                "timeseries"):
                                            tags.extend(interest_node[int_node]
                                                        ["timeseries"])
                                        for tag in tags:
                                            target = TBUtility.get_value(
                                                tag["path"], get_tag=True)
                                            try:
                                                tag_name_for_check = target.split(
                                                    '\\.'
                                                )[recursion_level -
                                                  1] if '\\.' in target else target
                                                tag_name_for_check = target.split(
                                                    '.'
                                                )[recursion_level -
                                                  1] if '.' in target else tag_name_for_check
                                            except IndexError:
                                                tag_name_for_check = target.split(
                                                    '\\.'
                                                )[-1] if '\\.' in target else target
                                                tag_name_for_check = target.split(
                                                    '.'
                                                )[-1] if '.' in target else tag_name_for_check
                                            current_node_name = ch.get_display_name(
                                            ).Text
                                            if current_node_name == tag_name_for_check:
                                                sub.subscribe_data_change(ch)
                                                if interest_node[int_node].get(
                                                        "uplink_converter"
                                                ) is None:
                                                    if interest_node[
                                                            int_node].get(
                                                                'converter'
                                                            ) is None:
                                                        converter = OpcUaUplinkConverter(
                                                            interest_node[
                                                                int_node])
                                                    else:
                                                        converter = TBUtility.check_and_import(
                                                            self.
                                                            __connector_type,
                                                            interest_node[
                                                                int_node]
                                                            ['converter'])
                                                    interest_node[int_node][
                                                        "uplink_converter"] = converter
                                                else:
                                                    converter = interest_node[
                                                        int_node][
                                                            "uplink_converter"]
                                                self.subscribed[ch] = {
                                                    "converter": converter,
                                                    "path": current_var_path
                                                }
                                    else:
                                        return
                        except BadWaitingForInitialData:
                            pass
                    elif not self.__interest_nodes:
                        log.error(
                            "Nodes in mapping not found, check your settings.")
        except Exception as e:
            log.exception(e)

    @property
    def subscribed(self):
        return self._subscribed
Ejemplo n.º 15
0
        print("Python: New data change event", node, val)

    def event_notification(self, event):
        print("Python: New event", event)


if __name__ == "__main__":
    logging.basicConfig(level=logging.WARN)
    #logger = logging.getLogger("KeepAlive")
    #logger.setLevel(logging.DEBUG)

    client = Client("opc.tcp://192.168.1.112:50840/freeopcua/server/")
    # client = Client("opc.tcp://admin@localhost:4840/freeopcua/server/") #connect using a user
    try:
        client.connect()
        client.load_type_definitions(
        )  # Ladet Definition von serverspezifischen Strukturen / Erweiterungsobjekten

        # Der Client verfügt über einige Methoden, um einen Proxy für UA-Knoten abzurufen,
        # die sich immer im Adressraum befinden sollten, z. B. Root oder Objects

        root = client.get_root_node()
        print("Root node is: ", root)
        objects = client.get_objects_node()
        print("Objects node is: ", objects)

        # Knotenobjekte verfügen über Methoden zum Lesen und Schreiben von Knotenattributen
        # sowie zum Durchsuchen oder Auffüllen des Adressraums
        print("Children of root are: ", root.get_children())

        # Holen Sie sich Informationen eines Knoten mit Kenntnis seiner Knoten-ID
Ejemplo n.º 16
0
        set_bool(read_b12,0,1,s_floor.get_value())
        plc.write_area( areas['MK'], 0,12,read_b12 )

        set_bool(read_b12,0,2,t_floor.get_value())
        plc.write_area( areas['MK'], 0,12,read_b12 )

        time.sleep(.01)

if __name__ == "__main__":
    try:
        opc_client.connect()
    except Exception as identifier:
        print("OPCUA Error:")
        print(identifier)
    else:
        opc_client.load_type_definitions()
        root = opc_client.get_root_node()
        objects = opc_client.get_objects_node()
        idx = opc_client.get_namespace_index(opc_uri)

        obj = root.get_child(["0:Objects", "{}:PLC_S71200".format(idx)])

        # dest =  root.get_child(["0:Objects", "{}:PLC_S71200".format(idx), "{}:Destination".format(idx)])
        dest = opc_client.get_node("ns=2;i=6")
        # disp = root.get_child(["0:Objects", "{}:PLC_S71200".format(idx), "{}:Displacement".format(idx)])
        disp = opc_client.get_node("ns=2;i=3")

        # spd = root.get_child(["0:Objects", "{}:PLC_S71200".format(idx), "{}:Speed".format(idx)])
        aspd = opc_client.get_node("ns=2;i=4")
        lspd = opc_client.get_node("ns=2;i=5")
Ejemplo n.º 17
0
class OpcUaConnector(Thread, Connector):
    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 is_connected(self):
        return self.__connected

    def open(self):
        self.__stopped = False
        self.start()
        log.info("Starting OPC-UA Connector")

    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 __check_connection(self):
        try:
            node = self.client.get_root_node()
            node.get_children()
            self.__connected = True
        except ConnectionRefusedError:
            self.__connected = False
            self._subscribed = {}
            self.__sub = None
        except OSError:
            self.__connected = False
            self._subscribed = {}
            self.__sub = None
        except TimeoutError:
            self.__connected = False
            self._subscribed = {}
            self.__sub = None
        except AttributeError:
            self.__connected = False
            self._subscribed = {}
            self.__sub = None
        except Exception as e:
            self.__connected = False
            self._subscribed = {}
            self.__sub = None
            log.exception(e)

    def close(self):
        self.__stopped = True
        if self.__connected:
            self.client.disconnect()
        self.__connected = False
        log.info('%s has been stopped.', self.get_name())

    def get_name(self):
        return self.name

    def on_attributes_update(self, content):
        log.debug(content)
        try:
            for server_variables in self.__available_object_resources[
                    content["device"]]['variables']:
                for attribute in content["data"]:
                    for variable in server_variables:
                        if attribute == variable:
                            server_variables[variable].set_value(
                                content["data"][variable])
        except Exception as e:
            log.exception(e)

    def server_side_rpc_handler(self, content):
        try:
            for method in self.__available_object_resources[
                    content["device"]]['methods']:
                rpc_method = content["data"].get("method")
                if rpc_method is not None and method.get(
                        rpc_method) is not None:
                    arguments_from_config = method["arguments"]
                    arguments = content["data"].get(
                        "params") if content["data"].get(
                            "params") is not None else arguments_from_config
                    try:
                        if type(arguments) is list:
                            result = method["node"].call_method(
                                method[rpc_method], *arguments)
                        elif arguments is not None:
                            result = method["node"].call_method(
                                method[rpc_method], arguments)
                        else:
                            result = method["node"].call_method(
                                method[rpc_method])

                        self.__gateway.send_rpc_reply(
                            content["device"], content["data"]["id"], {
                                content["data"]["method"]: result,
                                "code": 200
                            })
                        log.debug("method %s result is: %s",
                                  method[rpc_method], result)
                    except Exception as e:
                        log.exception(e)
                        self.__gateway.send_rpc_reply(content["device"],
                                                      content["data"]["id"], {
                                                          "error": str(e),
                                                          "code": 500
                                                      })
                else:
                    log.error("Method %s not found for device %s", rpc_method,
                              content["device"])
                    self.__gateway.send_rpc_reply(
                        content["device"], content["data"]["id"], {
                            "error": "%s - Method not found" % (rpc_method),
                            "code": 404
                        })
        except Exception as e:
            log.exception(e)

    def __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 Exception as e:
                            log.exception(e)
                log.debug(self.__interest_nodes)
        except Exception as e:
            log.exception(e)

    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 __save_methods(self, device_info):
        try:
            if self.__available_object_resources.get(
                    device_info["deviceName"]) is None:
                self.__available_object_resources[
                    device_info["deviceName"]] = {}
            if self.__available_object_resources[
                    device_info["deviceName"]].get("methods") is None:
                self.__available_object_resources[
                    device_info["deviceName"]]["methods"] = []
            if device_info["configuration"].get("rpc_methods"):
                node = device_info["deviceNode"]
                for method_object in device_info["configuration"][
                        "rpc_methods"]:
                    method_node_path = self._check_path(
                        method_object["method"], node)
                    methods = []
                    self.__search_node(node,
                                       method_node_path,
                                       True,
                                       result=methods)
                    for method in methods:
                        if method is not None:
                            node_method_name = method.get_display_name().Text
                            self.__available_object_resources[
                                device_info["deviceName"]]["methods"].append({
                                    node_method_name:
                                    method,
                                    "node":
                                    node,
                                    "arguments":
                                    method_object.get("arguments")
                                })
                        else:
                            log.error(
                                "Node for method with path %s - NOT FOUND!",
                                method_node_path)
        except Exception as e:
            log.exception(e)

    def __search_attribute_update_variables(self, device_info):
        try:
            if device_info["configuration"].get("attributes_updates"):
                node = device_info["deviceNode"]
                device_name = device_info["deviceName"]
                if self.__available_object_resources.get(device_name) is None:
                    self.__available_object_resources[device_name] = {}
                if self.__available_object_resources[device_name].get(
                        "variables") is None:
                    self.__available_object_resources[device_name][
                        "variables"] = []
                for attribute_update in device_info["configuration"][
                        "attributes_updates"]:
                    attribute_path = self._check_path(
                        attribute_update["attributeOnDevice"], node)
                    attribute_nodes = []
                    self.__search_node(node,
                                       attribute_path,
                                       result=attribute_nodes)
                    for attribute_node in attribute_nodes:
                        if attribute_node is not None:
                            self.__available_object_resources[device_name][
                                "variables"].append({
                                    attribute_update["attributeOnThingsBoard"]:
                                    attribute_node
                                })
                        else:
                            log.error(
                                "Attribute update node with path \"%s\" - NOT FOUND!",
                                attribute_path)
        except Exception as e:
            log.exception(e)

    def __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
                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 __search_node(self,
                      current_node,
                      fullpath,
                      search_method=False,
                      result=[]):
        fullpath_pattern = regex.compile(fullpath)
        try:
            for child_node in current_node.get_children():
                new_node = self.client.get_node(child_node)
                new_node_path = '\\\\.'.join(
                    char.split(":")[1]
                    for char in new_node.get_path(200000, True))
                if self.__show_map:
                    log.debug("SHOW MAP: Current node path: %s", new_node_path)
                new_node_class = new_node.get_node_class()
                # regex_fullmatch = re.fullmatch(fullpath, new_node_path.replace('\\\\.', '.')) or new_node_path.replace('\\\\', '\\') == fullpath
                regex_fullmatch = regex.fullmatch(fullpath_pattern, new_node_path.replace('\\\\.', '.')) or \
                                  new_node_path.replace('\\\\', '\\') == fullpath.replace('\\\\', '\\') or \
                                  new_node_path.replace('\\\\', '\\') == fullpath
                regex_search = fullpath_pattern.fullmatch(new_node_path.replace('\\\\.', '.'), partial=True) or \
                                  new_node_path.replace('\\\\', '\\') in fullpath.replace('\\\\', '\\')
                # regex_search = re.search(new_node_path, fullpath.replace('\\\\', '\\'))
                if regex_fullmatch:
                    if self.__show_map:
                        log.debug(
                            "SHOW MAP: Current node path: %s - NODE FOUND",
                            new_node_path.replace('\\\\', '\\'))
                    result.append(new_node)
                elif regex_search:
                    if self.__show_map:
                        log.debug(
                            "SHOW MAP: Current node path: %s - NODE FOUND",
                            new_node_path)
                    if new_node_class == ua.NodeClass.Object:
                        if self.__show_map:
                            log.debug("SHOW MAP: Search in %s", new_node_path)
                        self.__search_node(new_node, fullpath, result=result)
                    elif new_node_class == ua.NodeClass.Variable:
                        log.debug("Found in %s", new_node_path)
                        result.append(new_node)
                    elif new_node_class == ua.NodeClass.Method and search_method:
                        log.debug("Found in %s", new_node_path)
                        result.append(new_node)
        except Exception as e:
            log.exception(e)

    def _check_path(self, config_path, node):
        if re.search("^root", config_path.lower()) is None:
            node_path = '\\\\.'.join(
                char.split(":")[1] for char in node.get_path(200000, True))
            if config_path[-3:] != '\\.':
                information_path = node_path + '\\\\.' + config_path.replace(
                    '\\', '\\\\')
            else:
                information_path = node_path + config_path.replace(
                    '\\', '\\\\')
        else:
            information_path = config_path
        return information_path[:]

    @property
    def subscribed(self):
        return self._subscribed
import sys
sys.path.insert(0, "..")
import time
import logging
from IPython import embed

from opcua import Client
from opcua import ua



if __name__ == "__main__":
    logging.basicConfig(level=logging.WARN)
    client = Client("opc.tcp://opcua.demo-this.com:51210/UA/SampleServer")
    try:
        client.connect()
        root = client.get_root_node()
        objects = client.get_objects_node()
        struct = client.get_node("ns=2;i=10239")
        struct_array = client.get_node("ns=2;i=10323")
        before = struct.get_value()
        before_array = struct_array.get_value()
        client.load_type_definitions()  # scan server for custom structures and import them
        after = struct.get_value()
        after_array = struct_array.get_value()
        

        embed()
    finally:
        client.disconnect()
Ejemplo n.º 19
0
        print("Python: New data change event", node, val)

    def event_notification(self, event):
        print("Python: New event", event)


if __name__ == "__main__":
    logging.basicConfig(level=logging.WARN)
    #logger = logging.getLogger("KeepAlive")
    #logger.setLevel(logging.DEBUG)

    client = Client("opc.tcp://192.168.157.244:40840")
    # client = Client("opc.tcp://admin@localhost:4840/freeopcua/server/") #connect using a user
    try:
        client.connect()
        client.load_type_definitions(
        )  # load definition of server specific structures/extension objects

        # Client has a few methods to get proxy to UA nodes that should always be in address space such as Root or Objects
        root = client.get_root_node()
        print("Root node is: ", root)
        objects = client.get_objects_node()
        #for t in obje
        print("Objects node is: ", objects)

        # Node objects have methods to read and write node attributes as well as browse or populate address space
        print("Children of root are: ", root.get_children())

        # get a specific node knowing its node id
        #var = client.get_node(ua.NodeId(1002, 2))
        #var = client.get_node("ns=3;i=2002")
        #print(var)
Ejemplo n.º 20
0
class UaClient(object):
    """
    OPC-Ua client specialized for the need of GUI client
    return exactly what GUI needs, no customization possible
    """
    def __init__(self):
        self.settings = QSettings()
        self.client = None
        self._connected = False
        self._datachange_sub = None
        self._event_sub = None
        self._subs_dc = {}
        self._subs_ev = {}
        self.security_mode = None
        self.security_policy = None
        self.certificate_path = None
        self.private_key_path = None

    def _reset(self):
        self.client = None
        self._connected = False
        self._datachange_sub = None
        self._event_sub = None
        self._subs_dc = {}
        self._subs_ev = {}

    @staticmethod
    def get_endpoints(uri):
        client = Client(uri, timeout=2)
        edps = client.connect_and_get_server_endpoints()
        for i, ep in enumerate(edps, start=1):
            logger.info('Endpoint %s:', i)
            for (n, v) in endpoint_to_strings(ep):
                logger.info('  %s: %s', n, v)
            logger.info('')
        return edps

    def load_security_settings(self, uri):
        self.security_mode = None
        self.security_policy = None
        self.certificate_path = None
        self.private_key_path = None

        mysettings = self.settings.value("security_settings", None)
        if mysettings is None:
            return
        if uri in mysettings:
            mode, policy, cert, key = mysettings[uri]
            self.security_mode = mode
            self.security_policy = policy
            self.certificate_path = cert
            self.private_key_path = key

    def save_security_settings(self, uri):
        mysettings = self.settings.value("security_settings", None)
        if mysettings is None:
            mysettings = {}
        mysettings[uri] = [
            self.security_mode, self.security_policy, self.certificate_path,
            self.private_key_path
        ]
        self.settings.setValue("security_settings", mysettings)

    def get_node(self, nodeid):
        return self.client.get_node(nodeid)

    def connect(self, uri):
        self.disconnect()
        logger.info("Connecting to %s with parameters %s, %s, %s, %s", uri,
                    self.security_mode, self.security_policy,
                    self.certificate_path, self.private_key_path)
        self.client = Client(uri)
        if self.security_mode is not None and self.security_policy is not None:
            self.client.set_security(getattr(
                crypto.security_policies,
                'SecurityPolicy' + self.security_policy),
                                     self.certificate_path,
                                     self.private_key_path,
                                     mode=getattr(ua.MessageSecurityMode,
                                                  self.security_mode))
        self.client.connect()
        self._connected = True
        self.client.load_enums()
        self.client.load_type_definitions()
        self.save_security_settings(uri)

    def disconnect(self):
        if self._connected:
            print("Disconnecting from server")
            self._connected = False
            try:
                self.client.disconnect()
            finally:
                self._reset()

    def subscribe_datachange(self, node, handler):
        if not self._datachange_sub:
            self._datachange_sub = self.client.create_subscription(
                500, handler)
        handle = self._datachange_sub.subscribe_data_change(node)
        self._subs_dc[node.nodeid] = handle
        return handle

    def unsubscribe_datachange(self, node):
        self._datachange_sub.unsubscribe(self._subs_dc[node.nodeid])

    def subscribe_events(self, node, handler):
        if not self._event_sub:
            print("subscirbing with handler: ", handler, dir(handler))
            self._event_sub = self.client.create_subscription(500, handler)
        handle = self._event_sub.subscribe_events(node)
        self._subs_ev[node.nodeid] = handle
        return handle

    def unsubscribe_events(self, node):
        self._event_sub.unsubscribe(self._subs_ev[node.nodeid])

    def get_node_attrs(self, node):
        if not isinstance(node, Node):
            node = self.client.get_node(node)
        attrs = node.get_attributes([
            ua.AttributeIds.DisplayName, ua.AttributeIds.BrowseName,
            ua.AttributeIds.NodeId
        ])
        return node, [attr.Value.Value.to_string() for attr in attrs]

    @staticmethod
    def get_children(node):
        descs = node.get_children_descriptions()
        descs.sort(key=lambda x: x.BrowseName)
        return descs