【二】基于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就行了。

posted @ 2019-07-28 11:42  丶吃鱼的猫  阅读(4570)  评论(9编辑  收藏  举报