|
@@ -103,6 +103,15 @@ static void handleSourceTimestamps(UA_TimestampsToReturn timestamps, UA_DataValu
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+/* force cast for zero-copy reading. ensure that the variant is never written into. */
|
|
|
+static void forceVariantSetScalar(UA_Variant *v, const void *p, const UA_DataType *type) {
|
|
|
+ UA_Variant_init(v);
|
|
|
+ v->type = type;
|
|
|
+ v->arrayLength = 0;
|
|
|
+ v->data = (void*)(uintptr_t)p;
|
|
|
+ v->storageType = UA_VARIANT_DATA_NODELETE;
|
|
|
+}
|
|
|
+
|
|
|
static UA_StatusCode getVariableNodeValue(const UA_VariableNode *vn, const UA_TimestampsToReturn timestamps,
|
|
|
const UA_ReadValueId *id, UA_DataValue *v) {
|
|
|
UA_NumericRange range;
|
|
@@ -119,10 +128,11 @@ static UA_StatusCode getVariableNodeValue(const UA_VariableNode *vn, const UA_Ti
|
|
|
if(vn->value.variant.callback.onRead)
|
|
|
vn->value.variant.callback.onRead(vn->value.variant.callback.handle, vn->nodeId,
|
|
|
&v->value, rangeptr);
|
|
|
- if(rangeptr)
|
|
|
+ if(!rangeptr) {
|
|
|
+ v->value = vn->value.variant.value;
|
|
|
+ v->value.storageType = UA_VARIANT_DATA_NODELETE;
|
|
|
+ } else
|
|
|
retval = UA_Variant_copyRange(&vn->value.variant.value, &v->value, range);
|
|
|
- else
|
|
|
- retval = UA_Variant_copy(&vn->value.variant.value, &v->value);
|
|
|
if(retval == UA_STATUSCODE_GOOD)
|
|
|
handleSourceTimestamps(timestamps, v);
|
|
|
} else {
|
|
@@ -140,8 +150,8 @@ static UA_StatusCode getVariableNodeValue(const UA_VariableNode *vn, const UA_Ti
|
|
|
static UA_StatusCode getVariableNodeDataType(const UA_VariableNode *vn, UA_DataValue *v) {
|
|
|
UA_StatusCode retval = UA_STATUSCODE_GOOD;
|
|
|
if(vn->valueSource == UA_VALUESOURCE_VARIANT) {
|
|
|
- retval = UA_Variant_setScalarCopy(&v->value, &vn->value.variant.value.type->typeId,
|
|
|
- &UA_TYPES[UA_TYPES_NODEID]);
|
|
|
+ forceVariantSetScalar(&v->value, &vn->value.variant.value.type->typeId,
|
|
|
+ &UA_TYPES[UA_TYPES_NODEID]);
|
|
|
} else {
|
|
|
/* Read from the datasource to see the data type */
|
|
|
UA_DataValue val;
|
|
@@ -158,8 +168,9 @@ static UA_StatusCode getVariableNodeDataType(const UA_VariableNode *vn, UA_DataV
|
|
|
static UA_StatusCode getVariableNodeArrayDimensions(const UA_VariableNode *vn, UA_DataValue *v) {
|
|
|
UA_StatusCode retval = UA_STATUSCODE_GOOD;
|
|
|
if(vn->valueSource == UA_VALUESOURCE_VARIANT) {
|
|
|
- retval = UA_Variant_setArrayCopy(&v->value, vn->value.variant.value.arrayDimensions,
|
|
|
- vn->value.variant.value.arrayDimensionsSize, &UA_TYPES[UA_TYPES_INT32]);
|
|
|
+ UA_Variant_setArray(&v->value, vn->value.variant.value.arrayDimensions,
|
|
|
+ vn->value.variant.value.arrayDimensionsSize, &UA_TYPES[UA_TYPES_INT32]);
|
|
|
+ v->value.storageType = UA_VARIANT_DATA_NODELETE;
|
|
|
} else {
|
|
|
/* Read the datasource to see the array dimensions */
|
|
|
UA_DataValue val;
|
|
@@ -204,54 +215,53 @@ void Service_Read_single(UA_Server *server, UA_Session *session, const UA_Timest
|
|
|
/* When setting the value fails in the switch, we get an error code and set hasValue to false */
|
|
|
UA_StatusCode retval = UA_STATUSCODE_GOOD;
|
|
|
v->hasValue = UA_TRUE;
|
|
|
-
|
|
|
switch(id->attributeId) {
|
|
|
case UA_ATTRIBUTEID_NODEID:
|
|
|
- retval = UA_Variant_setScalarCopy(&v->value, &node->nodeId, &UA_TYPES[UA_TYPES_NODEID]);
|
|
|
+ forceVariantSetScalar(&v->value, &node->nodeId, &UA_TYPES[UA_TYPES_NODEID]);
|
|
|
break;
|
|
|
case UA_ATTRIBUTEID_NODECLASS:
|
|
|
- retval = UA_Variant_setScalarCopy(&v->value, &node->nodeClass, &UA_TYPES[UA_TYPES_INT32]);
|
|
|
+ forceVariantSetScalar(&v->value, &node->nodeClass, &UA_TYPES[UA_TYPES_INT32]);
|
|
|
break;
|
|
|
case UA_ATTRIBUTEID_BROWSENAME:
|
|
|
- retval = UA_Variant_setScalarCopy(&v->value, &node->browseName, &UA_TYPES[UA_TYPES_QUALIFIEDNAME]);
|
|
|
+ forceVariantSetScalar(&v->value, &node->browseName, &UA_TYPES[UA_TYPES_QUALIFIEDNAME]);
|
|
|
break;
|
|
|
case UA_ATTRIBUTEID_DISPLAYNAME:
|
|
|
- retval = UA_Variant_setScalarCopy(&v->value, &node->displayName, &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]);
|
|
|
+ forceVariantSetScalar(&v->value, &node->displayName, &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]);
|
|
|
break;
|
|
|
case UA_ATTRIBUTEID_DESCRIPTION:
|
|
|
- retval = UA_Variant_setScalarCopy(&v->value, &node->description, &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]);
|
|
|
+ forceVariantSetScalar(&v->value, &node->description, &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]);
|
|
|
break;
|
|
|
case UA_ATTRIBUTEID_WRITEMASK:
|
|
|
- retval = UA_Variant_setScalarCopy(&v->value, &node->writeMask, &UA_TYPES[UA_TYPES_UINT32]);
|
|
|
+ forceVariantSetScalar(&v->value, &node->writeMask, &UA_TYPES[UA_TYPES_UINT32]);
|
|
|
break;
|
|
|
case UA_ATTRIBUTEID_USERWRITEMASK:
|
|
|
- retval = UA_Variant_setScalarCopy(&v->value, &node->userWriteMask, &UA_TYPES[UA_TYPES_UINT32]);
|
|
|
+ forceVariantSetScalar(&v->value, &node->userWriteMask, &UA_TYPES[UA_TYPES_UINT32]);
|
|
|
break;
|
|
|
case UA_ATTRIBUTEID_ISABSTRACT:
|
|
|
CHECK_NODECLASS(UA_NODECLASS_REFERENCETYPE | UA_NODECLASS_OBJECTTYPE |
|
|
|
UA_NODECLASS_VARIABLETYPE | UA_NODECLASS_DATATYPE);
|
|
|
- retval = UA_Variant_setScalarCopy(&v->value, &((const UA_ReferenceTypeNode*)node)->isAbstract,
|
|
|
- &UA_TYPES[UA_TYPES_BOOLEAN]);
|
|
|
+ forceVariantSetScalar(&v->value, &((const UA_ReferenceTypeNode*)node)->isAbstract,
|
|
|
+ &UA_TYPES[UA_TYPES_BOOLEAN]);
|
|
|
break;
|
|
|
case UA_ATTRIBUTEID_SYMMETRIC:
|
|
|
CHECK_NODECLASS(UA_NODECLASS_REFERENCETYPE);
|
|
|
- retval = UA_Variant_setScalarCopy(&v->value, &((const UA_ReferenceTypeNode*)node)->symmetric,
|
|
|
- &UA_TYPES[UA_TYPES_BOOLEAN]);
|
|
|
+ forceVariantSetScalar(&v->value, &((const UA_ReferenceTypeNode*)node)->symmetric,
|
|
|
+ &UA_TYPES[UA_TYPES_BOOLEAN]);
|
|
|
break;
|
|
|
case UA_ATTRIBUTEID_INVERSENAME:
|
|
|
CHECK_NODECLASS(UA_NODECLASS_REFERENCETYPE);
|
|
|
- retval = UA_Variant_setScalarCopy(&v->value, &((const UA_ReferenceTypeNode*)node)->inverseName,
|
|
|
- &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]);
|
|
|
+ forceVariantSetScalar(&v->value, &((const UA_ReferenceTypeNode*)node)->inverseName,
|
|
|
+ &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]);
|
|
|
break;
|
|
|
case UA_ATTRIBUTEID_CONTAINSNOLOOPS:
|
|
|
CHECK_NODECLASS(UA_NODECLASS_VIEW);
|
|
|
- retval = UA_Variant_setScalarCopy(&v->value, &((const UA_ViewNode*)node)->containsNoLoops,
|
|
|
- &UA_TYPES[UA_TYPES_BOOLEAN]);
|
|
|
+ forceVariantSetScalar(&v->value, &((const UA_ViewNode*)node)->containsNoLoops,
|
|
|
+ &UA_TYPES[UA_TYPES_BOOLEAN]);
|
|
|
break;
|
|
|
case UA_ATTRIBUTEID_EVENTNOTIFIER:
|
|
|
CHECK_NODECLASS(UA_NODECLASS_VIEW | UA_NODECLASS_OBJECT);
|
|
|
- retval = UA_Variant_setScalarCopy(&v->value, &((const UA_ViewNode*)node)->eventNotifier,
|
|
|
- &UA_TYPES[UA_TYPES_BYTE]);
|
|
|
+ forceVariantSetScalar(&v->value, &((const UA_ViewNode*)node)->eventNotifier,
|
|
|
+ &UA_TYPES[UA_TYPES_BYTE]);
|
|
|
break;
|
|
|
case UA_ATTRIBUTEID_VALUE:
|
|
|
CHECK_NODECLASS(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE);
|
|
@@ -263,8 +273,8 @@ void Service_Read_single(UA_Server *server, UA_Session *session, const UA_Timest
|
|
|
break;
|
|
|
case UA_ATTRIBUTEID_VALUERANK:
|
|
|
CHECK_NODECLASS(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE);
|
|
|
- retval = UA_Variant_setScalarCopy(&v->value, &((const UA_VariableTypeNode*)node)->valueRank,
|
|
|
- &UA_TYPES[UA_TYPES_INT32]);
|
|
|
+ forceVariantSetScalar(&v->value, &((const UA_VariableTypeNode*)node)->valueRank,
|
|
|
+ &UA_TYPES[UA_TYPES_INT32]);
|
|
|
break;
|
|
|
case UA_ATTRIBUTEID_ARRAYDIMENSIONS:
|
|
|
CHECK_NODECLASS(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE);
|
|
@@ -272,33 +282,33 @@ void Service_Read_single(UA_Server *server, UA_Session *session, const UA_Timest
|
|
|
break;
|
|
|
case UA_ATTRIBUTEID_ACCESSLEVEL:
|
|
|
CHECK_NODECLASS(UA_NODECLASS_VARIABLE);
|
|
|
- retval = UA_Variant_setScalarCopy(&v->value, &((const UA_VariableNode*)node)->accessLevel,
|
|
|
- &UA_TYPES[UA_TYPES_BYTE]);
|
|
|
+ forceVariantSetScalar(&v->value, &((const UA_VariableNode*)node)->accessLevel,
|
|
|
+ &UA_TYPES[UA_TYPES_BYTE]);
|
|
|
break;
|
|
|
case UA_ATTRIBUTEID_USERACCESSLEVEL:
|
|
|
CHECK_NODECLASS(UA_NODECLASS_VARIABLE);
|
|
|
- retval = UA_Variant_setScalarCopy(&v->value, &((const UA_VariableNode*)node)->userAccessLevel,
|
|
|
- &UA_TYPES[UA_TYPES_BYTE]);
|
|
|
+ forceVariantSetScalar(&v->value, &((const UA_VariableNode*)node)->userAccessLevel,
|
|
|
+ &UA_TYPES[UA_TYPES_BYTE]);
|
|
|
break;
|
|
|
case UA_ATTRIBUTEID_MINIMUMSAMPLINGINTERVAL:
|
|
|
CHECK_NODECLASS(UA_NODECLASS_VARIABLE);
|
|
|
- retval = UA_Variant_setScalarCopy(&v->value, &((const UA_VariableNode*)node)->minimumSamplingInterval,
|
|
|
- &UA_TYPES[UA_TYPES_DOUBLE]);
|
|
|
+ forceVariantSetScalar(&v->value, &((const UA_VariableNode*)node)->minimumSamplingInterval,
|
|
|
+ &UA_TYPES[UA_TYPES_DOUBLE]);
|
|
|
break;
|
|
|
case UA_ATTRIBUTEID_HISTORIZING:
|
|
|
CHECK_NODECLASS(UA_NODECLASS_VARIABLE);
|
|
|
- retval = UA_Variant_setScalarCopy(&v->value, &((const UA_VariableNode*)node)->historizing,
|
|
|
- &UA_TYPES[UA_TYPES_BOOLEAN]);
|
|
|
+ forceVariantSetScalar(&v->value, &((const UA_VariableNode*)node)->historizing,
|
|
|
+ &UA_TYPES[UA_TYPES_BOOLEAN]);
|
|
|
break;
|
|
|
case UA_ATTRIBUTEID_EXECUTABLE:
|
|
|
CHECK_NODECLASS(UA_NODECLASS_METHOD);
|
|
|
- retval = UA_Variant_setScalarCopy(&v->value, &((const UA_MethodNode*)node)->executable,
|
|
|
- &UA_TYPES[UA_TYPES_BOOLEAN]);
|
|
|
+ forceVariantSetScalar(&v->value, &((const UA_MethodNode*)node)->executable,
|
|
|
+ &UA_TYPES[UA_TYPES_BOOLEAN]);
|
|
|
break;
|
|
|
case UA_ATTRIBUTEID_USEREXECUTABLE:
|
|
|
CHECK_NODECLASS(UA_NODECLASS_METHOD);
|
|
|
- retval = UA_Variant_setScalarCopy(&v->value, &((const UA_MethodNode*)node)->userExecutable,
|
|
|
- &UA_TYPES[UA_TYPES_BOOLEAN]);
|
|
|
+ forceVariantSetScalar(&v->value, &((const UA_MethodNode*)node)->userExecutable,
|
|
|
+ &UA_TYPES[UA_TYPES_BOOLEAN]);
|
|
|
break;
|
|
|
default:
|
|
|
retval = UA_STATUSCODE_BADATTRIBUTEIDINVALID;
|
|
@@ -373,8 +383,8 @@ void Service_Read(UA_Server *server, UA_Session *session, const UA_ReadRequest *
|
|
|
}
|
|
|
|
|
|
#ifdef EXTENSION_STATELESS
|
|
|
+ /* Add an expiry header for caching */
|
|
|
if(session==&anonymousSession){
|
|
|
- /* expiry header */
|
|
|
UA_ExtensionObject additionalHeader;
|
|
|
UA_ExtensionObject_init(&additionalHeader);
|
|
|
additionalHeader.typeId = UA_TYPES[UA_TYPES_VARIANT].typeId;
|
|
@@ -415,11 +425,11 @@ UA_StatusCode UA_Server_editNode(UA_Server *server, UA_Session *session, const U
|
|
|
UA_EditNodeCallback callback, const void *data) {
|
|
|
UA_StatusCode retval;
|
|
|
do {
|
|
|
- const UA_Node *node = UA_NodeStore_get(server->nodestore, nodeId);
|
|
|
+ UA_MT_CONST UA_Node *node = UA_NodeStore_get(server->nodestore, nodeId);
|
|
|
if(!node)
|
|
|
return UA_STATUSCODE_BADNODEIDUNKNOWN;
|
|
|
#ifndef UA_MULTITHREADING
|
|
|
- retval = callback(server, session, (UA_Node*)(uintptr_t)node, data);
|
|
|
+ retval = callback(server, session, node, data);
|
|
|
return retval;
|
|
|
#else
|
|
|
UA_Node *copy = UA_Node_copyAnyNodeClass(node);
|
|
@@ -454,7 +464,7 @@ UA_StatusCode UA_Server_editNode(UA_Server *server, UA_Session *session, const U
|
|
|
|
|
|
static UA_StatusCode
|
|
|
Service_Write_single_ValueDataSource(UA_Server *server, UA_Session *session, const UA_VariableNode *node,
|
|
|
- UA_WriteValue *wvalue) {
|
|
|
+ const UA_WriteValue *wvalue) {
|
|
|
UA_assert(wvalue->attributeId == UA_ATTRIBUTEID_VALUE);
|
|
|
UA_assert(node->nodeClass == UA_NODECLASS_VARIABLE || node->nodeClass == UA_NODECLASS_VARIABLETYPE);
|
|
|
UA_assert(node->valueSource == UA_VALUESOURCE_DATASOURCE);
|
|
@@ -493,7 +503,7 @@ static enum type_equivalence typeEquivalence(const UA_DataType *type) {
|
|
|
|
|
|
/* In the multithreaded case, node is a copy */
|
|
|
static UA_StatusCode
|
|
|
-MoveValueIntoNode(UA_Server *server, UA_Session *session, UA_VariableNode *node, UA_WriteValue *wvalue) {
|
|
|
+CopyValueIntoNode(UA_VariableNode *node, const UA_WriteValue *wvalue) {
|
|
|
UA_assert(wvalue->attributeId == UA_ATTRIBUTEID_VALUE);
|
|
|
UA_assert(node->nodeClass == UA_NODECLASS_VARIABLE || node->nodeClass == UA_NODECLASS_VARIABLETYPE);
|
|
|
UA_assert(node->valueSource == UA_VALUESOURCE_VARIANT);
|
|
@@ -511,7 +521,7 @@ MoveValueIntoNode(UA_Server *server, UA_Session *session, UA_VariableNode *node,
|
|
|
|
|
|
/* The nodeid on the wire may be != the nodeid in the node: opaque types, enums and bytestrings.
|
|
|
nodeV contains the correct type definition. */
|
|
|
- UA_Variant *newV = &wvalue->value.value;
|
|
|
+ const UA_Variant *newV = &wvalue->value.value;
|
|
|
UA_Variant *oldV = &node->value.variant.value;
|
|
|
UA_Variant cast_v;
|
|
|
if(!UA_NodeId_equal(&oldV->type->typeId, &newV->type->typeId)) {
|
|
@@ -522,14 +532,14 @@ MoveValueIntoNode(UA_Server *server, UA_Session *session, UA_VariableNode *node,
|
|
|
if(te1 != TYPE_EQUIVALENCE_NONE && te1 == te2) {
|
|
|
/* An enum was sent as an int32, or an opaque type as a bytestring. This is
|
|
|
detected with the typeIndex indicated the "true" datatype. */
|
|
|
- newV->type = oldV->type;
|
|
|
+ cast_v.type = oldV->type;
|
|
|
} else if(oldV->type == &UA_TYPES[UA_TYPES_BYTE] && !UA_Variant_isScalar(oldV) &&
|
|
|
newV->type == &UA_TYPES[UA_TYPES_BYTESTRING] && UA_Variant_isScalar(newV)) {
|
|
|
/* a string is written to a byte array */
|
|
|
UA_ByteString *str = (UA_ByteString*) newV->data;
|
|
|
- newV->arrayLength = str->length;
|
|
|
- newV->data = str->data;
|
|
|
- newV->type = &UA_TYPES[UA_TYPES_BYTE];
|
|
|
+ cast_v.arrayLength = str->length;
|
|
|
+ cast_v.data = str->data;
|
|
|
+ cast_v.type = &UA_TYPES[UA_TYPES_BYTE];
|
|
|
} else {
|
|
|
if(rangeptr)
|
|
|
UA_free(range.dimensions);
|
|
@@ -538,13 +548,10 @@ MoveValueIntoNode(UA_Server *server, UA_Session *session, UA_VariableNode *node,
|
|
|
}
|
|
|
|
|
|
if(!rangeptr) {
|
|
|
- // TODO: Avoid copying the whole node and then delete the old value for multithreading
|
|
|
UA_Variant_deleteMembers(&node->value.variant.value);
|
|
|
- node->value.variant.value = *newV;
|
|
|
- UA_Variant_init(&wvalue->value.value);
|
|
|
- } else {
|
|
|
+ UA_Variant_copy(newV, &node->value.variant.value);
|
|
|
+ } else
|
|
|
retval = UA_Variant_setRangeCopy(&node->value.variant.value, newV->data, newV->arrayLength, range);
|
|
|
- }
|
|
|
if(node->value.variant.callback.onWrite)
|
|
|
node->value.variant.callback.onWrite(node->value.variant.callback.handle, node->nodeId,
|
|
|
&node->value.variant.value, rangeptr);
|
|
@@ -554,9 +561,11 @@ MoveValueIntoNode(UA_Server *server, UA_Session *session, UA_VariableNode *node,
|
|
|
}
|
|
|
|
|
|
static UA_StatusCode
|
|
|
-MoveAttributeIntoNode(UA_Server *server, UA_Session *session, UA_Node *node, UA_WriteValue *wvalue) {
|
|
|
+CopyAttributeIntoNode(UA_Server *server, UA_Session *session, UA_Node *node, const UA_WriteValue *wvalue) {
|
|
|
UA_StatusCode retval = UA_STATUSCODE_GOOD;
|
|
|
void *value = wvalue->value.value.data;
|
|
|
+ void *target;
|
|
|
+ const UA_DataType *type = NULL;
|
|
|
switch(wvalue->attributeId) {
|
|
|
case UA_ATTRIBUTEID_NODEID:
|
|
|
case UA_ATTRIBUTEID_NODECLASS:
|
|
@@ -565,21 +574,18 @@ MoveAttributeIntoNode(UA_Server *server, UA_Session *session, UA_Node *node, UA_
|
|
|
break;
|
|
|
case UA_ATTRIBUTEID_BROWSENAME:
|
|
|
CHECK_DATATYPE(QUALIFIEDNAME);
|
|
|
- UA_QualifiedName_deleteMembers(&node->browseName);
|
|
|
- node->browseName = *(UA_QualifiedName*)value;
|
|
|
- UA_QualifiedName_init((UA_QualifiedName*)value);
|
|
|
+ target = &node->browseName;
|
|
|
+ type = &UA_TYPES[UA_TYPES_QUALIFIEDNAME];
|
|
|
break;
|
|
|
case UA_ATTRIBUTEID_DISPLAYNAME:
|
|
|
CHECK_DATATYPE(LOCALIZEDTEXT);
|
|
|
- UA_LocalizedText_deleteMembers(&node->displayName);
|
|
|
- node->displayName = *(UA_LocalizedText*)value;
|
|
|
- UA_LocalizedText_init((UA_LocalizedText*)value);
|
|
|
+ target = &node->displayName;
|
|
|
+ type = &UA_TYPES[UA_TYPES_LOCALIZEDTEXT];
|
|
|
break;
|
|
|
case UA_ATTRIBUTEID_DESCRIPTION:
|
|
|
CHECK_DATATYPE(LOCALIZEDTEXT);
|
|
|
- UA_LocalizedText_deleteMembers(&node->description);
|
|
|
- node->description = *(UA_LocalizedText*)value;
|
|
|
- UA_LocalizedText_init((UA_LocalizedText*)value);
|
|
|
+ target = &node->description;
|
|
|
+ type = &UA_TYPES[UA_TYPES_LOCALIZEDTEXT];
|
|
|
break;
|
|
|
case UA_ATTRIBUTEID_WRITEMASK:
|
|
|
CHECK_DATATYPE(UINT32);
|
|
@@ -603,10 +609,8 @@ MoveAttributeIntoNode(UA_Server *server, UA_Session *session, UA_Node *node, UA_
|
|
|
case UA_ATTRIBUTEID_INVERSENAME:
|
|
|
CHECK_NODECLASS_WRITE(UA_NODECLASS_REFERENCETYPE);
|
|
|
CHECK_DATATYPE(LOCALIZEDTEXT);
|
|
|
- UA_ReferenceTypeNode *n = (UA_ReferenceTypeNode*)node;
|
|
|
- UA_LocalizedText_deleteMembers(&n->inverseName);
|
|
|
- n->inverseName = *(UA_LocalizedText*)value;
|
|
|
- UA_LocalizedText_init((UA_LocalizedText*)value);
|
|
|
+ target = &((UA_ReferenceTypeNode*)node)->inverseName;
|
|
|
+ type = &UA_TYPES[UA_TYPES_LOCALIZEDTEXT];
|
|
|
break;
|
|
|
case UA_ATTRIBUTEID_CONTAINSNOLOOPS:
|
|
|
CHECK_NODECLASS_WRITE(UA_NODECLASS_VIEW);
|
|
@@ -620,7 +624,7 @@ MoveAttributeIntoNode(UA_Server *server, UA_Session *session, UA_Node *node, UA_
|
|
|
break;
|
|
|
case UA_ATTRIBUTEID_VALUE:
|
|
|
CHECK_NODECLASS_WRITE(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE);
|
|
|
- retval = MoveValueIntoNode(server, session, (UA_VariableNode*)node, wvalue);
|
|
|
+ retval = CopyValueIntoNode((UA_VariableNode*)node, wvalue);
|
|
|
break;
|
|
|
case UA_ATTRIBUTEID_ACCESSLEVEL:
|
|
|
CHECK_NODECLASS_WRITE(UA_NODECLASS_VARIABLE);
|
|
@@ -656,10 +660,14 @@ MoveAttributeIntoNode(UA_Server *server, UA_Session *session, UA_Node *node, UA_
|
|
|
retval = UA_STATUSCODE_BADATTRIBUTEIDINVALID;
|
|
|
break;
|
|
|
}
|
|
|
+ if(type) {
|
|
|
+ UA_deleteMembers(target, type);
|
|
|
+ retval = UA_copy(value, target, type);
|
|
|
+ }
|
|
|
return retval;
|
|
|
}
|
|
|
|
|
|
-UA_StatusCode Service_Write_single(UA_Server *server, UA_Session *session, UA_WriteValue *wvalue) {
|
|
|
+UA_StatusCode Service_Write_single(UA_Server *server, UA_Session *session, const UA_WriteValue *wvalue) {
|
|
|
if(!wvalue->value.hasValue || !wvalue->value.value.data)
|
|
|
return UA_STATUSCODE_BADNODATA; // TODO: is this the right return code?
|
|
|
if(wvalue->attributeId == UA_ATTRIBUTEID_VALUE) {
|
|
@@ -674,7 +682,7 @@ UA_StatusCode Service_Write_single(UA_Server *server, UA_Session *session, UA_Wr
|
|
|
}
|
|
|
}
|
|
|
return UA_Server_editNode(server, session, &wvalue->nodeId,
|
|
|
- (UA_EditNodeCallback)MoveAttributeIntoNode, wvalue);
|
|
|
+ (UA_EditNodeCallback)CopyAttributeIntoNode, wvalue);
|
|
|
}
|
|
|
|
|
|
void Service_Write(UA_Server *server, UA_Session *session, const UA_WriteRequest *request,
|