UA_StatusCode __UA_Client_writeAttribute(UA_Client *client, UA_NodeId nodeId, UA_AttributeId attributeId, void *in, const UA_DataType *inDataType) { if(in == NULL) return UA_STATUSCODE_BADTYPEMISMATCH; UA_Variant *tmp = (UA_Variant *) in; if (tmp == NULL) return 1; UA_WriteRequest *wReq = UA_WriteRequest_new(); wReq->nodesToWrite = UA_WriteValue_new(); wReq->nodesToWriteSize = 1; UA_NodeId_copy(&nodeId, &wReq->nodesToWrite[0].nodeId); wReq->nodesToWrite[0].attributeId = attributeId; if (attributeId == UA_ATTRIBUTEID_VALUE) { UA_Variant_copy((UA_Variant *) in, &wReq->nodesToWrite[0].value.value); wReq->nodesToWrite[0].value.hasValue = true; } else { if( ! UA_Variant_setScalarCopy(&wReq->nodesToWrite[0].value.value, in, inDataType) ) wReq->nodesToWrite[0].value.hasValue = true; } UA_WriteResponse wResp = UA_Client_Service_write(client, *wReq); UA_StatusCode retval = wResp.responseHeader.serviceResult; UA_WriteRequest_delete(wReq); UA_WriteResponse_deleteMembers(&wResp); return retval; }
static void variables_variants(void) { /* Set a scalar value */ UA_Variant v; UA_Int32 i = 42; UA_Variant_setScalar(&v, &i, &UA_TYPES[UA_TYPES_INT32]); /* Make a copy */ UA_Variant v2; UA_Variant_copy(&v, &v2); UA_Variant_deleteMembers(&v2); /* Set an array value */ UA_Variant v3; UA_Double d[9] = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0}; UA_Variant_setArrayCopy(&v3, d, 9, &UA_TYPES[UA_TYPES_DOUBLE]); /* Set array dimensions */ v3.arrayDimensions = (UA_UInt32 *)UA_Array_new(2, &UA_TYPES[UA_TYPES_UINT32]); v3.arrayDimensionsSize = 2; v3.arrayDimensions[0] = 3; v3.arrayDimensions[1] = 3; UA_Variant_deleteMembers(&v3); }
static UA_StatusCode 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); /* Parse the range */ UA_NumericRange range; UA_NumericRange *rangeptr = NULL; UA_StatusCode retval = UA_STATUSCODE_GOOD; if(wvalue->indexRange.length > 0) { retval = parse_numericrange(&wvalue->indexRange, &range); if(retval != UA_STATUSCODE_GOOD) return retval; rangeptr = ⦥ } /* The nodeid on the wire may be != the nodeid in the node: opaque types, enums and bytestrings. nodeV contains the correct type definition. */ const UA_Variant *newV = &wvalue->value.value; UA_Variant *oldV = &node->value.variant.value; UA_Variant cast_v; if (oldV->type != NULL) { // Don't run NodeId_equal on a NULL pointer (happens if the variable never held a variant) if(!UA_NodeId_equal(&oldV->type->typeId, &newV->type->typeId)) { cast_v = wvalue->value.value; newV = &cast_v; enum type_equivalence te1 = typeEquivalence(oldV->type); enum type_equivalence te2 = typeEquivalence(newV->type); 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. */ 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; 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); return UA_STATUSCODE_BADTYPEMISMATCH; } } } if(!rangeptr) { UA_Variant_deleteMembers(&node->value.variant.value); 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); if(rangeptr) UA_free(range.dimensions); return retval; }
/* copy an existing variable under the given parent. then instantiate the variable for all hastypedefinitions of the original version. */ static UA_StatusCode copyExistingVariable(UA_Server *server, UA_Session *session, const UA_NodeId *variable, const UA_NodeId *referenceType, const UA_NodeId *parent) { const UA_VariableNode *node = (const UA_VariableNode*)UA_NodeStore_get(server->nodestore, variable); if(!node) return UA_STATUSCODE_BADNODEIDINVALID; if(node->nodeClass != UA_NODECLASS_VARIABLE) return UA_STATUSCODE_BADNODECLASSINVALID; // copy the variable attributes UA_VariableAttributes attr; UA_VariableAttributes_init(&attr); UA_LocalizedText_copy(&node->displayName, &attr.displayName); UA_LocalizedText_copy(&node->description, &attr.description); attr.writeMask = node->writeMask; attr.userWriteMask = node->userWriteMask; // todo: handle data sources!!!! UA_Variant_copy(&node->value.variant.value, &attr.value); // datatype is taken from the value // valuerank is taken from the value // array dimensions are taken from the value attr.accessLevel = node->accessLevel; attr.userAccessLevel = node->userAccessLevel; attr.minimumSamplingInterval = node->minimumSamplingInterval; attr.historizing = node->historizing; UA_AddNodesItem item; UA_AddNodesItem_init(&item); UA_NodeId_copy(parent, &item.parentNodeId.nodeId); UA_NodeId_copy(referenceType, &item.referenceTypeId); UA_QualifiedName_copy(&node->browseName, &item.browseName); item.nodeClass = UA_NODECLASS_VARIABLE; item.nodeAttributes.encoding = UA_EXTENSIONOBJECT_DECODED_NODELETE; item.nodeAttributes.content.decoded.type = &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES]; item.nodeAttributes.content.decoded.data = &attr; // don't add a typedefinition here. // add the new variable UA_AddNodesResult res; UA_AddNodesResult_init(&res); Service_AddNodes_single(server, session, &item, &res); UA_VariableAttributes_deleteMembers(&attr); UA_AddNodesItem_deleteMembers(&item); // now instantiate the variable for all hastypedefinition references for(size_t i = 0; i < node->referencesSize; i++) { UA_ReferenceNode *rn = &node->references[i]; if(rn->isInverse) continue; const UA_NodeId hasTypeDef = UA_NODEID_NUMERIC(0, UA_NS0ID_HASTYPEDEFINITION); if(!UA_NodeId_equal(&rn->referenceTypeId, &hasTypeDef)) continue; instantiateVariableNode(server, session, &res.addedNodeId, &rn->targetId.nodeId); } UA_AddNodesResult_deleteMembers(&res); return UA_STATUSCODE_GOOD; }
static UA_StatusCode UA_VariableTypeNode_copy(const UA_VariableTypeNode *src, UA_VariableTypeNode *dst) { dst->valueRank = src->valueRank; dst->valueSource = src->valueSource; if(src->valueSource == UA_VALUESOURCE_VARIANT){ UA_StatusCode retval = UA_Variant_copy(&src->value.variant.value, &dst->value.variant.value); if(retval != UA_STATUSCODE_GOOD) return retval; dst->value.variant.callback = src->value.variant.callback; } else dst->value.dataSource = src->value.dataSource; dst->isAbstract = src->isAbstract; return UA_STATUSCODE_GOOD; }
UA_StatusCode UA_VariableTypeNode_copy(const UA_VariableTypeNode *src, UA_VariableTypeNode *dst) { UA_VariableTypeNode_init(dst); UA_StatusCode retval = UA_Node_copy((const UA_Node*)src, (UA_Node*)dst); dst->valueRank = src->valueRank; dst->valueSource = src->valueSource; if(src->valueSource == UA_VALUESOURCE_VARIANT) UA_Variant_copy(&src->value.variant, &dst->value.variant); else dst->value.dataSource = src->value.dataSource; if(retval) { UA_VariableTypeNode_deleteMembers(dst); return retval; } dst->isAbstract = src->isAbstract; return UA_STATUSCODE_GOOD; }
static UA_Node * variableTypeNodeFromAttributes(const UA_AddNodesItem *item, const UA_VariableTypeAttributes *attr) { UA_VariableTypeNode *vtnode = UA_VariableTypeNode_new(); if(!vtnode) return NULL; UA_StatusCode retval = copyStandardAttributes((UA_Node*)vtnode, item, (const UA_NodeAttributes*)attr); UA_Variant_copy(&attr->value, &vtnode->value.variant.value); // datatype is taken from the value vtnode->valueRank = attr->valueRank; // array dimensions are taken from the value vtnode->isAbstract = attr->isAbstract; if(retval != UA_STATUSCODE_GOOD) { UA_Node_deleteAnyNodeClass((UA_Node*)vtnode); return NULL; } return (UA_Node*)vtnode; }
/* Used in inline functions exposing the Read service with more syntactic sugar * for individual attributes */ UA_StatusCode __UA_Server_read(UA_Server *server, const UA_NodeId *nodeId, const UA_AttributeId attributeId, void *v) { /* Call the read service */ UA_ReadValueId item; UA_ReadValueId_init(&item); item.nodeId = *nodeId; item.attributeId = attributeId; UA_DataValue dv = UA_Server_read(server, &item, UA_TIMESTAMPSTORETURN_NEITHER); /* Check the return value */ UA_StatusCode retval = UA_STATUSCODE_GOOD; if(dv.hasStatus) retval = dv.status; else if(!dv.hasValue) retval = UA_STATUSCODE_BADUNEXPECTEDERROR; if(retval != UA_STATUSCODE_GOOD) { UA_DataValue_deleteMembers(&dv); return retval; } /* Prepare the result */ if(attributeId == UA_ATTRIBUTEID_VALUE || attributeId == UA_ATTRIBUTEID_ARRAYDIMENSIONS) { /* Return the entire variant */ if(dv.value.storageType == UA_VARIANT_DATA_NODELETE) { retval = UA_Variant_copy(&dv.value,(UA_Variant *) v); } else { /* storageType is UA_VARIANT_DATA. Copy the entire variant * (including pointers and all) */ memcpy(v, &dv.value, sizeof(UA_Variant)); } } else { /* Return the variant content only */ if(dv.value.storageType == UA_VARIANT_DATA_NODELETE) { retval = UA_copy(dv.value.data, v, dv.value.type); } else { /* storageType is UA_VARIANT_DATA. Copy the content of the type * (including pointers and all) */ memcpy(v, dv.value.data, dv.value.type->memSize); /* Delete the "carrier" in the variant */ UA_free(dv.value.data); } } return retval; }
static UA_StatusCode UA_VariableNode_copy(const UA_VariableNode *src, UA_VariableNode *dst) { dst->valueRank = src->valueRank; dst->valueSource = src->valueSource; if(src->valueSource == UA_VALUESOURCE_VARIANT) { UA_StatusCode retval = UA_Variant_copy(&src->value.variant.value, &dst->value.variant.value); if(retval != UA_STATUSCODE_GOOD) return retval; dst->value.variant.callback = src->value.variant.callback; } else dst->value.dataSource = src->value.dataSource; dst->accessLevel = src->accessLevel; dst->userAccessLevel = src->accessLevel; dst->minimumSamplingInterval = src->minimumSamplingInterval; dst->historizing = src->historizing; return UA_STATUSCODE_GOOD; }
static UA_Node * variableNodeFromAttributes(const UA_AddNodesItem *item, const UA_VariableAttributes *attr) { UA_VariableNode *vnode = UA_VariableNode_new(); if(!vnode) return NULL; UA_StatusCode retval = copyStandardAttributes((UA_Node*)vnode, item, (const UA_NodeAttributes*)attr); // todo: test if the type / valueRank / value attributes are consistent vnode->accessLevel = attr->accessLevel; vnode->userAccessLevel = attr->userAccessLevel; vnode->historizing = attr->historizing; vnode->minimumSamplingInterval = attr->minimumSamplingInterval; vnode->valueRank = attr->valueRank; retval |= UA_Variant_copy(&attr->value, &vnode->value.variant.value); if(retval != UA_STATUSCODE_GOOD) { UA_Node_deleteAnyNodeClass((UA_Node*)vnode); return NULL; } return (UA_Node*)vnode; }
UA_StatusCode UA_VariableNode_copy(const UA_VariableNode *src, UA_VariableNode *dst) { UA_VariableNode_init(dst); UA_StatusCode retval = UA_Node_copy((const UA_Node*)src, (UA_Node*)dst); dst->valueRank = src->valueRank; dst->valueSource = src->valueSource; if(src->valueSource == UA_VALUESOURCE_VARIANT) retval = UA_Variant_copy(&src->value.variant, &dst->value.variant); else dst->value.dataSource = src->value.dataSource; if(retval) { UA_VariableNode_deleteMembers(dst); return retval; } dst->accessLevel = src->accessLevel; dst->userAccessLevel = src->accessLevel; dst->minimumSamplingInterval = src->minimumSamplingInterval; dst->historizing = src->historizing; return UA_STATUSCODE_GOOD; }
/** Reads a single attribute from a node in the nodestore. */ static void readValue(UA_Server *server, UA_TimestampsToReturn timestamps, const UA_ReadValueId *id, UA_DataValue *v) { if(id->dataEncoding.name.length >= 0){ if(memcmp(id->dataEncoding.name.data, "DefaultBinary", 13) != 0 && memcmp(id->dataEncoding.name.data, "DefaultXml", 10) != 0) { v->hasStatus = UA_TRUE; v->status = UA_STATUSCODE_BADDATAENCODINGINVALID; return; } } //index range for a non-value if(id->indexRange.length >= 0 && id->attributeId != UA_ATTRIBUTEID_VALUE){ v->hasStatus = UA_TRUE; v->status = UA_STATUSCODE_BADINDEXRANGENODATA; return; } UA_Node const *node = UA_NodeStore_get(server->nodestore, &(id->nodeId)); if(!node) { v->hasStatus = UA_TRUE; v->status = UA_STATUSCODE_BADNODEIDUNKNOWN; return; } UA_StatusCode retval = UA_STATUSCODE_GOOD; switch(id->attributeId) { case UA_ATTRIBUTEID_NODEID: v->hasValue = UA_TRUE; retval |= UA_Variant_setScalarCopy(&v->value, &node->nodeId, &UA_TYPES[UA_TYPES_NODEID]); break; case UA_ATTRIBUTEID_NODECLASS: v->hasValue = UA_TRUE; retval |= UA_Variant_setScalarCopy(&v->value, &node->nodeClass, &UA_TYPES[UA_TYPES_INT32]); break; case UA_ATTRIBUTEID_BROWSENAME: v->hasValue = UA_TRUE; retval |= UA_Variant_setScalarCopy(&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]); if(retval == UA_STATUSCODE_GOOD) v->hasValue = UA_TRUE; break; case UA_ATTRIBUTEID_DESCRIPTION: v->hasValue = UA_TRUE; retval |= UA_Variant_setScalarCopy(&v->value, &node->description, &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]); break; case UA_ATTRIBUTEID_WRITEMASK: v->hasValue = UA_TRUE; retval |= UA_Variant_setScalarCopy(&v->value, &node->writeMask, &UA_TYPES[UA_TYPES_UINT32]); break; case UA_ATTRIBUTEID_USERWRITEMASK: v->hasValue = UA_TRUE; retval |= UA_Variant_setScalarCopy(&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); v->hasValue = UA_TRUE; retval |= UA_Variant_setScalarCopy(&v->value, &((const UA_ReferenceTypeNode *)node)->isAbstract, &UA_TYPES[UA_TYPES_BOOLEAN]); break; case UA_ATTRIBUTEID_SYMMETRIC: CHECK_NODECLASS(UA_NODECLASS_REFERENCETYPE); v->hasValue = UA_TRUE; retval |= UA_Variant_setScalarCopy(&v->value, &((const UA_ReferenceTypeNode *)node)->symmetric, &UA_TYPES[UA_TYPES_BOOLEAN]); break; case UA_ATTRIBUTEID_INVERSENAME: CHECK_NODECLASS(UA_NODECLASS_REFERENCETYPE); v->hasValue = UA_TRUE; retval |= UA_Variant_setScalarCopy(&v->value, &((const UA_ReferenceTypeNode *)node)->inverseName, &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]); break; case UA_ATTRIBUTEID_CONTAINSNOLOOPS: CHECK_NODECLASS(UA_NODECLASS_VIEW); v->hasValue = UA_TRUE; retval |= UA_Variant_setScalarCopy(&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); v->hasValue = UA_TRUE; if(node->nodeClass == UA_NODECLASS_VIEW){ retval |= UA_Variant_setScalarCopy(&v->value, &((const UA_ViewNode *)node)->eventNotifier, &UA_TYPES[UA_TYPES_BYTE]); } else { retval |= UA_Variant_setScalarCopy(&v->value, &((const UA_ObjectNode *)node)->eventNotifier, &UA_TYPES[UA_TYPES_BYTE]); } break; case UA_ATTRIBUTEID_VALUE: CHECK_NODECLASS(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE); { if(node->nodeClass != UA_NODECLASS_VARIABLE) { v->hasValue = UA_FALSE; handleSourceTimestamps(timestamps, v); } UA_Boolean hasRange = UA_FALSE; UA_NumericRange range; if(id->indexRange.length > 0) { retval = parse_numericrange(id->indexRange, &range); if(retval != UA_STATUSCODE_GOOD) break; hasRange = UA_TRUE; } const UA_VariableNode *vn = (const UA_VariableNode*)node; if(vn->valueSource == UA_VALUESOURCE_VARIANT) { if(hasRange) retval |= UA_Variant_copyRange(&vn->value.variant, &v->value, range); else retval |= UA_Variant_copy(&vn->value.variant, &v->value); if(retval == UA_STATUSCODE_GOOD) { v->hasValue = UA_TRUE; handleSourceTimestamps(timestamps, v); } } else { UA_DataValue val; UA_DataValue_init(&val); UA_Boolean sourceTimeStamp = (timestamps == UA_TIMESTAMPSTORETURN_SOURCE || timestamps == UA_TIMESTAMPSTORETURN_BOTH); retval |= vn->value.dataSource.read(vn->value.dataSource.handle, sourceTimeStamp, &val); if(retval == UA_STATUSCODE_GOOD) { retval |= UA_DataValue_copy(&val, v); // todo: selection of indexranges } vn->value.dataSource.release(vn->value.dataSource.handle, &val); } if(hasRange) UA_free(range.dimensions); } break; case UA_ATTRIBUTEID_DATATYPE: { CHECK_NODECLASS(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE); const UA_VariableNode *vn = (const UA_VariableNode*)node; if(vn->valueSource == UA_VALUESOURCE_VARIANT) retval = UA_Variant_setScalarCopy(&v->value, &vn->value.variant.type->typeId, &UA_TYPES[UA_TYPES_NODEID]); else { UA_DataValue val; UA_DataValue_init(&val); retval = vn->value.dataSource.read(vn->value.dataSource.handle, UA_FALSE, &val); if(retval != UA_STATUSCODE_GOOD) break; retval = UA_Variant_setScalarCopy(&v->value, &val.value.type->typeId, &UA_TYPES[UA_TYPES_NODEID]); vn->value.dataSource.release(vn->value.dataSource.handle, &val); } if(retval == UA_STATUSCODE_GOOD) v->hasValue = UA_TRUE; } break; case UA_ATTRIBUTEID_VALUERANK: CHECK_NODECLASS(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE); v->hasValue = UA_TRUE; retval |= UA_Variant_setScalarCopy(&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); { const UA_VariableNode *vn = (const UA_VariableNode *)node; if(vn->valueSource == UA_VALUESOURCE_VARIANT) { retval = UA_Variant_setArrayCopy(&v->value, vn->value.variant.arrayDimensions, vn->value.variant.arrayDimensionsSize, &UA_TYPES[UA_TYPES_INT32]); if(retval == UA_STATUSCODE_GOOD) v->hasValue = UA_TRUE; } else { UA_DataValue val; UA_DataValue_init(&val); retval |= vn->value.dataSource.read(vn->value.dataSource.handle, UA_FALSE, &val); if(retval != UA_STATUSCODE_GOOD) break; if(!val.hasValue) retval = UA_STATUSCODE_BADNOTREADABLE; else retval = UA_Variant_setArrayCopy(&v->value, val.value.arrayDimensions, val.value.arrayDimensionsSize, &UA_TYPES[UA_TYPES_INT32]); vn->value.dataSource.release(vn->value.dataSource.handle, &val); } } break; case UA_ATTRIBUTEID_ACCESSLEVEL: CHECK_NODECLASS(UA_NODECLASS_VARIABLE); v->hasValue = UA_TRUE; retval |= UA_Variant_setScalarCopy(&v->value, &((const UA_VariableNode *)node)->accessLevel, &UA_TYPES[UA_TYPES_BYTE]); break; case UA_ATTRIBUTEID_USERACCESSLEVEL: CHECK_NODECLASS(UA_NODECLASS_VARIABLE); v->hasValue = UA_TRUE; retval |= UA_Variant_setScalarCopy(&v->value, &((const UA_VariableNode *)node)->userAccessLevel, &UA_TYPES[UA_TYPES_BYTE]); break; case UA_ATTRIBUTEID_MINIMUMSAMPLINGINTERVAL: CHECK_NODECLASS(UA_NODECLASS_VARIABLE); v->hasValue = UA_TRUE; retval |= UA_Variant_setScalarCopy(&v->value, &((const UA_VariableNode *)node)->minimumSamplingInterval, &UA_TYPES[UA_TYPES_DOUBLE]); break; case UA_ATTRIBUTEID_HISTORIZING: CHECK_NODECLASS(UA_NODECLASS_VARIABLE); v->hasValue = UA_TRUE; retval |= UA_Variant_setScalarCopy(&v->value, &((const UA_VariableNode *)node)->historizing, &UA_TYPES[UA_TYPES_BOOLEAN]); break; case UA_ATTRIBUTEID_EXECUTABLE: CHECK_NODECLASS(UA_NODECLASS_METHOD); v->hasValue = UA_TRUE; retval |= UA_Variant_setScalarCopy(&v->value, &((const UA_MethodNode *)node)->executable, &UA_TYPES[UA_TYPES_BOOLEAN]); break; case UA_ATTRIBUTEID_USEREXECUTABLE: CHECK_NODECLASS(UA_NODECLASS_METHOD); v->hasValue = UA_TRUE; retval |= UA_Variant_setScalarCopy(&v->value, &((const UA_MethodNode *)node)->userExecutable, &UA_TYPES[UA_TYPES_BOOLEAN]); break; default: v->hasStatus = UA_TRUE; v->status = UA_STATUSCODE_BADATTRIBUTEIDINVALID; break; } UA_NodeStore_release(node); if(retval != UA_STATUSCODE_GOOD) { v->hasStatus = UA_TRUE; v->status = retval; } handleServerTimestamps(timestamps, v); }
static 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; 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_NODEID: case UA_ATTRIBUTEID_NODECLASS: case UA_ATTRIBUTEID_BROWSENAME: case UA_ATTRIBUTEID_DISPLAYNAME: case UA_ATTRIBUTEID_DESCRIPTION: case UA_ATTRIBUTEID_WRITEMASK: case UA_ATTRIBUTEID_USERWRITEMASK: case UA_ATTRIBUTEID_ISABSTRACT: case UA_ATTRIBUTEID_SYMMETRIC: case UA_ATTRIBUTEID_INVERSENAME: case UA_ATTRIBUTEID_CONTAINSNOLOOPS: case UA_ATTRIBUTEID_EVENTNOTIFIER: retval = UA_STATUSCODE_BADWRITENOTSUPPORTED; break; 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; break; } // todo: writing ranges retval = vn->value.dataSource.write(vn->value.dataSource.handle, &wvalue->value.value); done = UA_TRUE; break; } 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] && (!oldV->data || vn->value.variant.arrayLength > -1) /* isArray */ && wvalue->value.value.type == &UA_TYPES[UA_TYPES_BYTESTRING] && wvalue->value.value.data && wvalue->value.value.arrayLength == -1 /* isScalar */) { /* 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; break; } } /* 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; break; } 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_setRange(&newVn->value.variant, wvalue->value.value.data, range); else { UA_Variant_deleteMembers(&newVn->value.variant); retval = UA_Variant_copy(&wvalue->value.value, &newVn->value.variant); } if(retval != UA_STATUSCODE_GOOD || UA_NodeStore_replace(server->nodestore, node, (UA_Node*)newVn, UA_NULL) != UA_STATUSCODE_GOOD) goto clean_up; if(hasRange) UA_free(range.dimensions); done = UA_TRUE; break; clean_up: if(node->nodeClass == UA_NODECLASS_VARIABLE) UA_VariableNode_delete(newVn); else UA_VariableTypeNode_delete((UA_VariableTypeNode*)newVn); if(hasRange) UA_free(range.dimensions); } break; case UA_ATTRIBUTEID_DATATYPE: case UA_ATTRIBUTEID_VALUERANK: case UA_ATTRIBUTEID_ARRAYDIMENSIONS: case UA_ATTRIBUTEID_ACCESSLEVEL: case UA_ATTRIBUTEID_USERACCESSLEVEL: case UA_ATTRIBUTEID_MINIMUMSAMPLINGINTERVAL: case UA_ATTRIBUTEID_HISTORIZING: case UA_ATTRIBUTEID_EXECUTABLE: case UA_ATTRIBUTEID_USEREXECUTABLE: retval = UA_STATUSCODE_BADWRITENOTSUPPORTED; break; default: retval = UA_STATUSCODE_BADATTRIBUTEIDINVALID; break; } UA_NodeStore_release(node); if(retval != UA_STATUSCODE_GOOD) break; } return retval; }
/* movedValue returns whether the sample was moved to the notification. The * default is false. */ static UA_StatusCode sampleCallbackWithValue(UA_Server *server, UA_Session *session, UA_Subscription *sub, UA_MonitoredItem *mon, UA_DataValue *value, UA_Boolean *movedValue) { UA_assert(mon->attributeId != UA_ATTRIBUTEID_EVENTNOTIFIER); /* Contains heap-allocated binary encoding of the value if a change was detected */ UA_ByteString binValueEncoding = UA_BYTESTRING_NULL; /* Has the value changed? Allocates memory in binValueEncoding if necessary. * value is edited internally so we make a shallow copy. */ UA_Boolean changed = false; UA_StatusCode retval = detectValueChange(server, mon, *value, &binValueEncoding, &changed); if(retval != UA_STATUSCODE_GOOD) { UA_LOG_WARNING_SESSION(&server->config.logger, session, "Subscription %u | " "MonitoredItem %i | Value change detection failed with StatusCode %s", sub ? sub->subscriptionId : 0, mon->monitoredItemId, UA_StatusCode_name(retval)); return retval; } if(!changed) { UA_LOG_DEBUG_SESSION(&server->config.logger, session, "Subscription %u | " "MonitoredItem %i | The value has not changed", sub ? sub->subscriptionId : 0, mon->monitoredItemId); return UA_STATUSCODE_GOOD; } /* The MonitoredItem is attached to a subscription (not server-local). * Prepare a notification and enqueue it. */ if(sub) { /* Allocate a new notification */ UA_Notification *newNotification = (UA_Notification *)UA_malloc(sizeof(UA_Notification)); if(!newNotification) { UA_ByteString_deleteMembers(&binValueEncoding); return UA_STATUSCODE_BADOUTOFMEMORY; } if(value->value.storageType == UA_VARIANT_DATA) { newNotification->data.value = *value; /* Move the value to the notification */ *movedValue = true; } else { /* => (value->value.storageType == UA_VARIANT_DATA_NODELETE) */ retval = UA_DataValue_copy(value, &newNotification->data.value); if(retval != UA_STATUSCODE_GOOD) { UA_ByteString_deleteMembers(&binValueEncoding); UA_free(newNotification); return retval; } } /* <-- Point of no return --> */ UA_LOG_DEBUG_SESSION(&server->config.logger, session, "Subscription %u | " "MonitoredItem %i | Enqueue a new notification", sub ? sub->subscriptionId : 0, mon->monitoredItemId); newNotification->mon = mon; UA_Notification_enqueue(server, sub, mon, newNotification); } /* Store the encoding for comparison */ UA_ByteString_deleteMembers(&mon->lastSampledValue); mon->lastSampledValue = binValueEncoding; /* Store the value for filter comparison (we don't want to decode * lastSampledValue in every iteration). Don't test the return code here. If * this fails, lastValue is empty and a notification will be forced for the * next deadband comparison. */ if((mon->filter.dataChangeFilter.deadbandType == UA_DEADBANDTYPE_NONE || mon->filter.dataChangeFilter.deadbandType == UA_DEADBANDTYPE_ABSOLUTE || mon->filter.dataChangeFilter.deadbandType == UA_DEADBANDTYPE_PERCENT) && (mon->filter.dataChangeFilter.trigger == UA_DATACHANGETRIGGER_STATUS || mon->filter.dataChangeFilter.trigger == UA_DATACHANGETRIGGER_STATUSVALUE || mon->filter.dataChangeFilter.trigger == UA_DATACHANGETRIGGER_STATUSVALUETIMESTAMP)) { UA_Variant_deleteMembers(&mon->lastValue); UA_Variant_copy(&value->value, &mon->lastValue); #ifdef UA_ENABLE_DA UA_StatusCode_deleteMembers(&mon->lastStatus); UA_StatusCode_copy(&value->status, &mon->lastStatus); #endif } /* Call the local callback if the MonitoredItem is not attached to a * subscription. Do this at the very end. Because the callback might delete * the subscription. */ if(!sub) { UA_LocalMonitoredItem *localMon = (UA_LocalMonitoredItem*) mon; void *nodeContext = NULL; UA_Server_getNodeContext(server, mon->monitoredNodeId, &nodeContext); localMon->callback.dataChangeCallback(server, mon->monitoredItemId, localMon->context, &mon->monitoredNodeId, nodeContext, mon->attributeId, value); } return UA_STATUSCODE_GOOD; }
static UA_DataValue service_read_node(UA_Server *server, const UA_ReadValueId *id) { UA_DataValue v; UA_DataValue_init(&v); UA_Node const *node = UA_NULL; UA_Int32 result = UA_NodeStore_get(server->nodestore, &(id->nodeId), &node); if(result != UA_STATUSCODE_GOOD || node == UA_NULL) { v.encodingMask = UA_DATAVALUE_ENCODINGMASK_STATUSCODE; v.status = UA_STATUSCODE_BADNODEIDUNKNOWN; return v; } UA_StatusCode retval = UA_STATUSCODE_GOOD; switch(id->attributeId) { case UA_ATTRIBUTEID_NODEID: v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT; retval |= UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_NODEID], &node->nodeId); break; case UA_ATTRIBUTEID_NODECLASS: v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT; retval |= UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_INT32], &node->nodeClass); break; case UA_ATTRIBUTEID_BROWSENAME: v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT; retval |= UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_QUALIFIEDNAME], &node->browseName); break; case UA_ATTRIBUTEID_DISPLAYNAME: v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT; retval |= UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_LOCALIZEDTEXT], &node->displayName); break; case UA_ATTRIBUTEID_DESCRIPTION: v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT; retval |= UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_LOCALIZEDTEXT], &node->description); break; case UA_ATTRIBUTEID_WRITEMASK: v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT; retval |= UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_UINT32], &node->writeMask); break; case UA_ATTRIBUTEID_USERWRITEMASK: v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT; retval |= UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_UINT32], &node->userWriteMask); break; case UA_ATTRIBUTEID_ISABSTRACT: CHECK_NODECLASS( UA_NODECLASS_REFERENCETYPE | UA_NODECLASS_OBJECTTYPE | UA_NODECLASS_VARIABLETYPE | UA_NODECLASS_DATATYPE); v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT; retval |= UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_BOOLEAN], &((UA_ReferenceTypeNode *)node)->isAbstract); break; case UA_ATTRIBUTEID_SYMMETRIC: CHECK_NODECLASS(UA_NODECLASS_REFERENCETYPE); v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT; retval |= UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_BOOLEAN], &((UA_ReferenceTypeNode *)node)->symmetric); break; case UA_ATTRIBUTEID_INVERSENAME: CHECK_NODECLASS(UA_NODECLASS_REFERENCETYPE); v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT; retval |= UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_LOCALIZEDTEXT], &((UA_ReferenceTypeNode *)node)->inverseName); break; case UA_ATTRIBUTEID_CONTAINSNOLOOPS: CHECK_NODECLASS(UA_NODECLASS_VIEW); v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT; retval |= UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_BOOLEAN], &((UA_ViewNode *)node)->containsNoLoops); break; case UA_ATTRIBUTEID_EVENTNOTIFIER: CHECK_NODECLASS(UA_NODECLASS_VIEW | UA_NODECLASS_OBJECT); v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT; retval |= UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_BYTE], &((UA_ViewNode *)node)->eventNotifier); break; case UA_ATTRIBUTEID_VALUE: CHECK_NODECLASS(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE); v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT; retval |= UA_Variant_copy(&((UA_VariableNode *)node)->value, &v.value); // todo: zero-copy break; case UA_ATTRIBUTEID_DATATYPE: CHECK_NODECLASS(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE); v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT; retval |= UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_NODEID], &((UA_VariableTypeNode *)node)->dataType); break; case UA_ATTRIBUTEID_VALUERANK: CHECK_NODECLASS(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE); v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT; retval |= UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_INT32], &((UA_VariableTypeNode *)node)->valueRank); break; case UA_ATTRIBUTEID_ARRAYDIMENSIONS: CHECK_NODECLASS(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE); v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT; UA_Variant_copySetArray(&v.value, &UA_TYPES[UA_UINT32], ((UA_VariableTypeNode *)node)->arrayDimensionsSize, &((UA_VariableTypeNode *)node)->arrayDimensions); break; case UA_ATTRIBUTEID_ACCESSLEVEL: CHECK_NODECLASS(UA_NODECLASS_VARIABLE); v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT; retval |= UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_BYTE], &((UA_VariableNode *)node)->accessLevel); break; case UA_ATTRIBUTEID_USERACCESSLEVEL: CHECK_NODECLASS(UA_NODECLASS_VARIABLE); v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT; retval |= UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_BYTE], &((UA_VariableNode *)node)->userAccessLevel); break; case UA_ATTRIBUTEID_MINIMUMSAMPLINGINTERVAL: CHECK_NODECLASS(UA_NODECLASS_VARIABLE); v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT; retval |= UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_DOUBLE], &((UA_VariableNode *)node)->minimumSamplingInterval); break; case UA_ATTRIBUTEID_HISTORIZING: CHECK_NODECLASS(UA_NODECLASS_VARIABLE); v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT; retval |= UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_BOOLEAN], &((UA_VariableNode *)node)->historizing); break; case UA_ATTRIBUTEID_EXECUTABLE: CHECK_NODECLASS(UA_NODECLASS_METHOD); v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT; retval |= UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_BOOLEAN], &((UA_MethodNode *)node)->executable); break; case UA_ATTRIBUTEID_USEREXECUTABLE: CHECK_NODECLASS(UA_NODECLASS_METHOD); v.encodingMask = UA_DATAVALUE_ENCODINGMASK_VARIANT; retval |= UA_Variant_copySetValue(&v.value, &UA_TYPES[UA_BOOLEAN], &((UA_MethodNode *)node)->userExecutable); break; default: v.encodingMask = UA_DATAVALUE_ENCODINGMASK_STATUSCODE; v.status = UA_STATUSCODE_BADATTRIBUTEIDINVALID; break; } UA_NodeStore_release(node); if(retval != UA_STATUSCODE_GOOD) { v.encodingMask = UA_DATAVALUE_ENCODINGMASK_STATUSCODE; v.status = UA_STATUSCODE_BADNOTREADABLE; } return v; }
static UA_StatusCode Service_Write_writeNode(UA_Server *server, UA_WriteValue *writeValue) { UA_StatusCode retval = UA_STATUSCODE_GOOD; const UA_Node *node; retval = UA_NodeStore_get(server->nodestore, &writeValue->nodeId, &node); if(retval) return retval; switch(writeValue->attributeId) { case UA_ATTRIBUTEID_NODEID: /* if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){ } */ retval = UA_STATUSCODE_BADWRITENOTSUPPORTED; break; case UA_ATTRIBUTEID_NODECLASS: /* if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){ } */ retval = UA_STATUSCODE_BADWRITENOTSUPPORTED; break; case UA_ATTRIBUTEID_BROWSENAME: /* if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){} */ retval = UA_STATUSCODE_BADWRITENOTSUPPORTED; break; case UA_ATTRIBUTEID_DISPLAYNAME: /* if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){} */ retval = UA_STATUSCODE_BADWRITENOTSUPPORTED; break; case UA_ATTRIBUTEID_DESCRIPTION: /* if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){} */ retval = UA_STATUSCODE_BADWRITENOTSUPPORTED; break; case UA_ATTRIBUTEID_WRITEMASK: /* if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){} */ retval = UA_STATUSCODE_BADWRITENOTSUPPORTED; break; case UA_ATTRIBUTEID_USERWRITEMASK: /* if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){} */ retval = UA_STATUSCODE_BADWRITENOTSUPPORTED; break; case UA_ATTRIBUTEID_ISABSTRACT: /* if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){} */ retval = UA_STATUSCODE_BADWRITENOTSUPPORTED; break; case UA_ATTRIBUTEID_SYMMETRIC: /* if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){} */ retval = UA_STATUSCODE_BADWRITENOTSUPPORTED; break; case UA_ATTRIBUTEID_INVERSENAME: /* if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){} */ retval = UA_STATUSCODE_BADWRITENOTSUPPORTED; break; case UA_ATTRIBUTEID_CONTAINSNOLOOPS: /* if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){} */ retval = UA_STATUSCODE_BADWRITENOTSUPPORTED; break; case UA_ATTRIBUTEID_EVENTNOTIFIER: /* if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){} */ retval = UA_STATUSCODE_BADWRITENOTSUPPORTED; break; case UA_ATTRIBUTEID_VALUE: if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT) { retval |= UA_Variant_copy(&writeValue->value.value, &((UA_VariableNode *)node)->value); // todo: zero-copy } break; case UA_ATTRIBUTEID_DATATYPE: /* if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){} */ retval = UA_STATUSCODE_BADWRITENOTSUPPORTED; break; case UA_ATTRIBUTEID_VALUERANK: /* if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){} */ retval = UA_STATUSCODE_BADWRITENOTSUPPORTED; break; case UA_ATTRIBUTEID_ARRAYDIMENSIONS: /* if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){} */ retval = UA_STATUSCODE_BADWRITENOTSUPPORTED; break; case UA_ATTRIBUTEID_ACCESSLEVEL: /* if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){} */ retval = UA_STATUSCODE_BADWRITENOTSUPPORTED; break; case UA_ATTRIBUTEID_USERACCESSLEVEL: /* if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){} */ retval = UA_STATUSCODE_BADWRITENOTSUPPORTED; break; case UA_ATTRIBUTEID_MINIMUMSAMPLINGINTERVAL: /* if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){} */ retval = UA_STATUSCODE_BADWRITENOTSUPPORTED; break; case UA_ATTRIBUTEID_HISTORIZING: /* if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){} */ retval = UA_STATUSCODE_BADWRITENOTSUPPORTED; break; case UA_ATTRIBUTEID_EXECUTABLE: /* if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){} */ retval = UA_STATUSCODE_BADWRITENOTSUPPORTED; break; case UA_ATTRIBUTEID_USEREXECUTABLE: /* if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){} */ retval = UA_STATUSCODE_BADWRITENOTSUPPORTED; break; default: retval = UA_STATUSCODE_BADATTRIBUTEIDINVALID; break; } UA_NodeStore_release(node); return retval; }