open62541 browse

open62541이 OPC UA 규격에 맞춰 browse 기능을 지원한다. OPC UA 문서에 어떻게 사용하는지 알 수 있는데, 멤버만 볼 수 있다. 일단 대충 필요한 실린더를 구성했다 하자. 상용 PLC 메이커가 OPC UA 기능을 구현한다면 내부에 server 기능으로 넣을 듯 하다. 지금 구할 수 없으니, 대충 비슷하게 만들고 싶다. 여기서 문제를 알았다. 한번 Variable을 할당하면 어떻게 바꾸지? callback으로 바꿀 수 있는데, nodeId를 알아야 한다. nodeId를 null로 설정하면 server가 임의로 할당한다.

아래 그림 NodeID i=50235, Status Value를 True로 만들고 싶다. 어떻게??

이럴때 browse를 사용한다.(아마도..) sample로 구현되어 있는데, example 디렉토리 tutorial_server_object.c에 있다. 가장 간단한 방법이 아래와 같다.

#define TEST
#ifdef TEST

	//테스트
	//각 설비값을 설정하는 부분
	UA_Variant tmp_value;
	bool bitoff = 0;
	UA_Variant_setScalar(&tmp_value, &bitoff, &UA_TYPES[UA_TYPES_BOOLEAN]);
	UA_NodeId tmpNodeId = UA_NODEID_STRING(1, "Status");
	UA_Server_writeValue(server, tmpNodeId, tmp_value);

    /* Find the NodeId of the status child variable */
    UA_RelativePathElement rpe;
    UA_RelativePathElement_init(&rpe);
    rpe.referenceTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT);
    rpe.isInverse = false;
    rpe.includeSubtypes = true;
    //rpe.targetName = UA_QUALIFIEDNAME(1, "Status");
	rpe.targetName = UA_QUALIFIEDNAME(1, "latch");
	//rpe.targetName = UA_QUALIFIEDNAME(1, "variable");


    UA_BrowsePath bp;
    UA_BrowsePath_init(&bp);
	UA_NodeId *nodeId  = UA_NodeId_new();
	*nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
	//UA_NodeId tmp = {0, UA_NODEIDTYPE_NUMERIC, {50238}};
	//printf("\nNode id is %d\n", tmp);
	//UA_NodeId *nodeId = &tmp;
    bp.startingNode = *nodeId;
    bp.relativePath.elementsSize = 1;
    bp.relativePath.elements = &rpe;

    UA_BrowsePathResult bpr =
        UA_Server_translateBrowsePathToNodeIds(server, &bp);
    if(bpr.statusCode != UA_STATUSCODE_GOOD ||
       bpr.targetsSize < 1)
        return bpr.statusCode;

	UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "found target is %d", bpr.targets->targetId.nodeId.identifier.numeric);
	//print로 structure를 출력할 수 없음.
	//printf("\nnode id is %d\n", (UA_NodeId)bpr.targets->targetId.nodeId);
    //* Set the status value */
    UA_Boolean status = true;
    UA_Variant value;
    UA_Variant_setScalar(&value, &status, &UA_TYPES[UA_TYPES_BOOLEAN]);
    UA_Server_writeValue(server, bpr.targets->targetId.nodeId, value);
	//UA_Server_writeValue(server, tmp, value);
    UA_BrowsePathResult_clear(&bpr);
#endif

여기를 gdb로 보면 시작 위치를 설정, 알고 있는 정보를 입력, 두 구조체를 비교하여 찾는다. 찾으면 0을 반환한다.

