7.2.1. Binary: Mirror#

Under this section you will find a simple example of a client/server application using the eCAL ClientServer API. You should be already familiar with the base handling of eCAL from the Publisher/Subscriber samples, so this we will not be covered in detail any more.

7.2.1.1. Mirror Server#

A service server is an eCAL instance that is matched to service client instances by their name. It can offer multiple methods, which a connected service client can call. We will set up a simple server that provides two methods: “echo” and “mirror”.

The main process is:

  • Intialize eCAL

  • Create a Service Server

  • For each method, add meta information about that method (such as method name, and types of the request and response) and a callback function to be invoked, when a client calls that method

  • Keep the program running while the service is supposed to be executed

 1#include <ecal/ecal.h>
 2
 3#include <algorithm>
 4#include <iostream>
 5#include <chrono>
 6#include <thread>
 7
 8void printCallbackInformation(const eCAL::SServiceMethodInformation& method_info_, const std::string& request_, const std::string& response_)
 9{
10  std::cout << "Method   : '" << method_info_.method_name << "' called in C++" << "\n";
11  std::cout << "Request  : " << request_ << "\n";
12  std::cout << "Response : " << response_ << "\n";
13  std::cout << "\n";
14}
15
16/*
17  We define the callback function that will be called when a client calls the service method "echo".
18  This callback will simply return the request as the response.
19*/
20int OnEchoCallback(const eCAL::SServiceMethodInformation& method_info_, const std::string& request_, std::string& response_)
21{
22  response_ = request_;
23
24  printCallbackInformation(method_info_, request_, response_);
25  
26  // The return value here has no actual meaning. It's made available to the caller.
27  return 0;
28}
29
30/* 
31  This callback will be called when a client calls the service method "reverse".
32  It will return the request in reverse order as the response.
33*/
34int OnReverseCallback(const eCAL::SServiceMethodInformation& method_info_, const std::string& request_, std::string& response_)
35{
36  response_.resize(request_.size());
37  std::copy(request_.rbegin(), request_.rend(), response_.begin());
38
39  printCallbackInformation(method_info_, request_, response_);
40
41  // The return value here has no actual meaning. It's made available to the caller.
42  return 0;
43}
44
45int main()
46{
47  std::cout << "--------------------" << "\n";
48  std::cout << " C++: MIRROR SERVER"  << "\n";
49  std::cout << "--------------------" << "\n";
50
51  /*
52    As always: initialize the eCAL API and give your process a name.
53  */
54  eCAL::Initialize("mirror server");
55
56  std::cout << "eCAL " << eCAL::GetVersionString() << " (" << eCAL::GetVersionDateString() << ")" << "\n";
57  eCAL::Process::SetState(eCAL::Process::eSeverity::healthy, eCAL::Process::eSeverityLevel::level1, "I feel good!");
58
59  /*
60    Now we create the mirror server and give it the name "mirror".
61  */
62  eCAL::CServiceServer mirror_server("mirror");
63
64  /*
65    The server will have two methods: "echo" and "reverse".
66    To set a callback, we need to set a ServiceMethodInformation struct as well as the callback function.
67    We simplify the struct creation of ServiceMethodInformationStruct and the other two
68    fields can be left empty for our example.
69  */
70  mirror_server.SetMethodCallback({ "echo", {}, {} }, OnEchoCallback);
71  mirror_server.SetMethodCallback({ "reverse", {}, {} }, OnReverseCallback);
72
73  /*
74    Now we will go in an infinite loop, to wait for incoming service calls that will be handled with the callbacks.
75  */
76  while(eCAL::Ok())
77  {
78    std::this_thread::sleep_for(std::chrono::milliseconds(500));
79  }
80
81  /*
82    After we are done, as always, finalize the eCAL API.
83  */
84  eCAL::Finalize();
85
86  return(0);
87}

7.2.1.2. Mirror Server Files#


├─  C++
│  └─  mirror_server.cpp
│
├─  C
│  └─  mirror_server_c.c
│
├─  C#
│  └─  mirror_server_csharp.cs
│
└─  Python
   └─  minimal_service_server.py

7.2.1.3. Mirror Client#

The client will have some more logic to take care of, as its possible that multiple servers are running with the same service name.

  • Initialize eCAL

  • Create a service client with the service name and (optionally) register the functions it can call (in this case “echo” and “mirror”)

  • Waiting for a server to be available (this is optional and depends on how you want to design your application)

  • Retrieve all client instances. You can also call it directly on the service client, but if you iterate through all client instances, you are more flexible and could filter out instances you don’t want to call.

  • Then we call the methodnames with two different calls: with callback (non blocking) and with response (blocking)

  • Handle the received data (or error status)

