mirror of
https://github.com/open62541/open62541.git
synced 2025-06-03 04:00:21 +00:00
feat(server): Integrate node id header generation into nodeset compiler
This commit is contained in:
parent
1bbcdcb370
commit
0a6ccdbe44
@ -229,6 +229,7 @@ endfunction()
|
||||
# Options:
|
||||
#
|
||||
# [INTERNAL] Optional argument. If given, then the generated node set code will use internal headers.
|
||||
# [GENERATE_IDS] Optional argument. If given, then the generated code will include define directives for numeric node ids
|
||||
#
|
||||
# Arguments taking one value:
|
||||
#
|
||||
@ -252,7 +253,7 @@ endfunction()
|
||||
#
|
||||
function(ua_generate_nodeset)
|
||||
|
||||
set(options INTERNAL )
|
||||
set(options INTERNAL GENERATE_IDS)
|
||||
set(oneValueArgs NAME TYPES_ARRAY OUTPUT_DIR IGNORE TARGET_PREFIX BLACKLIST)
|
||||
set(multiValueArgs FILE DEPENDS_TYPES DEPENDS_NS DEPENDS_TARGET)
|
||||
cmake_parse_arguments(UA_GEN_NS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN} )
|
||||
@ -311,6 +312,11 @@ function(ua_generate_nodeset)
|
||||
set(GEN_IGNORE "--ignore=${UA_GEN_NS_IGNORE}")
|
||||
endif()
|
||||
|
||||
set(GEN_IDS "")
|
||||
if (UA_GEN_NS_GENERATE_IDS)
|
||||
set(GEN_IDS "--generate-ids")
|
||||
endif()
|
||||
|
||||
set(TYPES_ARRAY_LIST "")
|
||||
foreach(f ${UA_GEN_NS_DEPENDS_TYPES})
|
||||
# Replace dash with underscore to make valid c literal
|
||||
@ -338,13 +344,17 @@ function(ua_generate_nodeset)
|
||||
file(MAKE_DIRECTORY ${UA_GEN_NS_OUTPUT_DIR})
|
||||
endif()
|
||||
|
||||
string(REPLACE "-" "_" UA_GEN_NS_NAME ${UA_GEN_NS_NAME})
|
||||
string(TOUPPER "${UA_GEN_NS_NAME}" GEN_NAME_UPPER)
|
||||
|
||||
add_custom_command(OUTPUT ${UA_GEN_NS_OUTPUT_DIR}/namespace${FILE_SUFFIX}.c
|
||||
${UA_GEN_NS_OUTPUT_DIR}/namespace${FILE_SUFFIX}.h
|
||||
PRE_BUILD
|
||||
COMMAND ${PYTHON_EXECUTABLE} ${open62541_TOOLS_DIR}/nodeset_compiler/nodeset_compiler.py
|
||||
${GEN_INTERNAL_HEADERS}
|
||||
${GEN_NS0}
|
||||
${GEN_BIN_SIZE}
|
||||
${GEN_IDS}
|
||||
--ids-prefix=${GEN_NAME_UPPER}
|
||||
${GEN_IGNORE}
|
||||
${GEN_BLACKLIST}
|
||||
${TYPES_ARRAY_LIST}
|
||||
@ -376,9 +386,6 @@ function(ua_generate_nodeset)
|
||||
set_source_files_properties(${UA_GEN_NS_OUTPUT_DIR}/namespace${FILE_SUFFIX}.c PROPERTIES LANGUAGE CXX)
|
||||
endif()
|
||||
|
||||
string(REPLACE "-" "_" UA_GEN_NS_NAME ${UA_GEN_NS_NAME})
|
||||
string(TOUPPER "${UA_GEN_NS_NAME}" GEN_NAME_UPPER)
|
||||
|
||||
set_property(GLOBAL PROPERTY "UA_GEN_NS_DEPENDS_FILE_${UA_GEN_NS_NAME}" ${UA_GEN_NS_DEPENDS_NS} ${UA_GEN_NS_FILE})
|
||||
set_property(GLOBAL PROPERTY "UA_GEN_NS_DEPENDS_TYPES_${UA_GEN_NS_NAME}" ${UA_GEN_NS_DEPENDS_TYPES} ${UA_GEN_NS_TYPES_ARRAY})
|
||||
|
||||
@ -419,7 +426,7 @@ endfunction()
|
||||
# NAME Short name of the nodeset. E.g. 'di'
|
||||
# FILE_NS Path to the NodeSet2.xml file. Multiple values can be passed. These nodesets will be combined into one output.
|
||||
#
|
||||
# [FILE_CSV] Optional path to the .csv file containing the node ids, e.g. 'OpcUaDiModel.csv'
|
||||
# [FILE_CSV] Optional path to the .csv file containing the node ids, e.g. 'OpcUaDiModel.csv'. If not given, the nodeid defines will automatically be generated
|
||||
# [FILE_BSD] Optional path to the .bsd file containing the type definitions, e.g. 'Opc.Ua.Di.Types.bsd'. Multiple files can be
|
||||
# passed which will all combined to one resulting code.
|
||||
# [IMPORT_BSD] Optional combination of types array and path to the .bsd file containing additional type definitions referenced by
|
||||
@ -552,6 +559,10 @@ function(ua_generate_nodeset_and_datatypes)
|
||||
if (${UA_GEN_INTERNAL})
|
||||
set(NODESET_INTERNAL "INTERNAL")
|
||||
endif()
|
||||
set(NODESET_GENIDS "")
|
||||
if("${UA_GEN_FILE_CSV}" STREQUAL "")
|
||||
set(NODESET_GENIDS "GENERATE_IDS")
|
||||
endif()
|
||||
|
||||
ua_generate_nodeset(
|
||||
NAME "${UA_GEN_NAME}"
|
||||
@ -559,6 +570,7 @@ function(ua_generate_nodeset_and_datatypes)
|
||||
TYPES_ARRAY "${NODESET_TYPES_ARRAY}"
|
||||
BLACKLIST "${UA_GEN_BLACKLIST}"
|
||||
${NODESET_INTERNAL}
|
||||
${NODESET_GENIDS}
|
||||
DEPENDS_TYPES ${TYPES_DEPENDS}
|
||||
DEPENDS_NS ${NODESET_DEPENDS}
|
||||
DEPENDS_TARGET ${NODESET_DEPENDS_TARGET}
|
||||
|
@ -14,6 +14,7 @@ from __future__ import print_function
|
||||
from os.path import basename
|
||||
import logging
|
||||
import codecs
|
||||
import re
|
||||
import os
|
||||
try:
|
||||
from StringIO import StringIO
|
||||
@ -122,7 +123,86 @@ def sortNodes(nodeset):
|
||||
# Generate C Code #
|
||||
###################
|
||||
|
||||
def generateOpen62541Code(nodeset, outfilename, internal_headers=False, typesArray=[]):
|
||||
def generateOpen62541NodeIds(nodeset, generate_ids_prefix, generate_ids_ns_start):
|
||||
ids = StringIO()
|
||||
|
||||
ids.write(u"""#ifndef UA_NODEIDS_{prefix}_H_
|
||||
#define UA_NODEIDS_{prefix}_H_
|
||||
|
||||
/**
|
||||
* Namespace Zero NodeIds
|
||||
* ----------------------
|
||||
* Numeric identifiers of standard-defined nodes in namespace zero. The
|
||||
* following definitions are autogenerated by the nodeset compiler */
|
||||
""".format(prefix=generate_ids_prefix))
|
||||
|
||||
# get subset of nodes we should print and have numeric id
|
||||
|
||||
if (sys.version_info > (3, 0)):
|
||||
nodes_filtered = {k: node for k, node in nodeset.nodes.items() if node.id.ns >= generate_ids_ns_start and not node.id.i is None}
|
||||
else:
|
||||
nodes_filtered = {k: node for k, node in nodeset.nodes.iteritems() if node.id.ns >= generate_ids_ns_start and not node.id.i is None}
|
||||
|
||||
# sort the nodes with key numeric identifier
|
||||
nodes_filtered = sorted(nodes_filtered, key=lambda n: (n.ns, n.i))
|
||||
|
||||
def isNodeIdRoot(node):
|
||||
# If node type is one of these types we just take the name as it is
|
||||
return node.getTypeAsString() in [ "DataType", "ObjectType", "ReferenceType", "VariableType"]
|
||||
|
||||
def cleanDefineName(name):
|
||||
return re.sub('[=\-\.:\s]', '_', name)
|
||||
|
||||
for nid in nodes_filtered:
|
||||
node = nodeset.nodes[nid]
|
||||
|
||||
name = cleanDefineName(node.symbolicName.value if node.symbolicName is not None else node.browseName.name)
|
||||
n1 = node
|
||||
# Recursively walk up through the node's parents until we reach the base node and construct the name
|
||||
# along the path. Base node is determined by isNodeIdRoot
|
||||
while n1 is not None:
|
||||
if isNodeIdRoot(n1):
|
||||
break
|
||||
|
||||
parent = n1.parent
|
||||
parentref = n1.parentReference
|
||||
if parent is None:
|
||||
# check if this node is target of a hasEncoding reference. If yes we need to add an additional "Encoding_" part to the identifier
|
||||
parentreftypes = (getSubTypesOf(nodeset, nodeset.getNodeByBrowseName("HasEncoding"), []))
|
||||
parentreftypes = list(map(lambda x: x.id, parentreftypes))
|
||||
parentref = node.getParentReference(parentreftypes)
|
||||
if parentref is not None:
|
||||
parent = nodeset.nodes[parentref.target]
|
||||
name = "Encoding_" + name
|
||||
parentref = nodeset.nodes[parentref.referenceType]
|
||||
|
||||
skipParents = [ NodeId("i=92"), NodeId("i=93") ]
|
||||
if parent is None or parent.id.ns < generate_ids_ns_start or parent.id in skipParents:
|
||||
break
|
||||
# Stop on organizes references
|
||||
oragnizesId = NodeId("i=35")
|
||||
if parentref.id == oragnizesId:
|
||||
break
|
||||
|
||||
n1 = parent
|
||||
name2 = cleanDefineName(parent.symbolicName.value if parent.symbolicName is not None else parent.browseName.name)
|
||||
name = name2 + "_" + name
|
||||
|
||||
ids.write(u"#define UA_{namespace}ID_{name} {id} /* {description} */\n".format(
|
||||
namespace=generate_ids_prefix,
|
||||
name=name.upper(),
|
||||
id=node.id.i,
|
||||
description=node.getTypeAsString()))
|
||||
|
||||
ids.write(u'''#endif /* UA_NODEIDS_{0}_H_ */ \n'''.format(generate_ids_prefix))
|
||||
return ids.getvalue()
|
||||
|
||||
def generateOpen62541Code(
|
||||
nodeset,
|
||||
outfilename,
|
||||
internal_headers=False,
|
||||
typesArray=[],
|
||||
nodeIdsDefinitions=""):
|
||||
outfilebase = basename(outfilename)
|
||||
# Printing functions
|
||||
outfileh = codecs.open(outfilename + ".h", r"w+", encoding='utf-8')
|
||||
@ -200,7 +280,10 @@ UA_findDataTypeByBinary(const UA_NodeId *typeId);
|
||||
writeh("""
|
||||
_UA_BEGIN_DECLS
|
||||
|
||||
extern UA_StatusCode %s(UA_Server *server);
|
||||
""")
|
||||
writeh(nodeIdsDefinitions)
|
||||
|
||||
writeh("""extern UA_StatusCode %s(UA_Server *server);
|
||||
|
||||
_UA_END_DECLS
|
||||
|
||||
|
@ -185,6 +185,27 @@ class Node(object):
|
||||
new_refs.add(ref)
|
||||
self.references = new_refs
|
||||
|
||||
def getTypeAsString(self):
|
||||
if isinstance(self, ReferenceTypeNode):
|
||||
return "ReferenceType"
|
||||
elif isinstance(self, ObjectNode):
|
||||
return "Object"
|
||||
elif isinstance(self, VariableNode) and not isinstance(self, VariableTypeNode):
|
||||
return "Variable"
|
||||
elif isinstance(self, VariableTypeNode):
|
||||
return "VariableType"
|
||||
elif isinstance(self, MethodNode):
|
||||
return "Method"
|
||||
elif isinstance(self, ObjectTypeNode):
|
||||
return "ObjectType"
|
||||
elif isinstance(self, DataTypeNode):
|
||||
return "DataType"
|
||||
elif isinstance(self, ViewNode):
|
||||
return "View"
|
||||
else:
|
||||
return "Unknown"
|
||||
|
||||
|
||||
class ReferenceTypeNode(Node):
|
||||
def __init__(self, xmlelement=None):
|
||||
Node.__init__(self)
|
||||
|
@ -42,6 +42,17 @@ parser.add_argument('--internal-headers',
|
||||
dest="internal_headers",
|
||||
help='Include internal headers instead of amalgamated header')
|
||||
|
||||
parser.add_argument('--generate-ids',
|
||||
action='store_true',
|
||||
dest="generate_ids",
|
||||
default=False,
|
||||
help='Generated defines for node ids in header file for nodesets of the --xml parameter. Default True. This will genera')
|
||||
|
||||
parser.add_argument('--ids-prefix',
|
||||
dest="generate_ids_prefix",
|
||||
default="NS",
|
||||
help='Prefix for generated node id defines. Format is UA_{PREFIX}ID_{name}.')
|
||||
|
||||
parser.add_argument('-b', '--blacklist',
|
||||
metavar="<blacklistFile>",
|
||||
type=argparse.FileType('r'),
|
||||
@ -122,6 +133,10 @@ for xmlfile in args.existing:
|
||||
logger.info("Preprocessing (existing) " + str(xmlfile.name))
|
||||
ns.addNodeSet(xmlfile, True, typesArray=getTypesArray(nsCount))
|
||||
nsCount +=1
|
||||
|
||||
# We only generate nodeids for the nodesets passed as the --xml parameter
|
||||
nodeIdsNamespaceIndexToGenerate = nsCount
|
||||
|
||||
for xmlfile in args.infiles:
|
||||
if xmlfile.name in loadedFiles:
|
||||
logger.info("Skipping Nodeset since it is already loaded: {} ".format(xmlfile.name))
|
||||
@ -131,10 +146,6 @@ for xmlfile in args.infiles:
|
||||
ns.addNodeSet(xmlfile, typesArray=getTypesArray(nsCount))
|
||||
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.
|
||||
@ -185,8 +196,9 @@ logger.info("Generating Code for Backend: {}".format(args.backend))
|
||||
|
||||
if args.backend == "open62541":
|
||||
# Create the C code with the open62541 backend of the compiler
|
||||
from backend_open62541 import generateOpen62541Code
|
||||
generateOpen62541Code(ns, args.outputFile, args.internal_headers, args.typesArray)
|
||||
from backend_open62541 import generateOpen62541Code, generateOpen62541NodeIds
|
||||
nodeIdsDefinitions = generateOpen62541NodeIds(ns, args.generate_ids_prefix, nodeIdsNamespaceIndexToGenerate)
|
||||
generateOpen62541Code(ns, args.outputFile, args.internal_headers, args.typesArray, nodeIdsDefinitions)
|
||||
elif args.backend == "graphviz":
|
||||
from backend_graphviz import generateGraphvizCode
|
||||
generateGraphvizCode(ns, filename=args.outputFile)
|
||||
|
Loading…
Reference in New Issue
Block a user