Browse Source

Server: Read Datatype Definitions of Structures

matkonnerth 5 years ago
parent
commit
777eb9339d

+ 6 - 3
CMakeLists.txt

@@ -254,8 +254,8 @@ mark_as_advanced(UA_ENABLE_JSON_ENCODING)
 option(UA_ENABLE_STATUSCODE_DESCRIPTIONS "Enable conversion of StatusCode to human-readable error message" ON)
 option(UA_ENABLE_STATUSCODE_DESCRIPTIONS "Enable conversion of StatusCode to human-readable error message" ON)
 mark_as_advanced(UA_ENABLE_STATUSCODE_DESCRIPTIONS)
 mark_as_advanced(UA_ENABLE_STATUSCODE_DESCRIPTIONS)
 
 
-option(UA_ENABLE_TYPENAMES "Add the type and member names to the UA_DataType structure" ON)
-mark_as_advanced(UA_ENABLE_TYPENAMES)
+option(UA_ENABLE_TYPEDESCRIPTION "Add the type and member names to the UA_DataType structure" ON)
+mark_as_advanced(UA_ENABLE_TYPEDESCRIPTION)
 
 
 option(UA_ENABLE_NODESET_COMPILER_DESCRIPTIONS "Set node description attribute for nodeset compiler generated nodes" ON)
 option(UA_ENABLE_NODESET_COMPILER_DESCRIPTIONS "Set node description attribute for nodeset compiler generated nodes" ON)
 mark_as_advanced(UA_ENABLE_NODESET_COMPILER_DESCRIPTIONS)
 mark_as_advanced(UA_ENABLE_NODESET_COMPILER_DESCRIPTIONS)
@@ -335,7 +335,7 @@ option(UA_BUILD_FUZZING_CORPUS "Build the fuzzing corpus" OFF)
 mark_as_advanced(UA_BUILD_FUZZING_CORPUS)
 mark_as_advanced(UA_BUILD_FUZZING_CORPUS)
 if(UA_BUILD_FUZZING_CORPUS)
 if(UA_BUILD_FUZZING_CORPUS)
     add_definitions(-DUA_DEBUG_DUMP_PKGS_FILE)
     add_definitions(-DUA_DEBUG_DUMP_PKGS_FILE)
-    set(UA_ENABLE_TYPENAMES ON CACHE STRING "" FORCE)
+    set(UA_ENABLE_TYPEDESCRIPTION ON CACHE STRING "" FORCE)
     #set(UA_DEBUG_DUMP_PKGS ON CACHE STRING "" FORCE)
     #set(UA_DEBUG_DUMP_PKGS ON CACHE STRING "" FORCE)
 endif()
 endif()
 
 
@@ -843,6 +843,9 @@ else()
 	if(UA_ENABLE_DA)
 	if(UA_ENABLE_DA)
 		list(APPEND UA_FILE_DATATYPES ${PROJECT_SOURCE_DIR}/tools/schema/datatypes_dataaccess.txt)
 		list(APPEND UA_FILE_DATATYPES ${PROJECT_SOURCE_DIR}/tools/schema/datatypes_dataaccess.txt)
     endif()
     endif()
+    if(UA_ENABLE_TYPEDESCRIPTION)
+        list(APPEND UA_FILE_DATATYPES ${PROJECT_SOURCE_DIR}/tools/schema/datatypes_typedescription.txt)
+    endif()
 endif()
 endif()
 
 
 # standard-defined data types
 # standard-defined data types

+ 2 - 2
doc/building.rst

@@ -228,7 +228,7 @@ Detailed SDK Features
 Some options are marked as advanced. The advanced options need to be toggled to
 Some options are marked as advanced. The advanced options need to be toggled to
 be visible in the cmake GUIs.
 be visible in the cmake GUIs.
 
 
-**UA_ENABLE_TYPENAMES**
+**UA_ENABLE_TYPEDESCRIPTION**
    Add the type and member names to the UA_DataType structure. Enabled by default.
    Add the type and member names to the UA_DataType structure. Enabled by default.
 
 
 **UA_ENABLE_STATUSCODE_DESCRIPTIONS**
 **UA_ENABLE_STATUSCODE_DESCRIPTIONS**
