diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/RemoteControl.cpp | 226 | ||||
| -rw-r--r-- | src/RemoteControl.h | 233 | 
2 files changed, 459 insertions, 0 deletions
| diff --git a/src/RemoteControl.cpp b/src/RemoteControl.cpp new file mode 100644 index 0000000..876d71a --- /dev/null +++ b/src/RemoteControl.cpp @@ -0,0 +1,226 @@ +/* +   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 file is part of CRC-DabMux. + +   CRC-DabMux 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. + +   CRC-DabMux 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 CRC-DabMux.  If not, see <http://www.gnu.org/licenses/>. + */ +#include <list> +#include <string> +#include <iostream> +#include <string> +#include <boost/asio.hpp> + +#include "RemoteControl.h" + +using boost::asio::ip::tcp; + +void +RemoteControllerTelnet::process(long) +{ +    m_welcome = "CRC-DABMUX Remote Control CLI\nWrite 'help' for help.\n**********\n"; +    m_prompt = "> "; + +    std::string in_message; +    size_t length; + +    try { +        boost::asio::io_service io_service; +        tcp::acceptor acceptor(io_service, tcp::endpoint(tcp::v4(), m_port)); + +        while (m_running) { +            in_message = ""; + +            tcp::socket socket(io_service); + +            acceptor.accept(socket); + +            boost::system::error_code ignored_error; + +            boost::asio::write(socket, boost::asio::buffer(m_welcome), +                    boost::asio::transfer_all(), +                    ignored_error); + +            while (m_running && in_message != "quit") { +                boost::asio::write(socket, boost::asio::buffer(m_prompt), +                        boost::asio::transfer_all(), +                        ignored_error); + +                in_message = ""; + +                boost::asio::streambuf buffer; +                length = boost::asio::read_until( socket, buffer, "\n", ignored_error); + +                std::istream str(&buffer);  +                std::getline(str, in_message); + +                if (length == 0) { +                    std::cerr << "Connection terminated" << std::endl; +                    break; +                } + +                while (in_message.length() > 0 &&  +                        (in_message[in_message.length()-1] == '\r' || +                         in_message[in_message.length()-1] == '\n')) { +                    in_message.erase(in_message.length()-1, 1); +                } + +                if (in_message.length() == 0) { +                    continue; +                } + +                std::cerr << "Got message '" << in_message << "'" << std::endl; + +                dispatch_command(socket, in_message); +            } +            std::cerr << "Closing socket" << std::endl; +            socket.close(); +        } +    } +    catch (std::exception& e) +    { +        std::cerr << e.what() << std::endl; +    } +} + +void +RemoteControllerTelnet::dispatch_command(tcp::socket& socket, string command) +{ +    vector<string> cmd = tokenise_(command); + +    if (cmd[0] == "help") { +        reply(socket, +                "The following commands are supported:\n" +                "  list\n" +                "    * Lists the modules that are loaded\n" +                "  list MODULE\n" +                "    * Lists the parameters exported by module MODULE\n" +                "  show MODULE\n" +                "    * Lists all parameters and their values from module MODULE\n" +                "  get MODULE PARAMETER\n" +                "    * Gets the value for the specified PARAMETER from module MODULE\n" +                "  set MODULE PARAMETER VALUE\n" +                "    * Sets the value for the PARAMETER ofr module MODULE\n" +                "  quit\n" +                "    * Terminate this session\n" +                "\n"); +    } +    else if (cmd[0] == "list") { +        stringstream ss; + +        if (cmd.size() == 1) { +            for (list<RemoteControllable*>::iterator it = m_cohort.begin(); +                    it != m_cohort.end(); ++it) { +                ss << (*it)->get_rc_name() << " "; +            } +        } +        else if (cmd.size() == 2) { +            try { +                stringstream ss; + +                list< vector<string> > params = get_parameter_descriptions_(cmd[1]); +                for (list< vector<string> >::iterator it = params.begin(); +                        it != params.end(); ++it) { +                    ss << (*it)[0] << " : " << (*it)[1] << endl; +                } +                reply(socket, ss.str()); +            } +            catch (ParameterError &e) { +                reply(socket, e.what()); +            } +        } +        else { +            reply(socket, "Too many arguments for command 'list'"); +        } + +        reply(socket, ss.str()); +    } +    else if (cmd[0] == "show") { +        if (cmd.size() == 2) { +            try { +                stringstream ss; +                list< vector<string> > r = get_param_list_values_(cmd[1]); +                for (list< vector<string> >::iterator it = r.begin(); +                        it != r.end(); ++it) { +                    ss << (*it)[0] << ": " << (*it)[1] << endl; +                } +                reply(socket, ss.str()); + +            } +            catch (ParameterError &e) { +                reply(socket, e.what()); +            } +        } +        else +        { +            reply(socket, "Incorrect parameters for command 'show'"); +        } +    } +    else if (cmd[0] == "get") { +        if (cmd.size() == 3) { +            try { +                string r = get_param_(cmd[1], cmd[2]); +                reply(socket, r); +            } +            catch (ParameterError &e) { +                reply(socket, e.what()); +            } +        } +        else +        { +            reply(socket, "Incorrect parameters for command 'get'"); +        } +    } +    else if (cmd[0] == "set") { +        if (cmd.size() == 4) { +            try { +                set_param_(cmd[1], cmd[2], cmd[3]); +                reply(socket, "ok"); +            } +            catch (ParameterError &e) { +                reply(socket, e.what()); +            } +            catch (exception &e) { +                reply(socket, "Error: Invalid parameter value. "); +            } +        } +        else +        { +            reply(socket, "Incorrect parameters for command 'get'"); +        } +    } +    else if (cmd[0] == "quit") { +        reply(socket, "Goodbye"); +    } +    else { +        reply(socket, "Message not understood"); +    } +} + +void +RemoteControllerTelnet::reply(tcp::socket& socket, string message) +{ +    boost::system::error_code ignored_error; +    stringstream ss; +    ss << message << "\r\n"; +    boost::asio::write(socket, boost::asio::buffer(ss.str()), +            boost::asio::transfer_all(), +            ignored_error); +} + diff --git a/src/RemoteControl.h b/src/RemoteControl.h new file mode 100644 index 0000000..77dbff4 --- /dev/null +++ b/src/RemoteControl.h @@ -0,0 +1,233 @@ +/* +   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 CRC-DabMux. + +   CRC-DabMux 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. + +   CRC-DabMux 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 CRC-DabMux.  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 + | 
