Procházet zdrojové kódy

make the userspace attribute write function use the service. not the other
way around

Julius Pfrommer před 9 roky
rodič
revize
fe2996bdfd

+ 29 - 25
include/ua_server.h

@@ -317,35 +317,39 @@ typedef UA_StatusCode (*UA_NodeIteratorCallback)(UA_NodeId childId, UA_Boolean i
  */
  */
 UA_StatusCode UA_EXPORT UA_Server_forEachChildNodeCall(UA_Server *server, UA_NodeId parentNodeId, UA_NodeIteratorCallback callback, void *handle);
 UA_StatusCode UA_EXPORT UA_Server_forEachChildNodeCall(UA_Server *server, UA_NodeId parentNodeId, UA_NodeIteratorCallback callback, void *handle);
 
 
-UA_StatusCode UA_EXPORT UA_Server_setNodeAttribute(UA_Server *server, const UA_NodeId nodeId, const UA_AttributeId attributeId, const void *value);
+UA_StatusCode UA_EXPORT UA_Server_setNodeAttribute(UA_Server *server, const UA_NodeId nodeId, const UA_AttributeId attributeId, const UA_Variant value);
 
 
-#define UA_SERVER_SETNODEATTRIBUTE_DECL(ATTRIBUTE, ATTRIBUTEID, TYPE) \
+#define UA_SERVER_SETNODEATTRIBUTE_DECL(ATTRIBUTE, ATTRIBUTEID, TYPE, TYPEINDEX)	\
   static UA_INLINE UA_StatusCode UA_Server_setNodeAttribute_##ATTRIBUTE(UA_Server *server, const UA_NodeId nodeId, const TYPE value) { \
   static UA_INLINE UA_StatusCode UA_Server_setNodeAttribute_##ATTRIBUTE(UA_Server *server, const UA_NodeId nodeId, const TYPE value) { \
-	return UA_Server_setNodeAttribute(server, nodeId, ATTRIBUTEID, (const void *)&value); \
+	/* hack to cast to void*. but is treated as const internally. */	\
+	const UA_Variant var = {.type = &UA_TYPES[TYPEINDEX], .storageType = UA_VARIANT_DATA, \
+							.arrayLength = -1, .data = (void *)(uintptr_t)&value,	\
+							.arrayDimensionsSize = -1, .arrayDimensions = NULL};       \
+	return UA_Server_setNodeAttribute(server, nodeId, ATTRIBUTEID, var);                  \
   }
   }
 
 
