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