7.3.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.3.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}
  1#include <ecal_c/ecal.h>
  2
  3#include <stdio.h> //printf()
  4#include <string.h> //memcpy(), memset()
  5
  6void printCallbackInformation(const struct eCAL_SServiceMethodInformation* method_info_, const char* request_, size_t request_length_, const char* response_, size_t* response_length_)
  7{
  8   /*
  9    The data we get will not be \0 terminated. Hence we need to pass the size to printf.
 10  */
 11  printf("Method   : '%s' called in C\n", method_info_->method_name);
 12  printf("Request  : %.*s\n", (int)request_length_, request_);
 13  printf("Response : %.*s\n", (int)*response_length_, response_);
 14  printf("\n");
 15}
 16
 17/*
 18  We define the callback function that will be called when a client calls the service method "echo".
 19  This callback will simply return the request as the response.
 20*/
 21int OnEchoCallback(const struct eCAL_SServiceMethodInformation* method_info_, const void* request_, size_t request_length_, void** response_, size_t* response_length_, void* user_argument_)
 22{
 23  (void)user_argument_;
 24
 25  /*
 26    In order pass the server response properly to the callback API, the underlying memory needs to be allocated 
 27    with eCAL_Malloc(). The allocation via eCAL_Malloc() is required as the internal memory handler frees the
 28    resevered memory after callback execution.
 29  */
 30  *response_ = eCAL_Malloc(request_length_);
 31
 32  /*
 33    In case of a failure, the value that response_ points to, remains NULL.
 34  */
 35  if (*response_ == NULL) return -1; // memory allocation failed
 36
 37  /*
 38    The length of response buffer needs to be set accordingly
 39  */
 40  *response_length_ = request_length_;
 41  /*
 42    In this example the entire request buffer will be copied over to the response buffer.
 43  */
 44  memcpy(*response_, request_, request_length_);
 45
 46  printCallbackInformation(method_info_, (const char*)request_, request_length_, (const char*)(*response_), response_length_);
 47
 48  return 0;
 49}
 50
 51/* 
 52  This callback will be called when a client calls the service method "reverse".
 53  It will return the request in reverse order as the response.
 54*/
 55int OnReverseCallback(const struct eCAL_SServiceMethodInformation* method_info_, const void* request_, size_t request_length_, void** response_, size_t* response_length_, void* user_argument_)
 56{
 57  (void)user_argument_;
 58
 59  /* 
 60    In order pass the server response properly to the callback API, the underlying memory needs to be allocated 
 61    with eCAL_Malloc(). The allocation via eCAL_Malloc() is required as the internal memory handler frees the
 62    resevered memory after callback execution.
 63  */
 64  *response_ = eCAL_Malloc(request_length_);
 65
 66  /*
 67    In case of a failure, the value that response_ points to, remains NULL.
 68  */
 69  if (*response_ == NULL) return -1; // memory allocation failed
 70
 71  /*
 72    The length of response buffer needs to be set accordingly
 73  */
 74  *response_length_ = request_length_;
 75
 76  const char* request = (const char*)request_;
 77  char* response = (char*)(*response_);
 78
 79  for (size_t i = 0; i < request_length_; ++i) {
 80    response[i] = request[request_length_ - 1 - i];
 81  }
 82
 83  printCallbackInformation(method_info_, (const char*)request_, request_length_, (const char*)(*response_), response_length_);
 84
 85  return 0;
 86}
 87
 88int main()
 89{
 90  printf("------------------\n");
 91  printf(" C: Mirror Server\n");
 92  printf("------------------\n");
 93
 94  /*
 95    Here we already create the objects we want to work with. 
 96    Later we will create the server handle and fill the ServiceMethodInformation struct with the required information.
 97  */
 98  eCAL_ServiceServer *mirror_server;
 99  struct eCAL_SServiceMethodInformation echo_method_information;
100  struct eCAL_SServiceMethodInformation reverse_method_information;
101
102  /*
103    As always: initialize the eCAL API and give your process a name.
104  */
105  eCAL_Initialize("mirror server c", NULL, NULL);
106
107  printf("eCAL %s (%s)\n", eCAL_GetVersionString(), eCAL_GetVersionDateString());
108  eCAL_Process_SetState(eCAL_Process_eSeverity_healthy, eCAL_Process_eSeverityLevel_level1, "I feel good!");
109
110  /*
111    Now we create the mirror server and give it the name "mirror".
112  */
113  mirror_server = eCAL_ServiceServer_New("mirror", NULL);
114
115  /*
116    The server will have two methods: "echo" and "reverse".
117    To set a callback, we need to set a ServiceMethodInformation struct as well as the callback function.
118    In our example we will just set the method name of the struct and leave the other two fields empty.
119  */
120  memset(&echo_method_information, 0, sizeof(struct eCAL_SServiceMethodInformation));
121  echo_method_information.method_name = "echo";
122  eCAL_ServiceServer_SetMethodCallback(mirror_server, &echo_method_information, OnEchoCallback, NULL);
123
124  memset(&reverse_method_information, 0, sizeof(struct eCAL_SServiceMethodInformation));
125  reverse_method_information.method_name = "reverse";
126  eCAL_ServiceServer_SetMethodCallback(mirror_server, &reverse_method_information, OnReverseCallback, NULL);
127
128  /*
129    Now we will go in an infinite loop, to wait for incoming service calls that will be handled with the callbacks.
130  */
131  while (eCAL_Ok())
132  {
133    eCAL_Process_SleepMS(500);
134  }
135
136  /*
137    When finished, we need to delete the server handle to clean up properly.
138  */
139  eCAL_ServiceServer_Delete(mirror_server);
140
141  /*
142    After we are done, as always, finalize the eCAL API.
143  */
144  eCAL_Finalize();
145
146  return 0;
147}
 1using System;
 2using System.Text;
 3using Eclipse.eCAL.Core;
 4
 5public class MirrorServer
 6{
 7  static void PrintCallbackInformation(string method, byte[] request, byte[] response)
 8  {
 9    Console.WriteLine("Method   : '" + method + "' called in C#");
10    Console.WriteLine("Request  : " + Encoding.UTF8.GetString(request));
11    Console.WriteLine("Response : " + Encoding.UTF8.GetString(response));
12    Console.WriteLine();
13  }
14
15  /*
16    We define the callback function that will be called when a client calls the service method "echo".
17    This callback will simply return the request as the response.
18  */
19  static byte[] OnEchoCallback(ServiceMethodInformation methodInfo, byte[] request)
20  {
21    byte[] response = request;
22
23    PrintCallbackInformation(methodInfo.MethodName, request, response);
24    
25    return response;
26  }
27
28  /* 
29    This callback will be called when a client calls the service method "reverse".
30    It will return the request in reverse order as the response.
31  */
32  static byte[] OnReverseCallback(ServiceMethodInformation methodInfo, byte[] request)
33  {
34    byte[] response = (byte[])request.Clone();
35    Array.Reverse(response);
36
37    PrintCallbackInformation(methodInfo.MethodName, request, response);
38
39    return response;
40  }
41
42  static void Main()
43  {
44    Console.WriteLine("-------------------");
45    Console.WriteLine(" C#: MIRROR SERVER");
46    Console.WriteLine("-------------------");
47
48    /*
49      As always: initialize the eCAL API and give your process a name.
50    */
51    Core.Initialize("mirror server c#");
52
53    Console.WriteLine(string.Format("eCAL {0} ({1})\n", Core.GetVersionString(), Core.GetVersionDateString()));
54
55    /*
56      Now we create the mirror server and give it the name "mirror".
57    */
58    ServiceServer mirrorServer = new ServiceServer("mirror");
59
60    /*
61      The server will have two methods: "echo" and "reverse".
62      To set a callback, we need to set a ServiceMethodInformation struct as well as the callback function.
63      In our example we will just set the method name of the struct and leave the other two fields empty.
64    */
65    ServiceMethodInformation echoMethodInfo = new ServiceMethodInformation();
66    echoMethodInfo.MethodName = "echo";
67    mirrorServer.SetMethodCallback(echoMethodInfo, OnEchoCallback);
68
69    ServiceMethodInformation reverseMethodInfo = new ServiceMethodInformation();
70    reverseMethodInfo.MethodName = "reverse";
71    mirrorServer.SetMethodCallback(reverseMethodInfo, 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 (Core.Ok())
77    {
78      System.Threading.Thread.Sleep(500);
79    }
80
81    /*
82      When finished, we need to dispose the server to clean up properly.
83    */
84    mirrorServer.Dispose();
85
86    /*
87      After we are done, as always, finalize the eCAL API.
88    */
89    Core.Terminate();
90  }
91}
 1import time
 2import textwrap
 3from typing import Tuple
 4
 5import ecal.nanobind_core as ecal_core
 6
 7def print_data(method_name : str, request : bytes, response : bytes):
 8  print(f"Method   : '{method_name}' called in Python")
 9  print(f"Requests : {request}")
10  print(f"Response : {response}")
11  print()
12
13# Define the callback function that will be called when a client calls the service method "echo".
14def echo_req_callback(
15    method_information : ecal_core.ServiceMethodInformation,
16    request : bytes) -> Tuple[int, bytes]:
17  response = request  
18  print_data(method_information.method_name, request, response)
19  return 0, response
20
21# Define the callback function that will be called when a client calls the service method "reverse".
22def reverse_req_callback(
23    method_information : ecal_core.ServiceMethodInformation,
24    request : bytes) -> Tuple[int, bytes]:
25  response = request[::-1] #reverse the request
26  print_data(method_information.method_name, request, response)
27  return 0, response
28
29def main():
30  print(textwrap.dedent("""
31    -----------------------
32     Python: MIRROR SERVER
33    -----------------------
34  """).strip())
35          
36  # First, initialize the eCAL API.
37  ecal_core.initialize("mirror server python")
38
39  # Print eCAL version and date.
40  print(f"eCAL {ecal_core.get_version_string()} ({ecal_core.get_version_date_string()})")
41  ecal_core.process.set_state(ecal_core.process.Severity.HEALTHY, ecal_core.process.SeverityLevel.LEVEL1, "I feel good");
42
43  # Create the mirror server and give it the name "mirror".
44  server = ecal_core.ServiceServer("mirror")
45
46  # The server will have two methods: "echo" and "reverse".
47  # To set a callback, we need to set a ServiceMethodInformation struct as well as the callback function.
48  echo_method_info = ecal_core.ServiceMethodInformation(method_name="echo")
49  server.set_method_callback(echo_method_info, echo_req_callback)
50  reverse_method_info = ecal_core.ServiceMethodInformation(method_name="reverse")
51  server.set_method_callback(reverse_method_info, reverse_req_callback)
52  
53  # We can retrieve the services name / id.
54  print(server.get_service_name())
55  print(server.get_service_id())
56
57  # We can check if clients have connected to this service.
58  print(server.is_connected())
59
60  # Now we will go in an infinite loop, to wait for incoming service calls that will be handled with the callbacks.
61  while(ecal_core.ok()):
62    time.sleep(0.1)
63
64  # destroy server
65  server.destroy()
66  
67  # finalize eCAL API
68  server.finalize()
69
70if __name__ == "__main__":
71  main()
 1import sys
 2import time
 3
 4import ecal.core.core as ecal_core
 5import ecal.core.service as ecal_service
 6
 7def main():
 8  # print eCAL version and date
 9  print("eCAL {} ({})\n".format(ecal_core.getversion(), ecal_core.getdate()))
10  
11  # initialize eCAL API
12  ecal_core.initialize("py_minimal_service_server")
13  
14  # set process state
15  ecal_core.set_process_state(1, 1, "I feel good")
16
17  # create a server for the "DemoService" service
18  server = ecal_service.Server("DemoService")
19
20  # define the server method "foo" function
21  def foo_req_callback(method_name, req_type, resp_type, request):
22    print("'DemoService' method '{}' called with {}".format(method_name, request))
23    return 0, bytes("thank you for calling foo :-)", "ascii")
24
25  # define the server method "ping" function
26  def ping_req_callback(method_name, req_type, resp_type, request):
27    print("'DemoService' method '{}' called with {}".format(method_name, request))
28    return 0, bytes("pong", "ascii")
29
30  # define the server methods and connect them to the callbacks
31  server.add_method_callback("foo",  "string",    "string",    foo_req_callback)
32  server.add_method_callback("ping", "ping_type", "pong_type", ping_req_callback)
33
34  # idle
35  while(ecal_core.ok()):
36    time.sleep(1.0)
37
38  # destroy server
39  server.destroy()
40  
41  # finalize eCAL API
42  ecal_core.finalize()
43
44if __name__ == "__main__":
45  main()
├─ C++ │ └─mirror_server.cpp│ ├─ C │ └─mirror_server.c│ ├─ C# │ └─mirror_server.cs│ ├─ Python │ └─mirror_server.py│ └─ Python (legacy) └─mirror_server.py
7.3.1.2. 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}
  1#include <ecal_c/ecal.h>
  2#include <ecal_c/service/client.h>
  3
  4#include <stdio.h>  // printf()
  5#include <string.h> // strlen
  6#include <stdlib.h> // free
  7
  8/*
  9  Helper function to print the service response.
 10*/
 11void printServiceResponse(const struct eCAL_SServiceResponse* service_response)
 12{
 13  const char* call_state = NULL;
 14  
 15  switch (service_response->call_state)
 16  {
 17  case eCAL_eCallState_executed:
 18    call_state = "EXECUTED";
 19    break;
 20  case eCAL_eCallState_failed:
 21    call_state = "FAILED";
 22    break;
 23  default:
 24    call_state = "UNKNOWN";
 25    break;
 26  }
 27
 28  printf("Received service response in C: %s\n", call_state);
 29  printf("Method    : %s\n", service_response->service_method_information.method_name);
 30  printf("Response  : %.*s\n", (int)service_response->response_length, (char*)service_response->response);
 31  printf("Server ID : %llu\n", service_response->server_id.service_id.entity_id);
 32  printf("Host      : %s\n", service_response->server_id.service_id.host_name);
 33  printf("\n");
 34}
 35
 36/*
 37  Callback function that will be executed when we receive a response from a server.
 38*/
 39void serviceResponseCallback(const struct eCAL_SServiceResponse* service_response, void* user_data)
 40{
 41  (void)user_data;
 42
 43  printServiceResponse(service_response); 
 44}
 45
 46
 47int main()
 48{
 49  eCAL_ServiceClient* mirror_client;
 50  struct eCAL_SServiceMethodInformation* method_information_set;
 51
 52  printf("------------------\n");
 53  printf(" C: MIRROR CLIENT\n");
 54  printf("------------------\n");
 55
 56  /*
 57    As always: initialize the eCAL API and give your process a name.
 58  */
 59  eCAL_Initialize("mirror client c", NULL, NULL);
 60
 61  printf("eCAL %s (%s)\n", eCAL_GetVersionString(), eCAL_GetVersionDateString());
 62  eCAL_Process_SetState(eCAL_Process_eSeverity_healthy, eCAL_Process_eSeverityLevel_level1, "Waiting for a service ...");
 63
 64  /*
 65    Create a ServiceMethodInformation struct that contains the information about the service methods we want to call.
 66    In this case, we want to call the methods "echo" and "reverse".
 67  */
 68  method_information_set = (struct eCAL_SServiceMethodInformation*)eCAL_Malloc(2 * sizeof(struct eCAL_SServiceMethodInformation));
 69  if (method_information_set == NULL)
 70  {
 71    printf("Memory allocation failed.\n");
 72    return -1;
 73  }
 74
 75  memset(method_information_set, 0, 2 * sizeof(struct eCAL_SServiceMethodInformation));
 76  method_information_set[0].method_name = "echo";
 77  method_information_set[1].method_name = "reverse";
 78
 79  /*
 80    Create a client that connects to a "mirror" server.
 81    It may call the methods "echo" and "reverse".
 82  */
 83  mirror_client = eCAL_ServiceClient_New("mirror", method_information_set, 0, NULL);
 84
 85  /*
 86    Wait until the client is connected to a server,
 87    so we don't call methods that are not available.
 88  */
 89  while (!eCAL_ServiceClient_IsConnected(mirror_client))
 90  {
 91    printf("Waiting for a service ...\n");
 92    eCAL_Process_SleepMS(1000);
 93  }
 94
 95  /*
 96    Now that we are connected, we can set the process state to "healthy" and communicate the connection.
 97  */
 98  eCAL_Process_SetState(eCAL_Process_eSeverity_healthy, eCAL_Process_eSeverityLevel_level1, "Connected!");
 99
100  /*
101    Allow alternating between the two methods "echo" and "reverse".
102  */
103  const char* methods[] = { "echo", "reverse" };
104  size_t method_count = sizeof(methods) / sizeof(methods[0]);
105  size_t i = 0;
106  unsigned int calls_ok = 0;
107
108  while (eCAL_Ok())
109  {
110    /* 
111      Alternate between the two methods "echo" and "reverse".
112      Create the request payload.
113    */
114    const char* method_name = methods[i++ % method_count];
115    char request[] = "stressed";
116    
117    struct eCAL_ClientInstance** client_instances = eCAL_ServiceClient_GetClientInstances(mirror_client);
118    calls_ok = client_instances != NULL && client_instances[0] != NULL;
119
120    /*
121      We iterate now over all client instances and call the methods by name.
122      With this approach we have the option to filter out client instances that we don't want to call.
123      If you want to call either way all instances, then you can use
124
125      eCAL_ServiceClient_CallWithResponse(mirror_client, ...)
126      eCAL_ServiceClient_CallWithCallback(mirror_client, ...)
127
128      instead of the loop.
129    */
130    
131    if (client_instances != NULL)
132    {
133      for (size_t j = 0; client_instances[j] != NULL; ++j)
134      {
135        /*
136          Service call: blocking
137          We leave the default timeout value (infinite) for the blocking call.
138          You can change this for a specified timeout in ms.
139        */
140        struct eCAL_SServiceResponse* response = eCAL_ClientInstance_CallWithResponse(client_instances[j], method_name, request, strlen(request), NULL);
141        if (response != NULL)
142        {
143          printServiceResponse(response);
144          eCAL_Free(response);
145        }
146        else
147        {
148          printf("Method blocking call failed.\n");
149          calls_ok = 0;
150        }
151
152        /*
153          Service call: with callback
154          The callback will be executed when the server has processed the request and sent a response.
155          You can again set a timeout value for an internal waiting time. By default, we wait infinitely.
156        */
157        if(eCAL_ClientInstance_CallWithCallback(client_instances[j], method_name, request, strlen(request), serviceResponseCallback, NULL, NULL) != 0)
158        {
159          printf("Method callback call failed.\n");
160          calls_ok = 0;
161        }
162      }
163
164      /*
165        After usage, free the memory from the client instances.
166      */
167      eCAL_ClientInstances_Delete(client_instances);
168    }
169
170    /*
171      Now we set the process state according to the result of the service calls.
172      You will see the state in the eCAL Monitor or the eCAL Sys application.
173    */
174    if (calls_ok != 0)
175    {
176      eCAL_Process_SetState(eCAL_Process_eSeverity_healthy, eCAL_Process_eSeverityLevel_level1, "Connected!");
177    }
178    else
179    {
180      eCAL_Process_SetState(eCAL_Process_eSeverity_critical, eCAL_Process_eSeverityLevel_level3, "Calls failed!");
181    }
182
183    eCAL_Process_SleepMS(1000);
184  }
185
186  /*
187    Don't forget to delete the client handle.
188  */
189  eCAL_ServiceClient_Delete(mirror_client);
190
191  /*
192    After we are done, as always, finalize the eCAL API.
193  */
194  eCAL_Finalize();
195
196  return 0;
197}
  1using System;
  2using System.Collections.Generic;
  3using System.Text;
  4using Eclipse.eCAL.Core;
  5
  6public class MirrorClient
  7{
  8  /*
  9    Helper function to print the service response.
 10  */
 11  static void PrintServiceResponse(ServiceResponse serviceResponse)
 12  {
 13    string callState;
 14    switch (serviceResponse.CallState)
 15    {
 16      case CallState.Executed:
 17        callState = "EXECUTED";
 18        break;
 19      case CallState.Failed:
 20        callState = "FAILED";
 21        break;
 22      default:
 23        callState = "UNKNOWN";
 24        break;
 25    }
 26
 27    Console.WriteLine("Received service response in C: " + callState);
 28    Console.WriteLine("Method    : " + serviceResponse.MethodInformation.MethodName);
 29    Console.WriteLine("Response  : " + Encoding.UTF8.GetString(serviceResponse.Response));
 30    Console.WriteLine("Server ID : " + serviceResponse.ServerId.EntityID.Id);
 31    Console.WriteLine("Host      : " + serviceResponse.ServerId.EntityID.HostName);
 32    Console.WriteLine();
 33  }
 34
 35  static void Main()
 36  {
 37    Console.WriteLine("-------------------");
 38    Console.WriteLine(" C#: MIRROR CLIENT");
 39    Console.WriteLine("-------------------");
 40
 41    /*
 42      As always: initialize the eCAL API and give your process a name.
 43    */
 44    Core.Initialize("mirror client c#");
 45
 46    Console.WriteLine(String.Format("eCAL {0} ({1})\n", Core.GetVersionString(), Core.GetVersionDateString()));
 47
 48    /*
 49      Create a client that connects to a "mirror" server.
 50      It may call the methods "echo" and "reverse"
 51    */
 52    ServiceMethodInformationList methodInformationList = new ServiceMethodInformationList();
 53    methodInformationList.Methods.Add(new ServiceMethodInformation("echo", new DataTypeInformation(), new DataTypeInformation()));
 54    methodInformationList.Methods.Add(new ServiceMethodInformation("reverse", new DataTypeInformation(), new DataTypeInformation()));
 55
 56    ServiceClient mirrorClient = new ServiceClient("mirror", methodInformationList);
 57
 58    /*
 59      We wait until the client is connected to a server,
 60      so we don't call methods that are not available.
 61    */
 62    while (!mirrorClient.IsConnected())
 63    {
 64      Console.WriteLine("Waiting for a service ...");
 65      System.Threading.Thread.Sleep(1000);
 66    }
 67
 68    /*
 69      Allow to alternate between the two methods "echo" and "reverse".
 70    */
 71    int i = 0;
 72    string[] methods = new string[] { "echo", "reverse" };
 73
 74    while (Core.Ok())
 75    {
 76      /* 
 77        Alternate between the two methods "echo" and "reverse".
 78        Create the request payload.
 79      */
 80      string method = methods[i++ % methods.Length];
 81      byte[] request = Encoding.UTF8.GetBytes("stressed");
 82
 83      /*
 84        Service call with response
 85      */
 86      List<ServiceResponse> responseList = mirrorClient.CallWithResponse(method, request, (int)Eclipse.eCAL.Core.ServiceClient.DefaultTimeArgument);
 87      
 88      /*
 89        Iterate through all responses and print them.
 90      */
 91      if (responseList.Count > 0)
 92      {
 93        foreach (ServiceResponse response in responseList)
 94        {
 95          PrintServiceResponse(response);
 96        }
 97      }
 98      else
 99      {
100        Console.WriteLine("Method call with response failed.");
101      }
102
103      /*
104        Service call with callback
105      */
106      if (!mirrorClient.CallWithCallback(
107        method,
108        request,
109        response => PrintServiceResponse(response),
110        (int)ServiceClient.DefaultTimeArgument)
111      )
112      {
113        Console.WriteLine("Method call with callback failed.");
114      }
115
116      System.Threading.Thread.Sleep(1000);
117    }
118
119    /*
120      When finished, we need to dispose the client to clean up properly.
121    */
122    mirrorClient.Dispose();
123
124    /*
125      After we are done, as always, finalize the eCAL API.
126    */
127    Core.Terminate();
128  }
129}
 1from pickle import TRUE
 2import sys
 3import textwrap
 4import time
 5
 6import ecal.nanobind_core as ecal_core
 7
 8def print_service_response(service_response : ecal_core.ServiceResponse):
 9  print(f"Received service response in Python: {service_response.call_state}")