@@ -283,7 +283,7 @@ communication.
 Last, logging messages take up a lot of space in the binary and might not be
 Last, logging messages take up a lot of space in the binary and might not be
 needed in embedded scenarios. Setting ``UA_LOGLEVEL`` to a value above 600
 needed in embedded scenarios. Setting ``UA_LOGLEVEL`` to a value above 600
 (``FATAL``) disables all logging. In addition, the feature-flags
 (``FATAL``) disables all logging. In addition, the feature-flags
-``UA_ENABLE_TYPENAMES`` and ``UA_ENABLE_STATUSCODE_DESCRIPTIONS`` add static
+``UA_ENABLE_TYPEDESCRIPTION`` and ``UA_ENABLE_STATUSCODE_DESCRIPTIONS`` add static
 information to the binary that is only used for human-readable logging and
 information to the binary that is only used for human-readable logging and
 debugging.
 debugging.
 
 

+ 3 - 3
examples/server_ctt.c

@@ -498,7 +498,7 @@ setInformationModel(UA_Server *server) {
 
 
         UA_VariableAttributes attr = UA_VariableAttributes_default;
         UA_VariableAttributes attr = UA_VariableAttributes_default;
         attr.dataType = UA_TYPES[type].typeId;
         attr.dataType = UA_TYPES[type].typeId;
-#ifndef UA_ENABLE_TYPENAMES
+#ifndef UA_ENABLE_TYPEDESCRIPTION
         char name[15];
         char name[15];
         UA_snprintf(name, 15, "%02d", type);
         UA_snprintf(name, 15, "%02d", type);
         attr.displayName = UA_LOCALIZEDTEXT("en-US", name);
         attr.displayName = UA_LOCALIZEDTEXT("en-US", name);
@@ -547,7 +547,7 @@ setInformationModel(UA_Server *server) {
                                   UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), qualifiedName,
                                   UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), qualifiedName,
                                   baseDataVariableType, attr, NULL, NULL);
                                   baseDataVariableType, attr, NULL, NULL);
         UA_Variant_clear(&attr.value);
         UA_Variant_clear(&attr.value);
-#ifdef UA_ENABLE_TYPENAMES
+#ifdef UA_ENABLE_TYPEDESCRIPTION
         UA_LocalizedText_clear(&attr.displayName);
         UA_LocalizedText_clear(&attr.displayName);
         UA_QualifiedName_clear(&qualifiedName);
         UA_QualifiedName_clear(&qualifiedName);
 #endif
 #endif
@@ -676,7 +676,7 @@ setInformationModel(UA_Server *server) {
 
 
         for(size_t j = 0; j < 100; j++) {
         for(size_t j = 0; j < 100; j++) {
             char name[32];
             char name[32];
-#ifndef UA_ENABLE_TYPENAMES
+#ifndef UA_ENABLE_TYPEDESCRIPTION
             UA_snprintf(name, 20, "%02d - %i", type, scale_i);
             UA_snprintf(name, 20, "%02d - %i", type, scale_i);
 #else
 #else
             UA_snprintf(name, 20, "%s - %i", UA_TYPES[type].typeName, scale_i);
             UA_snprintf(name, 20, "%s - %i", UA_TYPES[type].typeName, scale_i);

+ 7 - 0
examples/tutorial_client_events.c

@@ -8,6 +8,7 @@
 #include <open62541/plugin/log_stdout.h>
 #include <open62541/plugin/log_stdout.h>
 #include <open62541/server.h>
 #include <open62541/server.h>
 #include <open62541/server_config_default.h>
 #include <open62541/server_config_default.h>
+#include <open62541/util.h>
 
 
 #include <signal.h>
 #include <signal.h>
 
 
@@ -50,8 +51,14 @@ handler_events(UA_Client *client, UA_UInt32 subId, void *subContext,
                         "Message: '%.*s'", (int)lt->text.length, lt->text.data);
                         "Message: '%.*s'", (int)lt->text.length, lt->text.data);
         }
         }
         else {
         else {
+#ifdef UA_ENABLE_TYPEDESCRIPTION
             UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
             UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
                         "Don't know how to handle type: '%s'", eventFields[i].type->typeName);
                         "Don't know how to handle type: '%s'", eventFields[i].type->typeName);
