Ver código fonte

generate type layout

Julius Pfrommer 10 anos atrás
pai
commit
7b5f3dac5e
3 arquivos alterados com 228 adições e 81 exclusões
  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. */
 /** @brief An XML element. */
 typedef UA_String UA_XmlElement;
 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. */
 /** @brief An identifier for a node in the address space of an OPC UA Server. */
 /* The shortened numeric types are introduced during encoding. */
 /* The shortened numeric types are introduced during encoding. */
 typedef struct {
 typedef struct {
     UA_UInt16 namespaceIndex;
     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 {
     union {
         UA_UInt32     numeric;
         UA_UInt32     numeric;
         UA_String     string;
         UA_String     string;
@@ -408,30 +410,27 @@ void UA_EXPORT UA_Array_print(const void *p, UA_Int32 noElements, const UA_TypeV
 /* TypeDescription */
 /* 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_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 {
 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;
 } UA_DataTypeLayout;
 
 
 typedef struct {
 typedef struct {
     UA_UInt16 tableSize;
     UA_UInt16 tableSize;
-    UA_DataTypeLayout *layouts;
+    UA_DataTypeLayout *typeLayouts;
     UA_NodeId *typeIds;
     UA_NodeId *typeIds;
-    UA_String typeNames;
+    UA_String *typeNames;
 } UA_TypeDescriptionTable;
 } 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_StatusCode", "UA_QualifiedName", "UA_LocalizedText", "UA_ExtensionObject",
                  "UA_Variant", "UA_DataValue", "UA_DiagnosticInfo"]
                  "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_SamplingIntervalDiagnosticsDataType", "UA_SessionSecurityDiagnosticsDataType",
                   "UA_SubscriptionDiagnosticsDataType", "UA_SessionDiagnosticsDataType"]
                   "UA_SubscriptionDiagnosticsDataType", "UA_SessionDiagnosticsDataType"]
 
 
@@ -33,6 +35,19 @@ class Type(object):
     def typelayout_c(self):
     def typelayout_c(self):
         pass
         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):
 class EnumerationType(Type):
     def __init__(self, name, description = "", elements = OrderedDict()):
     def __init__(self, name, description = "", elements = OrderedDict()):
         self.name = name
         self.name = name
@@ -42,24 +57,30 @@ class EnumerationType(Type):
     def append_enum(name, value):
     def append_enum(name, value):
         self.elements[name] = value
         self.elements[name] = value
 
 
+    def fixed_size(self):
+        return True
+
     def typedef_c(self):
     def typedef_c(self):
         return "typedef enum { \n    " + \
         return "typedef enum { \n    " + \
             ",\n    ".join(map(lambda (key,value) : key.upper() + " = " + value,self.elements.iteritems())) + \
             ",\n    ".join(map(lambda (key,value) : key.upper() + " = " + value,self.elements.iteritems())) + \
             "\n} " + self.name + ";"
             "\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):
 class OpaqueType(Type):
+    def fixed_size(self):
+        return False
+
     def typedef_c(self):
     def typedef_c(self):
         return "typedef UA_ByteString " + self.name + ";"
         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):
 class StructMember(object):
     def __init__(self, name, memberType, isArray):
     def __init__(self, name, memberType, isArray):
@@ -76,7 +97,7 @@ class StructType(Type):
     def fixed_size(self):
     def fixed_size(self):
         if self.name in fixed_size:
         if self.name in fixed_size:
             return True
             return True
-        for m in self.members:
+        for m in self.members.values():
             if m.isArray or not m.memberType.fixed_size():
             if m.isArray or not m.memberType.fixed_size():
                 return False
                 return False
         return True
         return True
@@ -87,19 +108,43 @@ class StructType(Type):
         returnstr =  "typedef struct {\n"
         returnstr =  "typedef struct {\n"
         for name, member in self.members.iteritems():
         for name, member in self.members.iteritems():
             if member.isArray:
             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:
             else:
-                returnstr += "    " + member.memberType + " " +name + ";\n"
+                returnstr += "    " + member.memberType.name + " " +name + ";\n"
         return returnstr + "} " + self.name + ";"
         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):
 def parseTypeDefinitions(xmlDescription, type_selection = None):
     '''Returns an ordered dict that maps names to types. The order is such that
     '''Returns an ordered dict that maps names to types. The order is such that
        every type depends only on known types. '''
        every type depends only on known types. '''
     ns = {"opc": "http://opcfoundation.org/BinarySchema/"}
     ns = {"opc": "http://opcfoundation.org/BinarySchema/"}
     tree = etree.parse(xmlDescription)
     tree = etree.parse(xmlDescription)
     typeSnippets = tree.xpath("/opc:TypeDictionary/*[not(self::opc:Import)]", namespaces=ns)
     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
     # types we do not want to autogenerate
     def skipType(name):
     def skipType(name):
@@ -169,9 +214,10 @@ def parseTypeDefinitions(xmlDescription, type_selection = None):
                 continue
                 continue
             if child.get("Name") in lengthfields:
             if child.get("Name") in lengthfields:
                 continue
                 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
                 return None
+            memberType = types[memberTypeName]
             memberName = camlCase2CCase(child.get("Name"))
             memberName = camlCase2CCase(child.get("Name"))
             isArray = True if child.get("LengthField") else False
             isArray = True if child.get("LengthField") else False
             members[memberName] = StructMember(memberName, memberType, isArray)
             members[memberName] = StructMember(memberName, memberType, isArray)
@@ -199,36 +245,31 @@ def parseTypeDefinitions(xmlDescription, type_selection = None):
                     types[t.name] = t
                     types[t.name] = t
     return types
     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_
 #ifndef ''' + outname.upper() + '''_GENERATED_H_
 #define ''' + outname.upper() + '''_GENERATED_H_
 #define ''' + outname.upper() + '''_GENERATED_H_
@@ -240,21 +281,29 @@ extern "C" {
 #include "ua_types.h"
 #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("")
         printh("")
         if t.description != "":
         if t.description != "":
             printh("/** @brief " + t.description + "*/")
             printh("/** @brief " + t.description + "*/")
         printh(t.typedef_c())
         printh(t.typedef_c())
+    printh("#define UA_" + t.name[3:].upper() + " " + str(i))
+    i += 1
 
 
-    printh('''
+printh('''
 /// @} /* end of group */
 /// @} /* end of group */
 
 
 #ifdef __cplusplus
 #ifdef __cplusplus
@@ -263,7 +312,28 @@ extern "C" {
 
 
 #endif''')
 #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()