Simple C++ Client TutorialΒΆ

The current implementation of the OPC UA Server is based on Open62541 (Commit: 4038a31). Below is an example implementation of an OPC UA client, using the Open62541 library. This example client, along with a simple build system using CMAKE, can be downloaded here.

#include <open62541/ua_client.h>
#include <open62541/ua_config_default.h>
#include <open62541/ua_client_highlevel.h>
#include <open62541/ua_log_stdout.h>

#include <opc_ua_service_types_generated.h>

#include <string>
#include <vector>

#include <stdlib.h>

UA_NodeId TranslateBrowsePathtoNodeId(UA_Client* client, std::vector<std::string> browse_path)
{
    UA_BrowsePath ua_browse_path;
    UA_BrowsePath_init(&ua_browse_path);
    ua_browse_path.startingNode = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
    ua_browse_path.relativePath.elements = (UA_RelativePathElement*)UA_Array_new(browse_path.size(), &UA_TYPES[UA_TYPES_RELATIVEPATHELEMENT]);
    ua_browse_path.relativePath.elementsSize = browse_path.size();

    for(size_t i = 0; i < browse_path.size(); i++) {
        UA_RelativePathElement *elem = &ua_browse_path.relativePath.elements[i];
        elem->targetName = UA_QUALIFIEDNAME_ALLOC(2, browse_path[i].c_str());
    }

    UA_TranslateBrowsePathsToNodeIdsRequest request;
    UA_TranslateBrowsePathsToNodeIdsRequest_init(&request);
    request.browsePaths = &ua_browse_path;
    request.browsePathsSize = 1;

    UA_TranslateBrowsePathsToNodeIdsResponse response = UA_Client_Service_translateBrowsePathsToNodeIds(client, request);

    UA_NodeId node_id = response.results[0].targets[0].targetId.nodeId;

    UA_BrowsePath_deleteMembers(&ua_browse_path);
    UA_TranslateBrowsePathsToNodeIdsResponse_deleteMembers(&response);

    return node_id;
}

void writeKeyIntPair(UA_Client* client, std::string key, int value) {
    UA_NodeId object_id = TranslateBrowsePathtoNodeId(client, std::vector<std::string>{"Robot", "KeyValueMaps", "KeyIntMap"});
    UA_NodeId replace_id = TranslateBrowsePathtoNodeId(client, std::vector<std::string>{"Robot", "KeyValueMaps", "KeyIntMap", "Replace"});

    UA_String argString = UA_String_fromChars(const_cast<char*>(key.c_str()));
    UA_KeyIntPair my_value;
    my_value.key = argString;
    my_value.value = value;

    UA_ExtensionObject eo;
    UA_ExtensionObject_init(&eo);
    eo.encoding = UA_EXTENSIONOBJECT_DECODED;
    eo.content.decoded.data = &my_value;
    eo.content.decoded.type = &OPC_UA_SERVICE_TYPES[OPC_UA_SERVICE_TYPES_KEYINTPAIR];

    UA_Variant input;
    UA_Variant_init(&input);
    UA_Variant_setScalarCopy(&input, &eo, &UA_TYPES[UA_TYPES_EXTENSIONOBJECT]);

    UA_Variant *output;
    size_t outputSize;
    UA_StatusCode retval = UA_Client_call(client, object_id, replace_id, 1, &input, &outputSize, &output);
    if (retval != UA_STATUSCODE_GOOD)
    {
        UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "[OPC UA Client] WriteKeyIntPair: Method call was unsuccessful!");
        return;
    }
    UA_Array_delete(output, outputSize, &UA_TYPES[UA_TYPES_VARIANT]);
}

UA_Int32 readKeyIntPair(UA_Client* client, std::string key) {
    UA_NodeId object_id = TranslateBrowsePathtoNodeId(client, std::vector<std::string>{"Robot", "KeyValueMaps", "KeyIntMap"});
    UA_NodeId read_id = TranslateBrowsePathtoNodeId(client, std::vector<std::string>{"Robot", "KeyValueMaps", "KeyIntMap", "Read"});

    UA_String argString = UA_String_fromChars(const_cast<char*>(key.c_str()));
    UA_KeyIntPair ret;

    UA_Variant input;
    UA_Variant_init(&input);
    UA_Variant_setScalarCopy(&input, &argString, &UA_TYPES[UA_TYPES_STRING]);

    UA_Variant *output;
    size_t outputSize;
    UA_StatusCode retval = UA_Client_call(client, object_id, read_id, 1, &input, &outputSize, &output);
    if (retval != UA_STATUSCODE_GOOD)
    {
        UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "[OPC UA Client] WriteKeyIntPair: Method call was unsuccessful!");
        return 0;
    }

    UA_Int32 value = *(static_cast<UA_Int32*>(output->data));
    UA_Array_delete(output, outputSize, &UA_TYPES[UA_TYPES_VARIANT]);

    return value;
}

int main(void) {
    UA_Client* client = UA_Client_new();
    UA_ClientConfig_setDefault(UA_Client_getConfig(client));
    UA_StatusCode retval = UA_Client_connect_username(client, "opc.tcp://<robot-ip>:4840", "<username>", "<password>");
    if(retval != UA_STATUSCODE_GOOD) {
        UA_Client_delete(client);
        return (int)retval;
    }

    writeKeyIntPair(client, "Hello, World!", 123);
    UA_Int32 val = readKeyIntPair(client, "Hello, World!");
    UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "Value: %u.", val);

    return EXIT_SUCCESS;
}

Building the sample client (please avoid white spaces in the project directory!):

$ cd dummy_client
$ mkdir build
$ cd build
$ cmake -DCMAKE_BUILD_TYPE=Debug ..
$ make

Running the sample client:

$ ./dummy_client
[2019-05-14 11:38:38.188 (UTC+0200)] info/client         Connecting to endpoint opc.tcp://<robot-ip>:4840
[2019-05-14 11:38:38.188 (UTC+0200)] warn/securitypolicy No PKI plugin set. Accepting all certificates
[2019-05-14 11:38:38.189 (UTC+0200)] info/client         TCP connection established
[2019-05-14 11:38:38.307 (UTC+0200)] info/client         Opened SecureChannel with SecurityPolicy http://opcfoundation.org/UA/SecurityPolicy#None
[2019-05-14 11:38:38.307 (UTC+0200)] info/client         Endpoint and UserTokenPolicy unconfigured, perform GetEndpoints
[2019-05-14 11:38:38.357 (UTC+0200)] info/client         Selected Endpoint opc.tcp://<robot-ip>:4840 with SecurityMode None and SecurityPolicy http://opcfoundation.org/UA/SecurityPolicy#None
[2019-05-14 11:38:38.357 (UTC+0200)] info/client         Selected UserTokenPolicy open62541-username-policy with UserTokenType UserName and SecurityPolicy http://opcfoundation.org/UA/SecurityPolicy#None
[2019-05-14 11:38:39.201 (UTC+0200)] info/userland       Value: 123.