450	    UA_RelativePathElement_init(&rpe);
(gdb) 
451	    rpe.referenceTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT);
(gdb) 
452	    rpe.isInverse = false;
(gdb) 
453	    rpe.includeSubtypes = true;
(gdb) 
455		rpe.targetName = UA_QUALIFIEDNAME(1, "latch");
(gdb) 
460	    UA_BrowsePath_init(&bp);
(gdb) 
461		UA_NodeId *nodeId  = UA_NodeId_new();
(gdb) 
462		*nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
(gdb) 
466	    bp.startingNode = *nodeId;
(gdb) 
467	    bp.relativePath.elementsSize = 1;
(gdb) 
468	    bp.relativePath.elements = &rpe;
(gdb) 
471	        UA_Server_translateBrowsePathToNodeIds(server, &bp);
(gdb) 
472	    if(bpr.statusCode != UA_STATUSCODE_GOOD ||
(gdb) display rpe
1: rpe = {referenceTypeId = {namespaceIndex = 0, identifierType = UA_NODEIDTYPE_NUMERIC, identifier = {numeric = 47, string = {length = 47, 
        data = 0x136b4 <main+692> "b?K\342\003"}, guid = {data1 = 47, data2 = 14004, data3 = 1, data4 = "\257\362\377\276l\362\377\276"}, byteString = {
        length = 47, data = 0x136b4 <main+692> "b?K\342\003"}}}, isInverse = false, includeSubtypes = true, targetName = {namespaceIndex = 1, name = {
      length = 5, data = 0x13bbc "latch"}}}
(gdb) display bp
2: bp = {startingNode = {namespaceIndex = 0, identifierType = UA_NODEIDTYPE_NUMERIC, identifier = {numeric = 85, string = {length = 85, data = 0x52a48 ""}, 
      guid = {data1 = 85, data2 = 10824, data3 = 5, data4 = "\024\361\377\276\234\016\001"}, byteString = {length = 85, data = 0x52a48 ""}}}, 
  relativePath = {elementsSize = 1, elements = 0xbefff26c}}
(gdb) display bpr
3: bpr = {statusCode = 0, targetsSize = 1, targets = 0x2c9c8}
(gdb) display *bpr.targets
4: *bpr.targets = {targetId = {nodeId = {namespaceIndex = 0, identifierType = UA_NODEIDTYPE_NUMERIC, identifier = {numeric = 50234, string = {
          length = 50234, data = 0x0}, guid = {data1 = 50234, data2 = 0, data3 = 0, data4 = "\000\000\000\000\000\000\000"}, byteString = {length = 50234, 
          data = 0x0}}}, namespaceUri = {length = 0, data = 0x0}, serverIndex = 0}, remainingPathIndex = 4294967295}
(gdb) n
473	       bpr.targetsSize < 1)
1: rpe = {referenceTypeId = {namespaceIndex = 0, identifierType = UA_NODEIDTYPE_NUMERIC, identifier = {numeric = 47, string = {length = 47, 
        data = 0x136b4 <main+692> "b?K\342\003"}, guid = {data1 = 47, data2 = 14004, data3 = 1, data4 = "\257\362\377\276l\362\377\276"}, byteString = {
        length = 47, data = 0x136b4 <main+692> "b?K\342\003"}}}, isInverse = false, includeSubtypes = true, targetName = {namespaceIndex = 1, name = {
      length = 5, data = 0x13bbc "latch"}}}
2: bp = {startingNode = {namespaceIndex = 0, identifierType = UA_NODEIDTYPE_NUMERIC, identifier = {numeric = 85, string = {length = 85, data = 0x52a48 ""}, 
      guid = {data1 = 85, data2 = 10824, data3 = 5, data4 = "\024\361\377\276\234\016\001"}, byteString = {length = 85, data = 0x52a48 ""}}}, 
  relativePath = {elementsSize = 1, elements = 0xbefff26c}}
3: bpr = {statusCode = 0, targetsSize = 1, targets = 0x2c9c8}
4: *bpr.targets = {targetId = {nodeId = {namespaceIndex = 0, identifierType = UA_NODEIDTYPE_NUMERIC, identifier = {numeric = 50234, string = {
          length = 50234, data = 0x0}, guid = {data1 = 50234, data2 = 0, data3 = 0, data4 = "\000\000\000\000\000\000\000"}, byteString = {length = 50234, 
          data = 0x0}}}, namespaceUri = {length = 0, data = 0x0}, serverIndex = 0}, remainingPathIndex = 4294967295}