10  print(f"Method    : {service_response.service_method_information.method_name}")
11  print(f"Response  : {service_response.response}")
12  print(f"Server ID : {service_response.server_id.service_id.entity_id}")
13  print(f"Host      : {service_response.server_id.service_id.host_name}")
14  print()
15
16# generator, alternating returns "echo", "reverse"
17def generate_method_name():
18  methods = ["echo", "reverse"]
19  while True:
20    for method in methods:
21      yield method 
22
23def main():
24  print(textwrap.dedent("""
25    -----------------------
26     Python: MIRROR CLIENT
27    -----------------------
28  """).strip())
29          
30  # First, initialize the eCAL API.
31  ecal_core.initialize("mirror client python")
32
33  # Print eCAL version and date.
34  print(f"eCAL {ecal_core.get_version_string()} ({ecal_core.get_version_date_string()})")
35  ecal_core.process.set_state(ecal_core.process.Severity.WARNING, ecal_core.process.SeverityLevel.LEVEL1, "Waiting for a server");
36  
37  # Create a client for the "mirror" service
38  mirror_client = ecal_core.ServiceClient("mirror")
39  
40  # Wait until the client is connected to a server,
41  # so we don't call methods that are not available.
42  while (not mirror_client.is_connected()):
43    print("Waiting for a service ...")
44    time.sleep(1.0)
45
46  # Now that we are connected, we can set the process state to "healthy" and communicate the connection.
47  ecal_core.process.set_state(ecal_core.process.Severity.HEALTHY, ecal_core.process.SeverityLevel.LEVEL1, "Connected!");
48  
49  # instantiate the generator to switch between calls to "echo" and "reverse".
50  method_generator = generate_method_name()
51
52  # idle and call service methods
53  while(ecal_core.ok()):
54
55    # We iterate now over all client instances and call the methods by name.
56    # With this approach we have the option to filter out client instances that we don't want to call.
57
58    service_request = bytes("stressed", "ascii")
59    service_method_name = next(method_generator)
60
61    client_instances = mirror_client.get_client_instances()
62    for client_instance in client_instances:
63        
64        # There are three ways that a client can call the server
65        # 1. Blocking, with a response 
66        (ok, response) = client_instance.call_with_response(service_method_name, service_request, timeout_ms=1000)
67        if (response):
68          print_service_response(response)
69        time.sleep(0.5)
70
71        # 2. Blocking with callback
72        ok = client_instance.call_with_callback(service_method_name, service_request, print_service_response, timeout_ms=1000)
73        time.sleep(0.5)
74
75        # 3. Asynchronous with callback
76        ok = client_instance.call_with_callback_async(service_method_name, service_request, print_service_response)
77        time.sleep(0.5)
78
79  del mirror_client
80
81  # finalize eCAL API
82  ecal_core.finalize()
83
84if __name__ == "__main__":
85  main()
 1import sys
 2import time
 3
 4import ecal.core.core as ecal_core
 5import ecal.core.service as ecal_service
 6
 7def main():
 8  # print eCAL version and date
 9  print("eCAL {} ({})\n".format(ecal_core.getversion(), ecal_core.getdate()))
