# Simple C++ Client Tutorial

The current implementation of the OPC UA Server is based on Open62541 (Commit: 036b8c9). 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 opcua-cpp-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.