/* Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012 Her Majesty the Queen in Right of Canada (Communications Research Center Canada) Written by Matthias P. Braendli, matthias.braendli@mpb.li, 2012 This module adds remote-control capability to some of the dabmod modules. see testremotecontrol/test.cpp for an example of how to use this. */ /* This file is part of ODR-DabMod. ODR-DabMod is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. ODR-DabMod is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with ODR-DabMod. If not, see <http://www.gnu.org/licenses/>. */ #ifndef _REMOTECONTROL_H #define _REMOTECONTROL_H #include <list> #include <map> #include <string> #include <iostream> #include <string> #include <boost/bind.hpp> #include <boost/shared_ptr.hpp> #include <boost/enable_shared_from_this.hpp> #include <boost/asio.hpp> #include <boost/foreach.hpp> #include <boost/tokenizer.hpp> #include <boost/thread.hpp> #include <stdexcept> #define RC_ADD_PARAMETER(p, desc) { \ vector<string> p; \ p.push_back(#p); \ p.push_back(desc); \ m_parameters.push_back(p); \ } using namespace std; using boost::asio::ip::tcp; class ParameterError : public std::exception { public: ParameterError(string message) : m_message(message) {} ~ParameterError() throw() {}; const char* what() const throw() { return m_message.c_str(); } private: string m_message; }; class RemoteControllable; /* Remote controllers (that recieve orders from the user) must implement BaseRemoteController */ class BaseRemoteController { public: /* Add a new controllable under this controller's command */ virtual void enrol(RemoteControllable* controllable) = 0; }; /* Objects that support remote control must implement the following class */ class RemoteControllable { public: RemoteControllable(string name) : m_name(name) {} /* return a short name used to identify the controllable. * It might be used in the commands the user has to type, so keep * it short */ virtual std::string get_rc_name() { return m_name; } /* Tell the controllable to enrol at the given controller */ virtual void enrol_at(BaseRemoteController& controller) { controller.enrol(this); } /* Return a list of possible parameters that can be set */ virtual list<string> get_supported_parameters() { cerr << "get_sup_par" << m_parameters.size() << endl; list<string> parameterlist; for (list< vector<string> >::iterator it = m_parameters.begin(); it != m_parameters.end(); ++it) { parameterlist.push_back((*it)[0]); } return parameterlist; } /* Return a mapping of the descriptions of all parameters */ virtual std::list< std::vector<std::string> > get_parameter_descriptions() { return m_parameters; } /* Base function to set parameters. */ virtual void set_parameter(string parameter, string value) = 0; /* Getting a parameter always returns a string. */ virtual string get_parameter(string parameter) = 0; protected: std::string m_name; std::list< std::vector<std::string> > m_parameters; }; /* Implements a Remote controller based on a simple telnet CLI * that listens on localhost */ class RemoteControllerTelnet : public BaseRemoteController { public: RemoteControllerTelnet(int port) { m_port = port; m_running = false; }; void start() { m_running = true; m_child_thread = boost::thread(&RemoteControllerTelnet::process, this, 0); } void stop() { m_running = false; m_child_thread.interrupt(); m_child_thread.join(); } void process(long); void dispatch_command(tcp::socket& socket, string command); void reply(tcp::socket& socket, string message); void enrol(RemoteControllable* controllable) { m_cohort.push_back(controllable); } private: vector<string> tokenise_(string message) { vector<string> all_tokens; boost::char_separator<char> sep(" "); boost::tokenizer< boost::char_separator<char> > tokens(message, sep); BOOST_FOREACH (const string& t, tokens) { all_tokens.push_back(t); } return all_tokens; } RemoteControllable* get_controllable_(string name) { for (list<RemoteControllable*>::iterator it = m_cohort.begin(); it != m_cohort.end(); ++it) { if ((*it)->get_rc_name() == name) { return *it; } } throw ParameterError("Module name unknown"); } list< vector<string> > get_parameter_descriptions_(string name) { RemoteControllable* controllable = get_controllable_(name); return controllable->get_parameter_descriptions(); } list<string> get_param_list_(string name) { RemoteControllable* controllable = get_controllable_(name); return controllable->get_supported_parameters(); } list< vector<string> > get_param_list_values_(string name) { RemoteControllable* controllable = get_controllable_(name); list< vector<string> > allparams; list<string> params = controllable->get_supported_parameters(); cerr << "# of supported parameters " << params.size() << endl; for (list<string>::iterator it = params.begin(); it != params.end(); ++it) { vector<string> item; item.push_back(*it); item.push_back(controllable->get_parameter(*it)); allparams.push_back(item); } return allparams; } string get_param_(string name, string param) { RemoteControllable* controllable = get_controllable_(name); return controllable->get_parameter(param); } void set_param_(string name, string param, string value) { RemoteControllable* controllable = get_controllable_(name); return controllable->set_parameter(param, value); } bool m_running; boost::thread m_child_thread; /* This controller commands the controllables in the cohort */ list<RemoteControllable*> m_cohort; std::string m_welcome; std::string m_prompt; int m_port; }; /* The Dummy remote controller does nothing */ class RemoteControllerDummy : public BaseRemoteController { public: void enrol(RemoteControllable* controllable) {}; }; #endif