Skip to content

Commit

Permalink
Added Service Sample for separate Server and Client
Browse files Browse the repository at this point in the history
  • Loading branch information
FlorianReimold committed May 3, 2024
1 parent a65a831 commit 1c56cae
Show file tree
Hide file tree
Showing 7 changed files with 344 additions and 2 deletions.
4 changes: 3 additions & 1 deletion ecal/service/CMakeLists.txt
Expand Up @@ -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
Expand Down
Expand Up @@ -16,7 +16,7 @@
#
# ========================= eCAL LICENSE =================================

project(service_sample)
project(service_sample_client)

find_package(Threads REQUIRED)

Expand Down
146 changes: 146 additions & 0 deletions 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 <ecal/service/client_manager.h>

#include <iostream>
#include <thread>
#include <chrono>

void print_usage(const std::string& arg0)
{
std::cout << "Usage: " << arg0 << " Host:Port [Host:Port ...] [--protocol-version <Version>]" << std::endl;
}

std::pair<std::string, uint16_t> 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<uint16_t>(std::stoi(server.substr(pos + 1))));
}

int main(int argc, char** argv)
{
// Convert command line arguments into vector<std::string>
std::vector<std::string> args;
for (int i = 0; i < argc; ++i)
{
args.push_back(argv[i]);
}

std::vector<std::pair<std::string, uint16_t>> 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::uint8_t>(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<asio::io_context>();

// 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<std::string>& 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<std::string>("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();

}
39 changes: 39 additions & 0 deletions 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}
)
116 changes: 116 additions & 0 deletions 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 <ecal/service/server_manager.h>

#include <iostream>
#include <thread>
#include <chrono>

void print_usage(const std::string& arg0)
{
std::cout << "Usage: " << arg0 << " [--port <Port>] [--protocol-version <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::string>
std::vector<std::string> 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<uint16_t>(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::uint8_t>(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<asio::io_context>();

// 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<const std::string>& request, const std::shared_ptr<std::string>& 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();
}
39 changes: 39 additions & 0 deletions 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}
)
File renamed without changes.

0 comments on commit 1c56cae

Please sign in to comment.