diff --git a/CMakeLists.txt b/CMakeLists.txt index 3076168db..fec9b2cac 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -173,6 +173,7 @@ endif() # Set source files set(SRCS src/cpp/Agent.cpp + src/cpp/AgentInstance.cpp src/cpp/Root.cpp src/cpp/processor/Processor.cpp src/cpp/client/ProxyClient.cpp diff --git a/include/uxr/agent/AgentInstance.hpp b/include/uxr/agent/AgentInstance.hpp new file mode 100644 index 000000000..d3bd9b9e5 --- /dev/null +++ b/include/uxr/agent/AgentInstance.hpp @@ -0,0 +1,102 @@ +// Copyright 2020 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// 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. + +#ifndef UXR_AGENT_AGENT_INSTANCE_HPP_ +#define UXR_AGENT_AGENT_INSTANCE_HPP_ + +#include + +#ifdef UAGENT_CLI_PROFILE +#include +#else +#include +#endif // UAGENT_CLI_PROFILE + +#include + +namespace eprosima { +namespace uxr { + +/** + * @brief Singleton class to manage the launch process of a MicroXRCE-DDS Agent. + */ +class AgentInstance +{ +public: + /** + * @brief Default constructor. + */ + UXR_AGENT_EXPORT AgentInstance(); + /** + * @brief AgentInstance class shall not be copy constructible. + */ + UXR_AGENT_EXPORT AgentInstance( + const AgentInstance &) = delete; + + UXR_AGENT_EXPORT AgentInstance( + AgentInstance &&) = delete; + + /** + * @brief AgentInstance class shall not be copy assignable. + */ + UXR_AGENT_EXPORT AgentInstance& operator =( + const AgentInstance &) = delete; + + UXR_AGENT_EXPORT AgentInstance& operator =( + AgentInstance &&) = delete; + + /** + * @brief Get instance associated to this class. + * @return Static reference to the singleton AgentInstance object. + */ + UXR_AGENT_EXPORT static AgentInstance& getInstance(); + + /** + * @brief Create an Agent instance, based on provided input parameters from user. + * @param[in] argc Number of available parameters introduced by the user. + * @param[in] argv List of parameters to be parsed to instantiate an Agent. + * @return Boolean value indicating if a Micro XRCE-DDS Agent was instantiated successfully. + */ + UXR_AGENT_EXPORT bool create( + int argc, + char** argv); + + /** + * @brief Run the created agent until finished via user interrupt or process error. + */ + UXR_AGENT_EXPORT void run(); + +private: +#ifdef UAGENT_CLI_PROFILE + CLI::App app_; + cli::UDPv4Subcommand udpv4_subcmd_; + cli::UDPv6Subcommand udpv6_subcmd_; + cli::TCPv4Subcommand tcpv4_subcmd_; + cli::TCPv6Subcommand tcpv6_subcmd_; +#ifndef _WIN32 + cli::TermiosSubcommand termios_subcmd_; + cli::PseudoTerminalSubcommand pseudo_serial_subcmd_; +#endif // _WIN32 + cli::ExitSubcommand exit_subcmd_; +#else + std::thread agent_thread_; +#endif // UAGENT_CLI_PROFILE +#ifndef _WIN32 + sigset_t signals_; +#endif // _WIN32 +}; +} // uxr +} // eprosima + +#endif // UXR_AGENT_AGENT_INSTANCE_HPP_ \ No newline at end of file diff --git a/include/uxr/agent/utils/ArgumentParser.hpp b/include/uxr/agent/utils/ArgumentParser.hpp index cb1728c5a..946a2b0e6 100644 --- a/include/uxr/agent/utils/ArgumentParser.hpp +++ b/include/uxr/agent/utils/ArgumentParser.hpp @@ -151,7 +151,7 @@ class Argument int argc, char** argv) { - for (size_t position = 0; position < argc; ++position) + for (int position = 0; position < argc; ++position) { if (0 == strcmp(argv[position], short_alias_.c_str()) || 0 == strcmp(argv[position], long_alias_.c_str())) @@ -226,9 +226,9 @@ class Argument private: ArgumentKind argument_kind_; - T value_; std::string short_alias_; std::string long_alias_; + T value_; bool parse_found_; std::set allowed_values_; }; @@ -282,7 +282,7 @@ ParseResult parse_argument( int argc, char** argv) { - for (size_t position = 0; position < argc; ++position) + for (int position = 0; position < argc; ++position) { if (0 == strcmp(argv[position], short_alias_.c_str()) || 0 == strcmp(argv[position], long_alias_.c_str())) @@ -346,9 +346,9 @@ ParseResult parse_argument( private: ArgumentKind argument_kind_; - std::string value_; std::string short_alias_; std::string long_alias_; + std::string value_; bool parse_found_; std::set allowed_values_; }; @@ -564,7 +564,10 @@ class SerialArgs : public PseudoTerminalArgs int argc, char** argv) { - bool result = PseudoTerminalArgs::parse(argc, argv); + if (!PseudoTerminalArgs::parse(argc, argv)) + { + return false; + } ParseResult parse_dev = dev_.parse_argument(argc, argv); if (ParseResult::VALID != parse_dev) { @@ -650,6 +653,12 @@ class ArgumentParser break; } #endif // _WIN32 + case TransportKind::INVALID: + default: + { + result = false; + break; + } } return (result ? ParseResult::VALID : ParseResult::INVALID); } @@ -672,7 +681,7 @@ class ArgumentParser #ifndef _WIN32 bool launch_termios_agent() { - struct termios attr; + struct termios attr = {}; /* Setting CONTROL OPTIONS. */ attr.c_cflag |= unsigned(CREAD); // Enable read. @@ -858,6 +867,7 @@ inline std::thread create_agent_thread( switch (parser.parse_arguments()) { case parser::ParseResult::INVALID: + case parser::ParseResult::NOT_FOUND: { parser::utils::usage(); break; @@ -909,6 +919,7 @@ inline std::thread create_agent_thread( switch (parser.parse_arguments()) { case parser::ParseResult::INVALID: + case parser::ParseResult::NOT_FOUND: { parser::utils::usage(); break; @@ -948,6 +959,7 @@ inline std::thread create_agent_thread( switch (parser.parse_arguments()) { case parser::ParseResult::INVALID: + case parser::ParseResult::NOT_FOUND: { parser::utils::usage(); break; diff --git a/include/uxr/agent/utils/CLI.hpp b/include/uxr/agent/utils/CLI.hpp index 13f5c2202..7e3e50c5f 100644 --- a/include/uxr/agent/utils/CLI.hpp +++ b/include/uxr/agent/utils/CLI.hpp @@ -529,7 +529,7 @@ class TermiosSubcommand : public ServerSubcommand private: void launch_server() final { - struct termios attr; + struct termios attr = {}; /* Setting CONTROL OPTIONS. */ attr.c_cflag |= unsigned(CREAD); // Enable read. diff --git a/microxrce_agent.cpp b/microxrce_agent.cpp index 05e5c71a3..e4fc766fc 100644 --- a/microxrce_agent.cpp +++ b/microxrce_agent.cpp @@ -12,149 +12,17 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include - -#ifdef UAGENT_CLI_PROFILE -#include -#else -#include -#endif - -#include - +#include int main(int argc, char** argv) { -#ifndef _WIN32 - sigset_t signals; - sigemptyset(&signals); - if(sigaddset(&signals, SIGINT) && sigaddset(&signals, SIGTERM)) - { - std::cerr << "Wrong signalset" << std::endl; - std::exit(EXIT_FAILURE); - } - sigprocmask( SIG_BLOCK, &signals, nullptr ); -#endif // _WIN32 - -#ifdef UAGENT_CLI_PROFILE - /* CLI application. */ - CLI::App app("eProsima Micro XRCE-DDS Agent"); - app.require_subcommand(1, 1); - app.get_formatter()->column_width(42); + eprosima::uxr::AgentInstance& agent_instance = agent_instance.getInstance(); - /* CLI subcommands. */ - eprosima::uxr::cli::UDPv4Subcommand udpv4_subcommand(app); - eprosima::uxr::cli::UDPv6Subcommand udpv6_subcommand(app); - eprosima::uxr::cli::TCPv4Subcommand tcpv4_subcommand(app); - eprosima::uxr::cli::TCPv6Subcommand tcpv6_subcommand(app); -#ifndef _WIN32 - eprosima::uxr::cli::TermiosSubcommand termios_subcommand(app); - eprosima::uxr::cli::PseudoTerminalSubcommand pseudo_serial_subcommand(app); -#endif // _WIN32 - eprosima::uxr::cli::ExitSubcommand exit_subcommand(app); - - /* CLI parse. */ - std::string cli_input{}; - for (int i = 1; i < argc; ++i) - { - cli_input.append(argv[i]); - cli_input.append(" "); - } - - while (true) + if (!agent_instance.create(argc, argv)) { - try - { - app.parse(cli_input); - break; - } - catch (const CLI::ParseError& e) - { - app.exit(e); - std::cin.clear(); - std::cout << std::endl; - std::cout << "Enter command: "; - std::getline(std::cin, cli_input); - } - } - -#ifdef _WIN32 - /* Waiting until exit. */ - std::cin.clear(); - char exit_flag = 0; - while ('q' != exit_flag) - { - std::cin >> exit_flag; - } -#else - /* Wait for SIGTERM/SIGINT instead, as reading from stdin may be redirected to /dev/null. */ - int n_signal = 0; - sigwait(&signals, &n_signal); -#endif // _WIN32 - return 0; - -#else - /* Use built-in argument parser */ - if (2 > argc) - { - eprosima::uxr::agent::parser::utils::usage(); - return 1; - } - std::string chosen_transport(argv[1]); - auto valid_transport = eprosima::uxr::agent::parser::utils::check_transport(chosen_transport); - std::thread agent_thread; - if (eprosima::uxr::agent::TransportKind::INVALID != valid_transport) - { - switch (valid_transport) - { - case eprosima::uxr::agent::TransportKind::UDP4: - { - agent_thread = std::move(eprosima::uxr::agent::create_agent_thread< - eprosima::uxr::UDPv4Agent>(argc, argv, valid_transport, &signals)); - break; - } - case eprosima::uxr::agent::TransportKind::UDP6: - { - agent_thread = std::move(eprosima::uxr::agent::create_agent_thread< - eprosima::uxr::UDPv6Agent>(argc, argv, valid_transport, &signals)); - break; - } - case eprosima::uxr::agent::TransportKind::TCP4: - { - agent_thread = std::move(eprosima::uxr::agent::create_agent_thread< - eprosima::uxr::TCPv4Agent>(argc, argv, valid_transport, &signals)); - break; - } - case eprosima::uxr::agent::TransportKind::TCP6: - { - agent_thread = std::move(eprosima::uxr::agent::create_agent_thread< - eprosima::uxr::TCPv6Agent>(argc, argv, valid_transport, &signals)); - break; - } -#ifndef _WIN32 - case eprosima::uxr::agent::TransportKind::SERIAL: - { - agent_thread = std::move(eprosima::uxr::agent::create_agent_thread< - eprosima::uxr::TermiosAgent>(argc, argv, valid_transport, &signals)); - break; - } - case eprosima::uxr::agent::TransportKind::PSEUDOTERMINAL: - { - agent_thread = std::move(eprosima::uxr::agent::create_agent_thread< - eprosima::uxr::PseudoTerminalAgent>(argc, argv, valid_transport, &signals)); - break; - } - } - agent_thread.join(); - return 0; - } - else - { - std::cerr << "Error: chosen transport '" << chosen_transport << "' is invalid!" << std::endl; - eprosima::uxr::agent::parser::utils::usage(); return 1; } + agent_instance.run(); -#endif // _WIN32 -#endif // UAGENT_CLI_PROFILE + return 0; } diff --git a/src/cpp/AgentInstance.cpp b/src/cpp/AgentInstance.cpp new file mode 100644 index 000000000..dd2aa8b32 --- /dev/null +++ b/src/cpp/AgentInstance.cpp @@ -0,0 +1,176 @@ +// Copyright 2020 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// 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. + +#include + +namespace eprosima { +namespace uxr { + +AgentInstance::AgentInstance() +#ifdef UAGENT_CLI_PROFILE + : app_("eProsima Micro XRCE-DDS Agent") + , udpv4_subcmd_(app_) + , udpv6_subcmd_(app_) + , tcpv4_subcmd_(app_) + , tcpv6_subcmd_(app_) +#ifndef _WIN32 + , termios_subcmd_(app_) + , pseudo_serial_subcmd_(app_) +#endif // _WIN32 + , exit_subcmd_(app_) +#endif // UAGENT_CLI_PROFILE +{ +#ifdef UAGENT_CLI_PROFILE + app_.require_subcommand(1, 1); + app_.get_formatter()->column_width(42); +#endif +} + +AgentInstance& AgentInstance::getInstance() +{ + static AgentInstance instance; + return instance; +} + +bool AgentInstance::create( + int argc, + char** argv) +{ +#ifndef _WIN32 + sigemptyset(&signals_); + if (sigaddset(&signals_, SIGINT) && sigaddset(&signals_, SIGTERM)) + { + std :: cerr << "Wrong signalset!" << std::endl; + return false; + } +#endif // _WIN32 +#ifdef UAGENT_CLI_PROFILE + // Parse CLI arguments. + std::stringstream ss; + for (int i = 1; i < argc; ++i) + { + ss << argv[i] << " "; + } + std::string cli_input(ss.str()); + while (true) + { + try + { + app_.parse(cli_input); + break; + } + catch(const CLI::ParseError& e) + { + app_.exit(e); + std::cin.clear(); + std::cout << std::endl; + std::cout << "Enter command: "; + std::getline(std::cin, cli_input); + } + } +#else + // Use built-in argument parser + if (2 > argc) + { + agent::parser::utils::usage(); + return false; + } + std::string chosen_transport(argv[1]); + auto valid_transport = agent::parser::utils::check_transport(chosen_transport); + + switch (valid_transport) + { + case agent::TransportKind::UDP4: + { + agent_thread_ = std::move(agent::create_agent_thread(argc, argv, valid_transport, +#ifndef _WIN32 + &signals_ +#endif // _WIN32 + )); + break; + } + case agent::TransportKind::UDP6: + { + agent_thread_ = std::move(agent::create_agent_thread(argc, argv, valid_transport, +#ifndef _WIN32 + &signals_ +#endif // _WIN32 + )); + break; + } + case agent::TransportKind::TCP4: + { + agent_thread_ = std::move(agent::create_agent_thread(argc, argv, valid_transport, +#ifndef _WIN32 + &signals_ +#endif // _WIN32 + )); + break; + } + case agent::TransportKind::TCP6: + { + agent_thread_ = std::move(agent::create_agent_thread(argc, argv, valid_transport, +#ifndef _WIN32 + &signals_ +#endif // _WIN32 + )); + break; + } +#ifndef _WIN32 + case agent::TransportKind::SERIAL: + { + agent_thread_ = std::move(agent::create_agent_thread(argc, argv, + valid_transport, &signals_)); + break; + } + case agent::TransportKind::PSEUDOTERMINAL: + { + agent_thread_ = std::move(agent::create_agent_thread(argc, argv, + valid_transport, &signals_)); + break; + } +#endif // _WIN32 + case agent::TransportKind::INVALID: + { + std::cerr << "Error: chosen transport '" << chosen_transport << "' is invalid!" << std::endl; + agent::parser::utils::usage(); + return false; + } + } +#endif // UAGENT_CLI_PROFILE + return true; +} + +void AgentInstance::run() +{ +#ifdef UAGENT_CLI_PROFILE + // Wait until exit. +#ifndef _WIN32 + int n_signal = 0; + sigwait(&signals_, &n_signal); +#else + std::cin.clear(); + char exit_flag = 0; + while ('q' != exit_flag) + { + std::cin >> exit_flag; + } +#endif // _WIN32 +#else + agent_thread_.join(); +#endif // UAGENT_CLI_PROFILE +} + +} // namespace uxr +} // namespace eprosima \ No newline at end of file