2017-12-19 23 views
0

opc ua로 시작하기 때문에, 나는 opc ua의 통신 계층에서 일어나는 일을 알고 싶었습니다.Opc-UA 통신 프로토콜, 클라이언트가 사용 가능한 서버 노드를 어떻게 이해합니까?

주소 공간에 3 개의 노드가있는 매우 간단한 서버 구현 예를 들어 보겠습니다. 이 노드는 opc-UA 클라이언트가 읽고 쓸 수있는 데이터를 제공합니다.

open62541과 함께 제공되는 코드의 일부를 읽으면서 TCP를 통해 통신이 이루어지는 것을 알게되었습니다. 서버가 클라이언트가 연결할 수있는 소켓을 시작하고 클라이언트가 노드에서 다양한 작업을 수행 할 수 있음을 의미합니다.

제 질문은 클라이언트가 사용 가능한 서버 노드를 어떻게 알 수 있습니까? 주소 공간을 탐색하지만 사용 가능한 노드를 찾기 위해 정확히 어디에서 찾아 볼 수 있습니까? opc-UA는 사용 가능한 노드를 클라이언트에 표시하기 위해 어떤 노출 메커니즘을 사용합니까? 서버가 일부 XML 파일이나 다른 곳에서 사용 가능한 정보 인 &을 쓸 수 있습니까? 따라서 클라이언트가 연결할 때 주소 공간 구조를 이해하기 위해 파일의 내용을 읽으려고합니까? open62541

#include <stdio.h> 
#include <open62541.h> 
#include <signal.h> 

static void 
addVariable(UA_Server *server) { 
    /* Define the attribute of the myInteger variable node */ 
    UA_VariableAttributes attr = UA_VariableAttributes_default; 
    UA_Int32 myInteger = 43; 
    UA_Variant_setScalar(&attr.value, &myInteger, &UA_TYPES[UA_TYPES_INT32]); 
    attr.description = UA_LOCALIZEDTEXT("en-US", "the answer"); 
    attr.displayName = UA_LOCALIZEDTEXT("en-US", "the answer"); 
    attr.dataType = UA_TYPES[UA_TYPES_INT32].typeId; 
    attr.accessLevel = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE; 

    /* Add the variable node to the information model */ 
    UA_NodeId myIntegerNodeId = UA_NODEID_STRING(1, "the.answer"); 
    UA_QualifiedName myIntegerName = UA_QUALIFIEDNAME(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, parentNodeId, 
     parentReferenceNodeId, myIntegerName, 
     UA_NODEID_NULL, attr, NULL, NULL); 
} 

static void 
addThirdVariable(UA_Server *server) { 
    /* Define the attribute of the myInteger variable node */ 
    UA_VariableAttributes attr = UA_VariableAttributes_default; 
    UA_String myInteger = UA_STRING("My name is variable 3"); // variable name 
    UA_Variant_setScalar(&attr.value, &myInteger, &UA_TYPES[UA_TYPES_STRING]); 
    attr.description = UA_LOCALIZEDTEXT("en-US", "the answer"); 
    attr.displayName = UA_LOCALIZEDTEXT("en-US", "the answer"); 
    attr.dataType = UA_TYPES[UA_TYPES_STRING].typeId; 
    attr.accessLevel = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE; 

    /* Add the variable node to the information model */ 
    UA_NodeId myIntegerNodeId = UA_NODEID_STRING(1, "third.variable"); 
    UA_QualifiedName myIntegerName = UA_QUALIFIEDNAME(1, "third varaible"); 
    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, parentNodeId, 
     parentReferenceNodeId, myIntegerName, 
     UA_NODEID_NULL, attr, NULL, NULL); 
} 

void addSecondVariable(UA_Server * server) { 
    //variable attributes 
    UA_VariableAttributes attr = UA_VariableAttributes_default; 
    UA_String machine_name = UA_STRING("My name is a machine"); // variable name 
    UA_Variant_setScalar(&attr.value, &machine_name, &UA_TYPES[UA_TYPES_STRING]); 

    attr.description = UA_LOCALIZEDTEXT("en-US", "machine name"); 
    attr.displayName = UA_LOCALIZEDTEXT("en-US", "machine name"); 
    attr.dataType = UA_TYPES[UA_TYPES_STRING].typeId; 
    //setting access level not important 

    //add the variable to the information model 
    UA_NodeId myStringNodeID = UA_NODEID_STRING(1, "the.machine"); 
    UA_QualifiedName myStringName = UA_QUALIFIEDNAME(1, "the machine"); 
    UA_NodeId parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER); 
    UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES); 

    UA_Server_addVariableNode(server, myStringNodeID, parentNodeId, 
     parentReferenceNodeId, myStringName, 
     UA_NODEID_NULL, attr, NULL, NULL); 



} 

UA_Boolean running = true; 
static void stopHandler(int sign) { 
    UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "received ctrl-c"); 
    running = false; 
} 

int main(void) { 
    signal(SIGINT, stopHandler); 
    signal(SIGTERM, stopHandler); 

    UA_ServerConfig *config = UA_ServerConfig_new_default(); 
    UA_Server *server = UA_Server_new(config); 
    addVariable(server); 
    addSecondVariable(server); 
    addThirdVariable(server); 
    UA_StatusCode retval = UA_Server_run(server, &running); 


    UA_Server_delete(server); 
    UA_ServerConfig_delete(config); 
    return (int)retval; 
} 
+0

OPC는 UA 클라이언트 서비스 FindServers 및 GetEndpoints을 사용하여야한다 . 먼저 OPC UA에 대한 정보를 얻으시기 바랍니다. OPC UA에 대한 도서 목록은 다음과 같습니다. https://opcfoundation.org/resources/books/ –

답변

0

클라이언트에 대한

샘플 서버 구현은 찾아보기 서비스를 사용하여 서버에 노드를 발견 할 수 있습니다.

클라이언트가 찾아보기를 시작할 수있는 모든 서버에는 일부 미리 정의 된 노드가 있습니다. 일반적으로 루트 폴더 또는 개체 폴더 중 하나가됩니다.

+0

메모 주셔서 감사합니다. – electronicsalim

0

노드는 노드 ID의 구조로 식별됩니다.

OPC UA 클라이언트를 프로그래밍하는 경우 사용자는 노드 트리를 탐색하는 기능을 추가해야하므로 사용자는 노드 ID을 선택할 수 있습니다. 따라서 값 중에서 그 속성을 읽거나 쓸 수 있습니다.

당신이 속성을 읽으려면 찾아 서비스를 사용하는 데 필요한 나무를 탐색하기 위해 당신은 서비스를 참조해야합니다. Browse는 주어진 노드의 하위 노드를 리턴합니다.

그러나이 먼저 GetEndpoints, OpenSecureChannel, CreateSession, ActivateSession 전화를해야하는 처음 세션을 만들 필요가 서비스 ... 서비스를 사용하도록

+0

Thanks @ Tonteria24, GetEndpoints, openSecureChannel ..... 찾아보기/읽기와 같은 서버 호출 순서에 대해 클라이언트를 알아 내기 위해 패킷을 캡처해야했습니다. – electronicsalim

+0

나는 당신이 충분히 멀리 얻을 포장을 스니핑한다고 생각하지 않는다. ... 물건은 약간 복잡하다. 그러나 그것은 꽤 잘 문서화되어있다. OPC 기초에서 무료로 사양을 다운로드 할 수 있습니다. 서비스에 관해서는 파트 4와 파트 6이 가장 중요합니다. –

관련 문제