}END_TEST START_TEST(ReadSingleAttributeDataTypeWithoutTimestamp) { UA_Server *server = makeTestSequence(); UA_DataValue resp; UA_DataValue_init(&resp); UA_ReadRequest rReq; UA_ReadRequest_init(&rReq); rReq.nodesToRead = UA_ReadValueId_new(); rReq.nodesToReadSize = 1; rReq.nodesToRead[0].nodeId = UA_NODEID_STRING(1, "the.answer"); rReq.nodesToRead[0].attributeId = UA_ATTRIBUTEID_DATATYPE; readValue(server, UA_TIMESTAMPSTORETURN_NEITHER, &rReq.nodesToRead[0], &resp); UA_NodeId* respval; respval = (UA_NodeId*) resp.value.data; ck_assert_int_eq(-1, resp.value.arrayLength); ck_assert_ptr_eq(&UA_TYPES[UA_TYPES_NODEID], resp.value.type); ck_assert_int_eq(respval->namespaceIndex,0); ck_assert_int_eq(respval->identifier.numeric,UA_NS0ID_INT32); }END_TEST
}END_TEST START_TEST(ReadSingleAttributeNodeIdWithoutTimestamp) { UA_Server *server = makeTestSequence(); UA_DataValue resp; UA_DataValue_init(&resp); UA_ReadRequest rReq; UA_ReadRequest_init(&rReq); rReq.nodesToRead = UA_ReadValueId_new(); rReq.nodesToReadSize = 1; rReq.nodesToRead[0].nodeId = UA_NODEID_STRING(1, "the.answer"); rReq.nodesToRead[0].attributeId = UA_ATTRIBUTEID_NODEID; readValue(server, UA_TIMESTAMPSTORETURN_NEITHER, &rReq.nodesToRead[0], &resp); UA_NodeId* respval; respval = (UA_NodeId*) resp.value.data; const UA_NodeId myIntegerNodeId = UA_NODEID_STRING(1, "the.answer"); ck_assert_int_eq(-1, resp.value.arrayLength); ck_assert_ptr_eq(&UA_TYPES[UA_TYPES_NODEID], resp.value.type); ck_assert_int_eq(1, respval->namespaceIndex); for (int var = 0; var < respval->identifier.string.length; ++var) { ck_assert_int_eq(myIntegerNodeId.identifier.string.data[var], respval->identifier.string.data[var]); } UA_free(respval); }END_TEST
}END_TEST START_TEST(ReadSingleAttributeMinimumSamplingIntervalWithoutTimestamp) { UA_Server *server = makeTestSequence(); UA_DataValue resp; UA_DataValue_init(&resp); UA_ReadRequest rReq; UA_ReadRequest_init(&rReq); rReq.nodesToRead = UA_ReadValueId_new(); rReq.nodesToReadSize = 1; rReq.nodesToRead[0].nodeId = UA_NODEID_STRING(1, "the.answer"); rReq.nodesToRead[0].attributeId = UA_ATTRIBUTEID_MINIMUMSAMPLINGINTERVAL; readValue(server, UA_TIMESTAMPSTORETURN_NEITHER, &rReq.nodesToRead[0], &resp); UA_Double* respval; respval = (UA_Double*) resp.value.data; UA_VariableNode *compNode = makeCompareSequence(); UA_Double comp; comp = (UA_Double) compNode->minimumSamplingInterval; ck_assert_int_eq(-1, resp.value.arrayLength); ck_assert_ptr_eq(&UA_TYPES[UA_TYPES_DOUBLE], resp.value.type); ck_assert(*respval == comp); }END_TEST
}END_TEST START_TEST(ReadSingleAttributeInverseNameWithoutTimestamp) { UA_Server *server = makeTestSequence(); UA_DataValue resp; UA_DataValue_init(&resp); UA_ReadRequest rReq; UA_ReadRequest_init(&rReq); rReq.nodesToRead = UA_ReadValueId_new(); rReq.nodesToReadSize = 1; rReq.nodesToRead[0].nodeId.identifier.numeric = UA_NS0ID_ORGANIZES; rReq.nodesToRead[0].attributeId = UA_ATTRIBUTEID_INVERSENAME; readValue(server, UA_TIMESTAMPSTORETURN_NEITHER, &rReq.nodesToRead[0], &resp); UA_LocalizedText* respval; respval = (UA_LocalizedText*) resp.value.data; const UA_LocalizedText comp = UA_LOCALIZEDTEXT("en_US", "OrganizedBy"); ck_assert_int_eq(-1, resp.value.arrayLength); ck_assert_ptr_eq(&UA_TYPES[UA_TYPES_LOCALIZEDTEXT],resp.value.type); ck_assert_int_eq(comp.text.length, respval->text.length); for (int var = 0; var < respval->text.length - 1; ++var) { ck_assert_int_eq(comp.text.data[var], respval->text.data[var]); } ck_assert_int_eq(comp.locale.length, respval->locale.length); for (int var = 0; var < respval->locale.length - 1; ++var) { ck_assert_int_eq(comp.locale.data[var], respval->locale.data[var]); } UA_free(respval); }END_TEST
}END_TEST START_TEST(ReadSingleAttributeUserWriteMaskWithoutTimestamp) { UA_Server *server = makeTestSequence(); UA_DataValue resp; UA_DataValue_init(&resp); UA_ReadRequest rReq; UA_ReadRequest_init(&rReq); rReq.nodesToRead = UA_ReadValueId_new(); rReq.nodesToReadSize = 1; rReq.nodesToRead[0].nodeId = UA_NODEID_STRING(1, "the.answer"); rReq.nodesToRead[0].attributeId = UA_ATTRIBUTEID_USERWRITEMASK; readValue(server, UA_TIMESTAMPSTORETURN_NEITHER, &rReq.nodesToRead[0], &resp); UA_UInt32* respval; respval = (UA_UInt32*) resp.value.data; ck_assert_int_eq(-1, resp.value.arrayLength); ck_assert_ptr_eq(&UA_TYPES[UA_TYPES_UINT32], resp.value.type); ck_assert_int_eq(0,*respval); }END_TEST
}END_TEST START_TEST(ReadSingleAttributeDescriptionWithoutTimestamp) { UA_Server *server = makeTestSequence(); UA_DataValue resp; UA_DataValue_init(&resp); UA_ReadRequest rReq; UA_ReadRequest_init(&rReq); rReq.nodesToRead = UA_ReadValueId_new(); rReq.nodesToReadSize = 1; rReq.nodesToRead[0].nodeId = UA_NODEID_STRING(1, "the.answer"); rReq.nodesToRead[0].attributeId = UA_ATTRIBUTEID_DESCRIPTION; readValue(server, UA_TIMESTAMPSTORETURN_NEITHER, &rReq.nodesToRead[0], &resp); UA_LocalizedText* respval; respval = (UA_LocalizedText*) resp.value.data; UA_VariableNode* compNode = makeCompareSequence(); ck_assert_int_eq(-1, resp.value.arrayLength); ck_assert_ptr_eq(&UA_TYPES[UA_TYPES_LOCALIZEDTEXT], resp.value.type); ck_assert_int_eq(compNode->description.locale.length, respval->locale.length); for (int var = 0; var < respval->locale.length - 1; ++var) { ck_assert_int_eq(compNode->description.locale.data[var], respval->locale.data[var]); } ck_assert_int_eq(compNode->description.text.length, respval->text.length); for (int var = 0; var < respval->text.length - 1; ++var) { ck_assert_int_eq(compNode->description.text.data[var], respval->text.data[var]); } UA_free(respval); }END_TEST
}END_TEST START_TEST(ReadSingleAttributeBrowseNameWithoutTimestamp) { UA_Server *server = makeTestSequence(); UA_DataValue resp; UA_DataValue_init(&resp); UA_ReadRequest rReq; UA_ReadRequest_init(&rReq); rReq.nodesToRead = UA_ReadValueId_new(); rReq.nodesToReadSize = 1; rReq.nodesToRead[0].nodeId = UA_NODEID_STRING(1, "the.answer"); rReq.nodesToRead[0].attributeId = UA_ATTRIBUTEID_BROWSENAME; readValue(server, UA_TIMESTAMPSTORETURN_NEITHER, &rReq.nodesToRead[0], &resp); UA_QualifiedName* respval; respval = (UA_QualifiedName*) resp.value.data; const UA_QualifiedName myIntegerName = UA_QUALIFIEDNAME(1, "the answer"); ck_assert_int_eq(-1, resp.value.arrayLength); ck_assert_ptr_eq(&UA_TYPES[UA_TYPES_QUALIFIEDNAME], resp.value.type); ck_assert_int_eq(1, respval->namespaceIndex); ck_assert_int_eq(myIntegerName.name.length, respval->name.length); for (int var = 0; var < respval->name.length - 1; ++var) { ck_assert_int_eq(myIntegerName.name.data[var], respval->name.data[var]); } UA_free(respval); }END_TEST
static UA_Boolean fillHistoricalDataBackend(UA_HistoryDataBackend backend) { int i = 0; UA_DateTime currentDateTime = testData[i]; fprintf(stderr, "Adding to historical data backend: "); while (currentDateTime) { fprintf(stderr, "%lld, ", currentDateTime / UA_DATETIME_SEC); UA_DataValue value; UA_DataValue_init(&value); value.hasValue = true; UA_Int64 d = currentDateTime; UA_Variant_setScalarCopy(&value.value, &d, &UA_TYPES[UA_TYPES_INT64]); value.hasSourceTimestamp = true; value.sourceTimestamp = currentDateTime; value.hasServerTimestamp = true; value.serverTimestamp = currentDateTime; value.hasStatus = true; value.status = UA_STATUSCODE_GOOD; if (backend.serverSetHistoryData(server, backend.context, NULL, NULL, &outNodeId, UA_FALSE, &value) != UA_STATUSCODE_GOOD) { fprintf(stderr, "\n"); return false; } UA_DataValue_deleteMembers(&value); currentDateTime = testData[++i]; } fprintf(stderr, "\n"); return true; }
UA_DataValue UA_Server_readWithSession(UA_Server *server, UA_Session *session, const UA_ReadValueId *item, UA_TimestampsToReturn timestamps) { UA_DataValue dv; UA_DataValue_init(&dv); op_timestampsToReturn = timestamps; Operation_Read(server, session, item, &dv); return dv; }
void UA_MonitoredItem_sampleCallback(UA_Server *server, UA_MonitoredItem *monitoredItem) { UA_Subscription *sub = monitoredItem->subscription; UA_Session *session = &server->adminSession; if(sub) session = sub->session; UA_LOG_DEBUG_SESSION(&server->config.logger, session, "Subscription %u | " "MonitoredItem %i | Sample callback called", sub ? sub->subscriptionId : 0, monitoredItem->monitoredItemId); UA_assert(monitoredItem->attributeId != UA_ATTRIBUTEID_EVENTNOTIFIER); /* Get the node */ const UA_Node *node = UA_Nodestore_getNode(server->nsCtx, &monitoredItem->monitoredNodeId); /* Sample the value. The sample can still point into the node. */ UA_DataValue value; UA_DataValue_init(&value); if(node) { UA_ReadValueId rvid; UA_ReadValueId_init(&rvid); rvid.nodeId = monitoredItem->monitoredNodeId; rvid.attributeId = monitoredItem->attributeId; rvid.indexRange = monitoredItem->indexRange; ReadWithNode(node, server, session, monitoredItem->timestampsToReturn, &rvid, &value); } else { value.hasStatus = true; value.status = UA_STATUSCODE_BADNODEIDUNKNOWN; } /* Operate on the sample */ UA_Boolean movedValue = false; UA_StatusCode retval = sampleCallbackWithValue(server, session, sub, monitoredItem, &value, &movedValue); if(retval != UA_STATUSCODE_GOOD) { UA_LOG_WARNING_SESSION(&server->config.logger, session, "Subscription %u | " "MonitoredItem %i | Sampling returned the statuscode %s", sub ? sub->subscriptionId : 0, monitoredItem->monitoredItemId, UA_StatusCode_name(retval)); } /* Delete the sample if it was not moved to the notification. */ if(!movedValue) UA_DataValue_deleteMembers(&value); /* Does nothing for UA_VARIANT_DATA_NODELETE */ if(node) UA_Nodestore_releaseNode(server->nsCtx, node); }
/* Stack layout: ... | node | type */ static UA_StatusCode writeValueRankAttribute(UA_Server *server, UA_Session *session, UA_VariableNode *node, const UA_VariableTypeNode *type, UA_Int32 valueRank) { UA_assert(node != NULL); UA_assert(type != NULL); UA_Int32 constraintValueRank = type->valueRank; /* If this is a variabletype, there must be no instances or subtypes of it * when we do the change */ if(node->nodeClass == UA_NODECLASS_VARIABLETYPE && UA_Node_hasSubTypeOrInstances((const UA_Node*)node)) return UA_STATUSCODE_BADINTERNALERROR; /* Check if the valuerank of the variabletype allows the change. */ if(!compatibleValueRanks(valueRank, constraintValueRank)) return UA_STATUSCODE_BADTYPEMISMATCH; /* Check if the new valuerank is compatible with the array dimensions. Use * the read service to handle data sources. */ size_t arrayDims = node->arrayDimensionsSize; if(arrayDims == 0) { /* the value could be an array with no arrayDimensions defined. dimensions zero indicate a scalar for compatibleValueRankArrayDimensions. */ UA_DataValue value; UA_DataValue_init(&value); UA_StatusCode retval = readValueAttribute(server, session, node, &value); if(retval != UA_STATUSCODE_GOOD) return retval; if(!value.hasValue || !value.value.type) { /* no value -> apply */ node->valueRank = valueRank; return UA_STATUSCODE_GOOD; } if(!UA_Variant_isScalar(&value.value)) arrayDims = 1; UA_DataValue_deleteMembers(&value); } if(!compatibleValueRankArrayDimensions(valueRank, arrayDims)) return UA_STATUSCODE_BADTYPEMISMATCH; /* All good, apply the change */ node->valueRank = valueRank; return UA_STATUSCODE_GOOD; }
static UA_StatusCode writeDataTypeAttribute(UA_Server *server, UA_Session *session, UA_VariableNode *node, const UA_VariableTypeNode *type, const UA_NodeId *dataType) { UA_assert(node != NULL); UA_assert(type != NULL); /* If this is a variabletype, there must be no instances or subtypes of it when we do the change */ if(node->nodeClass == UA_NODECLASS_VARIABLETYPE && UA_Node_hasSubTypeOrInstances((const UA_Node*)node)) return UA_STATUSCODE_BADINTERNALERROR; /* Does the new type match the constraints of the variabletype? */ if(!compatibleDataType(server, dataType, &type->dataType)) return UA_STATUSCODE_BADTYPEMISMATCH; /* Check if the current value would match the new type */ UA_DataValue value; UA_DataValue_init(&value); UA_StatusCode retval = readValueAttribute(server, session, node, &value); if(retval != UA_STATUSCODE_GOOD) return retval; if(value.hasValue) { if(!compatibleValue(server, dataType, node->valueRank, node->arrayDimensionsSize, node->arrayDimensions, &value.value, NULL)) retval = UA_STATUSCODE_BADTYPEMISMATCH; UA_DataValue_deleteMembers(&value); if(retval != UA_STATUSCODE_GOOD) { UA_LOG_DEBUG(server->config.logger, UA_LOGCATEGORY_SERVER, "The current value does not match the new data type"); return retval; } } /* Replace the datatype nodeid */ UA_NodeId dtCopy = node->dataType; retval = UA_NodeId_copy(dataType, &node->dataType); if(retval != UA_STATUSCODE_GOOD) { node->dataType = dtCopy; return retval; } UA_NodeId_deleteMembers(&dtCopy); return UA_STATUSCODE_GOOD; }
}END_TEST START_TEST(WriteSingleAttributeValue) { UA_Server *server = makeTestSequence(); UA_WriteValue wValue; UA_WriteValue_init(&wValue); UA_Variant *myIntegerVariant = UA_Variant_new(); UA_Int32 myInteger = 20; UA_Variant_setScalarCopy(myIntegerVariant, &myInteger, &UA_TYPES[UA_TYPES_INT32]); wValue.nodeId = UA_NODEID_STRING(1, "the.answer"); wValue.attributeId = UA_ATTRIBUTEID_VALUE; wValue.value.hasValue = UA_TRUE; wValue.value.value = *myIntegerVariant; UA_StatusCode retval = writeValue(server, &wValue); UA_DataValue resp; UA_DataValue_init(&resp); UA_ReadRequest rReq; UA_ReadRequest_init(&rReq); rReq.nodesToRead = UA_ReadValueId_new(); rReq.nodesToReadSize = 1; rReq.nodesToRead[0].nodeId = UA_NODEID_STRING(1, "the.answer"); rReq.nodesToRead[0].attributeId = UA_ATTRIBUTEID_VALUE; readValue(server, UA_TIMESTAMPSTORETURN_NEITHER, &rReq.nodesToRead[0], &resp); ck_assert_int_eq(retval, UA_STATUSCODE_GOOD); ck_assert(wValue.value.hasValue); const UA_Node *node = UA_NodeStore_get(server->nodestore, &wValue.nodeId); ck_assert_int_eq(node->nodeClass, UA_NODECLASS_VARIABLE); const UA_VariableNode *vn = (const UA_VariableNode*)node; const UA_Variant *oldV = &vn->value.variant.value; ck_assert_ptr_eq(&oldV->type->typeId, &wValue.value.value.type->typeId); ck_assert_int_eq(20, *(UA_Int32* )resp.value.data); }END_TEST
}END_TEST START_TEST(ReadSingleAttributeExecutableWithoutTimestamp) { UA_Server *server = makeTestSequence(); UA_DataValue resp; UA_DataValue_init(&resp); UA_ReadRequest rReq; UA_ReadRequest_init(&rReq); rReq.nodesToRead = UA_ReadValueId_new(); rReq.nodesToReadSize = 1; rReq.nodesToRead[0].nodeId.identifier.numeric = UA_NS0ID_METHODNODE; rReq.nodesToRead[0].attributeId = UA_ATTRIBUTEID_EXECUTABLE; readValue(server, UA_TIMESTAMPSTORETURN_NEITHER, &rReq.nodesToRead[0], &resp); ck_assert_int_eq(-1, resp.value.arrayLength); ck_assert_ptr_eq(&UA_TYPES[UA_TYPES_BOOLEAN], resp.value.type); ck_assert(*(UA_Boolean*)resp.value.data==UA_FALSE); }END_TEST
static UA_StatusCode getVariableNodeDataType(const UA_VariableNode *vn, UA_DataValue *v) { UA_StatusCode retval = UA_STATUSCODE_GOOD; if(vn->valueSource == UA_VALUESOURCE_VARIANT) { forceVariantSetScalar(&v->value, &vn->value.variant.value.type->typeId, &UA_TYPES[UA_TYPES_NODEID]); } else { if(vn->value.dataSource.read == NULL) return UA_STATUSCODE_BADINTERNALERROR; /* Read from the datasource to see the data type */ UA_DataValue val; UA_DataValue_init(&val); val.hasValue = UA_FALSE; // always assume we are not given a value by userspace retval = vn->value.dataSource.read(vn->value.dataSource.handle, vn->nodeId, UA_FALSE, NULL, &val); if(retval != UA_STATUSCODE_GOOD) return retval; if (val.hasValue && val.value.type != NULL) retval = UA_Variant_setScalarCopy(&v->value, &val.value.type->typeId, &UA_TYPES[UA_TYPES_NODEID]); UA_DataValue_deleteMembers(&val); } return retval; }
static UA_StatusCode getVariableNodeArrayDimensions(const UA_VariableNode *vn, UA_DataValue *v) { UA_StatusCode retval = UA_STATUSCODE_GOOD; if(vn->valueSource == UA_VALUESOURCE_VARIANT) { 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 { if(vn->value.dataSource.read == NULL) return UA_STATUSCODE_BADINTERNALERROR; /* Read the datasource to see the array dimensions */ UA_DataValue val; UA_DataValue_init(&val); retval = vn->value.dataSource.read(vn->value.dataSource.handle, vn->nodeId, UA_FALSE, NULL, &val); if(retval != UA_STATUSCODE_GOOD) return retval; retval = UA_Variant_setArrayCopy(&v->value, val.value.arrayDimensions, val.value.arrayDimensionsSize, &UA_TYPES[UA_TYPES_INT32]); UA_DataValue_deleteMembers(&val); } return retval; }
}END_TEST START_TEST(ReadSingleAttributeHistorizingWithoutTimestamp) { UA_Server *server = makeTestSequence(); UA_DataValue resp; UA_DataValue_init(&resp); UA_ReadRequest rReq; UA_ReadRequest_init(&rReq); rReq.nodesToRead = UA_ReadValueId_new(); rReq.nodesToReadSize = 1; rReq.nodesToRead[0].nodeId = UA_NODEID_STRING(1, "the.answer"); rReq.nodesToRead[0].attributeId = UA_ATTRIBUTEID_HISTORIZING; readValue(server, UA_TIMESTAMPSTORETURN_NEITHER, &rReq.nodesToRead[0], &resp); ck_assert_int_eq(-1, resp.value.arrayLength); ck_assert_ptr_eq(&UA_TYPES[UA_TYPES_BOOLEAN], resp.value.type); ck_assert(*(UA_Boolean* )resp.value.data==UA_FALSE); }END_TEST
}END_TEST START_TEST(ReadSingleAttributeEventNotifierWithoutTimestamp) { UA_Server *server = makeTestSequence(); UA_DataValue resp; UA_DataValue_init(&resp); UA_ReadRequest rReq; UA_ReadRequest_init(&rReq); rReq.nodesToRead = UA_ReadValueId_new(); rReq.nodesToReadSize = 1; rReq.nodesToRead[0].nodeId = UA_NODEID_NUMERIC(1, 50); rReq.nodesToRead[0].attributeId = UA_ATTRIBUTEID_EVENTNOTIFIER; readValue(server, UA_TIMESTAMPSTORETURN_NEITHER, &rReq.nodesToRead[0], &resp); ck_assert_int_eq(-1, resp.value.arrayLength); ck_assert_ptr_eq(&UA_TYPES[UA_TYPES_BYTE],resp.value.type); ck_assert_int_eq(*(UA_Byte*)resp.value.data, 0); }END_TEST
}END_TEST START_TEST(ReadSingleAttributeArrayDimensionsWithoutTimestamp) { UA_Server *server = makeTestSequence(); UA_DataValue resp; UA_DataValue_init(&resp); UA_ReadRequest rReq; UA_ReadRequest_init(&rReq); rReq.nodesToRead = UA_ReadValueId_new(); rReq.nodesToReadSize = 1; rReq.nodesToRead[0].nodeId = UA_NODEID_STRING(1, "the.answer"); rReq.nodesToRead[0].attributeId = UA_ATTRIBUTEID_ARRAYDIMENSIONS; readValue(server, UA_TIMESTAMPSTORETURN_NEITHER, &rReq.nodesToRead[0], &resp); ck_assert_int_eq(-1, resp.value.arrayLength); ck_assert_ptr_eq(&UA_TYPES[UA_TYPES_INT32], resp.value.type); ck_assert_ptr_eq((UA_Int32*)resp.value.data,0); }END_TEST
}END_TEST START_TEST(ReadSingleAttributeAccessLevelWithoutTimestamp) { UA_Server *server = makeTestSequence(); UA_DataValue resp; UA_DataValue_init(&resp); UA_ReadRequest rReq; UA_ReadRequest_init(&rReq); rReq.nodesToRead = UA_ReadValueId_new(); rReq.nodesToReadSize = 1; rReq.nodesToRead[0].nodeId = UA_NODEID_STRING(1, "the.answer"); rReq.nodesToRead[0].attributeId = UA_ATTRIBUTEID_ACCESSLEVEL; readValue(server, UA_TIMESTAMPSTORETURN_NEITHER, &rReq.nodesToRead[0], &resp); ck_assert_int_eq(-1, resp.value.arrayLength); ck_assert_ptr_eq(&UA_TYPES[UA_TYPES_BYTE], resp.value.type); ck_assert_int_eq(*(UA_Byte*)resp.value.data, 0); }END_TEST
/** 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); }
/* Stack layout: ... | node | type */ static UA_StatusCode writeArrayDimensionsAttribute(UA_Server *server, UA_Session *session, UA_VariableNode *node, const UA_VariableTypeNode *type, size_t arrayDimensionsSize, UA_UInt32 *arrayDimensions) { UA_assert(node != NULL); UA_assert(type != NULL); /* If this is a variabletype, there must be no instances or subtypes of it * when we do the change */ if(node->nodeClass == UA_NODECLASS_VARIABLETYPE && UA_Node_hasSubTypeOrInstances((UA_Node*)node)) { UA_LOG_INFO(server->config.logger, UA_LOGCATEGORY_SERVER, "Cannot change a variable type with existing instances"); return UA_STATUSCODE_BADINTERNALERROR; } /* Check that the array dimensions match with the valuerank */ if(!compatibleValueRankArrayDimensions(node->valueRank, arrayDimensionsSize)) { UA_LOG_DEBUG(server->config.logger, UA_LOGCATEGORY_SERVER, "The current value rank does not match the new array dimensions"); return UA_STATUSCODE_BADTYPEMISMATCH; } /* Check if the array dimensions match with the wildcards in the * variabletype (dimension length 0) */ if(type->arrayDimensions && !compatibleArrayDimensions(type->arrayDimensionsSize, type->arrayDimensions, arrayDimensionsSize, arrayDimensions)) { UA_LOG_DEBUG(server->config.logger, UA_LOGCATEGORY_SERVER, "Array dimensions in the variable type do not match"); return UA_STATUSCODE_BADTYPEMISMATCH; } /* Check if the current value is compatible with the array dimensions */ UA_DataValue value; UA_DataValue_init(&value); UA_StatusCode retval = readValueAttribute(server, session, node, &value); if(retval != UA_STATUSCODE_GOOD) return retval; if(value.hasValue) { if(!compatibleValueArrayDimensions(&value.value, arrayDimensionsSize, arrayDimensions)) retval = UA_STATUSCODE_BADTYPEMISMATCH; UA_DataValue_deleteMembers(&value); if(retval != UA_STATUSCODE_GOOD) { UA_LOG_DEBUG(server->config.logger, UA_LOGCATEGORY_SERVER, "Array dimensions in the current value do not match"); return retval; } } /* Ok, apply */ UA_UInt32 *oldArrayDimensions = node->arrayDimensions; retval = UA_Array_copy(arrayDimensions, arrayDimensionsSize, (void**)&node->arrayDimensions, &UA_TYPES[UA_TYPES_UINT32]); if(retval != UA_STATUSCODE_GOOD) return retval; UA_free(oldArrayDimensions); node->arrayDimensionsSize = arrayDimensionsSize; return UA_STATUSCODE_GOOD; }
static UA_StatusCode updateHistory(UA_PerformUpdateType updateType, UA_DateTime *updateData, UA_StatusCode ** operationResults, size_t *operationResultsSize) { UA_UpdateDataDetails *details = UA_UpdateDataDetails_new(); details->performInsertReplace = updateType; UA_NodeId_copy(&outNodeId, &details->nodeId); int updateDataSize = -1; while(updateData[++updateDataSize]); fprintf(stderr, "updateHistory for %d values.\n", updateDataSize); details->updateValuesSize = (size_t)updateDataSize; details->updateValues = (UA_DataValue*)UA_Array_new(details->updateValuesSize, &UA_TYPES[UA_TYPES_DATAVALUE]); for (size_t i = 0; i < details->updateValuesSize; ++i) { UA_DataValue_init(&details->updateValues[i]); details->updateValues[i].hasValue = true; UA_Int64 d = updateType; UA_Variant_setScalarCopy(&details->updateValues[i].value, &d, &UA_TYPES[UA_TYPES_INT64]); details->updateValues[i].hasSourceTimestamp = true; details->updateValues[i].sourceTimestamp = updateData[i]; details->updateValues[i].hasServerTimestamp = true; details->updateValues[i].serverTimestamp = updateData[i]; details->updateValues[i].hasStatus = true; details->updateValues[i].status = UA_STATUSCODE_GOOD; } UA_HistoryUpdateRequest request; UA_HistoryUpdateRequest_init(&request); request.historyUpdateDetailsSize = 1; request.historyUpdateDetails = UA_ExtensionObject_new(); UA_ExtensionObject_init(request.historyUpdateDetails); request.historyUpdateDetails[0].encoding = UA_EXTENSIONOBJECT_DECODED; request.historyUpdateDetails[0].content.decoded.type = &UA_TYPES[UA_TYPES_UPDATEDATADETAILS]; request.historyUpdateDetails[0].content.decoded.data = details; UA_HistoryUpdateResponse response; UA_HistoryUpdateResponse_init(&response); Service_HistoryUpdate(server, &server->adminSession, &request, &response); UA_HistoryUpdateRequest_deleteMembers(&request); UA_StatusCode ret = UA_STATUSCODE_GOOD; if (response.responseHeader.serviceResult != UA_STATUSCODE_GOOD) ret = response.responseHeader.serviceResult; else if (response.resultsSize != 1) ret = UA_STATUSCODE_BADUNEXPECTEDERROR; else if (response.results[0].statusCode != UA_STATUSCODE_GOOD) ret = response.results[0].statusCode; else if (response.results[0].operationResultsSize != (size_t)updateDataSize) ret = UA_STATUSCODE_BADUNEXPECTEDERROR; else { if (operationResults) { *operationResultsSize = response.results[0].operationResultsSize; ret = UA_Array_copy(response.results[0].operationResults, *operationResultsSize, (void**)operationResults, &UA_TYPES[UA_TYPES_STATUSCODE]); } else { for (size_t i = 0; i < response.results[0].operationResultsSize; ++i) { if (response.results[0].operationResults[i] != UA_STATUSCODE_GOOD) { ret = response.results[0].operationResults[i]; break; } } } } UA_HistoryUpdateResponse_deleteMembers(&response); return ret; }
END_TEST START_TEST(Server_HistorizingStrategyUser) { // set a data backend UA_HistorizingNodeIdSettings setting; setting.historizingBackend = UA_HistoryDataBackend_Memory(3, 100); setting.maxHistoryDataResponseSize = 100; setting.historizingUpdateStrategy = UA_HISTORIZINGUPDATESTRATEGY_USER; UA_StatusCode retval = gathering->registerNodeId(server, gathering->context, &outNodeId, setting); ck_assert_str_eq(UA_StatusCode_name(retval), UA_StatusCode_name(UA_STATUSCODE_GOOD)); // fill the data UA_DateTime start = UA_DateTime_now(); UA_DateTime end = start + (10 * UA_DATETIME_SEC); for (UA_UInt32 i = 0; i < 10; ++i) { UA_DataValue value; UA_DataValue_init(&value); value.hasValue = true; value.hasStatus = true; value.status = UA_STATUSCODE_GOOD; UA_Variant_setScalarCopy(&value.value, &i, &UA_TYPES[UA_TYPES_UINT32]); value.hasSourceTimestamp = true; value.sourceTimestamp = start + (i * UA_DATETIME_SEC); value.hasServerTimestamp = true; value.serverTimestamp = value.sourceTimestamp; retval = setting.historizingBackend.serverSetHistoryData(server, setting.historizingBackend.context, NULL, NULL, &outNodeId, UA_FALSE, &value); ck_assert_str_eq(UA_StatusCode_name(retval), UA_StatusCode_name(UA_STATUSCODE_GOOD)); UA_DataValue_deleteMembers(&value); } // request UA_HistoryReadResponse response; UA_HistoryReadResponse_init(&response); requestHistory(start, end, &response, 0, false, NULL); // test the response ck_assert_str_eq(UA_StatusCode_name(response.responseHeader.serviceResult), UA_StatusCode_name(UA_STATUSCODE_GOOD)); ck_assert_uint_eq(response.resultsSize, 1); for (size_t i = 0; i < response.resultsSize; ++i) { ck_assert_str_eq(UA_StatusCode_name(response.results[i].statusCode), UA_StatusCode_name(UA_STATUSCODE_GOOD)); ck_assert_uint_eq(response.results[i].historyData.encoding, UA_EXTENSIONOBJECT_DECODED); ck_assert(response.results[i].historyData.content.decoded.type == &UA_TYPES[UA_TYPES_HISTORYDATA]); UA_HistoryData * data = (UA_HistoryData *)response.results[i].historyData.content.decoded.data; ck_assert_uint_eq(data->dataValuesSize, 10); for (size_t j = 0; j < data->dataValuesSize; ++j) { ck_assert_uint_eq(data->dataValues[j].hasSourceTimestamp, true); ck_assert_uint_eq(data->dataValues[j].sourceTimestamp, start + (j * UA_DATETIME_SEC)); ck_assert_uint_eq(data->dataValues[j].hasStatus, true); ck_assert_str_eq(UA_StatusCode_name(data->dataValues[j].status), UA_StatusCode_name(UA_STATUSCODE_GOOD)); ck_assert_uint_eq(data->dataValues[j].hasValue, true); ck_assert(data->dataValues[j].value.type == &UA_TYPES[UA_TYPES_UINT32]); UA_UInt32 * value = (UA_UInt32 *)data->dataValues[j].value.data; ck_assert_uint_eq(*value, j); } } UA_HistoryReadResponse_deleteMembers(&response); UA_HistoryDataBackend_Memory_deleteMembers(&setting.historizingBackend); }
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; }