From cc5b9ae930b70a9ada0a9efc5a538fa357753e32 Mon Sep 17 00:00:00 2001 From: Tom Rondeau <tom@trondeau.com> Date: Tue, 3 Mar 2015 12:22:33 -0500 Subject: docs: adding in info on ControlPort and Thrift. --- docs/doxygen/other/ctrlport.dox | 424 ++++++++++++++++++++++++++++++++++- docs/doxygen/other/perf_counters.dox | 10 + 2 files changed, 431 insertions(+), 3 deletions(-) (limited to 'docs/doxygen') diff --git a/docs/doxygen/other/ctrlport.dox b/docs/doxygen/other/ctrlport.dox index 64bf9f7d38..57be07829d 100644 --- a/docs/doxygen/other/ctrlport.dox +++ b/docs/doxygen/other/ctrlport.dox @@ -16,8 +16,426 @@ gnuradio.ctrlport module, imported as: from gnuradio import ctrlport \endcode -ControlPort is currently a temporary stub implementation of a set of -RPC calls we would like to enable that would allow remote viewing, -command, and control of GNU Radio flowgraphs and blocks. + +\section ctrlport_conf Configuration + +ControlPort is configured using two files. The first is the GNU Radio +preferences file while the second file is specific to the type of +middleware used. + +The GNU Radio preferences file has three options. The 'on' option is +used to enable or disable the use of ControlPort, and is disabled by +default. The 'config' option allows a user to specify the +middleware-specific configuration file. The 'edges_list' is a special +option that exports the list of nodes and edges of the flowgraph +across ControlPort. This latter option is mainly used for redrawing +the flowgraph for the Performance Counter applications. + +\code + [ControlPort] + on = True + edges_list = True + config = path-to/ctrlport.conf +\endcode + +The ControlPort preferences are installed by default into +'gnuradio-runtime.conf'. These can always be overridden in the local +~/.gnuradio/config.conf file. + + + +\section ctrlport_deps Dependencies + +ControlPort is an abstracted remote procedure call tool that. It is +built on top of other middleware libraries. The following subsections +explain some details about the use of the particular middleware +project. + +Currently, the only implemented middleware library is the Apache +Thrift project. + +\subsection ctrlport_thrift Apache Thrift + +Current version support: >= 0.9.2 + +Apache Thrift is a middleware layer that defines interfaces of a +program using its own Thrift language. GNU Radio's interface file is: + +gnuradio-runtime/lib/controlport/thrift/gnuradio.thrift + +This file defines the interfaces set, get, trigger, and properties. It +also defines a set of data structure Knobs to allow us to pass any +type of data over the interfaces. + +To use Thrift in ControlPort requires a minimum Thrift version of +0.9.0. If a Thrift version greater than or equal to this version is +not found, the Thrift backend to ControlPort will not be installed, +through ControlPort itself still will be. During cmake configuration +time, it prints out information about finding Thrift and requires: + +\li Thrift header files by looking for thrift/Thrift.h +\li Thrift C++ libraries: libthrift.so +\li Thrift Python bindings: "import thrift" + +If all of these are not satisfied, the Thrift backend will not be +installed. Upon completion, cmake outputs a notification of what +components will be built. You will see this if Thrift was found and +can be used: + +\code +* gr-ctrlport +* * thrift +\endcode + +Cmake also uses the Thrift compiler ("thrift") to build the C++ and +Python files necessary for compiling ControlPort. It runs "thrift +--gen cpp" the C++ bindings in the build directory, and then +it runs "thrift --gen py" to build the Python bindings, also in the +build directory. These are used to compile the Thrift ControlPort +features and are necessary files to run the Python clients. If cmake +fails to produce these bindings, it should error out. + + + +\subsubsection ctrlport_thrift_prefs Configuration + +Thrift does not support its own concept of a configuration file, so we +have built one for our purposes in GNU Radio. The 'config' option in +the ControlPort section of the preference files tells ControlPort +where to find the backend-specific file format. GNU Radio's Thrift +format follows the same "[Section] key = value" scheme used in all of +its other preference files. Currently supported configuration options +are: + +\code +[thrift] +port = 9090 +nthreads = 2 +buffersize = 1434 +\endcode + + +\subsubsection ctrlport_thrift_issues Thrift: Current Issues + +Thrift uses a thread pool system to handle each connection, but it +will only allow up to a specified number of threads in the server. The +default value is 10 threads, but the Thrift configuration file allows +the user to change this value. + +Thrift also does not find and use a free ephemeral port when launching +the server. It must be told explicitly which port to launch on, which +we set in the configuration file. This makes it difficult to launch +multiple flowgraphs on the same machine because that will cause a port +collision. Until this is fixed, a way around this is to use the +environmental variable GR_CONF_THRIFT_PORT=xxx to set the port number +for that specific application. + +Efficiency issues of Thrift come from the over-the-wire formatting +done by the transport protocol. It defaults to using 512 byte packets, +which can lead to a lot of fragmentation of the data over the +connection. The buffersize configuration allows the user to set this +value to whatever number fits their network needs. The default 1434 is +designed around the standard 1500 byte Ethernet frame size limit minus +the TCP/IP and Ethernet header size. + + +\subsection ctrlport_client_translation Translation Layer for Clients + +Different backends will produce different ways to interface with the +system. ControlPort in the running flowgraph acts as the server by +exposing interfaces to blocks. The interfaces and API in GNU Radio to +communicate with ControlPort are all abstracted completely away from +the backend methods and data types. That is, the code in GNU Radio's +scheduler and in the blocks that expose their ControlPort interfaces +will work regardless of the backend used. + +We are building better abstractions on the clients sides now, as +well. Although certain backends will support other features of +discovery and services that work well with their products, GNU Radio +wants to make sure that clients can access the data from the +interfaces in the same way for any backend used. This abstraction is +done through the GNURadioControlPortClient. This class is told which +type of backend is used, and defaults to Thrift, and can be passed +information about the server's endpoint such as the host name and port +number to attach to. The GNURadioControlPortClient returns a 'radio' +object that represents the connection to the running flowgraph. + + +\section ctrlport_using Using ControlPort to Export Variables + +The ability to export variables from a block is inherited from +gr::block. Then, when the flowgraph is started, the function +<b>setup_rpc()</b> is called in turn for each block. By default, this +is an empty function. A block overloads this function and defines and +exports variables in it. + +Say we have a class <b>gr::blocks::foo</b> that has variables <b>a</b> +and <b>b</b> that we want to export. Specifically, we want to be able +to read the values of both <b>a</b> and <b>b</b> and also set the +value of <b>b</b>. The class <b>gr::blocks::foo</b> has setters and +getters all set up. So our class implementation header file looks +something like: + +\code +namespace gr { + namespace blocks { + + class foo_impl : public foo + { + private: + float d_a, d_b; + + public: + foo_impl(float a, float b); + ~foo_impl(); + + float a() const { return d_a; } + float b() const { return d_a; } + void set_a(float a) { d_a = a; } + void set_b(float b) { d_b = b; } + void setup_rpc(); + int work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + }; + + } /* namespace blocks */ +} /* namespace gr */ +\endcode + +The source code then sets up the class and fills in +<b>setup_rpc()</b>. + +\code +namespace gr { + namespace blocks { + + foo_impl::foo_impl(float a, float b): + sync_bloc(....), + d_a(a), d_b(b) + { } + + foo_impl::~foo_impl() + { } + + void + foo_impl::setup_rpc() + { +#ifdef GR_CTRLPORT + add_rpc_variable( + rpcbasic_sptr(new rpcbasic_register_get<foo, float>( + alias(), "a", + &foo::a, + pmt::mp(-2.0f), pmt::mp(2.0f), pmt::mp(0.0f), + "", "Get value of a", RPC_PRIVLVL_MIN, + DISPTIME | DISPOPTSTRIP))); + + add_rpc_variable( + rpcbasic_sptr(new rpcbasic_register_get<foo, float>( + alias(), "b", + &foo::b, + pmt::mp(0.0f), pmt::mp(20.0f), pmt::mp(10.0f), + "", "Get value of b", RPC_PRIVLVL_MIN, + DISPTIME | DISPOPTSTRIP))); + + add_rpc_variable( + rpcbasic_sptr(new rpcbasic_register_set<foo, float>( + alias(), "b", + &foo::set_b, + pmt::mp(0.0f), pmt::mp(20.0f), pmt::mp(10.0f), + "", "Set value of b", RPC_PRIVLVL_MIN, + DISPNULL))); +#endif /* GR_CTRLPORT */ + } + + int + foo_impl::work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) + { .... } + + } /* namespace blocks */ +} /* namespace gr */ +\endcode + +In the above example, we're ignoring some of the basic semantics of +the class as a GNU Radio block and focus just on the call to set up +the get and set functions over ControlPort. Each block has a function +that allows us to add a new ControlPort interface object to a list, +the <b>add_rpc_variable</b>. We don't care about that list anymore; +that's for ControlPort to worry about. We just add new variables, +either setters or getters. + +Without dissecting every piece of the above calls, notice that we use +the public class, <b>gr::blocks::foo</b> as the class, not the +implementation class. We also use the block's alias, which GNU Radio +uses as a database entry to connect a block by name to the pointer in +memory. This allows ControlPort to know where the object in memory is +at any given time to access the setters and getters. + +The three PMTs specified are simply an expected minimum, maximum, and +default value. None of these are strictly enforced and only serve as +guides. The RPC_PRIVLVL_MIN is currently a placeholder for a +privilege level setting. In many cases, reading <b>b</b> might be +fine for everyone, but we want strong restrictions on who has the +ability to set <b>b</b>. + +And finally, we can specify display options to hint at the right way +to display this variable when remotely plotting it. More on that in +the following section. + +Finally, note that we put \#ifdefs around the code. We always want +<b>setup_rpc</b> to be there and callable, but if ControlPort was not +built for GNU Radio, we cannot register any variables with it. This is +just a nicety to allow us to set up our code for use with ControlPort +without requiring it. + + +\subsection ctrlport_alt_reg Alternative Registers + +If using the concept above, <b>setup_rpc</b> automatically gets called +when the flowgraph is started. In most instances, this is all we ever +need since there's nothing interesting going on until then. However, +if not using a gr::block or needing access before we run the flowgraph, +the above method won't work (it comes down to when the block's alias +has meaning). + +There are alternate variable registration functions for the sets and +gets. These take the form: + +\code + rpcbasic_register_get(const std::string& name, + const char* functionbase, + T* obj, + Tfrom (T::*function)(), + const pmt::pmt_t &min, const pmt::pmt_t &max, const pmt::pmt_t &def, + const char* units_ = "", + const char* desc_ = "", + priv_lvl_t minpriv_ = RPC_PRIVLVL_MIN, + DisplayType display_ = DISPNULL) + + rpcbasic_register_set(const std::string& name, + const char* functionbase, + T* obj, + void (T::*function)(Tto), + const pmt::pmt_t &min, const pmt::pmt_t &max, const pmt::pmt_t &def, + const char* units_ = "", + const char* desc_ = "", + priv_lvl_t minpriv_ = RPC_PRIVLVL_MIN, + DisplayType display_ = DISPNULL) +\endcode + +The only thing different about the above code is that instead of +taking a single 'alias()' name, which provides us access to the +objects pointer, we instead provide a unique name +(<b>fucntionbase</b>) and a pointer to the object itself +(<b>obj</b>). These are templated functions, so the class T is known +from that. + +If using this method, the recommended way is to create a new function +(not <b>setup_rpc</b>), register the variable using +<b>add_rpc_variable</b> but with the different <b>register_get/set</b> +shown here, and then call this function either in the object's +constructor or make it a public member function to be called when you +need it. + + +\section ctrlport_disp Display Options + +When exporting a new RPC variable over ControlPort, one argument is a +display options mask. These options are useful to a remote client to +tell identify activities like default plotters and initial +conditions. The <b>gr-ctrlport-monitor</b> application uses this +heavily in determining how to plot ControlPort variables. + +The options mask is just a 32-bit value with options OR'd +together. Certain options are only appropriate for certain types of +plots. Options on plots where that option is not available will +simply be ignored. + +The main caveat to be aware of is that the DISPXY plot type is +specific to complex values. Therefore, DISPOPTCPLX is assumed. + +These options are specified in rpccallbackregister_base.h and are +exposed through SWIG to live in the \b gr namespace. + +<b>Plot Types</b> +\li <b>DISPNULL:</b> Nothing specified. +\li <b>DISPTIME:</b> Time-domain plot. +\li <b>DISPXY:</b> XY or constellation plot (complex only). +\li <b>DISPPSD:</b> PSD plot. +\li <b>DISPSPEC:</b> Spectrogram plot. +\li <b>DISPRAST:</b> Time raster plot (non-complex only) + +<b>Plot Options</b> +\li <b>DISPOPTCPLX:</b> Signal is complex. +\li <b>DISPOPTLOG:</b> Start plot in semilog-y mode (time domain only). +\li <b>DISPOPTSTEM:</b> Start plot in stem mode (time domain only). +\li <b>DISPOPTSTRIP:</b> Run plot as a stripchart (time domain only). +\li <b>DISPOPTSCATTER:</b> Do scatter plot instead of lines (XY plot only). + + +\section ctrlport_probes ControlPort Probes + +ControlPort provides a set of probes that can be used as sinks that +pass vectors of data across ControlPort. These probes are used to +sample or visualize data remotely. We can place a ControlPort probe +anywhere in the flowgraph to grab the latest sample of data from the +block it's connected to. + +The main ControlPort probe to use is +<b>blocks.ctrlport_probe2_x</b>. From GRC, this is simply "CtrlPort +Probe", which can handle complex, floats, ints, shorts, and bytes. The +blocks are named and given a description to identify them over +ControlPort. The blocks also take a vector length for how many samples +to pass back at a time. Finally, these blocks take a display hint, +as described in the above section. This allows us to specify the +default behavior for how to display the samples. + +Another block that can be used is the <b>fft.ctrlport_probe_psd</b> to +calculate the PSD and pass that over the ControlPort interface. + +\section ctrlport_monitors ControlPort Monitors + +There are two main ControlPort monitor applications provided with GNU +Radio. Both act similarly. The first is a standard ControlPort monitor +application. This connects to a running flowgraph and displays all +exported interfaces in a table format. The name, unit, latest sample, +and description of all interfaces are display in a +row. Double-clicking will open up the default display. Right clicking +any item will allow the user to select the type of plot to use to +display the data. + +When a display is active, using the buttons at the top, the subwindows +can all be tiled or windowed as needed to manage the full +interface. We can then drag-and-drop any other item on top of a +currently running display plot. + +To launch the ControlPort monitor application, know the IP address and +port of the ControlPort endpoint established by the flowgraph and run: + +<pre> +gr-ctrlport-monitor \<ip-addr\> -p \<port\> +</pre> + + +\subsection perfmonitor Performance Monitor + +A second application is used to locally redraw the flowgraph and +display some of the Performance Counters. In this application, the +nodes are blue boxes where the size of the box is proportional to the +work time and the color depth and line width are proportional to the +output buffer fullness. + +The controls at the top of the Performance Monitor application allow +us to select the instantaneous, average, and variance values of the +Performance Counters. And the work time and buffer fullness can be +displayed as a table or bar graph. + +To launch the Performance Monitor, run: + +<pre> +gr-perf-monitorx \<ip-addr\> -p \<port\> +</pre> */ diff --git a/docs/doxygen/other/perf_counters.dox b/docs/doxygen/other/perf_counters.dox index 1a5bf40cba..9bca38268a 100644 --- a/docs/doxygen/other/perf_counters.dox +++ b/docs/doxygen/other/perf_counters.dox @@ -85,4 +85,14 @@ The options for the [PerfCounters] section are: \li clock: sets the type of clock used when calculating work_time ('thread' or 'monotonic'). + +\section pc_perfmonitor Performance Monitor + +See \ref perfmonitor for some details of using a ControlPort-based +monitor application, gr-perf-monitorx, for visualizing the +counters. This application is particularly useful in learning which +blocks are the computationally complex blocks that could use extra +optimization or work to improve their performance. It can also be used +to understand the current 'health' of the application. + */ -- cgit v1.2.3 From 72507d9ab7bc87d1f4552d11fb33798fd86418c7 Mon Sep 17 00:00:00 2001 From: Tom Rondeau <tom@trondeau.com> Date: Wed, 11 Mar 2015 09:56:11 -0400 Subject: controlport: more documentation and linking info. Also allows use of Thrift config file to overload the max number of attempts when starting application and looking for a port. --- docs/doxygen/other/build_guide.dox | 13 ++- docs/doxygen/other/ctrlport.dox | 1 + .../include/gnuradio/thrift_application_base.h | 95 +++++++++++++++------- 3 files changed, 77 insertions(+), 32 deletions(-) (limited to 'docs/doxygen') diff --git a/docs/doxygen/other/build_guide.dox b/docs/doxygen/other/build_guide.dox index c21b9874ac..ebf47dc7e2 100644 --- a/docs/doxygen/other/build_guide.dox +++ b/docs/doxygen/other/build_guide.dox @@ -63,7 +63,7 @@ first. Most recent systems have these packages available. \li audio-osx \li audio-windows -* Optional but recommended dependencies. +<b>Optional but recommended dependencies.</b> It is not necessary to satisfy all of these dependencies; just the one(s) that are right for your system. On Linux, don't expect @@ -82,6 +82,17 @@ audio-osx and audio-windows to be either satisfied or built. \li log4cpp (>= 1.0) http://log4cpp.sourceforge.net/ +<b>Optional</b> + +\ref page_ctrlport may use various backends to perform the RPC +process, and each is its own dependency. + +Currently, ControlPort only supports the Apache Thrift backend. + +\li thrift (>= 0.9.2) https://thrift.apache.org/ + + + \section build_gr_cmake Building GNU Radio GNU Radio is built using the CMake build system diff --git a/docs/doxygen/other/ctrlport.dox b/docs/doxygen/other/ctrlport.dox index 57be07829d..94a768e429 100644 --- a/docs/doxygen/other/ctrlport.dox +++ b/docs/doxygen/other/ctrlport.dox @@ -112,6 +112,7 @@ are: port = 9090 nthreads = 2 buffersize = 1434 +init_attempts = 100 \endcode diff --git a/gnuradio-runtime/include/gnuradio/thrift_application_base.h b/gnuradio-runtime/include/gnuradio/thrift_application_base.h index 538f2969dd..f649ac297e 100644 --- a/gnuradio-runtime/include/gnuradio/thrift_application_base.h +++ b/gnuradio-runtime/include/gnuradio/thrift_application_base.h @@ -25,6 +25,7 @@ #include <gnuradio/api.h> #include <gnuradio/logger.h> +#include <gnuradio/prefs.h> #include <gnuradio/thread/thread.h> #include <boost/date_time/posix_time/posix_time.hpp> @@ -58,18 +59,19 @@ public: }; /*! - * \brief Base class for a Thrift application with a singleton with instance - * function ::i(). Lazy initialization is used to start the Thrift runtime, - * therefore the Thrift runtime is not started unless ::i() is called at least once. - * This typically means that at least one rpc variable must be registered by - * a block before the runtime will start. + * \brief Base class for a Thrift application with a singleton with + * instance function thrift_application_base::i(). Lazy initialization + * is used to start the Thrift runtime, therefore the Thrift runtime + * is not started unless thrift_application_base::i() is called at + * least once. This typically means that at least one rpc variable + * must be registered by a block before the runtime will start. * - * \param TserverBase Template parameter naming the type of the server base, - * which is typically rpcserverbase. - * \param TserverClass Template parameter naming the eventual type of the - * the fully derived application. - * \param _app Reference to the fully derived application instance to be returned - * by ::i(). + * \param TserverBase Template parameter naming the type of the server + * base, which is typically rpcserverbase. + * \param TserverClass Template parameter naming the eventual type of + * the the fully derived application. + * \param _app Reference to the fully derived application instance to + * be returned by thrift_application_base::i(). */ template<typename TserverBase, typename TserverClass> @@ -78,24 +80,29 @@ class thrift_application_base public: thrift_application_base(TserverClass* _app); - // Destructor for the application. Since shutdown and cleanup - // of the runtime is typically custom to a particular booter - // implementation, this must be implemented as a specalized - // function for a particular booter. Thus a template implementation - // is not provided here. + /*! + * Destructor for the application. Since shutdown and cleanup of the + * runtime is typically custom to a particular booter + * implementation, this must be implemented as a specalized function + * for a particular booter. Thus a template implementation is not + * provided here. + */ ~thrift_application_base(); /*! - * \brief The application singleton instance function. + * The application singleton instance function. */ static TserverBase* i(); - // Returns the endpoint string of this application. + + /*! + * Returns the endpoint string of this application. + */ static const std::vector<std::string> endpoints(); protected: /*! - * \brief Allows this application's booter to set the - * endpoint string after the Thrift runtime has initialized. + * Allows this application's booter to set the endpoint string after + * the Thrift runtime has initialized. * * \param[in] endpoint The endpoint string reported by this class. */ @@ -103,25 +110,46 @@ protected: virtual TserverBase* i_impl() = 0; - // Reference to the fully derived application instance. + /*! + * Reference to the fully derived application instance. + */ static TserverClass* d_application; - // Reference to the Thrift runtime; + /*! + * Reference to the Thrift runtime. + */ std::auto_ptr<apache::thrift::server::TServer> d_thriftserver; - // Max number of attempts when checking the Thrift runtime for - // Initialization before giving up. + /*! + * Max number of attempts when checking the Thrift runtime for + * Initialization before giving up. Set in the Thrift config file + * (see \ref ctrlport_thrift_prefs). + */ static const unsigned int d_default_max_init_attempts; - // Default port for the runtime to listen on, if a static port - // is not specified. + + /*! + * Default port for the runtime to listen on, if a static port is + * not specified. Set in the Thrift config file (see \ref + * ctrlport_thrift_prefs). + */ static const unsigned int d_default_thrift_port; - // Maximum number of threads to create when serving - // multiple rpc clients. + + /*! + * Maximum number of threads to create when serving multiple rpc + * clients. Set in the Thrift config file (see \ref + * ctrlport_thrift_prefs). + */ static const unsigned int d_default_num_thrift_threads; - // Default packet size for the IP payload of thrift packets. + + /*! + * Default packet size for the IP payload of thrift packets. Set in + * the Thrift config file (see \ref ctrlport_thrift_prefs). + */ static const unsigned int d_default_thrift_buffer_size; - // logger instances. + /*! + * \ref page_logger instances. + */ gr::logger_ptr d_logger, d_debug_logger; private: @@ -176,12 +204,17 @@ void thrift_application_base<TserverBase, TserverClass>::start_application() { //std::cerr << "thrift_application_base: start_application" << std::endl; + unsigned int max_init_attempts = \ + static_cast<unsigned int>(gr::prefs::singleton()->get_long("thrift", "init_attempts", + d_default_max_init_attempts)); + + if(!p_impl->d_application_initilized) { p_impl->d_start_thrift_thread.reset( (new gr::thread::thread(boost::bind(&thrift_application_base::start_thrift, d_application)))); bool app_started(false); - for(unsigned int attempts(0); (!app_started && attempts < d_default_max_init_attempts); ++attempts) { + for(unsigned int attempts(0); (!app_started && attempts < max_init_attempts); ++attempts) { boost::this_thread::sleep(boost::posix_time::milliseconds(THRIFTAPPLICATION_ACTIVATION_TIMEOUT_MS)); app_started = d_application->application_started(); -- cgit v1.2.3