-UA_SERVER_SETNODEATTRIBUTE_DECL(nodeId, UA_ATTRIBUTEID_NODEID, UA_NodeId)
-// UA_SERVER_SETNODEATTRIBUTE_DECL(nodeClass, UA_ATTRIBUTEID_NODECLASS, UA_NodeClass) // not supported
-UA_SERVER_SETNODEATTRIBUTE_DECL(browseName, UA_ATTRIBUTEID_BROWSENAME, UA_QualifiedName)
-UA_SERVER_SETNODEATTRIBUTE_DECL(displayName, UA_ATTRIBUTEID_DISPLAYNAME, UA_LocalizedText)
-UA_SERVER_SETNODEATTRIBUTE_DECL(description, UA_ATTRIBUTEID_DESCRIPTION, UA_LocalizedText)
-UA_SERVER_SETNODEATTRIBUTE_DECL(writeMask, UA_ATTRIBUTEID_WRITEMASK, UA_UInt32)
-UA_SERVER_SETNODEATTRIBUTE_DECL(userWriteMask, UA_ATTRIBUTEID_USERWRITEMASK, UA_UInt32)
-UA_SERVER_SETNODEATTRIBUTE_DECL(isAbstract, UA_ATTRIBUTEID_ISABSTRACT, UA_Boolean)
-UA_SERVER_SETNODEATTRIBUTE_DECL(symmetric, UA_ATTRIBUTEID_SYMMETRIC, UA_Boolean)
-UA_SERVER_SETNODEATTRIBUTE_DECL(inverseName, UA_ATTRIBUTEID_INVERSENAME, UA_LocalizedText)
-UA_SERVER_SETNODEATTRIBUTE_DECL(containsNoLoops, UA_ATTRIBUTEID_CONTAINSNOLOOPS, UA_Boolean)
-UA_SERVER_SETNODEATTRIBUTE_DECL(eventNotifier, UA_ATTRIBUTEID_EVENTNOTIFIER, UA_Byte)
-UA_SERVER_SETNODEATTRIBUTE_DECL(value, UA_ATTRIBUTEID_VALUE, UA_Variant)
-// UA_SERVER_SETNODEATTRIBUTE_DECL(dataType, UA_ATTRIBUTEID_DATATYPE, UA_NodeId) // not supported. set via the value variant.
-// UA_SERVER_SETNODEATTRIBUTE_DECL(valueRank, UA_ATTRIBUTEID_VALUERANK, UA_Int32) // not supported. set via the value variant.
-// UA_SERVER_SETNODEATTRIBUTE_DECL(arrayDimensions, UA_ATTRIBUTEID_ARRAYDIMENSIONS, UA_Int32) // not supported. set via the value variant.
-UA_SERVER_SETNODEATTRIBUTE_DECL(accessLevel, UA_ATTRIBUTEID_ACCESSLEVEL, UA_UInt32)
-UA_SERVER_SETNODEATTRIBUTE_DECL(userAccessLevel, UA_ATTRIBUTEID_USERACCESSLEVEL, UA_UInt32)
-UA_SERVER_SETNODEATTRIBUTE_DECL(minimumSamplingInterval, UA_ATTRIBUTEID_MINIMUMSAMPLINGINTERVAL, UA_Double)
-UA_SERVER_SETNODEATTRIBUTE_DECL(historizing, UA_ATTRIBUTEID_HISTORIZING, UA_Boolean)
-UA_SERVER_SETNODEATTRIBUTE_DECL(executable, UA_ATTRIBUTEID_EXECUTABLE, UA_Boolean)
-UA_SERVER_SETNODEATTRIBUTE_DECL(userExecutable, UA_ATTRIBUTEID_USEREXECUTABLE, UA_Boolean)
+UA_SERVER_SETNODEATTRIBUTE_DECL(nodeId, UA_ATTRIBUTEID_NODEID, UA_NodeId, UA_TYPES_NODEID)
+// UA_SERVER_SETNODEATTRIBUTE_DECL(nodeClass, UA_ATTRIBUTEID_NODECLASS, UA_NodeClass, UA_TYPES_NODECLASS) // not supported
+UA_SERVER_SETNODEATTRIBUTE_DECL(browseName, UA_ATTRIBUTEID_BROWSENAME, UA_QualifiedName, UA_TYPES_QUALIFIEDNAME)
+UA_SERVER_SETNODEATTRIBUTE_DECL(displayName, UA_ATTRIBUTEID_DISPLAYNAME, UA_LocalizedText, UA_TYPES_LOCALIZEDTEXT)
+UA_SERVER_SETNODEATTRIBUTE_DECL(description, UA_ATTRIBUTEID_DESCRIPTION, UA_LocalizedText, UA_TYPES_LOCALIZEDTEXT)
+UA_SERVER_SETNODEATTRIBUTE_DECL(writeMask, UA_ATTRIBUTEID_WRITEMASK, UA_UInt32, UA_TYPES_UINT32)
+UA_SERVER_SETNODEATTRIBUTE_DECL(userWriteMask, UA_ATTRIBUTEID_USERWRITEMASK, UA_UInt32, UA_TYPES_UINT32)
+UA_SERVER_SETNODEATTRIBUTE_DECL(isAbstract, UA_ATTRIBUTEID_ISABSTRACT, UA_Boolean, UA_TYPES_BOOLEAN)
+UA_SERVER_SETNODEATTRIBUTE_DECL(symmetric, UA_ATTRIBUTEID_SYMMETRIC, UA_Boolean, UA_TYPES_BOOLEAN)
+UA_SERVER_SETNODEATTRIBUTE_DECL(inverseName, UA_ATTRIBUTEID_INVERSENAME, UA_LocalizedText, UA_TYPES_LOCALIZEDTEXT)
+UA_SERVER_SETNODEATTRIBUTE_DECL(containsNoLoops, UA_ATTRIBUTEID_CONTAINSNOLOOPS, UA_Boolean, UA_TYPES_BOOLEAN)
+UA_SERVER_SETNODEATTRIBUTE_DECL(eventNotifier, UA_ATTRIBUTEID_EVENTNOTIFIER, UA_Byte, UA_TYPES_BYTE)
+#define UA_Server_setNodeAttribute_value(server, nodeId, value) UA_Server_setNodeAttribute(server, nodeId, UA_ATTRIBUTEID_VALUE, value)
+// UA_SERVER_SETNODEATTRIBUTE_DECL(dataType, UA_ATTRIBUTEID_DATATYPE, UA_NodeId, UA_TYPES_NODEID) // not supported. set via the value variant.
+// UA_SERVER_SETNODEATTRIBUTE_DECL(valueRank, UA_ATTRIBUTEID_VALUERANK, UA_Int32, UA_TYPES_INT32) // not supported. set via the value variant.
+// UA_SERVER_SETNODEATTRIBUTE_DECL(arrayDimensions, UA_ATTRIBUTEID_ARRAYDIMENSIONS, UA_Int32, UA_TYPES_INT32) // not supported. set via the value variant.
+UA_SERVER_SETNODEATTRIBUTE_DECL(accessLevel, UA_ATTRIBUTEID_ACCESSLEVEL, UA_UInt32, UA_TYPES_UINT32)
+UA_SERVER_SETNODEATTRIBUTE_DECL(userAccessLevel, UA_ATTRIBUTEID_USERACCESSLEVEL, UA_UInt32, UA_TYPES_UINT32)
+UA_SERVER_SETNODEATTRIBUTE_DECL(minimumSamplingInterval, UA_ATTRIBUTEID_MINIMUMSAMPLINGINTERVAL, UA_Double, UA_TYPES_DOUBLE)
+UA_SERVER_SETNODEATTRIBUTE_DECL(historizing, UA_ATTRIBUTEID_HISTORIZING, UA_Boolean, UA_TYPES_BOOLEAN)
+UA_SERVER_SETNODEATTRIBUTE_DECL(executable, UA_ATTRIBUTEID_EXECUTABLE, UA_Boolean, UA_TYPES_BOOLEAN)
+UA_SERVER_SETNODEATTRIBUTE_DECL(userExecutable, UA_ATTRIBUTEID_USEREXECUTABLE, UA_Boolean, UA_TYPES_BOOLEAN)
 
 
 #ifdef ENABLE_METHODCALLS
 #ifdef ENABLE_METHODCALLS
 UA_StatusCode UA_EXPORT
 UA_StatusCode UA_EXPORT

+ 10 - 148
src/server/ua_server_addressspace.c

@@ -1,4 +1,5 @@
 #include "ua_server.h"
 #include "ua_server.h"
