Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add RPC example in Python and C++11 #616

15 changes: 0 additions & 15 deletions examples/connext_dds/coherent_presentation/c++11/application.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,21 +19,6 @@

namespace application {

// Catch control-C and tell application to shut down
bool shutdown_requested = false;

inline void stop_handler(int)
{
shutdown_requested = true;
std::cout << "preparing to shut down..." << std::endl;
}

inline void setup_signal_handlers()
{
signal(SIGINT, stop_handler);
signal(SIGTERM, stop_handler);
}

enum class ParseReturn { ok, failure, exit };

struct ApplicationArguments {
Expand Down
23 changes: 23 additions & 0 deletions examples/connext_dds/remote_procedure_call/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Example Code: Remote Procedure Call

:warning: **Note**: The RPC communication pattern is experimental and available
in the Python and Modern C++ **Connext** APIs.

## Concept

Remote Procedure Call (RPC) is a specialization of the Request-Reply
communication pattern. It simplifies "client" and "service" applications by
using a service interface with operations that are implemented in the service
and called from the clients.

See the [User's Manual](https://community.rti.com/static/documentation/connext-dds/7.2.0/doc/manuals/connext_dds_professional/users_manual/users_manual/RPC.htm)
for more information.

## Example Description

This example shows how to create a simple Inventory service with operations
to add and remove items and get the current inventory.

- The service application implements the interface and runs the service.

- The client application calls the service operations.
36 changes: 36 additions & 0 deletions examples/connext_dds/remote_procedure_call/c++11/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# (c) 2023 Copyright, Real-Time Innovations, Inc. All rights reserved.
# RTI grants Licensee a license to use, modify, compile, and create derivative
# works of the software solely for use with RTI Connext DDS. Licensee may
# redistribute copies of the software provided that all such copies are subject
# to this license. The software is provided "as is", with no warranty of any
# type, including any warranty for fitness for any purpose. RTI is under no
# obligation to maintain or support the software. RTI shall not be liable for
# any incidental or consequential damages arising out of the use or inability
# to use the software.

cmake_minimum_required(VERSION 3.11)
project(rtiexamples-batching)
list(APPEND CMAKE_MODULE_PATH
"${CMAKE_CURRENT_SOURCE_DIR}/../../../../resources/cmake/Modules"
)

include(ConnextDdsConfigureCmakeUtils)
connextdds_configure_cmake_utils()

find_package(RTIConnextDDS
"7.0.0"
REQUIRED
COMPONENTS
core
messaging_api
)

include(ConnextDdsAddExample)

connextdds_add_example(
IDL "Inventory"
LANG "C++11"
SUB_NAME "service"
PUB_NAME "client"
DEPENDENCIES RTIConnextDDS::messaging_cpp2_api
)
30 changes: 30 additions & 0 deletions examples/connext_dds/remote_procedure_call/c++11/Inventory.idl
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*******************************************************************************
(c) 2023 Copyright, Real-Time Innovations, Inc. All rights reserved.
RTI grants Licensee a license to use, modify, compile, and create derivative
works of the Software. Licensee has the right to distribute object form only
for use with RTI products. The Software is provided "as is", with no warranty
of any type, including any warranty for fitness for any purpose. RTI is under
no obligation to maintain or support the Software. RTI shall not be liable for
any incidental or consequential damages arising out of the use or inability to
use the software.
******************************************************************************/

struct Item {
string name;
int64 quantity;
};

struct InventoryContents {
sequence<Item> items;
};

exception UnknownItemError {
string name;
};

@service("DDS")
interface InventoryService {
InventoryContents get_inventory();
void add_item(Item item);
void remove_item(Item item) raises (UnknownItemError);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/*
* (c) Copyright, Real-Time Innovations, 2020. All rights reserved.
* RTI grants Licensee a license to use, modify, compile, and create derivative
* works of the software solely for use with RTI Connext DDS. Licensee may
* redistribute copies of the software provided that all such copies are subject
* to this license. The software is provided "as is", with no warranty of any
* type, including any warranty for fitness for any purpose. RTI is under no
* obligation to maintain or support the software. RTI shall not be liable for
* any incidental or consequential damages arising out of the use or inability
* to use the software.
*/

#include <iostream>
#include "application.hpp"
#include "Inventory.hpp"

void run_client(
int domain_id,
bool add,
const std::string &item_name,
int quantity)
{
// Create a DomainParticipant with default Qos
dds::domain::DomainParticipant participant(domain_id);

// Create a ClientParams to provide a service name and a maximum wait time
// for remote calls
dds::rpc::ClientParams client_params(participant);
client_params.service_name("Inventory");
client_params.function_call_max_wait(dds::core::Duration(20));

// Create a client with the ClientParams
::InventoryServiceClient client(client_params);

// Wait until the service is started
client.wait_for_service();

std::cout << "Initial inventory: " << client.get_inventory() << std::endl;

if (add) {
std::cout << "Add " << quantity << " " << item_name << std::endl;
client.add_item(::Item(item_name, quantity));
} else {
std::cout << "Remove " << quantity << " " << item_name << std::endl;
try {
client.remove_item(::Item(item_name, quantity));
} catch (const UnknownItemError &e) {
std::cout << "Unknown item: " << e.name() << std::endl;
}
}

std::cout << "Updated inventory: " << client.get_inventory() << std::endl;
}

int main(int argc, char *argv[])
{
using namespace application;

// Parse arguments
auto arguments = parse_arguments(argc, argv, true);
if (arguments.parse_result == ParseReturn::exit) {
return EXIT_SUCCESS;
} else if (arguments.parse_result == ParseReturn::failure) {
return EXIT_FAILURE;
}

// Sets Connext verbosity to help debugging
rti::config::Logger::instance().verbosity(arguments.verbosity);

try {
run_client(
arguments.domain_id,
arguments.add,
arguments.item_name,
arguments.quantity);
} catch (const std::exception &ex) {
// This will catch DDS exceptions
std::cerr << "Exception in run_client(): " << ex.what() << std::endl;
return EXIT_FAILURE;
}

// Releases the memory used by the participant factory. Optional at
// application exit
dds::domain::DomainParticipant::finalize_participant_factory();

return EXIT_SUCCESS;
}
136 changes: 136 additions & 0 deletions examples/connext_dds/remote_procedure_call/c++11/Inventory_service.cxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
/*
* (c) Copyright, Real-Time Innovations, 2020. All rights reserved.
* RTI grants Licensee a license to use, modify, compile, and create derivative
* works of the software solely for use with RTI Connext DDS. Licensee may
* redistribute copies of the software provided that all such copies are subject
* to this license. The software is provided "as is", with no warranty of any
* type, including any warranty for fitness for any purpose. RTI is under no
* obligation to maintain or support the software. RTI shall not be liable for
* any incidental or consequential damages arising out of the use or inability
* to use the software.
*/

#include <iostream>
#include <mutex>
#include <thread>
#include <map>
#include <rti/config/Logger.hpp>

#include "application.hpp"

#include "Inventory.hpp"


class InventoryImpl : public InventoryService {
public:
InventoryImpl(unsigned int delay_param = 0) : delay(delay_param)
{
inventory["apples"] = 100;
inventory["oranges"] = 50;
}

::InventoryContents get_inventory() override
{
::InventoryContents contents;

std::lock_guard<std::mutex> lock(mutex);
for (const auto &item : inventory) {
contents.items().push_back(::Item(item.first, item.second));
}

return contents;
}

void add_item(const ::Item &item) override
{
if (delay > 0) {
std::this_thread::sleep_for(std::chrono::seconds(delay));
}

std::lock_guard<std::mutex> lock(mutex);
if (inventory.find(item.name()) == inventory.end()) {
inventory[item.name()] = item.quantity();
} else {
inventory[item.name()] += item.quantity();
}
}

void remove_item(const ::Item &item) override
{
if (delay > 0) {
std::this_thread::sleep_for(std::chrono::seconds(delay));
}

std::lock_guard<std::mutex> lock(mutex);
if (inventory.find(item.name()) == inventory.end()) {
throw UnknownItemError(item.name());
}

inventory[item.name()] -= item.quantity();
if (inventory[item.name()] <= 0) {
inventory.erase(item.name());
}
}

private:
unsigned int delay; // artificial delay in seconds to simulate processing
std::map<std::string, int> inventory; // stores the items and quantities

// The mutex protects the map from concurrent access, since the server
// can process concurrent remote calls.
std::mutex mutex;
};

int main(int argc, char *argv[])
{
using namespace application;

// Parse arguments
auto arguments = parse_arguments(argc, argv, false);
if (arguments.parse_result == ParseReturn::exit) {
return EXIT_SUCCESS;
} else if (arguments.parse_result == ParseReturn::failure) {
return EXIT_FAILURE;
}

// Sets Connext verbosity to help debugging
rti::config::Logger::instance().verbosity(arguments.verbosity);

// To turn on additional logging, include <rti/config/Logger.hpp> and
// uncomment the following line:
// rti::config::Logger::instance().verbosity(rti::config::Verbosity::STATUS_ALL);

// Create a DomainParticipant with default Qos. The Service will communicate
// only with Clients that join the same domain_id
dds::domain::DomainParticipant participant(arguments.domain_id);

// Create an instance of the service interface
auto service_impl = std::make_shared<InventoryImpl>(arguments.delay);

// A server provides the execution environment (a thread pool) for one or
// more services
dds::rpc::ServerParams server_params;

// Use a thread_pool_size > 1 to dispatch incoming function calls in
// parallel
server_params.extensions().thread_pool_size(4);

// Create the server with the server_params configuration
dds::rpc::Server server(server_params);

// Create a service with an instance of the service interface, and attach
// it to a server. The service will wait for remote calls, call the
// service interface to compute the results, and send them back.
dds::rpc::ServiceParams params(participant);
params.service_name("Inventory");
::InventoryServiceService service(service_impl, server, params);

std::cout << "InventoryService running... " << std::endl;
server.run();

// Releases the memory used by the participant factory. Optional at
// application exit
dds::domain::DomainParticipant::finalize_participant_factory();

return 0;
}
Loading
Loading