【二】基於open62541的服務器配置


在前一篇【一】基於open62541的OPC UA服務器和客戶端的基礎上,本篇主要講述怎么配置默認的server配置,使其成為我們需要的服務器。

 

1. 創建和初始化server配置

  這是open62541建立服務器最省事的function,啥都默認的。

UA_ServerConfig *config = UA_ServerConfig_new_default();

  進入其中查看,發現port已經指定為了4840,然后另一個是certificate——證書,OPC UA采用無驗證、用戶名+密碼或者證書+簽名方式進行數據傳輸與通訊的加密,默認是不用加密的,如下所示

/* Creates a server config on the default port 4840 with no server
 * certificate. */
static UA_INLINE UA_ServerConfig *
UA_ServerConfig_new_default(void) {
    return UA_ServerConfig_new_minimal(4840, NULL);
}

  再次深入可以看到,需要的參數更多,但是都給你設置成默認的了,后面的兩個0分別是發送和接收緩沖區大小

/* Creates a new server config with one endpoint.
 * 
 * The config will set the tcp network layer to the given port and adds a single
 * endpoint with the security policy ``SecurityPolicy#None`` to the server. A
 * server certificate may be supplied but is optional.
 *
 * @param portNumber The port number for the tcp network layer
 * @param certificate Optional certificate for the server endpoint. Can be
 *        ``NULL``. */
static UA_INLINE UA_ServerConfig *
UA_ServerConfig_new_minimal(UA_UInt16 portNumber, const UA_ByteString *certificate) {
    return UA_ServerConfig_new_customBuffer(portNumber, certificate, 0 ,0);
}

  再次進入可以看到一些更加詳細的設置了,createDefaultConfig就是創建和初始化config結構體,配置了線程數,服務器的一些描述,接受哪些證書驗證(默認全部)等等,詳細的自己進去看吧

UA_ServerConfig *
UA_ServerConfig_new_customBuffer(UA_UInt16 portNumber,
                                 const UA_ByteString *certificate,
                                 UA_UInt32 sendBufferSize,
                                 UA_UInt32 recvBufferSize) {
    UA_ServerConfig *conf = createDefaultConfig();

    UA_StatusCode retval = UA_Nodestore_default_new(&conf->nodestore);
    if(retval != UA_STATUSCODE_GOOD) {
        UA_ServerConfig_delete(conf);
        return NULL;
    }

    if(addDefaultNetworkLayers(conf, portNumber, sendBufferSize, recvBufferSize) != UA_STATUSCODE_GOOD) {
        UA_ServerConfig_delete(conf);
        return NULL;
    }

    /* Allocate the endpoint */
    conf->endpointsSize = 1;
    conf->endpoints = (UA_Endpoint *)UA_malloc(sizeof(UA_Endpoint));
    if(!conf->endpoints) {
        UA_ServerConfig_delete(conf);
        return NULL;
    }

    /* Populate the endpoint */
    UA_ByteString localCertificate = UA_BYTESTRING_NULL;
    if(certificate)
        localCertificate = *certificate;
    retval =
        createSecurityPolicyNoneEndpoint(conf, &conf->endpoints[0], localCertificate);
    if(retval != UA_STATUSCODE_GOOD) {
        UA_ServerConfig_delete(conf);
        return NULL;
    }

    return conf;
}

  createSecurityPolicyNoneEndpoint就是配置無加密方式的endpoint的函數,主要是設置security policy,這里列出了opcua支持的安全策略,有興趣的可以看看

2. 設置ip

  我不知道設置這個的意義在哪,主要是我還沒用到過,畢竟localhost和本機ip都能到(有知道的朋友可以給我說下嘛),但是還是說下吧,萬一有朋友需要可以自己設置啊,這個函數就在UA_ServerConfig_new_default下面

/* Set a custom hostname in server configuration
 *
 * @param config A valid server configuration
 * @param customHostname The custom hostname used by the server */

UA_EXPORT void
UA_ServerConfig_set_customHostname(UA_ServerConfig *config,
                                   const UA_String customHostname);

  直接 UA_ServerConfig_set_customHostname(config, UA_String_fromChars("192.168.5.133")); 就行了

3. 創建服務

UA_Server *server = UA_Server_new(config);

  用上面創建和配置好的config初始化一個服務器模型,這個函數就是初始化一些服務器的配置。open62541默認建立了2個命名空間0和1,其中命名空間0建立了標准OPC UA所需要的所有type,還有一個標准的OPC UA服務器模型,也就是我們連接之后的server

4. 添加命名空間

  opcua是用節點來標識一個個folder、object、variable、type等等的,節點由命名空間——namespace,識別號——identifier組成來表示。所以,我們建立我們自己的服務器的時候可以參考server,命名空間的話推薦用2開始的,同時我們所需要的type可以直接引用官方給建立的,當然,也可自定義自己的type,這是添加命名空間的函數

