mirror of
https://github.com/open62541/open62541.git
synced 2025-06-03 04:00:21 +00:00

commitefd4337064
Author: Julius Pfrommer <julius.pfrommer@web.de> Date: Tue Jul 7 18:41:40 2015 +0200 checking for empty call requests and cosmetic improvements commite9efbb784d
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 commit9c55fe640d
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: a3ad1e860a46e6
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 commit60a46e6abf
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 commit232bb4ecf7
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. commit028ed266a2
Author: ChristianFimmers <christian.fimmers@rwth-aachen.de> Date: Mon Jul 6 10:39:49 2015 +0200 Merge pull request #290 commit6b2e0c6c36
Author: Stasik0 <github@stasik.com> Date: Mon Jul 6 10:39:49 2015 +0200 one more space removed from the tag, relates to #289 commitfd71ca7fa2
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 commit0b9b7095ca
Author: Julius Pfrommer <julius.pfrommer@web.de> Date: Fri Jul 3 17:54:43 2015 +0200 no compiler flag required to compile amalgamated server commit4d5ddc81f8
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 commit6ff67cf40c
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. commit66caa20e93
Author: Stasik0 <github@stasik.com> Date: Wed Jul 1 08:39:19 2015 +0200 fixing client blocking on server disonnect commit54cb2b4a7d
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 commit435eaf918d
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 commit50512f48fe
Author: Stasik0 <github@stasik.com> Date: Mon Jun 29 09:55:52 2015 +0200 added MSVC-builds to the readme commita06f3ed9af
Author: FlorianPalm <palm@plt.rwth-aachen.de> Date: Sun Jun 28 23:28:56 2015 +0200 fixed a typo in README commitcee54343c0
Author: Julius Pfrommer <julius.pfrommer@web.de> Date: Sun Jun 28 17:53:53 2015 +0200 restore calcSizeBinary as a single function UA_calcSizeBinary commit462b813509
Author: Julius Pfrommer <julius.pfrommer@web.de> Date: Sun Jun 28 15:25:31 2015 +0200 replace redundant functions with ifdef, some cosmetic cleanup commit1652c19b8b
Author: Julius Pfrommer <jpfr@users.noreply.github.com> Date: Sun Jun 28 11:06:24 2015 +0200 wrap coveralls. the service fails regularly commit88cc9a3da2
Author: Julius Pfrommer <julius.pfrommer@web.de> Date: Sun Jun 28 09:24:05 2015 +0200 small documentation improvements commit0f60259a7a
Author: Julius Pfrommer <julius.pfrommer@web.de> Date: Sat Jun 27 23:42:40 2015 +0200 install sphinx theme commitc2f87ed825
Author: Julius Pfrommer <julius.pfrommer@web.de> Date: Sat Jun 27 23:26:26 2015 +0200 add documentation generation with sphinx commitbc5151e65d
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 commit5caa997870
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) commitd0e09b24ff
Author: Stasik0 <github@stasik.com> Date: Sat Jun 27 15:32:48 2015 +0200 removing enforcing NameSpaceId in BrowseName, relates to #284 commita8bbad7e0a
Author: Stasik0 <github@stasik.com> Date: Sat Jun 27 11:53:11 2015 +0200 64bit support + artifacts commitd4c98b6c4e
Author: Julius Pfrommer <julius.pfrommer@web.de> Date: Sat Jun 27 13:14:37 2015 +0200 fix client amalgamation commit6b52e3c5dc
Author: Julius Pfrommer <julius.pfrommer@web.de> Date: Sat Jun 27 11:58:17 2015 +0200 add a define for multithreading commitdf4753b00d
Author: Julius Pfrommer <julius.pfrommer@web.de> Date: Fri Jun 26 21:48:49 2015 +0200 improve amalgamation commit2c869f59ab
Author: Stasik0 <github@stasik.com> Date: Fri Jun 26 11:41:58 2015 +0200 appveyor ci support, fixes #280 commitde5cb7d32b
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 commit1adbb44483
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 commit0632a8363b
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 commit494ac6234e
Author: Stasik0 <github@stasik.com> Date: Wed Jun 24 17:25:44 2015 +0200 typo commit263644e8fb
Author: Stasik0 <github@stasik.com> Date: Wed Jun 24 17:23:51 2015 +0200 partial revert of CMake changes commit6f95b1146d
Author: Stasik0 <github@stasik.com> Date: Wed Jun 24 17:18:25 2015 +0200 fixing compile dependencies commitb3dc6cff71
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 commitf235344418
Author: Stasik0 <github@stasik.com> Date: Wed Jun 24 16:42:29 2015 +0200 adding dependencies in CMake commitb2277cfc2b
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 commit203ef35038
Author: Julius Pfrommer <julius.pfrommer@web.de> Date: Tue Jul 7 14:53:35 2015 +0200 fix two memory bugs commit7791f6e455
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 commit5a623caab2
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) commitd853a34392
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 commitf7f9ed359f
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). commit3a5f33b153
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. commit33dd54bf09
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. commitc93008c606
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. commitf0fc16c569
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. commitd78aab3cd2
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. commitbdb0efe99b
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 commitb1a2edcbdf
. commitb1a2edcbdf
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. commit13148053bf
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 commit256d2fbdd1
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 commit7ae87bc3bd
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. commitbe08b2d01b
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 commit728ae56546
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. commite59ee567f2
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 commit2c02507427
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. commitc4379a0a9a
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. commit4eeb09e507
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. commit4b641ee683
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. commit7f1e4d8a9b
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. commitc319932e99
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. commite6713751a1
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 commit3d38228995
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. commitbf40c249c3
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.
563 lines
25 KiB
Python
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()
|