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

ArDataLogger.cpp

00001 /*
00002 ActivMedia Robotics Interface for Applications (ARIA)
00003 Copyright (C) 2004,2005 ActivMedia Robotics, LLC
00004 
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 ActivMedia Robotics for information about a commercial version of ARIA at 
00022 robots@activmedia.com or 
00023 ActivMedia Robotics, 19 Columbia Drive, Amherst, NH 03031; 800-639-9481
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   // add everything to the config
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   // out with the old memory
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   // see how much memory we need
00253   myAnalogCount = myRobot->getIOAnalogSize();
00254   myAnalogVoltageCount = myRobot->getIOAnalogSize();
00255   myDigInCount = myRobot->getIODigInSize();
00256   myDigOutCount = myRobot->getIODigOutSize();
00257   myStringsCount = myStrings.size();
00258   // make and initialize the new memory
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   // if our file name is different and we're not using a permanent
00303   // file name or if we're disabled close the old one
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   // try to open the file
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       // if we couldn't open it fail
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   // if we could then dump in the header
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   // if we don't need to do anything just return
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 }

Generated on Wed Oct 19 12:56:34 2005 for Aria by  doxygen 1.4.0