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:
|
# Options:
|
||||||
#
|
#
|
||||||
# [INTERNAL] Optional argument. If given, then the generated node set code will use internal headers.
|
# [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:
|
# Arguments taking one value:
|
||||||
#
|
#
|
||||||
@ -252,7 +253,7 @@ endfunction()
|
|||||||
#
|
#
|
||||||
function(ua_generate_nodeset)
|
function(ua_generate_nodeset)
|
||||||
|
|
||||||
set(options INTERNAL )
|
set(options INTERNAL GENERATE_IDS)
|
||||||
set(oneValueArgs NAME TYPES_ARRAY OUTPUT_DIR IGNORE TARGET_PREFIX BLACKLIST)
|
set(oneValueArgs NAME TYPES_ARRAY OUTPUT_DIR IGNORE TARGET_PREFIX BLACKLIST)
|
||||||
set(multiValueArgs FILE DEPENDS_TYPES DEPENDS_NS DEPENDS_TARGET)
|
set(multiValueArgs FILE DEPENDS_TYPES DEPENDS_NS DEPENDS_TARGET)
|
||||||
cmake_parse_arguments(UA_GEN_NS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN} )
|
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}")
|
set(GEN_IGNORE "--ignore=${UA_GEN_NS_IGNORE}")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
set(GEN_IDS "")
|
||||||
|
if (UA_GEN_NS_GENERATE_IDS)
|
||||||
|
set(GEN_IDS "--generate-ids")
|
||||||
|
endif()
|
||||||
|
|
||||||
set(TYPES_ARRAY_LIST "")
|
set(TYPES_ARRAY_LIST "")
|
||||||
foreach(f ${UA_GEN_NS_DEPENDS_TYPES})
|
foreach(f ${UA_GEN_NS_DEPENDS_TYPES})
|
||||||
# Replace dash with underscore to make valid c literal
|
# 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})
|
file(MAKE_DIRECTORY ${UA_GEN_NS_OUTPUT_DIR})
|
||||||
endif()
|
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
|
add_custom_command(OUTPUT ${UA_GEN_NS_OUTPUT_DIR}/namespace${FILE_SUFFIX}.c
|
||||||
${UA_GEN_NS_OUTPUT_DIR}/namespace${FILE_SUFFIX}.h
|
${UA_GEN_NS_OUTPUT_DIR}/namespace${FILE_SUFFIX}.h
|
||||||
PRE_BUILD
|
PRE_BUILD
|
||||||
COMMAND ${PYTHON_EXECUTABLE} ${open62541_TOOLS_DIR}/nodeset_compiler/nodeset_compiler.py
|
COMMAND ${PYTHON_EXECUTABLE} ${open62541_TOOLS_DIR}/nodeset_compiler/nodeset_compiler.py
|
||||||
${GEN_INTERNAL_HEADERS}
|
${GEN_INTERNAL_HEADERS}
|
||||||
${GEN_NS0}
|
${GEN_NS0}
|
||||||
${GEN_BIN_SIZE}
|
${GEN_IDS}
|
||||||
|
--ids-prefix=${GEN_NAME_UPPER}
|
||||||
${GEN_IGNORE}
|
${GEN_IGNORE}
|
||||||
${GEN_BLACKLIST}
|
${GEN_BLACKLIST}
|
||||||
${TYPES_ARRAY_LIST}
|
${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)
|
set_source_files_properties(${UA_GEN_NS_OUTPUT_DIR}/namespace${FILE_SUFFIX}.c PROPERTIES LANGUAGE CXX)
|
||||||
endif()
|
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_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})
|
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'
|
# 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_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
|
# [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.
|
# 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
|
# [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})
|
if (${UA_GEN_INTERNAL})
|
||||||
set(NODESET_INTERNAL "INTERNAL")
|
set(NODESET_INTERNAL "INTERNAL")
|
||||||
endif()
|
endif()
|
||||||
|
set(NODESET_GENIDS "")
|
||||||
|
if("${UA_GEN_FILE_CSV}" STREQUAL "")
|
||||||
|
set(NODESET_GENIDS "GENERATE_IDS")
|
||||||
|
endif()
|
||||||
|
|
||||||
ua_generate_nodeset(
|
ua_generate_nodeset(
|
||||||
NAME "${UA_GEN_NAME}"
|
NAME "${UA_GEN_NAME}"
|
||||||
@ -559,6 +570,7 @@ function(ua_generate_nodeset_and_datatypes)
|
|||||||
TYPES_ARRAY "${NODESET_TYPES_ARRAY}"
|
TYPES_ARRAY "${NODESET_TYPES_ARRAY}"
|
||||||
BLACKLIST "${UA_GEN_BLACKLIST}"
|
BLACKLIST "${UA_GEN_BLACKLIST}"
|
||||||
${NODESET_INTERNAL}
|
${NODESET_INTERNAL}
|
||||||
|
${NODESET_GENIDS}
|
||||||
DEPENDS_TYPES ${TYPES_DEPENDS}
|
DEPENDS_TYPES ${TYPES_DEPENDS}
|
||||||
DEPENDS_NS ${NODESET_DEPENDS}
|
DEPENDS_NS ${NODESET_DEPENDS}
|
||||||
DEPENDS_TARGET ${NODESET_DEPENDS_TARGET}
|
DEPENDS_TARGET ${NODESET_DEPENDS_TARGET}
|
||||||
|
@ -14,6 +14,7 @@ from __future__ import print_function
|
|||||||
from os.path import basename
|
from os.path import basename
|
||||||
import logging
|
import logging
|
||||||
import codecs
|
import codecs
|
||||||
|
import re
|
||||||
import os
|
import os
|
||||||
try:
|
try:
|
||||||
from StringIO import StringIO
|
from StringIO import StringIO
|
||||||
@ -122,7 +123,86 @@ def sortNodes(nodeset):
|
|||||||
# Generate C Code #
|
# 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)
|
outfilebase = basename(outfilename)
|
||||||
# Printing functions
|
# Printing functions
|
||||||
outfileh = codecs.open(outfilename + ".h", r"w+", encoding='utf-8')
|
outfileh = codecs.open(outfilename + ".h", r"w+", encoding='utf-8')
|
||||||
@ -200,7 +280,10 @@ UA_findDataTypeByBinary(const UA_NodeId *typeId);
|
|||||||
writeh("""
|
writeh("""
|
||||||
_UA_BEGIN_DECLS
|
_UA_BEGIN_DECLS
|
||||||
|
|
||||||
extern UA_StatusCode %s(UA_Server *server);
|
""")
|
||||||
|
writeh(nodeIdsDefinitions)
|
||||||
|
|
||||||
|
writeh("""extern UA_StatusCode %s(UA_Server *server);
|
||||||
|
|
||||||
_UA_END_DECLS
|
_UA_END_DECLS
|
||||||
|
|
||||||
|
@ -185,6 +185,27 @@ class Node(object):
|
|||||||
new_refs.add(ref)
|
new_refs.add(ref)
|
||||||
self.references = new_refs
|
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):
|
class ReferenceTypeNode(Node):
|
||||||
def __init__(self, xmlelement=None):
|
def __init__(self, xmlelement=None):
|
||||||
Node.__init__(self)
|
Node.__init__(self)
|
||||||
|
@ -42,6 +42,17 @@ parser.add_argument('--internal-headers',
|
|||||||
dest="internal_headers",
|
dest="internal_headers",
|
||||||
help='Include internal headers instead of amalgamated header')
|
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',
|
parser.add_argument('-b', '--blacklist',
|
||||||
metavar="<blacklistFile>",
|
metavar="<blacklistFile>",
|
||||||
type=argparse.FileType('r'),
|
type=argparse.FileType('r'),
|
||||||
@ -122,6 +133,10 @@ for xmlfile in args.existing:
|
|||||||
logger.info("Preprocessing (existing) " + str(xmlfile.name))
|
logger.info("Preprocessing (existing) " + str(xmlfile.name))
|
||||||
ns.addNodeSet(xmlfile, True, typesArray=getTypesArray(nsCount))
|
ns.addNodeSet(xmlfile, True, typesArray=getTypesArray(nsCount))
|
||||||
nsCount +=1
|
nsCount +=1
|
||||||
|
|
||||||
|
# We only generate nodeids for the nodesets passed as the --xml parameter
|
||||||
|
nodeIdsNamespaceIndexToGenerate = nsCount
|
||||||
|
|
||||||
for xmlfile in args.infiles:
|
for xmlfile in args.infiles:
|
||||||
if xmlfile.name in loadedFiles:
|
if xmlfile.name in loadedFiles:
|
||||||
logger.info("Skipping Nodeset since it is already loaded: {} ".format(xmlfile.name))
|
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))
|
ns.addNodeSet(xmlfile, typesArray=getTypesArray(nsCount))
|
||||||
nsCount +=1
|
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
|
# Set the nodes from the ignore list to hidden. This removes them from dependency calculation
|
||||||
# and from printing their generated code.
|
# and from printing their generated code.
|
||||||
@ -185,8 +196,9 @@ logger.info("Generating Code for Backend: {}".format(args.backend))
|
|||||||
|
|
||||||
if args.backend == "open62541":
|
if args.backend == "open62541":
|
||||||
# Create the C code with the open62541 backend of the compiler
|
# Create the C code with the open62541 backend of the compiler
|
||||||
from backend_open62541 import generateOpen62541Code
|
from backend_open62541 import generateOpen62541Code, generateOpen62541NodeIds
|
||||||
generateOpen62541Code(ns, args.outputFile, args.internal_headers, args.typesArray)
|
nodeIdsDefinitions = generateOpen62541NodeIds(ns, args.generate_ids_prefix, nodeIdsNamespaceIndexToGenerate)
|
||||||
|
generateOpen62541Code(ns, args.outputFile, args.internal_headers, args.typesArray, nodeIdsDefinitions)
|
||||||
elif args.backend == "graphviz":
|
elif args.backend == "graphviz":
|
||||||
from backend_graphviz import generateGraphvizCode
|
from backend_graphviz import generateGraphvizCode
|
||||||
generateGraphvizCode(ns, filename=args.outputFile)
|
generateGraphvizCode(ns, filename=args.outputFile)
|
||||||
|
Loading…
Reference in New Issue
Block a user