As a little extra we also added a little bit more eCAL state handling as in the previous examples.

  1#include <ecal/ecal.h>
  2
  3#include <iostream>
  4#include <chrono>
  5#include <thread>
  6
  7/*
  8  Helper function to print the service response.
  9*/
 10void printServiceResponse(const eCAL::SServiceResponse& service_response_)
 11{
 12  std::string call_state;
 13  switch (service_response_.call_state)
 14  {
 15    case eCAL::eCallState::executed:
 16      call_state = "EXECUTED";
 17      break;
 18    case eCAL::eCallState::failed:
 19      call_state = "FAILED";
 20      break;
 21    default:
 22      call_state = "UNKNOWN";
 23      break;
 24  }
 25
 26  std::cout << "Received service response in C++: " << call_state                         << "\n";
 27  std::cout << "Method    : " << service_response_.service_method_information.method_name << "\n";
 28  std::cout << "Response  : " << service_response_.response                               << "\n";
 29  std::cout << "Server ID : " << service_response_.server_id.service_id.entity_id         << "\n";
 30  std::cout << "Host      : " << service_response_.server_id.service_id.host_name         << "\n";
 31  std::cout << "\n";
 32}
 33
 34int main()
 35{
 36  std::cout << "--------------------" << "\n";
 37  std::cout << " C++: MIRROR CLIENT"  << "\n";
 38  std::cout << "--------------------" << "\n";
 39
 40  /*
 41    As always: initialize the eCAL API and give your process a name.
 42  */
 43  eCAL::Initialize("mirror client c++");
 44
 45  std::cout << "eCAL " << eCAL::GetVersionString() << " (" << eCAL::GetVersionDateString() << ")" << "\n";
 46  eCAL::Process::SetState(eCAL::Process::eSeverity::warning, eCAL::Process::eSeverityLevel::level1, "Waiting for a service ...");
 47
 48  /*
 49    Create a client that connects to a "mirror" server.
 50    It may call the methods "echo" and "reverse"
 51  */
 52  const eCAL::CServiceClient mirror_client("mirror", { {"echo", {}, {} }, {"reverse", {}, {} } });
 53
 54  /*
 55    This lambda serves as a callback which will be executed when we receive a response from a server.
 56  */
 57  auto service_response_callback = [](const eCAL::SServiceResponse& service_response_) {
 58    printServiceResponse(service_response_);
 59  };
 60
 61  /*
 62    We wait until the client is connected to a server,
 63    so we don't call methods that are not available.
 64  */
 65  while (!mirror_client.IsConnected())
 66  {
 67    std::cout << "Waiting for a service ..." << "\n";
 68
 69    std::this_thread::sleep_for(std::chrono::milliseconds(1000));
 70  }
 71
 72  /*
 73    Now that we are connected, we can set the process state to "healthy" and communicate the connection.
 74  */
 75  eCAL::Process::SetState(eCAL::Process::eSeverity::healthy, eCAL::Process::eSeverityLevel::level1, "Connected!");
 76
 77  /*
 78    Allow to alternate between the two methods "echo" and "reverse".
 79  */
 80  unsigned int i = 0;
 81  std::vector<std::string> methods = { "echo", "reverse" };
 82  bool calls_ok = false;;
 83
 84  while(eCAL::Ok())
 85  {
 86    /* 
 87      Alternate between the two methods "echo" and "reverse".
 88      Create the request payload.
 89    */
 90    std::string method_name = methods[i++ % methods.size()];
 91    std::string request("stressed");
 92
 93    calls_ok = !mirror_client.GetClientInstances().empty();
 94    
 95    /*
 96      We iterate now over all client instances and call the methods by name.
 97      With this approach we have the option to filter out client instances that we don't want to call.
 98      If you want to call either way all instances, then you can use
 99
100      mirror_client.CallWithResponse(...)
101      mirror_client.CallWithCallback(...)
102
103      instead of the loop.
104    */
105    for (auto& client_instance : mirror_client.GetClientInstances())
106    {
107      /*
108        Service call: blocking
109        We leave the default timeout value (infinite) for the blocking call.
110        You can change this for a specified timeout in ms.
111      */
112      const auto service_response = client_instance.CallWithResponse(method_name, request, eCAL::CClientInstance::DEFAULT_TIME_ARGUMENT);
113      if (std::get<0>(service_response))
114      {
115        const auto& response_content = std::get<1>(service_response);
116        
117        printServiceResponse(response_content);
118      }
119      else
120      {
121        std::cout << "Method blocking call failed." << "\n";
122        calls_ok = false;
123      }
124
125      /*
126        Service call: with callback
127        The callback will be executed when the server has processed the request and sent a response.
128        You can again set a timeout value for an internal waiting time. By default, we wait infinitely.
129      */
130      if (!client_instance.CallWithCallback(method_name, request, service_response_callback, eCAL::CClientInstance::DEFAULT_TIME_ARGUMENT))
131      {
132        std::cout << "Method callback call failed." << "\n";
133        calls_ok = false;
134      }
135      break;
136    }
137
138    /*
139      Now we set the process state according to the result of the service calls.
140      You will see the state in the eCAL Monitor or the eCAL Sys application.
141    */
142    if (calls_ok)
143    {
144      eCAL::Process::SetState(eCAL::Process::eSeverity::healthy, eCAL::Process::eSeverityLevel::level1, "Connected!");
145    }
146    else
147    {
148      eCAL::Process::SetState(eCAL::Process::eSeverity::critical, eCAL::Process::eSeverityLevel::level3, "Calls failed!");
149    }
150
151    std::this_thread::sleep_for(std::chrono::milliseconds(1000));
152  }
153
154  /*
155    After we are done, as always, finalize the eCAL API.
156  */
157  eCAL::Finalize();
158
159  return(0);
160}

7.2.1.4. Mirror Client Files#


├─  C++
│  └─  mirror_client.cpp
│
├─  C
│  └─  mirror_client.c
│
├─  C#
│  └─  mirror_client_csharp.cs
│
└─  Python
   └─  minimal_service_client.py