open62541/tools/generate_datatypes.py
ichrispa f4629df441 Merge pull request #291 from acplt/service_call_implementation
commit efd4337064
Author: Julius Pfrommer <julius.pfrommer@web.de>
Date:   Tue Jul 7 18:41:40 2015 +0200

    checking for empty call requests and cosmetic improvements

commit e9efbb784d
Merge: 9c55fe6 60a46e6
Author: Julius Pfrommer <julius.pfrommer@web.de>
Date:   Tue Jul 7 17:13:22 2015 +0200

    Merge branch 'master' into service_call_implementation

commit 9c55fe640d
Author: Julius Pfrommer <julius.pfrommer@web.de>
Date:   Tue Jul 7 17:12:41 2015 +0200

    Squashed commit of the following:

    commit f2f5e97f913a2944c2ca0b699939c785769b1f8d
    Author: Julius Pfrommer <julius.pfrommer@web.de>
    Date:   Tue Jul 7 16:46:31 2015 +0200

        remove small memleaks during startup

    commit 23fc4a0c6b2e2cdfbfab8c71546030ff05554065
    Author: Julius Pfrommer <julius.pfrommer@web.de>
    Date:   Tue Jul 7 16:21:21 2015 +0200

        clean up the branch

    commit cb1cead03ca5c3cf3d26ec8ac5418d0c5f3921b9
    Merge: a3ad1e8 60a46e6
    Author: Julius Pfrommer <julius.pfrommer@web.de>
    Date:   Tue Jul 7 16:19:14 2015 +0200

        Merge branch 'master' into service_call_interface

        Conflicts:
        	tools/pyUANamespace/ua_node_types.py

    commit a3ad1e845b539a7bed64358b057a54fe7acb3e75
    Author: Julius Pfrommer <julius.pfrommer@web.de>
    Date:   Tue Jul 7 16:14:00 2015 +0200

        simplify methods API on the client side

    commit 60a46e6abf
    Merge: 232bb4e 028ed26
    Author: ichrispa <Chris_Paul.Iatrou@tu-dresden.de>
    Date:   Tue Jul 7 15:17:08 2015 +0200

        Merge branch 'master' of https://github.com/acplt/open62541

    commit 232bb4ecf7
    Author: ichrispa <Chris_Paul.Iatrou@tu-dresden.de>
    Date:   Tue Jul 7 15:16:43 2015 +0200

        Backported (parts of) 1b6331b6333a49286e273a49286e27 from service_call_implementation: Fix duplicate reference from parent to child being created by namespace generator.

    commit 028ed266a2
    Author: ChristianFimmers <christian.fimmers@rwth-aachen.de>
    Date:   Mon Jul 6 10:39:49 2015 +0200

        Merge pull request #290

    commit 6b2e0c6c36
    Author: Stasik0 <github@stasik.com>
    Date:   Mon Jul 6 10:39:49 2015 +0200

        one more space removed from the tag, relates to #289

    commit fd71ca7fa2
    Author: Stasik0 <github@stasik.com>
    Date:   Mon Jul 6 10:38:27 2015 +0200

        removing non-ascii literal, fixing amalgamation tag in case no git found, relates to #289

    commit 0b9b7095ca
    Author: Julius Pfrommer <julius.pfrommer@web.de>
    Date:   Fri Jul 3 17:54:43 2015 +0200

        no compiler flag required to compile amalgamated server

    commit 4d5ddc81f8
    Merge: 66caa20 6ff67cf
    Author: Sten Grüner <Stasik0@users.noreply.github.com>
    Date:   Thu Jul 2 09:10:57 2015 +0200

        Merge pull request #287 from acplt/simple_tcp

        simplfy closing connections in the network layer

    commit 6ff67cf40c
    Author: Julius Pfrommer <julius.pfrommer@web.de>
    Date:   Wed Jul 1 20:48:51 2015 +0200

        simplfy closing connections in the network layer

        sockets are only closed in the main loop. if a worker wants to close a connection, it is shutdown.
        this is then picked up by the select-call in the next main loop iteration.
        when a connection is closed, two jobs are returned. one to immediately detach the securechannel and a delayed job that eventually frees the connection memory.

    commit 66caa20e93
    Author: Stasik0 <github@stasik.com>
    Date:   Wed Jul 1 08:39:19 2015 +0200

        fixing client blocking on server disonnect

    commit 54cb2b4a7d
    Merge: 50512f4 435eaf9
    Author: Julius Pfrommer <jpfr@users.noreply.github.com>
    Date:   Tue Jun 30 21:45:42 2015 +0200

        Merge pull request #286 from acplt/externalNodestoreLeakFix

        fixed a memleak when using external namespaces

    commit 435eaf918d
    Author: LEvertz <l.evertz@plt.rwth-aachen.de>
    Date:   Tue Jun 30 17:04:59 2015 +0200

        added a function to delete external namespaces to the server --> fixes a
        memleak when using external namespaces

    commit 50512f48fe
    Author: Stasik0 <github@stasik.com>
    Date:   Mon Jun 29 09:55:52 2015 +0200

        added MSVC-builds to the readme

    commit a06f3ed9af
    Author: FlorianPalm <palm@plt.rwth-aachen.de>
    Date:   Sun Jun 28 23:28:56 2015 +0200

        fixed a typo in README

    commit cee54343c0
    Author: Julius Pfrommer <julius.pfrommer@web.de>
    Date:   Sun Jun 28 17:53:53 2015 +0200

        restore calcSizeBinary as a single function UA_calcSizeBinary

    commit 462b813509
    Author: Julius Pfrommer <julius.pfrommer@web.de>
    Date:   Sun Jun 28 15:25:31 2015 +0200

        replace redundant functions with ifdef, some cosmetic cleanup

    commit 1652c19b8b
    Author: Julius Pfrommer <jpfr@users.noreply.github.com>
    Date:   Sun Jun 28 11:06:24 2015 +0200

        wrap coveralls. the service fails regularly

    commit 88cc9a3da2
    Author: Julius Pfrommer <julius.pfrommer@web.de>
    Date:   Sun Jun 28 09:24:05 2015 +0200

        small documentation improvements

    commit 0f60259a7a
    Author: Julius Pfrommer <julius.pfrommer@web.de>
    Date:   Sat Jun 27 23:42:40 2015 +0200

        install sphinx theme

    commit c2f87ed825
    Author: Julius Pfrommer <julius.pfrommer@web.de>
    Date:   Sat Jun 27 23:26:26 2015 +0200

        add documentation generation with sphinx

    commit bc5151e65d
    Author: Julius Pfrommer <julius.pfrommer@web.de>
    Date:   Sat Jun 27 17:43:42 2015 +0200

        improve documentation, get rid of a duplicate function for string comparison

    commit 5caa997870
    Author: Julius Pfrommer <julius.pfrommer@web.de>
    Date:   Sat Jun 27 15:38:51 2015 +0200

        always copy data from the data source (removing the release function pointer)

    commit d0e09b24ff
    Author: Stasik0 <github@stasik.com>
    Date:   Sat Jun 27 15:32:48 2015 +0200

        removing enforcing NameSpaceId in BrowseName, relates to #284

    commit a8bbad7e0a
    Author: Stasik0 <github@stasik.com>
    Date:   Sat Jun 27 11:53:11 2015 +0200

        64bit support + artifacts

    commit d4c98b6c4e
    Author: Julius Pfrommer <julius.pfrommer@web.de>
    Date:   Sat Jun 27 13:14:37 2015 +0200

        fix client amalgamation

    commit 6b52e3c5dc
    Author: Julius Pfrommer <julius.pfrommer@web.de>
    Date:   Sat Jun 27 11:58:17 2015 +0200

        add a define for multithreading

    commit df4753b00d
    Author: Julius Pfrommer <julius.pfrommer@web.de>
    Date:   Fri Jun 26 21:48:49 2015 +0200

        improve amalgamation

    commit 2c869f59ab
    Author: Stasik0 <github@stasik.com>
    Date:   Fri Jun 26 11:41:58 2015 +0200

        appveyor ci support, fixes #280

    commit de5cb7d32b
    Author: FlorianPalm <palm@plt.rwth-aachen.de>
    Date:   Wed Jun 24 20:57:05 2015 +0200

        Update server.c

        removed instant test setup from server.c related to #265

    commit 1adbb44483
    Merge: 0632a83 494ac62
    Author: FlorianPalm <palm@plt.rwth-aachen.de>
    Date:   Wed Jun 24 18:00:18 2015 +0200

        Merge branch 'master' of https://github.com/acplt/open62541

    commit 0632a8363b
    Author: FlorianPalm <palm@plt.rwth-aachen.de>
    Date:   Wed Jun 24 17:59:02 2015 +0200

        related to #265, added another define as well as "faster" encoding functions for ARM7TDMI with "mixed" endian

    commit 494ac6234e
    Author: Stasik0 <github@stasik.com>
    Date:   Wed Jun 24 17:25:44 2015 +0200

        typo

    commit 263644e8fb
    Author: Stasik0 <github@stasik.com>
    Date:   Wed Jun 24 17:23:51 2015 +0200

        partial revert of CMake changes

    commit 6f95b1146d
    Author: Stasik0 <github@stasik.com>
    Date:   Wed Jun 24 17:18:25 2015 +0200

        fixing compile dependencies

    commit b3dc6cff71
    Author: Stasik0 <github@stasik.com>
    Date:   Wed Jun 24 16:57:44 2015 +0200

        masking out non-windows parts of the example - get rid of F_OK completely

    commit f235344418
    Author: Stasik0 <github@stasik.com>
    Date:   Wed Jun 24 16:42:29 2015 +0200

        adding dependencies in CMake

    commit b2277cfc2b
    Author: Sten Grüner <Stasik0@users.noreply.github.com>
    Date:   Wed Jun 24 16:10:07 2015 +0200

        F_OK is now direclty defined in server.c