+#else
+            UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
+                        "Don't know how to handle type, enable UA_ENABLE_TYPEDESCRIPTION "
+                        "for typename");
+#endif
         }
         }
     }
     }
 }
 }

+ 1 - 1
include/open62541/config.h.in

@@ -49,7 +49,7 @@
 /* Advanced Options */
 /* Advanced Options */
 #cmakedefine UA_ENABLE_CUSTOM_NODESTORE
 #cmakedefine UA_ENABLE_CUSTOM_NODESTORE
 #cmakedefine UA_ENABLE_STATUSCODE_DESCRIPTIONS
 #cmakedefine UA_ENABLE_STATUSCODE_DESCRIPTIONS
-#cmakedefine UA_ENABLE_TYPENAMES
+#cmakedefine UA_ENABLE_TYPEDESCRIPTION
 #cmakedefine UA_ENABLE_NODESET_COMPILER_DESCRIPTIONS
 #cmakedefine UA_ENABLE_NODESET_COMPILER_DESCRIPTIONS
 #cmakedefine UA_ENABLE_DETERMINISTIC_RNG
 #cmakedefine UA_ENABLE_DETERMINISTIC_RNG
 #cmakedefine UA_ENABLE_DISCOVERY
 #cmakedefine UA_ENABLE_DISCOVERY

+ 2 - 1
include/open62541/constants.h