10  
11  # initialize eCAL API
12  ecal_core.initialize("py_minimal_service_client")
13  
14  # set process state
15  ecal_core.set_process_state(1, 1, "I feel good")
16
17  # create a client for the "DemoService" service
18  client = ecal_service.Client("DemoService")
19
20  # define the client response callback to catch server responses
21  def client_resp_callback(service_info, response):
22    if (service_info["call_state"] == "call_state_executed"):
23      print("'DemoService' method '{}' responded : '{}'".format(service_info["method_name"], response))
24      print()
25    else:
26      print("server {} response failed, error : '{}'".format(service_info["host_name"], service_info["error_msg"]))
27      print()
28
29  # and add it to the client
30  client.add_response_callback(client_resp_callback)
31
32  # idle and call service methods
33  i = 0
34  while(ecal_core.ok()):
35    i = i + 1
36    # call foo
37    request = bytes("hello foo {}".format(i), "ascii")
38    print("'DemoService' method 'foo' requested with : {}".format(request))
39    client.call_method("foo", request)
40    time.sleep(0.5)
41    # call ping
42    request = bytes("ping number {}".format(i), "ascii")
43    print("'DemoService' method 'ping' requested with : {}".format(request))
44    client.call_method("ping", request)
45    time.sleep(0.5)
46
47  # destroy client
48  client.destroy()
49  
50  # finalize eCAL API
51  ecal_core.finalize()
52
53if __name__ == "__main__":
54  main()
├─ C++ │ └─mirror_client.cpp│ ├─ C │ └─mirror_client.c│ ├─ C# │ └─mirror_client.cs│ ├─ Python │ └─mirror_client.py│ └─ Python (legacy) └─mirror_client.py