commit 203ef35038
Author: Julius Pfrommer <julius.pfrommer@web.de>
Date:   Tue Jul 7 14:53:35 2015 +0200

    fix two memory bugs

commit 7791f6e455
Author: Julius Pfrommer <julius.pfrommer@web.de>
Date:   Thu Jul 2 12:07:36 2015 +0200

    remove private headers from the example server. simplify the callback signature

commit 5a623caab2
Author: ichrispa <Chris_Paul.Iatrou@tu-dresden.de>
Date:   Tue Jun 30 17:42:52 2015 +0200

    In/Out Variable nodes now get a random ID on creation (thank to @Stasik0 for the patch in master)

commit d853a34392
Merge: f7f9ed3 67840c9
Author: ichrispa <Chris_Paul.Iatrou@tu-dresden.de>
Date:   Tue Jun 30 17:39:17 2015 +0200

    Merge branch 'master' into service_call_implementation

    Conflicts:
    	examples/client.c
    	include/ua_client.h
    	src/client/ua_client.c
    	src/server/ua_server_binary.c

commit f7f9ed359f
Author: ichrispa <Chris_Paul.Iatrou@tu-dresden.de>
Date:   Tue Jun 30 17:33:41 2015 +0200

    Service_Call now checks whether Arguments encoded in the InputArguments variable of a method is an extensionObject or an Argument and picks the appropriate strategy (ExtensionObjects should ultimatively be removed once the namespace compilers find out how).

