From 1c56cae3e195e92e29e143248e7a41a7f91e74d5 Mon Sep 17 00:00:00 2001 From: Florian Reimold <11774314+FlorianReimold@users.noreply.github.com> Date: Fri, 3 May 2024 11:59:35 +0200 Subject: [PATCH] Added Service Sample for separate Server and Client --- ecal/service/CMakeLists.txt | 4 +- .../sample_client}/CMakeLists.txt | 2 +- .../samples/sample_client/src/main.cpp | 146 ++++++++++++++++++ .../samples/sample_server/CMakeLists.txt | 39 +++++ .../samples/sample_server/src/main.cpp | 116 ++++++++++++++ .../samples/sample_standalone/CMakeLists.txt | 39 +++++ .../sample_standalone}/src/main.cpp | 0 7 files changed, 344 insertions(+), 2 deletions(-) rename ecal/service/{sample => samples/sample_client}/CMakeLists.txt (97%) create mode 100644 ecal/service/samples/sample_client/src/main.cpp create mode 100644 ecal/service/samples/sample_server/CMakeLists.txt create mode 100644 ecal/service/samples/sample_server/src/main.cpp create mode 100644 ecal/service/samples/sample_standalone/CMakeLists.txt rename ecal/service/{sample => samples/sample_standalone}/src/main.cpp (100%) diff --git a/ecal/service/CMakeLists.txt b/ecal/service/CMakeLists.txt index 83a34bb9c0..dd248b13ac 100644 --- a/ecal/service/CMakeLists.txt +++ b/ecal/service/CMakeLists.txt @@ -21,7 +21,9 @@ add_subdirectory(ecal_service) # Samples if(ECAL_CORE_BUILD_SAMPLES) - add_subdirectory(sample) + add_subdirectory(samples/sample_client) + add_subdirectory(samples/sample_server) + add_subdirectory(samples/sample_standalone) endif() # Tests diff --git a/ecal/service/sample/CMakeLists.txt b/ecal/service/samples/sample_client/CMakeLists.txt similarity index 97% rename from ecal/service/sample/CMakeLists.txt rename to ecal/service/samples/sample_client/CMakeLists.txt index 0296bbc6e4..90c4cd4a67 100644 --- a/ecal/service/sample/CMakeLists.txt +++ b/ecal/service/samples/sample_client/CMakeLists.txt @@ -16,7 +16,7 @@ # # ========================= eCAL LICENSE ================================= -project(service_sample) +project(service_sample_client) find_package(Threads REQUIRED) diff --git a/ecal/service/samples/sample_client/src/main.cpp b/ecal/service/samples/sample_client/src/main.cpp new file mode 100644 index 0000000000..0f104640e5 --- /dev/null +++ b/ecal/service/samples/sample_client/src/main.cpp @@ -0,0 +1,146 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2023 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ + +#include + +#include +#include +#include + +void print_usage(const std::string& arg0) +{ + std::cout << "Usage: " << arg0 << " Host:Port [Host:Port ...] [--protocol-version ]" << std::endl; +} + +std::pair parse_server(const std::string& server) +{ + // Find the last ':' to split into host and port + auto pos = server.find_last_of(':'); + if (pos == std::string::npos) + { + throw std::runtime_error("Invalid server: " + server); + } + + return std::make_pair(server.substr(0, pos), static_cast(std::stoi(server.substr(pos + 1)))); +} + +int main(int argc, char** argv) +{ + // Convert command line arguments into vector + std::vector args; + for (int i = 0; i < argc; ++i) + { + args.push_back(argv[i]); + } + + std::vector> server_list; + std::uint8_t protocol_version = 1; + + // Parse command line arguments + for (size_t i = 1; i < args.size(); ++i) + { + if (args[i] == "-h" || args[i] == "--help") + { + print_usage(args[0]); + return 0; + } + else if (args[i] == "--protocol-version") + { + if (i + 1 < args.size()) + { + protocol_version = static_cast(std::stoi(args[i + 1])); + ++i; + } + else + { + print_usage(args[0]); + return 1; + } + } + else + { + try + { + server_list.push_back(parse_server(args[i])); + } + catch (const std::exception& e) + { + std::cerr << e.what() << std::endl; + print_usage(args[0]); + return 1; + } + } + } + + // Fail if the server list is empty + if (server_list.empty()) + { + print_usage(args[0]); + return 1; + } + + // Create an io_context + auto io_context = std::make_shared(); + + // Create a client manager + auto client_manager = eCAL::service::ClientManager::create(io_context, eCAL::service::default_logger("Server", eCAL::service::LogLevel::Info)); + + // Create and start an io_context thread. + // The io_context will be stopped, when the server_manager and client_manager are stopped. + std::thread io_context_thread([&io_context]() { io_context->run(); }); + + // Client Callback + // + // This callback will be called, when the service call is finished. + auto client_response_callback + = [](const eCAL::service::Error& error, const std::shared_ptr& response) -> void + { + if (error) + std::cerr << "Error calling service: " << error.ToString() << std::endl; + else + std::cout << "Received response: " << *response << std::endl; + }; + + // Event callbacks (empty) + auto client_event_callback = [](eCAL::service::ClientEventType /*event*/, const std::string& /*message*/) {}; + + // Create client + // The client will connect to the server on the given port. + auto client = client_manager->create_client(protocol_version, server_list[0].first, server_list[0].second, client_event_callback); // TODO: use entire list + + // Call the service non-blocking. The response will be passed to the callback. + int counter = 1; + while(true) + { + const auto request = std::make_shared("Hello World " + std::to_string(counter)); + client->async_call_service(request, client_response_callback); + + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + ++counter; + } + + std::cout << "Shutting down :)" << std::endl; + + // Use managers to stop servers and clients + client_manager->stop(); + + // Join the io_context thread + io_context_thread.join(); + +} diff --git a/ecal/service/samples/sample_server/CMakeLists.txt b/ecal/service/samples/sample_server/CMakeLists.txt new file mode 100644 index 0000000000..ffeca863a7 --- /dev/null +++ b/ecal/service/samples/sample_server/CMakeLists.txt @@ -0,0 +1,39 @@ +# ========================= eCAL LICENSE ================================= +# +# Copyright (C) 2016 - 2023 Continental Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# ========================= eCAL LICENSE ================================= + +project(service_sample_server) + +find_package(Threads REQUIRED) + +set(sources + src/main.cpp +) + +add_executable(${PROJECT_NAME} ${sources}) + +target_link_libraries(${PROJECT_NAME} + PRIVATE + ecal_service) + +target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_14) + +set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER core/service) + +source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}" FILES + ${sources} +) diff --git a/ecal/service/samples/sample_server/src/main.cpp b/ecal/service/samples/sample_server/src/main.cpp new file mode 100644 index 0000000000..3ce2c9d7f8 --- /dev/null +++ b/ecal/service/samples/sample_server/src/main.cpp @@ -0,0 +1,116 @@ +/* ========================= eCAL LICENSE ================================= + * + * Copyright (C) 2016 - 2023 Continental Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ========================= eCAL LICENSE ================================= +*/ + +#include + +#include +#include +#include + +void print_usage(const std::string& arg0) +{ + std::cout << "Usage: " << arg0 << " [--port ] [--protocol-version ]" << std::endl; +} + +int main(int argc, char** argv) +{ + uint16_t port = 0; + std::uint8_t protocol_version = 1; + + // convert command line arguments into vector + std::vector args; + for (int i = 0; i < argc; ++i) + { + args.push_back(argv[i]); + } + + // parse command line arguments + for (size_t i = 1; i < args.size(); ++i) + { + if (args[i] == "-h" || args[i] == "--help") + { + print_usage(args[0]); + return 0; + } + else if (args[i] == "--port") + { + if (i + 1 < args.size()) + { + port = static_cast(std::stoi(args[i + 1])); + ++i; + } + else + { + print_usage(args[0]); + return 1; + } + } + else if (args[i] == "--protocol-version") + { + if (i + 1 < args.size()) + { + protocol_version = static_cast(std::stoi(args[i + 1])); + ++i; + } + else + { + print_usage(args[0]); + return 1; + } + } + else + { + print_usage(args[0]); + return 1; + } + } + + // Create an io_context + auto io_context = std::make_shared(); + + // Create a server manager + auto server_manager = eCAL::service::ServerManager::create(io_context, eCAL::service::default_logger("Server", eCAL::service::LogLevel::Info)); + + // Server Service callback + // + // This callback will be called, when a client calls the service. + // It is responsible for filling the response object. + auto server_service_callback + = [](const std::shared_ptr& request, const std::shared_ptr& response) -> void + { + *response = "Response on \"" + *request + "\""; + }; + + // Event callbacks (empty) + auto server_event_callback = [](eCAL::service::ServerEventType /*event*/, const std::string& /*message*/) {}; + + // Create server + // If the port is 0, the server will choose a port automatically + auto server = server_manager->create_server(protocol_version, port, server_service_callback, true, server_event_callback); + + // Print server port + std::cout << "Started Service Server on port: " << server->get_port() << std::endl; + std::cout << "Using protocol version: " << std::to_string(protocol_version) << std::endl; + + // Start io_context in main thread + io_context->run(); + + // Use managers to stop server + server_manager->stop(); +} diff --git a/ecal/service/samples/sample_standalone/CMakeLists.txt b/ecal/service/samples/sample_standalone/CMakeLists.txt new file mode 100644 index 0000000000..0806b506a7 --- /dev/null +++ b/ecal/service/samples/sample_standalone/CMakeLists.txt @@ -0,0 +1,39 @@ +# ========================= eCAL LICENSE ================================= +# +# Copyright (C) 2016 - 2023 Continental Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# ========================= eCAL LICENSE ================================= + +project(service_sample_standalone) + +find_package(Threads REQUIRED) + +set(sources + src/main.cpp +) + +add_executable(${PROJECT_NAME} ${sources}) + +target_link_libraries(${PROJECT_NAME} + PRIVATE + ecal_service) + +target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_14) + +set_property(TARGET ${PROJECT_NAME} PROPERTY FOLDER core/service) + +source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}" FILES + ${sources} +) diff --git a/ecal/service/sample/src/main.cpp b/ecal/service/samples/sample_standalone/src/main.cpp similarity index 100% rename from ecal/service/sample/src/main.cpp rename to ecal/service/samples/sample_standalone/src/main.cpp