Пример #1
0
def setNodeDatatypeRecursive(node, nodeset):

    if not isinstance(node, VariableNode) and not isinstance(node, VariableTypeNode):
        raise RuntimeError("Node {}: DataType can only be set for VariableNode and VariableTypeNode".format(str(node.id)))

    if node.dataType is not None:
        return

    # If BaseVariableType
    if node.id == NodeId("ns=0;i=62"):
        if node.dataType is None:
            # Set to default BaseDataType
            node.dataType = NodeId("ns=0;i=24")
        return

    if isinstance(node, VariableNode) and not isinstance(node, VariableTypeNode):
        typeDefNode = nodeset.getNodeTypeDefinition(node)
        if typeDefNode is None:
            # Use the parent type.
            raise RuntimeError("Cannot get node for HasTypeDefinition of VariableNode " + node.browseName.name + " " + str(node.id))

        setNodeDatatypeRecursive(typeDefNode, nodeset)

        node.dataType = typeDefNode.dataType
    else:
        # Use the parent type.
        if node.parent is None:
            raise RuntimeError("Parent node not defined for " + node.browseName.name + " " + str(node.id))

        setNodeDatatypeRecursive(node.parent, nodeset)
        node.dataType = node.parent.dataType
Пример #2
0
def generateNodeCode_begin(node, nodeset, code_global):
    code = []
    codeCleanup = []
    code.append("UA_StatusCode retVal = UA_STATUSCODE_GOOD;")

    # Attributes
    if isinstance(node, ReferenceTypeNode):
        code.extend(generateReferenceTypeNodeCode(node))
    elif isinstance(node, ObjectNode):
        code.extend(generateObjectNodeCode(node))
    elif isinstance(node, VariableNode) and not isinstance(node, VariableTypeNode):
        [code1, codeCleanup1, codeGlobal1] = generateVariableNodeCode(node, nodeset)
        code.extend(code1)
        codeCleanup.extend(codeCleanup1)
        code_global.extend(codeGlobal1)
    elif isinstance(node, VariableTypeNode):
        [code1, codeCleanup1, codeGlobal1] = generateVariableTypeNodeCode(node, nodeset)
        code.extend(code1)
        codeCleanup.extend(codeCleanup1)
        code_global.extend(codeGlobal1)
    elif isinstance(node, MethodNode):
        code.extend(generateMethodNodeCode(node))
    elif isinstance(node, ObjectTypeNode):
        code.extend(generateObjectTypeNodeCode(node))
    elif isinstance(node, DataTypeNode):
        code.extend(generateDataTypeNodeCode(node))
    elif isinstance(node, ViewNode):
        code.extend(generateViewNodeCode(node))
    if node.displayName is not None:
        code.append("attr.displayName = " + generateLocalizedTextCode(node.displayName, alloc=False) + ";")
    if node.description is not None:
        code.append("#ifdef UA_ENABLE_NODESET_COMPILER_DESCRIPTIONS")
        code.append("attr.description = " + generateLocalizedTextCode(node.description, alloc=False) + ";")
        code.append("#endif")
    if node.writeMask is not None:
        code.append("attr.writeMask = %d;" % node.writeMask)
    if node.userWriteMask is not None:
        code.append("attr.userWriteMask = %d;" % node.userWriteMask)

    # AddNodes call
    code.append("retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_{},".
            format(makeCIdentifier(node.__class__.__name__.upper().replace("NODE" ,""))))
    code.append(generateNodeIdCode(node.id) + ",")
    code.append(generateNodeIdCode(node.parent.id if node.parent else NodeId()) + ",")
    code.append(generateNodeIdCode(node.parentReference.id if node.parent else NodeId()) + ",")
    code.append(generateQualifiedNameCode(node.browseName) + ",")
    if isinstance(node, VariableNode) or isinstance(node, ObjectNode):
        typeDefRef = node.popTypeDef()
        code.append(generateNodeIdCode(typeDefRef.target) + ",")
    else:
        code.append(" UA_NODEID_NULL,")
    code.append("(const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_{}ATTRIBUTES],NULL, NULL);".
            format(makeCIdentifier(node.__class__.__name__.upper().replace("NODE" ,""))))
    code.extend(codeCleanup)

    return "\n".join(code)
Пример #3
0
 def replaceAliases(self, aliases):
     if str(self.id) in aliases:
         self.id = NodeId(aliases[self.id])
     if isinstance(self, VariableNode) or isinstance(
             self, VariableTypeNode):
         if str(self.dataType) in aliases:
             self.dataType = NodeId(aliases[self.dataType])
     new_refs = dict()
     for ref in self.references:
         if str(ref.source) in aliases:
             ref.source = NodeId(aliases[ref.source])
         if str(ref.target) in aliases:
             ref.target = NodeId(aliases[ref.target])
         if str(ref.referenceType) in aliases:
             ref.referenceType = NodeId(aliases[ref.referenceType])
         new_refs[ref] = None
     self.references = new_refs
