static UA_Server* makeTestSequence(void) { UA_Server *server = UA_Server_new(UA_ServerConfig_standard); /* VariableNode */ UA_Variant *myIntegerVariant = UA_Variant_new(); UA_Int32 myInteger = 42; UA_Variant_setScalarCopy(myIntegerVariant, &myInteger, &UA_TYPES[UA_TYPES_INT32]); const UA_QualifiedName myIntegerName = UA_QUALIFIEDNAME(1, "the answer"); const UA_NodeId myIntegerNodeId = UA_NODEID_STRING(1, "the.answer"); UA_NodeId parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER); UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES); UA_Server_addVariableNode(server, myIntegerNodeId, myIntegerName, UA_LOCALIZEDTEXT("en_US","the answer"), UA_LOCALIZEDTEXT("en_US","the answer"), 0, 0, parentNodeId, parentReferenceNodeId, myIntegerVariant, NULL); /* ObjectNode */ UA_Server_addObjectNode(server, UA_NODEID_NUMERIC(1, 50), UA_QUALIFIEDNAME(1, "Demo"), UA_LOCALIZEDTEXT("en_US","Demo"), UA_LOCALIZEDTEXT("en_US","Demo"), 0, 0, UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER), UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE), NULL); /* ReferenceTypeNode */ UA_ReferenceTypeNode *organizes = UA_ReferenceTypeNode_new(); copyNames((UA_Node*)organizes, "Organizes"); organizes->inverseName = UA_LOCALIZEDTEXT_ALLOC("", "OrganizedBy"); organizes->nodeId.identifier.numeric = UA_NS0ID_ORGANIZES; organizes->isAbstract = UA_FALSE; organizes->symmetric = UA_FALSE; UA_Server_addNode(server, (UA_Node*)organizes, UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_HIERARCHICALREFERENCES), UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE)); /* ViewNode */ UA_ViewNode *viewtest = UA_ViewNode_new(); copyNames((UA_Node*)viewtest, "Viewtest"); viewtest->nodeId.identifier.numeric = UA_NS0ID_VIEWNODE; UA_Server_addNode(server, (UA_Node*)viewtest, UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_VIEWSFOLDER), UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE)); /* MethodNode */ UA_MethodNode *methodtest = UA_MethodNode_new(); copyNames((UA_Node*)methodtest, "Methodtest"); methodtest->nodeId.identifier.numeric = UA_NS0ID_METHODNODE; UA_Server_addNode(server, (UA_Node*)methodtest, UA_EXPANDEDNODEID_NUMERIC(0, 3), UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE)); return server; }
int main(void) { UA_Client *client = UA_Client_new(); UA_ClientConfig_setDefault(UA_Client_getConfig(client)); UA_StatusCode retval = UA_Client_connect_username(client, "opc.tcp://localhost:4840", "paula", "paula123"); if(retval != UA_STATUSCODE_GOOD) { UA_Client_delete(client); return EXIT_FAILURE; } UA_NodeId newVariableIdRequest = UA_NODEID_NUMERIC(1, 1001); UA_NodeId newVariableId = UA_NODEID_NULL; UA_VariableAttributes newVariableAttributes = UA_VariableAttributes_default; newVariableAttributes.accessLevel = UA_ACCESSLEVELMASK_READ; newVariableAttributes.description = UA_LOCALIZEDTEXT_ALLOC("en-US", "NewVariable desc"); newVariableAttributes.displayName = UA_LOCALIZEDTEXT_ALLOC("en-US", "NewVariable"); newVariableAttributes.dataType = UA_TYPES[UA_TYPES_UINT32].typeId; UA_UInt32 value = 50; UA_Variant_setScalarCopy(&newVariableAttributes.value, &value, &UA_TYPES[UA_TYPES_UINT32]); UA_StatusCode retCode; retCode = UA_Client_addVariableNode(client, newVariableIdRequest, UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER), UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), UA_QUALIFIEDNAME(1, "newVariable"), UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE), newVariableAttributes, &newVariableId); printf("addVariable returned: %s\n", UA_StatusCode_name(retCode)); UA_ExpandedNodeId extNodeId = UA_EXPANDEDNODEID_NUMERIC(0, 0); extNodeId.nodeId = newVariableId; retCode = UA_Client_addReference(client, UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER), UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), UA_TRUE, UA_STRING_NULL, extNodeId, UA_NODECLASS_VARIABLE); printf("addReference returned: %s\n", UA_StatusCode_name(retCode)); retCode = UA_Client_deleteReference(client, UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER), UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), UA_TRUE, extNodeId, UA_TRUE); printf("deleteReference returned: %s\n", UA_StatusCode_name(retCode)); retCode = UA_Client_deleteNode(client, newVariableId, UA_TRUE); printf("deleteNode returned: %s\n", UA_StatusCode_name(retCode)); /* Clean up */ UA_VariableAttributes_clear(&newVariableAttributes); UA_Client_delete(client); /* Disconnects the client internally */ return EXIT_SUCCESS; }
END_TEST START_TEST(Nodes_createCustomObjectType) { // Create a custom object type "CustomDemoType" which has a "CustomStateType" component UA_StatusCode retval = UA_STATUSCODE_GOOD; /* create new object type node which has a subcomponent of the type StateType */ UA_ObjectTypeAttributes otAttr = UA_ObjectTypeAttributes_default; otAttr.displayName = UA_LOCALIZEDTEXT("", "CustomDemoType"); otAttr.description = UA_LOCALIZEDTEXT("", ""); retval = UA_Server_addObjectTypeNode(server, UA_NODEID_NUMERIC(1, 6010), UA_NODEID_NUMERIC(0, UA_NS0ID_BASEOBJECTTYPE), UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE), UA_QUALIFIEDNAME(1, "CustomDemoType"), otAttr, NULL, NULL); ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD); UA_ObjectAttributes oAttr = UA_ObjectAttributes_default; oAttr.displayName = UA_LOCALIZEDTEXT("", "State"); oAttr.description = UA_LOCALIZEDTEXT("", ""); retval = UA_Server_addObjectNode(server, UA_NODEID_NUMERIC(1, 6011), UA_NODEID_NUMERIC(1, 6010), UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), UA_QUALIFIEDNAME(1, "State"), UA_NODEID_NUMERIC(1, 6000), oAttr, NULL, NULL); ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD); /* modelling rule is mandatory so it will be inherited for the object created from CustomDemoType */ retval = UA_Server_addReference(server, UA_NODEID_NUMERIC(1, 6011), UA_NODEID_NUMERIC(0, UA_NS0ID_HASMODELLINGRULE), UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_MODELLINGRULE_MANDATORY), true); ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD); /* assign a default value to the attribute "StateNumber" inside the state attribute (part of the MyDemoType) */ UA_Variant stateNum; UA_Variant_init(&stateNum); UA_Variant_setScalar(&stateNum, &valueToBeInherited, &UA_TYPES[UA_TYPES_UINT32]); UA_NodeId childID; findChildId(UA_NODEID_NUMERIC(1, 6011), UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY), UA_QUALIFIEDNAME(1, "CustomStateNumber"), &childID); ck_assert(!UA_NodeId_isNull(&childID)); retval = UA_Server_writeValue(server, childID, stateNum); ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD); }
UA_StatusCode UA_Server_addDataSourceVariableNode(UA_Server *server, UA_DataSource dataSource, const UA_QualifiedName browseName, UA_NodeId nodeId, const UA_NodeId parentNodeId, const UA_NodeId referenceTypeId) { UA_VariableNode *node = UA_VariableNode_new(); node->valueSource = UA_VALUESOURCE_DATASOURCE; node->value.dataSource = dataSource; UA_NodeId_copy(&nodeId, &node->nodeId); UA_QualifiedName_copy(&browseName, &node->browseName); UA_String_copy(&browseName.name, &node->displayName.text); UA_ExpandedNodeId parentId; // dummy exapndednodeid UA_ExpandedNodeId_init(&parentId); UA_NodeId_copy(&parentNodeId, &parentId.nodeId); UA_AddNodesResult res = UA_Server_addNodeWithSession(server, &adminSession, (UA_Node*)node, parentId, referenceTypeId); ADDREFERENCE(res.addedNodeId, UA_NODEID_NUMERIC(0, UA_NS0ID_HASTYPEDEFINITION), UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE)); if(res.statusCode != UA_STATUSCODE_GOOD) UA_VariableNode_delete(node); UA_AddNodesResult_deleteMembers(&res); return res.statusCode; }
UA_StatusCode UA_Server_addVariableNode(UA_Server *server, UA_Variant *value, const UA_QualifiedName browseName, UA_NodeId nodeId, const UA_NodeId parentNodeId, const UA_NodeId referenceTypeId) { UA_VariableNode *node = UA_VariableNode_new(); node->value.variant = *value; // copy content UA_NodeId_copy(&nodeId, &node->nodeId); UA_QualifiedName_copy(&browseName, &node->browseName); UA_String_copy(&browseName.name, &node->displayName.text); UA_ExpandedNodeId parentId; // we need an expandednodeid UA_ExpandedNodeId_init(&parentId); UA_NodeId_copy(&parentNodeId, &parentId.nodeId); UA_AddNodesResult res = UA_Server_addNodeWithSession(server, &adminSession, (UA_Node*)node, parentId, referenceTypeId); ADDREFERENCE(res.addedNodeId, UA_NODEID_NUMERIC(0, UA_NS0ID_HASTYPEDEFINITION), UA_EXPANDEDNODEID_NUMERIC(0, value->type->typeId.identifier.numeric)); if(res.statusCode != UA_STATUSCODE_GOOD) { UA_Variant_init(&node->value.variant); UA_VariableNode_delete(node); } else UA_free(value); UA_AddNodesResult_deleteMembers(&res); return res.statusCode; }
OV_DLLFNCEXPORT UA_StatusCode servicesOPCUAInterface_interface_ovServiceOutputArgumentsNodeToOPCUA( void *handle, const UA_NodeId *nodeId, UA_Node** opcuaNode) { UA_Node *newNode = NULL; UA_StatusCode result = UA_STATUSCODE_GOOD; OV_PATH path; OV_INSTPTR_ov_object pobj = NULL; OV_TICKET *pTicket = NULL; OV_VTBLPTR_ov_object pVtblObj = NULL; OV_ACCESS access; UA_NodeClass nodeClass; OV_ELEMENT element; OV_STRING tmpString = NULL; OV_UINT len = 0; OV_STRING *plist = NULL; copyOPCUAStringToOV(nodeId->identifier.string, &tmpString); plist = ov_string_split(tmpString, "||", &len); ov_string_setvalue(&tmpString, NULL); UA_NodeId tmpNodeId; UA_NodeId_init(&tmpNodeId); tmpNodeId.namespaceIndex = nodeId->namespaceIndex; tmpNodeId.identifierType = nodeId->identifierType; tmpNodeId.identifier.string = UA_String_fromChars(plist[0]); ov_string_freelist(plist); ov_memstack_lock(); result = opcua_nodeStoreFunctions_resolveNodeIdToPath(tmpNodeId, &path); UA_NodeId_deleteMembers(&tmpNodeId); if(result != UA_STATUSCODE_GOOD){ ov_memstack_unlock(); return result; } element = path.elements[path.size-1]; ov_memstack_unlock(); result = opcua_nodeStoreFunctions_getVtblPointerAndCheckAccess(&(element), pTicket, &pobj, &pVtblObj, &access); if(result != UA_STATUSCODE_GOOD){ return result; } nodeClass = UA_NODECLASS_VARIABLE; newNode = (UA_Node*)UA_calloc(1, sizeof(UA_VariableNode)); // Basic Attribute // BrowseName UA_QualifiedName qName; qName.name = UA_String_fromChars("OutputArguments"); qName.namespaceIndex = 0; //pinterface->v_interfacenamespace.index; newNode->browseName = qName; // Description OV_STRING tempString = pVtblObj->m_getcomment(pobj, &element); UA_LocalizedText lText; UA_LocalizedText_init(&lText); lText.locale = UA_String_fromChars("en"); if(tempString){ lText.text = UA_String_fromChars(tempString); } else { lText.text = UA_String_fromChars(""); } UA_LocalizedText_copy(&lText,&newNode->description); UA_LocalizedText_deleteMembers(&lText); // DisplayName UA_LocalizedText displayName; UA_LocalizedText_init(&displayName); displayName.locale = UA_String_fromChars("en"); displayName.text = UA_String_fromChars("OutputArguments"); UA_LocalizedText_copy(&displayName, &newNode->displayName); UA_LocalizedText_deleteMembers(&displayName); // NodeId UA_NodeId_copy(nodeId, &newNode->nodeId); // NodeClass newNode->nodeClass = nodeClass; // WriteMask UA_UInt32 writeMask = 0; if(element.elemtype != OV_ET_VARIABLE){ if(access & OV_AC_WRITE){ writeMask |= (1<<2); // BrowseName writeMask |= (1<<6); // DisplayName } if(access & OV_AC_RENAMEABLE){ writeMask |= (1<<14); // NodeId } } newNode->writeMask = writeMask; // Variable specific attributes // value OV_ELEMENT tmpPart; tmpPart.elemtype = OV_ET_NONE; tmpPart.pobj = NULL; OV_ELEMENT tmpParrent; tmpParrent.pobj = pobj; tmpParrent.elemtype = OV_ET_OBJECT; OV_UINT sizeOutput = 0; do { ov_element_getnextpart(&tmpParrent, &tmpPart, OV_ET_VARIABLE); if (tmpPart.elemtype == OV_ET_NONE) break; if (tmpPart.elemunion.pvar->v_flags == 16384){ // OutputFlag is set sizeOutput++; continue; } } while(TRUE); // arrayDemensions if (sizeOutput > 1){ ((UA_VariableNode*)newNode)->arrayDimensionsSize = 1; UA_UInt32 *pOutputDimension = UA_UInt32_new(); *pOutputDimension = sizeOutput; ((UA_VariableNode*)newNode)->arrayDimensions = pOutputDimension; //UA_Array_new(((UA_VariableNode*)newNode)->arrayDimensionsSize, &UA_TYPES[UA_TYPES_INT32]); /* scalar or one dimension */ // valuerank ((UA_VariableNode*)newNode)->valueRank = 1; /* one dimension */ }else{ ((UA_VariableNode*)newNode)->arrayDimensionsSize = 0; ((UA_VariableNode*)newNode)->arrayDimensions = NULL; //UA_Array_new(((UA_VariableNode*)newNode)->arrayDimensionsSize, &UA_TYPES[UA_TYPES_INT32]); /* scalar or one dimension */ // valuerank ((UA_VariableNode*)newNode)->valueRank = -1; /* one dimension */ } // value ((UA_Variant*)&((UA_VariableNode*)newNode)->value.data.value.value)->type = &UA_TYPES[UA_TYPES_ARGUMENT]; ((UA_Variant*)&((UA_VariableNode*)newNode)->value.data.value.value)->arrayLength = sizeOutput; if (sizeOutput > 1){ ((UA_Variant*)&((UA_VariableNode*)newNode)->value.data.value.value)->data = UA_Array_new(sizeOutput, &UA_TYPES[UA_TYPES_ARGUMENT]); }else{ ((UA_Variant*)&((UA_VariableNode*)newNode)->value.data.value.value)->data = UA_Argument_new(); } if (!((UA_Variant*)&((UA_VariableNode*)newNode)->value.data.value.value)->data){ result = UA_STATUSCODE_BADOUTOFMEMORY; return result; } ((UA_VariableNode*)newNode)->value.data.value.hasValue = TRUE; ((UA_VariableNode*)newNode)->valueSource = UA_VALUESOURCE_DATA; tmpPart.elemtype = OV_ET_NONE; tmpPart.pobj = NULL; OV_UINT count = 0; do { ov_element_getnextpart(&tmpParrent, &tmpPart, OV_ET_VARIABLE); if (tmpPart.elemtype == OV_ET_NONE) break; if (tmpPart.elemunion.pvar->v_flags == 16384){ // OutputFlag is set UA_Argument_init(&((UA_Argument*)((UA_Variant*)&((UA_VariableNode*)newNode)->value.data.value.value)->data)[count]); ((UA_Argument*)((UA_Variant*)&((UA_VariableNode*)newNode)->value.data.value.value)->data)[count].description = UA_LOCALIZEDTEXT_ALLOC("en_US",""); ((UA_Argument*)((UA_Variant*)&((UA_VariableNode*)newNode)->value.data.value.value)->data)[count].name = UA_STRING_ALLOC(tmpPart.elemunion.pvar->v_identifier); ((UA_Argument*)((UA_Variant*)&((UA_VariableNode*)newNode)->value.data.value.value)->data)[count].valueRank = -1; switch(tmpPart.elemunion.pvar->v_vartype){ case OV_VT_BOOL: ((UA_Argument*)((UA_Variant*)&((UA_VariableNode*)newNode)->value.data.value.value)->data)[count].dataType = UA_TYPES[UA_TYPES_BOOLEAN].typeId; ((UA_Argument*)((UA_Variant*)&((UA_VariableNode*)newNode)->value.data.value.value)->data)[count].valueRank = -1; break; case OV_VT_INT: ((UA_Argument*)((UA_Variant*)&((UA_VariableNode*)newNode)->value.data.value.value)->data)[count].dataType = UA_TYPES[UA_TYPES_INT32].typeId; ((UA_Argument*)((UA_Variant*)&((UA_VariableNode*)newNode)->value.data.value.value)->data)[count].valueRank = -1; break; case OV_VT_UINT: ((UA_Argument*)((UA_Variant*)&((UA_VariableNode*)newNode)->value.data.value.value)->data)[count].dataType = UA_TYPES[UA_TYPES_UINT32].typeId; ((UA_Argument*)((UA_Variant*)&((UA_VariableNode*)newNode)->value.data.value.value)->data)[count].valueRank = -1; break; case OV_VT_SINGLE: ((UA_Argument*)((UA_Variant*)&((UA_VariableNode*)newNode)->value.data.value.value)->data)[count].dataType = UA_TYPES[UA_TYPES_FLOAT].typeId; ((UA_Argument*)((UA_Variant*)&((UA_VariableNode*)newNode)->value.data.value.value)->data)[count].valueRank = -1; break; case OV_VT_DOUBLE: ((UA_Argument*)((UA_Variant*)&((UA_VariableNode*)newNode)->value.data.value.value)->data)[count].dataType = UA_TYPES[UA_TYPES_DOUBLE].typeId; ((UA_Argument*)((UA_Variant*)&((UA_VariableNode*)newNode)->value.data.value.value)->data)[count].valueRank = -1; break; case OV_VT_ANY: ((UA_Argument*)((UA_Variant*)&((UA_VariableNode*)newNode)->value.data.value.value)->data)[count].dataType = UA_TYPES[UA_TYPES_VARIANT].typeId; ((UA_Argument*)((UA_Variant*)&((UA_VariableNode*)newNode)->value.data.value.value)->data)[count].valueRank = 1; break; case OV_VT_STRING: ((UA_Argument*)((UA_Variant*)&((UA_VariableNode*)newNode)->value.data.value.value)->data)[count].dataType = UA_TYPES[UA_TYPES_STRING].typeId; ((UA_Argument*)((UA_Variant*)&((UA_VariableNode*)newNode)->value.data.value.value)->data)[count].valueRank = -1; break; case OV_VT_BOOL_VEC: ((UA_Argument*)((UA_Variant*)&((UA_VariableNode*)newNode)->value.data.value.value)->data)[count].dataType = UA_TYPES[UA_TYPES_BOOLEAN].typeId; ((UA_Argument*)((UA_Variant*)&((UA_VariableNode*)newNode)->value.data.value.value)->data)[count].valueRank = 1; break; case OV_VT_INT_VEC: ((UA_Argument*)((UA_Variant*)&((UA_VariableNode*)newNode)->value.data.value.value)->data)[count].dataType = UA_TYPES[UA_TYPES_INT32].typeId; ((UA_Argument*)((UA_Variant*)&((UA_VariableNode*)newNode)->value.data.value.value)->data)[count].valueRank = 1; break; case OV_VT_UINT_VEC: ((UA_Argument*)((UA_Variant*)&((UA_VariableNode*)newNode)->value.data.value.value)->data)[count].dataType = UA_TYPES[UA_TYPES_UINT32].typeId; ((UA_Argument*)((UA_Variant*)&((UA_VariableNode*)newNode)->value.data.value.value)->data)[count].valueRank = 1; break; case OV_VT_SINGLE_VEC: ((UA_Argument*)((UA_Variant*)&((UA_VariableNode*)newNode)->value.data.value.value)->data)[count].dataType = UA_TYPES[UA_TYPES_FLOAT].typeId; ((UA_Argument*)((UA_Variant*)&((UA_VariableNode*)newNode)->value.data.value.value)->data)[count].valueRank = 1; break; case OV_VT_DOUBLE_VEC: ((UA_Argument*)((UA_Variant*)&((UA_VariableNode*)newNode)->value.data.value.value)->data)[count].dataType = UA_TYPES[UA_TYPES_DOUBLE].typeId; ((UA_Argument*)((UA_Variant*)&((UA_VariableNode*)newNode)->value.data.value.value)->data)[count].valueRank = 1; break; case OV_VT_STRING_VEC: ((UA_Argument*)((UA_Variant*)&((UA_VariableNode*)newNode)->value.data.value.value)->data)[count].dataType = UA_TYPES[UA_TYPES_STRING].typeId; ((UA_Argument*)((UA_Variant*)&((UA_VariableNode*)newNode)->value.data.value.value)->data)[count].valueRank = 1; break; default: break; } count++; if (count == sizeOutput){ break; }else{ continue; } } } while(TRUE); ov_string_setvalue(&tmpString, NULL); // accessLevel UA_Byte accessLevel = 0; if(access & OV_AC_READ){ accessLevel |= (1<<0); } if(access & OV_AC_WRITE){ accessLevel |= (1<<1); } ((UA_VariableNode*)newNode)->accessLevel = accessLevel; // minimumSamplingInterval ((UA_VariableNode*)newNode)->minimumSamplingInterval = -1; // historizing ((UA_VariableNode*)newNode)->historizing = UA_FALSE; // dataType ((UA_VariableNode*)newNode)->dataType = UA_NODEID_NUMERIC(0, 0); // References have to do manual because it is an virtual node size_t size_references = 0; size_references = size_references + 2;// For Parent&TypeNode newNode->references = UA_calloc(size_references, sizeof(UA_ReferenceNode)); if (!newNode->references){ result = ov_resultToUaStatusCode(OV_ERR_HEAPOUTOFMEMORY); UA_free(newNode); return result; } newNode->referencesSize = size_references; // ParentNode newNode->references[0].referenceTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY); newNode->references[0].isInverse = UA_TRUE; len = 0; plist = NULL; tmpString = NULL; copyOPCUAStringToOV(nodeId->identifier.string, &tmpString); plist = ov_string_split(tmpString, "||", &len); newNode->references[0].targetId = UA_EXPANDEDNODEID_STRING_ALLOC(pinterface->v_interfacenamespace.index, plist[0]); ov_string_freelist(plist); ov_string_setvalue(&tmpString, NULL); // TypeNode newNode->references[1].referenceTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_HASTYPEDEFINITION); newNode->references[1].isInverse = UA_FALSE; newNode->references[1].targetId = UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_PROPERTYTYPE); *opcuaNode = newNode; return UA_STATUSCODE_GOOD; }
OV_DLLFNCEXPORT UA_StatusCode propertyValueStatementOPCUAInterface_interface_ovExpressionSemanticNodeToOPCUA( void *handle, const UA_NodeId *nodeId, UA_Node** opcuaNode) { UA_Node *newNode = NULL; UA_StatusCode result = UA_STATUSCODE_GOOD; OV_PATH path; OV_INSTPTR_ov_object pobj = NULL; OV_TICKET *pTicket = NULL; OV_VTBLPTR_ov_object pVtblObj = NULL; OV_ACCESS access; UA_NodeClass nodeClass; OV_ELEMENT element; ov_memstack_lock(); result = opcua_nodeStoreFunctions_resolveNodeIdToPath(*nodeId, &path); if(result != UA_STATUSCODE_GOOD){ ov_memstack_unlock(); return result; } element = path.elements[path.size-1]; ov_memstack_unlock(); result = opcua_nodeStoreFunctions_getVtblPointerAndCheckAccess(&(element), pTicket, &pobj, &pVtblObj, &access); if(result != UA_STATUSCODE_GOOD){ return result; } nodeClass = UA_NODECLASS_VARIABLE; newNode = (UA_Node*)UA_calloc(1, sizeof(UA_VariableNode)); // Basic Attribute // BrowseName UA_QualifiedName qName; qName.name = UA_String_fromChars(pobj->v_identifier); qName.namespaceIndex = nodeId->namespaceIndex; newNode->browseName = qName; // Description // Description OV_STRING tempString = pVtblObj->m_getcomment(pobj, &element); UA_LocalizedText lText; UA_LocalizedText_init(&lText); lText.locale = UA_String_fromChars("en"); if(tempString){ lText.text = UA_String_fromChars(tempString); } else { lText.text = UA_String_fromChars(""); } UA_LocalizedText_copy(&lText,&newNode->description); UA_LocalizedText_deleteMembers(&lText); // DisplayName UA_LocalizedText displayName; UA_LocalizedText_init(&displayName); displayName.locale = UA_String_fromChars("en"); displayName.text = UA_String_fromChars(pobj->v_identifier); UA_LocalizedText_copy(&displayName, &newNode->displayName); UA_LocalizedText_deleteMembers(&displayName); // NodeId UA_NodeId_copy(nodeId, &newNode->nodeId); // NodeClass newNode->nodeClass = nodeClass; // WriteMask UA_UInt32 writeMask = 0; if(element.elemtype != OV_ET_VARIABLE){ if(access & OV_AC_WRITE){ writeMask |= (1<<2); // BrowseName writeMask |= (1<<6); // DisplayName } if(access & OV_AC_RENAMEABLE){ writeMask |= (1<<14); // NodeId } } newNode->writeMask = writeMask; // Variable specific attributes // arrayDemensions ((UA_VariableNode*)newNode)->arrayDimensionsSize = 0; ((UA_VariableNode*)newNode)->arrayDimensions = NULL; // UA_Array_new(((UA_VariableNode*)newNode)->arrayDimensionsSize, &UA_TYPES[UA_TYPES_INT32]); /* scalar or one dimension */ // valuerank ((UA_VariableNode*)newNode)->valueRank = 1; /* one dimension */ // value OV_ELEMENT tmpPart; tmpPart.elemtype = OV_ET_NONE; OV_ELEMENT tmpParrent; tmpParrent.pobj = pobj; tmpParrent.elemtype = OV_ET_OBJECT; UA_ExpressionSemanticEnum tmpExpressionSemantic = 0; do { ov_element_getnextpart(&tmpParrent, &tmpPart, OV_ET_VARIABLE); if (tmpPart.elemtype == OV_ET_NONE) break; if (ov_string_compare(tmpPart.elemunion.pvar->v_identifier, "ExpressionSemanticEnum") == OV_STRCMP_EQUAL){ tmpExpressionSemantic = *(UA_UInt32*)tmpPart.pvalue; break; } } while(TRUE); ((UA_Variant*)&((UA_VariableNode*)newNode)->value.data.value.value)->type = &UA_PROPERTYVALUESTATEMENT[UA_PROPERTYVALUESTATEMENT_EXPRESSIONSEMANTICENUM]; ((UA_Variant*)&((UA_VariableNode*)newNode)->value.data.value.value)->data = UA_ExpressionSemanticEnum_new(); if (!((UA_Variant*)&((UA_VariableNode*)newNode)->value.data.value.value)->data){ result = UA_STATUSCODE_BADOUTOFMEMORY; return result; } ((UA_VariableNode*)newNode)->value.data.value.hasValue = TRUE; ((UA_VariableNode*)newNode)->valueSource = UA_VALUESOURCE_DATA; UA_ExpressionSemanticEnum_copy(&tmpExpressionSemantic, ((UA_Variant*)&((UA_VariableNode*)newNode)->value.data.value.value)->data); UA_ExpressionSemanticEnum_deleteMembers(&tmpExpressionSemantic); // accessLevel UA_Byte accessLevel = 0; if(access & OV_AC_READ){ accessLevel |= (1<<0); } if(access & OV_AC_WRITE){ accessLevel |= (1<<1); } ((UA_VariableNode*)newNode)->accessLevel = accessLevel; // minimumSamplingInterval ((UA_VariableNode*)newNode)->minimumSamplingInterval = -1; // historizing ((UA_VariableNode*)newNode)->historizing = UA_FALSE; // dataType ((UA_VariableNode*)newNode)->dataType = UA_NODEID_NUMERIC(pinterface->v_modelnamespace.index, UA_NSPROPERTYVALUESTATEMENTID_EXPRESSIONSEMANTICENUM); // References addReference(newNode); OV_UINT len = 0; OV_STRING *plist = NULL; OV_STRING tmpString = NULL; copyOPCUAStringToOV(nodeId->identifier.string, &tmpString); plist = ov_string_split(tmpString, "/", &len); ov_string_setvalue(&tmpString, plist[0]); for (OV_UINT i = 1; i < len-1; i++){ ov_string_append(&tmpString, "/"); ov_string_append(&tmpString, plist[i]); } ov_string_freelist(plist); UA_NodeId tmpNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_HASTYPEDEFINITION); for (size_t i = 0; i < newNode->referencesSize; i++){ if (UA_NodeId_equal(&newNode->references[i].referenceTypeId, &tmpNodeId)){ newNode->references[i].targetId = UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_PROPERTYTYPE); continue; } OV_STRING tmpString2 = NULL; copyOPCUAStringToOV(newNode->references[i].targetId.nodeId.identifier.string, &tmpString2); if (ov_string_compare(tmpString, tmpString2) == OV_STRCMP_EQUAL && newNode->references[i].isInverse == UA_TRUE){ newNode->references[i].referenceTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY); } ov_string_setvalue(&tmpString2, NULL); } ov_string_setvalue(&tmpString, NULL); UA_NodeId_deleteMembers(&tmpNodeId); *opcuaNode = newNode; 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; }
static void defineObjectTypes(UA_Server *server) { /* Define the object type for "Device" */ UA_NodeId deviceTypeId; /* get the nodeid assigned by the server */ UA_ObjectTypeAttributes dtAttr = UA_ObjectTypeAttributes_default; dtAttr.displayName = UA_LOCALIZEDTEXT("en-US", "DeviceType"); UA_Server_addObjectTypeNode(server, UA_NODEID_NULL, UA_NODEID_NUMERIC(0, UA_NS0ID_BASEOBJECTTYPE), UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE), UA_QUALIFIEDNAME(1, "DeviceType"), dtAttr, NULL, &deviceTypeId); UA_VariableAttributes mnAttr = UA_VariableAttributes_default; mnAttr.displayName = UA_LOCALIZEDTEXT("en-US", "ManufacturerName"); UA_NodeId manufacturerNameId; UA_Server_addVariableNode(server, UA_NODEID_NULL, deviceTypeId, UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), UA_QUALIFIEDNAME(1, "ManufacturerName"), UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE), mnAttr, NULL, &manufacturerNameId); /* Make the manufacturer name mandatory */ UA_Server_addReference(server, manufacturerNameId, UA_NODEID_NUMERIC(0, UA_NS0ID_HASMODELLINGRULE), UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_MODELLINGRULE_MANDATORY), true); UA_VariableAttributes modelAttr = UA_VariableAttributes_default; modelAttr.displayName = UA_LOCALIZEDTEXT("en-US", "ModelName"); UA_Server_addVariableNode(server, UA_NODEID_NULL, deviceTypeId, UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), UA_QUALIFIEDNAME(1, "ModelName"), UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE), modelAttr, NULL, NULL); /* Define the object type for "Pump" */ UA_ObjectTypeAttributes ptAttr = UA_ObjectTypeAttributes_default; ptAttr.displayName = UA_LOCALIZEDTEXT("en-US", "PumpType"); UA_Server_addObjectTypeNode(server, pumpTypeId, deviceTypeId, UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE), UA_QUALIFIEDNAME(1, "PumpType"), ptAttr, NULL, NULL); UA_VariableAttributes statusAttr = UA_VariableAttributes_default; statusAttr.displayName = UA_LOCALIZEDTEXT("en-US", "Status"); statusAttr.valueRank = UA_VALUERANK_SCALAR; UA_NodeId statusId; UA_Server_addVariableNode(server, UA_NODEID_NULL, pumpTypeId, UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), UA_QUALIFIEDNAME(1, "Status"), UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE), statusAttr, NULL, &statusId); /* Make the status variable mandatory */ UA_Server_addReference(server, statusId, UA_NODEID_NUMERIC(0, UA_NS0ID_HASMODELLINGRULE), UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_MODELLINGRULE_MANDATORY), true); UA_VariableAttributes rpmAttr = UA_VariableAttributes_default; rpmAttr.displayName = UA_LOCALIZEDTEXT("en-US", "MotorRPM"); rpmAttr.valueRank = UA_VALUERANK_SCALAR; UA_Server_addVariableNode(server, UA_NODEID_NULL, pumpTypeId, UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), UA_QUALIFIEDNAME(1, "MotorRPMs"), UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE), rpmAttr, NULL, NULL); }
g_TemperatureSensor1NodeId = UA_NODEID_STRING(1, "TemperatureSensor1"); UA_Server_addObjectNode(pServer, g_TemperatureSensor1NodeId, // requested NodeId for the new object UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER), // Parent node of the new object UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), // reference between parent and new object (child) UA_QUALIFIEDNAME(1, "TemperatureSensor1"), // Browsename UA_NODEID_NULL, // NodeId of object type ObjAttr, // attributes for the new object NULL, // (optional) instantiation callback NULL); // (optional) pointer to variable receiving actual nodeid // add a HasTypeDefinition reference to TemperatureSensor1 object UA_Server_addReference(pServer, g_TemperatureSensor1NodeId, // NodeId of source UA_NODEID_NUMERIC(0, UA_NS0ID_HASTYPEDEFINITION), // NodeId of ReferenceType UA_EXPANDEDNODEID_NUMERIC(1, UA_NS1ID_TEMPERATURESENSORTYPE), // NodeId of target UA_TRUE); // isForward // add Temperature member variable to TemperatureSensor1 object // Unlike with an object, we do pass the type with this function, because it has the intended effect. // The addVariableNode will also create the correct properties of the variable. UA_VariableAttributes VarAttr; UA_VariableAttributes_init(&VarAttr); UA_Int32 initialTemperature = 0; UA_Variant_setScalar(&VarAttr.value, &initialTemperature, &UA_TYPES[UA_TYPES_DOUBLE]); VarAttr.displayName = UA_LOCALIZEDTEXT("", "Temperature"); g_TemperatureSensor1TemperatureNodeId = UA_NODEID_STRING(1, "TemperatureSensor1_Temperature"); UA_DataSource TemperatureSensor1TemperatureDataSource = { .handle = NULL, .read = temperature_sensor_read, .write = NULL };
} END_TEST /* Example taken from tutorial_server_object.c */ START_TEST(InstantiateObjectType) { /* Define the object type */ UA_NodeId pumpTypeId = {1, UA_NODEIDTYPE_NUMERIC, {1001}}; UA_StatusCode retval; /* Define the object type for "Device" */ UA_NodeId deviceTypeId; /* get the nodeid assigned by the server */ UA_ObjectTypeAttributes dtAttr = UA_ObjectTypeAttributes_default; dtAttr.displayName = UA_LOCALIZEDTEXT("en-US", "DeviceType"); retval = UA_Server_addObjectTypeNode(server, UA_NODEID_NULL, UA_NODEID_NUMERIC(0, UA_NS0ID_BASEOBJECTTYPE), UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE), UA_QUALIFIEDNAME(1, "DeviceType"), dtAttr, NULL, &deviceTypeId); ck_assert_int_eq(retval, UA_STATUSCODE_GOOD); UA_VariableAttributes mnAttr = UA_VariableAttributes_default; mnAttr.displayName = UA_LOCALIZEDTEXT("en-US", "ManufacturerName"); UA_NodeId manufacturerNameId; retval = UA_Server_addVariableNode(server, UA_NODEID_NULL, deviceTypeId, UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), UA_QUALIFIEDNAME(1, "ManufacturerName"), UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE), mnAttr, NULL, &manufacturerNameId); ck_assert_int_eq(retval, UA_STATUSCODE_GOOD); /* Make the manufacturer name mandatory */ retval = UA_Server_addReference(server, manufacturerNameId, UA_NODEID_NUMERIC(0, UA_NS0ID_HASMODELLINGRULE), UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_MODELLINGRULE_MANDATORY), true); ck_assert_int_eq(retval, UA_STATUSCODE_GOOD); UA_VariableAttributes modelAttr = UA_VariableAttributes_default; modelAttr.displayName = UA_LOCALIZEDTEXT("en-US", "ModelName"); retval = UA_Server_addVariableNode(server, UA_NODEID_NULL, deviceTypeId, UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), UA_QUALIFIEDNAME(1, "ModelName"), UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE), modelAttr, NULL, NULL); ck_assert_int_eq(retval, UA_STATUSCODE_GOOD); /* Define the object type for "Pump" */ UA_ObjectTypeAttributes ptAttr = UA_ObjectTypeAttributes_default; ptAttr.displayName = UA_LOCALIZEDTEXT("en-US", "PumpType"); retval = UA_Server_addObjectTypeNode(server, pumpTypeId, deviceTypeId, UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE), UA_QUALIFIEDNAME(1, "PumpType"), ptAttr, NULL, NULL); ck_assert_int_eq(retval, UA_STATUSCODE_GOOD); UA_VariableAttributes statusAttr = UA_VariableAttributes_default; statusAttr.displayName = UA_LOCALIZEDTEXT("en-US", "Status"); statusAttr.valueRank = -1; UA_NodeId statusId; retval = UA_Server_addVariableNode(server, UA_NODEID_NULL, pumpTypeId, UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), UA_QUALIFIEDNAME(1, "Status"), UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE), statusAttr, NULL, &statusId); ck_assert_int_eq(retval, UA_STATUSCODE_GOOD); /* Make the status variable mandatory */ retval = UA_Server_addReference(server, statusId, UA_NODEID_NUMERIC(0, UA_NS0ID_HASMODELLINGRULE), UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_MODELLINGRULE_MANDATORY), true); ck_assert_int_eq(retval, UA_STATUSCODE_GOOD); UA_VariableAttributes rpmAttr = UA_VariableAttributes_default; rpmAttr.displayName = UA_LOCALIZEDTEXT("en-US", "MotorRPM"); rpmAttr.valueRank = -1; retval = UA_Server_addVariableNode(server, UA_NODEID_NULL, pumpTypeId, UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), UA_QUALIFIEDNAME(1, "MotorRPMs"), UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE), rpmAttr, NULL, NULL); ck_assert_int_eq(retval, UA_STATUSCODE_GOOD); /* Instantiate the variable */ UA_ObjectAttributes oAttr = UA_ObjectAttributes_default; oAttr.displayName = UA_LOCALIZEDTEXT("en-US", "MyPump"); retval = UA_Server_addObjectNode(server, UA_NODEID_NULL, UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER), UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), UA_QUALIFIEDNAME(1, "MyPump"), pumpTypeId, /* this refers to the object type identifier */ oAttr, NULL, NULL); ck_assert_int_eq(retval, UA_STATUSCODE_GOOD); } END_TEST
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; }