From be628b36b72d8ef52ad63ee359eb6f5c493d024b Mon Sep 17 00:00:00 2001 From: James Turner Date: Thu, 19 Dec 2019 00:00:41 +0000 Subject: [PATCH] Allow add/remove of IO channels at runtime Commands: add-io-channel remove-io-channel --- src/Main/fg_io.cxx | 92 ++++++++++++++++++++++++++++++++++------ src/Main/fg_io.hxx | 8 +++- src/Network/protocol.cxx | 6 ++- src/Network/protocol.hxx | 7 ++- 4 files changed, 95 insertions(+), 18 deletions(-) diff --git a/src/Main/fg_io.cxx b/src/Main/fg_io.cxx index 06ed0ed59..702d8b139 100644 --- a/src/Main/fg_io.cxx +++ b/src/Main/fg_io.cxx @@ -40,6 +40,7 @@ #include #include #include +#include #include #include @@ -87,24 +88,29 @@ FGIO::~FGIO() // configure a port based on the config string + FGProtocol* FGIO::parse_port_config( const string& config ) { SG_LOG( SG_IO, SG_INFO, "Parse I/O channel request: " << config ); - string_list tokens = simgear::strutils::split( config, "," ); if (tokens.empty()) { SG_LOG( SG_IO, SG_ALERT, "Port configuration error: empty config string" ); - return NULL; + return nullptr; } + + return parse_port_config(tokens); +} - string protocol = tokens[0]; +FGProtocol* +FGIO::parse_port_config( const string_list& tokens ) +{ + const string protocol = tokens[0]; SG_LOG( SG_IO, SG_INFO, " protocol = " << protocol ); - FGProtocol *io = NULL; - + FGProtocol *io = nullptr; try { if ( protocol == "atcsim" ) { @@ -319,7 +325,7 @@ FGIO::parse_port_config( const string& config ) } else { - SG_LOG( SG_IO, SG_ALERT, "Unknown transport medium \"" << medium << "\" in \"" << config << "\""); + SG_LOG( SG_IO, SG_ALERT, "Unknown transport medium \"" << medium << "\" for \"" << protocol << "\""); delete io; return nullptr; } @@ -342,32 +348,35 @@ FGIO::init() // port onto the port list copies the structure and destroys the // original, which closes the port and frees up the fd ... doh!!! - string_list::iterator i = globals->get_channel_options_list()->begin(); - string_list::iterator end = globals->get_channel_options_list()->end(); - for (; i != end; ++i ) { - add_channel( *i ); + for (const auto config : *(globals->get_channel_options_list())) { + add_channel(config); } // of channel options iteration + + auto cmdMgr = globals->get_commands(); + cmdMgr->addCommand("add-io-channel", this, &FGIO::commandAddChannel); + cmdMgr->addCommand("remove-io-channel", this, &FGIO::commandRemoveChannel); } // add another I/O channel -void FGIO::add_channel(const string& config) +FGProtocol* FGIO::add_channel(const string& config) { // parse the configuration string and store the results in the // appropriate FGIOChannel structure FGProtocol *p = parse_port_config( config ); if (!p) { - return; + return nullptr; } p->open(); if ( !p->is_enabled() ) { SG_LOG( SG_IO, SG_ALERT, "I/O Channel config failed." ); delete p; - return; + return nullptr; } io_channels.push_back( p ); + return p; } void @@ -420,6 +429,10 @@ FGIO::shutdown() } io_channels.clear(); + + auto cmdMgr = globals->get_commands(); + cmdMgr->removeCommand("add-io-channel"); + cmdMgr->removeCommand("remove-io-channel"); } void @@ -440,7 +453,7 @@ bool FGIO::isMultiplayerRequested() // check the channel options list for a multiplay setting - this // is easier than checking the raw Options arguments, but works before - // this subsytem is actuallyt created. + // this subsytem is actually created. auto channels = globals->get_channel_options_list(); if (!channels) return false; // happens running tests @@ -451,5 +464,56 @@ bool FGIO::isMultiplayerRequested() return it != channels->end(); } +bool FGIO::commandAddChannel(const SGPropertyNode * arg, SGPropertyNode * root) +{ + if (!arg->hasChild("config")) { + SG_LOG(SG_NETWORK, SG_WARN, "add-io-channel: missing 'config' argument"); + return false; + } + + const string name = arg->getStringValue("name"); + const string config = arg->getStringValue("config"); + auto protocol = add_channel(config); + if (!protocol) { + SG_LOG(SG_NETWORK, SG_WARN, "add-io-channel: adding channel failed"); + return false; + } + + if (!name.empty()) { + // TODO: add entry to /io/channels/ + + // set the name so we can find the protocol again in the + // future + protocol->set_name(name); + } + + return true; +} + +bool FGIO::commandRemoveChannel(const SGPropertyNode * arg, SGPropertyNode * root) +{ + if (!arg->hasChild("name")) { + SG_LOG(SG_NETWORK, SG_WARN, "remove-io-channel: missing 'name' argument"); + } + + const string name = arg->getStringValue("name"); + auto it = find_if(io_channels.begin(), io_channels.end(), + [name](const FGProtocol* proto) + { return proto->get_name() == name; }); + if (it == io_channels.end()) { + SG_LOG(SG_NETWORK, SG_WARN, "remove-io-channel: no channel with name:" + name); + return false; + } + + FGProtocol* p = *it; + if (p->is_enabled()) { + p->close(); + } + delete p; + io_channels.erase(it); + return true; +} + + // Register the subsystem. SGSubsystemMgr::Registrant registrantFGIO; diff --git a/src/Main/fg_io.hxx b/src/Main/fg_io.hxx index 8f9fe97e2..68715c20c 100644 --- a/src/Main/fg_io.hxx +++ b/src/Main/fg_io.hxx @@ -59,9 +59,10 @@ public: static bool isMultiplayerRequested(); private: - void add_channel(const std::string& config); + FGProtocol* add_channel(const std::string& config); + FGProtocol* parse_port_config( const std::string& cfgstr ); - + FGProtocol* parse_port_config( const string_list& tokens ); private: // define the global I/O channel list //io_container global_io_list; @@ -70,6 +71,9 @@ private: ProtocolVec io_channels; SGPropertyNode_ptr _realDeltaTime; + + bool commandAddChannel(const SGPropertyNode * arg, SGPropertyNode * root); + bool commandRemoveChannel(const SGPropertyNode * arg, SGPropertyNode * root); }; #endif // _FG_IO_HXX diff --git a/src/Network/protocol.cxx b/src/Network/protocol.cxx index a76f5efdd..06247d71b 100644 --- a/src/Network/protocol.cxx +++ b/src/Network/protocol.cxx @@ -33,7 +33,7 @@ FGProtocol::FGProtocol() : count(0), dir(SG_IO_NONE), enabled(false), - io(NULL) + io(nullptr) { } @@ -42,6 +42,10 @@ FGProtocol::~FGProtocol() { delete io; } +void FGProtocol::set_name(const std::string& n) +{ + m_name = n; +} // standard I/O channel open routine bool FGProtocol::open() { diff --git a/src/Network/protocol.hxx b/src/Network/protocol.hxx index 3bd7f83ea..20a981d79 100644 --- a/src/Network/protocol.hxx +++ b/src/Network/protocol.hxx @@ -55,7 +55,8 @@ private: bool enabled; SGIOChannel *io; - + std::string m_name; + public: FGProtocol(); @@ -92,6 +93,10 @@ public: inline SGIOChannel *get_io_channel() const { return io; } inline void set_io_channel( SGIOChannel *c ) { io = c; } + + // allow storing an identifying name on the Protocol instance + void set_name(const std::string& n); + std::string get_name() const { return m_name; } };