콘텐츠로 바로가기

now0930 일지

이런저런 생각

  • 홈
  • 비공개
  • 강좌
  • 잔여 작업 조회
  • 위치

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를 어떻게 해야 하는지 알아야겠다. 삽질해도 보람차다.ㅠㅠ

이 글 공유하기:

  • Tweet
발행일 2020-09-08글쓴이 이대원
카테고리 생활코딩 태그 opcua, open62541, plc, raspberry

댓글 남기기응답 취소

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

글 내비게이션

이전 글

open62541 server 설정

다음 글

open62531 server plc 시뮬레이션

2025 6월
일 월 화 수 목 금 토
1234567
891011121314
15161718192021
22232425262728
2930  
5월    

최신 글

  • 자기 회로 정리 2025-06-22
  • common mode, differential mode 2025-05-11
  • signal conditioner, 신호 처리기 2025-05-10
  • strain gage 2025-05-09
  • 칼만 필터 2025-05-01

카테고리

  • 산업계측제어기술사
  • 삶 자국
    • 책과 영화
    • 투자
  • 생활코딩
    • LEGO
    • ROS
    • tensorflow
  • 전기기사
  • 피아노 악보

메타

  • 로그인
  • 엔트리 피드
  • 댓글 피드
  • WordPress.org

페이지

  • 소개
  • 잔여 작업 조회
    • 작업 추가
    • 작업의 사진 조회
    • 작업 수정 페이지
  • 사진
    • GPS 입력된 사진
    • 사진 조회
  • 위치
    • 하기 휴가 방문지
    • 해외 출장

태그

android bash c docker driver FSM gps java kernel LEGO linux mysql network program opcua open62541 plc programmers python raspberry reinforcementLearning ros state space system program tensorflow transfer function 경제 미국 민수 삼국지 세계사 실기 에너지 역사 유전자 일본 임베디드 리눅스 전기기사 조선 중국 채윤 코딩 테스트 통계 한국사 한국어

팔로우하세요

  • Facebook
now0930 일지
WordPress로 제작.
 

댓글 로드중...