Browse Source

type descriptions

Julius Pfrommer 10 years ago
parent
commit
b9bfc400d2

+ 3 - 0
examples/opcuaServer.c

@@ -19,6 +19,7 @@
 #include "networklayer_udp.h"
 #endif
 
+#include "ua_types.h"
 
 UA_Boolean running = 1;
 
@@ -28,6 +29,7 @@ static void stopHandler(int sign) {
 }
 
 static UA_ByteString loadCertificate(void) {
+
     UA_ByteString certificate = UA_STRING_NULL;
 	FILE *fp = NULL;
 	//FIXME: a potiential bug of locating the certificate, we need to get the path from the server's config
@@ -58,6 +60,7 @@ static void testCallback(UA_Server *server, void *data) {
 
 int main(int argc, char** argv) {
 	signal(SIGINT, stopHandler); /* catches ctrl-c */
+    printf("--- %lu\n", sizeof(UA_DataTypeLayout));
 
 	UA_Server *server = UA_Server_new();
     UA_Server_setServerCertificate(server, loadCertificate());

+ 41 - 22
include/ua_types.h

@@ -20,13 +20,11 @@
 extern "C" {
 #endif
 
-#include "ua_config.h"
-
 #include <stdint.h>
 #include <stdbool.h>
-#ifdef UA_DEBUG
 #include <stdio.h>
-#endif
+
+#include "ua_config.h"
 
 /**
  * @defgroup types Datatypes
@@ -282,21 +280,13 @@ typedef void UA_InvalidType;
 /* Functions */
 /*************/
 
-#ifdef UA_DEBUG
-#define PRINTTYPE(TYPE) void UA_EXPORT TYPE##_print(const TYPE *p, FILE *stream);
-#define PRINTTYPE_NOEXPORT(TYPE) void TYPE##_print(const TYPE *p, FILE *stream);
-#else
-#define PRINTTYPE(TYPE)
-#define PRINTTYPE_NOEXPORT(TYPE)
-#endif
-
 #define UA_TYPE_PROTOTYPES(TYPE)                                     \
     TYPE UA_EXPORT * TYPE##_new(void);                               \
     void UA_EXPORT TYPE##_init(TYPE * p);                            \
     void UA_EXPORT TYPE##_delete(TYPE * p);                          \
     void UA_EXPORT TYPE##_deleteMembers(TYPE * p);                   \
     UA_StatusCode UA_EXPORT TYPE##_copy(const TYPE *src, TYPE *dst); \
-    PRINTTYPE(TYPE)
+    void UA_EXPORT TYPE##_print(const TYPE *p, FILE *stream);
 
 #define UA_TYPE_PROTOTYPES_NOEXPORT(TYPE)                            \
     TYPE * TYPE##_new(void);                                         \
@@ -304,7 +294,7 @@ typedef void UA_InvalidType;
     void TYPE##_delete(TYPE * p);                                    \
     void TYPE##_deleteMembers(TYPE * p);                             \
     UA_StatusCode TYPE##_copy(const TYPE *src, TYPE *dst);           \
-    PRINTTYPE_NOEXPORT(TYPE)
+    void TYPE##_print(const TYPE *p, FILE *stream);
 
 /* Functions for all types */
 UA_TYPE_PROTOTYPES(UA_Boolean)
@@ -347,11 +337,9 @@ UA_TYPE_PROTOTYPES(UA_InvalidType)
 UA_StatusCode UA_EXPORT UA_String_copycstring(char const *src, UA_String *dst);
 UA_StatusCode UA_EXPORT UA_String_copyprintf(char const *fmt, UA_String *dst, ...);
 UA_Boolean UA_EXPORT UA_String_equal(const UA_String *string1, const UA_String *string2);
-#ifdef UA_DEBUG
 void UA_EXPORT UA_String_printf(char const *label, const UA_String *string);
 void UA_EXPORT UA_String_printx(char const *label, const UA_String *string);
 void UA_EXPORT UA_String_printx_hex(char const *label, const UA_String *string);
-#endif
 
 /* DateTime */
 UA_DateTime UA_EXPORT UA_DateTime_now(void);
@@ -377,11 +365,9 @@ UA_Guid UA_EXPORT UA_Guid_random(UA_UInt32 *seed);
 /* ByteString */
 UA_Boolean UA_EXPORT UA_ByteString_equal(const UA_ByteString *string1, const UA_ByteString *string2);
 UA_StatusCode UA_EXPORT UA_ByteString_newMembers(UA_ByteString *p, UA_Int32 length);
-#ifdef UA_DEBUG
 void UA_EXPORT UA_ByteString_printf(char *label, const UA_ByteString *string);
 void UA_EXPORT UA_ByteString_printx(char *label, const UA_ByteString *string);
 void UA_EXPORT UA_ByteString_printx_hex(char *label, const UA_ByteString *string);
-#endif
 
 /* NodeId */
 UA_Boolean UA_EXPORT UA_NodeId_equal(const UA_NodeId *n1, const UA_NodeId *n2);
@@ -395,9 +381,7 @@ UA_Boolean UA_EXPORT UA_ExpandedNodeId_isNull(const UA_ExpandedNodeId *p);
         VARIABLE.namespaceIndex = 0;                   \
         UA_STRING_STATIC(VARIABLE.name, STRING); } while(0)
 UA_StatusCode UA_EXPORT UA_QualifiedName_copycstring(char const *src, UA_QualifiedName *dst);
-#ifdef UA_DEBUG
 void UA_EXPORT UA_QualifiedName_printf(char const *label, const UA_QualifiedName *qn);
-#endif
 
 /* LocalizedText */
 #define UA_LOCALIZEDTEXT_STATIC(VARIABLE, STRING) do { \
@@ -417,9 +401,44 @@ void UA_EXPORT UA_Array_delete(void *p, UA_Int32 noElements, const UA_TypeVTable
 
 /* @brief The destination array is allocated with size noElements. */
 UA_StatusCode UA_EXPORT UA_Array_copy(const void *src, UA_Int32 noElements, const UA_TypeVTable *vt, void **dst);
-#ifdef UA_DEBUG
 void UA_EXPORT UA_Array_print(const void *p, UA_Int32 noElements, const UA_TypeVTable *vt, FILE *stream);
-#endif
+
+
+/*******************/
+/* TypeDescription */
+/*******************/
+
+#define UA_MAX_MEMBERS 16 // Maximum number of members per complex type
+
+/**
+ * The type descriptions points to the table in which the type is defined.
+ * That is necessary so that we can find the layouts of the member types.
+ *
+ * DataTypeTables are self-contained. Only the built-in types can be assumed.
+ * All the other types must be built up within the same table and cannot
+ * cross-reference. In the server, we store one datatypetable per namespace.
+ */
+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_UInt16 memberTypeIndex : 10; ///< Index of the member in the datatypelayout table
+        UA_Byte padding : 5; ///< How much padding is there before this member element?
+        UA_Boolean isArray : 1;
+    } 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_DataTypeLayout;
+
+typedef struct {
+    UA_UInt16 tableSize;
+    UA_DataTypeLayout *layouts;
+    UA_NodeId *typeIds;
+    UA_String typeNames;
+} UA_TypeDescriptionTable;
 
 /**********/
 /* VTable */

examples/opcuaServerMini.c → src/ongoing/opcuaServerMini.c


+ 1 - 1
src/server/ua_nodestore.c

@@ -164,7 +164,7 @@ static void deleteEntry(struct nodeEntry *entry) {
 }
 
 /** Copies the node into the entry. Then free the original node (but not its content). */
-static INLINE struct nodeEntry * nodeEntryFromNode(UA_Node *node) {
+static struct nodeEntry * nodeEntryFromNode(UA_Node *node) {
     UA_UInt32 nodesize = 0;
     
     switch(node->nodeClass) {

+ 2 - 2
src/server/ua_nodestore_hash.inc

@@ -1,7 +1,7 @@
 typedef UA_UInt32 hash_t;
 
-static INLINE hash_t mod(hash_t h, hash_t size) { return h % size; }
-static INLINE hash_t mod2(hash_t h, hash_t size) { return 1 + (h % (size - 2)); }
+static hash_t mod(hash_t h, hash_t size) { return h % size; }
+static hash_t mod2(hash_t h, hash_t size) { return 1 + (h % (size - 2)); }
 
 /* Based on Murmur-Hash 3 by Austin Appleby (public domain, freely usable) */
 static hash_t hash_array(const UA_Byte *data, UA_UInt32 len, UA_UInt32 seed) {

+ 1 - 8
src/ua_config.h.in

@@ -5,13 +5,6 @@
 #cmakedefine UA_DEBUG
 #cmakedefine UA_MULTITHREADING
 
-/* Visibility */
-#ifdef _MSC_VER
-#define INLINE __inline
-#else
-#define INLINE inline
-#endif
-
 /* Function Export */
 #ifdef _WIN32
 #  ifdef UA_DYNAMIC_LINKING
@@ -32,7 +25,7 @@
 #    endif
 #  endif
 #else
-#  if __GNUC__ >= 4 || __clang__
+#  if __GNUC__ || __clang__
 #    define UA_EXPORT __attribute__ ((visibility ("default")))
 #  else
 #    define UA_EXPORT

+ 8 - 72
src/ua_types.c

@@ -4,6 +4,7 @@
 #include <string.h> // strlen
 #define __USE_POSIX
 #include <stdlib.h> // malloc, free, rand
+#include <inttypes.h>
 
 #ifdef _WIN32
 #include <windows.h>
@@ -19,10 +20,6 @@
 #define RAND(SEED) (UA_UInt32)rand_r(SEED)
 #endif
 
-#ifdef UA_DEBUG
-#include <inttypes.h>
-#endif
-
 #include "ua_types.h"
 #include "ua_types_macros.h"
 #include "ua_types_encoding_binary.h"
@@ -38,104 +35,81 @@ UA_TYPE_DELETE_DEFAULT(UA_Boolean)
 UA_TYPE_DELETEMEMBERS_NOACTION(UA_Boolean)
 UA_TYPE_NEW_DEFAULT(UA_Boolean)
 UA_TYPE_COPY_DEFAULT(UA_Boolean)
-#ifdef UA_DEBUG
 void UA_Boolean_print(const UA_Boolean *p, FILE *stream) {
     if(*p) fprintf(stream, "UA_TRUE");
     else fprintf(stream, "UA_FALSE");
 }
-#endif
 
 /* SByte */
 UA_TYPE_DEFAULT(UA_SByte)
-#ifdef UA_DEBUG
 void UA_SByte_print(const UA_SByte *p, FILE *stream) {
     if(!p || !stream) return;
     UA_SByte x = *p;
     fprintf(stream, "%s%x\n", x < 0 ? "-" : "", x < 0 ? -x : x);
 }
-#endif
 
 /* Byte */
 UA_TYPE_DEFAULT(UA_Byte)
-#ifdef UA_DEBUG
 void UA_Byte_print(const UA_Byte *p, FILE *stream) {
     if(!p || !stream) return;
     fprintf(stream, "%x", *p);
 }
-#endif
 
 /* Int16 */
 UA_TYPE_DEFAULT(UA_Int16)
-#ifdef UA_DEBUG
 void UA_Int16_print(const UA_Int16 *p, FILE *stream) {
     if(!p || !stream) return;
     fprintf(stream, "%d", *p);
 }
-#endif
 
 /* UInt16 */
 UA_TYPE_DEFAULT(UA_UInt16)
-#ifdef UA_DEBUG
 void UA_UInt16_print(const UA_UInt16 *p, FILE *stream) {
     if(!p || !stream) return;
     fprintf(stream, "%u", *p);
 }
-#endif
 
 /* Int32 */
 UA_TYPE_DEFAULT(UA_Int32)
-#ifdef UA_DEBUG
 void UA_Int32_print(const UA_Int32 *p, FILE *stream) {
     if(!p || !stream) return;
     fprintf(stream, "%d", *p);
 }
 
-#endif
-
 /* UInt32 */
 UA_TYPE_DEFAULT(UA_UInt32)
-#ifdef UA_DEBUG
 void UA_UInt32_print(const UA_UInt32 *p, FILE *stream) {
     if(!p || !stream) return;
     fprintf(stream, "%u", *p);
 }
-#endif
 
 /* Int64 */
 UA_TYPE_DEFAULT(UA_Int64)
-#ifdef UA_DEBUG
 void UA_Int64_print(const UA_Int64 *p, FILE *stream) {
     if(!p || !stream) return;
-    fprintf(stream, "%" PRIi64, *p);
+    fprintf(stream, "%" PRId64, *p);
 }
-#endif
 
 /* UInt64 */
 UA_TYPE_DEFAULT(UA_UInt64)
-#ifdef UA_DEBUG
 void UA_UInt64_print(const UA_UInt64 *p, FILE *stream) {
     if(!p || !stream) return;
     fprintf(stream, "%" PRIu64, *p);
 }
-#endif
 
 /* Float */
 UA_TYPE_DEFAULT(UA_Float)
-#ifdef UA_DEBUG
 void UA_Float_print(const UA_Float *p, FILE *stream) {
     if(!p || !stream) return;
     fprintf(stream, "%f", *p);
 }
-#endif
 
 /* Double */
 UA_TYPE_DEFAULT(UA_Double)
-#ifdef UA_DEBUG
 void UA_Double_print(const UA_Double *p, FILE *stream) {
     if(!p || !stream) return;
     fprintf(stream, "%f", *p);
 }
-#endif
 
 /* String */
 UA_TYPE_NEW_DEFAULT(UA_String)
@@ -160,7 +134,6 @@ UA_StatusCode UA_String_copy(UA_String const *src, UA_String *dst) {
     return UA_STATUSCODE_GOOD;
 }
 
-#ifdef UA_DEBUG
 void UA_String_print(const UA_String *p, FILE *stream) {
     fprintf(stream, "(UA_String){%d,", p->length);
     if(p->data)
@@ -168,7 +141,6 @@ void UA_String_print(const UA_String *p, FILE *stream) {
     else
         fprintf(stream, "UA_NULL}");
 }
-#endif
 
 /* The c-string needs to be null-terminated. the string cannot be smaller than zero. */
 UA_Int32 UA_String_copycstring(char const *src, UA_String *dst) {
@@ -226,14 +198,11 @@ UA_Boolean UA_String_equal(const UA_String *string1, const UA_String *string2) {
     return (is == 0) ? UA_TRUE : UA_FALSE;
 }
 
-#ifdef UA_DEBUG
 void UA_String_printf(char const *label, const UA_String *string) {
     printf("%s {Length=%d, Data=%.*s}\n", label, string->length,
            string->length, (char *)string->data);
 }
-#endif
 
-#ifdef UA_DEBUG
 void UA_String_printx(char const *label, const UA_String *string) {
     printf("%s {Length=%d, Data=", label, string->length);
     if(string->length > 0) {
@@ -245,9 +214,7 @@ void UA_String_printx(char const *label, const UA_String *string) {
         printf("{");
     printf("}}\n");
 }
-#endif
 
-#ifdef UA_DEBUG
 void UA_String_printx_hex(char const *label, const UA_String *string) {
     printf("%s {Length=%d, Data=", label, string->length);
     if(string->length > 0) {
@@ -257,7 +224,6 @@ void UA_String_printx_hex(char const *label, const UA_String *string) {
         printf("{");
     printf("}}\n");
 }
-#endif
 
 /* DateTime */
 UA_TYPE_AS(UA_DateTime, UA_Int64)
@@ -364,12 +330,10 @@ UA_StatusCode UA_Guid_copy(UA_Guid const *src, UA_Guid *dst) {
     return UA_STATUSCODE_GOOD;
 }
 
-#ifdef UA_DEBUG
 void UA_Guid_print(const UA_Guid *p, FILE *stream) {
     fprintf(stream, "(UA_Guid){%u, %u %u {%x,%x,%x,%x,%x,%x,%x,%x}}", p->data1, p->data2, p->data3, p->data4[0],
             p->data4[1], p->data4[2], p->data4[3], p->data4[4], p->data4[5], p->data4[6], p->data4[7]);
 }
-#endif
 
 /* ByteString */
 UA_TYPE_AS(UA_ByteString, UA_String)
@@ -377,23 +341,17 @@ UA_Boolean UA_ByteString_equal(const UA_ByteString *string1, const UA_ByteString
     return UA_String_equal((const UA_String *)string1, (const UA_String *)string2);
 }
 
-#ifdef UA_DEBUG
 void UA_ByteString_printf(char *label, const UA_ByteString *string) {
     UA_String_printf(label, (const UA_String *)string);
 }
-#endif
 
-#ifdef UA_DEBUG
 void UA_ByteString_printx(char *label, const UA_ByteString *string) {
     UA_String_printx(label, (const UA_String *)string);
 }
-#endif
 
-#ifdef UA_DEBUG
 void UA_ByteString_printx_hex(char *label, const UA_ByteString *string) {
     UA_String_printx_hex(label, (const UA_String *)string);
 }
-#endif
 
 /** Creates a ByteString of the indicated length. The content is not set to zero. */
 UA_StatusCode UA_ByteString_newMembers(UA_ByteString *p, UA_Int32 length) {
@@ -478,7 +436,6 @@ void UA_NodeId_deleteMembers(UA_NodeId *p) {
     }
 }
 
-#ifdef UA_DEBUG
 void UA_NodeId_print(const UA_NodeId *p, FILE *stream) {
     fprintf(stream, "(UA_NodeId){");
     switch(p->identifierType) {
@@ -528,7 +485,6 @@ void UA_NodeId_print(const UA_NodeId *p, FILE *stream) {
     }
     fprintf(stream, "}");
 }
-#endif
 
 UA_Boolean UA_NodeId_equal(const UA_NodeId *n1, const UA_NodeId *n2) {
     if(n1->namespaceIndex != n2->namespaceIndex)
@@ -606,7 +562,6 @@ UA_StatusCode UA_ExpandedNodeId_copy(UA_ExpandedNodeId const *src, UA_ExpandedNo
     return retval;
 }
 
-#ifdef UA_DEBUG
 void UA_ExpandedNodeId_print(const UA_ExpandedNodeId *p, FILE *stream) {
     fprintf(stream, "(UA_ExpandedNodeId){");
     UA_NodeId_print(&p->nodeId, stream);
@@ -616,7 +571,6 @@ void UA_ExpandedNodeId_print(const UA_ExpandedNodeId *p, FILE *stream) {
     UA_UInt32_print(&p->serverIndex, stream);
     fprintf(stream, "}");
 }
-#endif
 
 UA_Boolean UA_ExpandedNodeId_isNull(const UA_ExpandedNodeId *p) {
     return UA_NodeId_isNull(&p->nodeId);
@@ -651,7 +605,6 @@ UA_StatusCode UA_QualifiedName_copycstring(char const *src, UA_QualifiedName *ds
     return UA_String_copycstring(src, &dst->name);
 }
 
-#ifdef UA_DEBUG
 void UA_QualifiedName_print(const UA_QualifiedName *p, FILE *stream) {
     fprintf(stream, "(UA_QualifiedName){");
     UA_UInt16_print(&p->namespaceIndex, stream);
@@ -659,14 +612,11 @@ void UA_QualifiedName_print(const UA_QualifiedName *p, FILE *stream) {
     UA_String_print(&p->name, stream);
     fprintf(stream, "}");
 }
-#endif
 
-#ifdef UA_DEBUG
-void UA_QualifiedName_printf(char const *label, const UA_QualifiedName *qn) {
-    printf("%s {NamespaceIndex=%u, Length=%d, Data=%.*s}\n", label, qn->namespaceIndex,
-           qn->name.length, qn->name.length, (char *)qn->name.data);
-}
-#endif
+/* void UA_QualifiedName_printf(char const *label, const UA_QualifiedName *qn) { */
+/*     printf("%s {NamespaceIndex=%u, Length=%d, Data=%.*s}\n", label, qn->namespaceIndex, */
+/*            qn->name.length, qn->name.length, (char *)qn->name.data); */
+/* } */
 
 /* LocalizedText */
 UA_TYPE_DELETE_DEFAULT(UA_LocalizedText)
@@ -699,7 +649,6 @@ UA_StatusCode UA_LocalizedText_copy(UA_LocalizedText const *src, UA_LocalizedTex
     return retval;
 }
 
-#ifdef UA_DEBUG
 void UA_LocalizedText_print(const UA_LocalizedText *p, FILE *stream) {
     fprintf(stream, "(UA_LocalizedText){");
     UA_String_print(&p->locale, stream);
@@ -707,7 +656,6 @@ void UA_LocalizedText_print(const UA_LocalizedText *p, FILE *stream) {
     UA_String_print(&p->text, stream);
     fprintf(stream, "}");
 }
-#endif
 
 /* ExtensionObject */
 UA_TYPE_DELETE_DEFAULT(UA_ExtensionObject)
@@ -732,7 +680,6 @@ UA_StatusCode UA_ExtensionObject_copy(UA_ExtensionObject const *src, UA_Extensio
     return retval;
 }
 
-#ifdef UA_DEBUG
 void UA_ExtensionObject_print(const UA_ExtensionObject *p, FILE *stream) {
     fprintf(stream, "(UA_ExtensionObject){");
     UA_NodeId_print(&p->typeId, stream);
@@ -745,7 +692,6 @@ void UA_ExtensionObject_print(const UA_ExtensionObject *p, FILE *stream) {
     UA_ByteString_print(&p->body, stream);
     fprintf(stream, "}");
 }
-#endif
 
 /* DataValue */
 UA_TYPE_DELETE_DEFAULT(UA_DataValue)
@@ -777,7 +723,6 @@ UA_StatusCode UA_DataValue_copy(UA_DataValue const *src, UA_DataValue *dst) {
     return retval;
 }
 
-#ifdef UA_DEBUG
 void UA_DataValue_print(const UA_DataValue *p, FILE *stream) {
     fprintf(stream, "(UA_DataValue){");
     UA_Byte_print(&p->encodingMask, stream);
@@ -795,7 +740,6 @@ void UA_DataValue_print(const UA_DataValue *p, FILE *stream) {
     UA_Int16_print(&p->serverPicoseconds, stream);
     fprintf(stream, "}");
 }
-#endif
 
 /* Variant */
 UA_TYPE_DELETE_DEFAULT(UA_Variant)
@@ -900,7 +844,6 @@ UA_StatusCode UA_Variant_copySetArray(UA_Variant *v, const UA_TypeVTable *vt, UA
     return retval;
 }
 
-#ifdef UA_DEBUG
 void UA_Variant_print(const UA_Variant *p, FILE *stream) {
     UA_UInt32 ns0id = UA_ns0ToVTableIndex(&p->vt->typeId);
     if(p->storageType == UA_VARIANT_DATASOURCE) {
@@ -922,7 +865,6 @@ void UA_Variant_print(const UA_Variant *p, FILE *stream) {
                    &UA_TYPES[UA_INT32], stream);
     fprintf(stream, "}");
 }
-#endif
 
 /* DiagnosticInfo */
 UA_TYPE_DELETE_DEFAULT(UA_DiagnosticInfo)
@@ -968,7 +910,6 @@ UA_StatusCode UA_DiagnosticInfo_copy(UA_DiagnosticInfo const *src, UA_Diagnostic
     return retval;
 }
 
-#ifdef UA_DEBUG
 void UA_DiagnosticInfo_print(const UA_DiagnosticInfo *p, FILE *stream) {
     fprintf(stream, "(UA_DiagnosticInfo){");
     UA_Byte_print(&p->encodingMask, stream);
@@ -992,7 +933,6 @@ void UA_DiagnosticInfo_print(const UA_DiagnosticInfo *p, FILE *stream) {
         fprintf(stream, "UA_NULL");
     fprintf(stream, "}");
 }
-#endif
 
 /* InvalidType */
 void UA_InvalidType_delete(UA_InvalidType *p) {
@@ -1015,11 +955,9 @@ UA_InvalidType * UA_InvalidType_new() {
     return UA_NULL;
 }
 
-#ifdef UA_DEBUG
 void UA_InvalidType_print(const UA_InvalidType *p, FILE *stream) {
-    fprintf(stream, "(UA_InvalidType){ERROR (invalid type)}");
+    fprintf(stream, "(UA_InvalidType){(invalid type)}");
 }
-#endif
 
 /*********/
 /* Array */
@@ -1090,15 +1028,13 @@ UA_StatusCode UA_Array_copy(const void *src, UA_Int32 noElements, const UA_TypeV
     return retval;
 }
 
-#ifdef UA_DEBUG
 void UA_Array_print(const void *p, UA_Int32 noElements, const UA_TypeVTable *vt, FILE *stream) {
     fprintf(stream, "(%s){", vt->name);
     const char *cp = (const char *)p; // so compilers allow pointer arithmetic
     UA_UInt32 memSize = vt->memSize;
     for(UA_Int32 i = 0;i < noElements;i++) {
-        vt->print(cp, stream);
+        // vt->print(cp, stream);
         fprintf(stream, ",");
         cp += memSize;
     }
 }
-#endif

+ 1 - 1
src/ua_types_encoding_binary.c

@@ -4,7 +4,7 @@
 #include "ua_namespace_0.h"
 #include "ua_statuscodes.h"
 
-static INLINE UA_Boolean is_builtin(const UA_NodeId *typeid ) {
+static UA_Boolean is_builtin(const UA_NodeId *typeid ) {
     return typeid ->namespaceIndex == 0 && 1 <= typeid ->identifier.numeric &&
         typeid ->identifier.numeric <= 25;
 }

+ 0 - 4
src/ua_types_macros.h

@@ -56,14 +56,10 @@
         return TYPE_AS##_copy((const TYPE_AS *)src, (TYPE_AS *)dst); \
     }
 
-#ifdef UA_DEBUG //print functions only in debug mode
 #define UA_TYPE_PRINT_AS(TYPE, TYPE_AS)                    \
     void TYPE##_print(const TYPE *p, FILE *stream) {       \
         TYPE_AS##_print((const TYPE_AS *)p, stream);       \
     }
-#else
-#define UA_TYPE_PRINT_AS(TYPE, TYPE_AS)
-#endif
 
 #define UA_TYPE_AS(TYPE, TYPE_AS)           \
     UA_TYPE_NEW_DEFAULT(TYPE)               \

+ 256 - 0
tools/generate_datatypes.py

@@ -0,0 +1,256 @@
+from __future__ import print_function
+import sys
+import time
+import platform
+import getpass
+from collections import OrderedDict
+import re
+from lxml import etree
+import argparse
+
+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}
+
+builtin_types = ["UA_Boolean", "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_Variant", "UA_DataValue", "UA_DiagnosticInfo"]
+
+excluded_types = ["UA_InstanceNode", "UA_TypeNode", "UA_ServerDiagnosticsSummaryDataType",
+                  "UA_SamplingIntervalDiagnosticsDataType", "UA_SessionSecurityDiagnosticsDataType",
+                  "UA_SubscriptionDiagnosticsDataType", "UA_SessionDiagnosticsDataType"]
+
+class Type(object):
+    def __init__(self, name, description = ""):
+        self.name = name
+        self.description = description
+
+    def string_c(self):
+        pass
+
+class EnumerationType(Type):
+    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 string_c(self):
+        return "typedef enum { \n    " + \
+            ",\n    ".join(map(lambda (key, value) : key.upper() + " = " + value, self.elements.iteritems())) + \
+            "\n} " + self.name + ";"
+
+class OpaqueType(Type):
+    def string_c(self):
+        return "typedef UA_ByteString " + self.name + ";"
+
+class StructMember(object):
+    def __init__(self, name, memberType, isArray):
+        self.name = name
+        self.memberType = memberType
+        self.isArray = isArray
+
+class StructType(Type):
+    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):
+        if self.name in fixed_size:
+            return True
+        for m in self.members:
+            if m.isArray or not m.memberType.fixed_size():
+                return False
+        return True
+
+    def string_c(self):
+        if len(self.members) == 0:
+            return "typedef void * " + self.name + ";"
+        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"
+            else:
+                returnstr += "    " + member.memberType + " " +name + ";\n"
+        return returnstr + "} " + self.name + ";"
+
+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 we do not want to autogenerate
+    def skipType(name):
+        if name in builtin_types:
+            return True
+        if name in excluded_types:
+            return True
+        if "Test" in name: # skip all test types
+            return True
+        if re.search("NodeId$", name) != None:
+            return True
+        if type_selection and not(name in type_selection):
+            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
+            memberType = "UA_" + stripTypename(child.get("TypeName"))
+            if not memberType in types and not memberType in builtin_types:
+                return None
+            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
+    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") + '''
+ */
+
+#ifndef ''' + outname.upper() + '''_GENERATED_H_
+#define ''' + outname.upper() + '''_GENERATED_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#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():
+        printh("")
+        if t.description != "":
+            printh("/** @brief " + t.description + "*/")
+        printh(t.string_c())
+
+    printh('''
+/// @} /* end of group */
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif''')
+
+    fh.close()
+
+if __name__ == "__main__":
+    main()

+ 70 - 0
tools/generate_typedescriptions.py

@@ -0,0 +1,70 @@
+from generate_datatypes import *
+
+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)
+
+    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") + '''
+ */
+
+#ifndef ''' + outname.upper() + '''_GENERATED_H_
+#define ''' + outname.upper() + '''_GENERATED_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "ua_types.h"
+
+/**
+ * @ingroup types
+ *
+ * @defgroup ''' + outname + '''_generated Autogenerated ''' + outname + ''' Types
+ *
+ * @brief Data structures that are autogenerated from an XML-Schema.
+ * @{
+ */''')
+
+    maxmem = 0
+    for t in types.itervalues():
+        printh("")
+        if type(t) == StructuredType:
+            if len(t.members) > maxmem:
+                maxmem = len(t.members)
+        if t.description != "":
+            printh("/** @brief " + t.description + "*/")
+        printh(t.string_c())
+
+    print(len(types))
+    print(maxmem)
+    printh('''
+/// @} /* end of group */
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif''')
+
+    fh.close()
+
+if __name__ == "__main__":
+    main()