From 6652eb4a033b38bd952563f3544eb11e98f27327 Mon Sep 17 00:00:00 2001 From: Martin Braun Date: Wed, 31 Jan 2018 20:20:14 +0100 Subject: uhd: Move internal headers to uhdlib/ To avoid the proliferation of additional include directories and multiple ways of including project-local headers, we now default to moving all headers that are used across UHD into the uhdlib/ subdirectory. Some #include statements were also reordered as they were modified for closer compliance with the coding guidelines. Internal cpp source files should now include files like this: #include Reviewed-by: Ashish Chaudhari --- host/lib/include/uhdlib/utils/rpc.hpp | 191 ++++++++++++++++++++++++++++++++++ 1 file changed, 191 insertions(+) create mode 100644 host/lib/include/uhdlib/utils/rpc.hpp (limited to 'host/lib/include/uhdlib/utils/rpc.hpp') diff --git a/host/lib/include/uhdlib/utils/rpc.hpp b/host/lib/include/uhdlib/utils/rpc.hpp new file mode 100644 index 000000000..c7c27afd2 --- /dev/null +++ b/host/lib/include/uhdlib/utils/rpc.hpp @@ -0,0 +1,191 @@ +// +// Copyright 2017 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#ifndef INCLUDED_UTILS_RPC_HPP +#define INCLUDED_UTILS_RPC_HPP + +#include +#include +#include +#include +#include + +namespace uhd { + +/*! Abstraction for RPC client + * + * Purpose of this class is to wrap the underlying RPC implementation. + * This class holds a connection to an RPC server (the connection is severed on + * destruction). + */ +class rpc_client +{ + public: + using sptr = std::shared_ptr; + + static sptr make( + const std::string &addr, + const uint16_t port, + const std::string &get_last_error_cmd="" + ) { + return std::make_shared(addr, port, get_last_error_cmd); + } + + /*! + * \param addr An IP address to connect to + * \param port Port to connect to + * \param get_last_error_cmd A command that queries an error string from + * the RPC server. If set, the RPC client will + * try and use this command to fetch information + * about what went wrong on the client side. + */ + rpc_client( + const std::string &addr, + const uint16_t port, + std::string const &get_last_error_cmd="" + ) : _client(addr, port) + , _get_last_error_cmd(get_last_error_cmd) + { + // nop + } + + /*! Perform an RPC request. + * + * Thread safe (locked). This function blocks until it receives a valid + * response from the server. + * + * \param func_name The function name that is called via RPC + * \param args All these arguments are passed to the RPC call + * + * \throws uhd::runtime_error in case of failure + */ + template + return_type request(std::string const& func_name, Args&&... args) + { + std::lock_guard lock(_mutex); + try { + return _client.call(func_name, std::forward(args)...) + .template as(); + } catch (const ::rpc::rpc_error &ex) { + const std::string error = _get_last_error_safe(); + if (not error.empty()) { + UHD_LOG_ERROR("RPC", error); + } + throw uhd::runtime_error(str( + boost::format("Error during RPC call to `%s'. Error message: %s") + % func_name % (error.empty() ? ex.what() : error) + )); + } catch (const std::bad_cast& ex) { + throw uhd::runtime_error(str( + boost::format("Error during RPC call to `%s'. Error message: %s") + % func_name % ex.what() + )); + } + }; + + /*! Perform an RPC notification. + * + * Thread safe (locked). This function does not require a response from the + * server, although the underlying implementation may provide one. + * + * \param func_name The function name that is called via RPC + * \param args All these arguments are passed to the RPC call + * + * \throws uhd::runtime_error in case of failure + */ + template + void notify(std::string const& func_name, Args&&... args) + { + std::lock_guard lock(_mutex); + try { + _client.call(func_name, std::forward(args)...); + } catch (const ::rpc::rpc_error &ex) { + const std::string error = _get_last_error_safe(); + if (not error.empty()) { + UHD_LOG_ERROR("RPC", error); + } + throw uhd::runtime_error(str( + boost::format("Error during RPC call to `%s'. Error message: %s") + % func_name % (error.empty() ? ex.what() : error) + )); + } catch (const std::bad_cast& ex) { + throw uhd::runtime_error(str( + boost::format("Error during RPC call to `%s'. Error message: %s") + % func_name % ex.what() + )); + } + }; + + /*! Like request(), also provides a token. + * + * This is a convenience wrapper to directly call a function that requires + * a token without having to have a copy of the token. + */ + template + return_type request_with_token(std::string const& func_name, Args&&... args) + { + return request(func_name, _token, std::forward(args)...); + }; + + /*! Like notify(), also provides a token. + * + * This is a convenience wrapper to directly call a function that requires + * a token without having to have a copy of the token. + */ + template + void notify_with_token(std::string const& func_name, Args&&... args) + { + notify(func_name, _token, std::forward(args)...); + }; + + /*! Sets the token value. This is used by the `_with_token` methods. + */ + void set_token(const std::string &token) + { + _token = token; + } + + void set_timeout(size_t timeout_ms) + { + _client.set_timeout(timeout_ms); + } + + private: + /*! Pull the last error out of the RPC server. Not thread-safe, meant to + * be called from notify() or request(). + * + * This function will do its best not to get in anyone's way. If it can't + * get an error string, it'll return an empty string. + */ + std::string _get_last_error_safe() + { + if (_get_last_error_cmd.empty()) { + return ""; + } + try { + return _client.call(_get_last_error_cmd).as(); + } catch (const ::rpc::rpc_error &ex) { + // nop + } catch (const std::bad_cast& ex) { + // nop + } catch (...) { + // nop + } + return ""; + } + + //! Reference the actual RPC client + ::rpc::client _client; + //! If set, this is the command that will retrieve an error + const std::string _get_last_error_cmd; + + std::string _token; + std::mutex _mutex; +}; + +} /* namespace uhd */ + +#endif /* INCLUDED_UTILS_RPC_HPP */ -- cgit v1.2.3