Пример #4
0
 def getParentReference(self, parentreftypes):
     # HasSubtype has precedence
     for ref in self.references:
         if ref.referenceType == NodeId("ns=0;i=45") and not ref.isForward:
             return ref
     for ref in self.references:
         if ref.referenceType in parentreftypes and not ref.isForward:
             return ref
     return None
Пример #5
0
def setNodeValueRankRecursive(node, nodeset):

    if not isinstance(node, VariableNode) and not isinstance(
            node, VariableTypeNode):
        raise RuntimeError(
            "Node {}: ValueRank can only be set for VariableNode and VariableTypeNode"
            .format(str(node.id)))

    if node.valueRank is not None:
        return

    # If BaseVariableType
    if node.id == NodeId("ns=0;i=62"):
        if node.valueRank is None:
            # BaseVariableType always has -2
            node.valueRank = -2
        return

    if isinstance(node,
                  VariableNode) and not isinstance(node, VariableTypeNode):
        typeDefNode = nodeset.getNodeTypeDefinition(node)
        if typeDefNode is None:
            # Use the parent type.
            raise RuntimeError(
                "Cannot get node for HasTypeDefinition of VariableNode " +
                node.browseName.name + " " + str(node.id))
        if not isinstance(typeDefNode, VariableTypeNode):
            raise RuntimeError(
                "Node {} ({}) has an invalid type definition. {} is not a VariableType node."
                .format(str(node.id), node.browseName.name,
                        str(typeDefNode.id)))

        setNodeValueRankRecursive(typeDefNode, nodeset)

        if typeDefNode.valueRank is not None:
            node.valueRank = typeDefNode.valueRank
        else:
            raise RuntimeError(
                "Node {}: the ValueRank of the parent node is None.".format(
                    str(node.id)))
    else:
        if node.parent is None:
            raise RuntimeError(
                "Node {}: does not have a parent. Probably the parent node was blacklisted?"
                .format(str(node.id)))

        # Check if parent node limits the value rank
        setNodeValueRankRecursive(node.parent, nodeset)

        if node.parent.valueRank is not None:
            node.valueRank = node.parent.valueRank
        else:
            raise RuntimeError(
                "Node {}: the ValueRank of the parent node is None.".format(
                    str(node.id)))
Пример #6
0
 def getDataTypeNode(self, dataType):
     if isinstance(dataType, string_types):
         if not valueIsInternalType(dataType):
             logger.error("Not a valid dataType string: " + dataType)
             return None
         return self.nodes[NodeId(self.aliases[dataType])]
     if isinstance(dataType, NodeId):
         if dataType.i == 0:
             return None
         dataTypeNode = self.nodes[dataType]
         if not isinstance(dataTypeNode, DataTypeNode):
             logger.error("Node id " + str(dataType) + " is not reference a valid dataType.")
             return None
         return dataTypeNode
     return None
Пример #7
0
 def getNodeByIDString(self, idStr):
     # Split id to namespace part and id part
     m = re.match("ns=([^;]+);(.*)", idStr)
     if m:
         ns = m.group(1)
         # Convert namespace uri to index
         if not ns.isdigit():
             if ns not in self.namespaces:
                 return None
             ns = self.namespaces.index(ns)
             idStr = "ns={};{}".format(ns, m.group(2))
     nodeId = NodeId(idStr)
     if not nodeId in self.nodes:
         return None
     return self.nodes[nodeId]