+#include "ua_services.h"
 #include "ua_server_internal.h"
 #include "ua_server_internal.h"
 
 
 #define UA_SERVER_DELETENODEALIAS(TYPE) \
 #define UA_SERVER_DELETENODEALIAS(TYPE) \
@@ -910,154 +911,15 @@ if ((anyTypeNode.node->nodeClass & ( CLASS )) == 0) {                     \
   }                                                               \
   }                                                               \
 }\
 }\
     
     
-UA_StatusCode UA_Server_setNodeAttribute(UA_Server *server, const UA_NodeId nodeId, const UA_AttributeId attributeId, const void *value) {
-  UA_StatusCode retval = UA_STATUSCODE_GOOD;
-  
-  if (!value)
-    return UA_STATUSCODE_BADNOTHINGTODO;
-  
-  union {
-    UA_Node *node;
-    UA_ObjectNode *oObj;
-    UA_ObjectTypeNode *otObj;
-    UA_VariableNode *vObj;
-    UA_VariableTypeNode *vtObj;
-    UA_ReferenceTypeNode *rtObj;
-    UA_MethodNode *mObj;
-    UA_DataTypeNode *dtObj;
-    UA_ViewNode *vwObj;
-  } anyTypeNode;
-  retval = UA_Server_getNodeCopy(server, nodeId, (void **) &anyTypeNode.node);
-  if (retval)
-    return retval;
-  
-  switch(attributeId) {
-    case UA_ATTRIBUTEID_NODEID:
-      UA_Server_deleteNodeCopy(server, (void **) &anyTypeNode.node);
-      return UA_STATUSCODE_BADATTRIBUTEIDINVALID;
-      break;
-    case UA_ATTRIBUTEID_NODECLASS:
-      UA_Server_deleteNodeCopy(server, (void **) &anyTypeNode.node);
-      return UA_STATUSCODE_BADATTRIBUTEIDINVALID;
-      break;
-    case UA_ATTRIBUTEID_BROWSENAME:
-      UA_QualifiedName_deleteMembers(&anyTypeNode.node->browseName);
-      UA_QualifiedName_copy((const UA_QualifiedName *) value, &anyTypeNode.node->browseName);
-      break;
-    case UA_ATTRIBUTEID_DISPLAYNAME:
-      UA_LocalizedText_deleteMembers(&anyTypeNode.node->displayName);
-      UA_LocalizedText_copy((const UA_LocalizedText *) value, &anyTypeNode.node->displayName);
-      break;
-    case UA_ATTRIBUTEID_DESCRIPTION:
-      UA_LocalizedText_deleteMembers(&anyTypeNode.node->description);
-      UA_LocalizedText_copy((const UA_LocalizedText *) value, &anyTypeNode.node->description);
-      break;
-    case UA_ATTRIBUTEID_WRITEMASK:
-      anyTypeNode.node->writeMask = *(const UA_UInt32*) value;
-      break;
-    case UA_ATTRIBUTEID_USERWRITEMASK:
-      anyTypeNode.node->userWriteMask = *(const UA_UInt32*) value;
-      break;    
-    case UA_ATTRIBUTEID_ISABSTRACT:
-      SETATTRIBUTE_ASSERTNODECLASS(UA_NODECLASS_OBJECTTYPE | UA_NODECLASS_REFERENCETYPE | UA_NODECLASS_VARIABLETYPE | UA_NODECLASS_DATATYPE)
-      switch(anyTypeNode.node->nodeClass) {
-        case UA_NODECLASS_OBJECTTYPE:
-        case UA_NODECLASS_REFERENCETYPE:
-        case UA_NODECLASS_VARIABLETYPE:
-        case UA_NODECLASS_DATATYPE:
-          anyTypeNode.otObj->isAbstract = *(const UA_Boolean *) value;
-          break;
-        default:
-          UA_Server_deleteNodeCopy(server, (void **) &anyTypeNode.node);
-          return UA_STATUSCODE_BADATTRIBUTEIDINVALID;
-          break;
-      }
-      break;
-    case UA_ATTRIBUTEID_SYMMETRIC:
-      SETATTRIBUTE_ASSERTNODECLASS(UA_NODECLASS_REFERENCETYPE)
-      anyTypeNode.rtObj->symmetric = *(const UA_Boolean *) value;
-      break;
-    case UA_ATTRIBUTEID_INVERSENAME:
-      SETATTRIBUTE_ASSERTNODECLASS(UA_NODECLASS_REFERENCETYPE)
-      UA_LocalizedText_deleteMembers(&anyTypeNode.rtObj->inverseName);
-      UA_LocalizedText_copy((const UA_LocalizedText *) value, &anyTypeNode.rtObj->inverseName);
-      break;
-    case UA_ATTRIBUTEID_CONTAINSNOLOOPS:
-      SETATTRIBUTE_ASSERTNODECLASS(UA_NODECLASS_VIEW)
-      anyTypeNode.vwObj->containsNoLoops = *(const UA_Boolean *) value;
-      break;
-    case UA_ATTRIBUTEID_EVENTNOTIFIER:
-      SETATTRIBUTE_ASSERTNODECLASS(UA_NODECLASS_VIEW | UA_NODECLASS_OBJECT)
-      switch(anyTypeNode.node->nodeClass) {
-        case UA_NODECLASS_VIEW:
-        case UA_NODECLASS_OBJECT:
-          anyTypeNode.vwObj->eventNotifier = *(const UA_Byte *) value;
-          break;
-        default:
-          UA_Server_deleteNodeCopy(server, (void **) &anyTypeNode.node);
-          return UA_STATUSCODE_BADATTRIBUTEIDINVALID;
-          break;
-      }
-      break;
-    case UA_ATTRIBUTEID_VALUE:
-      SETATTRIBUTE_ASSERTNODECLASS(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE)
-      if(anyTypeNode.vObj->valueSource == UA_VALUESOURCE_DATASOURCE) {
-        UA_Server_deleteNodeCopy(server, (void **) &anyTypeNode.node);
-        return UA_STATUSCODE_BADATTRIBUTEIDINVALID;
-      }
-      UA_Variant_deleteMembers(&anyTypeNode.vObj->value.variant);
-      UA_Variant_copy((const UA_Variant*)value, &anyTypeNode.vObj->value.variant);
-      break;
-    case UA_ATTRIBUTEID_DATATYPE:
-      UA_Server_deleteNodeCopy(server, (void **) &anyTypeNode.node);
-      return UA_STATUSCODE_BADATTRIBUTEIDINVALID;
-      break;
-    case UA_ATTRIBUTEID_VALUERANK:
-      UA_Server_deleteNodeCopy(server, (void **) &anyTypeNode.node);
-      return UA_STATUSCODE_BADATTRIBUTEIDINVALID;
-      break;
-    case UA_ATTRIBUTEID_ARRAYDIMENSIONS:
-      UA_Server_deleteNodeCopy(server, (void **) &anyTypeNode.node);
-      return UA_STATUSCODE_BADATTRIBUTEIDINVALID;
-      break;
-    case UA_ATTRIBUTEID_ACCESSLEVEL:
-      SETATTRIBUTE_ASSERTNODECLASS(UA_NODECLASS_VARIABLE)
-      anyTypeNode.vObj->accessLevel = *(const UA_Byte*) value;
-      break;
-    case UA_ATTRIBUTEID_USERACCESSLEVEL:
-      SETATTRIBUTE_ASSERTNODECLASS(UA_NODECLASS_VARIABLE)
-      anyTypeNode.vObj->userAccessLevel = *(const UA_Byte*) value;
-      break;
-    case UA_ATTRIBUTEID_MINIMUMSAMPLINGINTERVAL:
-      SETATTRIBUTE_ASSERTNODECLASS(UA_NODECLASS_VARIABLE)
-      anyTypeNode.vObj->minimumSamplingInterval = *(const UA_Double *) value;
-      break;
-    case UA_ATTRIBUTEID_HISTORIZING:
-      SETATTRIBUTE_ASSERTNODECLASS(UA_NODECLASS_VARIABLE)
-      anyTypeNode.vObj->historizing = *(const UA_Boolean *) value;
-      break;
-    case UA_ATTRIBUTEID_EXECUTABLE:
-      SETATTRIBUTE_ASSERTNODECLASS(UA_NODECLASS_METHOD)
-      anyTypeNode.mObj->executable = *(const UA_Boolean *) value;
-      break;
-    case UA_ATTRIBUTEID_USEREXECUTABLE:
-      SETATTRIBUTE_ASSERTNODECLASS(UA_NODECLASS_METHOD)
-      anyTypeNode.mObj->userExecutable = *(const UA_Boolean *) value;
-      break;
-    default:
-      UA_Server_deleteNodeCopy(server, (void **) &anyTypeNode.node);
-      return UA_STATUSCODE_BADATTRIBUTEIDINVALID;
-      break;
-  }
-  
-  const UA_Node *oldNode = UA_NodeStore_get(server->nodestore, &nodeId);
-  const UA_Node **inserted = UA_NULL;
-  retval |= UA_NodeStore_replace(server->nodestore, oldNode, anyTypeNode.node, inserted);
-  UA_NodeStore_release(oldNode);
-  
-  // Node Copy deleted by nodeEntryFromNode() during nodestore_replace()!
-  //UA_Server_deleteNodeCopy(server, (void **) &anyTypeNode.node);
-  return retval;
+UA_StatusCode UA_Server_setNodeAttribute(UA_Server *server, const UA_NodeId nodeId,
+                                         const UA_AttributeId attributeId, const UA_Variant value) {
+    UA_WriteValue wvalue;
+    UA_WriteValue_init(&wvalue);
+    wvalue.nodeId = nodeId;
+    wvalue.attributeId = attributeId;
+    wvalue.value.value = value;
+    wvalue.value.hasValue = UA_TRUE;
+    return UA_Service_Write_single(server, &adminSession, &wvalue);
 }
 }
 
 
 #ifdef ENABLE_METHODCALLS
 #ifdef ENABLE_METHODCALLS

