Simple C++ Client Tutorial
**************************

The current implementation of the OPC UA Server is based on `Open62541 <https://open62541.org/>`_ (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 :download:`here <_static/download/sampleClientCpp.zip>`.

.. code-block:: c++

  #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!):

.. code-block:: bash

  $ cd opcua-cpp-client
  $ mkdir build
  $ cd build
  $ cmake -DCMAKE_BUILD_TYPE=Debug ..
  $ make

Running the sample client:

.. code-block:: bash

  $ ./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.