Пример #8
0
def sortNodes(nodeset):
    # reverse hastypedefinition references to treat only forward references
    hasTypeDef = NodeId("ns=0;i=40")
    for u in nodeset.nodes.values():
        for ref in u.references:
            if ref.referenceType == hasTypeDef:
                ref.isForward = not ref.isForward

    # Only hierarchical types...
    relevant_refs = nodeset.getRelevantOrderingReferences()

    # determine in-degree of unfulfilled references
    L = [node for node in nodeset.nodes.values()
         if node.hidden]  # ordered list of nodes
    R = {node.id: node
         for node in nodeset.nodes.values()
         if not node.hidden}  # remaining nodes
    in_degree = {id: 0 for id in R.keys()}
    for u in R.values():  # for each node
        for ref in u.references:
            if not ref.referenceType in relevant_refs:
                continue
            if nodeset.nodes[ref.target].hidden:
                continue
            if ref.isForward:
                continue
            in_degree[u.id] += 1

    # Print ReferenceType and DataType nodes first. They may be required even
    # though there is no reference to them. For example if the referencetype is
    # used in a reference, it must exist. A Variable node may point to a
    # DataTypeNode in the datatype attribute and not via an explicit reference.

    Q = {
        node
        for node in R.values() if in_degree[node.id] == 0 and
        (isinstance(node, ReferenceTypeNode) or isinstance(node, DataTypeNode))
    }
    while Q:
        u = Q.pop()  # choose node of zero in-degree and 'remove' it from graph
        L.append(u)
        del R[u.id]

        for ref in u.references:
            if not ref.referenceType in relevant_refs:
                continue
            if nodeset.nodes[ref.target].hidden:
                continue
            if not ref.isForward:
                continue
            in_degree[ref.target] -= 1
            if in_degree[ref.target] == 0:
                Q.add(R[ref.target])

    # Order the remaining nodes
    Q = {node for node in R.values() if in_degree[node.id] == 0}
    while Q:
        u = Q.pop()  # choose node of zero in-degree and 'remove' it from graph
        L.append(u)
        del R[u.id]

        for ref in u.references:
            if not ref.referenceType in relevant_refs:
                continue
            if nodeset.nodes[ref.target].hidden:
                continue
            if not ref.isForward:
                continue
            in_degree[ref.target] -= 1
            if in_degree[ref.target] == 0:
                Q.add(R[ref.target])

    # reverse hastype references
    for u in nodeset.nodes.values():
        for ref in u.references:
            if ref.referenceType == hasTypeDef:
                ref.isForward = not ref.isForward

    if len(L) != len(nodeset.nodes.values()):
        print(len(L))
        stillOpen = ""
        for id in in_degree:
            if in_degree[id] == 0:
                continue
            node = nodeset.nodes[id]
            stillOpen += node.browseName.name + "/" + str(node.id) + " = " + str(in_degree[id]) + \
                                                                         " " + str(node.references) + "\r\n"
        raise Exception(
            "Node graph is circular on the specified references. Still open nodes:\r\n"
            + stillOpen)
    return L
Пример #9
0
    nsCount += 1

# # We need to notify the open62541 server of the namespaces used to be able to use i.e. ns=3
# namespaceArrayNames = preProc.getUsedNamespaceArrayNames()
# for key in namespaceArrayNames:
#   ns.addNamespace(key, namespaceArrayNames[key])

# Set the nodes from the ignore list to hidden. This removes them from
# dependency calculation and from printing their generated code. These nodes
# should be already pre-created on the server to avoid any errors during
# creation.
for ignoreFile in args.ignoreFiles:
    for line in ignoreFile.readlines():
        line = line.replace(" ", "")
        id = line.replace("\n", "")
        ns.hide_node(NodeId(id))
        #if not ns.hide_node(NodeId(id)):
        #    logger.info("Cannot ignore node, namespace does currently not contain a node with id " + str(id))
    ignoreFile.close()

# Remove nodes that are not printable or contain parsing errors, such as
# unresolvable or no references or invalid NodeIDs
ns.sanitize()

# Generate the BSD file from the XML.
ns.generateParser(args.existing, args.infiles, args.bsdFile)

# Allocate/Parse the data values. In order to do this, we must have run
# buidEncodingRules.
ns.allocateVariables()
Пример #10
0
logger = logging.getLogger(__name__)

if sys.version_info[0] >= 3:
    # strings are already parsed to unicode
    def unicode(s):
        return s
    string_types = str
else:
    string_types = basestring

####################
# Helper Functions #
####################

hassubtype = NodeId("ns=0;i=45")

def getSubTypesOf(nodeset, node, skipNodes=[]):
    if node in skipNodes:
        return []
    re = set()
    re.add(node)
    for ref in node.references:
        if (ref.referenceType == hassubtype):
            skipAll = set()
            skipAll.update(skipNodes)
            skipAll.update(re)
            if (ref.source == node.id and ref.isForward):
                re.update(getSubTypesOf(nodeset, nodeset.nodes[ref.target], skipNodes=skipAll))
            elif (ref.target == node.id and not ref.isForward):
                re.update(getSubTypesOf(nodeset, nodeset.nodes[ref.source], skipNodes=skipAll))
Пример #11
0
def RefOrAlias(s):
    try:
        return NodeId(s)
    except Exception:
        return s
