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 #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
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
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
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
00250
00251
00252
00253
00254
00255
00256 myAnalogCount = myRobot->getIOAnalogSize();
00257 myAnalogVoltageCount = myRobot->getIOAnalogSize();
00258 myDigInCount = myRobot->getIODigInSize();
00259 myDigOutCount = myRobot->getIODigOutSize();
00260
00261
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
00289
00290
00291
00292
00293
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
00307
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
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
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
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
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
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