+ 3 - 5
src/server/ua_services.h

@@ -218,10 +218,8 @@ void readValue(UA_Server *server, UA_TimestampsToReturn timestamps,
 void Service_Write(UA_Server *server, UA_Session *session, const UA_WriteRequest *request,
 void Service_Write(UA_Server *server, UA_Session *session, const UA_WriteRequest *request,
                    UA_WriteResponse *response);
                    UA_WriteResponse *response);
 
 
-/* Mock-Up of the function signature for Unit Tests */
-#ifdef BUILD_UNIT_TESTS
-UA_StatusCode writeValue(UA_Server *server, UA_WriteValue *wvalue);
-#endif
+/** Single attribute writes are exposed to the userspace */
+UA_StatusCode UA_Service_Write_single(UA_Server *server, UA_Session *session, const UA_WriteValue *wvalue);
 
 
 // Service_HistoryUpdate
 // Service_HistoryUpdate
 /** @} */
 /** @} */
@@ -315,4 +313,4 @@ void Service_Call(UA_Server *server, UA_Session *session,
 /** @} */
 /** @} */
 #endif
 #endif
 #endif /* UA_SERVICES_H_ */
 #endif /* UA_SERVICES_H_ */
-/** @} */
+/** @} */

+ 189 - 207
src/server/ua_services_attribute.c

@@ -424,217 +424,199 @@ void Service_Read(UA_Server *server, UA_Session *session, const UA_ReadRequest *
 #endif
 #endif
 }
 }
 
 
-#define SETATTRIBUTE_IF_DATATYPE_IS(EXP_DT)                                                                             \
-/* The Userspace setAttribute expects the value being passed being the correct type and has no own means for checking   \
-   We need to check for setAttribute if the dataType is correct                                                         \
-*/                                                                                                                      \
-expectType = UA_NODEID_NUMERIC(0, UA_NS0ID_##EXP_DT);                                                                   \
-if (! UA_NodeId_equal(&expectType, &wvalue->value.value.type->typeId))                                                  \
-  retval |= UA_STATUSCODE_BADTYPEMISMATCH;                                                                              \
-else                                                                                                                    \
-  retval = UA_Server_setNodeAttribute(server, wvalue->nodeId, wvalue->attributeId, (void *) wvalue->value.value.data); \
-
-#ifndef BUILD_UNIT_TESTS
-static
-#endif
-UA_StatusCode writeValue(UA_Server *server, UA_WriteValue *wvalue) {
-    UA_StatusCode retval = UA_STATUSCODE_GOOD;
-
-    /* is there a value at all */
-    if(!wvalue->value.hasValue)
-        return UA_STATUSCODE_BADTYPEMISMATCH;
-
-    // we might repeat writing, e.g. when the node got replaced mid-work
-    UA_Boolean done = UA_FALSE;
-    UA_NodeId expectType;
-    while(!done) {
-        const UA_Node *node = UA_NodeStore_get(server->nodestore, &wvalue->nodeId);
-        if(!node)
-            return UA_STATUSCODE_BADNODEIDUNKNOWN;
-
-        switch(wvalue->attributeId) {
-        case UA_ATTRIBUTEID_BROWSENAME:
-          SETATTRIBUTE_IF_DATATYPE_IS(QUALIFIEDNAME)
-          done = UA_TRUE;
-          break;
-        case UA_ATTRIBUTEID_DISPLAYNAME:
-          SETATTRIBUTE_IF_DATATYPE_IS(LOCALIZEDTEXT)
-          done = UA_TRUE;
-          break;
-        case UA_ATTRIBUTEID_DESCRIPTION:
-          SETATTRIBUTE_IF_DATATYPE_IS(LOCALIZEDTEXT)
-          done = UA_TRUE;
-          break;
-        case UA_ATTRIBUTEID_WRITEMASK:
-          SETATTRIBUTE_IF_DATATYPE_IS(UINT32)
-          done = UA_TRUE;
-          break;
-        case UA_ATTRIBUTEID_USERWRITEMASK:
-          SETATTRIBUTE_IF_DATATYPE_IS(UINT32)
-          done = UA_TRUE;
-          break;
-        case UA_ATTRIBUTEID_ISABSTRACT:
-          SETATTRIBUTE_IF_DATATYPE_IS(BOOLEAN)
-          done = UA_TRUE;
-          break;
-        case UA_ATTRIBUTEID_SYMMETRIC:
-          SETATTRIBUTE_IF_DATATYPE_IS(BOOLEAN)
-          done = UA_TRUE;
-          break;
-        case UA_ATTRIBUTEID_INVERSENAME:
-          SETATTRIBUTE_IF_DATATYPE_IS(LOCALIZEDTEXT)
-          done = UA_TRUE;
-          break;
-        case UA_ATTRIBUTEID_CONTAINSNOLOOPS:
-          SETATTRIBUTE_IF_DATATYPE_IS(BOOLEAN)
-          done = UA_TRUE;
-          break;
-        case UA_ATTRIBUTEID_EVENTNOTIFIER:
-          SETATTRIBUTE_IF_DATATYPE_IS(BYTE)
-          done = UA_TRUE;
-          break;
-        case UA_ATTRIBUTEID_VALUERANK:
-          SETATTRIBUTE_IF_DATATYPE_IS(INT32)
-          done = UA_TRUE;
-          break;
-        case UA_ATTRIBUTEID_ARRAYDIMENSIONS:
-          SETATTRIBUTE_IF_DATATYPE_IS(INT32)
-          done = UA_TRUE;
-          break;
-        case UA_ATTRIBUTEID_ACCESSLEVEL:
-          SETATTRIBUTE_IF_DATATYPE_IS(UINT32)
-          done = UA_TRUE;
-          break;
-        case UA_ATTRIBUTEID_USERACCESSLEVEL:
-          SETATTRIBUTE_IF_DATATYPE_IS(UINT32)
-          done = UA_TRUE;
-          break;
-        case UA_ATTRIBUTEID_MINIMUMSAMPLINGINTERVAL:
-          SETATTRIBUTE_IF_DATATYPE_IS(DOUBLE)
-          done = UA_TRUE;
-          break;
-        case UA_ATTRIBUTEID_HISTORIZING:
-          SETATTRIBUTE_IF_DATATYPE_IS(BOOLEAN)
-          done = UA_TRUE;
-          break;
-        case UA_ATTRIBUTEID_EXECUTABLE:
-          SETATTRIBUTE_IF_DATATYPE_IS(BOOLEAN)
-          done = UA_TRUE;
-          break;
-        case UA_ATTRIBUTEID_USEREXECUTABLE:
-          SETATTRIBUTE_IF_DATATYPE_IS(BOOLEAN)
-          done = UA_TRUE;
-          break;
-        // The value logic implemented by jpfr is far more advanced that that in the userspace, so we use that
-        // here
-        case UA_ATTRIBUTEID_VALUE: {
-            if(node->nodeClass != UA_NODECLASS_VARIABLE &&
-               node->nodeClass != UA_NODECLASS_VARIABLETYPE) {
-                retval = UA_STATUSCODE_BADTYPEMISMATCH;
-                break;
-            }
-
-            /* parse the range */
-            UA_Boolean hasRange = UA_FALSE;
-            UA_NumericRange range;
-            if(wvalue->indexRange.length > 0) {
-                retval = parse_numericrange(wvalue->indexRange, &range);
-                if(retval != UA_STATUSCODE_GOOD)
-                    break;
-                hasRange = UA_TRUE;
-            }
-
-            /* the relevant members are similar for variables and variabletypes */
-            const UA_VariableNode *vn = (const UA_VariableNode*)node;
-            if(vn->valueSource == UA_VALUESOURCE_DATASOURCE) {
-                if(!vn->value.dataSource.write) {
-                    retval = UA_STATUSCODE_BADWRITENOTSUPPORTED;
-                    goto clean_up_range;
-                }
-                // todo: writing ranges
-                if(hasRange)
-                    retval = vn->value.dataSource.write(vn->value.dataSource.handle, &wvalue->value.value, &range);
-                else
-                    retval = vn->value.dataSource.write(vn->value.dataSource.handle, &wvalue->value.value, UA_NULL);
-                done = UA_TRUE;
-                goto clean_up_range;
-            }
-            const UA_Variant *oldV = &vn->value.variant;
-
-            /* the nodeid on the wire may be != the nodeid in the node: opaque types, enums and bytestrings */
-            if(!UA_NodeId_equal(&oldV->type->typeId, &wvalue->value.value.type->typeId)) {
-                if(oldV->type->namespaceZero && wvalue->value.value.type->namespaceZero &&
-                   oldV->type->typeIndex == wvalue->value.value.type->typeIndex)
-                    /* An enum was sent as an int32, or an opaque type as a bytestring. This is
-                       detected with the typeIndex indicated the "true" datatype. */
-
-                    wvalue->value.value.type = oldV->type;
-                else if(oldV->type == &UA_TYPES[UA_TYPES_BYTE] && !UA_Variant_isScalar(oldV) &&
-                        wvalue->value.value.type == &UA_TYPES[UA_TYPES_BYTESTRING] &&
-                        UA_Variant_isScalar(&wvalue->value.value)) {
-                    /* a string is written to a byte array */
-                    UA_ByteString *str = (UA_ByteString*) wvalue->value.value.data;
-                    wvalue->value.value.arrayLength = str->length;
-                    wvalue->value.value.data = str->data;
-                    wvalue->value.value.type = &UA_TYPES[UA_TYPES_BYTE];
-                    UA_free(str);
-                } else {
-                    retval = UA_STATUSCODE_BADTYPEMISMATCH;
-                    goto clean_up_range;
-                }
-            }
+#define CHECK_DATATYPE(EXP_DT) do {										\
+		if(!wvalue->value.hasValue || &UA_TYPES[UA_TYPES_##EXP_DT] != wvalue->value.value.type || \
+		   !UA_Variant_isScalar(&wvalue->value.value)) {				\
+			retval |= UA_STATUSCODE_BADTYPEMISMATCH;					\
+			break;														\
+		} } while(0)
+
+#define CHECK_NODECLASS_WRITE(CLASS) do {										\
+		if((anyTypeNode.node->nodeClass & (CLASS)) == 0) {				\
+			retval |= UA_STATUSCODE_BADNODECLASSINVALID;				\
+			break;														\
+		} } while(0)
+
+UA_StatusCode UA_Service_Write_single(UA_Server *server, UA_Session *session, const UA_WriteValue *wvalue) {
+	UA_assert(server != UA_NULL && session != UA_NULL && wvalue != UA_NULL);
+
+	union {
+		UA_Node *node;
+		UA_ObjectNode *oObj;
+		UA_ObjectTypeNode *otObj;
+		UA_VariableNode *vObj;
+		UA_VariableTypeNode *vtObj;
+		UA_ReferenceTypeNode *rtObj;
+		UA_MethodNode *mObj;
+		UA_DataTypeNode *dtObj;
+		UA_ViewNode *vwObj;
+	} anyTypeNode;
+
+	UA_StatusCode retval = UA_Server_getNodeCopy(server, wvalue->nodeId, (void **) &anyTypeNode.node);
+	if (retval)
+		return retval;
+  
+	void *value = wvalue->value.value.data;
+	switch(wvalue->attributeId) {
+	case UA_ATTRIBUTEID_BROWSENAME:
+		CHECK_DATATYPE(QUALIFIEDNAME);
+		UA_QualifiedName_deleteMembers(&anyTypeNode.node->browseName);
+		UA_QualifiedName_copy((const UA_QualifiedName *) value, &anyTypeNode.node->browseName);
+		break;
+	case UA_ATTRIBUTEID_DISPLAYNAME:
+		CHECK_DATATYPE(LOCALIZEDTEXT);
+		UA_LocalizedText_deleteMembers(&anyTypeNode.node->displayName);
+		UA_LocalizedText_copy((const UA_LocalizedText *) value, &anyTypeNode.node->displayName);
+		break;
+	case UA_ATTRIBUTEID_DESCRIPTION:
+		CHECK_DATATYPE(LOCALIZEDTEXT);
+		UA_LocalizedText_deleteMembers(&anyTypeNode.node->description);
+		UA_LocalizedText_copy((const UA_LocalizedText *) value, &anyTypeNode.node->description);
+		break;
+	case UA_ATTRIBUTEID_WRITEMASK:
+		CHECK_DATATYPE(UINT32);
+		anyTypeNode.node->writeMask = *(const UA_UInt32*) value;
+		break;
+	case UA_ATTRIBUTEID_USERWRITEMASK:
+		CHECK_DATATYPE(UINT32);
+		anyTypeNode.node->userWriteMask = *(const UA_UInt32*) value;
+		break;    
+	case UA_ATTRIBUTEID_ISABSTRACT:
+		CHECK_NODECLASS_WRITE(UA_NODECLASS_OBJECTTYPE | UA_NODECLASS_REFERENCETYPE | UA_NODECLASS_VARIABLETYPE | UA_NODECLASS_DATATYPE);
+		CHECK_DATATYPE(BOOLEAN);
+		anyTypeNode.otObj->isAbstract = *(const UA_Boolean *) value;
+		break;
+	case UA_ATTRIBUTEID_SYMMETRIC:
+		CHECK_NODECLASS_WRITE(UA_NODECLASS_REFERENCETYPE);
+		CHECK_DATATYPE(BOOLEAN);
+		anyTypeNode.rtObj->symmetric = *(const UA_Boolean *) value;
+		break;
+	case UA_ATTRIBUTEID_INVERSENAME:
+		CHECK_NODECLASS_WRITE(UA_NODECLASS_REFERENCETYPE);
+		CHECK_DATATYPE(LOCALIZEDTEXT);
+		UA_LocalizedText_deleteMembers(&anyTypeNode.rtObj->inverseName);
+		UA_LocalizedText_copy((const UA_LocalizedText *) value, &anyTypeNode.rtObj->inverseName);
+		break;
+	case UA_ATTRIBUTEID_CONTAINSNOLOOPS:
+		CHECK_NODECLASS_WRITE(UA_NODECLASS_VIEW);
+		CHECK_DATATYPE(BOOLEAN);
+		anyTypeNode.vwObj->containsNoLoops = *(const UA_Boolean *) value;
+		break;
+	case UA_ATTRIBUTEID_EVENTNOTIFIER:
+		CHECK_NODECLASS_WRITE(UA_NODECLASS_VIEW | UA_NODECLASS_OBJECT);
+		CHECK_DATATYPE(BYTE);
+		anyTypeNode.vwObj->eventNotifier = *(const UA_Byte *) value;
+		break;
+	case UA_ATTRIBUTEID_VALUE:
+		CHECK_NODECLASS_WRITE(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE);
+
+		/* parse the range */
+		UA_Boolean hasRange = UA_FALSE;
+		UA_NumericRange range;
+		if(wvalue->indexRange.length > 0) {
+			retval = parse_numericrange(wvalue->indexRange, &range);
+			if(retval != UA_STATUSCODE_GOOD)
+				break;
+			hasRange = UA_TRUE;
+		}
 
 
-            /* copy the node */
-            UA_VariableNode *newVn = (node->nodeClass == UA_NODECLASS_VARIABLE) ?
-                UA_VariableNode_new() : (UA_VariableNode*)UA_VariableTypeNode_new();
-            if(!newVn) {
-                retval = UA_STATUSCODE_BADOUTOFMEMORY;
-                goto clean_up_range;
-            }
-            retval = (node->nodeClass == UA_NODECLASS_VARIABLE) ? UA_VariableNode_copy(vn, newVn) : 
-                UA_VariableTypeNode_copy((const UA_VariableTypeNode*)vn, (UA_VariableTypeNode*)newVn);
-            if(retval != UA_STATUSCODE_GOOD)
-                goto clean_up;
-                
-            /* insert the new value */
-            if(hasRange)
-                retval = UA_Variant_setRangeCopy(&newVn->value.variant, wvalue->value.value.data,
-                                                 wvalue->value.value.arrayLength, range);
-            else {
-                UA_Variant_deleteMembers(&newVn->value.variant);
-                retval = UA_Variant_copy(&wvalue->value.value, &newVn->value.variant);
-            }
+		/* the relevant members are similar for variables and variabletypes */
+		UA_VariableNode *vn = anyTypeNode.vObj;
+		if(vn->valueSource == UA_VALUESOURCE_DATASOURCE) {
+			if(!vn->value.dataSource.write) {
+				retval = UA_STATUSCODE_BADWRITENOTSUPPORTED;
+				goto clean_up_range;
+			}
+			if(hasRange)
+				retval = vn->value.dataSource.write(vn->value.dataSource.handle, &wvalue->value.value, &range);
+			else
+				retval = vn->value.dataSource.write(vn->value.dataSource.handle, &wvalue->value.value, UA_NULL);
+			goto clean_up_range;
+		}
 
 
-            if(retval == UA_STATUSCODE_GOOD && UA_NodeStore_replace(server->nodestore, node,
-                                                   (UA_Node*)newVn, UA_NULL) == UA_STATUSCODE_GOOD) {
-                done = UA_TRUE;
-                goto clean_up_range;
-            }
+		/* the nodeid on the wire may be != the nodeid in the node: opaque types, enums and bytestrings */
+		// TODO: fix the dirty hack of writing into const
+		UA_Variant *oldV = &vn->value.variant;
+		UA_WriteValue *wv = (UA_WriteValue*)(uintptr_t)wvalue;
+		if(!UA_NodeId_equal(&oldV->type->typeId, &wvalue->value.value.type->typeId)) {
+			if(oldV->type->namespaceZero && wvalue->value.value.type->namespaceZero &&
+			   oldV->type->typeIndex == wvalue->value.value.type->typeIndex)
+				/* An enum was sent as an int32, or an opaque type as a bytestring. This is
+				   detected with the typeIndex indicated the "true" datatype. */
+				wv->value.value.type = oldV->type;
+			else if(oldV->type == &UA_TYPES[UA_TYPES_BYTE] && !UA_Variant_isScalar(oldV) &&
+					wvalue->value.value.type == &UA_TYPES[UA_TYPES_BYTESTRING] &&
+					UA_Variant_isScalar(&wvalue->value.value)) {
+				/* a string is written to a byte array */
+				UA_ByteString *str = (UA_ByteString*) wvalue->value.value.data;
+				wv->value.value.arrayLength = str->length;
+				wv->value.value.data = str->data;
+				wv->value.value.type = &UA_TYPES[UA_TYPES_BYTE];
+				UA_free(str);
+			} else {
+				retval = UA_STATUSCODE_BADTYPEMISMATCH;
+				goto clean_up_range;
+			}
+		}
 
 
-            clean_up:
-            if(node->nodeClass == UA_NODECLASS_VARIABLE)
-                UA_VariableNode_delete(newVn);
-            else
-                UA_VariableTypeNode_delete((UA_VariableTypeNode*)newVn);
-            clean_up_range:
-            if(hasRange)
-                UA_free(range.dimensions);
-            }
-            break;
-        case UA_ATTRIBUTEID_NODEID:
-        case UA_ATTRIBUTEID_NODECLASS:
-        case UA_ATTRIBUTEID_DATATYPE:
-        default:
-            retval = UA_STATUSCODE_BADWRITENOTSUPPORTED;
-            break;
-        }
+		/* insert the new value */
+		if(hasRange)
+			retval = UA_Variant_setRangeCopy(&vn->value.variant, wvalue->value.value.data, wvalue->value.value.arrayLength, range);
+		else {
+			UA_Variant_deleteMembers(&vn->value.variant);
+			retval = UA_Variant_copy(&wvalue->value.value, &vn->value.variant);
+		}
 
 
-        UA_NodeStore_release(node);
-        if(retval != UA_STATUSCODE_GOOD)
-            break;
-    }
+	clean_up_range:
+		if(hasRange)
+			UA_free(range.dimensions);
+
+		break;
+	case UA_ATTRIBUTEID_ACCESSLEVEL:
+		CHECK_NODECLASS_WRITE(UA_NODECLASS_VARIABLE);
+		CHECK_DATATYPE(BYTE);
+		anyTypeNode.vObj->accessLevel = *(const UA_Byte*) value;
+		break;
+	case UA_ATTRIBUTEID_USERACCESSLEVEL:
+		CHECK_NODECLASS_WRITE(UA_NODECLASS_VARIABLE);
+		CHECK_DATATYPE(BYTE);
+		anyTypeNode.vObj->userAccessLevel = *(const UA_Byte*) value;
+		break;
+	case UA_ATTRIBUTEID_MINIMUMSAMPLINGINTERVAL:
+		CHECK_NODECLASS_WRITE(UA_NODECLASS_VARIABLE);
+		CHECK_DATATYPE(DOUBLE);
+		anyTypeNode.vObj->minimumSamplingInterval = *(const UA_Double *) value;
+		break;
+	case UA_ATTRIBUTEID_HISTORIZING:
+		CHECK_NODECLASS_WRITE(UA_NODECLASS_VARIABLE);
+		CHECK_DATATYPE(BOOLEAN);
+		anyTypeNode.vObj->historizing = *(const UA_Boolean *) value;
+		break;
+	case UA_ATTRIBUTEID_EXECUTABLE:
+		CHECK_NODECLASS_WRITE(UA_NODECLASS_METHOD);
+		CHECK_DATATYPE(BOOLEAN);
+		anyTypeNode.mObj->executable = *(const UA_Boolean *) value;
+		break;
+	case UA_ATTRIBUTEID_USEREXECUTABLE:
+		CHECK_NODECLASS_WRITE(UA_NODECLASS_METHOD);
+		CHECK_DATATYPE(BOOLEAN);
+		anyTypeNode.mObj->userExecutable = *(const UA_Boolean *) value;
+		break;
+	default:
+		retval = UA_STATUSCODE_BADATTRIBUTEIDINVALID;
+		break;
+	}
 
 
-    return retval;
+	if(retval != UA_STATUSCODE_GOOD) {
+		UA_Server_deleteNodeCopy(server, (void **) &anyTypeNode.node);
+		return retval;
+	}
+  
+	const UA_Node *oldNode = UA_NodeStore_get(server->nodestore, &wvalue->nodeId);
+	// Node copy is now in the nodestore. Do not delete here!
+	retval = UA_NodeStore_replace(server->nodestore, oldNode, anyTypeNode.node, UA_NULL);
+	UA_NodeStore_release(oldNode);
+	return retval;
 }
 }
 
 
 void Service_Write(UA_Server *server, UA_Session *session, const UA_WriteRequest *request,
 void Service_Write(UA_Server *server, UA_Session *session, const UA_WriteRequest *request,
@@ -684,6 +666,6 @@ void Service_Write(UA_Server *server, UA_Session *session, const UA_WriteRequest
 #ifdef UA_EXTERNAL_NAMESPACES
 #ifdef UA_EXTERNAL_NAMESPACES
         if(!isExternal[i])
         if(!isExternal[i])
 #endif
 #endif
-            response->results[i] = writeValue(server, &request->nodesToWrite[i]);
+		  response->results[i] = UA_Service_Write_single(server, session, &request->nodesToWrite[i]);
     }
     }
 }
 }