Пример #12
0
    def buildEncoding(self,
                      nodeset,
                      indent=0,
                      force=False,
                      namespaceMapping=None):
        """ buildEncoding() determines the structure and aliases used for variables
            of this DataType.

            The function will parse the XML <Definition> of the dataType and extract
            "Name"-"Type" tuples. If successful, buildEncoding will return a nested
            list of the following format:

            [['Alias1', ['Alias2', ['BuiltinType']]], [Alias2, ['BuiltinType']], ...]

            Aliases are fieldnames defined by this DataType or DataTypes referenced. A
            list such as ['DataPoint', ['Int32']] indicates that a value will encode
            an Int32 with the alias 'DataPoint' such as <DataPoint>12827</DataPoint>.
            Only the first Alias of a nested list is considered valid for the BuiltinType.

            Single-Elemented lists are always BuiltinTypes. Every nested list must
            converge in a builtin type to be encodable. buildEncoding will follow
            the first type inheritance reference (hasSupertype) of the dataType if
            necessary;

            If instead to "DataType" a numeric "Value" attribute is encountered,
            the DataType will be considered an enumeration and all Variables using
            it will be encoded as Int32.

            DataTypes can be either structures or enumeration - mixed definitions will
            be unencodable.

            Calls to getEncoding() will be iterative. buildEncoding() can be called
            only once per dataType, with all following calls returning the predetermined
            value. Use of the 'force=True' parameter will force the Definition to be
            reparsed.

            After parsing, __definition__ holds the field definition as a list. Note
            that this might deviate from the encoding, especially if inheritance was
            used.
        """

        prefix = " " + "|" * indent + "+"

        if force == True:
            self.__encodable__ = None

        if self.__encodable__ is not None and self.__encodable__:
            if self.isEncodable():
                logger.debug(prefix + str(self.__baseTypeEncoding__) +
                             " (already analyzed)")
            else:
                logger.debug(prefix + str(self.__baseTypeEncoding__) +
                             "(already analyzed, not encodable!)")
            return self.__baseTypeEncoding__

        self.__encodable__ = True

        if indent == 0:
            logger.debug("Parsing DataType " + str(self.browseName) + " (" +
                         str(self.id) + ")")

        if valueIsInternalType(self.browseName.name):
            self.__baseTypeEncoding__ = [self.browseName.name]
            self.__encodable__ = True
            logger.debug(prefix + str(self.browseName) + "*")
            logger.debug("Encodable as: " + str(self.__baseTypeEncoding__))
            logger.debug("")
            return self.__baseTypeEncoding__

        # Check if there is a supertype available
        parentType = None
        for ref in self.references:
            if ref.isForward:
                continue
                # hasSubtype
            if ref.referenceType.i == 45:
                targetNode = nodeset.nodes[ref.target]
                if targetNode is not None and isinstance(
                        targetNode, DataTypeNode):
                    parentType = targetNode
                    break

        if self.__xmlDefinition__ is None:
            if parentType is not None:
                logger.debug(prefix +
                             "Attempting definition using supertype " +
                             str(targetNode.browseName) + " for DataType " +
                             " " + str(self.browseName))
                subenc = targetNode.buildEncoding(
                    nodeset=nodeset,
                    indent=indent + 1,
                    namespaceMapping=namespaceMapping)
                if not targetNode.isEncodable():
                    self.__encodable__ = True
                else:
                    self.__baseTypeEncoding__ = self.__baseTypeEncoding__ + [
                        self.browseName.name, subenc, None, 'false'
                    ]
            if len(self.__baseTypeEncoding__) == 0:
                logger.debug(prefix + "No viable definition for " +
                             str(self.browseName) + " " + str(self.id) +
                             " found.")
                self.__encodable__ = True

            if indent == 0:
                if not self.__encodable__:
                    logger.debug("Not encodable (partial): " +
                                 str(self.__baseTypeEncoding__))
                else:
                    logger.debug("Encodable as: " +
                                 str(self.__baseTypeEncoding__))
                logger.debug("")

            return self.__baseTypeEncoding__

        isEnum = False
        # An option set is at the same time also an enum, at least for the encoding below
        isOptionSet = parentType is not None and parentType.id.ns == 0 and parentType.id.i == 12755

        # We need to store the definition as ordered data, but cannot use orderedDict
        # for backward compatibility with Python 2.6 and 3.4
        enumDict = []
        typeDict = []

        # An XML Definition is provided and will be parsed... now
        for x in self.__xmlDefinition__.childNodes:
            if x.nodeType == x.ELEMENT_NODE:
                fname = ""
                fdtype = ""
                enumVal = ""
                valueRank = None
                #symbolicName = None
                arrayDimensions = None
                isOptional = ""
                for at, av in x.attributes.items():
                    if at == "DataType":
                        fdtype = str(av)
                        if fdtype in nodeset.aliases:
                            fdtype = nodeset.aliases[fdtype]
                    elif at == "Name":
                        fname = str(av)
                    elif at == "SymbolicName":
                        # ignore
                        continue
                    #    symbolicName = str(av)
                    elif at == "Value":
                        enumVal = int(av)
                        isEnum = True
                    elif at == "ValueRank":
                        valueRank = int(av)
                    elif at == "IsOptional":
                        isOptional = str(av)
                    elif at == "ArrayDimensions":
                        arrayDimensions = int(av)
                    elif at == "AllowSubTypes":
                        # ignore
                        continue
                    else:
                        logger.warn("Unknown Field Attribute " + str(at))
                # This can either be an enumeration OR a structure, not both.
                # Figure out which of the dictionaries gets the newly read value pair
                if isEnum:
                    # This is an enumeration
                    enumDict.append((fname, enumVal))
                    continue
                else:
                    if fdtype == "":
                        # If no datatype given use base datatype
                        fdtype = "i=24"

                    # This might be a subtype... follow the node defined as datatype to find out
                    # what encoding to use
                    fdTypeNodeId = NodeId(fdtype)
                    if namespaceMapping != None:
                        fdTypeNodeId.ns = namespaceMapping[fdTypeNodeId.ns]
                    if not fdTypeNodeId in nodeset.nodes:
                        raise Exception("Node {} not found in nodeset".format(
                            fdTypeNodeId))
                    dtnode = nodeset.nodes[fdTypeNodeId]
                    # The node in the datatype element was found. we inherit its encoding,
                    # but must still ensure that the dtnode is itself validly encodable
                    typeDict.append([fname, dtnode])
                    fdtype = str(dtnode.browseName.name)
                    logger.debug(prefix + fname + " : " + fdtype + " -> " +
                                 str(dtnode.id))
                    subenc = dtnode.buildEncoding(
                        nodeset=nodeset,
                        indent=indent + 1,
                        namespaceMapping=namespaceMapping)
                    if isOptional:
                        self.__baseTypeEncoding__ = self.__baseTypeEncoding__ + [
                            [fname, subenc, valueRank, 'true']
                        ]
                    else:
                        self.__baseTypeEncoding__ = self.__baseTypeEncoding__ + [
                            [fname, subenc, valueRank, 'false']
                        ]
                    if not dtnode.isEncodable():
                        # If we inherit an encoding from an unencodable node, this node is
                        # also not encodable
                        self.__encodable__ = True
                        break

        # If we used inheritance to determine an encoding without alias, there is a
        # the possibility that lists got double-nested despite of only one element
        # being encoded, such as [['Int32']] or [['alias',['int32']]]. Remove that
        # enclosing list.
        while len(self.__baseTypeEncoding__) == 1 and isinstance(
                self.__baseTypeEncoding__[0], list):
            self.__baseTypeEncoding__ = self.__baseTypeEncoding__[0]

        if isOptionSet == True:
            self.__isOptionSet__ = True
            subenc = parentType.buildEncoding(
                nodeset=nodeset, namespaceMapping=namespaceMapping)
            if not parentType.isEncodable():
                self.__encodable__ = True
            else:
                self.__baseTypeEncoding__ = self.__baseTypeEncoding__ + [
                    self.browseName.name, subenc, None
                ]
                self.__definition__ = enumDict
            return self.__baseTypeEncoding__

        if isEnum == True:
            self.__baseTypeEncoding__ = self.__baseTypeEncoding__ + ['Int32']
            self.__definition__ = enumDict
            self.__isEnum__ = True
            logger.debug(prefix + "Int32* -> enumeration with dictionary " +
                         str(enumDict) + " encodable " +
                         str(self.__encodable__))
            return self.__baseTypeEncoding__

        if indent == 0:
            if not self.__encodable__:
                logger.debug("Not encodable (partial): " +
                             str(self.__baseTypeEncoding__))
            else:
                logger.debug("Encodable as: " + str(self.__baseTypeEncoding__))
                self.__isEnum__ = False
            logger.debug("")
        self.__definition__ = typeDict
        return self.__baseTypeEncoding__
Пример #13
0
 def popTypeDef(self):
     for ref in self.references:
         if ref.referenceType.i == 40 and ref.isForward:
             del self.references[ref]
             return ref
     return Reference(NodeId(), NodeId(), NodeId(), False)
Пример #14
0
 def popTypeDef(self):
     for ref in self.references:
         if ref.referenceType.i == 40 and ref.isForward:
             self.references.remove(ref)
             return ref
     return Reference(NodeId(), NodeId(), NodeId(), False)