|
@@ -503,6 +503,7 @@ UA_Variant_matchVariableDefinition(UA_Server *server, const UA_NodeId *variableD
|
|
case -1: /* the value is a scalar */
|
|
case -1: /* the value is a scalar */
|
|
if(!UA_Variant_isScalar(value))
|
|
if(!UA_Variant_isScalar(value))
|
|
return UA_STATUSCODE_BADTYPEMISMATCH;
|
|
return UA_STATUSCODE_BADTYPEMISMATCH;
|
|
|
|
+ break;
|
|
case 0: /* the value is an array with one or more dimensions */
|
|
case 0: /* the value is an array with one or more dimensions */
|
|
if(UA_Variant_isScalar(value))
|
|
if(UA_Variant_isScalar(value))
|
|
return UA_STATUSCODE_BADTYPEMISMATCH;
|
|
return UA_STATUSCODE_BADTYPEMISMATCH;
|
|
@@ -545,14 +546,11 @@ UA_VariableNode_setDataType(UA_Server *server, UA_VariableNode *node,
|
|
return UA_STATUSCODE_BADTYPEMISMATCH;
|
|
return UA_STATUSCODE_BADTYPEMISMATCH;
|
|
|
|
|
|
/* Does the new type match the constraints of the variabletype? */
|
|
/* Does the new type match the constraints of the variabletype? */
|
|
- const UA_NodeId *vtDataType = &vt->dataType;
|
|
|
|
UA_NodeId subtypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE);
|
|
UA_NodeId subtypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE);
|
|
UA_Boolean found = false;
|
|
UA_Boolean found = false;
|
|
UA_StatusCode retval = isNodeInTree(server->nodestore, dataType,
|
|
UA_StatusCode retval = isNodeInTree(server->nodestore, dataType,
|
|
- vtDataType, &subtypeId, 1, &found);
|
|
|
|
- if(retval != UA_STATUSCODE_GOOD)
|
|
|
|
- return retval;
|
|
|
|
- if(!found)
|
|
|
|
|
|
+ &vt->dataType, &subtypeId, 1, &found);
|
|
|
|
+ if(retval != UA_STATUSCODE_GOOD || !found)
|
|
return UA_STATUSCODE_BADTYPEMISMATCH;
|
|
return UA_STATUSCODE_BADTYPEMISMATCH;
|
|
|
|
|
|
/* Check if the current value would match the new type */
|
|
/* Check if the current value would match the new type */
|
|
@@ -641,12 +639,14 @@ UA_VariableNode_setArrayDimensions(UA_Server *server, UA_VariableNode *node,
|
|
return UA_STATUSCODE_BADTYPEMISMATCH;
|
|
return UA_STATUSCODE_BADTYPEMISMATCH;
|
|
break;
|
|
break;
|
|
case 0: /* the value is an array with one or more dimensions */
|
|
case 0: /* the value is an array with one or more dimensions */
|
|
- if(arrayDimensionsSize == 0)
|
|
|
|
- return UA_STATUSCODE_BADTYPEMISMATCH;
|
|
|
|
|
|
+ /* no arraydimensions => array of dimension 1 */
|
|
break;
|
|
break;
|
|
default: /* >= 1: the value is an array with the specified number of dimensions */
|
|
default: /* >= 1: the value is an array with the specified number of dimensions */
|
|
if(node->valueRank < 0)
|
|
if(node->valueRank < 0)
|
|
return UA_STATUSCODE_BADTYPEMISMATCH;
|
|
return UA_STATUSCODE_BADTYPEMISMATCH;
|
|
|
|
+ if(arrayDimensionsSize == 0 && node->valueRank == 1)
|
|
|
|
+ /* no arraydimensions => array of dimension 1 */
|
|
|
|
+ break;
|
|
if(arrayDimensionsSize != (size_t)node->valueRank)
|
|
if(arrayDimensionsSize != (size_t)node->valueRank)
|
|
return UA_STATUSCODE_BADTYPEMISMATCH;
|
|
return UA_STATUSCODE_BADTYPEMISMATCH;
|
|
}
|
|
}
|
|
@@ -717,9 +717,14 @@ UA_VariableNode_setValue(UA_Server *server, UA_VariableNode *node,
|
|
|
|
|
|
/* write the value */
|
|
/* write the value */
|
|
if(node->valueSource == UA_VALUESOURCE_DATA) {
|
|
if(node->valueSource == UA_VALUESOURCE_DATA) {
|
|
- if(!rangeptr)
|
|
|
|
|
|
+ if(!rangeptr) {
|
|
|
|
+ UA_Variant old_value = node->value.data.value.value;
|
|
retval = UA_DataValue_copy(&editableValue, &node->value.data.value);
|
|
retval = UA_DataValue_copy(&editableValue, &node->value.data.value);
|
|
- else {
|
|
|
|
|
|
+ if(retval == UA_STATUSCODE_GOOD)
|
|
|
|
+ UA_Variant_deleteMembers(&old_value);
|
|
|
|
+ else
|
|
|
|
+ node->value.data.value.value = old_value;
|
|
|
|
+ } else {
|
|
if(!node->value.data.value.hasValue || !editableValue.hasValue) {
|
|
if(!node->value.data.value.hasValue || !editableValue.hasValue) {
|
|
retval = UA_STATUSCODE_BADINDEXRANGEINVALID;
|
|
retval = UA_STATUSCODE_BADINDEXRANGEINVALID;
|
|
goto cleanup;
|
|
goto cleanup;
|
|
@@ -745,8 +750,11 @@ UA_VariableNode_setValue(UA_Server *server, UA_VariableNode *node,
|
|
&node->value.data.value.value, rangeptr);
|
|
&node->value.data.value.value, rangeptr);
|
|
} else {
|
|
} else {
|
|
/* TODO: Don't make a copy of the node in the multithreaded case */
|
|
/* TODO: Don't make a copy of the node in the multithreaded case */
|
|
- retval = node->value.dataSource.write(node->value.dataSource.handle, node->nodeId,
|
|
|
|
- &value->value, rangeptr);
|
|
|
|
|
|
+ if(node->value.dataSource.write)
|
|
|
|
+ retval = node->value.dataSource.write(node->value.dataSource.handle, node->nodeId,
|
|
|
|
+ &value->value, rangeptr);
|
|
|
|
+ else
|
|
|
|
+ retval = UA_STATUSCODE_BADWRITENOTSUPPORTED;
|
|
}
|
|
}
|
|
|
|
|
|
cleanup:
|
|
cleanup:
|
|
@@ -777,7 +785,7 @@ writeIsAbstractAttribute(UA_Node *node, UA_Boolean value) {
|
|
return UA_STATUSCODE_GOOD;
|
|
return UA_STATUSCODE_GOOD;
|
|
}
|
|
}
|
|
|
|
|
|
-#define CHECK_DATATYPE(EXP_DT) \
|
|
|
|
|
|
+#define CHECK_DATATYPE_SCALAR(EXP_DT) \
|
|
if(!wvalue->value.hasValue || \
|
|
if(!wvalue->value.hasValue || \
|
|
&UA_TYPES[UA_TYPES_##EXP_DT] != wvalue->value.value.type || \
|
|
&UA_TYPES[UA_TYPES_##EXP_DT] != wvalue->value.value.type || \
|
|
!UA_Variant_isScalar(&wvalue->value.value)) { \
|
|
!UA_Variant_isScalar(&wvalue->value.value)) { \
|
|
@@ -785,6 +793,14 @@ writeIsAbstractAttribute(UA_Node *node, UA_Boolean value) {
|
|
break; \
|
|
break; \
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+#define CHECK_DATATYPE_ARRAY(EXP_DT) \
|
|
|
|
+ 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; \
|
|
|
|
+ }
|
|
|
|
+
|
|
#define CHECK_NODECLASS_WRITE(CLASS) \
|
|
#define CHECK_NODECLASS_WRITE(CLASS) \
|
|
if((node->nodeClass & (CLASS)) == 0) { \
|
|
if((node->nodeClass & (CLASS)) == 0) { \
|
|
retval = UA_STATUSCODE_BADNODECLASSINVALID; \
|
|
retval = UA_STATUSCODE_BADNODECLASSINVALID; \
|
|
@@ -803,54 +819,54 @@ CopyAttributeIntoNode(UA_Server *server, UA_Session *session,
|
|
switch(wvalue->attributeId) {
|
|
switch(wvalue->attributeId) {
|
|
case UA_ATTRIBUTEID_NODEID:
|
|
case UA_ATTRIBUTEID_NODEID:
|
|
case UA_ATTRIBUTEID_NODECLASS:
|
|
case UA_ATTRIBUTEID_NODECLASS:
|
|
- retval = UA_STATUSCODE_BADNOTWRITABLE;
|
|
|
|
|
|
+ retval = UA_STATUSCODE_BADWRITENOTSUPPORTED;
|
|
break;
|
|
break;
|
|
case UA_ATTRIBUTEID_BROWSENAME:
|
|
case UA_ATTRIBUTEID_BROWSENAME:
|
|
- CHECK_DATATYPE(QUALIFIEDNAME);
|
|
|
|
|
|
+ CHECK_DATATYPE_SCALAR(QUALIFIEDNAME);
|
|
UA_QualifiedName_deleteMembers(&node->browseName);
|
|
UA_QualifiedName_deleteMembers(&node->browseName);
|
|
UA_QualifiedName_copy(value, &node->browseName);
|
|
UA_QualifiedName_copy(value, &node->browseName);
|
|
break;
|
|
break;
|
|
case UA_ATTRIBUTEID_DISPLAYNAME:
|
|
case UA_ATTRIBUTEID_DISPLAYNAME:
|
|
- CHECK_DATATYPE(LOCALIZEDTEXT);
|
|
|
|
|
|
+ CHECK_DATATYPE_SCALAR(LOCALIZEDTEXT);
|
|
UA_LocalizedText_deleteMembers(&node->displayName);
|
|
UA_LocalizedText_deleteMembers(&node->displayName);
|
|
UA_LocalizedText_copy(value, &node->displayName);
|
|
UA_LocalizedText_copy(value, &node->displayName);
|
|
break;
|
|
break;
|
|
case UA_ATTRIBUTEID_DESCRIPTION:
|
|
case UA_ATTRIBUTEID_DESCRIPTION:
|
|
- CHECK_DATATYPE(LOCALIZEDTEXT);
|
|
|
|
|
|
+ CHECK_DATATYPE_SCALAR(LOCALIZEDTEXT);
|
|
UA_LocalizedText_deleteMembers(&node->description);
|
|
UA_LocalizedText_deleteMembers(&node->description);
|
|
UA_LocalizedText_copy(value, &node->description);
|
|
UA_LocalizedText_copy(value, &node->description);
|
|
break;
|
|
break;
|
|
case UA_ATTRIBUTEID_WRITEMASK:
|
|
case UA_ATTRIBUTEID_WRITEMASK:
|
|
- CHECK_DATATYPE(UINT32);
|
|
|
|
|
|
+ CHECK_DATATYPE_SCALAR(UINT32);
|
|
node->writeMask = *(const UA_UInt32*)value;
|
|
node->writeMask = *(const UA_UInt32*)value;
|
|
break;
|
|
break;
|
|
case UA_ATTRIBUTEID_USERWRITEMASK:
|
|
case UA_ATTRIBUTEID_USERWRITEMASK:
|
|
- CHECK_DATATYPE(UINT32);
|
|
|
|
|
|
+ CHECK_DATATYPE_SCALAR(UINT32);
|
|
node->userWriteMask = *(const UA_UInt32*)value;
|
|
node->userWriteMask = *(const UA_UInt32*)value;
|
|
break;
|
|
break;
|
|
case UA_ATTRIBUTEID_ISABSTRACT:
|
|
case UA_ATTRIBUTEID_ISABSTRACT:
|
|
- CHECK_DATATYPE(BOOLEAN);
|
|
|
|
|
|
+ CHECK_DATATYPE_SCALAR(BOOLEAN);
|
|
retval = writeIsAbstractAttribute(node, *(const UA_Boolean*)value);
|
|
retval = writeIsAbstractAttribute(node, *(const UA_Boolean*)value);
|
|
break;
|
|
break;
|
|
case UA_ATTRIBUTEID_SYMMETRIC:
|
|
case UA_ATTRIBUTEID_SYMMETRIC:
|
|
CHECK_NODECLASS_WRITE(UA_NODECLASS_REFERENCETYPE);
|
|
CHECK_NODECLASS_WRITE(UA_NODECLASS_REFERENCETYPE);
|
|
- CHECK_DATATYPE(BOOLEAN);
|
|
|
|
|
|
+ CHECK_DATATYPE_SCALAR(BOOLEAN);
|
|
((UA_ReferenceTypeNode*)node)->symmetric = *(const UA_Boolean*)value;
|
|
((UA_ReferenceTypeNode*)node)->symmetric = *(const UA_Boolean*)value;
|
|
break;
|
|
break;
|
|
case UA_ATTRIBUTEID_INVERSENAME:
|
|
case UA_ATTRIBUTEID_INVERSENAME:
|
|
CHECK_NODECLASS_WRITE(UA_NODECLASS_REFERENCETYPE);
|
|
CHECK_NODECLASS_WRITE(UA_NODECLASS_REFERENCETYPE);
|
|
- CHECK_DATATYPE(LOCALIZEDTEXT);
|
|
|
|
|
|
+ CHECK_DATATYPE_SCALAR(LOCALIZEDTEXT);
|
|
UA_LocalizedText_deleteMembers(&((UA_ReferenceTypeNode*)node)->inverseName);
|
|
UA_LocalizedText_deleteMembers(&((UA_ReferenceTypeNode*)node)->inverseName);
|
|
UA_LocalizedText_copy(value, &((UA_ReferenceTypeNode*)node)->inverseName);
|
|
UA_LocalizedText_copy(value, &((UA_ReferenceTypeNode*)node)->inverseName);
|
|
break;
|
|
break;
|
|
case UA_ATTRIBUTEID_CONTAINSNOLOOPS:
|
|
case UA_ATTRIBUTEID_CONTAINSNOLOOPS:
|
|
CHECK_NODECLASS_WRITE(UA_NODECLASS_VIEW);
|
|
CHECK_NODECLASS_WRITE(UA_NODECLASS_VIEW);
|
|
- CHECK_DATATYPE(BOOLEAN);
|
|
|
|
|
|
+ CHECK_DATATYPE_SCALAR(BOOLEAN);
|
|
((UA_ViewNode*)node)->containsNoLoops = *(const UA_Boolean*)value;
|
|
((UA_ViewNode*)node)->containsNoLoops = *(const UA_Boolean*)value;
|
|
break;
|
|
break;
|
|
case UA_ATTRIBUTEID_EVENTNOTIFIER:
|
|
case UA_ATTRIBUTEID_EVENTNOTIFIER:
|
|
CHECK_NODECLASS_WRITE(UA_NODECLASS_VIEW | UA_NODECLASS_OBJECT);
|
|
CHECK_NODECLASS_WRITE(UA_NODECLASS_VIEW | UA_NODECLASS_OBJECT);
|
|
- CHECK_DATATYPE(BYTE);
|
|
|
|
|
|
+ CHECK_DATATYPE_SCALAR(BYTE);
|
|
((UA_ViewNode*)node)->eventNotifier = *(const UA_Byte*)value;
|
|
((UA_ViewNode*)node)->eventNotifier = *(const UA_Byte*)value;
|
|
break;
|
|
break;
|
|
case UA_ATTRIBUTEID_VALUE:
|
|
case UA_ATTRIBUTEID_VALUE:
|
|
@@ -860,7 +876,7 @@ CopyAttributeIntoNode(UA_Server *server, UA_Session *session,
|
|
break;
|
|
break;
|
|
case UA_ATTRIBUTEID_DATATYPE: {
|
|
case UA_ATTRIBUTEID_DATATYPE: {
|
|
CHECK_NODECLASS_WRITE(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE);
|
|
CHECK_NODECLASS_WRITE(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE);
|
|
- CHECK_DATATYPE(NODEID);
|
|
|
|
|
|
+ CHECK_DATATYPE_SCALAR(NODEID);
|
|
const UA_VariableTypeNode *vt = getVariableNodeType(server, (UA_VariableNode*)node);
|
|
const UA_VariableTypeNode *vt = getVariableNodeType(server, (UA_VariableNode*)node);
|
|
if(vt)
|
|
if(vt)
|
|
retval = UA_VariableNode_setDataType(server, (UA_VariableNode*)node,
|
|
retval = UA_VariableNode_setDataType(server, (UA_VariableNode*)node,
|
|
@@ -870,7 +886,7 @@ CopyAttributeIntoNode(UA_Server *server, UA_Session *session,
|
|
break; }
|
|
break; }
|
|
case UA_ATTRIBUTEID_VALUERANK: {
|
|
case UA_ATTRIBUTEID_VALUERANK: {
|
|
CHECK_NODECLASS_WRITE(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE);
|
|
CHECK_NODECLASS_WRITE(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE);
|
|
- CHECK_DATATYPE(INT32);
|
|
|
|
|
|
+ CHECK_DATATYPE_SCALAR(INT32);
|
|
const UA_VariableTypeNode *vt = getVariableNodeType(server, (UA_VariableNode*)node);
|
|
const UA_VariableTypeNode *vt = getVariableNodeType(server, (UA_VariableNode*)node);
|
|
if(vt)
|
|
if(vt)
|
|
retval = UA_VariableNode_setValueRank(server, (UA_VariableNode*)node,
|
|
retval = UA_VariableNode_setValueRank(server, (UA_VariableNode*)node,
|
|
@@ -880,8 +896,7 @@ CopyAttributeIntoNode(UA_Server *server, UA_Session *session,
|
|
break; }
|
|
break; }
|
|
case UA_ATTRIBUTEID_ARRAYDIMENSIONS:
|
|
case UA_ATTRIBUTEID_ARRAYDIMENSIONS:
|
|
CHECK_NODECLASS_WRITE(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE);
|
|
CHECK_NODECLASS_WRITE(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE);
|
|
- CHECK_DATATYPE(UINT32);
|
|
|
|
- if(!UA_Variant_isScalar(&wvalue->value.value)) {
|
|
|
|
|
|
+ CHECK_DATATYPE_ARRAY(UINT32); {
|
|
const UA_VariableTypeNode *vt = getVariableNodeType(server, (UA_VariableNode*)node);
|
|
const UA_VariableTypeNode *vt = getVariableNodeType(server, (UA_VariableNode*)node);
|
|
if(vt)
|
|
if(vt)
|
|
retval = UA_VariableNode_setArrayDimensions(server, (UA_VariableNode*)node,
|
|
retval = UA_VariableNode_setArrayDimensions(server, (UA_VariableNode*)node,
|
|
@@ -889,38 +904,36 @@ CopyAttributeIntoNode(UA_Server *server, UA_Session *session,
|
|
wvalue->value.value.data);
|
|
wvalue->value.value.data);
|
|
else
|
|
else
|
|
retval = UA_STATUSCODE_BADINTERNALERROR;
|
|
retval = UA_STATUSCODE_BADINTERNALERROR;
|
|
- } else {
|
|
|
|
- retval = UA_STATUSCODE_BADTYPEMISMATCH;
|
|
|
|
}
|
|
}
|
|
break;
|
|
break;
|
|
case UA_ATTRIBUTEID_ACCESSLEVEL:
|
|
case UA_ATTRIBUTEID_ACCESSLEVEL:
|
|
CHECK_NODECLASS_WRITE(UA_NODECLASS_VARIABLE);
|
|
CHECK_NODECLASS_WRITE(UA_NODECLASS_VARIABLE);
|
|
- CHECK_DATATYPE(BYTE);
|
|
|
|
|
|
+ CHECK_DATATYPE_SCALAR(BYTE);
|
|
((UA_VariableNode*)node)->accessLevel = *(const UA_Byte*)value;
|
|
((UA_VariableNode*)node)->accessLevel = *(const UA_Byte*)value;
|
|
break;
|
|
break;
|
|
case UA_ATTRIBUTEID_USERACCESSLEVEL:
|
|
case UA_ATTRIBUTEID_USERACCESSLEVEL:
|
|
CHECK_NODECLASS_WRITE(UA_NODECLASS_VARIABLE);
|
|
CHECK_NODECLASS_WRITE(UA_NODECLASS_VARIABLE);
|
|
- CHECK_DATATYPE(BYTE);
|
|
|
|
|
|
+ CHECK_DATATYPE_SCALAR(BYTE);
|
|
((UA_VariableNode*)node)->userAccessLevel = *(const UA_Byte*)value;
|
|
((UA_VariableNode*)node)->userAccessLevel = *(const UA_Byte*)value;
|
|
break;
|
|
break;
|
|
case UA_ATTRIBUTEID_MINIMUMSAMPLINGINTERVAL:
|
|
case UA_ATTRIBUTEID_MINIMUMSAMPLINGINTERVAL:
|
|
CHECK_NODECLASS_WRITE(UA_NODECLASS_VARIABLE);
|
|
CHECK_NODECLASS_WRITE(UA_NODECLASS_VARIABLE);
|
|
- CHECK_DATATYPE(DOUBLE);
|
|
|
|
|
|
+ CHECK_DATATYPE_SCALAR(DOUBLE);
|
|
((UA_VariableNode*)node)->minimumSamplingInterval = *(const UA_Double*)value;
|
|
((UA_VariableNode*)node)->minimumSamplingInterval = *(const UA_Double*)value;
|
|
break;
|
|
break;
|
|
case UA_ATTRIBUTEID_HISTORIZING:
|
|
case UA_ATTRIBUTEID_HISTORIZING:
|
|
CHECK_NODECLASS_WRITE(UA_NODECLASS_VARIABLE);
|
|
CHECK_NODECLASS_WRITE(UA_NODECLASS_VARIABLE);
|
|
- CHECK_DATATYPE(BOOLEAN);
|
|
|
|
|
|
+ CHECK_DATATYPE_SCALAR(BOOLEAN);
|
|
((UA_VariableNode*)node)->historizing = *(const UA_Boolean*)value;
|
|
((UA_VariableNode*)node)->historizing = *(const UA_Boolean*)value;
|
|
break;
|
|
break;
|
|
case UA_ATTRIBUTEID_EXECUTABLE:
|
|
case UA_ATTRIBUTEID_EXECUTABLE:
|
|
CHECK_NODECLASS_WRITE(UA_NODECLASS_METHOD);
|
|
CHECK_NODECLASS_WRITE(UA_NODECLASS_METHOD);
|
|
- CHECK_DATATYPE(BOOLEAN);
|
|
|
|
|
|
+ CHECK_DATATYPE_SCALAR(BOOLEAN);
|
|
((UA_MethodNode*)node)->executable = *(const UA_Boolean*)value;
|
|
((UA_MethodNode*)node)->executable = *(const UA_Boolean*)value;
|
|
break;
|
|
break;
|
|
case UA_ATTRIBUTEID_USEREXECUTABLE:
|
|
case UA_ATTRIBUTEID_USEREXECUTABLE:
|
|
CHECK_NODECLASS_WRITE(UA_NODECLASS_METHOD);
|
|
CHECK_NODECLASS_WRITE(UA_NODECLASS_METHOD);
|
|
- CHECK_DATATYPE(BOOLEAN);
|
|
|
|
|
|
+ CHECK_DATATYPE_SCALAR(BOOLEAN);
|
|
((UA_MethodNode*)node)->userExecutable = *(const UA_Boolean*)value;
|
|
((UA_MethodNode*)node)->userExecutable = *(const UA_Boolean*)value;
|
|
break;
|
|
break;
|
|
default:
|
|
default:
|