Ejemplo n.º 1
0
    def deprovision_node_instance(self, instance_id: str) -> DeprovisionServiceSpec:
        if self.service_instance_map is None or instance_id not in self.service_instance_map:
            return DeprovisionServiceSpec(is_async=False)

        service_instance = self.service_instance_map.get(instance_id)
        url = service_instance.params.get("url")
        nodes = service_instance.params.get("nodes")
        print(nodes)

        client = Client(url)
        try:
            client.connect()
            client.delete_nodes(nodes)
        except Exception as e:
            print("Error: {0}\n".format(e))
            return DeprovisionServiceSpec(is_async=False)

        self.service_instance_map.pop(instance_id)

        print("Node service instance {0} is deprovisioned successfully\n".format(instance_id))
        return DeprovisionServiceSpec(is_async=False)
Ejemplo n.º 2
0
class OPCUAServer(object):
    """
    Each instance of this class manages a connection to its own OPC UA server.
    Methods are called to get node data from the server.
    """
    def __init__(self,
                 name,
                 endPointAddress,
                 nameSpaceUri=None,
                 browseRootNodeIdentifier=None):
        # ---------- Setup -----------
        self.name = name
        self.logger = logging.getLogger(self.name)
        self.endPointAddress = endPointAddress
        self.nameSpaceUri = nameSpaceUri
        self.nameSpaceIndex = None
        self.browseRootNodeIdentifier = browseRootNodeIdentifier
        self.rootNodeId = None
        self.client = Client(self.endPointAddress, timeout=2)
        self.sub = None
        self.subscriptions = {}
        # ----------------------------

    def check_connection(self):
        """
        Check if a connection has been established before
        or if connection thread is running.

        If either fails, try to (re)connect.
        """

        if self.client.uaclient._uasocket is None:
            self.connect()
        elif self.client.uaclient._uasocket._thread is None:
            self.connect()
        elif not self.client.uaclient._uasocket._thread.is_alive():
            self.connect()

    def connect(self):
        """
        Connect to OPC UA server.
        If fails clean up session and socket, and raise exception.
        """

        try:
            self.logger.info("Connecting to " + self.name + ".")
            self.client.connect()
            self.update_namespace_and_root_node_id()
        except socket.timeout:
            self.logger.info(self.name + " socket timed out.")
            try:
                self.logger.info("Cleaning up session and socket.")
                self.client.uaclient.disconnect()
            except AttributeError:
                pass
            self.logger.info("Socket and session cleaned up.")
            raise TimeoutError(self.name + " timed out.")

    def update_namespace_and_root_node_id(self):
        """
        Update rootNodeId and nameSpaceIndex.
        If no namespace given, sets root node (id: i=84) as root node.
        """

        if self.nameSpaceUri and self.browseRootNodeIdentifier:
            nsArray = self.client.get_namespace_array()
            index = nsArray.index(self.nameSpaceUri)
            if index > 0:
                nodeId = "ns={};".format(index) + self.browseRootNodeIdentifier
            else:
                nodeId = self.browseRootNodeIdentifier
        else:
            nodeId = "i=84"
            index = None

        self.rootNodeId = nodeId
        self.nameSpaceIndex = index
        return

    def get_node_path(self, nodeId):
        """
        Create node path from node id for current server settings.
        Attempts to create a path of node from rootNode.
        Only works for folderly like string node ids.
        Example: "ns=2;s=node1.node2.node3.node4"
        """

        identifierType = self.rootNodeId.split(";")[-1].split("=")[0]
        if (identifierType == "s") and ("." in self.rootNodeId):
            rootNodeName = self.rootNodeId.lower().split(".")[-1]
            nodePath = nodeId.lower().split(rootNodeName)[-1]
            nodePath = nodePath.replace(".", "", 1).replace(".", "/")
        else:
            nodePath = nodeId.split("=")[-1]

        return nodePath

    def get_node(self, nodeId=""):
        """
        Returns node from nodeId or identifier.
        If no namespace given in nodeId,
        assumes the namespace to namespace given for the server in settings.py.
        Only the ns set for the server in servers.json is accessible via
        browsing.
        """

        self.check_connection()

        if nodeId == "":
            nodeId = self.rootNodeId
        elif self.nameSpaceIndex is None:
            nodeId = nodeId
        elif nodeId[:3] == "ns=":
            identifier = nodeId.split(";")[-1]
            if self.nameSpaceIndex == 0:
                nodeId = identifier
            else:
                nodeId = f"ns={self.nameSpaceIndex};{identifier}"
        else:
            nodeId = f"ns={self.nameSpaceIndex};{nodeId}"

        return self.client.get_node(nodeId)

    async def get_variable_nodes(self,
                                 node,
                                 nodeClass=2,
                                 variableList=None,
                                 depth=0,
                                 maxDepth=10):
        """
        Eats a list of node object(s).
        Recursively finds nodes under given nodes that have given nodeClass.
        Returns node objects in a list.
        """

        if variableList is None:
            variableList = []

        depth += 1
        if depth >= maxDepth:
            return variableList

        nodes = node.get_children()
        params = ua.ReadParameters()
        for node in nodes:
            rv = ua.ReadValueId()
            rv.NodeId = node.nodeid
            rv.AttributeId = ua.AttributeIds.NodeClass
            params.NodesToRead.append(rv)

        results = []
        if len(params.NodesToRead) > 0:
            results, readTime = await self.read(params)

        for i in range(len(results)):
            if nodeClass == results[i].Value.Value:
                variableList.append(nodes[i])
            await self.get_variable_nodes(node=nodes[i],
                                          nodeClass=nodeClass,
                                          variableList=variableList,
                                          depth=depth)

        return variableList

    def subscribe_variable(self, nodeId):

        if self.sub is None:
            handler = self
            self.sub = self.client.create_subscription(100, handler)

        node = self.get_node(nodeId)
        if 2 == node.get_attribute(ua.AttributeIds.NodeClass).Value.Value:
            return self.sub.subscribe_data_change(node)
        else:
            return None

    def datachange_notification(self, node, value, data):

        self.subscriptions[node.nodeid.to_string()] = data.monitored_item.Value

    async def read_node_attribute(self, nodeId, attribute):
        """
        Read node attribute based on given arguments.
        Giving correct dataType for value and node speeds
        up the write operation.

        Arguments                               Example
        nodeId:     Target nodeId               "ns=2;i=2"
        attribute:  Target attribute of node    "Value"

        Results
        OPCUAVar:   OPC UA variable object      <object>
        readTime:   Time taken for read (ns)    12345678
        """

        rv = ua.ReadValueId()
        if nodeId == "":
            rv.NodeId = ua.NodeId.from_string(server.rootNodeId)
        else:
            rv.NodeId = ua.NodeId.from_string(nodeId)
        rv.AttributeId = ua.AttributeIds[attribute]

        params = ua.ReadParameters()
        params.NodesToRead.append(rv)

        result, readTime = await self.read(params)
        if attribute == "Value":
            return result[0], readTime
        else:
            return result[0]

    async def set_node_attribute(self,
                                 nodeId,
                                 attribute,
                                 value,
                                 dataType=None):
        """
        Sets node attribute based on given arguments.
        Giving correct dataType for value and node speeds
        up the write operation.

        Arguments                               Example
        nodeId:     Target nodeId               "ns=2;i=2"
        attribute:  Target attribute of node    "Value"
        value:      Value for the attribute     1234
        dataType:   Data type of value          "Int32"

        Results
        boolean:    Indicates success           True
        writeTime:  Time taken for write (ns)   12345678
        """

        attr = ua.WriteValue()

        if nodeId == "":
            attr.NodeId = ua.NodeId.from_string(self.rootNodeId)
        else:
            attr.NodeId = ua.NodeId.from_string(nodeId)

        attr.AttributeId = ua.AttributeIds[attribute]

        if attribute == "Description":
            dataValue = ua.LocalizedText(value)
        else:
            if dataType is None:
                variantType = self.variant_type_finder(value, nodeId)
            else:
                variantType = ua.VariantType[dataType]
            dataValue = ua.Variant(value, variantType)
        attr.Value = ua.DataValue(dataValue)

        params = ua.WriteParameters()
        params.NodesToWrite.append(attr)

        result, writeTime = await self.write(params)
        if attribute == "Value":
            return result[0].is_good(), writeTime
        else:
            return result[0].is_good()

    def add_node(self, name, nodeId, parentId, value=None, writable=True):
        """
        Adds a node to OPC UA server.
        If value given, adds a variable node, else, a folder node.
        Requires server admin powers in server servers.json, for example
        endPointAddress: "opc.tcp://[email protected]:4840/freeopcua/server/".
        """

        self.check_connection()

        if self.nameSpaceIndex is not None:
            index = self.nameSpaceIndex
        elif nodeId[:3] == "ns=":
            index = nodeId.split("=")[1][0]
        else:
            index = 0
        browseName = f"{index}:{name}"
        parentNode = self.get_node(parentId)

        if value is None:
            node = parentNode.add_folder(nodeId, browseName)
            result = {
                "name": node.get_display_name().to_string(),
                "nodeId": node.nodeid.to_string(),
            }
        else:
            node = parentNode.add_variable(nodeId, browseName, value)
            attribute = node.get_attribute(ua.AttributeIds.Value)
            result = {
                "name": node.get_display_name().to_string(),
                "nodeId": node.nodeid.to_string(),
                "value": attribute.Value.Value,
                "dataType": attribute.Value.VariantType.name,
                "sourceTimestamp": attribute.SourceTimestamp,
                "statusCode": attribute.StatusCode.name
            }

            if writable is True:
                node.set_writable()

        return result

    def delete_node(self, nodeId, recursive=True):
        """
        Recursively deletes node and it's subnodes unless recursive=False.
        Requires admins.
        Doesn't raise errors if deleting is unsuccessful.
        """

        self.check_connection()
        node = self.get_node(nodeId)
        result = self.client.delete_nodes([node], recursive)
        result[1][0].check()
        return result[1][0].is_good()

    async def read(self, params):
        """
        Reads from OPC UA server
        params == ua.ReadParameters() that are properly set up.

        Returns result object and time it took to read from OPC UA server.
        """

        self.check_connection()
        start = time.time_ns()
        result = self.client.uaclient.read(params)
        readTime = time.time_ns() - start
        return result, readTime

    async def write(self, params):
        """
        Writes to OPC UA server
        params == ua.WriteParameters() that are properly set up.

        Returns result object and time it took to read from OPC UA server.
        """

        self.check_connection()
        start = time.time_ns()
        result = self.client.uaclient.write(params)
        writeTime = time.time_ns() - start
        return result, writeTime

    def variant_type_finder(self, value, nodeId):
        """
        Attempts to find variant type of given value.
        If not found, retrieves variant type of node from OPC UA server.
        """

        valueType = type(value)
        if isinstance(valueType, datetime.datetime):
            variantType = ua.uatypes.VariantType.DateTime
        elif valueType == bool:
            variantType = ua.uatypes.VariantType.Boolean
        elif valueType == str:
            variantType = ua.uatypes.VariantType.String
        elif valueType == int or valueType == float:
            node = self.get_node(nodeId)
            self.check_connection()
            variantType = node.get_data_type_as_variant_type()
        else:
            raise ValueError("Unsupported datatype")
        return variantType