commit 3a5f33b153
Author: ichrispa <Chris_Paul.Iatrou@tu-dresden.de>
Date:   Tue Jun 30 17:12:23 2015 +0200

    Recreated UA_Server_addMethodNode for userspace operations. FIXME: Cannot be called, service_call needs to evade extensionObject decoding when arguments are not stored as extensionObjects.

commit 33dd54bf09
Author: ichrispa <Chris_Paul.Iatrou@tu-dresden.de>
Date:   Mon Jun 29 19:33:06 2015 +0200

    Renamed serviceCall headers into more appropriate form (still holding I/O data for the actual function call). Service call now checks the structure of passed arguments; return argument checking is still on the TODO list.

commit c93008c606
Author: ichrispa <Chris_Paul.Iatrou@tu-dresden.de>
Date:   Mon Jun 29 16:06:25 2015 +0200

    Python compiler can now compile flat extension objects (i.e. extObj's that don't contain other extObj's) directly from XML. This allows for any Arguments to be shown when calling methods. FIXME: There are preparations in the extObj printCode function that will allow hierarchic encodings of extObjs. Note also that identifying arrays inside extObj structs is not extensively tested.

commit f0fc16c569
Author: ichrispa <Chris_Paul.Iatrou@tu-dresden.de>
Date:   Thu Jun 25 21:03:39 2015 +0200

    Modifications in namespace compiler now allow basic scalar extensionObjects to be read and enconded from XML files. WARNING: ExtensionObjects containing ExtensionObjects and arrays > 1 element will propably fail to encode.

commit d78aab3cd2
Author: ichrispa <Chris_Paul.Iatrou@tu-dresden.de>
Date:   Wed Jun 24 18:18:25 2015 +0200

    Added methods base structure for testing argument conformance to the methods definition.

commit bdb0efe99b
Author: ichrispa <Chris_Paul.Iatrou@tu-dresden.de>
Date:   Wed Jun 24 17:51:30 2015 +0200

    Revert "Use UA_Argument for method calls. A memleak remains in the argument datasource."

    This reverts commit b1a2edcbdf.

commit b1a2edcbdf
Author: Julius Pfrommer <julius.pfrommer@web.de>
Date:   Tue Jun 23 22:42:08 2015 +0200

    Use UA_Argument for method calls. A memleak remains in the argument datasource.

commit 13148053bf
Merge: 256d2fb 68252e5
Author: Julius Pfrommer <julius.pfrommer@web.de>
Date:   Tue Jun 23 19:45:31 2015 +0200

    Merge branch 'master' into service_call_implementation

commit 256d2fbdd1
Merge: 7ae87bc 2b2a866
Author: Julius Pfrommer <julius.pfrommer@web.de>
Date:   Tue Jun 23 17:26:58 2015 +0200

    Merge branch 'master' into service_call_implementation

commit 7ae87bc3bd
Author: ichrispa <Chris_Paul.Iatrou@tu-dresden.de>
Date:   Fri Jun 19 10:14:46 2015 +0200

    Added missing #ifdef ENABLE_METHODCALLS in example server.

commit be08b2d01b
Merge: 728ae56 b515f7d
Author: ichrispa <Chris_Paul.Iatrou@tu-dresden.de>
Date:   Thu Jun 18 18:37:39 2015 +0200

    Merge branch 'master' into service_call_implementation

    Conflicts:
    	src/server/ua_server_binary.c

commit 728ae56546
Author: ichrispa <Chris_Paul.Iatrou@tu-dresden.de>
Date:   Thu Jun 18 18:31:51 2015 +0200

    Commented on UA_NodeAttachedMethod currently _not_ being a dynamic struct (even though _new() exists). This may change in case the current implementation is lacking.

commit e59ee567f2
Author: ichrispa <Chris_Paul.Iatrou@tu-dresden.de>
Date:   Thu Jun 18 17:33:17 2015 +0200

    Detaching methods from nodes now possible; attaching a method sets/unsets the executable bit

commit 2c02507427
Author: ichrispa <Chris_Paul.Iatrou@tu-dresden.de>
Date:   Thu Jun 18 17:29:53 2015 +0200

    Removed List from UA_NodeAttachedMethod; UA_MethodNode->attachedMethod is now a direct struct; Attaching methods now replaces node for Multithreading safety.

commit c4379a0a9a
Author: ichrispa <Chris_Paul.Iatrou@tu-dresden.de>
Date:   Thu Jun 18 14:14:03 2015 +0200

    Methods are no longer being decorated by a MethodCall Manager. Instead MethodType nodes hold a UA_NodeAttachedMethod struct directly as per @jpfr's and @Stasik0's request. NodeAttachedMethod is still necessary, as otherwise the const attribute of the UA_Node is not maintained.

commit 4eeb09e507
Author: ichrispa <Chris_Paul.Iatrou@tu-dresden.de>
Date:   Thu Jun 18 12:09:34 2015 +0200

    Slightly changed clientside UA_Client_CallMethod() function to return a statuscode (equal to callStatus also contained in outArgs), with outArgs now being set as a pointer-pointer.

commit 4b641ee683
Author: ichrispa <Chris_Paul.Iatrou@tu-dresden.de>
Date:   Thu Jun 18 11:22:03 2015 +0200

    Server/Client return call argument statuscodes to userspace; Separated function call return status from argument status in ArgumentList. Actually working client example.

commit 7f1e4d8a9b
Author: ichrispa <Chris_Paul.Iatrou@tu-dresden.de>
Date:   Thu Jun 18 10:47:51 2015 +0200

    Statuscodes and returned arguments are now being made available to the client in the outputargs struct.

commit c319932e99
Author: ichrispa <Chris_Paul.Iatrou@tu-dresden.de>
Date:   Thu Jun 18 10:25:11 2015 +0200

    Fixed several (really stupid) memory allocation errors on the serverside.

commit e6713751a1
Merge: 3d38228 a4cd3c5
Author: ichrispa <Chris_Paul.Iatrou@tu-dresden.de>
Date:   Wed Jun 17 18:58:04 2015 +0200

    Merge branch 'master' into service_call_implementation

    Conflicts:
    	tools/generate_datatypes.py

commit 3d38228995
Author: ichrispa <Chris_Paul.Iatrou@tu-dresden.de>
Date:   Wed Jun 17 18:54:30 2015 +0200

    Server now executes userspace function when the call service is invoked. Basic scaffolding for clientside support put in place.

commit bf40c249c3
Author: ichrispa <Chris_Paul.Iatrou@tu-dresden.de>
Date:   Tue Jun 16 16:16:56 2015 +0200

    Cumulative commit for enabling methodCalls service set (cmake -DENABLE_METHODCALLS); creating/remove method hooks is still WIP.
2015-07-08 08:19:39 +02:00

563 lines
25 KiB
Python

from __future__ import print_function
import sys
import time
import platform
import getpass
from collections import OrderedDict
import re
from lxml import etree
import itertools
import argparse
from pprint import pprint
fixed_size = {"UA_Boolean": 1, "UA_SByte": 1, "UA_Byte": 1, "UA_Int16": 2, "UA_UInt16": 2,
"UA_Int32": 4, "UA_UInt32": 4, "UA_Int64": 8, "UA_UInt64": 8, "UA_Float": 4,
"UA_Double": 8, "UA_DateTime": 8, "UA_Guid": 16, "UA_StatusCode": 4}
zero_copy = ["UA_Boolean", "UA_SByte", "UA_Byte", "UA_Int16", "UA_UInt16", "UA_Int32", "UA_UInt32",
"UA_Int64", "UA_UInt64", "UA_Float", "UA_Double", "UA_DateTime", "UA_StatusCode"]
# The order of the builtin-types is not as in the standard. We put all the
# fixed_size types in the front, so they can be distinguished by a simple geq
# comparison. That's ok, since we use the type-index only internally!!
builtin_types = ["UA_Boolean", "UA_SByte", "UA_Byte", "UA_Int16", "UA_UInt16",
"UA_Int32", "UA_UInt32", "UA_Int64", "UA_UInt64", "UA_Float",
"UA_Double", "UA_String", "UA_DateTime", "UA_Guid", "UA_ByteString",
"UA_XmlElement", "UA_NodeId", "UA_ExpandedNodeId", "UA_StatusCode",
"UA_QualifiedName", "UA_LocalizedText", "UA_ExtensionObject", "UA_DataValue",
"UA_Variant", "UA_DiagnosticInfo"]
excluded_types = ["UA_NodeIdType", "UA_InstanceNode", "UA_TypeNode", "UA_Node", "UA_ObjectNode",
"UA_ObjectTypeNode", "UA_VariableNode", "UA_VariableTypeNode", "UA_ReferenceTypeNode",
"UA_MethodNode", "UA_ViewNode", "UA_DataTypeNode", "UA_ServerDiagnosticsSummaryDataType",
"UA_SamplingIntervalDiagnosticsDataType", "UA_SessionSecurityDiagnosticsDataType",
"UA_SubscriptionDiagnosticsDataType", "UA_SessionDiagnosticsDataType"]
minimal_types = ["InvalidType", "Node", "NodeClass", "ReferenceNode", "ApplicationDescription", "ApplicationType",
"ChannelSecurityToken", "OpenSecureChannelRequest", "OpenSecureChannelResponse",
"CloseSecureChannelRequest", "CloseSecureChannelResponse", "RequestHeader", "ResponseHeader",
"SecurityTokenRequestType", "MessageSecurityMode", "CloseSessionResponse", "CloseSessionRquest",
"ActivateSessionRequest", "ActivateSessionResponse", "SignatureData", "SignedSoftwareCertificate",
"CreateSessionResponse", "CreateSessionRequest", "EndpointDescription", "UserTokenPolicy", "UserTokenType",
"GetEndpointsRequest", "GetEndpointsResponse", "PublishRequest", "PublishResponse", "FindServersRequest", "FindServersResponse",
"SetPublishingModeResponse", "SubscriptionAcknowledgement", "NotificationMessage", "ExtensionObject",
"Structure", "ReadRequest", "ReadResponse", "ReadValueId", "TimestampsToReturn", "WriteRequest",
"WriteResponse", "WriteValue", "SetPublishingModeRequest", "CreateMonitoredItemsResponse",
"MonitoredItemCreateResult", "CreateMonitoredItemsRequest", "MonitoredItemCreateRequest",
"MonitoringMode", "MonitoringParameters", "TranslateBrowsePathsToNodeIdsRequest",
"TranslateBrowsePathsToNodeIdsResponse", "BrowsePath", "BrowsePathResult", "RelativePath",
"BrowsePathTarget", "RelativePathElement", "CreateSubscriptionRequest", "CreateSubscriptionResponse",
"BrowseResponse", "BrowseResult", "ReferenceDescription", "BrowseRequest", "ViewDescription",
"BrowseNextRequest", "BrowseNextResponse",
"BrowseDescription", "BrowseDirection", "CloseSessionRequest", "AddNodesRequest", "AddNodesResponse",
"AddNodesItem", "AddNodesResult", "DeleteNodesItem","AddReferencesRequest", "AddReferencesResponse",
"AddReferencesItem","DeleteReferencesItem", "VariableNode", "MethodNode", "VariableTypeNode",
"ViewNode", "ReferenceTypeNode", "BrowseResultMask", "ServerState", "ServerStatusDataType", "BuildInfo",
"ObjectNode", "DataTypeNode", "ObjectTypeNode", "IdType", "VariableAttributes", "ObjectAttributes",
"NodeAttributes","ReferenceTypeAttributes", "ViewAttributes", "ObjectTypeAttributes",
"NodeAttributesMask","DeleteNodesItem", "DeleteNodesRequest", "DeleteNodesResponse",
"DeleteReferencesItem", "DeleteReferencesRequest", "DeleteReferencesResponse",
"RegisterNodesRequest", "RegisterNodesResponse", "UnregisterNodesRequest", "UnregisterNodesResponse",
"UserIdentityToken", "UserNameIdentityToken", "AnonymousIdentityToken", "ServiceFault",
"CallMethodRequest", "CallMethodResult", "CallResponse", "CallRequest", "Argument"]
class TypeDescription(object):
def __init__(self, name, nodeid, namespaceid):
self.name = name # without the UA_ prefix
self.nodeid = nodeid
self.namespaceid = namespaceid
def parseTypeDescriptions(filename, namespaceid):
definitions = {}
f = open(filename[0])
input_str = f.read()
f.close()
input_str = input_str.replace('\r','')
rows = map(lambda x:tuple(x.split(',')), input_str.split('\n'))
for index, row in enumerate(rows):
if len(row) < 3:
continue
if row[2] != "DataType":
continue
if row[0] == "BaseDataType":
definitions["UA_Variant"] = TypeDescription(row[0], row[1], namespaceid)
elif row[0] == "Structure":
definitions["UA_ExtensionObject"] = TypeDescription(row[0], row[1], namespaceid)
else:
definitions["UA_" + row[0]] = TypeDescription(row[0], row[1], namespaceid)
return definitions
class BuiltinType(object):
"Generic type without members. Used for builtin types."
def __init__(self, name, description = ""):
self.name = name
self.description = description
def fixed_size(self):
return self.name in fixed_size
def mem_size(self):
return fixed_size[self.name]
def zero_copy(self):
return self.name in zero_copy
def typedef_c(self):
pass
def typelayout_c(self, namespace_0, description, outname):
if description == None:
typeid = "{.namespaceIndex = 0, .identifierType = UA_NODEIDTYPE_NUMERIC, .identifier.numeric = 0}, "
else:
typeid = "{.namespaceIndex = %s, .identifierType = UA_NODEIDTYPE_NUMERIC, .identifier.numeric = %s}, " % \
(description.namespaceid, description.nodeid)
if self.name in ["UA_String", "UA_ByteString", "UA_XmlElement"]:
return "{.typeId = " + typeid + \
".memSize = sizeof(" + self.name + "), " + \
".namespaceZero = UA_TRUE, .fixedSize = UA_FALSE, .zeroCopyable = UA_FALSE, " + \
".membersSize = 1,\n\t.members = {{.memberTypeIndex = UA_TYPES_BYTE, .namespaceZero = UA_TRUE, " + \
".padding = offsetof(UA_String, data) - sizeof(UA_Int32), .isArray = UA_TRUE }}, " + \
".typeIndex = %s }" % (outname.upper() + "_" + self.name[3:].upper())
if self.name == "UA_QualifiedName":
return "{.typeId = " + typeid + \
".memSize = sizeof(UA_QualifiedName), " + \
".namespaceZero = UA_TRUE, .fixedSize = UA_FALSE, .zeroCopyable = UA_FALSE, " + \
".membersSize = 2, .members = {" + \
"\n\t{.memberTypeIndex = UA_TYPES_UINT16, .namespaceZero = UA_TRUE, " + \
".padding = 0, .isArray = UA_FALSE }," + \
"\n\t{.memberTypeIndex = UA_TYPES_STRING, .namespaceZero = UA_TRUE, " + \
".padding = offsetof(UA_QualifiedName, name) - sizeof(UA_UInt16), .isArray = UA_FALSE }},\n" + \
".typeIndex = UA_TYPES_QUALIFIEDNAME }"
return "{.typeId = " + typeid + \
".memSize = sizeof(" + self.name + "), " + \
".namespaceZero = UA_TRUE, " + \
".fixedSize = " + ("UA_TRUE" if self.fixed_size() else "UA_FALSE") + \
", .zeroCopyable = " + ("UA_TRUE" if self.zero_copy() else "UA_FALSE") + \
", .membersSize = 1,\n\t.members = {{.memberTypeIndex = UA_TYPES_" + self.name[3:].upper() + "," + \
".namespaceZero = UA_TRUE, .padding = 0, .isArray = UA_FALSE }}, " + \
".typeIndex = %s }" % (outname.upper() + "_" + self.name[3:].upper())
class EnumerationType(object):
def __init__(self, name, description = "", elements = OrderedDict()):
self.name = name
self.description = description
self.elements = elements # maps a name to an integer value
def append_enum(name, value):
self.elements[name] = value
def fixed_size(self):
return True
def mem_size(self):
return 4
def zero_copy(self):
return True
def typedef_c(self):
if sys.version_info[0] < 3:
values = self.elements.iteritems()
else:
values = self.elements.items()
return "typedef enum { \n " + \
",\n ".join(map(lambda kv : kv[0].upper() + " = " + kv[1], values)) + \
"\n} " + self.name + ";"
def typelayout_c(self, namespace_0, description, outname):
if description == None:
typeid = "{.namespaceIndex = 0, .identifierType = UA_NODEIDTYPE_NUMERIC, .identifier.numeric = 0}, "
else:
typeid = "{.namespaceIndex = %s, .identifierType = UA_NODEIDTYPE_NUMERIC, .identifier.numeric = %s}, " % (description.namespaceid, description.nodeid)
return "{.typeId = " + typeid + \
".memSize = sizeof(" + self.name + "), " +\
".namespaceZero = UA_TRUE, " + \
".fixedSize = UA_TRUE, .zeroCopyable = UA_TRUE, " + \
".membersSize = 1,\n\t.members = {{.memberTypeIndex = UA_TYPES_INT32," + \
".namespaceZero = UA_TRUE, .padding = 0, .isArray = UA_FALSE }}, .typeIndex = UA_TYPES_INT32 }"
def functions_c(self, typeTableName):
return '''#define %s_new (%s*)UA_Int32_new
#define %s_init(p) UA_Int32_init((UA_Int32*)p)
#define %s_delete(p) UA_Int32_delete((UA_Int32*)p)
#define %s_deleteMembers(p) UA_Int32_deleteMembers((UA_Int32*)p)
#define %s_copy(src, dst) UA_Int32_copy((const UA_Int32*)src, (UA_Int32*)dst)
#define %s_encodeBinary(src, dst, offset) UA_Int32_encodeBinary((UA_Int32*)src, dst, offset)
#define %s_decodeBinary(src, offset, dst) UA_Int32_decodeBinary(src, offset, (UA_Int32*)dst)''' % tuple(itertools.repeat(self.name, 8))
class OpaqueType(object):
def __init__(self, name, description = ""):
self.name = name
self.description = description
def fixed_size(self):
return False
def zero_copy(self):
return False
def typedef_c(self):
return "typedef UA_ByteString " + self.name + ";"
def typelayout_c(self, namespace_0, description, outname):
if description == None:
typeid = "{.namespaceIndex = 0, .identifierType = UA_NODEIDTYPE_NUMERIC, .identifier.numeric = 0}, "
else:
typeid = "{.namespaceIndex = %s, .identifierType = UA_NODEIDTYPE_NUMERIC, .identifier.numeric = %s}, " % (description.namespaceid, description.nodeid)
return "{.typeId = " + typeid + \
".memSize = sizeof(" + self.name + "), .fixedSize = UA_FALSE, .zeroCopyable = UA_FALSE, " + \
".namespaceZero = UA_TRUE, .membersSize = 1,\n\t.members = {{.memberTypeIndex = UA_TYPES_BYTESTRING," + \
".namespaceZero = UA_TRUE, .padding = 0, .isArray = UA_FALSE }}, .typeIndex = UA_TYPES_BYTESTRING }"
def functions_c(self, typeTableName):
return '''#define %s_new UA_ByteString_new
#define %s_init UA_ByteString_init
#define %s_delete UA_ByteString_delete
#define %s_deleteMembers UA_ByteString_deleteMembers
#define %s_copy UA_ByteString_copy
#define %s_encodeBinary UA_ByteString_encodeBinary
#define %s_decodeBinary UA_ByteString_decodeBinary''' % tuple(itertools.repeat(self.name, 8))
class StructMember(object):
def __init__(self, name, memberType, isArray):
self.name = name
self.memberType = memberType
self.isArray = isArray
class StructType(object):
def __init__(self, name, description, members = OrderedDict()):
self.name = name
self.description = description
self.members = members # maps a name to a member definition
def fixed_size(self):
for m in self.members.values():
if m.isArray or not m.memberType.fixed_size():
return False
return True
def mem_size(self):
total = 0
for m in self.members.values():
if m.isArray:
raise Exception("Arrays have no fixed size!")
else:
total += m.memberType.mem_size()
return total
def zero_copy(self):
for m in self.members.values():
if m.isArray or not m.memberType.zero_copy():
return False
return True
def typedef_c(self):
if len(self.members) == 0:
return "typedef void * " + self.name + ";"
returnstr = "typedef struct {\n"
if sys.version_info[0] < 3:
values = self.members.iteritems()
else:
values = self.members.items()
for name, member in values:
if member.isArray:
returnstr += " UA_Int32 " + name + "Size;\n"
returnstr += " " + member.memberType.name + " *" +name + ";\n"
else:
returnstr += " " + member.memberType.name + " " +name + ";\n"
return returnstr + "} " + self.name + ";"
def typelayout_c(self, namespace_0, description, outname):
if description == None:
typeid = "{.namespaceIndex = 0, .identifierType = UA_NODEIDTYPE_NUMERIC, .identifier.numeric = 0}, "
else:
typeid = "{.namespaceIndex = %s, .identifierType = UA_NODEIDTYPE_NUMERIC, .identifier.numeric = %s}, " % (description.namespaceid, description.nodeid)
layout = "{.typeId = "+ typeid + \
".memSize = sizeof(" + self.name + "), "+ \
".namespaceZero = " + ("UA_TRUE" if namespace_0 else "UA_FALSE") + \
", .fixedSize = " + ("UA_TRUE" if self.fixed_size() else "UA_FALSE") + \
", .zeroCopyable = " + ("sizeof(" + self.name + ") == " + str(self.mem_size()) if self.zero_copy() \
else "UA_FALSE") + \
", .typeIndex = " + outname.upper() + "_" + self.name[3:].upper() + \
", .membersSize = " + str(len(self.members)) + ","
if len(self.members) > 0:
layout += "\n\t.members={"
for index, member in enumerate(self.members.values()):
layout += "\n\t{" + \
".memberTypeIndex = " + ("UA_TYPES_" + member.memberType.name[3:].upper() if args.namespace_id == 0 or member.memberType.name in existing_types else \
outname.upper() + "_" + member.memberType.name[3:].upper()) + ", " + \
".namespaceZero = "+ \
("UA_TRUE, " if args.namespace_id == 0 or member.memberType.name in existing_types else "UA_FALSE, ") + \
".padding = "
before_endpos = "0"
thispos = "offsetof(%s, %s)" % (self.name, member.name)
if index > 0:
if sys.version_info[0] < 3:
before = self.members.values()[index-1]
else:
before = list(self.members.values())[index-1]
before_endpos = "(offsetof(%s, %s)" % (self.name, before.name)
if before.isArray:
before_endpos += " + sizeof(void*))"
else:
before_endpos += " + sizeof(%s))" % before.memberType.name
if member.isArray:
# the first two bytes are padding for the length index, the last three for the pointer
length_pos = "offsetof(%s, %sSize)" % (self.name, member.name)
if index != 0:
layout += "((%s - %s) << 3) + " % (length_pos, before_endpos)
layout += "(%s - sizeof(UA_Int32) - %s)" % (thispos, length_pos)
else:
layout += "%s - %s" % (thispos, before_endpos)
layout += ", .isArray = " + ("UA_TRUE" if member.isArray else "UA_FALSE") + " }, "
layout += "}"
return layout + "}"
def functions_c(self, typeTableName):
return '''#define %s_new() (%s*)UA_new(%s)
#define %s_init(p) UA_init(p, %s)
#define %s_delete(p) UA_delete(p, %s)
#define %s_deleteMembers(p) UA_deleteMembers(p, %s)
#define %s_copy(src, dst) UA_copy(src, dst, %s)
#define %s_encodeBinary(src, dst, offset) UA_encodeBinary(src, %s, dst, offset)
#define %s_decodeBinary(src, offset, dst) UA_decodeBinary(src, offset, dst, %s)''' % \
tuple([self.name] + list(itertools.chain(*itertools.repeat([self.name, "&"+typeTableName+"[" + typeTableName + "_" + self.name[3:].upper()+"]"], 7))))
def parseTypeDefinitions(xmlDescription, existing_types = OrderedDict()):
'''Returns an ordered dict that maps names to types. The order is such that
every type depends only on known types. '''
ns = {"opc": "http://opcfoundation.org/BinarySchema/"}
tree = etree.parse(xmlDescription)
typeSnippets = tree.xpath("/opc:TypeDictionary/*[not(self::opc:Import)]", namespaces=ns)
types = OrderedDict(existing_types.items())
# types we do not want to autogenerate
def skipType(name):
if name in builtin_types:
return True
if name in excluded_types:
return True
if outname == "ua_types" and not name[3:] in minimal_types:
return True
if "Test" in name: # skip all test types
return True
if re.search("NodeId$", name) != None:
return True
return False
def stripTypename(tn):
return tn[tn.find(":")+1:]
def camlCase2CCase(item):
"Member names begin with a lower case character"
return item[:1].lower() + item[1:] if item else ''
def typeReady(element):
"Do we have the member types yet?"
for child in element:
if child.tag == "{http://opcfoundation.org/BinarySchema/}Field":
if stripTypename(child.get("TypeName")) not in types:
return False
return True
def parseEnumeration(typeXml):
name = "UA_" + typeXml.get("Name")
description = ""
elements = OrderedDict()
for child in typeXml:
if child.tag == "{http://opcfoundation.org/BinarySchema/}Documentation":
description = child.text
if child.tag == "{http://opcfoundation.org/BinarySchema/}EnumeratedValue":
elements[name + "_" + child.get("Name")] = child.get("Value")
return EnumerationType(name, description, elements)
def parseOpaque(typeXml):
name = "UA_" + typeXml.get("Name")
description = ""
for child in typeXml:
if child.tag == "{http://opcfoundation.org/BinarySchema/}Documentation":
description = child.text
return OpaqueType(name, description)
def parseStructured(typeXml):
"Returns None if we miss member descriptions"
name = "UA_" + typeXml.get("Name")
description = ""
for child in typeXml:
if child.tag == "{http://opcfoundation.org/BinarySchema/}Documentation":
description = child.text
# ignore lengthfields, just tag the array-members as an array
lengthfields = []
for child in typeXml:
if child.get("LengthField"):
lengthfields.append(child.get("LengthField"))
members = OrderedDict()
for child in typeXml:
if not child.tag == "{http://opcfoundation.org/BinarySchema/}Field":
continue
if child.get("Name") in lengthfields:
continue
memberTypeName = "UA_" + stripTypename(child.get("TypeName"))
if not memberTypeName in types:
return None
memberType = types[memberTypeName]
memberName = camlCase2CCase(child.get("Name"))
isArray = True if child.get("LengthField") else False
members[memberName] = StructMember(memberName, memberType, isArray)
return StructType(name, description, members)
finished = False
while(not finished):
finished = True
for typeXml in typeSnippets:
name = "UA_" + typeXml.get("Name")
if name in types or skipType(name):
continue
if typeXml.tag == "{http://opcfoundation.org/BinarySchema/}EnumeratedType":
t = parseEnumeration(typeXml)
types[t.name] = t
elif typeXml.tag == "{http://opcfoundation.org/BinarySchema/}OpaqueType":
t = parseOpaque(typeXml)
types[t.name] = t
elif typeXml.tag == "{http://opcfoundation.org/BinarySchema/}StructuredType":
t = parseStructured(typeXml)
if t == None:
finished = False
else:
types[t.name] = t
# remove the existing types
for k in existing_types.keys():
types.pop(k)
return types
parser = argparse.ArgumentParser()
parser.add_argument('--ns0-types-xml', nargs=1, help='xml-definition of the ns0 types that are assumed to already exist')
parser.add_argument('--typedescriptions', nargs=1, help='csv file with type descriptions')
parser.add_argument('namespace_id', type=int, help='the id of the target namespace')
parser.add_argument('types_xml', help='path/to/Opc.Ua.Types.bsd')
parser.add_argument('outfile', help='output file w/o extension')
args = parser.parse_args()
outname = args.outfile.split("/")[-1]
inname = args.types_xml.split("/")[-1]
existing_types = OrderedDict()
if args.namespace_id == 0 or args.ns0_types_xml:
existing_types = OrderedDict([(t, BuiltinType(t)) for t in builtin_types])
if args.ns0_types_xml:
if sys.version_info[0] < 3:
OrderedDict(existing_types.items() + parseTypeDefinitions(args.ns0_types_xml[0], existing_types).items())
else:
OrderedDict(list(existing_types.items()) + list(parseTypeDefinitions(args.ns0_types_xml[0], existing_types).items()))
types = parseTypeDefinitions(args.types_xml, existing_types)
if args.namespace_id == 0:
if sys.version_info[0] < 3:
types = OrderedDict(existing_types.items() + types.items())
else:
types = OrderedDict(list(existing_types.items()) + list(types.items()))
typedescriptions = {}
if args.typedescriptions:
typedescriptions = parseTypeDescriptions(args.typedescriptions, args.namespace_id)
fh = open(args.outfile + "_generated.h",'w')
fc = open(args.outfile + "_generated.c",'w')
def printh(string):
print(string, end='\n', file=fh)
def printc(string):
print(string, end='\n', file=fc)
printh('''/**
* @file ''' + outname + '''_generated.h
*
* @brief Autogenerated data types
*
* Generated from ''' + inname + ''' with script ''' + sys.argv[0] + '''
* on host ''' + platform.uname()[1] + ''' by user ''' + getpass.getuser() + ''' at ''' + time.strftime("%Y-%m-%d %I:%M:%S") + '''
*/
#ifndef ''' + outname.upper() + '''_GENERATED_H_
#define ''' + outname.upper() + '''_GENERATED_H_
#ifdef __cplusplus
extern "C" {
#endif
#include "ua_types.h" '''
+ ('\n#include "ua_types_generated.h"\n' if args.namespace_id != 0 else '') + '''
/**
* @ingroup types
*
* @defgroup ''' + outname + '''_generated Autogenerated ''' + outname + ''' Types
*
* @brief Data structures that are autogenerated from an XML-Schema.
*
* @{
*/
''')
printh("#define " + outname.upper() + "_COUNT %s\n" % (str(len(types))))
printh("extern UA_EXPORT const UA_DataType *" + outname.upper() + ";\n")
i = 0
if sys.version_info[0] < 3:
values = types.itervalues()
else:
values = types.values()
for t in values:
if type(t) != BuiltinType:
printh("")
if t.description != "":
printh("/** @brief " + t.description + " */")
printh(t.typedef_c())
printh("#define " + outname.upper() + "_" + t.name[3:].upper() + " " + str(i))
if type(t) != BuiltinType:
printh(t.functions_c(outname.upper()))
i += 1
printh('''
/// @} /* end of group */\n
#ifdef __cplusplus
} // extern "C"
#endif\n
#endif /* %s_GENERATED_H_ */''' % outname.upper())
printc('''/**
* @file ''' + outname + '''_generated.c
*
* @brief Autogenerated data types
*
* Generated from ''' + inname + ''' with script ''' + sys.argv[0] + '''
* on host ''' + platform.uname()[1] + ''' by user ''' + getpass.getuser() + ''' at ''' + time.strftime("%Y-%m-%d %I:%M:%S") + '''
*/\n
#include "stddef.h"
#include "ua_types.h"
#include "''' + outname + '''_generated.h"\n
const UA_DataType *''' + outname.upper() + ''' = (UA_DataType[]){''')
if sys.version_info[0] < 3:
values = types.itervalues()
else:
values = types.values()
for t in values:
printc("")
printc("/* " + t.name + " */")
if args.typedescriptions:
td = typedescriptions[t.name]
else:
td = None
printc(t.typelayout_c(args.namespace_id == 0, td, outname) + ",")
printc("};\n")
# if args.typedescriptions:
# printc('const UA_UInt32 *' + outname.upper() + '_IDS = (UA_UInt32[]){')
# for t in types.itervalues():
# print(str(typedescriptions[t.name].nodeid) + ", ", end='', file=fc)
# printc("};")
fh.close()
fc.close()