mirror of
https://github.com/open62541/open62541.git
synced 2025-06-03 04:00:21 +00:00
fix(nodeset-compiler): Use correct namespace mapping to fix ExtensionObject initialization
This also introduces a breaking change as it is now necessary to use the `NAMESPACE_MAP` parameter for the CMake macro, instead of the `NAMESPACE_IDX`. Up to now, complex extension objects initialized in the nodeset were not correctly set, especially if a data type from an dependent nodeset was used.
This commit is contained in:
parent
50933d83cb
commit
8103c27992
@ -284,7 +284,7 @@ If you look into the files generated by the nodeset compiler, you will see that
|
||||
This is how you can use the nodeset compiler to compile simple NodeSet XMLs to be used by the open62541 stack.
|
||||
|
||||
For your convenience and for simpler use we also provide a CMake function which simplifies the use of the ``ua_generate_datatypes`` and ``ua_generate_nodeset`` function even more.
|
||||
It is highly recommended to use this function: ``ua_generate_nodeset_and_datatypes``. It uses some best practice settings and you only need to pass a name, the namespace index ``NAMESPACE_IDX`` (as described above) and the nodeset files.
|
||||
It is highly recommended to use this function: ``ua_generate_nodeset_and_datatypes``. It uses some best practice settings and you only need to pass a name, the namespace mapping ``NAMESPACE_MAP`` (as described further below) and the nodeset files.
|
||||
Passing the .csv and .bsd files is optional and if not given, generating datatypes for that noteset will be skipped. You can also define dependencies between nodesets using the ``DEPENDS`` argument.
|
||||
|
||||
Here are some examples for the ``DI`` and ``PLCOpen`` nodesets::
|
||||
@ -294,7 +294,7 @@ Here are some examples for the ``DI`` and ``PLCOpen`` nodesets::
|
||||
NAME "di"
|
||||
FILE_CSV "${PROJECT_SOURCE_DIR}/deps/ua-nodeset/DI/OpcUaDiModel.csv"
|
||||
FILE_BSD "${PROJECT_SOURCE_DIR}/deps/ua-nodeset/DI/Opc.Ua.Di.Types.bsd"
|
||||
NAMESPACE_IDX 2
|
||||
NAMESPACE_MAP "2:http://opcfoundation.org/UA/DI/"
|
||||
FILE_NS "${PROJECT_SOURCE_DIR}/deps/ua-nodeset/DI/Opc.Ua.Di.NodeSet2.xml"
|
||||
)
|
||||
|
||||
@ -400,12 +400,13 @@ This DI nodeset makes use of some additional data types in ``deps/ua-nodeset/DI/
|
||||
ua_generate_datatypes(
|
||||
NAME "ua_types_di"
|
||||
TARGET_SUFFIX "types-di"
|
||||
NAMESPACE_IDX 2
|
||||
NAMESPACE_MAP "2:http://opcfoundation.org/UA/DI/"
|
||||
FILE_CSV "${PROJECT_SOURCE_DIR}/deps/ua-nodeset/DI/OpcUaDiModel.csv"
|
||||
FILES_BSD "${PROJECT_SOURCE_DIR}/deps/ua-nodeset/DI/Opc.Ua.Di.Types.bsd"
|
||||
)
|
||||
|
||||
The ``NAMESPACE_IDX`` parameter indicates the namespace index of the generated node IDs for the type definitions. Currently we need to rely that the namespace is also added at this position in the final server. There is no automatic inferring yet (pull requests are warmly welcome).
|
||||
The ``NAMESPACE_MAP`` parameter is an array of strings which indicates the mapping of specific namespace uris to the resulting namespace index.
|
||||
This mapping is required for correct mapping of DataType nodes and their node ids. Currently we need to rely that the namespace is also added at this position in the final server. There is no automatic inferring yet (pull requests are warmly welcome).
|
||||
The CSV and BSD files contain the metadata and definition for the types. ``TARGET_SUFFIX`` is used to create a new target with the name ``open62541-generator-TARGET_SUFFIX``.
|
||||
|
||||
Now you can compile the DI nodeset XML using the following command::
|
||||
|
@ -47,7 +47,7 @@ if(UA_NAMESPACE_ZERO STREQUAL "FULL")
|
||||
NAME "testnodeset"
|
||||
FILE_CSV "${FILE_CSV_DIRPREFIX}/testnodeset.csv"
|
||||
FILE_BSD "${FILE_BSD_DIRPREFIX}/testtypes.bsd"
|
||||
NAMESPACE_IDX 2
|
||||
NAMESPACE_MAP "2:http://yourorganisation.org/test/"
|
||||
FILE_NS "${FILE_NS_DIRPREFIX}/testnodeset.xml"
|
||||
INTERNAL
|
||||
)
|
||||
@ -82,7 +82,7 @@ if(UA_NAMESPACE_ZERO STREQUAL "FULL")
|
||||
NAME "di"
|
||||
FILE_CSV "${FILE_CSV_DIRPREFIX}/DI/OpcUaDiModel.csv"
|
||||
FILE_BSD "${FILE_BSD_PLCOPEN_DIRPREFIX}/DI/Opc.Ua.Di.Types.bsd"
|
||||
NAMESPACE_IDX 2
|
||||
NAMESPACE_MAP "2:http://opcfoundation.org/UA/DI/"
|
||||
FILE_NS "${FILE_NS_DIRPREFIX}/DI/Opc.Ua.Di.NodeSet2.xml"
|
||||
INTERNAL
|
||||
)
|
||||
@ -116,7 +116,7 @@ if(UA_NAMESPACE_ZERO STREQUAL "FULL")
|
||||
NAME "powerlink"
|
||||
FILE_CSV "${FILE_CSV_DIRPREFIX}/POWERLINK/Opc.Ua.POWERLINK.NodeIds.csv"
|
||||
FILE_BSD "${FILE_BSD_POWERLINK_DIRPREFIX}/Opc.Ua.POWERLINK.NodeSet2.bsd"
|
||||
NAMESPACE_IDX 3
|
||||
NAMESPACE_MAP "2:http://opcfoundation.org/UA/DI/" "3:http://opcfoundation.org/UA/POWERLINK/"
|
||||
FILE_NS "${FILE_NS_DIRPREFIX}/POWERLINK/Opc.Ua.POWERLINK.NodeSet2.xml"
|
||||
# POWERLINK depends on the di nodeset, which must be generated before
|
||||
DEPENDS "di"
|
||||
@ -131,4 +131,4 @@ if(UA_NAMESPACE_ZERO STREQUAL "FULL")
|
||||
add_dependencies(server_nodeset_powerlink open62541-generator-ns-powerlink)
|
||||
|
||||
|
||||
endif()
|
||||
endif()
|
||||
|
@ -13,7 +13,7 @@ if(UA_NAMESPACE_ZERO STREQUAL "FULL")
|
||||
NAME "tests-di"
|
||||
FILE_CSV "${PROJECT_SOURCE_DIR}/deps/ua-nodeset/DI/OpcUaDiModel.csv"
|
||||
FILE_BSD "${PROJECT_SOURCE_DIR}/deps/ua-nodeset/DI/Opc.Ua.Di.Types.bsd"
|
||||
NAMESPACE_IDX 2
|
||||
NAMESPACE_MAP "2:http://opcfoundation.org/UA/DI/"
|
||||
OUTPUT_DIR "${GENERATE_OUTPUT_DIR}"
|
||||
FILE_NS "${PROJECT_SOURCE_DIR}/deps/ua-nodeset/DI/Opc.Ua.Di.NodeSet2.xml"
|
||||
INTERNAL
|
||||
@ -24,7 +24,7 @@ if(UA_NAMESPACE_ZERO STREQUAL "FULL")
|
||||
NAME "tests-adi"
|
||||
FILE_CSV "${PROJECT_SOURCE_DIR}/deps/ua-nodeset/ADI/OpcUaAdiModel.csv"
|
||||
FILE_BSD "${PROJECT_SOURCE_DIR}/deps/ua-nodeset/ADI/Opc.Ua.Adi.Types.bsd"
|
||||
NAMESPACE_IDX 3
|
||||
NAMESPACE_MAP "2:http://opcfoundation.org/UA/DI/" "3:http://opcfoundation.org/UA/ADI/"
|
||||
OUTPUT_DIR "${GENERATE_OUTPUT_DIR}"
|
||||
FILE_NS "${PROJECT_SOURCE_DIR}/deps/ua-nodeset/ADI/Opc.Ua.Adi.NodeSet2.xml"
|
||||
DEPENDS "tests-di"
|
||||
@ -67,7 +67,7 @@ if(UA_NAMESPACE_ZERO STREQUAL "FULL")
|
||||
NAME "tests-autoid"
|
||||
FILE_CSV "${PROJECT_SOURCE_DIR}/tests/nodeset-compiler/Opc.Ua.AutoID.NodeIds.csv"
|
||||
FILE_BSD "${PROJECT_SOURCE_DIR}/tests/nodeset-compiler/Opc.Ua.AutoID.Types.bsd"
|
||||
NAMESPACE_IDX 3
|
||||
NAMESPACE_MAP "2:http://opcfoundation.org/UA/DI/" "3:http://opcfoundation.org/UA/AutoID/"
|
||||
OUTPUT_DIR "${GENERATE_OUTPUT_DIR}"
|
||||
FILE_NS "${PROJECT_SOURCE_DIR}/deps/ua-nodeset/AutoID/Opc.Ua.AutoID.NodeSet2.xml"
|
||||
DEPENDS "tests-di"
|
||||
@ -113,7 +113,7 @@ if(UA_NAMESPACE_ZERO STREQUAL "FULL")
|
||||
NAME "tests-testnodeset"
|
||||
FILE_CSV "${PROJECT_SOURCE_DIR}/tests/nodeset-compiler/testnodeset.csv"
|
||||
FILE_BSD "${PROJECT_SOURCE_DIR}/tests/nodeset-compiler/testtypes.bsd"
|
||||
NAMESPACE_IDX 2
|
||||
NAMESPACE_MAP "2:http://yourorganisation.org/test/"
|
||||
OUTPUT_DIR "${GENERATE_OUTPUT_DIR}"
|
||||
FILE_NS "${PROJECT_SOURCE_DIR}/tests/nodeset-compiler/testnodeset.xml"
|
||||
)
|
||||
|
@ -88,9 +88,6 @@ endfunction()
|
||||
# NAME Full name of the generated files, e.g. ua_types_di
|
||||
# TARGET_SUFFIX Suffix for the resulting target. e.g. types-di
|
||||
# [TARGET_PREFIX] Optional prefix for the resulting target. Default `open62541-generator`
|
||||
# NAMESPACE_IDX Namespace index of the nodeset, when it is loaded into the server. This index
|
||||
# is used for the node ids withing the types array and is currently not determined automatically.
|
||||
# Make sure that it matches the namespace index in the server.
|
||||
# [OUTPUT_DIR] Optional target directory for the generated files. Default is '${PROJECT_BINARY_DIR}/src_generated'
|
||||
# FILE_CSV Path to the .csv file containing the node ids, e.g. 'OpcUaDiModel.csv'
|
||||
#
|
||||
@ -104,12 +101,16 @@ endfunction()
|
||||
# Multiple files can be passed which will all be imported.
|
||||
# [FILES_SELECTED] Optional path to a simple text file which contains a list of types which should be included in the generation.
|
||||
# The file should contain one type per line. Multiple files can be passed to this argument.
|
||||
# NAMESPACE_MAP Array of Namespace index mappings to indicate the final namespace index of a namespace uri when the server is started.
|
||||
# This is required to correctly map datatype node ids to the resulting server namespace index.
|
||||
# "0:http://opcfoundation.org/UA/" is added by default.
|
||||
# Example: ["2:http://example.org/UA/"]
|
||||
#
|
||||
#
|
||||
function(ua_generate_datatypes)
|
||||
set(options BUILTIN INTERNAL)
|
||||
set(oneValueArgs NAME TARGET_SUFFIX TARGET_PREFIX NAMESPACE_IDX OUTPUT_DIR FILE_CSV)
|
||||
set(multiValueArgs FILES_BSD IMPORT_BSD FILES_SELECTED)
|
||||
set(oneValueArgs NAME TARGET_SUFFIX TARGET_PREFIX OUTPUT_DIR FILE_CSV)
|
||||
set(multiValueArgs FILES_BSD IMPORT_BSD FILES_SELECTED NAMESPACE_MAP)
|
||||
cmake_parse_arguments(UA_GEN_DT "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN} )
|
||||
|
||||
if(NOT DEFINED open62541_TOOLS_DIR)
|
||||
@ -117,8 +118,8 @@ function(ua_generate_datatypes)
|
||||
endif()
|
||||
|
||||
# ------ Argument checking -----
|
||||
if(NOT DEFINED UA_GEN_DT_NAMESPACE_IDX AND NOT "${UA_GEN_DT_NAMESPACE_IDX}" STREQUAL "0")
|
||||
message(FATAL_ERROR "ua_generate_datatype function requires a value for the NAMESPACE_IDX argument")
|
||||
if(NOT DEFINED UA_GEN_DT_NAMESPACE_MAP AND NOT "${UA_GEN_DT_NAMESPACE_MAP}" STREQUAL "")
|
||||
message(FATAL_ERROR "ua_generate_datatype function requires a value for the NAMESPACE_MAP argument")
|
||||
endif()
|
||||
if(NOT UA_GEN_DT_NAME OR "${UA_GEN_DT_NAME}" STREQUAL "")
|
||||
message(FATAL_ERROR "ua_generate_datatype function requires a value for the NAME argument")
|
||||
@ -144,6 +145,11 @@ function(ua_generate_datatypes)
|
||||
|
||||
# ------ Add custom command and target -----
|
||||
|
||||
set(NAMESPACE_MAP_TMP "")
|
||||
foreach(f ${UA_GEN_DT_NAMESPACE_MAP})
|
||||
set(NAMESPACE_MAP_TMP ${NAMESPACE_MAP_TMP} "--namespaceMap=${f}")
|
||||
endforeach()
|
||||
|
||||
set(UA_GEN_DT_NO_BUILTIN "--no-builtin")
|
||||
if (UA_GEN_DT_BUILTIN)
|
||||
set(UA_GEN_DT_NO_BUILTIN "")
|
||||
@ -183,7 +189,7 @@ function(ua_generate_datatypes)
|
||||
${UA_GEN_DT_OUTPUT_DIR}/${UA_GEN_DT_NAME}_generated_encoding_binary.h
|
||||
PRE_BUILD
|
||||
COMMAND ${PYTHON_EXECUTABLE} ${open62541_TOOLS_DIR}/generate_datatypes.py
|
||||
--namespace=${UA_GEN_DT_NAMESPACE_IDX}
|
||||
${NAMESPACE_MAP_TMP}
|
||||
${SELECTED_TYPES_TMP}
|
||||
${BSD_FILES_TMP}
|
||||
${IMPORT_BSD_TMP}
|
||||
@ -397,7 +403,7 @@ endfunction()
|
||||
# This is a combination of the ua_generate_datatypes, ua_generate_nodeset, and
|
||||
# ua_generate_nodeid_header macros.
|
||||
# This function can also be used to just create a nodeset without datatypes by
|
||||
# omitting the CSV, BSD, and NAMESPACE_IDX parameter.
|
||||
# omitting the CSV, BSD, and NAMESPACE_MAP parameter.
|
||||
# If only one of the previous parameters is given, all of them are required.
|
||||
#
|
||||
# It is possible to define dependencies of nodesets by using the DEPENDS argument.
|
||||
@ -426,8 +432,8 @@ endfunction()
|
||||
# the FILES_BSD files. The value is separated with a hash sign, i.e.
|
||||
# 'UA_TYPES#${PROJECT_SOURCE_DIR}/deps/ua-nodeset/Schema/Opc.Ua.Types.bsd'
|
||||
# Multiple files can be passed which will all be imported.
|
||||
# [NAMESPACE_IDX] Optional namespace index of the nodeset, when it is loaded into the server. This parameter is mandatory if FILE_CSV
|
||||
# or FILE_BSD is set. See ua_generate_datatypes function.
|
||||
# [NAMESPACE_MAP] Array of Namespace index mappings to indicate the final namespace index of a namespace uri when the server is started.
|
||||
# This parameter is mandatory if FILE_CSV or FILE_BSD is set.
|
||||
# [BLACKLIST] Blacklist file passed as --blacklist to the nodeset compiler. All the given nodes will be removed from the generated
|
||||
# nodeset, including all the references to and from that node. The format is a node id per line.
|
||||
# Supported formats: "i=123" (for NS0), "ns=2;s=asdf" (matches NS2 in that specific file), or recommended
|
||||
@ -442,8 +448,8 @@ endfunction()
|
||||
function(ua_generate_nodeset_and_datatypes)
|
||||
|
||||
set(options INTERNAL)
|
||||
set(oneValueArgs NAME FILE_NS FILE_CSV FILE_BSD IMPORT_BSD NAMESPACE_IDX OUTPUT_DIR TARGET_PREFIX BLACKLIST)
|
||||
set(multiValueArgs DEPENDS)
|
||||
set(oneValueArgs NAME FILE_NS FILE_CSV FILE_BSD IMPORT_BSD OUTPUT_DIR TARGET_PREFIX BLACKLIST)
|
||||
set(multiValueArgs DEPENDS NAMESPACE_MAP)
|
||||
cmake_parse_arguments(UA_GEN "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN} )
|
||||
|
||||
if(NOT DEFINED open62541_TOOLS_DIR)
|
||||
@ -466,20 +472,20 @@ function(ua_generate_nodeset_and_datatypes)
|
||||
|
||||
if((NOT UA_GEN_FILE_CSV OR "${UA_GEN_FILE_CSV}" STREQUAL "") AND
|
||||
(NOT "${UA_GEN_FILE_BSD}" STREQUAL "" OR
|
||||
NOT "${UA_GEN_NAMESPACE_IDX}" STREQUAL ""))
|
||||
message(FATAL_ERROR "ua_generate_nodeset_and_datatypes function requires FILE_CSV argument if any of FILE_BSD or NAMESPACE_IDX are set")
|
||||
NOT "${UA_GEN_NAMESPACE_MAP}" STREQUAL ""))
|
||||
message(FATAL_ERROR "ua_generate_nodeset_and_datatypes function requires FILE_CSV argument if any of FILE_BSD or NAMESPACE_MAP are set")
|
||||
endif()
|
||||
|
||||
if((NOT UA_GEN_FILE_BSD OR "${UA_GEN_FILE_BSD}" STREQUAL "") AND
|
||||
(NOT "${UA_GEN_FILE_CSV}" STREQUAL "" OR
|
||||
NOT "${UA_GEN_NAMESPACE_IDX}" STREQUAL ""))
|
||||
message(FATAL_ERROR "ua_generate_nodeset_and_datatypes function requires FILE_BSD argument if any of FILE_CSV or NAMESPACE_IDX are set")
|
||||
NOT "${UA_GEN_NAMESPACE_MAP}" STREQUAL ""))
|
||||
message(FATAL_ERROR "ua_generate_nodeset_and_datatypes function requires FILE_BSD argument if any of FILE_CSV or NAMESPACE_MAP are set")
|
||||
endif()
|
||||
|
||||
if(NOT UA_GEN_NAMESPACE_IDX OR "${UA_GEN_NAMESPACE_IDX}" STREQUAL "" AND
|
||||
if(NOT UA_GEN_NAMESPACE_MAP OR "${UA_GEN_NAMESPACE_MAP}" STREQUAL "" AND
|
||||
(NOT "${UA_GEN_FILE_CSV}" STREQUAL "" OR
|
||||
NOT "${UA_GEN_FILE_BSD}" STREQUAL ""))
|
||||
message(FATAL_ERROR "ua_generate_nodeset_and_datatypes function requires NAMESPACE_IDX argument if any of FILE_CSV or FILE_BSD are set")
|
||||
message(FATAL_ERROR "ua_generate_nodeset_and_datatypes function requires NAMESPACE_MAP argument if any of FILE_CSV or FILE_BSD are set")
|
||||
endif()
|
||||
|
||||
# Set default value for output dir
|
||||
@ -500,7 +506,7 @@ function(ua_generate_nodeset_and_datatypes)
|
||||
NAME "types_${UA_GEN_NAME}"
|
||||
TARGET_PREFIX "${UA_GEN_TARGET_PREFIX}"
|
||||
TARGET_SUFFIX "types-${UA_GEN_NAME}"
|
||||
NAMESPACE_IDX ${UA_GEN_NAMESPACE_IDX}
|
||||
NAMESPACE_MAP "${UA_GEN_NAMESPACE_MAP}"
|
||||
FILE_CSV "${UA_GEN_FILE_CSV}"
|
||||
FILES_BSD "${UA_GEN_FILE_BSD}"
|
||||
IMPORT_BSD "${UA_GEN_IMPORT_BSD}"
|
||||
|
@ -22,11 +22,20 @@ parser.add_argument('-c', '--type-csv',
|
||||
default=[],
|
||||
help='csv file with type descriptions')
|
||||
|
||||
parser.add_argument('--namespace',
|
||||
type=int,
|
||||
dest="namespace",
|
||||
default=0,
|
||||
help='namespace id of the generated type nodeids (defaults to 0)')
|
||||
parser.add_argument('--namespaceUri',
|
||||
type=str,
|
||||
dest="namespaceUri",
|
||||
default="http://opcfoundation.org/UA/",
|
||||
help='namespace uri of the generated type nodeids (defaults to "http://opcfoundation.org/UA/")')
|
||||
|
||||
parser.add_argument('--namespaceMap',
|
||||
metavar="<namespaceMap>",
|
||||
type=str,
|
||||
dest="namespace_map",
|
||||
action='append',
|
||||
default=["0:http://opcfoundation.org/UA/"],
|
||||
help='Mapping of namespace uri to the resulting namespace index in the server. Default only contains Namespace 0: "0:http://opcfoundation.org/UA/". '
|
||||
'Parameter can be used multiple times to define multiple mappings.')
|
||||
|
||||
parser.add_argument('-s', '--selected-types',
|
||||
metavar="<selectedTypes>",
|
||||
@ -78,9 +87,18 @@ args = parser.parse_args()
|
||||
outname = args.outfile.split("/")[-1]
|
||||
inname = ', '.join(list(map(lambda x: x.name.split("/")[-1], args.type_bsd)))
|
||||
|
||||
parser = CSVBSDTypeParser(args.opaque_map, args.selected_types, args.no_builtin, outname, args.namespace, args.import_bsd,
|
||||
args.type_bsd, args.type_csv)
|
||||
namespaceMap = {
|
||||
"http://opcfoundation.org/UA/": 0
|
||||
}
|
||||
|
||||
for m in args.namespace_map:
|
||||
[idx, ns] = m.split(':', 1)
|
||||
namespaceMap[ns] = int(idx)
|
||||
|
||||
|
||||
parser = CSVBSDTypeParser(args.opaque_map, args.selected_types, args.no_builtin, outname, args.import_bsd,
|
||||
args.type_bsd, args.type_csv, namespaceMap)
|
||||
parser.create_types()
|
||||
|
||||
generator = backend.CGenerator(parser, inname, args.outfile, args.internal)
|
||||
generator = backend.CGenerator(parser, inname, args.outfile, args.internal, namespaceMap)
|
||||
generator.write_definitions()
|
||||
|
@ -2,6 +2,7 @@ from __future__ import print_function
|
||||
import re
|
||||
import itertools
|
||||
import sys
|
||||
import copy
|
||||
import time
|
||||
import getpass
|
||||
import platform
|
||||
@ -58,12 +59,13 @@ def getNodeidTypeAndId(nodeId):
|
||||
return "UA_NODEIDTYPE_STRING, {{ .string = UA_STRING_STATIC(\"{id}\") }}".format(id=strId.replace("\"", "\\\""))
|
||||
|
||||
class CGenerator(object):
|
||||
def __init__(self, parser, inname, outfile, is_internal_types):
|
||||
def __init__(self, parser, inname, outfile, is_internal_types, namespaceMap):
|
||||
self.parser = parser
|
||||
self.inname = inname
|
||||
self.outfile = outfile
|
||||
self.is_internal_types = is_internal_types
|
||||
self.filtered_types = None
|
||||
self.namespaceMap = namespaceMap
|
||||
self.fh = None
|
||||
self.ff = None
|
||||
self.fc = None
|
||||
@ -74,7 +76,7 @@ class CGenerator(object):
|
||||
if isinstance(datatype, BuiltinType):
|
||||
return makeCIdentifier("UA_TYPES_" + datatype.name.upper())
|
||||
if isinstance(datatype, EnumerationType):
|
||||
return datatype.strTypeIndex;
|
||||
return datatype.strTypeIndex
|
||||
|
||||
if datatype.name is not None:
|
||||
return "UA_" + makeCIdentifier(datatype.outname.upper() + "_" + datatype.name.upper())
|
||||
@ -123,9 +125,9 @@ class CGenerator(object):
|
||||
return self.get_struct_overlayable(datatype)
|
||||
raise RuntimeError("Unknown datatype")
|
||||
|
||||
def print_datatype(self, datatype):
|
||||
typeid = "{%s, %s}" % (datatype.namespace, getNodeidTypeAndId(datatype.nodeId))
|
||||
binaryEncodingId = "{%s, %s}" % (datatype.namespace,
|
||||
def print_datatype(self, datatype, namespaceMap):
|
||||
typeid = "{%s, %s}" % (namespaceMap[datatype.namespaceUri], getNodeidTypeAndId(datatype.nodeId))
|
||||
binaryEncodingId = "{%s, %s}" % (namespaceMap[datatype.namespaceUri],
|
||||
getNodeidTypeAndId(datatype.binaryEncodingId))
|
||||
idName = makeCIdentifier(datatype.name)
|
||||
pointerfree = "true" if datatype.pointerfree else "false"
|
||||
@ -143,7 +145,7 @@ class CGenerator(object):
|
||||
"}"
|
||||
|
||||
@staticmethod
|
||||
def print_members(datatype):
|
||||
def print_members(datatype, namespaceMap):
|
||||
idName = makeCIdentifier(datatype.name)
|
||||
if len(datatype.members) == 0:
|
||||
return "#define %s_members NULL" % (idName)
|
||||
@ -179,7 +181,7 @@ class CGenerator(object):
|
||||
else:
|
||||
m += " - sizeof(UA_%s)," % makeCIdentifier(before.member_type.name)
|
||||
m += " /* .padding */\n"
|
||||
m += " %s, /* .namespaceZero */\n" % ("true" if member.member_type.ns0 else "false")
|
||||
m += " %s, /* .namespaceZero */\n" % ("true" if (namespaceMap[member.member_type.namespaceUri] == 0) else "false")
|
||||
m += (" true" if member.is_array else " false") + ", /* .isArray */\n"
|
||||
m += (" true" if member.is_optional else " false") + " /* .isOptional */\n"
|
||||
m += " UA_TYPENAME(\"%s\") /* .memberName */\n}" % member_name_capital
|
||||
@ -333,16 +335,23 @@ class CGenerator(object):
|
||||
print(string, end='\n', file=self.fc)
|
||||
|
||||
def iter_types(self, v):
|
||||
l = None
|
||||
if sys.version_info[0] < 3:
|
||||
l = list(v.itervalues())
|
||||
else:
|
||||
l = list(v.values())
|
||||
l = copy.deepcopy(v)
|
||||
if len(self.parser.selected_types) > 0:
|
||||
l = list(filter(lambda t: t.name in self.parser.selected_types, l))
|
||||
for ns in v:
|
||||
for t in v[ns]:
|
||||
if t not in self.parser.selected_types:
|
||||
if ns in l and t in l[ns]:
|
||||
del l[ns][t]
|
||||
if self.parser.no_builtin:
|
||||
l = list(filter(lambda t: not isinstance(t, BuiltinType), l))
|
||||
l = list(filter(lambda t: t.name not in self.parser.existing_types, l))
|
||||
for ns in v:
|
||||
for t in v[ns]:
|
||||
if isinstance(v[ns][t], BuiltinType):
|
||||
if ns in l and t in l[ns]:
|
||||
del l[ns][t]
|
||||
for ns in self.parser.existing_types:
|
||||
for t in self.parser.existing_types[ns]:
|
||||
if ns in l and t in l[ns]:
|
||||
del l[ns][t]
|
||||
return l
|
||||
|
||||
def print_header(self):
|
||||
@ -368,24 +377,29 @@ _UA_BEGIN_DECLS
|
||||
* Every type is assigned an index in an array containing the type descriptions.
|
||||
* These descriptions are used during type handling (copying, deletion,
|
||||
* binary encoding, ...). */''')
|
||||
self.printh("#define UA_" + self.parser.outname.upper() + "_COUNT %s" % (str(len(self.filtered_types))))
|
||||
totalCount = 0
|
||||
for ns in self.filtered_types:
|
||||
totalCount += len(self.filtered_types[ns])
|
||||
self.printh("#define UA_" + self.parser.outname.upper() + "_COUNT %s" % (str(totalCount)))
|
||||
|
||||
if len(self.filtered_types) > 0:
|
||||
|
||||
self.printh(
|
||||
"extern UA_EXPORT const UA_DataType UA_" + self.parser.outname.upper() + "[UA_" + self.parser.outname.upper() + "_COUNT];")
|
||||
|
||||
for i, t in enumerate(self.filtered_types):
|
||||
self.printh("\n/**\n * " + t.name)
|
||||
self.printh(" * " + "^" * len(t.name))
|
||||
if t.description == "":
|
||||
self.printh(" */")
|
||||
else:
|
||||
self.printh(" * " + t.description + " */")
|
||||
if not isinstance(t, BuiltinType):
|
||||
self.printh(self.print_datatype_typedef(t) + "\n")
|
||||
self.printh(
|
||||
"#define UA_" + makeCIdentifier(self.parser.outname.upper() + "_" + t.name.upper()) + " " + str(i))
|
||||
for ns in self.filtered_types:
|
||||
for i, t_name in enumerate(self.filtered_types[ns]):
|
||||
t = self.filtered_types[ns][t_name]
|
||||
self.printh("\n/**\n * " + t.name)
|
||||
self.printh(" * " + "^" * len(t.name))
|
||||
if t.description == "":
|
||||
self.printh(" */")
|
||||
else:
|
||||
self.printh(" * " + t.description + " */")
|
||||
if not isinstance(t, BuiltinType):
|
||||
self.printh(self.print_datatype_typedef(t) + "\n")
|
||||
self.printh(
|
||||
"#define UA_" + makeCIdentifier(self.parser.outname.upper() + "_" + t.name.upper()) + " " + str(i))
|
||||
|
||||
self.printh('''
|
||||
|
||||
@ -412,9 +426,11 @@ _UA_BEGIN_DECLS
|
||||
#endif
|
||||
''')
|
||||
|
||||
for t in self.filtered_types:
|
||||
self.printf("\n/* " + t.name + " */")
|
||||
self.printf(self.print_functions(t))
|
||||
for ns in self.filtered_types:
|
||||
for i, t_name in enumerate(self.filtered_types[ns]):
|
||||
t = self.filtered_types[ns][t_name]
|
||||
self.printf("\n/* " + t.name + " */")
|
||||
self.printf(self.print_functions(t))
|
||||
|
||||
self.printf('''
|
||||
#if defined(__GNUC__) && __GNUC__ >= 4 && __GNUC_MINOR__ >= 6
|
||||
@ -432,18 +448,22 @@ _UA_END_DECLS
|
||||
|
||||
#include "''' + self.parser.outname + '''_generated.h"''')
|
||||
|
||||
for t in self.filtered_types:
|
||||
self.printc("")
|
||||
self.printc("/* " + t.name + " */")
|
||||
self.printc(CGenerator.print_members(t))
|
||||
for ns in self.filtered_types:
|
||||
for i, t_name in enumerate(self.filtered_types[ns]):
|
||||
t = self.filtered_types[ns][t_name]
|
||||
self.printc("")
|
||||
self.printc("/* " + t.name + " */")
|
||||
self.printc(CGenerator.print_members(t, self.namespaceMap))
|
||||
|
||||
if len(self.filtered_types) > 0:
|
||||
self.printc(
|
||||
"const UA_DataType UA_%s[UA_%s_COUNT] = {" % (self.parser.outname.upper(), self.parser.outname.upper()))
|
||||
|
||||
for t in self.filtered_types:
|
||||
self.printc("/* " + t.name + " */")
|
||||
self.printc(self.print_datatype(t) + ",")
|
||||
for ns in self.filtered_types:
|
||||
for i, t_name in enumerate(self.filtered_types[ns]):
|
||||
t = self.filtered_types[ns][t_name]
|
||||
self.printc("/* " + t.name + " */")
|
||||
self.printc(self.print_datatype(t, self.namespaceMap) + ",")
|
||||
self.printc("};\n")
|
||||
|
||||
def print_encoding(self):
|
||||
@ -463,8 +483,10 @@ _UA_END_DECLS
|
||||
|
||||
''')
|
||||
|
||||
for t in self.filtered_types:
|
||||
self.printe("\n/* " + t.name + " */")
|
||||
self.printe(self.print_datatype_encoding(t))
|
||||
for ns in self.filtered_types:
|
||||
for i, t_name in enumerate(self.filtered_types[ns]):
|
||||
t = self.filtered_types[ns][t_name]
|
||||
self.printe("\n/* " + t.name + " */")
|
||||
self.printe(self.print_datatype_encoding(t))
|
||||
|
||||
self.printe("\n#endif /* " + self.parser.outname.upper() + "_GENERATED_ENCODING_BINARY_H_ */")
|
||||
|
@ -2,6 +2,7 @@ import abc
|
||||
import csv
|
||||
import json
|
||||
import xml.etree.ElementTree as etree
|
||||
import copy
|
||||
import re
|
||||
from collections import OrderedDict
|
||||
import sys
|
||||
@ -32,6 +33,8 @@ type_aliases = {"CharArray": "String"}
|
||||
|
||||
user_opaque_type_mapping = {} # contains user defined opaque type mapping
|
||||
|
||||
class TypeNotDefinedException(Exception):
|
||||
pass
|
||||
|
||||
def get_base_type_for_opaque(name):
|
||||
if name in user_opaque_type_mapping:
|
||||
@ -41,21 +44,33 @@ def get_base_type_for_opaque(name):
|
||||
|
||||
|
||||
def get_type_name(xml_type_name):
|
||||
type_name = xml_type_name[xml_type_name.find(":") + 1:]
|
||||
return type_aliases.get(type_name, type_name)
|
||||
[namespace, type_name] = xml_type_name.split(':', 1)
|
||||
return [namespace, type_aliases.get(type_name, type_name)]
|
||||
|
||||
def get_type_for_name(xml_type_name, types, xmlNamespaces):
|
||||
[member_type_name_ns, member_type_name] = get_type_name(xml_type_name)
|
||||
resultNs = xmlNamespaces[member_type_name_ns]
|
||||
if resultNs == 'http://opcfoundation.org/BinarySchema/':
|
||||
resultNs = 'http://opcfoundation.org/UA/'
|
||||
if resultNs not in types:
|
||||
raise TypeNotDefinedException("Unknown namespace: '{resultNs}'".format(
|
||||
resultNs=resultNs))
|
||||
if member_type_name not in types[resultNs]:
|
||||
raise TypeNotDefinedException("Unknown type: '{type}'".format(
|
||||
type=member_type_name))
|
||||
return types[resultNs][member_type_name]
|
||||
|
||||
|
||||
class Type(object):
|
||||
def __init__(self, outname, xml, namespace):
|
||||
def __init__(self, outname, xml, namespaceUri):
|
||||
self.name = None
|
||||
if xml is not None:
|
||||
self.name = xml.get("Name")
|
||||
self.outname = outname
|
||||
self.namespace = namespace
|
||||
self.namespaceUri = namespaceUri
|
||||
self.pointerfree = False
|
||||
self.members = []
|
||||
self.description = ""
|
||||
self.ns0 = (namespace == 0)
|
||||
self.nodeId = None
|
||||
self.binaryEncodingId = None
|
||||
if xml is not None:
|
||||
@ -67,7 +82,7 @@ class Type(object):
|
||||
|
||||
class BuiltinType(Type):
|
||||
def __init__(self, name):
|
||||
Type.__init__(self, "types", None, 0)
|
||||
Type.__init__(self, "types", None, "http://opcfoundation.org/UA/")
|
||||
self.name = name
|
||||
if self.name in builtin_overlayable:
|
||||
self.pointerfree = True
|
||||
@ -134,27 +149,27 @@ class StructMember(object):
|
||||
|
||||
|
||||
class StructType(Type):
|
||||
def __init__(self, outname, xml, namespace, types):
|
||||
def __init__(self, outname, xml, namespace, types, xmlNamespaces):
|
||||
Type.__init__(self, outname, xml, namespace)
|
||||
length_fields = []
|
||||
optional_fields = []
|
||||
|
||||
bt = xml.get("BaseType")
|
||||
self.is_union = True if bt and get_type_name(bt) == "Union" else False
|
||||
self.is_union = True if bt and get_type_name(bt)[1] == "Union" else False
|
||||
for child in xml:
|
||||
length_field = child.get("LengthField")
|
||||
if length_field:
|
||||
length_fields.append(length_field)
|
||||
for child in xml:
|
||||
child_type = child.get("TypeName")
|
||||
if child_type and get_type_name(child_type) == "Bit":
|
||||
if child_type and get_type_name(child_type)[1] == "Bit":
|
||||
optional_fields.append(child.get("Name"))
|
||||
for child in xml:
|
||||
if not child.tag == "{http://opcfoundation.org/BinarySchema/}Field":
|
||||
continue
|
||||
if child.get("Name") in length_fields:
|
||||
continue
|
||||
if get_type_name(child.get("TypeName")) == "Bit":
|
||||
if get_type_name(child.get("TypeName"))[1] == "Bit":
|
||||
continue
|
||||
switch_field = child.get("SwitchField")
|
||||
if switch_field and switch_field in optional_fields:
|
||||
@ -163,8 +178,7 @@ class StructType(Type):
|
||||
member_is_optional = False
|
||||
member_name = child.get("Name")
|
||||
member_name = member_name[:1].lower() + member_name[1:]
|
||||
member_type_name = get_type_name(child.get("TypeName"))
|
||||
member_type = types[member_type_name]
|
||||
member_type = get_type_for_name(child.get("TypeName"), types, xmlNamespaces)
|
||||
is_array = True if child.get("LengthField") else False
|
||||
self.members.append(StructMember(member_name, member_type, is_array, member_is_optional))
|
||||
|
||||
@ -177,7 +191,7 @@ class StructType(Type):
|
||||
class TypeParser():
|
||||
__metaclass__ = abc.ABCMeta
|
||||
|
||||
def __init__(self, opaque_map, selected_types, no_builtin, outname, namespace):
|
||||
def __init__(self, opaque_map, selected_types, no_builtin, outname, namespaceIndexMap):
|
||||
self.selected_types = []
|
||||
self.fh = None
|
||||
self.ff = None
|
||||
@ -187,8 +201,8 @@ class TypeParser():
|
||||
self.selected_types = selected_types
|
||||
self.no_builtin = no_builtin
|
||||
self.outname = outname
|
||||
self.namespace = namespace
|
||||
self.types = OrderedDict()
|
||||
self.namespaceIndexMap = namespaceIndexMap
|
||||
|
||||
@staticmethod
|
||||
def merge_dicts(*dict_args):
|
||||
@ -202,23 +216,28 @@ class TypeParser():
|
||||
return result
|
||||
|
||||
def parseTypeDefinitions(self, outname, xmlDescription):
|
||||
def typeReady(element):
|
||||
def typeReady(element, types, xmlNamespaces):
|
||||
"Are all member types defined?"
|
||||
for child in element:
|
||||
if child.tag == "{http://opcfoundation.org/BinarySchema/}Field":
|
||||
childname = get_type_name(child.get("TypeName"))
|
||||
if childname not in self.types and childname != "Bit":
|
||||
return False
|
||||
if get_type_name(child.get("TypeName"))[1] != "Bit":
|
||||
try:
|
||||
get_type_for_name(child.get("TypeName"), types, xmlNamespaces)
|
||||
except TypeNotDefinedException:
|
||||
# Type is using other types which are not yet loaded, try later
|
||||
return False
|
||||
return True
|
||||
|
||||
def unknownTypes(element):
|
||||
def unknownTypes(element, types, xmlNamespaces):
|
||||
"Return all unknown types (for debugging)"
|
||||
unknowns = []
|
||||
for child in element:
|
||||
if child.tag == "{http://opcfoundation.org/BinarySchema/}Field":
|
||||
childname = get_type_name(child.get("TypeName"))
|
||||
if childname not in self.types:
|
||||
unknowns.append(childname)
|
||||
try:
|
||||
get_type_for_name(child.get("TypeName"), types, xmlNamespaces)
|
||||
except TypeNotDefinedException:
|
||||
# Type is using other types which are not yet loaded, try later
|
||||
unknowns.append(child.get("TypeName"))
|
||||
return unknowns
|
||||
|
||||
def skipType(name):
|
||||
@ -236,7 +255,7 @@ class TypeParser():
|
||||
if child.tag != "{http://opcfoundation.org/BinarySchema/}Field":
|
||||
continue
|
||||
typename = child.get("TypeName")
|
||||
if typename and get_type_name(typename) == "Bit":
|
||||
if typename and get_type_name(typename)[1] == "Bit":
|
||||
if re.match(re.compile('.+Specified'), child.get("Name")):
|
||||
opt_fields.append(child.get("Name"))
|
||||
elif child.get("Name") == "Reserved1":
|
||||
@ -258,12 +277,26 @@ class TypeParser():
|
||||
"Is this a structure with bitfields?"
|
||||
for child in element:
|
||||
typename = child.get("TypeName")
|
||||
if typename and get_type_name(typename) == "Bit":
|
||||
if typename and get_type_name(typename)[1] == "Bit":
|
||||
return True
|
||||
return False
|
||||
|
||||
snippets = {}
|
||||
for typeXml in etree.parse(xmlDescription).getroot():
|
||||
xmlDoc = etree.iterparse(
|
||||
xmlDescription, events=['start-ns']
|
||||
)
|
||||
xmlNamespaces = dict([
|
||||
node for _, node in xmlDoc
|
||||
])
|
||||
if xmlDoc.root.get("TargetNamespace"):
|
||||
targetNamespace = xmlDoc.root.get("TargetNamespace")
|
||||
if not targetNamespace in self.namespaceIndexMap:
|
||||
raise RuntimeError("TargetNamespace '{targetNs}' is not listed in namespace index mapping. "
|
||||
"Use the following option to define the mapping (can be used multiple times). "
|
||||
"--namespaceMap=X:{targetNs} where X is the resulting namespace index in the server.".format(targetNs=targetNamespace))
|
||||
else:
|
||||
raise RuntimeError("TargetNamespace Attribute not defined in BSD file.")
|
||||
for typeXml in xmlDoc.root:
|
||||
if not typeXml.get("Name"):
|
||||
continue
|
||||
name = typeXml.get("Name")
|
||||
@ -274,7 +307,7 @@ class TypeParser():
|
||||
if detectLoop == len(snippets):
|
||||
name, typeXml = snippets.popitem()
|
||||
raise RuntimeError("Infinite loop detected or type not found while processing types " +
|
||||
name + ": unknonwn subtype " + str(unknownTypes(typeXml)) +
|
||||
name + ": unknonwn subtype " + str(unknownTypes(typeXml, self.types, xmlNamespaces)) +
|
||||
". If the unknown subtype is 'Bit', then maybe a struct with " +
|
||||
"optional fields is defined wrong in the .bsd-file. If not, maybe " +
|
||||
"you need to import additional types with the --import flag. " +
|
||||
@ -282,35 +315,45 @@ class TypeParser():
|
||||
"Opc.Ua.Types.bsd'")
|
||||
detectLoop = len(snippets)
|
||||
for name, typeXml in list(snippets.items()):
|
||||
if name in self.types or skipType(name):
|
||||
if (targetNamespace in self.types and name in self.types[targetNamespace]) or skipType(name):
|
||||
del snippets[name]
|
||||
continue
|
||||
if not typeReady(typeXml):
|
||||
if not typeReady(typeXml, self.types, xmlNamespaces):
|
||||
continue
|
||||
if structWithBitFields(typeXml) and not structWithOptionalFields(typeXml):
|
||||
continue
|
||||
if name in builtin_types:
|
||||
new_type = BuiltinType(name)
|
||||
elif typeXml.tag == "{http://opcfoundation.org/BinarySchema/}EnumeratedType":
|
||||
new_type = EnumerationType(outname, typeXml, self.namespace)
|
||||
new_type = EnumerationType(outname, typeXml, targetNamespace)
|
||||
elif typeXml.tag == "{http://opcfoundation.org/BinarySchema/}OpaqueType":
|
||||
new_type = OpaqueType(outname, typeXml, self.namespace,
|
||||
new_type = OpaqueType(outname, typeXml, targetNamespace,
|
||||
get_base_type_for_opaque(name)['name'])
|
||||
elif typeXml.tag == "{http://opcfoundation.org/BinarySchema/}StructuredType":
|
||||
new_type = StructType(outname, typeXml, self.namespace, self.types)
|
||||
try:
|
||||
new_type = StructType(outname, typeXml, targetNamespace, self.types, xmlNamespaces)
|
||||
except TypeNotDefinedException:
|
||||
# Type is using other types which are not yet loaded, try later
|
||||
continue
|
||||
else:
|
||||
raise Exception("Type not known")
|
||||
|
||||
self.types[name] = new_type
|
||||
self.insert_type(new_type)
|
||||
del snippets[name]
|
||||
|
||||
@abc.abstractmethod
|
||||
def parse_types(self):
|
||||
pass
|
||||
|
||||
def insert_type(self, typeObject):
|
||||
if typeObject.namespaceUri not in self.types:
|
||||
self.types[typeObject.namespaceUri] = OrderedDict()
|
||||
if typeObject.name not in self.types[typeObject.namespaceUri]:
|
||||
self.types[typeObject.namespaceUri][typeObject.name] = typeObject
|
||||
|
||||
def create_types(self):
|
||||
for builtin in builtin_types:
|
||||
self.types[builtin] = BuiltinType(builtin)
|
||||
self.insert_type(BuiltinType(builtin))
|
||||
|
||||
for f in self.opaque_map:
|
||||
user_opaque_type_mapping.update(json.load(f))
|
||||
@ -322,15 +365,12 @@ class TypeParser():
|
||||
self.selected_types = []
|
||||
for f in arg_selected_types:
|
||||
self.selected_types += list(filter(len, [line.strip() for line in f]))
|
||||
# Use all types if none are selected
|
||||
if len(self.selected_types) == 0:
|
||||
self.selected_types = self.types.keys()
|
||||
|
||||
|
||||
class CSVBSDTypeParser(TypeParser):
|
||||
def __init__(self, opaque_map, selected_types, no_builtin, outname,
|
||||
namespace, existing_bsd, type_bsd, type_csv):
|
||||
TypeParser.__init__(self, opaque_map, selected_types, no_builtin, outname, namespace)
|
||||
existing_bsd, type_bsd, type_csv, namespaceIndexMap):
|
||||
TypeParser.__init__(self, opaque_map, selected_types, no_builtin, outname, namespaceIndexMap)
|
||||
self.existing_bsd = existing_bsd # bsd files with existing types that shall not be printed again
|
||||
self.type_bsd = type_bsd # bsd files with new types
|
||||
self.type_csv = type_csv # csv files with nodeids, etc.
|
||||
@ -345,14 +385,18 @@ class CSVBSDTypeParser(TypeParser):
|
||||
outname_import = outname_import[3:]
|
||||
self.parseTypeDefinitions(outname_import, file_import)
|
||||
|
||||
# extract the names of the existing types
|
||||
# the builtin types shall be printed for the "types" outname
|
||||
# all types loaded up to now should be assumed as existing types and therefore
|
||||
# no code should be generated
|
||||
self.existing_types = copy.deepcopy(self.types)
|
||||
# if outname is types (generate typedefinitions for NS0), we still need the BuiltinType
|
||||
# therefore remove them from the existing array
|
||||
if self.outname == "types":
|
||||
self.existing_types = list(filter(lambda n: n not in builtin_types, self.types.keys()))
|
||||
else:
|
||||
self.existing_types = list(self.types.keys()) # must be surrounded by "list"
|
||||
# otherwise the iterator changes
|
||||
# when self.types is extended
|
||||
for ns in self.types:
|
||||
for t in self.types[ns]:
|
||||
if isinstance(self.types[ns][t], BuiltinType):
|
||||
del self.existing_types[ns][t]
|
||||
|
||||
self.existing_types = OrderedDict()
|
||||
|
||||
# parse the new types
|
||||
for f in self.type_bsd:
|
||||
@ -373,9 +417,10 @@ class CSVBSDTypeParser(TypeParser):
|
||||
m = re.match('(.*?)_Encoding_DefaultBinary$', row[0])
|
||||
if m:
|
||||
baseType = m.group(1)
|
||||
if baseType not in self.types:
|
||||
continue
|
||||
self.types[baseType].binaryEncodingId = row[1]
|
||||
for ns in self.types:
|
||||
if baseType in self.types[ns]:
|
||||
self.types[ns][baseType].binaryEncodingId = row[1]
|
||||
break
|
||||
continue
|
||||
|
||||
if row[2] != "DataType":
|
||||
@ -386,6 +431,7 @@ class CSVBSDTypeParser(TypeParser):
|
||||
typeName = "Variant"
|
||||
elif typeName == "Structure":
|
||||
typeName = "ExtensionObject"
|
||||
if not typeName in self.types:
|
||||
continue
|
||||
self.types[typeName].nodeId = row[1]
|
||||
for ns in self.types:
|
||||
if typeName in self.types[ns]:
|
||||
self.types[ns][typeName].nodeId = row[1]
|
||||
break
|
||||
|
Loading…
Reference in New Issue
Block a user