어디에서 시작할 지 정해줘야 한다. 가장 상위인 Object에서 시작했다. NodeId i=84다. 이 번호도 정했을 것 같다. 문제는 이 설정이 어떻게 되어 있는지 Objects 바로 아래 항목만 찾는다. 위 방식으로 NodeId i=50234까지는 찾았다. 그러나 Varible이 없어 write를 하더라도 효과 없다. 하위 항목인 1:Status, NodeId i=50235를 찾아야 했다. 편법으로 시작점을 50243으로 넣어 줬다.

#define TEST
#ifdef TEST

	//테스트
	//각 설비값을 설정하는 부분
	UA_Variant tmp_value;
	bool bitoff = 0;
	UA_Variant_setScalar(&tmp_value, &bitoff, &UA_TYPES[UA_TYPES_BOOLEAN]);
	UA_NodeId tmpNodeId = UA_NODEID_STRING(1, "Status");
	UA_Server_writeValue(server, tmpNodeId, tmp_value);

    /* Find the NodeId of the status child variable */
    UA_RelativePathElement rpe;
    UA_RelativePathElement_init(&rpe);
    rpe.referenceTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT);
    rpe.isInverse = false;
    rpe.includeSubtypes = true;
    //rpe.targetName = UA_QUALIFIEDNAME(1, "Status");
	rpe.targetName = UA_QUALIFIEDNAME(1, "latch");
	//rpe.targetName = UA_QUALIFIEDNAME(1, "variable");


    UA_BrowsePath bp;
    UA_BrowsePath_init(&bp);
	UA_NodeId *nodeId  = UA_NodeId_new();
	*nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
	//UA_NodeId tmp = {0, UA_NODEIDTYPE_NUMERIC, {50238}};
	//printf("\nNode id is %d\n", tmp);
	//UA_NodeId *nodeId = &tmp;
    bp.startingNode = *nodeId;
    bp.relativePath.elementsSize = 1;
    bp.relativePath.elements = &rpe;

    UA_BrowsePathResult bpr =
        UA_Server_translateBrowsePathToNodeIds(server, &bp);
    if(bpr.statusCode != UA_STATUSCODE_GOOD ||
       bpr.targetsSize < 1)
        return bpr.statusCode;

	UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "found target is %d", bpr.targets->targetId.nodeId.identifier.numeric);
	//print로 structure를 출력할 수 없음.
	//printf("\nnode id is %d\n", (UA_NodeId)bpr.targets->targetId.nodeId);
    //* Set the status value */
    UA_Boolean status = true;
    UA_Variant value;
    UA_Variant_setScalar(&value, &status, &UA_TYPES[UA_TYPES_BOOLEAN]);
    UA_Server_writeValue(server, bpr.targets->targetId.nodeId, value);
	//UA_Server_writeValue(server, tmp, value);

	//다시 아래로 내림.
	bp.startingNode = bpr.targets->targetId.nodeId;
	rpe.targetName = UA_QUALIFIEDNAME(1, "Status");
    UA_BrowsePathResult bpr2 =
        UA_Server_translateBrowsePathToNodeIds(server, &bp);
    if(bpr2.statusCode != UA_STATUSCODE_GOOD ||
       bpr2.targetsSize < 1)
        return bpr.statusCode;

	UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "found target is %d", bpr2.targets->targetId.nodeId.identifier.numeric);
	UA_Server_writeValue(server, bpr2.targets->targetId.nodeId, value);
    UA_BrowsePathResult_clear(&bpr);
#endif

Variable을 제대로 수정한다. 이제 rucursive browse를 어떻게 해야 하는지 알아야겠다. 삽질해도 보람차다.ㅠㅠ

코멘트

댓글 남기기

이 사이트는 Akismet을 사용하여 스팸을 줄입니다. 댓글 데이터가 어떻게 처리되는지 알아보세요.