@@ -50,7 +50,8 @@ typedef enum {
     UA_ATTRIBUTEID_MINIMUMSAMPLINGINTERVAL = 19,
     UA_ATTRIBUTEID_MINIMUMSAMPLINGINTERVAL = 19,
     UA_ATTRIBUTEID_HISTORIZING             = 20,
     UA_ATTRIBUTEID_HISTORIZING             = 20,
     UA_ATTRIBUTEID_EXECUTABLE              = 21,
     UA_ATTRIBUTEID_EXECUTABLE              = 21,
-    UA_ATTRIBUTEID_USEREXECUTABLE          = 22
+    UA_ATTRIBUTEID_USEREXECUTABLE          = 22,
+    UA_ATTRIBUTEID_DATATYPEDEFINITION      = 23
 } UA_AttributeId;
 } UA_AttributeId;
 
 
 /**
 /**

+ 3 - 3
include/open62541/types.h

@@ -796,7 +796,7 @@ typedef struct UA_DiagnosticInfo {
  * type operations as static inline functions. */
  * type operations as static inline functions. */
 
 
 typedef struct {
 typedef struct {
-#ifdef UA_ENABLE_TYPENAMES
+#ifdef UA_ENABLE_TYPEDESCRIPTION
     const char *memberName;
     const char *memberName;
 #endif
 #endif
     UA_UInt16 memberTypeIndex;    /* Index of the member in the array of data
     UA_UInt16 memberTypeIndex;    /* Index of the member in the array of data
@@ -852,7 +852,7 @@ typedef enum {
 } UA_DataTypeKind;
 } UA_DataTypeKind;
 
 
 struct UA_DataType {
 struct UA_DataType {
-#ifdef UA_ENABLE_TYPENAMES
+#ifdef UA_ENABLE_TYPEDESCRIPTION
     const char *typeName;
     const char *typeName;
 #endif
 #endif
     UA_NodeId typeId;                /* The nodeid of the type */
     UA_NodeId typeId;                /* The nodeid of the type */
@@ -992,7 +992,7 @@ UA_Guid UA_EXPORT UA_Guid_random(void);     /* no cryptographic entropy */
 
 
 /* The following is used to exclude type names in the definition of UA_DataType
 /* The following is used to exclude type names in the definition of UA_DataType
  * structures if the feature is disabled. */
  * structures if the feature is disabled. */
-#ifdef UA_ENABLE_TYPENAMES
+#ifdef UA_ENABLE_TYPEDESCRIPTION
 # define UA_TYPENAME(name) name,
 # define UA_TYPENAME(name) name,
 #else
 #else
 # define UA_TYPENAME(name)
 # define UA_TYPENAME(name)

+ 1 - 1
src/client/ua_client.c

@@ -303,7 +303,7 @@ processServiceResponse(void *application, UA_SecureChannel *channel,
         goto finish;
         goto finish;
     }
     }
 
 
-#ifdef UA_ENABLE_TYPENAMES
+#ifdef UA_ENABLE_TYPEDESCRIPTION
     UA_LOG_DEBUG(&rd->client->config.logger, UA_LOGCATEGORY_CLIENT,
     UA_LOG_DEBUG(&rd->client->config.logger, UA_LOGCATEGORY_CLIENT,
                  "Decode a message of type %s", rd->responseType->typeName);
                  "Decode a message of type %s", rd->responseType->typeName);
 #else
 #else

+ 2 - 2
src/server/ua_server_binary.c

@@ -480,7 +480,7 @@ processMSGDecoded(UA_Server *server, UA_SecureChannel *channel, UA_UInt32 reques
     UA_Session anonymousSession;
     UA_Session anonymousSession;
     if(!session) {
     if(!session) {
         if(sessionRequired) {
         if(sessionRequired) {
-#ifdef UA_ENABLE_TYPENAMES
+#ifdef UA_ENABLE_TYPEDESCRIPTION
             UA_LOG_WARNING_CHANNEL(&server->config.logger, channel,
             UA_LOG_WARNING_CHANNEL(&server->config.logger, channel,
                                    "%s refused without a valid session",
                                    "%s refused without a valid session",
                                    requestType->typeName);
                                    requestType->typeName);
@@ -503,7 +503,7 @@ processMSGDecoded(UA_Server *server, UA_SecureChannel *channel, UA_UInt32 reques
      * CloseSessionRequest */
      * CloseSessionRequest */
     if(sessionRequired && !session->activated &&
     if(sessionRequired && !session->activated &&
        requestType != &UA_TYPES[UA_TYPES_CLOSESESSIONREQUEST]) {
        requestType != &UA_TYPES[UA_TYPES_CLOSESESSIONREQUEST]) {
-#ifdef UA_ENABLE_TYPENAMES
+#ifdef UA_ENABLE_TYPEDESCRIPTION
         UA_LOG_WARNING_SESSION(&server->config.logger, session,
         UA_LOG_WARNING_SESSION(&server->config.logger, session,
                                "%s refused on a non-activated session",
                                "%s refused on a non-activated session",
                                requestType->typeName);
                                requestType->typeName);

+ 73 - 0
src/server/ua_services_attribute.c

@@ -189,6 +189,55 @@ static const UA_String jsonEncoding = {sizeof("Default JSON")-1, (UA_Byte*)"Defa
         break;                                                  \
         break;                                                  \
     }
     }
 
 
+#ifdef UA_ENABLE_TYPEDESCRIPTION
+static const UA_DataType *
+findDataType(const UA_Node *node, const UA_DataTypeArray *customTypes) {
+    for(size_t i = 0; i < UA_TYPES_COUNT; ++i) {
+        if(UA_NodeId_equal(&UA_TYPES[i].typeId, &node->nodeId)) {
+            return &UA_TYPES[i];
+        }
+    }
+
+    // lookup custom type
+    while(customTypes) {
+        for(size_t i = 0; i < customTypes->typesSize; ++i) {
+            if(UA_NodeId_equal(&customTypes->types[i].typeId, &node->nodeId))
+                return &customTypes->types[i];
+        }
+        customTypes = customTypes->next;
+    }
+    return NULL;
+}
+
+static UA_StatusCode
+getStructureDefinition(const UA_DataType *type, UA_StructureDefinition *def) {
+    def->baseDataType = UA_NODEID_NUMERIC(0, UA_NS0ID_STRUCTURE);
+    def->defaultEncodingId =
+        UA_NODEID_NUMERIC(type->typeId.namespaceIndex, type->binaryEncodingId);
+    def->structureType = UA_STRUCTURETYPE_STRUCTURE;
+    def->fieldsSize = type->membersSize;
+    def->fields =
+        (UA_StructureField *)UA_calloc(def->fieldsSize, sizeof(UA_StructureField));
+    if(!def->fields) {
+        return UA_STATUSCODE_BADOUTOFMEMORY;
+    }
+    const UA_DataType *typelists[2] = {UA_TYPES, &type[-type->typeIndex]};
+    for(size_t cnt = 0; cnt < def->fieldsSize; cnt++) {
+        const UA_DataTypeMember *m = &type->members[cnt];
+        def->fields[cnt].valueRank = UA_TRUE == m->isArray ? 1 : -1;
+        def->fields[cnt].arrayDimensions = NULL;
+        def->fields[cnt].arrayDimensionsSize = 0;
+        def->fields[cnt].name =
+            UA_STRING((char *)(uintptr_t)m->memberName);
+        def->fields[cnt].description.locale = UA_STRING_NULL;
+        def->fields[cnt].description.text = UA_STRING_NULL;
+        def->fields[cnt].dataType = typelists[!m->namespaceZero][m->memberTypeIndex].typeId;
+        def->fields[cnt].maxStringLength = 0;
+    }
+    return UA_STATUSCODE_GOOD;
+}
+#endif
+
 /* Returns a datavalue that may point into the node via the
 /* Returns a datavalue that may point into the node via the
  * UA_VARIANT_DATA_NODELETE tag. Don't access the returned DataValue once the
  * UA_VARIANT_DATA_NODELETE tag. Don't access the returned DataValue once the
  * node has been released! */
  * node has been released! */
@@ -340,6 +389,30 @@ ReadWithNode(const UA_Node *node, UA_Server *server, UA_Session *session,
         UA_Boolean userExecutable = getUserExecutable(server, session, (const UA_MethodNode*)node);
         UA_Boolean userExecutable = getUserExecutable(server, session, (const UA_MethodNode*)node);
         retval = UA_Variant_setScalarCopy(&v->value, &userExecutable, &UA_TYPES[UA_TYPES_BOOLEAN]);
         retval = UA_Variant_setScalarCopy(&v->value, &userExecutable, &UA_TYPES[UA_TYPES_BOOLEAN]);
         break; }
         break; }
+    case UA_ATTRIBUTEID_DATATYPEDEFINITION: {
+        CHECK_NODECLASS(UA_NODECLASS_DATATYPE);
+
+#ifdef UA_ENABLE_TYPEDESCRIPTION
+        const UA_DataType *type = findDataType(node, server->config.customDataTypes);
+        if(!type) {
+            retval = UA_STATUSCODE_BADATTRIBUTEIDINVALID;
+            break;
+        }
+
+        if(UA_DATATYPEKIND_STRUCTURE == type->typeKind ||
+           UA_DATATYPEKIND_OPTSTRUCT == type->typeKind) {
+            UA_StructureDefinition def;
+            retval = getStructureDefinition(type, &def);
+            if(UA_STATUSCODE_GOOD!=retval)
+                break;            
+            retval = UA_Variant_setScalarCopy(&v->value, &def,
+                                              &UA_TYPES[UA_TYPES_STRUCTUREDEFINITION]);
+            UA_free(def.fields);
+            break;
+        }
+#endif
+        retval = UA_STATUSCODE_BADATTRIBUTEIDINVALID;
+        break; }
     default:
     default:
         retval = UA_STATUSCODE_BADATTRIBUTEIDINVALID;
         retval = UA_STATUSCODE_BADATTRIBUTEIDINVALID;
     }
     }

+ 1 - 1
src/ua_types_encoding_json.c

@@ -3289,7 +3289,7 @@ decodeJsonInternal(void *dst, const UA_DataType *type,
 status UA_FUNC_ATTR_WARN_UNUSED_RESULT
 status UA_FUNC_ATTR_WARN_UNUSED_RESULT
 UA_decodeJson(const UA_ByteString *src, void *dst, const UA_DataType *type) {
 UA_decodeJson(const UA_ByteString *src, void *dst, const UA_DataType *type) {
     
     
-#ifndef UA_ENABLE_TYPENAMES
+#ifndef UA_ENABLE_TYPEDESCRIPTION
     return UA_STATUSCODE_BADNOTSUPPORTED;
     return UA_STATUSCODE_BADNOTSUPPORTED;
 #endif
 #endif
     
     

+ 29 - 0
tests/server/check_services_attributes.c

@@ -128,6 +128,14 @@ static void setup(void) {
                                    UA_QUALIFIEDNAME(0, "Viewtest"), view_attr, NULL, NULL);
                                    UA_QUALIFIEDNAME(0, "Viewtest"), view_attr, NULL, NULL);
     ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
     ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
 
 
+    /* DataTypeNode */
+    UA_DataTypeAttributes typeattr = UA_DataTypeAttributes_default;
+    typeattr.displayName = UA_LOCALIZEDTEXT("en-US", "TestDataType");
+    UA_Server_addDataTypeNode(server, UA_NODEID_NUMERIC(0, UA_NS0ID_ARGUMENT),
+                                  UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATATYPE),
+                                  UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE),
+                                  UA_QUALIFIEDNAME(0, "Argument"), typeattr, NULL, NULL);
+
 #ifdef UA_ENABLE_METHODCALLS
 #ifdef UA_ENABLE_METHODCALLS
     /* MethodNode */
     /* MethodNode */
     UA_MethodAttributes ma = UA_MethodAttributes_default;
     UA_MethodAttributes ma = UA_MethodAttributes_default;
@@ -585,6 +593,26 @@ START_TEST(ReadSingleDataSourceAttributeArrayDimensionsWithoutTimestamp) {
     UA_DataValue_deleteMembers(&resp);
     UA_DataValue_deleteMembers(&resp);
 } END_TEST
 } END_TEST
 
 
+START_TEST(ReadSingleAttributeDataTypeDefinitionWithoutTimestamp) {
+    UA_ReadValueId rvi;
+    UA_ReadValueId_init(&rvi);
+    rvi.nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ARGUMENT);
+    rvi.attributeId = UA_ATTRIBUTEID_DATATYPEDEFINITION;
+
+    UA_DataValue resp = UA_Server_read(server, &rvi, UA_TIMESTAMPSTORETURN_NEITHER);
+
+
+#ifdef UA_ENABLE_TYPEDESCRIPTION
+    ck_assert_int_eq(UA_STATUSCODE_GOOD, resp.status);
+    ck_assert_uint_eq(resp.value.type->typeIndex, UA_TYPES_STRUCTUREDEFINITION);
+    UA_StructureDefinition *def = (UA_StructureDefinition*)resp.value.data;
+    ck_assert_uint_eq(def->fieldsSize, 5);
+#else
+    ck_assert_int_eq(UA_STATUSCODE_BADATTRIBUTEIDINVALID, resp.status);
+#endif
+    UA_DataValue_deleteMembers(&resp);
+} END_TEST
+
 /* Tests for writeValue method */
 /* Tests for writeValue method */
 
 
 START_TEST(WriteSingleAttributeNodeId) {
 START_TEST(WriteSingleAttributeNodeId) {
@@ -927,6 +955,7 @@ static Suite * testSuite_services_attributes(void) {
     tcase_add_test(tc_readSingleAttributes, ReadSingleDataSourceAttributeValueEmptyWithoutTimestamp);
     tcase_add_test(tc_readSingleAttributes, ReadSingleDataSourceAttributeValueEmptyWithoutTimestamp);
     tcase_add_test(tc_readSingleAttributes, ReadSingleDataSourceAttributeDataTypeWithoutTimestamp);
     tcase_add_test(tc_readSingleAttributes, ReadSingleDataSourceAttributeDataTypeWithoutTimestamp);
     tcase_add_test(tc_readSingleAttributes, ReadSingleDataSourceAttributeArrayDimensionsWithoutTimestamp);
     tcase_add_test(tc_readSingleAttributes, ReadSingleDataSourceAttributeArrayDimensionsWithoutTimestamp);
+    tcase_add_test(tc_readSingleAttributes, ReadSingleAttributeDataTypeDefinitionWithoutTimestamp);
 
 
     suite_add_tcase(s, tc_readSingleAttributes);
     suite_add_tcase(s, tc_readSingleAttributes);
 
 

+ 3 - 0
tools/schema/datatypes_typedescription.txt

@@ -0,0 +1,3 @@
+StructureDefinition
+StructureField
+StructureType