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

ArDataLogger.cpp

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 #include "ariaOSDef.h"
00027 #include "ArExport.h"
00028 #include "ArRobot.h"
00029 #include "ArConfig.h"
00030 #include "ArDataLogger.h"
00031 #include <vector>
00032 
00043 AREXPORT ArDataLogger::ArDataLogger(ArRobot *robot, const char *fileName) :
00044   myAddStringFunctor(this, &ArDataLogger::addString),
00045   myConnectCB(this, &ArDataLogger::connectCallback),
00046   myProcessFileCB(this, &ArDataLogger::processFile),
00047   myUserTaskCB(this, &ArDataLogger::userTask)
00048 {
00049   myRobot = robot;
00050   if (fileName == NULL || fileName[0] == '\0')
00051     myPermanentFileName = "";
00052   else
00053     myPermanentFileName = fileName;
00054   myRobot->addUserTask("DataLogger", 50, &myUserTaskCB);
00055   myRobot->requestIOPackets();
00056   myConfig = false;
00057   myAddToConfigAtConnect = false;
00058   myAddedToConfig = false;
00059   myConfigLogging = false;
00060   myConfigLogInterval = 0;
00061   myConfigFileName[0] = '\0';
00062   myOpenedFileName[0] = '\0';
00063   myAnalogCount = 0;
00064   myAnalogEnabled = NULL;
00065   myAnalogVoltageCount = 0;
00066   myAnalogVoltageEnabled = NULL;
00067   myDigInCount = 0;
00068   myDigInEnabled = NULL;
00069   myDigOutCount = 0;
00070   myDigOutEnabled = NULL;
00071   myStringsCount = 0;
00072   //myStringsEnabled = NULL;
00073 
00074   myLogVoltage = false;
00075   myLogLeftVel = false;
00076   myLogRightVel = false;
00077   myLogTransVel = false;
00078   myLogRotVel = false;
00079   myLogLeftStalled = false;
00080   myLogRightStalled = false;
00081   myLogStallBits = false;
00082   myLogFlags = false;
00083   myLogPose = false;
00084   myLogEncoderPose = false;
00085   myLogCorrectedEncoderPose = false;
00086   myLogEncoders = false;
00087 
00088   myFile = NULL;
00089 }
00090 
00091 AREXPORT ArDataLogger::~ArDataLogger(void)
00092 {
00093 
00094 }
00095 
00096 AREXPORT void ArDataLogger::addToConfig(ArConfig *config)
00097 {
00098   if (config == NULL || myAddedToConfig)
00099     return;
00100   myConfig = config;
00101   if (!myRobot->isConnected())
00102   {
00103     myAddToConfigAtConnect = true;
00104     myRobot->addConnectCB(&myConnectCB);
00105     return;
00106   }
00107   else
00108   {
00109     connectCallback();
00110   }
00111 
00112   myAddedToConfig = true;
00113   ArLog::log(ArLog::Verbose, "ArDataLogger::addToConfig");
00114   std::string section;
00115   char name[512];
00116   char desc[512];
00117   int i;
00118   section = "Data logging";
00119   // add everything to the config
00120   myConfig->addParam(
00121       ArConfigArg("DataLog", &myConfigLogging, "True to log data, false not to"),
00122       section.c_str(), ArPriority::NORMAL);
00123 
00124   myConfig->addParam(
00125       ArConfigArg("DataLogInterval", &myConfigLogInterval, "Seconds between logs", 0),
00126       section.c_str(), ArPriority::NORMAL);
00127 
00128   if (myPermanentFileName.size() == 0)
00129     myConfig->addParam(
00130         ArConfigArg("DataLogFileName", myConfigFileName, 
00131             "File to log data into", sizeof(myConfigFileName)),
00132         section.c_str(), ArPriority::NORMAL);
00133   
00134   for (i = 0; i < myStringsCount; i++)
00135   {
00136     snprintf(name, sizeof(name), "DataLog%s", myStrings[i]->getName());
00137     snprintf(desc, sizeof(desc), "Logs %s", myStrings[i]->getName());
00138     myConfig->addParam(
00139         ArConfigArg(name, myStringsEnabled[i], desc),
00140         "Custom data logging", ArPriority::NORMAL);
00141   }
00142 
00143   myConfig->addParam(
00144       ArConfigArg("DataLogBatteryVoltage", &myLogVoltage, "True to log battery voltage"),
00145       section.c_str(), ArPriority::DETAILED);
00146   myConfig->addParam(
00147       ArConfigArg("DataLogPose", &myLogPose, "True to log robot's pose"),
00148       section.c_str(), ArPriority::NORMAL);
00149   myConfig->addParam(
00150       ArConfigArg("DataLogEncoderPose", &myLogEncoderPose, "True to log robot's raw encoder pose"),
00151       section.c_str(), ArPriority::DETAILED);
00152   myConfig->addParam(
00153       ArConfigArg("DataLogCorrectedEncoderPose", &myLogCorrectedEncoderPose, "True to log robot's corrected (by gyro, etc) encoder pose"),
00154       section.c_str(), ArPriority::DETAILED);
00155   myConfig->addParam(
00156       ArConfigArg("DataLogEncoders", &myLogEncoders, "True to log the raw encoder readings"),
00157       section.c_str(), ArPriority::DETAILED);
00158   myConfig->addParam(
00159       ArConfigArg("DataLogLeftVel", &myLogLeftVel, "True to log left wheel velocity"),
00160       section.c_str(), ArPriority::DETAILED);
00161   myConfig->addParam(
00162       ArConfigArg("DataLogRightVel", &myLogRightVel, "True to log right wheel velocity"),
00163       section.c_str(), ArPriority::DETAILED);
00164   myConfig->addParam(
00165       ArConfigArg("DataLogTransVel", &myLogTransVel, "True to log translational wheel velocity"),
00166       section.c_str(), ArPriority::DETAILED);
00167   myConfig->addParam(
00168       ArConfigArg("DataLogRotVel", &myLogRotVel, "True to log rotational wheel velocity"),
00169       section.c_str(), ArPriority::DETAILED);
00170   myConfig->addParam(
00171       ArConfigArg("DataLogLeftStalled", &myLogLeftStalled, "True to log if the left wheel is stalled"),
00172       section.c_str(), ArPriority::DETAILED);
00173   myConfig->addParam(
00174       ArConfigArg("DataLogRightStalled", &myLogRightStalled, "True to log if the right wheel is stalled"),
00175       section.c_str(), ArPriority::DETAILED);
00176   myConfig->addParam(
00177       ArConfigArg("DataLogStallBits", &myLogStallBits, "True to log all the stall bits is stalled"),
00178       section.c_str(), ArPriority::DETAILED);
00179   myConfig->addParam(
00180       ArConfigArg("DataLogFlags", &myLogFlags, "True to log all the flags"),
00181       section.c_str(), ArPriority::DETAILED);
00182 
00183   for (i = 0; i < myAnalogCount; i++)
00184   {
00185     snprintf(name, sizeof(name), "DataLogAnalog%d", i);
00186     snprintf(desc, sizeof(desc), 
00187          "Logs the value of analog %d as a 10 bit (0-1024) value",
00188          i);
00189     myConfig->addParam(
00190         ArConfigArg(name, &myAnalogEnabled[i], desc),
00191         section.c_str(), ArPriority::DETAILED);
00192   }
00193   for (i = 0; i < myAnalogVoltageCount; i++)
00194   {
00195     snprintf(name, sizeof(name), "DataLogAnalogVoltage%d", i);
00196     snprintf(desc, sizeof(desc), 
00197          "Logs the value of analog %d as voltage from 0 to 5",
00198          i);
00199     myConfig->addParam(
00200         ArConfigArg(name, &myAnalogVoltageEnabled[i], desc),
00201         section.c_str(), ArPriority::DETAILED);
00202   }
00203   for (i = 0; i < myDigInCount; i++)
00204   {
00205     snprintf(name, sizeof(name), "DataLogDigIn%d", i);
00206     snprintf(desc, sizeof(desc), "Logs digital in %d", i);
00207     myConfig->addParam(
00208         ArConfigArg(name, &myDigInEnabled[i], desc),
00209         section.c_str(), ArPriority::DETAILED);
00210   }
00211   for (i = 0; i < myDigOutCount; i++)
00212   {
00213     snprintf(name, sizeof(name), "DataLogDigOut%d", i);
00214     snprintf(desc, sizeof(desc), "Logs digital out %d", i);
00215     myConfig->addParam(
00216         ArConfigArg(name, &myDigOutEnabled[i], desc),
00217         section.c_str(), ArPriority::DETAILED);
00218   }
00219   myProcessFileCB.setName("ArDataLogger");
00220   myConfig->addProcessFileWithErrorCB(&myProcessFileCB, 100);
00221 }
00222 
00223 AREXPORT void ArDataLogger::connectCallback(void)
00224 {
00225   int i;
00226   ArLog::log(ArLog::Verbose, "ArDataLogger::connectCallback");
00227   // out with the old memory
00228   if (myAnalogEnabled != NULL)
00229   {
00230     delete myAnalogEnabled;
00231     myAnalogEnabled = NULL;
00232   }
00233   if (myAnalogVoltageEnabled != NULL)
00234   {
00235     delete myAnalogVoltageEnabled;
00236     myAnalogVoltageEnabled = NULL;
00237   }
00238   if (myDigInEnabled != NULL)
00239   {
00240     delete myDigInEnabled;
00241     myDigInEnabled = NULL;
00242   }
00243   if (myDigOutEnabled != NULL)
00244   {
00245     delete myDigOutEnabled;
00246     myDigOutEnabled = NULL;
00247   }
00248   /*
00249   if (myStringsEnabled != NULL)
00250   {
00251     delete myStringsEnabled;
00252     myStringsEnabled = NULL;
00253   }
00254   */
00255   // see how much memory we need
00256   myAnalogCount = myRobot->getIOAnalogSize();
00257   myAnalogVoltageCount = myRobot->getIOAnalogSize();
00258   myDigInCount = myRobot->getIODigInSize();
00259   myDigOutCount = myRobot->getIODigOutSize();
00260   //myStringsCount = myStrings.size();
00261   // make and initialize the new memory
00262   if (myAnalogCount > 0)
00263   {
00264     myAnalogEnabled = new bool[myAnalogCount];
00265     for (i = 0; i < myAnalogCount; i++)
00266     {
00267       myAnalogEnabled[i] = false;
00268     }
00269   }
00270   if (myAnalogVoltageCount > 0)
00271   {
00272     myAnalogVoltageEnabled = new bool[myAnalogVoltageCount];
00273     for (i = 0; i < myAnalogVoltageCount; i++)
00274       myAnalogVoltageEnabled[i] = false;
00275   }
00276   if (myDigInCount > 0)
00277   {
00278     myDigInEnabled = new bool[myDigInCount];
00279     for (i = 0; i < myDigInCount; i++)
00280       myDigInEnabled[i] = false;
00281   }
00282   if (myDigOutCount > 0)
00283   {
00284     myDigOutEnabled = new bool[myDigOutCount];
00285     for (i = 0; i < myDigOutCount; i++)
00286       myDigOutEnabled[i] = false;
00287   }
00288   /* Taking this out since we do it with a map now.
00289     if (myStringsCount > 0)
00290   {
00291     myStringsEnabled = new bool[myStringsCount];
00292     for (i = 0; i < myStringsCount; i++)
00293       myStringsEnabled[i] = false;
00294       }*/
00295   if (myAddToConfigAtConnect && !myAddedToConfig)
00296   {
00297     myAddToConfigAtConnect = false;
00298     addToConfig(myConfig);
00299   }
00300 }
00301 
00302 AREXPORT bool ArDataLogger::processFile(char *errorBuffer, 
00303                     size_t errorBufferLen)
00304 {
00305   myMutex.lock();
00306   // if our file name is different and we're not using a permanent
00307   // file name or if we're disabled close the old one
00308   if ((strcmp(myOpenedFileName, myConfigFileName) != 0 && myFile != NULL && 
00309        myPermanentFileName.size() == 0) ||
00310        (myFile != NULL && !myConfigLogging))
00311   {
00312     ArLog::log(ArLog::Normal, "Closed data log file '%s'", myOpenedFileName);
00313     fclose(myFile);
00314     myFile = NULL;
00315   }
00316   // try to open the file
00317   if (myConfigLogging && myFile == NULL)
00318   {
00319     if (myPermanentFileName.size() == 0  && strlen(myConfigFileName) == 0)
00320     {
00321       ArLog::log(ArLog::Verbose, "ArDataLogger: no log file to open");
00322       myMutex.unlock();
00323       return true;
00324     }
00325     std::string fileName;
00326     if (myPermanentFileName.size() > 0)
00327     {
00328       if ((myFile = fopen(myPermanentFileName.c_str(), "a")) != NULL)
00329       {
00330     ArLog::log(ArLog::Normal, "Opened data log file '%s'", 
00331            myPermanentFileName.c_str());
00332       }
00333       else
00334       {
00335     ArLog::log(ArLog::Normal, "Could not open data log file '%s'", 
00336            myPermanentFileName.c_str());
00337     myMutex.unlock();
00338     return true;
00339       }
00340     }
00341     else
00342     {
00343       // if we couldn't open it fail
00344       if ((myFile = fopen(myConfigFileName, "w")) != NULL)
00345       {
00346     strcpy(myOpenedFileName, myConfigFileName);
00347     ArLog::log(ArLog::Normal, "Opened data log file '%s'", 
00348            myOpenedFileName);
00349       }
00350       else
00351       {
00352     ArLog::log(ArLog::Normal, "Could not open data log file '%s'", 
00353            myConfigFileName);
00354     myMutex.unlock();
00355     if (errorBuffer != NULL)
00356       snprintf(errorBuffer, errorBufferLen, "DataLogFileName of '%s' cannot be opened", myConfigFileName);
00357     return false;
00358       }
00359     }
00360   }
00361   else if (!myConfigLogging)
00362   {
00363     myMutex.unlock();
00364     return true;
00365   }
00366   int i;
00367   // if we could then dump in the header
00368   fprintf(myFile, ";%12s", "Time");
00369   std::map<std::string, bool *, ArStrCaseCmpOp>::iterator it;
00370   for (i = 0; i < myStringsCount; i++)
00371   {
00372     if (*(myStringsEnabled[i]))
00373     {
00374       char formatBuf[64];
00375       sprintf(formatBuf, "\t%%0%ds", myStrings[i]->getMaxLength());
00376       fprintf(myFile, formatBuf, myStrings[i]->getName());
00377     }
00378   }
00379   if (myLogVoltage)
00380     fprintf(myFile, "\tVolt");
00381   if (myLogPose)
00382     fprintf(myFile, "\t%010s\t%010s\t%010s", "X", "Y", "Th");
00383   if (myLogEncoderPose)
00384     fprintf(myFile, "\t%010s\t%010s\t%010s", "encX", "encY", "encTh");
00385   if (myLogCorrectedEncoderPose)
00386     fprintf(myFile, "\t%010s\t%010s\t%010s", 
00387         "corrEncX", "corrEncY", "corrEncTh");
00388   if (myLogEncoders)
00389   {
00390     fprintf(myFile, "\t%010s\t%010s", "encL", "encR");
00391     myRobot->requestEncoderPackets();
00392   }
00393   if (myLogLeftVel)
00394     fprintf(myFile, "\tLeftV");
00395   if (myLogRightVel)
00396     fprintf(myFile, "\tRightV");
00397   if (myLogTransVel)
00398     fprintf(myFile, "\tTransV");
00399   if (myLogRotVel)
00400     fprintf(myFile, "\tRotV");
00401   if (myLogLeftStalled)
00402     fprintf(myFile, "\tLStall");
00403   if (myLogRightStalled)
00404     fprintf(myFile, "\tRStall");
00405   if (myLogStallBits)
00406     fprintf(myFile, "\tStllBts%16s", "");
00407   if (myLogFlags)
00408     fprintf(myFile, "\tFlags%16s", "");
00409   for (i = 0; i < myAnalogCount; i++)
00410   {
00411     if (myAnalogEnabled[i])
00412       fprintf(myFile, "\tAn%d", i);
00413   }
00414   for (i = 0; i < myAnalogVoltageCount; i++)
00415   {
00416     if (myAnalogVoltageEnabled[i])
00417       fprintf(myFile, "\tAnV%d", i);
00418   }
00419   for (i = 0; i < myDigInCount; i++)
00420   {
00421     if (myDigInEnabled[i])
00422       fprintf(myFile, "\tDigIn%d%8s", i, "");
00423   }
00424   for (i = 0; i < myDigOutCount; i++)
00425   {
00426     if (myDigOutEnabled[i])
00427       fprintf(myFile, "\tDigOut%d%8s", i, "");
00428   }
00429   fprintf(myFile, "\n");
00430   fflush(myFile);
00431   myMutex.unlock();
00432   return true;
00433 }
00434 
00435 AREXPORT void ArDataLogger::userTask(void)
00436 {
00437   myMutex.lock();
00438   // if we don't need to do anything just return
00439   if (myFile == NULL || myLastLogged.secSince() < myConfigLogInterval)
00440   {
00441     myMutex.unlock();
00442     return;
00443   }
00444   int i;
00445   int j;
00446   int val;
00447 
00448   fprintf(myFile, "%ld", time(NULL));
00449 
00450   char *buf;
00451   buf = new char[myMaxMaxLength];
00452   ArStringInfoHolder *infoHolder;
00453   for (i = 0; i < myStringsCount; i++)
00454   {
00455     if (*(myStringsEnabled[i]))
00456     {
00457       char formatBuf[64];
00458       infoHolder = myStrings[i];
00459       sprintf(formatBuf, "\t%%0%ds", myStrings[i]->getMaxLength());
00460       infoHolder->getFunctor()->invoke(buf, infoHolder->getMaxLength());
00461       fprintf(myFile, formatBuf, buf);
00462     }
00463   }
00464   delete buf;
00465 
00466   if (myLogVoltage)
00467     fprintf(myFile, "\t%.2f", myRobot->getRealBatteryVoltageNow());
00468   if (myLogPose)
00469     fprintf(myFile, "\t%10.0f\t%10.0f\t%10.0f", myRobot->getX(), 
00470         myRobot->getY(), myRobot->getTh());
00471   if (myLogEncoderPose)
00472     fprintf(myFile, "\t%10.0f\t%10.0f\t%10.0f",
00473         myRobot->getRawEncoderPose().getX(), 
00474         myRobot->getRawEncoderPose().getY(), 
00475         myRobot->getRawEncoderPose().getTh());
00476   if (myLogCorrectedEncoderPose)
00477     fprintf(myFile, "\t%10.0f\t%10.0f\t%10.0f", 
00478         myRobot->getEncoderPose().getX(), 
00479         myRobot->getEncoderPose().getY(), 
00480         myRobot->getEncoderPose().getTh());
00481   if (myLogEncoders)
00482     fprintf(myFile, "\t%10d\t%10d", 
00483         myRobot->getLeftEncoder(), myRobot->getRightEncoder());
00484   if (myLogLeftVel)
00485     fprintf(myFile, "\t%.0f", myRobot->getLeftVel());
00486   if (myLogRightVel)
00487     fprintf(myFile, "\t%.0f", myRobot->getRightVel());
00488   if (myLogTransVel)
00489     fprintf(myFile, "\t%.0f", myRobot->getVel());
00490   if (myLogRotVel)
00491     fprintf(myFile, "\t%.0f", myRobot->getRotVel());
00492   if (myLogLeftStalled)
00493     fprintf(myFile, "\t%d", (bool)myRobot->isLeftMotorStalled());
00494   if (myLogRightStalled)
00495     fprintf(myFile, "\t%d", (bool)myRobot->isRightMotorStalled());
00496   if (myLogStallBits)
00497   {
00498     fprintf(myFile, "\t");
00499     for (i = 0, val = 1; i < 16; i++, val *= 2)
00500       fprintf(myFile, "%d", (bool)(myRobot->getStallValue() & val));
00501   }
00502   if (myLogFlags)
00503   {
00504     fprintf(myFile, "\t");
00505     for (i = 0, val = 1; i < 16; i++, val *= 2)
00506       fprintf(myFile, "%d", (bool)(myRobot->getFlags() & val));
00507   }
00508   for (i = 0; i < myAnalogCount; i++)
00509   {
00510     if (myAnalogEnabled[i])
00511       fprintf(myFile, "\t%d", myRobot->getIOAnalog(i));
00512   }
00513   for (i = 0; i < myAnalogVoltageCount; i++)
00514   {
00515     if (myAnalogVoltageEnabled[i])
00516       fprintf(myFile, "\t%.2f", myRobot->getIOAnalogVoltage(i));
00517   }
00518   for (i = 0; i < myDigInCount; i++)
00519   {
00520     if (myDigInEnabled[i])
00521     {
00522       fprintf(myFile, "\t");
00523       for (j = 0, val = 1; j < 8; j++, val *= 2)
00524     fprintf(myFile, "%d", (bool)(myRobot->getIODigIn(i) & val));
00525     }
00526   }
00527   for (i = 0; i < myDigOutCount; i++)
00528   {
00529     if (myDigOutEnabled[i])
00530     {
00531       fprintf(myFile, "\t");
00532       for (j = 0, val = 1; j < 8; j++, val *= 2)
00533     fprintf(myFile, "%d", (bool)(myRobot->getIODigOut(i) & val));
00534     }
00535   }
00536   fprintf(myFile, "\n");
00537   fflush(myFile);
00538   myLastLogged.setToNow();
00539   myMutex.unlock();
00540 }
00541 
00542 
00543 AREXPORT void ArDataLogger::addString(
00544     const char *name, ArTypes::UByte2 maxLength,
00545     ArFunctor2<char *, ArTypes::UByte2> *functor)
00546 {
00547   ArTypes::UByte2 len;
00548 
00549   myMutex.lock();
00550   if (myMaxMaxLength < maxLength)
00551     myMaxMaxLength = maxLength;
00552   if (maxLength < strlen(name))
00553     len = strlen(name);
00554   else
00555     len = maxLength;  
00556   myStrings.push_back(new ArStringInfoHolder(name, len, functor));
00557   bool *boolPtr;
00558   boolPtr = new bool;
00559   // if we've added to config we default to true
00560   if (myAddedToConfig)
00561     *boolPtr = true;
00562   else
00563     *boolPtr = false;
00564   myStringsEnabled.push_back(boolPtr);
00565   myStringsCount++;
00566   myMutex.unlock();
00567   if (myAddedToConfig)
00568     processFile(NULL, 0);
00569 }
00570 

Generated on Tue Feb 20 10:51:39 2007 for Aria by  doxygen 1.4.0