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; }
static UA_StatusCode writeValueAttributeWithRange(UA_VariableNode *node, const UA_DataValue *value, const UA_NumericRange *rangeptr) { /* Value on both sides? */ if(value->status != node->value.data.value.status || !value->hasValue || !node->value.data.value.hasValue) return UA_STATUSCODE_BADINDEXRANGEINVALID; /* Make scalar a one-entry array for range matching */ UA_Variant editableValue; const UA_Variant *v = &value->value; if(UA_Variant_isScalar(&value->value)) { editableValue = value->value; editableValue.arrayLength = 1; v = &editableValue; } /* Write the value */ UA_StatusCode retval = UA_Variant_setRangeCopy(&node->value.data.value.value, v->data, v->arrayLength, *rangeptr); if(retval != UA_STATUSCODE_GOOD) return retval; /* Write the status and timestamps */ node->value.data.value.hasStatus = value->hasStatus; node->value.data.value.status = value->status; node->value.data.value.hasSourceTimestamp = value->hasSourceTimestamp; node->value.data.value.sourceTimestamp = value->sourceTimestamp; node->value.data.value.hasSourcePicoseconds = value->hasSourcePicoseconds; node->value.data.value.sourcePicoseconds = value->sourcePicoseconds; return UA_STATUSCODE_GOOD; }
static void adjustValue(UA_Server *server, UA_Variant *value, const UA_NodeId *targetDataTypeId) { const UA_DataType *targetDataType = UA_findDataType(targetDataTypeId); if(!targetDataType) return; /* A string is written to a byte array. the valuerank and array dimensions * are checked later */ if(targetDataType == &UA_TYPES[UA_TYPES_BYTE] && value->type == &UA_TYPES[UA_TYPES_BYTESTRING] && UA_Variant_isScalar(value)) { UA_ByteString *str = (UA_ByteString*)value->data; value->type = &UA_TYPES[UA_TYPES_BYTE]; value->arrayLength = str->length; value->data = str->data; return; } /* An enum was sent as an int32, or an opaque type as a bytestring. This * is detected with the typeIndex indicating the "true" datatype. */ enum type_equivalence te1 = typeEquivalence(targetDataType); enum type_equivalence te2 = typeEquivalence(value->type); if(te1 != TYPE_EQUIVALENCE_NONE && te1 == te2) { value->type = targetDataType; return; } /* No more possible equivalencies */ }
static UA_Boolean updateNeededForStatusCode(const UA_DataValue *value, const UA_MonitoredItem *mon) { if (UA_Variant_isScalar(&value->value)) { if(value->status != mon->lastStatus) return true; } return false; }
UA_StatusCode __UA_Client_readAttribute(UA_Client *client, const UA_NodeId *nodeId, UA_AttributeId attributeId, void *out, const UA_DataType *outDataType) { UA_ReadValueId item; UA_ReadValueId_init(&item); item.nodeId = *nodeId; item.attributeId = attributeId; UA_ReadRequest request; UA_ReadRequest_init(&request); request.nodesToRead = &item; request.nodesToReadSize = 1; UA_ReadResponse response = UA_Client_Service_read(client, request); UA_StatusCode retval = response.responseHeader.serviceResult; if(retval == UA_STATUSCODE_GOOD && response.resultsSize != 1) retval = UA_STATUSCODE_BADUNEXPECTEDERROR; if(retval != UA_STATUSCODE_GOOD) { UA_ReadResponse_deleteMembers(&response); return retval; } UA_DataValue *res = response.results; if(res->hasStatus != UA_STATUSCODE_GOOD) retval = res->hasStatus; else if(!res->hasValue || !UA_Variant_isScalar(&res->value)) retval = UA_STATUSCODE_BADUNEXPECTEDERROR; if(retval != UA_STATUSCODE_GOOD) { UA_ReadResponse_deleteMembers(&response); return retval; } if(attributeId == UA_ATTRIBUTEID_VALUE) { memcpy(out, &res->value, sizeof(UA_Variant)); UA_Variant_init(&res->value); } else if(UA_Variant_isScalar(&res->value) && res->value.type == outDataType) { memcpy(out, res->value.data, res->value.type->memSize); UA_free(res->value.data); res->value.data = NULL; } else { retval = UA_STATUSCODE_BADUNEXPECTEDERROR; } UA_ReadResponse_deleteMembers(&response); return retval; }
static UA_StatusCode writeInteger(void *handle, const UA_NodeId nodeid, const UA_Variant *data, const UA_NumericRange *range){ if(UA_Variant_isScalar(data) && data->type == &UA_TYPES[UA_TYPES_INT32] && data->data){ *(UA_UInt32*)handle = *(UA_Int32*)data->data; } //note that this is only possible if the identifier is a string - but we are sure to have one here UA_LOG_INFO(logger, UA_LOGCATEGORY_USERLAND, "Node written %.*s",nodeid.identifier.string.length, nodeid.identifier.string.data); UA_LOG_INFO(logger, UA_LOGCATEGORY_USERLAND, "written value %i", *(UA_UInt32*)handle); return UA_STATUSCODE_GOOD; }
static UA_StatusCode satisfySignature(const UA_Variant *var, const UA_Argument *arg) { if(!UA_NodeId_equal(&var->type->typeId, &arg->dataType) ) return UA_STATUSCODE_BADINVALIDARGUMENT; // Note: The namespace compiler will compile nodes with their actual array dimensions // Todo: Check if this is standard conform for scalars if(arg->arrayDimensionsSize > 0 && var->arrayDimensionsSize > 0) if(var->arrayDimensionsSize != arg->arrayDimensionsSize) return UA_STATUSCODE_BADINVALIDARGUMENT; UA_UInt32 *varDims = var->arrayDimensions; size_t varDimsSize = var->arrayDimensionsSize; UA_Boolean scalar = UA_Variant_isScalar(var); /* The dimension 1 is implicit in the array length */ UA_UInt32 fakeDims; if(!scalar && !varDims) { fakeDims = (UA_UInt32)var->arrayLength; varDims = &fakeDims; varDimsSize = 1; } /* ValueRank Semantics * n >= 1: the value is an array with the specified number of dimens*ions. * n = 0: the value is an array with one or more dimensions. * n = -1: the value is a scalar. * n = -2: the value can be a scalar or an array with any number of dimensions. * n = -3: the value can be a scalar or a one dimensional array. */ switch(arg->valueRank) { case -3: if(varDimsSize > 1) return UA_STATUSCODE_BADINVALIDARGUMENT; break; case -2: break; case -1: if(!scalar) return UA_STATUSCODE_BADINVALIDARGUMENT; break; case 0: if(scalar || !varDims) return UA_STATUSCODE_BADINVALIDARGUMENT; break; default: break; } /* do the array dimensions match? */ if(arg->arrayDimensionsSize != varDimsSize) return UA_STATUSCODE_BADINVALIDARGUMENT; for(size_t i = 0; i < varDimsSize; i++) { if(arg->arrayDimensions[i] != varDims[i]) return UA_STATUSCODE_BADINVALIDARGUMENT; } return UA_STATUSCODE_GOOD; }
UA_StatusCode UA_Client_readArrayDimensionsAttribute(UA_Client *client, const UA_NodeId nodeId, UA_Int32 **outArrayDimensions, size_t *outArrayDimensionsSize) { UA_ReadValueId item; UA_ReadValueId_init(&item); item.nodeId = nodeId; item.attributeId = UA_ATTRIBUTEID_ARRAYDIMENSIONS; UA_ReadRequest request; UA_ReadRequest_init(&request); request.nodesToRead = &item; request.nodesToReadSize = 1; UA_ReadResponse response = UA_Client_Service_read(client, request); UA_StatusCode retval = response.responseHeader.serviceResult; if(retval == UA_STATUSCODE_GOOD && response.resultsSize != 1) retval = UA_STATUSCODE_BADUNEXPECTEDERROR; if(retval != UA_STATUSCODE_GOOD) goto cleanup; UA_DataValue *res = response.results; if(res->hasStatus != UA_STATUSCODE_GOOD) retval = res->hasStatus; else if(!res->hasValue || UA_Variant_isScalar(&res->value)) retval = UA_STATUSCODE_BADUNEXPECTEDERROR; if(retval != UA_STATUSCODE_GOOD) goto cleanup; if(UA_Variant_isScalar(&res->value) || res->value.type != &UA_TYPES[UA_TYPES_INT32]) { retval = UA_STATUSCODE_BADUNEXPECTEDERROR; goto cleanup; } *outArrayDimensions = res->value.data; *outArrayDimensionsSize = res->value.arrayLength; UA_free(res->value.data); res->value.data = NULL; res->value.arrayLength = 0; cleanup: UA_ReadResponse_deleteMembers(&response); return retval; }
/* Check if the valuerank allows for the value dimension */ static UA_Boolean compatibleValueRankValue(UA_Int32 valueRank, const UA_Variant *value) { /* empty arrays (-1) always match */ if(!value->data) return false; size_t arrayDims = value->arrayDimensionsSize; if(!UA_Variant_isScalar(value)) arrayDims = 1; /* array but no arraydimensions -> implicit array dimension 1 */ return compatibleValueRankArrayDimensions(valueRank, arrayDims); }
static UA_StatusCode writeInteger(void *handle, const UA_NodeId nodeid, const UA_Variant *data, const UA_NumericRange *range) { if(UA_Variant_isScalar(data) && data->type == &UA_TYPES[UA_TYPES_INT32] && data->data){ *(UA_UInt32*)handle = *(UA_UInt32*)data->data; } // we know the nodeid is a string UA_LOG_INFO(logger, UA_LOGCATEGORY_USERLAND, "Node written %.*s", nodeid.identifier.string.length, nodeid.identifier.string.data); UA_LOG_INFO(logger, UA_LOGCATEGORY_USERLAND, "written value %i", *(UA_UInt32*)handle); return UA_STATUSCODE_GOOD; }
UA_Boolean compatibleValueArrayDimensions(const UA_Variant *value, size_t targetArrayDimensionsSize, const UA_UInt32 *targetArrayDimensions) { size_t valueArrayDimensionsSize = value->arrayDimensionsSize; UA_UInt32 *valueArrayDimensions = value->arrayDimensions; UA_UInt32 tempArrayDimensions; if(valueArrayDimensions == 0 && !UA_Variant_isScalar(value)) { valueArrayDimensionsSize = 1; tempArrayDimensions = (UA_UInt32)value->arrayLength; valueArrayDimensions = &tempArrayDimensions; } return compatibleArrayDimensions(targetArrayDimensionsSize, targetArrayDimensions, valueArrayDimensionsSize, valueArrayDimensions); }
void FORTE_OPC_UA_READ::executeEvent(int pa_nEIID){ switch(pa_nEIID){ case scm_nEventREQID: UA_Int32 value = 0; OUT() = value; UA_Client *client = UA_Client_new(UA_ClientConfig_standard, Logger_Stdout_new()); UA_StatusCode connected = UA_Client_connect(client, ClientNetworkLayerTCP_connect, SERVER()); if(connected != UA_STATUSCODE_GOOD) { UA_Client_delete(client); sendOutputEvent(scm_nEventCNFID); } UA_ReadRequest rReq; UA_ReadRequest_init(&rReq); rReq.nodesToRead = UA_ReadValueId_new(); rReq.nodesToReadSize = 1; rReq.nodesToRead[0].nodeId = UA_NODEID_STRING_ALLOC(1, NODE()); /* assume this node exists */ rReq.nodesToRead[0].attributeId = UA_ATTRIBUTEID_VALUE; UA_ReadResponse rResp = UA_Client_read(client, &rReq); if(rResp.responseHeader.serviceResult == UA_STATUSCODE_GOOD && rResp.resultsSize > 0 && rResp.results[0].hasValue && UA_Variant_isScalar(&rResp.results[0].value) && rResp.results[0].value.type == &UA_TYPES[UA_TYPES_INT32]) { value = *(UA_Int32*)rResp.results[0].value.data; OUT() = value; }else{ sendOutputEvent(scm_nEventCNFID); } UA_ReadRequest_deleteMembers(&rReq); UA_ReadResponse_deleteMembers(&rResp); UA_Client_disconnect(client); UA_Client_delete(client); sendOutputEvent(scm_nEventCNFID); break; break; } }
/* 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 statisfySignature(UA_Variant *var, UA_Argument arg) { if(!UA_NodeId_equal(&var->type->typeId, &arg.dataType) ) return UA_STATUSCODE_BADINVALIDARGUMENT; // Note: The namespace compiler will compile nodes with their actual array dimensions, never -1 if(arg.arrayDimensionsSize > 0 && var->arrayDimensionsSize > 0) if(var->arrayDimensionsSize != arg.arrayDimensionsSize) return UA_STATUSCODE_BADINVALIDARGUMENT; // Continue with jpfr's statisfySignature from here on /* ValueRank Semantics * n >= 1: the value is an array with the specified number of dimens*ions. * n = 0: the value is an array with one or more dimensions. * n = -1: the value is a scalar. * n = -2: the value can be a scalar or an array with any number of dimensions. * n = -3: the value can be a scalar or a one dimensional array. */ UA_Boolean scalar = UA_Variant_isScalar(var); if(arg.valueRank == 0 && scalar) return UA_STATUSCODE_BADINVALIDARGUMENT; if(arg.valueRank == -1 && !scalar) return UA_STATUSCODE_BADINVALIDARGUMENT; if(arg.valueRank == -3 && var->arrayDimensionsSize > 1) return UA_STATUSCODE_BADINVALIDARGUMENT; if(arg.valueRank > 1 && var->arrayDimensionsSize != arg.arrayDimensionsSize) return UA_STATUSCODE_BADINVALIDARGUMENT; //variants do not always encode the dimension flag (e.g. 1d array) if(var->arrayDimensionsSize==-1 && arg.arrayDimensionsSize == 1 && var->arrayLength > 0 && arg.arrayDimensions[0] == (UA_UInt32)var->arrayLength ){ return UA_STATUSCODE_GOOD; }else{ if(arg.valueRank >= 1 && var->arrayDimensionsSize != arg.arrayDimensionsSize) return UA_STATUSCODE_BADINVALIDARGUMENT; if(arg.arrayDimensionsSize >= 1) { if(arg.arrayDimensionsSize != var->arrayDimensionsSize) return UA_STATUSCODE_BADINVALIDARGUMENT; for(UA_Int32 i = 0; i < arg.arrayDimensionsSize; i++) { if(arg.arrayDimensions[i] != (UA_UInt32) var->arrayDimensions[i]) return UA_STATUSCODE_BADINVALIDARGUMENT; } } } return UA_STATUSCODE_GOOD; }
static UA_StatusCode writeInteger(UA_Server *server, const UA_NodeId *sessionId, void *sessionContext, const UA_NodeId *nodeId, void *nodeContext, const UA_NumericRange *range, const UA_DataValue *value) { UA_Int32 *myInteger = (UA_Int32*)nodeContext; if(value->hasValue && UA_Variant_isScalar(&value->value) && value->value.type == &UA_TYPES[UA_TYPES_INT32] && value->value.data) *myInteger = *(UA_Int32 *)value->value.data; // we know the nodeid is a string UA_LOG_INFO(logger, UA_LOGCATEGORY_USERLAND, "Node written %.*s", (int)nodeId->identifier.string.length, nodeId->identifier.string.data); UA_LOG_INFO(logger, UA_LOGCATEGORY_USERLAND, "written value %i", *(UA_UInt32 *)myInteger); return UA_STATUSCODE_GOOD; }
static UA_INLINE UA_Boolean updateNeededForFilteredPercentValue(const UA_Variant *value, const UA_Variant *oldValue, const UA_Double deadbandValue, UA_Range* euRange) { if(value->arrayLength != oldValue->arrayLength) return true; if(value->type != oldValue->type) return true; if (UA_Variant_isScalar(value)) { return outOfPercentDeadBand(value->data, oldValue->data, 0, value->type, deadbandValue, euRange); } for (size_t i = 0; i < value->arrayLength; ++i) { if (outOfPercentDeadBand(value->data, oldValue->data, i, value->type, deadbandValue, euRange)) return true; } return false; }
static UA_StatusCode argConformsToDefinition(UA_Server *server, const UA_VariableNode *argRequirements, size_t argsSize, const UA_Variant *args) { if(argRequirements->value.variant.value.type != &UA_TYPES[UA_TYPES_ARGUMENT]) return UA_STATUSCODE_BADINTERNALERROR; UA_Argument *argReqs = (UA_Argument*)argRequirements->value.variant.value.data; size_t argReqsSize = argRequirements->value.variant.value.arrayLength; if(argRequirements->valueSource != UA_VALUESOURCE_VARIANT) return UA_STATUSCODE_BADINTERNALERROR; if(UA_Variant_isScalar(&argRequirements->value.variant.value)) argReqsSize = 1; if(argReqsSize > argsSize) return UA_STATUSCODE_BADARGUMENTSMISSING; if(argReqsSize != argsSize) return UA_STATUSCODE_BADINVALIDARGUMENT; UA_StatusCode retval = UA_STATUSCODE_GOOD; for(size_t i = 0; i < argReqsSize; i++) retval |= satisfySignature(server, &args[i], &argReqs[i]); return retval; }
static UA_StatusCode satisfySignature(UA_Server *server, const UA_Variant *var, const UA_Argument *arg) { if(!UA_NodeId_equal(&var->type->typeId, &arg->dataType)){ if(!UA_NodeId_equal(&var->type->typeId, &UA_TYPES[UA_TYPES_INT32].typeId)) return UA_STATUSCODE_BADINVALIDARGUMENT; /* enumerations are encoded as int32 -> if provided var is integer, check if arg is an enumeration type */ UA_NodeId enumerationNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ENUMERATION); UA_NodeId hasSubTypeNodeId = UA_NODEID_NUMERIC(0,UA_NS0ID_HASSUBTYPE); UA_Boolean found = false; UA_StatusCode retval = isNodeInTree(server->nodestore, &arg->dataType, &enumerationNodeId, &hasSubTypeNodeId, 1, 1, &found); if(retval != UA_STATUSCODE_GOOD) return UA_STATUSCODE_BADINTERNALERROR; if(!found) return UA_STATUSCODE_BADINVALIDARGUMENT; } // Note: The namespace compiler will compile nodes with their actual array dimensions // Todo: Check if this is standard conform for scalars if(arg->arrayDimensionsSize > 0 && var->arrayDimensionsSize > 0) if(var->arrayDimensionsSize != arg->arrayDimensionsSize) return UA_STATUSCODE_BADINVALIDARGUMENT; UA_Int32 *varDims = var->arrayDimensions; size_t varDimsSize = var->arrayDimensionsSize; UA_Boolean scalar = UA_Variant_isScalar(var); /* The dimension 1 is implicit in the array length */ UA_Int32 fakeDims; if(!scalar && !varDims) { fakeDims = (UA_Int32)var->arrayLength; varDims = &fakeDims; varDimsSize = 1; } /* ValueRank Semantics * n >= 1: the value is an array with the specified number of dimens*ions. * n = 0: the value is an array with one or more dimensions. * n = -1: the value is a scalar. * n = -2: the value can be a scalar or an array with any number of dimensions. * n = -3: the value can be a scalar or a one dimensional array. */ switch(arg->valueRank) { case -3: if(varDimsSize > 1) return UA_STATUSCODE_BADINVALIDARGUMENT; break; case -2: break; case -1: if(!scalar) return UA_STATUSCODE_BADINVALIDARGUMENT; break; case 0: if(scalar || !varDims) return UA_STATUSCODE_BADINVALIDARGUMENT; break; default: break; } /* do the array dimensions match? */ if(arg->arrayDimensionsSize != varDimsSize) return UA_STATUSCODE_BADINVALIDARGUMENT; for(size_t i = 0; i < varDimsSize; i++) { if((UA_Int32)arg->arrayDimensions[i] != varDims[i]) return UA_STATUSCODE_BADINVALIDARGUMENT; } return UA_STATUSCODE_GOOD; }
int main(int argc, char *argv[]) { UA_Client *client = UA_Client_new(UA_ClientConfig_standard, Logger_Stdout_new()); UA_StatusCode retval = UA_Client_connect(client, ClientNetworkLayerTCP_connect, "opc.tcp://localhost:16664"); if(retval != UA_STATUSCODE_GOOD) { UA_Client_delete(client); return retval; } // Browse some objects printf("Browsing nodes in objects folder:\n"); UA_BrowseRequest bReq; UA_BrowseRequest_init(&bReq); bReq.requestedMaxReferencesPerNode = 0; bReq.nodesToBrowse = UA_BrowseDescription_new(); bReq.nodesToBrowseSize = 1; bReq.nodesToBrowse[0].nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER); //browse objects folder bReq.nodesToBrowse[0].resultMask = UA_BROWSERESULTMASK_ALL; //return everything UA_BrowseResponse bResp = UA_Client_browse(client, &bReq); printf("%-9s %-16s %-16s %-16s\n", "NAMESPACE", "NODEID", "BROWSE NAME", "DISPLAY NAME"); for (int i = 0; i < bResp.resultsSize; ++i) { for (int j = 0; j < bResp.results[i].referencesSize; ++j) { UA_ReferenceDescription *ref = &(bResp.results[i].references[j]); if(ref->nodeId.nodeId.identifierType == UA_NODEIDTYPE_NUMERIC) { printf("%-9d %-16d %-16.*s %-16.*s\n", ref->browseName.namespaceIndex, ref->nodeId.nodeId.identifier.numeric, ref->browseName.name.length, ref->browseName.name.data, ref->displayName.text.length, ref->displayName.text.data); } else if(ref->nodeId.nodeId.identifierType == UA_NODEIDTYPE_STRING) { printf("%-9d %-16.*s %-16.*s %-16.*s\n", ref->browseName.namespaceIndex, ref->nodeId.nodeId.identifier.string.length, ref->nodeId.nodeId.identifier.string.data, ref->browseName.name.length, ref->browseName.name.data, ref->displayName.text.length, ref->displayName.text.data); } //TODO: distinguish further types } } UA_BrowseRequest_deleteMembers(&bReq); UA_BrowseResponse_deleteMembers(&bResp); UA_Int32 value = 0; // Read node's value printf("\nReading the value of node (1, \"the.answer\"):\n"); UA_ReadRequest rReq; UA_ReadRequest_init(&rReq); rReq.nodesToRead = UA_ReadValueId_new(); rReq.nodesToReadSize = 1; rReq.nodesToRead[0].nodeId = UA_NODEID_STRING_ALLOC(1, "the.answer"); /* assume this node exists */ rReq.nodesToRead[0].attributeId = UA_ATTRIBUTEID_VALUE; UA_ReadResponse rResp = UA_Client_read(client, &rReq); if(rResp.responseHeader.serviceResult == UA_STATUSCODE_GOOD && rResp.resultsSize > 0 && rResp.results[0].hasValue && UA_Variant_isScalar(&rResp.results[0].value) && rResp.results[0].value.type == &UA_TYPES[UA_TYPES_INT32]) { value = *(UA_Int32*)rResp.results[0].value.data; printf("the value is: %i\n", value); } UA_ReadRequest_deleteMembers(&rReq); UA_ReadResponse_deleteMembers(&rResp); value++; // Write node's value printf("\nWriting a value of node (1, \"the.answer\"):\n"); UA_WriteRequest wReq; UA_WriteRequest_init(&wReq); wReq.nodesToWrite = UA_WriteValue_new(); wReq.nodesToWriteSize = 1; wReq.nodesToWrite[0].nodeId = UA_NODEID_STRING_ALLOC(1, "the.answer"); /* assume this node exists */ wReq.nodesToWrite[0].attributeId = UA_ATTRIBUTEID_VALUE; wReq.nodesToWrite[0].value.hasValue = UA_TRUE; wReq.nodesToWrite[0].value.value.type = &UA_TYPES[UA_TYPES_INT32]; wReq.nodesToWrite[0].value.value.storageType = UA_VARIANT_DATA_NODELETE; //do not free the integer on deletion wReq.nodesToWrite[0].value.value.data = &value; UA_WriteResponse wResp = UA_Client_write(client, &wReq); if(wResp.responseHeader.serviceResult == UA_STATUSCODE_GOOD) printf("the new value is: %i\n", value); UA_WriteRequest_deleteMembers(&wReq); UA_WriteResponse_deleteMembers(&wResp); #ifdef ENABLE_ADDNODES /* Create a new object type node */ // New ReferenceType UA_AddNodesResponse *adResp = UA_Client_createReferenceTypeNode(client, UA_EXPANDEDNODEID_NUMERIC(1, 12133), // Assign this NodeId (will fail if client is called multiple times) UA_QUALIFIEDNAME(0, "NewReference"), UA_LOCALIZEDTEXT("en_US", "TheNewReference"), UA_LOCALIZEDTEXT("en_US", "References something that might or might not exist."), UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER), UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), (UA_UInt32) 0, (UA_UInt32) 0, UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER), UA_LOCALIZEDTEXT("en_US", "IsNewlyReferencedBy")); if (adResp->resultsSize > 0 && adResp->results[0].statusCode == UA_STATUSCODE_GOOD ) { printf("Created 'NewReference' with numeric NodeID %u\n", adResp->results[0].addedNodeId.identifier.numeric ); } UA_AddNodesResponse_deleteMembers(adResp); free(adResp); // New ObjectType adResp = UA_Client_createObjectTypeNode(client, UA_EXPANDEDNODEID_NUMERIC(1, 12134), // Assign this NodeId (will fail if client is called multiple times) UA_QUALIFIEDNAME(0, "NewObjectType"), UA_LOCALIZEDTEXT("en_US", "TheNewObjectType"), UA_LOCALIZEDTEXT("en_US", "Put innovative description here."), UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER), UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), (UA_UInt32) 0, (UA_UInt32) 0, UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER)); if (adResp->resultsSize > 0 && adResp->results[0].statusCode == UA_STATUSCODE_GOOD ) { printf("Created 'NewObjectType' with numeric NodeID %u\n", adResp->results[0].addedNodeId.identifier.numeric ); } // New Object adResp = UA_Client_createObjectNode(client, UA_EXPANDEDNODEID_NUMERIC(1, 0), // Assign new/random NodeID UA_QUALIFIEDNAME(0, "TheNewGreatNodeBrowseName"), UA_LOCALIZEDTEXT("en_US", "TheNewGreatNode"), UA_LOCALIZEDTEXT("de_DE", "Hier koennte Ihre Webung stehen!"), UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER), UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), (UA_UInt32) 0, (UA_UInt32) 0, UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER)); if (adResp->resultsSize > 0 && adResp->results[0].statusCode == UA_STATUSCODE_GOOD ) { printf("Created 'NewObject' with numeric NodeID %u\n", adResp->results[0].addedNodeId.identifier.numeric ); } UA_AddNodesResponse_deleteMembers(adResp); free(adResp); // New Integer Variable UA_Variant *theValue = UA_Variant_new(); UA_Int32 *theValueDate = UA_Int32_new(); *theValueDate = 1234; theValue->type = &UA_TYPES[UA_TYPES_INT32]; theValue->data = theValueDate; adResp = UA_Client_createVariableNode(client, UA_EXPANDEDNODEID_NUMERIC(1, 0), // Assign new/random NodeID UA_QUALIFIEDNAME(0, "VariableNode"), UA_LOCALIZEDTEXT("en_US", "TheNewVariableNode"), UA_LOCALIZEDTEXT("en_US", "This integer is just amazing - it has digits and everything."), UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER), UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), (UA_UInt32) 0, (UA_UInt32) 0, UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER), UA_NODEID_NUMERIC(0, UA_NS0ID_INT32), theValue); if (adResp->resultsSize > 0 && adResp->results[0].statusCode == UA_STATUSCODE_GOOD ) { printf("Created 'NewVariable' with numeric NodeID %u\n", adResp->results[0].addedNodeId.identifier.numeric ); } UA_AddNodesResponse_deleteMembers(adResp); free(adResp); free(theValue); /* Done creating a new node*/ #endif UA_Client_disconnect(client); UA_Client_delete(client); return UA_STATUSCODE_GOOD; }
OV_DLLFNCEXPORT OV_RESULT opcuafb_Read_Execute_set( OV_INSTPTR_opcuafb_Read pinst, const OV_BOOL value ) { OV_INSTPTR_opcuafb_Connect pConnect = NULL; OV_INSTPTR_opcuafb_NodeGetHandle pNodeGetHandle = NULL; UA_ReadRequest ReadRequest; UA_ReadResponse ReadResponse; unsigned long int tempulong = 0; char *endPtr = NULL; if(value == FALSE || pinst->v_Execute == TRUE){ //only react on the rising edge pinst->v_Execute = value; return OV_ERR_OK; } pConnect = Ov_DynamicPtrCast(opcuafb_Connect, fb_connection_getFirstConnectedObject(Ov_PtrUpCast(fb_object, pinst), FALSE, TRUE, "ConnectionHdl")); if(pConnect == NULL){ pinst->v_Error = TRUE; pinst->v_ErrorID = 1; //todo return OV_ERR_BADVALUE; } if(pConnect->v_ConnectionHdl == 0){ pinst->v_Error = TRUE; pinst->v_ErrorID = 1; //todo return OV_ERR_BADVALUE; } if(pConnect->v_Client == NULL){ //internal error pinst->v_Error = TRUE; pinst->v_ErrorID = 1; //todo return OV_ERR_BADVALUE; } pNodeGetHandle = Ov_DynamicPtrCast(opcuafb_NodeGetHandle, fb_connection_getFirstConnectedObject(Ov_PtrUpCast(fb_object, pinst), FALSE, TRUE, "NodeHdl")); if(pNodeGetHandle == NULL){ pinst->v_Error = TRUE; pinst->v_ErrorID = 1; //todo return OV_ERR_BADVALUE; } if(pNodeGetHandle->v_NodeHdl == 0){ pinst->v_Error = TRUE; pinst->v_ErrorID = 1; //todo return OV_ERR_BADVALUE; } //free memory of old ANY variable if(pinst->v_Variable.value.vartype == OV_VT_STRING){ ov_string_setvalue(&pinst->v_Variable.value.valueunion.val_string, NULL); pinst->v_Variable.value.vartype = OV_VT_VOID; }else if(pinst->v_Variable.value.vartype & OV_VT_ISVECTOR){ Ov_SetAnyValue(&pinst->v_Variable, NULL); } UA_ReadRequest_init(&ReadRequest); ReadRequest.nodesToRead = UA_ReadValueId_new(); ReadRequest.nodesToReadSize = 1; ReadRequest.nodesToRead[0].attributeId = UA_ATTRIBUTEID_VALUE; if(pNodeGetHandle->v_NodeID.IdentifierType == UA_IDTYPE_STRING){ ReadRequest.nodesToRead[0].nodeId = UA_NODEID_STRING_ALLOC(pNodeGetHandle->v_NodeID.NamespaceIndex, pNodeGetHandle->v_NodeID.Identifier); }else if(pNodeGetHandle->v_NodeID.IdentifierType == UA_IDTYPE_NUMERIC){ tempulong = strtoul(pNodeGetHandle->v_NodeID.Identifier, &endPtr, 10); if (ERANGE != errno && tempulong < UINT16_MAX && endPtr != pNodeGetHandle->v_NodeID.Identifier) { ReadRequest.nodesToRead[0].nodeId = UA_NODEID_NUMERIC(pNodeGetHandle->v_NodeID.NamespaceIndex, (UA_UInt16)tempulong); } // }else if(pNodeGetHandle->v_NodeID.IdentifierType == UA_IDTYPE_GUID){ //todo //ReadRequest.nodesToRead[0].nodeId = UA_NODEID_GUID(pNodeGetHandle->v_NodeID.NamespaceIndex, pNodeGetHandle->v_NodeID.Identifier); }else{ UA_ReadRequest_deleteMembers(&ReadRequest); return OV_ERR_BADVALUE; } ReadResponse = UA_Client_Service_read(pConnect->v_Client, ReadRequest); if(ReadResponse.responseHeader.serviceResult == UA_STATUSCODE_GOOD && ReadResponse.resultsSize > 0 && ReadResponse.results[0].hasValue){ pinst->v_Error = FALSE; pinst->v_ErrorID = 0; pinst->v_Done = TRUE; if(UA_Variant_isScalar(&ReadResponse.results[0].value)){ if(ReadResponse.results[0].hasSourceTimestamp){ pinst->v_TimeStamp = ov_1601nsTimeToOvTime(ReadResponse.results[0].sourceTimestamp); //todo }else if(ReadResponse.results[0].hasSourcePicoseconds){ }else if(ReadResponse.results[0].hasServerTimestamp){ pinst->v_TimeStamp = ov_1601nsTimeToOvTime(ReadResponse.results[0].sourceTimestamp); //todo }else if(ReadResponse.results[0].hasServerPicoseconds){ }else{ ov_time_gettime(&pinst->v_TimeStamp); } if(ReadResponse.results[0].value.type == &UA_TYPES[UA_TYPES_INT32]){ pinst->v_Variable.value.vartype = OV_VT_INT; pinst->v_Variable.value.valueunion.val_int = (OV_INT)*(UA_Int32*)ReadResponse.results[0].value.data; }else if(ReadResponse.results[0].value.type == &UA_TYPES[UA_TYPES_UINT32]){ pinst->v_Variable.value.vartype = OV_VT_UINT; pinst->v_Variable.value.valueunion.val_uint = (OV_UINT)*(UA_UInt32*)ReadResponse.results[0].value.data; }else if(ReadResponse.results[0].value.type == &UA_TYPES[UA_TYPES_FLOAT]){ pinst->v_Variable.value.vartype = OV_VT_SINGLE; pinst->v_Variable.value.valueunion.val_single = (OV_SINGLE)*(UA_Float*)ReadResponse.results[0].value.data; }else if(ReadResponse.results[0].value.type == &UA_TYPES[UA_TYPES_DOUBLE]){ pinst->v_Variable.value.vartype = OV_VT_DOUBLE; pinst->v_Variable.value.valueunion.val_double = (OV_DOUBLE)*(UA_Double*)ReadResponse.results[0].value.data; }else if(ReadResponse.results[0].value.type == &UA_TYPES[UA_TYPES_STRING]){ pinst->v_Variable.value.vartype = OV_VT_STRING; pinst->v_Variable.value.valueunion.val_string = Ov_DbMalloc(((UA_String*)ReadResponse.results[0].value.data)->length); if(pinst->v_Variable.value.valueunion.val_string != NULL){ memcpy(((UA_String*)ReadResponse.results[0].value.data)->data, pinst->v_Variable.value.valueunion.val_string, ((UA_String*)ReadResponse.results[0].value.data)->length); } }else if(ReadResponse.results[0].value.type == &UA_TYPES[UA_TYPES_DATETIME]){ pinst->v_Variable.value.vartype = OV_VT_TIME; pinst->v_Variable.value.valueunion.val_time = ov_1601nsTimeToOvTime((OV_INT64)*(UA_DateTime*)ReadResponse.results[0].value.data); }else{ if(ReadResponse.results[0].value.type == &UA_TYPES[UA_TYPES_STRING]){ //free memory Ov_SetAnyValue(&pinst->v_Variable, NULL); //set length and type Ov_SetDynamicVectorLength(&pinst->v_Variable.value.valueunion.val_string_vec, ReadResponse.results[0].value.arrayLength, STRING); for(size_t iterator = 0; iterator < ReadResponse.results[0].value.arrayLength;iterator++){ pinst->v_Variable.value.valueunion.val_string = Ov_DbMalloc(((UA_String*)ReadResponse.results[0].value.data)[iterator].length); if(pinst->v_Variable.value.valueunion.val_string != NULL){ memcpy(((UA_String*)ReadResponse.results[0].value.data)[iterator].data, pinst->v_Variable.value.valueunion.val_string_vec.value[iterator], ((UA_String*)ReadResponse.results[0].value.data)[iterator].length); } } }else{ //not implemented pinst->v_Done = TRUE; pinst->v_Error = TRUE; pinst->v_ErrorID = 1; //todo } } }else{ //not implemented pinst->v_Done = TRUE; pinst->v_Error = TRUE; pinst->v_ErrorID = 1; //todo } }else{ pinst->v_Done = FALSE; pinst->v_Error = TRUE; pinst->v_ErrorID = 1; //todo } UA_ReadResponse_deleteMembers(&ReadResponse); UA_ReadRequest_deleteMembers(&ReadRequest); pinst->v_Execute = value; return OV_ERR_OK; }
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; goto clean_up_range; } // todo: writing ranges if(hasRange) retval = vn->value.dataSource.write(vn->value.dataSource.handle, &wvalue->value.value, &range); else retval = vn->value.dataSource.write(vn->value.dataSource.handle, &wvalue->value.value, UA_NULL); done = UA_TRUE; goto clean_up_range; } const UA_Variant *oldV = &vn->value.variant; /* the nodeid on the wire may be != the nodeid in the node: opaque types, enums and bytestrings */ if(!UA_NodeId_equal(&oldV->type->typeId, &wvalue->value.value.type->typeId)) { if(oldV->type->namespaceZero && wvalue->value.value.type->namespaceZero && oldV->type->typeIndex == wvalue->value.value.type->typeIndex) /* An enum was sent as an int32, or an opaque type as a bytestring. This is detected with the typeIndex indicated the "true" datatype. */ wvalue->value.value.type = oldV->type; else if(oldV->type == &UA_TYPES[UA_TYPES_BYTE] && !UA_Variant_isScalar(oldV) && wvalue->value.value.type == &UA_TYPES[UA_TYPES_BYTESTRING] && UA_Variant_isScalar(&wvalue->value.value)) { /* a string is written to a byte array */ UA_ByteString *str = (UA_ByteString*) wvalue->value.value.data; wvalue->value.value.arrayLength = str->length; wvalue->value.value.data = str->data; wvalue->value.value.type = &UA_TYPES[UA_TYPES_BYTE]; UA_free(str); } else { retval = UA_STATUSCODE_BADTYPEMISMATCH; goto clean_up_range; } } /* copy the node */ UA_VariableNode *newVn = (node->nodeClass == UA_NODECLASS_VARIABLE) ? UA_VariableNode_new() : (UA_VariableNode*)UA_VariableTypeNode_new(); if(!newVn) { retval = UA_STATUSCODE_BADOUTOFMEMORY; goto clean_up_range; } retval = (node->nodeClass == UA_NODECLASS_VARIABLE) ? UA_VariableNode_copy(vn, newVn) : UA_VariableTypeNode_copy((const UA_VariableTypeNode*)vn, (UA_VariableTypeNode*)newVn); if(retval != UA_STATUSCODE_GOOD) goto clean_up; /* insert the new value */ if(hasRange) retval = UA_Variant_setRangeCopy(&newVn->value.variant, wvalue->value.value.data, wvalue->value.value.arrayLength, range); else { UA_Variant_deleteMembers(&newVn->value.variant); retval = UA_Variant_copy(&wvalue->value.value, &newVn->value.variant); } if(retval == UA_STATUSCODE_GOOD && UA_NodeStore_replace(server->nodestore, node, (UA_Node*)newVn, UA_NULL) == UA_STATUSCODE_GOOD) { done = UA_TRUE; goto clean_up_range; } clean_up: if(node->nodeClass == UA_NODECLASS_VARIABLE) UA_VariableNode_delete(newVn); else UA_VariableTypeNode_delete((UA_VariableTypeNode*)newVn); clean_up_range: if(hasRange) UA_free(range.dimensions); } break; case UA_ATTRIBUTEID_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; }
int main(int argc, char *argv[]) { UA_Client *client = UA_Client_new(UA_ClientConfig_standard); //listing endpoints UA_EndpointDescription* endpointArray = NULL; size_t endpointArraySize = 0; UA_StatusCode retval = UA_Client_getEndpoints(client, "opc.tcp://localhost:16664", &endpointArraySize, &endpointArray); //freeing the endpointArray if(retval != UA_STATUSCODE_GOOD) { //cleanup array UA_Array_delete(endpointArray,endpointArraySize, &UA_TYPES[UA_TYPES_ENDPOINTDESCRIPTION]); UA_Client_delete(client); return (int)retval; } printf("%i endpoints found\n", (int)endpointArraySize); for(size_t i=0;i<endpointArraySize;i++){ printf("URL of endpoint %i is %.*s\n", (int)i, (int)endpointArray[i].endpointUrl.length, endpointArray[i].endpointUrl.data); } //cleanup array of enpoints UA_Array_delete(endpointArray,endpointArraySize, &UA_TYPES[UA_TYPES_ENDPOINTDESCRIPTION]); //connect to a server //anonymous connect would be: retval = UA_Client_connect_username(client, UA_ClientConnectionTCP, "opc.tcp://localhost:16664"); retval = UA_Client_connect_username(client, "opc.tcp://localhost:16664", "user1", "password"); if(retval != UA_STATUSCODE_GOOD) { UA_Client_delete(client); return (int)retval; } // Browse some objects printf("Browsing nodes in objects folder:\n"); UA_BrowseRequest bReq; UA_BrowseRequest_init(&bReq); bReq.requestedMaxReferencesPerNode = 0; bReq.nodesToBrowse = UA_BrowseDescription_new(); bReq.nodesToBrowseSize = 1; bReq.nodesToBrowse[0].nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER); //browse objects folder bReq.nodesToBrowse[0].resultMask = UA_BROWSERESULTMASK_ALL; //return everything UA_BrowseResponse bResp = UA_Client_Service_browse(client, bReq); printf("%-9s %-16s %-16s %-16s\n", "NAMESPACE", "NODEID", "BROWSE NAME", "DISPLAY NAME"); for (size_t i = 0; i < bResp.resultsSize; ++i) { for (size_t j = 0; j < bResp.results[i].referencesSize; ++j) { UA_ReferenceDescription *ref = &(bResp.results[i].references[j]); if(ref->nodeId.nodeId.identifierType == UA_NODEIDTYPE_NUMERIC) { printf("%-9d %-16d %-16.*s %-16.*s\n", ref->browseName.namespaceIndex, ref->nodeId.nodeId.identifier.numeric, (int)ref->browseName.name.length, ref->browseName.name.data, (int)ref->displayName.text.length, ref->displayName.text.data); } else if(ref->nodeId.nodeId.identifierType == UA_NODEIDTYPE_STRING) { printf("%-9d %-16.*s %-16.*s %-16.*s\n", ref->browseName.namespaceIndex, (int)ref->nodeId.nodeId.identifier.string.length, ref->nodeId.nodeId.identifier.string.data, (int)ref->browseName.name.length, ref->browseName.name.data, (int)ref->displayName.text.length, ref->displayName.text.data); } //TODO: distinguish further types } } UA_BrowseRequest_deleteMembers(&bReq); UA_BrowseResponse_deleteMembers(&bResp); // Same thing, this time using the node iterator... UA_NodeId *parent = UA_NodeId_new(); *parent = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER); UA_Client_forEachChildNodeCall(client, UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER), nodeIter, (void *) parent); UA_NodeId_delete(parent); #ifdef UA_ENABLE_SUBSCRIPTIONS // Create a subscription with interval 0 (immediate)... UA_UInt32 subId=0; UA_Client_Subscriptions_new(client, UA_SubscriptionSettings_standard, &subId); if(subId) printf("Create subscription succeeded, id %u\n", subId); // .. and monitor TheAnswer UA_NodeId monitorThis = UA_NODEID_STRING(1, "the.answer"); UA_UInt32 monId=0; UA_Client_Subscriptions_addMonitoredItem(client, subId, monitorThis, UA_ATTRIBUTEID_VALUE, &handler_TheAnswerChanged, NULL, &monId); if (monId) printf("Monitoring 'the.answer', id %u\n", subId); // First Publish always generates data (current value) and call out handler. UA_Client_Subscriptions_manuallySendPublishRequest(client); // This should not generate anything UA_Client_Subscriptions_manuallySendPublishRequest(client); #endif UA_Int32 value = 0; // Read node's value printf("\nReading the value of node (1, \"the.answer\"):\n"); UA_ReadRequest rReq; UA_ReadRequest_init(&rReq); rReq.nodesToRead = UA_Array_new(1, &UA_TYPES[UA_TYPES_READVALUEID]); rReq.nodesToReadSize = 1; rReq.nodesToRead[0].nodeId = UA_NODEID_STRING_ALLOC(1, "the.answer"); /* assume this node exists */ rReq.nodesToRead[0].attributeId = UA_ATTRIBUTEID_VALUE; UA_ReadResponse rResp = UA_Client_Service_read(client, rReq); if(rResp.responseHeader.serviceResult == UA_STATUSCODE_GOOD && rResp.resultsSize > 0 && rResp.results[0].hasValue && UA_Variant_isScalar(&rResp.results[0].value) && rResp.results[0].value.type == &UA_TYPES[UA_TYPES_INT32]) { value = *(UA_Int32*)rResp.results[0].value.data; printf("the value is: %i\n", value); } UA_ReadRequest_deleteMembers(&rReq); UA_ReadResponse_deleteMembers(&rResp); value++; // Write node's value printf("\nWriting a value of node (1, \"the.answer\"):\n"); UA_WriteRequest wReq; UA_WriteRequest_init(&wReq); wReq.nodesToWrite = UA_WriteValue_new(); wReq.nodesToWriteSize = 1; wReq.nodesToWrite[0].nodeId = UA_NODEID_STRING_ALLOC(1, "the.answer"); /* assume this node exists */ wReq.nodesToWrite[0].attributeId = UA_ATTRIBUTEID_VALUE; wReq.nodesToWrite[0].value.hasValue = true; wReq.nodesToWrite[0].value.value.type = &UA_TYPES[UA_TYPES_INT32]; wReq.nodesToWrite[0].value.value.storageType = UA_VARIANT_DATA_NODELETE; //do not free the integer on deletion wReq.nodesToWrite[0].value.value.data = &value; UA_WriteResponse wResp = UA_Client_Service_write(client, wReq); if(wResp.responseHeader.serviceResult == UA_STATUSCODE_GOOD) printf("the new value is: %i\n", value); UA_WriteRequest_deleteMembers(&wReq); UA_WriteResponse_deleteMembers(&wResp); // Alternate Form, this time using the hl API value++; UA_Variant *myVariant = UA_Variant_new(); UA_Variant_setScalarCopy(myVariant, &value, &UA_TYPES[UA_TYPES_INT32]); UA_Client_writeValueAttribute(client, UA_NODEID_STRING(1, "the.answer"), myVariant); UA_Variant_delete(myVariant); #ifdef UA_ENABLE_SUBSCRIPTIONS // Take another look at the.answer... this should call the handler. UA_Client_Subscriptions_manuallySendPublishRequest(client); // Delete our subscription (which also unmonitors all items) if(!UA_Client_Subscriptions_remove(client, subId)) printf("Subscription removed\n"); #endif #ifdef UA_ENABLE_METHODCALLS /* Note: This example requires Namespace 0 Node 11489 (ServerType -> GetMonitoredItems) FIXME: Provide a namespace 0 independant example on the server side */ UA_Variant input; UA_String argString = UA_STRING("Hello Server"); UA_Variant_init(&input); UA_Variant_setScalarCopy(&input, &argString, &UA_TYPES[UA_TYPES_STRING]); size_t outputSize; UA_Variant *output; retval = UA_Client_call(client, UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER), UA_NODEID_NUMERIC(1, 62541), 1, &input, &outputSize, &output); if(retval == UA_STATUSCODE_GOOD) { printf("Method call was successfull, and %lu returned values available.\n", (unsigned long)outputSize); UA_Array_delete(output, outputSize, &UA_TYPES[UA_TYPES_VARIANT]); } else { printf("Method call was unsuccessfull, and %x returned values available.\n", retval); } UA_Variant_deleteMembers(&input); #endif #ifdef UA_ENABLE_NODEMANAGEMENT /* New ReferenceType */ UA_NodeId ref_id; UA_ReferenceTypeAttributes ref_attr; UA_ReferenceTypeAttributes_init(&ref_attr); ref_attr.displayName = UA_LOCALIZEDTEXT("en_US", "NewReference"); ref_attr.description = UA_LOCALIZEDTEXT("en_US", "References something that might or might not exist"); ref_attr.inverseName = UA_LOCALIZEDTEXT("en_US", "IsNewlyReferencedBy"); retval = UA_Client_addReferenceTypeNode(client, UA_NODEID_NUMERIC(1, 12133), UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE), UA_QUALIFIEDNAME(1, "NewReference"), ref_attr, &ref_id); if(retval == UA_STATUSCODE_GOOD ) printf("Created 'NewReference' with numeric NodeID %u\n", ref_id.identifier.numeric); /* New ObjectType */ UA_NodeId objt_id; UA_ObjectTypeAttributes objt_attr; UA_ObjectTypeAttributes_init(&objt_attr); objt_attr.displayName = UA_LOCALIZEDTEXT("en_US", "TheNewObjectType"); objt_attr.description = UA_LOCALIZEDTEXT("en_US", "Put innovative description here"); retval = UA_Client_addObjectTypeNode(client, UA_NODEID_NUMERIC(1, 12134), UA_NODEID_NUMERIC(0, UA_NS0ID_BASEOBJECTTYPE), UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), UA_QUALIFIEDNAME(1, "NewObjectType"), objt_attr, &objt_id); if(retval == UA_STATUSCODE_GOOD) printf("Created 'NewObjectType' with numeric NodeID %u\n", objt_id.identifier.numeric); /* New Object */ UA_NodeId obj_id; UA_ObjectAttributes obj_attr; UA_ObjectAttributes_init(&obj_attr); obj_attr.displayName = UA_LOCALIZEDTEXT("en_US", "TheNewGreatNode"); obj_attr.description = UA_LOCALIZEDTEXT("de_DE", "Hier koennte Ihre Webung stehen!"); retval = UA_Client_addObjectNode(client, UA_NODEID_NUMERIC(1, 0), UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER), UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), UA_QUALIFIEDNAME(1, "TheGreatNode"), UA_NODEID_NUMERIC(1, 12134), obj_attr, &obj_id); if(retval == UA_STATUSCODE_GOOD ) printf("Created 'NewObject' with numeric NodeID %u\n", obj_id.identifier.numeric); /* New Integer Variable */ UA_NodeId var_id; UA_VariableAttributes var_attr; UA_VariableAttributes_init(&var_attr); var_attr.displayName = UA_LOCALIZEDTEXT("en_US", "TheNewVariableNode"); var_attr.description = UA_LOCALIZEDTEXT("en_US", "This integer is just amazing - it has digits and everything."); UA_Int32 int_value = 1234; /* This does not copy the value */ UA_Variant_setScalar(&var_attr.value, &int_value, &UA_TYPES[UA_TYPES_INT32]); var_attr.dataType = UA_TYPES[UA_TYPES_INT32].typeId; retval = UA_Client_addVariableNode(client, UA_NODEID_NUMERIC(1, 0), // Assign new/random NodeID UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER), UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), UA_QUALIFIEDNAME(0, "VariableNode"), UA_NODEID_NULL, // no variable type var_attr, &var_id); if(retval == UA_STATUSCODE_GOOD ) printf("Created 'NewVariable' with numeric NodeID %u\n", var_id.identifier.numeric); #endif UA_Client_disconnect(client); UA_Client_delete(client); return (int) UA_STATUSCODE_GOOD; }
int main(void) { UA_Client *clt; //client UA_StatusCode retval; UA_ReadRequest req; //read request UA_ReadResponse resp; //response /* * Step 1. * create & init the client */ clt = UA_Client_new(UA_ClientConfig_standard); retval = UA_Client_connect(clt, URL); if(retval != UA_STATUSCODE_GOOD){ UA_Client_delete(clt); return retval; } /* * Step 2. * Create read request */ UA_ReadRequest_init(&req); // memset 0 in req req.nodesToRead = UA_Array_new(1, &UA_TYPES[UA_TYPES_READVALUEID]); req.nodesToReadSize = 1; /* * Step 3. * define which node and attribute to be read */ req.nodesToRead[0].nodeId = UA_NODEID_STRING_ALLOC(1, "the.f****r"); req.nodesToRead[0].attributeId = UA_ATTRIBUTEID_VALUE; /* * Step 4. * call the `UA_Client_Service_read` to read */ UA_ReadResponse_init(&resp); resp = UA_Client_Service_read(clt, req); /* if(resp.responseHeader.serviceResult == UA_STATUSCODE_GOOD && resp.resultsSize > 0 && resp.results[0].hasValue && UA_Variant_isScalar(&resp.results[0].value) && resp.results[0].value.type == &UA_TYPES[UA_TYPES_INT32]) {*/ if(resp.responseHeader.serviceResult == UA_STATUSCODE_GOOD && resp.resultsSize > 0 && resp.results[0].hasValue && UA_Variant_isScalar(&resp.results[0].value) && resp.results[0].value.type == &UA_TYPES[UA_TYPES_STRING]) { UA_String *str = (UA_String *)resp.results[0].value.data; printf("The string : %s\n", str->data); /* UA_Int32 *val = (UA_Int32 *)resp.results[0].value.data; printf("The value is: %d\n", *val);*/ } /* * Step 5. * free resource */ UA_ReadRequest_deleteMembers(&req); UA_ReadResponse_deleteMembers(&resp); UA_Client_disconnect(clt); UA_Client_delete(clt); return UA_STATUSCODE_GOOD; }
int main(int argc, char *argv[]) { UA_Client *client = UA_Client_new(UA_ClientConfig_standard, Logger_Stdout_new()); UA_StatusCode retval = UA_Client_connect(client, ClientNetworkLayerTCP_connect, "opc.tcp://localhost:16664"); if(retval != UA_STATUSCODE_GOOD) { UA_Client_delete(client); return retval; } // Browse some objects printf("Browsing nodes in objects folder:\n"); UA_BrowseRequest bReq; UA_BrowseRequest_init(&bReq); bReq.requestedMaxReferencesPerNode = 0; bReq.nodesToBrowse = UA_BrowseDescription_new(); bReq.nodesToBrowseSize = 1; bReq.nodesToBrowse[0].nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER); //browse objects folder bReq.nodesToBrowse[0].resultMask = UA_BROWSERESULTMASK_ALL; //return everything UA_BrowseResponse bResp = UA_Client_browse(client, &bReq); printf("%-9s %-16s %-16s %-16s\n", "NAMESPACE", "NODEID", "BROWSE NAME", "DISPLAY NAME"); for (int i = 0; i < bResp.resultsSize; ++i) { for (int j = 0; j < bResp.results[i].referencesSize; ++j) { UA_ReferenceDescription *ref = &(bResp.results[i].references[j]); if(ref->nodeId.nodeId.identifierType == UA_NODEIDTYPE_NUMERIC) { printf("%-9d %-16d %-16.*s %-16.*s\n", ref->browseName.namespaceIndex, ref->nodeId.nodeId.identifier.numeric, ref->browseName.name.length, ref->browseName.name.data, ref->displayName.text.length, ref->displayName.text.data); } else if(ref->nodeId.nodeId.identifierType == UA_NODEIDTYPE_STRING) { printf("%-9d %-16.*s %-16.*s %-16.*s\n", ref->browseName.namespaceIndex, ref->nodeId.nodeId.identifier.string.length, ref->nodeId.nodeId.identifier.string.data, ref->browseName.name.length, ref->browseName.name.data, ref->displayName.text.length, ref->displayName.text.data); } //TODO: distinguish further types } } UA_BrowseRequest_deleteMembers(&bReq); UA_BrowseResponse_deleteMembers(&bResp); #ifdef ENABLE_SUBSCRIPTIONS // Create a subscription with interval 0 (immediate)... UA_Int32 subId = UA_Client_newSubscription(client, 0); if (subId) printf("Create subscription succeeded, id %u\n", subId); // .. and monitor TheAnswer UA_NodeId monitorThis; monitorThis = UA_NODEID_STRING_ALLOC(1, "the.answer"); UA_UInt32 monId = UA_Client_monitorItemChanges(client, subId, monitorThis, UA_ATTRIBUTEID_VALUE, &handler_TheAnswerChanged ); if (monId) printf("Monitoring 'the.answer', id %u\n", subId); UA_NodeId_deleteMembers(&monitorThis); // First Publish always generates data (current value) and call out handler. UA_Client_doPublish(client); // This should not generate anything UA_Client_doPublish(client); #endif UA_Int32 value = 0; // Read node's value printf("\nReading the value of node (1, \"the.answer\"):\n"); UA_ReadRequest rReq; UA_ReadRequest_init(&rReq); rReq.nodesToRead = UA_ReadValueId_new(); rReq.nodesToReadSize = 1; rReq.nodesToRead[0].nodeId = UA_NODEID_STRING_ALLOC(1, "the.answer"); /* assume this node exists */ rReq.nodesToRead[0].attributeId = UA_ATTRIBUTEID_VALUE; UA_ReadResponse rResp = UA_Client_read(client, &rReq); if(rResp.responseHeader.serviceResult == UA_STATUSCODE_GOOD && rResp.resultsSize > 0 && rResp.results[0].hasValue && UA_Variant_isScalar(&rResp.results[0].value) && rResp.results[0].value.type == &UA_TYPES[UA_TYPES_INT32]) { value = *(UA_Int32*)rResp.results[0].value.data; printf("the value is: %i\n", value); } UA_ReadRequest_deleteMembers(&rReq); UA_ReadResponse_deleteMembers(&rResp); value++; // Write node's value printf("\nWriting a value of node (1, \"the.answer\"):\n"); UA_WriteRequest wReq; UA_WriteRequest_init(&wReq); wReq.nodesToWrite = UA_WriteValue_new(); wReq.nodesToWriteSize = 1; wReq.nodesToWrite[0].nodeId = UA_NODEID_STRING_ALLOC(1, "the.answer"); /* assume this node exists */ wReq.nodesToWrite[0].attributeId = UA_ATTRIBUTEID_VALUE; wReq.nodesToWrite[0].value.hasValue = UA_TRUE; wReq.nodesToWrite[0].value.value.type = &UA_TYPES[UA_TYPES_INT32]; wReq.nodesToWrite[0].value.value.storageType = UA_VARIANT_DATA_NODELETE; //do not free the integer on deletion wReq.nodesToWrite[0].value.value.data = &value; UA_WriteResponse wResp = UA_Client_write(client, &wReq); if(wResp.responseHeader.serviceResult == UA_STATUSCODE_GOOD) printf("the new value is: %i\n", value); UA_WriteRequest_deleteMembers(&wReq); UA_WriteResponse_deleteMembers(&wResp); #ifdef ENABLE_SUBSCRIPTIONS // Take another look at the.answer... this should call the handler. UA_Client_doPublish(client); // Delete our subscription (which also unmonitors all items) if(!UA_Client_removeSubscription(client, subId)) printf("Subscription removed\n"); #endif #ifdef ENABLE_METHODCALLS /* Note: This example requires Namespace 0 Node 11489 (ServerType -> GetMonitoredItems) FIXME: Provide a namespace 0 independant example on the server side */ UA_Variant input; UA_String argString = UA_STRING("Hello Server"); UA_Variant_init(&input); UA_Variant_setScalarCopy(&input, &argString, &UA_TYPES[UA_TYPES_STRING]); UA_Int32 outputSize; UA_Variant *output; retval = UA_Client_CallServerMethod(client, UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER), UA_NODEID_NUMERIC(1, 62541), 1, &input, &outputSize, &output); if(retval == UA_STATUSCODE_GOOD) { printf("Method call was successfull, and %i returned values available.\n", outputSize); UA_Array_delete(output, &UA_TYPES[UA_TYPES_VARIANT], outputSize); } else { printf("Method call was unsuccessfull, and %x returned values available.\n", retval); } UA_Variant_deleteMembers(&input); #endif #ifdef ENABLE_ADDNODES /* Create a new object type node */ // New ReferenceType UA_AddNodesResponse *adResp = UA_Client_createReferenceTypeNode(client, UA_EXPANDEDNODEID_NUMERIC(1, 12133), // Assign this NodeId (will fail if client is called multiple times) UA_QUALIFIEDNAME(0, "NewReference"), UA_LOCALIZEDTEXT("en_US", "TheNewReference"), UA_LOCALIZEDTEXT("en_US", "References something that might or might not exist."), UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER), UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), (UA_UInt32) 0, (UA_UInt32) 0, UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER), UA_LOCALIZEDTEXT("en_US", "IsNewlyReferencedBy")); if (adResp->resultsSize > 0 && adResp->results[0].statusCode == UA_STATUSCODE_GOOD ) { printf("Created 'NewReference' with numeric NodeID %u\n", adResp->results[0].addedNodeId.identifier.numeric ); } UA_AddNodesResponse_deleteMembers(adResp); free(adResp); // New ObjectType adResp = UA_Client_createObjectTypeNode(client, UA_EXPANDEDNODEID_NUMERIC(1, 12134), // Assign this NodeId (will fail if client is called multiple times) UA_QUALIFIEDNAME(0, "NewObjectType"), UA_LOCALIZEDTEXT("en_US", "TheNewObjectType"), UA_LOCALIZEDTEXT("en_US", "Put innovative description here."), UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER), UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), (UA_UInt32) 0, (UA_UInt32) 0, UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER)); if (adResp->resultsSize > 0 && adResp->results[0].statusCode == UA_STATUSCODE_GOOD ) { printf("Created 'NewObjectType' with numeric NodeID %u\n", adResp->results[0].addedNodeId.identifier.numeric ); } // New Object adResp = UA_Client_createObjectNode(client, UA_EXPANDEDNODEID_NUMERIC(1, 0), // Assign new/random NodeID UA_QUALIFIEDNAME(0, "TheNewGreatNodeBrowseName"), UA_LOCALIZEDTEXT("en_US", "TheNewGreatNode"), UA_LOCALIZEDTEXT("de_DE", "Hier koennte Ihre Webung stehen!"), UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER), UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), (UA_UInt32) 0, (UA_UInt32) 0, UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER)); if (adResp->resultsSize > 0 && adResp->results[0].statusCode == UA_STATUSCODE_GOOD ) { printf("Created 'NewObject' with numeric NodeID %u\n", adResp->results[0].addedNodeId.identifier.numeric ); } UA_AddNodesResponse_deleteMembers(adResp); free(adResp); // New Integer Variable UA_Variant *theValue = UA_Variant_new(); UA_Int32 *theValueDate = UA_Int32_new(); *theValueDate = 1234; theValue->type = &UA_TYPES[UA_TYPES_INT32]; theValue->data = theValueDate; adResp = UA_Client_createVariableNode(client, UA_EXPANDEDNODEID_NUMERIC(1, 0), // Assign new/random NodeID UA_QUALIFIEDNAME(0, "VariableNode"), UA_LOCALIZEDTEXT("en_US", "TheNewVariableNode"), UA_LOCALIZEDTEXT("en_US", "This integer is just amazing - it has digits and everything."), UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER), UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), (UA_UInt32) 0, (UA_UInt32) 0, UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER), UA_NODEID_NUMERIC(0, UA_NS0ID_INT32), theValue); if (adResp->resultsSize > 0 && adResp->results[0].statusCode == UA_STATUSCODE_GOOD ) { printf("Created 'NewVariable' with numeric NodeID %u\n", adResp->results[0].addedNodeId.identifier.numeric ); } UA_AddNodesResponse_deleteMembers(adResp); free(adResp); free(theValue); /* Done creating a new node*/ #endif UA_Client_disconnect(client); UA_Client_delete(client); return UA_STATUSCODE_GOOD; }