Main Page | Class Hierarchy | Class List | Directories | File List | Class Members | File Members | Related Pages | Examples

clientDemo.cpp

Go to the documentation of this file.
00001 /*
00002 MobileRobots Advanced Robotics Interface for Applications (ARIA)
00003 Copyright (C) 2004, 2005 ActivMedia Robotics LLC
00004 Copyright (C) 2006, 2007 MobileRobots Inc.
00005 
00006      This program is free software; you can redistribute it and/or modify
00007      it under the terms of the GNU General Public License as published by
00008      the Free Software Foundation; either version 2 of the License, or
00009      (at your option) any later version.
00010 
00011      This program is distributed in the hope that it will be useful,
00012      but WITHOUT ANY WARRANTY; without even the implied warranty of
00013      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014      GNU General Public License for more details.
00015 
00016      You should have received a copy of the GNU General Public License
00017      along with this program; if not, write to the Free Software
00018      Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00019 
00020 If you wish to redistribute ARIA under different terms, contact 
00021 MobileRobots for information about a commercial version of ARIA at 
00022 robots@mobilerobots.com or 
00023 MobileRobots Inc, 19 Columbia Drive, Amherst, NH 03031; 800-639-9481
00024 */
00025 
00026 /* This is the ArNetworking example client. 
00027  * It connects to an ArNetworking server, and provides continuous 
00028  * information about the robot state (position, speed, server mode, etc.),
00029  * and, if the server supports the commands, lets you drive the robot with
00030  * the keyboard.  
00031  *
00032  * To see the example client in action, first run a server on the robot's 
00033  * onboard computer, for example, serverDemo, testServer, guiServer (from ARNL),
00034  * or ARAM. Make sure the robot computer is on a network, and run this
00035  * clientDemo with the hostname of the robot computer as an argument:
00036  *
00037  *    ./clientDemo -host myrobot
00038  *
00039  */
00040 
00041 #include "Aria.h"
00042 #include "ArNetworking.h"
00043 
00044 
00045 /* This class provides callback functions which are called by the ArKeyHandler
00046  * object when the user presses keys. These callback functions adjust member
00047  * variables.  The sendInput() method sends a command with those values
00048  * to the server using the "ratioDrive" request.
00049  */
00050 
00051 class InputHandler
00052 {
00053 public: 
00058   InputHandler(ArClientBase *client, ArKeyHandler *keyHandler);
00059   virtual ~InputHandler(void);
00060 
00062   void up(void);
00063 
00065   void down(void);
00066 
00068   void left(void);
00069 
00071   void right(void);
00072 
00074   void sendInput(void);
00075 
00077   void safeDrive();
00078 
00080   void unsafeDrive();
00081 
00082   void listData();
00083 
00084   void logTrackingTerse();
00085   void logTrackingVerbose();
00086   void resetTracking();
00087 protected:
00088   ArClientBase *myClient;
00089   ArKeyHandler *myKeyHandler;
00090 
00092   bool myPrinting;
00093 
00095   double myTransRatio;
00096 
00098   double myRotRatio;
00099 
00102 
00103   ArFunctorC<InputHandler> myUpCB;
00104   ArFunctorC<InputHandler> myDownCB;
00105   ArFunctorC<InputHandler> myLeftCB;
00106   ArFunctorC<InputHandler> myRightCB;
00107   ArFunctorC<InputHandler> mySafeDriveCB;
00108   ArFunctorC<InputHandler> myUnsafeDriveCB;
00109   ArFunctorC<InputHandler> myListDataCB;
00110   ArFunctorC<InputHandler> myLogTrackingTerseCB;
00111   ArFunctorC<InputHandler> myLogTrackingVerboseCB;
00112   ArFunctorC<InputHandler> myResetTrackingCB;
00114 };
00115 
00116 InputHandler::InputHandler(ArClientBase *client, ArKeyHandler *keyHandler) : 
00117   myClient(client), myKeyHandler(keyHandler),
00118   myPrinting(false), myTransRatio(0.0), myRotRatio(0.0) ,
00119 
00120   /* Initialize functor objects with pointers to our handler methods: */
00121   myUpCB(this, &InputHandler::up),
00122   myDownCB(this, &InputHandler::down),
00123   myLeftCB(this, &InputHandler::left),
00124   myRightCB(this, &InputHandler::right),
00125   mySafeDriveCB(this, &InputHandler::safeDrive),
00126   myUnsafeDriveCB(this, &InputHandler::unsafeDrive),
00127   myListDataCB(this, &InputHandler::listData),
00128   myLogTrackingTerseCB(this, &InputHandler::logTrackingTerse),
00129   myLogTrackingVerboseCB(this, &InputHandler::logTrackingVerbose),
00130   myResetTrackingCB(this, &InputHandler::resetTracking)
00131 {
00132 
00133   /* Add our functor objects to the key handler, associated with the appropriate
00134    * keys: */
00135   myKeyHandler->addKeyHandler(ArKeyHandler::UP, &myUpCB);
00136   myKeyHandler->addKeyHandler(ArKeyHandler::DOWN, &myDownCB);
00137   myKeyHandler->addKeyHandler(ArKeyHandler::LEFT, &myLeftCB);
00138   myKeyHandler->addKeyHandler(ArKeyHandler::RIGHT, &myRightCB);
00139   myKeyHandler->addKeyHandler('s', &mySafeDriveCB);
00140   myKeyHandler->addKeyHandler('u', &myUnsafeDriveCB);
00141   myKeyHandler->addKeyHandler('l', &myListDataCB);
00142   myKeyHandler->addKeyHandler('t', &myLogTrackingTerseCB);
00143   myKeyHandler->addKeyHandler('v', &myLogTrackingVerboseCB);
00144   myKeyHandler->addKeyHandler('r', &myResetTrackingCB);
00145 }
00146 
00147 InputHandler::~InputHandler(void)
00148 {
00149 
00150 }
00151 
00152 void InputHandler::up(void)
00153 {
00154   if (myPrinting)
00155     printf("Forwards\n");
00156   myTransRatio = 100;
00157 }
00158 
00159 void InputHandler::down(void)
00160 {
00161   if (myPrinting)
00162     printf("Backwards\n");
00163   myTransRatio = -100;
00164 }
00165 
00166 void InputHandler::left(void)
00167 {
00168   if (myPrinting)
00169     printf("Left\n");
00170   myRotRatio = 100;
00171 }
00172 
00173 void InputHandler::right(void)
00174 {
00175   if (myPrinting)
00176     printf("Right\n");
00177   myRotRatio = -100;
00178 }
00179 
00180 void InputHandler::safeDrive()
00181 {
00182   /* Construct a request packet. The data is a single byte, with value 
00183    * 1 to enable safe drive, 0 to disable. */
00184   ArNetPacket p;
00185   p.byteToBuf(1);
00186 
00187   /* Send the packet as a single request: */
00188   if(myPrinting)
00189     printf("Sending setSafeDrive 1.\n");
00190   myClient->requestOnce("setSafeDrive",&p);
00191   if(myPrinting)
00192     printf("\nSent enable safe drive.\n");
00193 }
00194 
00195 void InputHandler::unsafeDrive()
00196 {
00197   /* Construct a request packet. The data is a single byte, with value 
00198    * 1 to enable safe drive, 0 to disable. */
00199   ArNetPacket p;
00200   p.byteToBuf(0);
00201 
00202   /* Send the packet as a single request: */
00203   if(myPrinting)
00204     printf("Sending setSafeDrive 0.\n");
00205   myClient->requestOnce("setSafeDrive",&p);
00206   if(myPrinting)
00207     printf("\nSent disable safe drive command. Your robot WILL run over things if you're not careful.\n");
00208 }
00209 
00210 void InputHandler::listData()
00211 {
00212   myClient->logDataList();
00213 }
00214 
00215 void InputHandler::logTrackingTerse()
00216 {
00217   myClient->logTracking(true);
00218 }
00219 
00220 void InputHandler::logTrackingVerbose()
00221 {
00222   myClient->logTracking(false);
00223 }
00224 
00225 void InputHandler::resetTracking()
00226 {
00227   myClient->resetTracking();
00228 }
00229 
00230 
00231 void InputHandler::sendInput(void)
00232 {
00233   /* This method is called by the main function to send a ratioDrive
00234    * request with our current velocity values. If the server does 
00235    * not support the ratioDrive request, then we abort now: */
00236   if(!myClient->dataExists("ratioDrive")) return;
00237 
00238   /* Construct a ratioDrive request packet.  It consists
00239    * of three doubles: translation ratio, rotation ratio, and an overall scaling
00240    * factor. */
00241   ArNetPacket packet;
00242   packet.doubleToBuf(myTransRatio);
00243   packet.doubleToBuf(myRotRatio);
00244   packet.doubleToBuf(50); // use half of the robot's maximum.
00245   if (myPrinting)
00246     printf("Sending\n");
00247   myClient->requestOnce("ratioDrive", &packet);
00248   myTransRatio = 0;
00249   myRotRatio = 0;
00250 }
00251 
00252 
00256 class OutputHandler
00257 {
00258 public:
00259   OutputHandler(ArClientBase *client);
00260   virtual ~OutputHandler(void);
00261   
00263   void handleOutput(ArNetPacket *packet);
00264 
00266   void handleBatteryInfo(ArNetPacket *packet);
00268   void handlePhysicalInfo(ArNetPacket *packet);
00269 
00270 protected:
00271 
00273 
00274   double myX;
00275   double myY;
00276   double myTh;
00277   double myVel;
00278   double myRotVel;
00279   double myVoltage;
00280   char myStatus[256];
00281   char myMode[32];
00283   ArClientBase *myClient;
00284 
00289   ArFunctor1C<OutputHandler, ArNetPacket *> myHandleOutputCB;
00290   ArFunctor1C<OutputHandler, ArNetPacket *> myHandleBatteryInfoCB;
00291   ArFunctor1C<OutputHandler, ArNetPacket *> myHandlePhysicalInfoCB;
00293   
00295   bool myNeedToPrintHeader;
00297   bool myGotBatteryInfo;
00298 };
00299 
00300 OutputHandler::OutputHandler(ArClientBase *client) :
00301   myClient(client),
00302   myHandleOutputCB(this, &OutputHandler::handleOutput),
00303   myHandleBatteryInfoCB(this, &OutputHandler::handleBatteryInfo),
00304   myHandlePhysicalInfoCB(this, &OutputHandler::handlePhysicalInfo),
00305   myNeedToPrintHeader(false),
00306   myGotBatteryInfo(false)
00307 {
00308   /* Add a handler for battery info, and make a single request for it  */
00309   myClient->addHandler("physicalInfo", &myHandlePhysicalInfoCB);
00310   myClient->requestOnce("physicalInfo");
00311 
00312 
00313   /* Add a handler for battery info, and make a single request for it  */
00314   myClient->addHandler("batteryInfo", &myHandleBatteryInfoCB);
00315   myClient->requestOnce("batteryInfo");
00316 
00317   /* Add a handler for general info, and request it to be called every 100 ms */
00318   myClient->addHandler("update", &myHandleOutputCB);
00319   myClient->request("update", 100);
00320 }
00321 
00322 OutputHandler::~OutputHandler(void)
00323 {
00324   /* Halt the request for data updates */
00325   myClient->requestStop("update");
00326 }
00327 
00328 void OutputHandler::handleOutput(ArNetPacket *packet)
00329 {
00330   /* Extract the data from the update packet. Its format is status and
00331    * mode (null-terminated strings), then 6 doubles for battery voltage, 
00332    * x position, y position and orientation (theta) (from odometry), current
00333    * translational velocity, and current rotational velocity. Translation is
00334    * always milimeters, rotation in degrees.
00335    */
00336   memset(myStatus, 0, sizeof(myStatus));
00337   memset(myMode, 0, sizeof(myMode));
00338   packet->bufToStr(myStatus, sizeof(myStatus));
00339   packet->bufToStr(myMode, sizeof(myMode));
00340   myVoltage = ( (double) packet->bufToByte2() )/10.0;
00341   myX = (double) packet->bufToByte4();
00342   myY = (double) packet->bufToByte4();
00343   myTh = (double) packet->bufToByte2();
00344   myVel = (double) packet->bufToByte2();
00345   myRotVel = (double) packet->bufToByte2();
00346 
00347   if(myNeedToPrintHeader) 
00348   {
00349     printf("\n%6s|%6s|%6s|%6s|%6s|%6s|%15s|%20s|\n",
00350       "x","y","theta", "vel", "rotVel", "volts", "mode","status");
00351     fflush(stdout);
00352     myNeedToPrintHeader = false;
00353   }
00354   if (myGotBatteryInfo)
00355     printf("%6.0f|%6.0f|%6.1f|%6.1f|%6.1f|%6.1f|%15s|%20s|\r",
00356            myX, myY, myTh, myVel, myRotVel, myVoltage, myMode, myStatus);
00357   
00358   fflush(stdout);
00359 }
00360 
00361 void OutputHandler::handleBatteryInfo(ArNetPacket *packet)
00362 {
00363   /* Get battery configuration parameters: when the robot will begin beeping and 
00364    * warning about low battery, and when it will automatically disconnect and
00365    * shutdown. */
00366   double lowBattery = packet->bufToDouble();
00367   double shutdown = packet->bufToDouble();
00368   printf("Low battery voltage: %6g       Shutdown battery voltage: %6g\n", lowBattery, shutdown);
00369   fflush(stdout);
00370   myNeedToPrintHeader = true;
00371   myGotBatteryInfo = true;
00372 }
00373 
00374 
00375 void OutputHandler::handlePhysicalInfo(ArNetPacket *packet)
00376 {
00377   /* Get phyiscal configuration parameters: */
00378   char robotType[512];
00379   char robotSubtype[512];
00380   int width;
00381   int lengthFront;
00382   int lengthRear;
00383 
00384   packet->bufToStr(robotType, sizeof(robotType));
00385   packet->bufToStr(robotSubtype, sizeof(robotSubtype));
00386   width = packet->bufToByte2();
00387   lengthFront = packet->bufToByte2();
00388   lengthRear = packet->bufToByte2();
00389 
00390   printf("Type: %s Subtype: %s Width %d: LengthFront: %d LengthRear: %d\n",
00391          robotType, robotSubtype, width, lengthFront, lengthRear);
00392   fflush(stdout);
00393 }
00394 
00395 
00396 /* Key handler for the escape key: shutdown all of Aria. */
00397 void escape(void)
00398 {
00399   printf("esc pressed, shutting down aria\n");
00400   Aria::shutdown();
00401 }
00402 
00403 int main(int argc, char **argv)
00404 {
00405   /* Aria initialization: */
00406   Aria::init();
00407 
00408 
00409 
00410   /* Create our client object. This is the object which connects with a remote
00411    * server over the network, and which manages all of our communication with it
00412    * once connected by sending data "requests".  Requests may be sent once, or
00413    * may be repeated at any frequency. Requests and replies to requsets contain 
00414    * payload "packets", into which various data types may be packed (when making a 
00415    * request), and from which they may also be extracted (when handling a reply). 
00416    * See the InputHandler and OutputHandler classes above for
00417    * examples of making requests and reading/writing the data in packets.
00418    */
00419   ArClientBase client;
00420 
00421   /* Aria components use this to get options off the command line: */
00422   ArArgumentParser parser(&argc, argv);
00423 
00424   /* This will be used to connect our client to the server, including
00425    * various bits of handshaking (e.g. sending a password, retrieving a list
00426    * of data requests and commands...)
00427    * It will get the hostname from the -host command line argument: */
00428   ArClientSimpleConnector clientConnector(&parser);
00429 
00430   parser.loadDefaultArguments();
00431 
00432   /* Check for -help, and unhandled arguments: */
00433   if (!Aria::parseArgs() || !parser.checkHelpAndWarnUnparsed())
00434   {
00435     Aria::logOptions();
00436     exit(0);
00437   }
00438 
00439   
00440   /* Connect our client object to the remote server: */
00441   if (!clientConnector.connectClient(&client))
00442   {
00443     if (client.wasRejected())
00444       printf("Server '%s' rejected connection, exiting\n", client.getHost());
00445     else
00446       printf("Could not connect to server '%s', exiting\n", client.getHost());
00447     exit(1);
00448   } 
00449 
00450   printf("Connected to server.\n");
00451 
00452 
00453   /* Create a key handler and also tell Aria about it */
00454   ArKeyHandler keyHandler;
00455   Aria::setKeyHandler(&keyHandler);
00456 
00457   /* Global escape-key handler to shut everythnig down */
00458   ArGlobalFunctor escapeCB(&escape);
00459   keyHandler.addKeyHandler(ArKeyHandler::ESCAPE, &escapeCB);
00460 
00461   /* Now that we're connected, we can run the client in a background thread, 
00462    * sending requests and receiving replies. When a reply to a request arrives,
00463    * or the server makes a request of the client, a handler functor is invoked. 
00464    * The handlers for this program are registered with the client by the 
00465    * InputHandler and OutputHandler classes (in their constructors, above) */
00466   client.runAsync();
00467 
00468   /* Create the InputHandler object and request safe-drive mode */
00469   InputHandler inputHandler(&client, &keyHandler);
00470   inputHandler.safeDrive();
00471 
00472   /* Use ArClientBase::dataExists() to see if the "ratioDrive" request is available on the 
00473    * currently connected server.  */
00474   if(!client.dataExists("ratioDrive") )
00475       printf("Warning: server does not have ratioDrive command, can not use drive commands!\n");
00476   else
00477     printf("Keys are:\nUP: Forward\nDOWN: Backward\nLEFT: Turn Left\nRIGHT: Turn Right\n");
00478   printf("s: Enable safe drive mode (if supported).\nu: Disable safe drive mode (if supported).\nl: list all data requests on server\n\nDrive commands use 'ratioDrive'.\nt: logs the network tracking tersely\nv: logs the network tracking verbosely\nr: resets the network tracking\n\n");
00479 
00480 
00481   /* Create the OutputHandler object. It will begin printing out data from the
00482    * server. */
00483   OutputHandler outputHandler(&client);
00484 
00485 
00486   /* Begin capturing keys into the key handler. Callbacks will be called
00487    * asyncrosously from this main thread when pressed.  */
00488 
00489   /* While the client is still running (getRunningWithLock locks the "running"
00490    * flag until it returns), check keys on the key handler (which will call
00491    * our callbacks), then tell the input handler to send drive commands. 
00492    * Sleep a fraction of a second as well to avoid using
00493    * too much CPU time, and give other threads time to work.
00494    */
00495   while (client.getRunningWithLock())
00496   {
00497     keyHandler.checkKeys();
00498     inputHandler.sendInput();
00499     ArUtil::sleep(100);
00500   }
00501 
00502   /* The client stopped running, due to disconnection from the server, general
00503    * Aria shutdown, or some other reason. */
00504   Aria::shutdown();
00505   return 0;
00506 }

Generated on Tue Feb 20 10:51:50 2007 for ArNetworking by  doxygen 1.4.0