Browse Source

generate type layout

Julius Pfrommer 10 years ago
parent
commit
7b5f3dac5e
3 changed files with 228 additions and 81 deletions
  1. 20 21
      include/ua_types.h
  2. 78 0
      src/server/ua_nodes.h
  3. 130 60
      tools/generate_datatypes.py

+ 20 - 21
include/ua_types.h

@@ -137,16 +137,18 @@ typedef UA_String UA_ByteString;
 /** @brief An XML element. */
 typedef UA_String UA_XmlElement;
 
+typedef enum {
+    UA_NODEIDTYPE_NUMERIC    = 2,
+    UA_NODEIDTYPE_STRING     = 3,
+    UA_NODEIDTYPE_GUID       = 4,
+    UA_NODEIDTYPE_BYTESTRING = 5
+} UA_NodeIdType;
+    
 /** @brief An identifier for a node in the address space of an OPC UA Server. */
 /* The shortened numeric types are introduced during encoding. */
 typedef struct {
     UA_UInt16 namespaceIndex;
-    enum {
-        UA_NODEIDTYPE_NUMERIC    = 2,
-        UA_NODEIDTYPE_STRING     = 3,
-        UA_NODEIDTYPE_GUID       = 4,
-        UA_NODEIDTYPE_BYTESTRING = 5
-    } identifierType;
+    UA_NodeIdType identifierType;
     union {
         UA_UInt32     numeric;
         UA_String     string;
@@ -408,30 +410,27 @@ void UA_EXPORT UA_Array_print(const void *p, UA_Int32 noElements, const UA_TypeV
 /* TypeDescription */
 /*******************/
 
-#define UA_MAX_MEMBERS 16 // Maximum number of members per complex type
+#define UA_MAX_MEMBERS 13 // Maximum number of members per complex type
 
-struct UA_DataTypeMember {
-    UA_UInt16 memberTypeIndex : 10; ///< Index of the member in the datatypelayout table
+typedef struct {
+    UA_UInt16 memberTypeIndex : 9; ///< Index of the member in the datatypelayout table
+    UA_Boolean nameSpaceZero : 1; ///< The type of the member is defined in namespace zero
     UA_Byte padding : 5; ///< How much padding is there before this member element?
-    UA_Boolean isArray : 1;
-};
+    UA_Boolean isArray : 1; ///< The member is an array if the given type
+} UA_DataTypeMember;
     
 typedef struct {
-    UA_UInt16 memSize; ///< Size of the struct in memory
-    UA_UInt16 binarySize : 14; ///< Size of the type in binary encoding. Including _all_ members with constantSize == true.
-    UA_Boolean constantSize : 1; ///< Does the type have constant size in memory? (no pointers, also not in members)
-    UA_Boolean binaryZeroCopy: 1; ///< Given an array of this type, can we just point into the binary stream? The boolean is a shortcut for (memSize == binarySize && constantSize).
-    UA_Boolean isBuiltin : 1; ///< The type is builtin. Use special functions if necessary. membersSize is 0, but the builtin-type index we have is encoded in memberDetails[0].memberTypeIndex.
-    struct UA_DataTypeMember memberDetails[UA_MAX_MEMBERS];
-    UA_Byte membersSize; ///< How many members does the struct have? (max. 32)
-    struct UA_DataTypeLayout *table; /**< Point to the beginning of the table where the members can be found with their indices  */
+    UA_UInt16 memSize : 15; ///< Size of the struct in memory
+    UA_Boolean zeroCopyable; ///< Can the type be copied directly off the stream?
+    UA_Byte membersSize; ///< How many members does the struct have?
+    UA_DataTypeMember members[UA_MAX_MEMBERS];
 } UA_DataTypeLayout;
 
 typedef struct {
     UA_UInt16 tableSize;
-    UA_DataTypeLayout *layouts;
+    UA_DataTypeLayout *typeLayouts;
     UA_NodeId *typeIds;
-    UA_String typeNames;
+    UA_String *typeNames;
 } UA_TypeDescriptionTable;
 
 /**********/

+ 78 - 0
src/server/ua_nodes.h

@@ -0,0 +1,78 @@
+#ifndef UA_NODES_H_
+#define UA_NODES_H_
+
+#include "ua_types_generated.h"
+
+#define UA_STANDARD_NODEMEMBERS                 \
+    UA_NodeId nodeId;                           \
+    UA_NodeClass nodeClass;                     \
+    UA_QualifiedName browseName;                \
+    UA_LocalizedText displayName;               \
+    UA_LocalizedText description;               \
+    UA_UInt32 writeMask;                        \
+    UA_UInt32 userWriteMask;                    \
+    UA_Int32 referencesSize;                    \
+    UA_ReferenceNode *references;
+
+typedef struct {
+    UA_STANDARD_NODEMEMBERS
+} UA_Node;
+
+typedef struct {
+    UA_STANDARD_NODEMEMBERS
+    UA_Byte eventNotifier;
+} UA_ObjectNode;
+
+typedef struct {
+    UA_STANDARD_NODEMEMBERS
+    UA_Boolean isAbstract;
+} UA_ObjectTypeNode;
+
+typedef struct {
+    UA_STANDARD_NODEMEMBERS
+    UA_Variant value;
+    UA_NodeId dataType;
+    UA_Int32 valueRank;
+    UA_Int32 arrayDimensionsSize;
+    UA_UInt32 *arrayDimensions;
+    UA_Byte accessLevel;
+    UA_Byte userAccessLevel;
+    UA_Double minimumSamplingInterval;
+    UA_Boolean historizing;
+} UA_VariableNode;
+
+typedef struct {
+    UA_STANDARD_NODEMEMBERS
+    UA_Variant value;
+    UA_NodeId dataType;
+    UA_Int32 valueRank;
+    UA_Int32 arrayDimensionsSize;
+    UA_UInt32 *arrayDimensions;
+    UA_Boolean isAbstract;
+} UA_VariableTypeNode;
+
+typedef struct {
+    UA_STANDARD_NODEMEMBERS
+    UA_Boolean isAbstract;
+    UA_Boolean symmetric;
+    UA_LocalizedText inverseName;
+} UA_ReferenceTypeNode;
+
+typedef struct {
+    UA_STANDARD_NODEMEMBERS
+    UA_Boolean executable;
+    UA_Boolean userExecutable;
+} UA_MethodNode;
+
+typedef struct {
+    UA_STANDARD_NODEMEMBERS
+    UA_Boolean containsNoLoops;
+    UA_Byte eventNotifier;
+} UA_ViewNode;
+
+typedef struct {
+    UA_STANDARD_NODEMEMBERS
+    UA_Boolean isAbstract;
+} UA_DataTypeNode;
+
+#endif /* UA_NODES_H_ */

+ 130 - 60
tools/generate_datatypes.py

@@ -18,7 +18,9 @@ builtin_types = ["UA_Boolean", "UA_Byte", "UA_Int16", "UA_UInt16", "UA_Int32", "
                  "UA_StatusCode", "UA_QualifiedName", "UA_LocalizedText", "UA_ExtensionObject",
                  "UA_Variant", "UA_DataValue", "UA_DiagnosticInfo"]
 
-excluded_types = ["UA_InstanceNode", "UA_TypeNode", "UA_ServerDiagnosticsSummaryDataType",
+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"]
 
@@ -33,6 +35,19 @@ class Type(object):
     def typelayout_c(self):
         pass
 
+class BuiltinType(Type):
+    def __init__(self, name, description = "", zeroCopyable = False):
+        self.name = name
+        self.description = description
+        self.zeroCopyable = zeroCopyable
+
+    def fixed_size(self):
+        return self.name in fixed_size
+
+    def typelayout_c(self):
+        return "{.memSize = sizeof(" + self.name + "), .zeroCopyable = " + \
+            ("UA_TRUE" if self.zeroCopyable else "UA_FALSE") + ", .membersSize = 0 }"
+
 class EnumerationType(Type):
     def __init__(self, name, description = "", elements = OrderedDict()):
         self.name = name
@@ -42,24 +57,30 @@ class EnumerationType(Type):
     def append_enum(name, value):
         self.elements[name] = value
 
+    def fixed_size(self):
+        return True
+
     def typedef_c(self):
         return "typedef enum { \n    " + \
             ",\n    ".join(map(lambda (key,value) : key.upper() + " = " + value,self.elements.iteritems())) + \
             "\n} " + self.name + ";"
 
-    def typelayout_c(self, tablename):
-        return "(UA_DataTypeLayout) {.memsize = sizeof(" + self.name "), .binarySize = 4," + \
-            ".constantSize = UA_TRUE, .binaryZeroCopy = UA_TRUE, isBuiltin = UA_FALSE," + \
-            ".memberDetails = " + \
-            ".membersSize = 0, .table = " + tablename + " }"
+    def typelayout_c(self):
+        return "{.memSize = sizeof(" + self.name + "), .zeroCopyable = UA_TRUE, " + \
+            ".membersSize = 1,\n\t.members[0] = {.memberTypeIndex = UA_INT32," + \
+            ".nameSpaceZero = UA_TRUE, .padding = 0, .isArray = UA_FALSE } }"
 
 class OpaqueType(Type):
+    def fixed_size(self):
+        return False
+
     def typedef_c(self):
         return "typedef UA_ByteString " + self.name + ";"
-    def typelayout_c(self, tablename):
-        return "(UA_DataTypeLayout) {.memsize = sizeof(" + self.name "), .binarySize = 4," + \
-            ".constantSize = UA_TRUE, .binaryZeroCopy = UA_TRUE, isBuiltin = UA_FALSE," + \
-            ".membersSize = 0, .table = " + tablename + " }"
+
+    def typelayout_c(self):
+        return "{.memSize = sizeof(" + self.name + "), .zeroCopyable = UA_FALSE, " + \
+            ".membersSize = 1,\n\t.members[0] = {.memberTypeIndex = UA_BYTE," + \
+            ".nameSpaceZero = UA_TRUE, .padding = offsetof(UA_String, data), .isArray = UA_TRUE } }"
 
 class StructMember(object):
     def __init__(self, name, memberType, isArray):
@@ -76,7 +97,7 @@ class StructType(Type):
     def fixed_size(self):
         if self.name in fixed_size:
             return True
-        for m in self.members:
+        for m in self.members.values():
             if m.isArray or not m.memberType.fixed_size():
                 return False
         return True
@@ -87,19 +108,43 @@ class StructType(Type):
         returnstr =  "typedef struct {\n"
         for name, member in self.members.iteritems():
             if member.isArray:
-                returnstr += "    UA_Int32 noOf" + name[0].upper() + name[1:] + ";\n"
-                returnstr += "    " + member.memberType + " *" +name + ";\n"
+                returnstr += "    UA_Int32 " + name + "Size;\n"
+                returnstr += "    " + member.memberType.name + " *" +name + ";\n"
             else:
-                returnstr += "    " + member.memberType + " " +name + ";\n"
+                returnstr += "    " + member.memberType.name + " " +name + ";\n"
         return returnstr + "} " + self.name + ";"
 
+    def typelayout_c(self):
+        layout = "{.memSize = sizeof(" + self.name + "), .zeroCopyable = " + \
+                 ("UA_TRUE, " if self.fixed_size() else "UA_FALSE, ") + \
+                 ".membersSize = " + str(len(self.members)) + ","
+        for index, member in enumerate(self.members.values()):
+            layout += "\n\t.members["+ str(index)+ "] = {" + \
+                      ".memberTypeIndex = UA_" + member.memberType.name.upper()[3:] + ", " + \
+                      ".nameSpaceZero = "+ \
+                      ("UA_TRUE, " if args.is_ns0 or member.memberType.name in builtin_types else "UA_FALSE, ") + \
+                      ".padding = "
+            if index == 0:
+                layout += "0, "
+            else:
+                before = self.members.values()[index-1]
+                layout += "offsetof(" + self.name + ", "+ member.name + ") - offsetof(" + self.name + ", " + before.name + ")"
+                if member.isArray:
+                    layout += " - sizeof(UA_Int32) "
+                if before.isArray:
+                    layout += " - sizeof(void *), "
+                else:
+                    layout += " - sizeof(" + before.memberType.name + "), "
+            layout += ".isArray = " + ("UA_TRUE" if member.isArray else "UA_FALSE") + " }, "
+        return layout + "}"
+
 def parseTypeDefinitions(xmlDescription, type_selection = None):
     '''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()
+    types = OrderedDict([(t, BuiltinType(t, "", t in fixed_size)) for t in builtin_types])
 
     # types we do not want to autogenerate
     def skipType(name):
@@ -169,9 +214,10 @@ def parseTypeDefinitions(xmlDescription, type_selection = None):
                 continue
             if child.get("Name") in lengthfields:
                 continue
-            memberType = "UA_" + stripTypename(child.get("TypeName"))
-            if not memberType in types and not memberType in builtin_types:
+            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)
@@ -199,36 +245,31 @@ def parseTypeDefinitions(xmlDescription, type_selection = None):
                     types[t.name] = t
     return types
 
-def main():
-    parser = argparse.ArgumentParser()
-    parser.add_argument('--only-needed', action='store_true', help='generate only types needed for compile')
-    parser.add_argument('xml', help='path/to/Opc.Ua.Types.bsd')
-    parser.add_argument('outfile', help='outfile w/o extension')
-
-    args = parser.parse_args()
-    outname = args.outfile.split("/")[-1] 
-    inname = args.xml.split("/")[-1]
-
-    fh = open(args.outfile + "_generated.h",'w')
-    def printh(string):
-        print(string, end='\n', file=fh)
-
-    # # whitelist for "only needed" profile
-    # from type_lists import only_needed_types
-
-    # # some types are omitted (pretend they exist already)
-    # existing_types.add("NodeIdType")
-
-    types = parseTypeDefinitions(args.xml)
-
-    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") + '''
- */
+parser = argparse.ArgumentParser()
+parser.add_argument('--is-ns0', action='store_true', help='the builtin-types are added to namespace 0')
+parser.add_argument('xml', help='path/to/Opc.Ua.Types.bsd')
+parser.add_argument('outfile', help='outfile w/o extension')
+
+args = parser.parse_args()
+outname = args.outfile.split("/")[-1] 
+inname = args.xml.split("/")[-1]
+types = OrderedDict(parseTypeDefinitions(args.xml).items())
+
+fh = open("ua_" + args.outfile + "_generated.h",'w')
+fc = open("ua_" + 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_
@@ -240,21 +281,29 @@ extern "C" {
 #include "ua_types.h"
 
 /**
- * @ingroup types
- *
- * @defgroup ''' + outname + '''_generated Autogenerated ''' + outname + ''' Types
- *
- * @brief Data structures that are autogenerated from an XML-Schema.
- * @{
- */''')
-
-    for t in types.itervalues():
+* @ingroup types
+*
+* @defgroup ''' + outname + '''_generated Autogenerated ''' + outname + ''' Types
+*
+* @brief Data structures that are autogenerated from an XML-Schema.
+*
+* @{
+*/
+
+extern const UA_DataTypeLayout *UA_''' + outname.upper() + ''';
+''')
+
+i = 0
+for t in types.itervalues():
+    if type(t) != BuiltinType:
         printh("")
         if t.description != "":
             printh("/** @brief " + t.description + "*/")
         printh(t.typedef_c())
+    printh("#define UA_" + t.name[3:].upper() + " " + str(i))
+    i += 1
 
-    printh('''
+printh('''
 /// @} /* end of group */
 
 #ifdef __cplusplus
@@ -263,7 +312,28 @@ extern "C" {
 
 #endif''')
 
-    fh.close()
+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") + '''
+*/
+
+#include "stddef.h"
+#include "ua_''' + outname + '''_generated.h"
+
+const UA_DataTypeLayout *UA_TYPES = (const UA_DataTypeLayout[]){''')
+
+for t in types.itervalues():
+    if type(t) == BuiltinType and not args.is_ns0:
+        continue
+    printc("")
+    printc("/* " + t.name + "*/")
+    printc(t.typelayout_c() + ",")
+
+printc("};")
 
-if __name__ == "__main__":
-    main()
+fh.close()
+fc.close()