Tekkotsu Homepage
Demos
Overview
Downloads
Dev. Resources
Reference
Credits

Controller.h

Go to the documentation of this file.
00001 //-*-c++-*-
00002 #ifndef INCLUDED_Controller_h
00003 #define INCLUDED_Controller_h
00004 
00005 #include "Controls/ControlBase.h"
00006 #include "Behaviors/BehaviorBase.h"
00007 #include "Events/EventBase.h"
00008 #include "Events/EventRouter.h"
00009 #include "Motion/MotionManager.h"
00010 #include "Wireless/Wireless.h"
00011 #include "Wireless/Socket.h"
00012 #include <stack>
00013 
00014 //! Handles the menu/command system... when it detects the EmergencyStopMC is activated, it'll kick into high priority.
00015 /*! Keeps track of a command stack.  A Control can designate another sub-control, which will receive events until it finishes\n
00016  *  Events will then be sent to the parent again.
00017  *
00018  *  The GUI uses the same commands as the user (makes it much easier to have only one parser).
00019  *  The commands are:
00020  *  - '<tt>!refresh</tt>' - redisplays the current control (handy on first connecting, or when other output has scrolled it off the screen)
00021  *  - '<tt>!reset</tt>' - return to the root control
00022  *  - '<tt>!next</tt>' - calls ControlBase::doNextItem() of the current control
00023  *  - '<tt>!prev</tt>' - calls ControlBase::doPrevItem() of the current control
00024  *  - '<tt>!select</tt> [<i>item</i>]' - calls ControlBase::doSelect() of the current control, unless <i>item</i> is specified, in which case it is searched for, starting at the root.
00025  *  - '<tt>!cancel</tt>' - calls current ControlBase::doCancel(), indicates control should cease activity and return to parent (e.g. "Back" button)
00026  *  - '<tt>!dump_stack</tt>' - requests a dump of the current stack of submenus (useful if the GUI (re)connects and thus current robot state is unknown)
00027  *  - '<tt>!post </tt><i>generator source type</i> [<i>duration</i>]' - posts an event of your choosing; <i>Generator</i> should be an entry in EventBase::EventGeneratorNames (e.g. timerEGID) or numeric value; <i>source</i> should be a numeric value (unless generator is buttonEGID, in which case it could be an entry from #buttonNames); <i>type</i> can be a numeric value, EventBase::EventTypeNames (e.g. <tt>activate</tt>), or EventBase::EventTypeAbbr (e.g. <tt>A</tt>); <i>duration</i>, if specified, gives the value for EventBase::duration
00028  *  - '<tt>!msg </tt><i>text</i>' - sends <i>text</i> out as a TextMsgEvent; also note that any text entered on the console port while a GUI is also connected will also be sent as a TextMsgEvent, without needing the !input.
00029  *  - '<tt>!root </tt><i>text</i>' - calls ControlBase::takeInput(<i>text</i>) on the root control
00030  *  - '<tt>!hello</tt>' - responds with '<tt>hello\\n</tt><i>count</i>\\n' where <i>count</i> is the number of times '<tt>!hello</tt>' has been sent.  Good for detecting first connection after boot vs. a reconnect.
00031  *  - '<tt>!hilight</tt> [<i>n1</i> [<i>n2</i> [...]]]' - hilights zero, one, or more items in the menu
00032  *  - '<tt>!input </tt><i>text</i>' - calls ControlBase::takeInput(text) on the currently hilighted control(s)
00033  *  - '<tt>!set </tt><i>section</i><tt>.</tt><i>key</i><tt>=</tt><i>value</i>' - will be sent to Config::setValue(<i>section</i>,<i>key</i>,<i>value</i>)
00034  *  - '<tt>!refreshsketchworld</tt>' - refresh world sketch
00035  *  - '<tt>!refreshsketchlocal</tt>' - refresh local sketch
00036  *  - '<tt>!refreshsketchcamera</tt>' - refresh camera sketch
00037  *  - any text not beginning with '<tt>!</tt>' - sent to ControlBase::takeInput() of the current control
00038  *
00039  *  In return, to send the menus to the GUI, the following messages are sent: (newlines are required where shown)
00040  *  - '<tt>push</tt>' - signals a submenu has been activated
00041  *  - '<tt>pop</tt>' - signals a submenu has been closed
00042  *  - '<tt>refresh</tt>\n
00043  *    <i>text:title</i>\n
00044  *    <i>int:numitems</i>\n
00045  *    <i>bool:hasSubmenus</i><sub>1</sub>\n
00046  *    <i>bool:hilighted</i><sub>1</sub>\n
00047  *    <i>text:item-title</i><sub>1</sub>\n
00048  *    <i>text:item-description</i><sub>1</sub>\n
00049  *    ...\n
00050  *    <i>bool:hasSubmenus<sub>numitems</sub></i>\n
00051  *    <i>bool:hilighted<sub>numitems</sub></i>\n
00052  *    <i>text:item-title<sub>numitems</sub></i>\n
00053  *    <i>text:item-description<sub>numitems</sub></i>' - refreshes the current menu\n
00054  *  - '<tt>status</tt>\n
00055  *    <i>text</i>' - sets the status bar to <i>text</i> (until the next refresh)
00056  *  - '<tt>load</tt>\n
00057  *    <i>text:classname</i>\n
00058  *    <i>text:instancename</i>\n
00059  *    <i>int:port</i>\n
00060  *    [<i>arg1</i> [<i>arg2</i> [...]]]' - tells the GUI to load the java class named <i>classname</i>, and have it connect to <i>port</i>, passing it the argument list.
00061  *    <i>classname</i> should contain a constructor of the form <tt>Classname(String </tt><i>host</i>, <tt>int </tt><i>port</i>, <tt>String </tt><i>args</i><tt>[])</tt>
00062  *    the argument list is parsed as if it were on the console - unescaped or unquoted spaces will separate args into elements in the array
00063  *  - '<tt>close</tt>\n
00064  *    <i>text:instancename</i>' - calls <tt>close()</tt> on an object previously created by a <tt>load</tt> message.
00065  *    The Java object is expected to contain a function <tt>void close()</tt>.
00066  *  - '<tt>stack_dump</tt>\n
00067  *    <i>int:depth</i>\n
00068  *    <i>text:item-title</i><sub>1</sub>\n
00069  *    ...\n
00070  *    <i>text:item-title</i><sub>depth</sub>' - a listing of the current stack, first item is root, last item is current control
00071  *  - '<tt>goodbye</tt>' - Indicates the connection is about to be closed purposefully, to differentiate from an accidental cut off.
00072  *  
00073  *  bool types are expected to be numerical values, 0 for false,
00074  *  non-zero for true.
00075  *
00076  *  <tt>load</tt> and <tt>close</tt> are intended to allow pop-up
00077  *  windows for custom displays.
00078  *
00079  *  The upstream is the responsibility of the individual Controls, but
00080  *  the protocol is listed here to keep it together.  When a control's
00081  *  state changes, it's that control's responsiblity to refresh the UI
00082  *  (LEDs, console, and GUI as appropriate).  Thus, future extensions
00083  *  to the upstream protocol are between the control which will use it
00084  *  and the GUI.  Future extensions to the downstream protocol would
00085  *  involve changing Controller and the GUI.
00086  *
00087  *  The Controller may connect to serr in the future to pop-up an alert
00088  *  anytime output to serr occurs.
00089  *
00090  *  Note that all state is maintained on the robot - even if the GUI
00091  *  is connected, you can still use the buttons to interact with the
00092  *  controller, and the GUI will update to reflect the changes.  In
00093  *  HCI (Human Computer Interaction) parlance, this is the MVC,
00094  *  Model-View-Controller architecture, almost by necessity. (HCI
00095  *  happens to be my double major when I was an undergrad ;)
00096  *    
00097  *  Also, the Controller is responsible for sending out TextMsgEvents
00098  *  from user input it receives - either a !msg command from the
00099  *  console or GUI, or <i>any text at all</i> which is received on the
00100  *  console if there is already a GUI connected.
00101  *
00102  *  These TextMsgEvents are always status events, and the duration
00103  *  field is always 0.
00104  */
00105 class Controller : public BehaviorBase, public EventTrapper {
00106 public:
00107   Controller() : BehaviorBase("Controller"), EventTrapper(), display(MotionManager::invalid_MC_ID), estop_id(MotionManager::invalid_MC_ID), root(NULL), cmdstack(), last_time(0), cur_time(0), nextEv_val(0), nextEv_dur(0), prevEv_val(0), prevEv_dur(0), alreadyGotBoth(false), isControlling(false), usesButtons(false), gui_comm(NULL)  {init();} //!< Constructor
00108   Controller(ControlBase* r) : BehaviorBase("Controller"), EventTrapper(), display(MotionManager::invalid_MC_ID), estop_id(MotionManager::invalid_MC_ID), root(r), cmdstack(), last_time(0), cur_time(0), nextEv_val(0), nextEv_dur(0), prevEv_val(0), prevEv_dur(0), alreadyGotBoth(false), isControlling(false), usesButtons(false), gui_comm(NULL) { init(); } //!< Constructor, sets a default root control
00109   virtual ~Controller() {
00110     delete root;
00111     theOneController=NULL;
00112   } //!< Destructor
00113 
00114   //@{
00115   //! event masks used by processEvent()
00116   static EventBase nextItem; 
00117   static EventBase prevItem;
00118   static EventBase nextItemFast;
00119   static EventBase prevItemFast;
00120   static EventBase selectItem;
00121   static EventBase cancel; //@}
00122 
00123   virtual void doStart(); //!< register for events and resets the cmdstack
00124   virtual void doStop(); //!< stop listening for events and resets the cmdstack
00125   virtual void doEvent(); //!< just for e-stop activation/deactivation
00126   virtual bool trapEvent(const EventBase& e); //!< passes an event to the top control
00127   
00128   void reset();   //!< will take the command stack back down to the root
00129   void refresh(); //!< refreshes the display, for times like sub-control dying, the previous control needs to reset it's display
00130 
00131   void refreshSketchWorld(); //!< refresh world sketches
00132   void refreshSketchLocal(); //!< refresh local sketches
00133   void refreshSketchCamera(); //!< refresh camera sketches
00134 
00135   void push(ControlBase* c); //!< puts a new control on top
00136   void pop();                //!< kills the top control, goes to previous
00137   ControlBase* top() { return cmdstack.top(); } //!< returns the current control
00138 
00139   Controller& setRoot(ControlBase* r); //!< sets the root level control
00140 
00141   Controller& setEStopID(MotionManager::MC_ID estopid); //!< Sets the emergency stop MC to monitor for pausing
00142   
00143   static std::string getClassDescription() { return "Provides interface for activating/deactivating controls (and through them, behaviors)"; }
00144   virtual std::string getDescription() const { return getClassDescription(); }
00145 
00146 
00147   static void loadGUI(const std::string& type, const std::string& name, unsigned int port) {loadGUI(type,name,port,std::vector<std::string>());} //!< attempts to open a Java object on the desktop
00148   static void loadGUI(const std::string& type, const std::string& name, unsigned int port, const std::vector<std::string>& args); //!< attempts to open a Java object on the desktop
00149   static void closeGUI(const std::string& name); //!< calls close() on a Java object loaded with loadGUI() (on the desktop)
00150 
00151   static int gui_comm_callback(char *buf, int bytes); //!< called by wireless when there's new data from the GUI
00152   static int console_callback(char *buf, int bytes);  //!< called by wireless when someone has entered new data on the tekkotsu console (NOT cin)
00153 
00154 protected:
00155   //! calls initButtons with the appropriate button offsets for the host robot model
00156   void init();
00157 
00158   //! assigns appropriate values to the static event bases
00159   void initButtons(unsigned fastTime, unsigned downTime, unsigned nextB, unsigned prevB, unsigned nextFastB, unsigned prevFastB, unsigned selectB, unsigned cancelB);
00160 
00161   //! called with each line that's entered on the tekkotsu console or from the GUI
00162   void takeLine(const std::string& s);
00163   
00164   //! sends stack of currently active controls
00165   void dumpStack();
00166 
00167   //! called with slots (options), a name to lookup; will select the named control
00168   bool select(ControlBase* item, const std::string& name);
00169   
00170   //! sets a config value - some values may require additional processing (done here) to have the new values take effect
00171   int setConfig(const std::string& str);
00172 
00173   //! maintains top Control
00174   /*! @param next one of: @li NULL: pop() ::cmdstack @li ::cmdstack.top(): nothing @li other address: ::push(@a next)
00175    *  @return true, all the time, for convenience from trapEvent() */
00176   bool setNext(ControlBase* next);
00177 
00178   //! called when the estop switches on
00179   /*!  causes the top control to activate, registers for button events */
00180   void activate();
00181 
00182   //! called when the estop switches off\n
00183   /*!  causes the top control to deactivate, stops listening for buttons */
00184   void deactivate();
00185   
00186   //! @brief returns true if a valid control is available on the stack
00187   /*!  if the stack is empty, will push root if it's non-null */
00188   bool chkCmdStack();
00189 
00190   //! invalid_MC_ID if not active, otherwise id of high priority LEDs
00191   MotionManager::MC_ID display;
00192 
00193   //! the EmergencyStopMC MC_ID that this Controller is monitoring
00194   MotionManager::MC_ID estop_id;
00195 
00196   //! the base control, if cmdstack underflows, it will be reset to this
00197   ControlBase * root;
00198 
00199   /*! @brief the stack of the current control hierarchy\n
00200    *  should never contain NULL entries */
00201   std::stack< ControlBase* > cmdstack;
00202 
00203   //! returns true when the current time and last time are in different periods
00204   static bool calcPulse(unsigned int t, unsigned int last, unsigned int period) {
00205     if(period<t-last)
00206       return true;
00207     bool nextclock=(t/period)&1;
00208     bool lastclock=(last/period)&1;
00209     return (lastclock!=nextclock);
00210   }
00211 
00212 
00213   unsigned int last_time; //!< the time of the last event
00214   unsigned int cur_time; //!< the time of the current event (do*() can check this instead of calling get_time() )
00215   float nextEv_val; //!< the magnitude of the last next event (::nextItem)
00216   unsigned int nextEv_dur; //!< the duration of the last next event (::nextItem)
00217   float prevEv_val; //!< the magnitude of the last prev event (::prevItem)
00218   unsigned int prevEv_dur; //!< the duration of the last prev event (::prevItem)
00219   bool alreadyGotBoth; //!< if doReadStdIn() was already called, but the buttons are both still down
00220   bool isControlling; //!< true if the Controller is currently active (in the activate()/deactivate() sense, not doStart()/doStop() sense - use isActive() for that...)
00221   bool usesButtons; //!< true if ControllerGUI knows how to use the buttons for menu navigation, will intercept button presses
00222 
00223   Socket * gui_comm; //!< the socket to listen on for the gui
00224 public:
00225   static Controller * theOneController; //!< currently can't pull connection socket off of server socket, so only one Controller
00226   
00227 private:
00228   Controller(const Controller&); //!< shouldn't be called...
00229   Controller& operator=(const Controller&); //!< shouldn't be called...
00230 };
00231 
00232 /*! @file
00233  * @brief Describes Controller class, a behavior that should be started whenever the emergency stop goes on to provide menus for robot control
00234  * @author ejt (Creator)
00235  */
00236 
00237 #endif

Tekkotsu v5.1CVS
Generated Mon May 9 04:58:37 2016 by Doxygen 1.6.3