Ejemplo n.º 3
0
import sys
sys.path.insert(0, "..")
import logging

from opcua import Client
from opcua import ua


if __name__ == "__main__":
    logging.basicConfig(level=logging.WARN)

    client = Client("opc.tcp://admin@localhost:4840/freeopcua/server/") #connect using a user
    try:
        client.connect()

        objects = client.get_objects_node()
        folder = objects.add_folder("ns=2;i=3007", "2:Folder1")
        var = folder.add_variable("ns=2;i=3008", "2:Variable1", 3.45)
        # Now getting a variable node using its browse path
        var.set_value(9.89) # just to check it works

        results = client.delete_nodes([folder, var])
        try:
            #var.set_value(9.89) # just to check it does not work
            var.get_browse_name()
        except ua.UaStatusCodeError:
            print("The variable has been removed OK")

    finally:
        client.disconnect()
Ejemplo n.º 4
0
class UaClientWrapper(object):
    def __init__(self, endpoint, targetNs):
        self.endpoint = endpoint
        self.client = Client(endpoint)
        try:
            self.client.connect()
            self.namespaceArray = self.client.get_namespace_array()
            self.targetNs = self.client.get_namespace_index(targetNs)
            print("Target NS %s" % targetNs)
        except:
            self.disconnect()

    def get_namespace_array(self):
        return self.namespaceArray

    def collect_child_nodes(self, node, tree):
        # iterate over all referenced nodes (31), only hierarchical references (33)
        for child in node.get_children(refs=33):
            if not tree.get("children"):
                tree["children"] = []
            tree["children"].append({"node": child})
            self.collect_child_nodes(child, tree["children"][-1])

    def import_nodes(self, root_node=None):
        for ns in self.client.get_namespace_array():
            self.nsMapping[self.client.get_namespace_index(ns)] = ns

        if root_node is None:
            root = self.client.get_root_node()
        else:
            root = self.client.get_node(root_node)
        tree = {}
        self.collect_child_nodes(root, tree)
        return tree

    def getNamespaceIndices(self, list):
        self.nsMapping = {}
        for ns in list:
            self.nsMapping[ns] = self.client.get_namespace_index(ns)
        return self.nsMapping

    def clearModel(self, rootNode):
        root = self.client.get_node(rootNode)
        self.client.delete_nodes(root.get_children(), True)

    def addObject(self, parentNodeId, bName, objectType, identifier=0):
        parent = self.client.get_node(parentNodeId)

        node = parent.add_object("ns=%i;i=%i" % (self.targetNs, identifier),
                                 bName, objectType)
        node.__class__ = Node
        return node

    def escapeName(self, name: str):
        return name.replace(":", "|")

    def addNodes(self, rootNode, objects):
        for obj in objects:
            if 'ua_object_type' in obj:
                if obj['value'] != '':
                    name = obj['value']
                else:
                    name = obj['object']
                node = self.addObject(
                    rootNode, self.escapeName(name), "ns=%s;i=%s" %
                    (self.nsMapping[obj['ua_object_type']['ns']],
                     obj['ua_object_type']['i']))
                obj['ua_node_id'] = node.nodeid.to_string()
                if 'objects' in obj:
                    obj['objects'] = self.addNodes(obj['ua_node_id'],
                                                   obj['objects'])
            elif 'ua_variable_qualified_name' in obj and obj['value'] != '':
                qname = ua.QualifiedName(
                    obj['ua_variable_qualified_name']['browse_name'],
                    self.nsMapping[obj['ua_variable_qualified_name']['ns']])
                root = self.client.get_node(rootNode)
                variable = root.get_child(qname)
                variable.set_value(obj['value'])
        return obj

    def disconnect(self):
        self.client.disconnect()