UA_UInt16 UA_Server_addNamespace(UA_Server *server, const char* name) {
    /* Override const attribute to get string (dirty hack) */
    UA_String nameString;
    nameString.length = strlen(name);
    nameString.data = (UA_Byte*)(uintptr_t)name;
    return addNamespace(server, nameString);
}

  直接用就行了,UA_UInt16 ns = UA_Server_addNamespace(server, "https://www.cnblogs.com/eatfishcat/"); 返回值是命名空間的序號,如下圖所示

5. 建立自己的服務器

  一名優秀的程序員不可能上來就直接建立variable,既不美觀,也不好直接反應對應變量與設備的關系

  1. 建立folder

    話不多說,直接上代碼

    UA_UInt16 ns = UA_Server_addNamespace(server, "https://www.cnblogs.com/eatfishcat/");  // 添加命名空間

    UA_NodeId folderId;  // 建立folder、object、type等之類的返回節點信息,方便后續使用
    UA_ObjectAttributes folderAttr = UA_ObjectAttributes_default;  // 創建默認object節點
    folderAttr.displayName = UA_LOCALIZEDTEXT("en-US", "myFolder");  // 設置節點名字
    UA_NodeId folderNodeid = UA_NODEID_NUMERIC(ns, 1);  // 設置節點的id ns就用的我上面建立的namespace, identity隨便
    UA_NodeId folderParNodeid = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);  // 設置父節點,我放在了Objects下面
    UA_NodeId folderParReferNodeid = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);  // 設置參考類型,其實就是與Objects的關系
    UA_QualifiedName folderBrowseName = UA_QUALIFIEDNAME(ns, "myFolder");  // 設置由命名空間決定的瀏覽名稱
    UA_NodeId folderType = UA_NODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE);  // 設置我們建立的節點的類型

    UA_Server_addObjectNode(server, folderNodeid, folderParNodeid, folderParReferNodeid, folderBrowseName, folderType, folderAttr, NULL, &folderId);  // 往server中添加節點
    printf("ns = %d\tidentifier=%d\r\n", folderId.namespaceIndex, folderId.identifier);  // 打印添加成功的節點信息

  結果如下,1為代碼,2為uaExpert查看的結果,3為server終端打印的我建立的folder的nodeid信息

  2. 建立object

    其實跟建立folder差不多,代碼如下

    UA_NodeId outId;  // 建立folder、object、type等之類的返回節點信息,方便后續使用
    UA_ObjectAttributes objattr = UA_ObjectAttributes_default;  // 創建默認object節點
    objattr.displayName = UA_LOCALIZEDTEXT("en-US", "myObject");  // 設置節點名字
    UA_NodeId objNodeid = UA_NODEID_NUMERIC(ns, 2);  // 設置節點的id ns就用的我上面建立的namespace, identity隨便
    UA_NodeId parReferNodeid = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);  // 設置參考類型,其實就是與Objects的關系
    UA_QualifiedName objBrowseName = UA_QUALIFIEDNAME(ns, "myObject");  // 設置由命名空間決定的瀏覽名稱
    UA_NodeId objType = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEOBJECTTYPE);  // 設置我們建立的節點的類型,類型是引用ns0的

    UA_Server_addObjectNode(server, objNodeid, folderId, parReferNodeid, objBrowseName, objType, objattr, NULL, &outId);  // 往server中添加節點
    printf("ns = %d\tidentifier=%d\r\n", outId.namespaceIndex, outId.identifier);  // 打印添加成功的節點信息

  結果如下,1為代碼,2為uaExpert查看的結果,3為server終端打印的我建立在我上面建立的folder下的object的nodeid信息

 

  3. 建立variable

    代碼如下,主要說下UA_NodeId newNodeId = UA_NODEID_STRING(ns, "the.answer"); 這是設置identity為字符串形式,為整數就是UA_NodeId newNodeId = UA_NODEID_NUMERIC(ns, 3); ,如果想添加float、double、string等數據類型的變量就修改UA_Variant_setScalar(&attr.value, &myInteger, &UA_TYPES[UA_TYPES_INT32]); 后面數組的傳參

    UA_VariableAttributes attr = UA_VariableAttributes_default;
    attr.displayName = UA_LOCALIZEDTEXT("en-US", "the answer");
    UA_Int32 myInteger = 42;  // 定義並初始化變量
    UA_Variant_setScalar(&attr.value, &myInteger, &UA_TYPES[UA_TYPES_INT32]);  // 設置變量類型,並將值傳入
    UA_NodeId newNodeId = UA_NODEID_STRING(ns, "the.answer");
    UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
    UA_NodeId variableType = UA_NODEID_NULL;
    UA_QualifiedName browseName = UA_QUALIFIEDNAME(ns, "the answer");
    UA_Server_addVariableNode(server, newNodeId, outId, parentReferenceNodeId,
                              browseName, variableType, attr, NULL, NULL);

   其余的自己去查看源碼吧,有例程參考應該很簡單的。最后運行UA_Server_run就行了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM