InputLogger.cpp

Go to the documentation of this file.
00001 /*************** <auto-copyright.pl BEGIN do not edit this line> **************
00002  *
00003  * VR Juggler is (C) Copyright 1998-2005 by Iowa State University
00004  *
00005  * Original Authors:
00006  *   Allen Bierbaum, Christopher Just,
00007  *   Patrick Hartling, Kevin Meinert,
00008  *   Carolina Cruz-Neira, Albert Baker
00009  *
00010  * This library is free software; you can redistribute it and/or
00011  * modify it under the terms of the GNU Library General Public
00012  * License as published by the Free Software Foundation; either
00013  * version 2 of the License, or (at your option) any later version.
00014  *
00015  * This library is distributed in the hope that it will be useful,
00016  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00017  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00018  * Library General Public License for more details.
00019  *
00020  * You should have received a copy of the GNU Library General Public
00021  * License along with this library; if not, write to the
00022  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
00023  * Boston, MA 02111-1307, USA.
00024  *
00025  * -----------------------------------------------------------------
00026  * File:          $RCSfile$
00027  * Date modified: $Date: 2005-05-16 12:59:11 -0500 (Mon, 16 May 2005) $
00028  * Version:       $Revision: 17486 $
00029  * -----------------------------------------------------------------
00030  *
00031  *************** <auto-copyright.pl END do not edit this line> ***************/
00032 
00033 #include <gadget/gadgetConfig.h>
00034 
00035 #include <fstream>
00036 #include <stdio.h>
00037 #include <boost/progress.hpp>
00038 #include <cppdom/cppdom.h>
00039 
00040 #include <vpr/vpr.h>
00041 #include <vpr/IO/XMLObjectWriter.h>
00042 #include <vpr/IO/XMLObjectReader.h>
00043 #include <vpr/System.h>
00044 #include <jccl/Config/ConfigElement.h>
00045 
00046 #include <gadget/InputLogger.h>
00047 
00048 /*
00049 
00050 --- Input logger format ---
00051 The logger format is made up of a list of samples.
00052 For each sample there is a list of devices that got sampled.
00053 
00054 Each of these devices has an individual element with that device name
00055 and then the contents are an XML serialized version of that device
00056 and it's data (this includes the buffer).
00057 
00058 There may be logger_stamp's intermixed between logger_samples at any point.
00059 The stamps only become valid after a logger sample (ie. they stamp the
00060 previous sample).
00061 
00062 Note: I could probably write a DTD for this, but I haven't taken the time
00063 to do this yet. :(
00064 --------------------------
00065 
00066 <gadget_logger>
00067 
00068    <sample>
00069       <device dev_name="device name">
00070         XML serialized version of that device
00071          <devroot...>
00072          </devroot...>
00073       </device>
00074       * <device>
00075    </sample>
00076    <stamp id="log_name"/>   // Not needed after all logger_sample's
00077 
00078    <sample>
00079    ...
00080 
00081 </gadget_logger>
00082 */
00083 
00084 namespace gadget
00085 {
00086 
00088 bool InputLogger::config( jccl::ConfigElementPtr element)
00089 {
00090    std::string start_name = element->getProperty<std::string>("start_digital");
00091    std::string stamp_name = element->getProperty<std::string>("stamp_digital");
00092 
00093    mStartStopButton.init(start_name);
00094    mStampButton.init(stamp_name);
00095 
00096    int max_frame_rate = element->getProperty<int>("max_framerate");
00097    mCompressFactor = element->getProperty<unsigned>("compress_factor");
00098 
00099    // Get ignore attribs and elems for compressing
00100    unsigned num_ignore_elems, num_ignore_attribs;
00101    num_ignore_elems = element->getNum("ignore_elems");
00102    num_ignore_attribs = element->getNum("ignore_attribs");
00103 
00104    for(unsigned i=0;i<num_ignore_elems;i++)
00105    { mIgnoreElems.push_back(element->getProperty<std::string>("ignore_elems",i)); }
00106 
00107    for(unsigned i=0;i<num_ignore_attribs;i++)
00108    { mIgnoreAttribs.push_back(element->getProperty<std::string>("ignore_attribs",i)); }
00109 
00110    if(max_frame_rate > 0)   // If we are supposed to limit frame rate
00111    {
00112       mLimitFrameRate = true;
00113 
00114       mPrevFrameTimestamp.setNow();             // Initialize value
00115       mMinFrameTime.set( (1000/max_frame_rate), vpr::Interval::Msec);
00116    }
00117 
00118    vprDEBUG(gadgetDBG_INPUT_MGR, vprDBG_CONFIG_LVL) << "\n--- LOGGER: Configured ---\n" << vprDEBUG_FLUSH;
00119    vprDEBUG(gadgetDBG_INPUT_MGR, vprDBG_CONFIG_LVL) << "StartStop: " << start_name << std::endl
00120                                                      << "Stamp: " << stamp_name << std::endl
00121                                                      << "Max framerate: " << max_frame_rate << std::endl
00122                                                      << "Min frametime: " << mMinFrameTime.msec() << "ms" << std::endl << vprDEBUG_FLUSH;
00123 
00124    return true;
00125 }
00126 
00142 void InputLogger::process()
00143 {
00144    // Get state of control keys
00145    // Note, because of how this is called this sees the state one frame late
00146    // ie. If toggle on is true here it is NOT true in the data being saved
00147    const bool start_stop_triggered(mStartStopButton->getData() == Digital::TOGGLE_ON);
00148    const bool stamp_triggered(mStampButton->getData() == Digital::TOGGLE_ON);
00149 
00150    if(0 == mSleepFramesLeft)
00151    {
00152       // Check for control keys
00153       if(Playing != mCurState)
00154       {
00155          if(start_stop_triggered)
00156          {
00157             if(Recording == mCurState)
00158             {  stopRecording(); }
00159             else
00160             {  startRecording(); }
00161          }
00162          else if(stamp_triggered && (Recording == mCurState))
00163          {  stampRecord(); }
00164       }
00165 
00166       if(Recording == mCurState)
00167       {
00168          addRecordingSample();
00169 
00170          if(mLimitFrameRate)
00171          {  limitFramerate(); }
00172       }
00173       else if(Playing == mCurState)
00174       {
00175          playNextSample();
00176       }
00177    }
00178    else
00179    {
00180       vprDEBUG(vprDBG_ALL, vprDBG_CONFIG_LVL) << "InputLogger: Sleeping: " << mSleepFramesLeft << std::endl << vprDEBUG_FLUSH;
00181       mSleepFramesLeft--;
00182    }
00183 }
00184 
00185 /* Starting the recording and initialize all necessary stuff
00186 *
00187 * - Get the output filename
00188 * - Initialize root node
00189 * - Set to recording state
00190 */
00191 void InputLogger::startRecording()
00192 {
00193    vprDEBUG(gadgetDBG_INPUT_MGR, vprDBG_STATE_LVL) << "\n--- LOGGER: startRecording ---\n" << vprDEBUG_FLUSH;
00194 
00195    // -- Get recording filename
00196    std::cout << "/n/n------- LOGGER ------\nEnter log filename:" << std::flush;
00197    std::string file_name;
00198    //std::string file_name("test_logging.xml");
00199    std::cin >> file_name;
00200    std::cout << "\nUsing file: " << file_name << std::endl;
00201 
00202    mRecordingFilename = file_name;
00203 
00204    // -- Init recording
00205    cppdom::ContextPtr ctx( new cppdom::Context );
00206    mRootNode = cppdom::NodePtr(new cppdom::Node("gadget_logger", ctx ));
00207 
00208    mCurState = Recording;
00209 }
00210 
00211 void InputLogger::stopRecording()
00212 {
00213    vprDEBUG(gadgetDBG_INPUT_MGR, vprDBG_STATE_LVL) << "\n--- LOGGER: stopRecording ---\n" << vprDEBUG_FLUSH;
00214    vprDEBUG(gadgetDBG_INPUT_MGR, vprDBG_STATE_LVL) << "Done recording\n"
00215                                     << "Saving data to: " << mRecordingFilename << std::endl << vprDEBUG_FLUSH;
00216 
00217    // Compress the data before saving
00218    std::cout << "Before compressing: size:" << mRootNode->getChildren().size() << std::endl;
00219    compressSamples();
00220    std::cout << "After compressing: size:" << mRootNode->getChildren().size() << std::endl;
00221 
00222    std::ofstream out_file;
00223    out_file.exceptions(std::ofstream::badbit | std::ofstream::failbit);
00224    try
00225    {
00226       out_file.open(mRecordingFilename.c_str());
00227       mRootNode->save(out_file);
00228       out_file.flush();
00229       out_file.close();
00230    }
00231 #if defined(__GNUC__) && __GNUC__ == 2 && __GNUC_MINOR__ == 96
00232    catch(...)
00233    {
00234       std::cerr << "Unknown error saving file." << std::endl;
00235       if(out_file.is_open())
00236       {  out_file.close(); }
00237    }
00238 #else
00239    catch(std::ofstream::failure& se)
00240    {
00241       std::cerr << "IOS failure saving file: desc:" << se.what() << std::endl;
00242       if(out_file.is_open())
00243       {  out_file.close(); }
00244    }
00245    catch(...)
00246    {
00247       std::cerr << "Unknown error saving file." << std::endl;
00248    }
00249 #endif
00250 
00251    mCurState = Inactive;
00252    mSleepFramesLeft = 10;     // Wait 10 frames until we start processing anything again
00253 }
00254 
00255 void InputLogger::stampRecord()
00256 {
00257    vprASSERT(Recording == mCurState && "Tried to stamp input while not recording");
00258 
00259    // -- Get recording filename
00260    char tag_name[1024];
00261    std::cout << "/n/n------- LOGGER ------\nEnter stamp id:" << std::flush;
00262    std::string stamp_id;
00263    //std::cin >> stamp_id;
00264 #if defined(_MSC_VER) && _MSC_VER >= 1400
00265    scanf_s("%s", tag_name);
00266 #else
00267    scanf("%s", tag_name);
00268 #endif
00269    stamp_id = std::string(tag_name);
00270    std::cout << "\nStamping with: " << stamp_id << std::endl;
00271 
00272    cppdom::NodePtr stamp_node(new cppdom::Node("stamp", mRootNode->getContext()));
00273    stamp_node->setAttribute("id", stamp_id);
00274 
00275    mRootNode->addChild(stamp_node);
00276 }
00277 
00282 void InputLogger::load(std::string logFilename)
00283 {
00284    vprASSERT(Inactive == mCurState && "Tried to load a file while logger is active");
00285 
00286    std::cout << "InputLogger: Loading file: " << logFilename << std::endl;
00287 
00288    // Create new root to read into
00289    cppdom::ContextPtr ctx( new cppdom::Context );
00290    mRootNode = cppdom::NodePtr(new cppdom::Node("not_set", ctx ));
00291 
00292    std::ifstream in_file;
00293    in_file.exceptions(std::ifstream::badbit | std::ifstream::failbit);
00294    try
00295    {
00296       in_file.open(logFilename.c_str(), std::ios::in);
00297       mRootNode->load(in_file, ctx);
00298       in_file.close();
00299    }
00300 #if defined(__GNUC__) && __GNUC__ == 2 && __GNUC_MINOR__ == 96
00301    catch(...)
00302    {
00303       std::cerr << "Unknown error loading file." << std::endl;
00304       if(in_file.is_open())
00305       {  in_file.close(); }
00306    }
00307 #else
00308    catch(std::ifstream::failure& se)
00309    {
00310       std::cerr << "IOS failure saving file: desc:" << se.what() << std::endl;
00311       if(in_file.is_open())
00312       {  in_file.close(); }
00313    }
00314    catch(...)
00315    {
00316       std::cerr << "Unknown error loading file." << std::endl;
00317    }
00318 #endif
00319 
00320    vprDEBUG(vprDBG_ALL, vprDBG_STATE_LVL) << "InputLogger: Loaded file: num_samples:" << mRootNode->getChildren().size() << std::endl << vprDEBUG_FLUSH;
00321 }
00322 
00323 /* Start playing
00324 *
00325 * - Set state to playing
00326 * - Find the first node is list
00327 * - Set the start and end nodes
00328 * - Clear the stamp
00329 */
00330 void InputLogger::play()
00331 {
00332    vprASSERT(mCurState == Inactive);
00333 
00334    if(mRootNode.get() == NULL)
00335    {
00336       vprDEBUG(vprDBG_ALL, vprDBG_WARNING_LVL) << "Logger::play: Null root node, so can't play.\n" << vprDEBUG_FLUSH;
00337       return;
00338    }
00339 
00340    mNextSample_i = mRootNode->getChildren().begin();
00341    mEndSample_i = mRootNode->getChildren().end();
00342    mActiveStamp.clear();
00343 
00344    if(mNextSample_i == mEndSample_i)
00345    {
00346       vprDEBUG(vprDBG_ALL, vprDBG_WARNING_LVL) << "Logger::play: Zero children, so can't play.\n" << vprDEBUG_FLUSH;
00347       return;
00348    }
00349 
00350    mCurState = Playing;
00351 }
00352 
00354 void InputLogger::stop()
00355 {;
00356 }
00357 
00359 void InputLogger::pause()
00360 {;
00361 }
00362 
00366 std::string InputLogger::getStamp()
00367 {
00368    return mActiveStamp;
00369 }
00370 
00371 
00382 void InputLogger::addRecordingSample()
00383 {
00384    vprDEBUG(gadgetDBG_INPUT_MGR, vprDBG_STATE_LVL) << "LOGGER: adding sample.\n" << vprDEBUG_FLUSH;
00385 
00386    try
00387    {
00388       gadget::InputManager* input_mgr = gadget::InputManager::instance();
00389 
00390       cppdom::NodePtr sample_node(new cppdom::Node("sample", mRootNode->getContext()));
00391 
00392       // For each device
00393       for(InputManager::tDevTableType::iterator dev_i=input_mgr->mDevTable.begin();
00394           dev_i != input_mgr->mDevTable.end(); ++dev_i)
00395       {
00396          gadget::Input* cur_dev = (*dev_i).second;
00397          std::string dev_name = cur_dev->getInstanceName();
00398 
00399          cppdom::NodePtr dev_node(new cppdom::Node("device", mRootNode->getContext()));
00400          dev_node->setAttribute("dev_name", dev_name);
00401 
00402          vpr::XMLObjectWriter xml_writer;       // Create writer for the device
00403          cur_dev->writeObject(&xml_writer);
00404          cppdom::NodePtr serialized_dev_node = xml_writer.getRootNode();
00405          vprASSERT(serialized_dev_node.get() != NULL);
00406 
00407          dev_node->addChild(serialized_dev_node);  // Add the child node
00408          sample_node->addChild(dev_node);          // Add dev node to the sample
00409       }
00410 
00411       mRootNode->addChild(sample_node);
00412    }
00413    catch (cppdom::Error& ce)
00414    {
00415       std::cerr << "Cppdom Error [InputLogger::addRecordingSample]: " << ce.getString() << std::endl;
00416       std::cerr << "Info: " << ce.getInfo() << std::endl;
00417       vprASSERT(false);
00418    }
00419 }
00420 
00421 /*
00422 * - Get the node for the next sample
00423 * - For each device element
00424 *    - Get the device name
00425 *    - Get handle to that device from the input manager
00426 *    - Deserialize the device
00427 *
00428 * - Increment next sample
00429 * - If nextSample is a stamp element
00430 *    - Read and set the stamp
00431 *    - Increment next sample
00432 * - Else, clear the stamp
00433 */
00434 void InputLogger::playNextSample()
00435 {
00436    vprDEBUG_OutputGuard(gadgetDBG_INPUT_MGR, vprDBG_STATE_LVL, "InputLogger::playNextSample\n", "done playing sample\n");
00437 
00438    vprASSERT(mNextSample_i != mEndSample_i && "Overran the logger sample list");
00439    gadget::InputManager* input_mgr = gadget::InputManager::instance();
00440 
00441    cppdom::NodePtr next_node = (*mNextSample_i);
00442    vprASSERT(next_node->getName() == std::string("sample") && "Didn't get element of name sample");
00443 
00444    // For each device element
00445    cppdom::NodeList dev_nodes = next_node->getChildren();
00446    for(cppdom::NodeListIterator cur_dev_node=dev_nodes.begin(); cur_dev_node != dev_nodes.end(); ++cur_dev_node)
00447    {
00448       vprASSERT((*cur_dev_node)->getName() == std::string("device"));
00449       std::string dev_name = (*cur_dev_node)->getAttribute("dev_name").getValue<std::string>();
00450       cppdom::NodePtr serial_dev_node = *((*cur_dev_node)->getChildren().begin());
00451       vprASSERT(serial_dev_node.get() != NULL && "Got null serialized device node");
00452 
00453       gadget::Input* dev_ptr = input_mgr->getDevice(dev_name);
00454       if(NULL != dev_ptr)
00455       {
00456          vprDEBUG_OutputGuard(gadgetDBG_INPUT_MGR, vprDBG_STATE_LVL,
00457                               std::string("Reading device: ") + dev_name + std::string("\n"), "done reading");
00458 
00459          vpr::XMLObjectReader xml_reader(serial_dev_node);     // Create XML reader
00460          xml_reader.setAttrib("rim.timestamp.delta", 0);       // Hack for now to work around RIM
00461          dev_ptr->readObject(&xml_reader);                     // Deserialize the device
00462       }
00463       else
00464       {
00465          vprDEBUG(gadgetDBG_INPUT_MGR,vprDBG_WARNING_LVL) << "Skipping device: [" << dev_name
00466                                          << "]  Could not find it.\n" << vprDEBUG_FLUSH;
00467       }
00468    }
00469 
00470    // Increment next sample
00471    mNextSample_i++;
00472    mActiveStamp.clear();      // Clear the stamp for now
00473 
00474    if(mNextSample_i != mEndSample_i)
00475    {
00476       if( (*mNextSample_i)->getName() == std::string("stamp"))
00477       {
00478          mActiveStamp = (*mNextSample_i)->getAttribute("id").getValue<std::string>();
00479          mNextSample_i++;
00480          vprDEBUG(gadgetDBG_INPUT_MGR, vprDBG_STATE_LVL) << "Logger: Got stamp: [" << mActiveStamp << "]\n" << vprDEBUG_FLUSH;
00481       }
00482    }
00483 
00484    if(mNextSample_i == mEndSample_i)   // If done playing
00485    {
00486       mCurState = Inactive;
00487       mSleepFramesLeft = 10;     // Wait 10 frames until we start processing anything again
00488       vprDEBUG(gadgetDBG_INPUT_MGR, vprDBG_STATE_LVL) << "Logger: Done playing.\n" << vprDEBUG_FLUSH;
00489    }
00490 
00491 }
00492 
00493 void InputLogger::limitFramerate()
00494 {
00495    vprASSERT(mLimitFrameRate);
00496 
00497    vpr::Interval cur_frame_time = vpr::Interval::now() - mPrevFrameTimestamp;
00498    if(cur_frame_time < mMinFrameTime)
00499    {
00500       vpr::Interval sleep_time = (mMinFrameTime - cur_frame_time);
00501       std::cout << "Sleeping: " << sleep_time.msec() << "ms\n";
00502       vprASSERT(sleep_time.msec() > 0);
00503       vpr::System::msleep(sleep_time.msec());                     // Sleep
00504    }
00505 
00506    mPrevFrameTimestamp.setNow();
00507 }
00508 
00509 void InputLogger::compressSamples()
00510 {
00511    // Get the current children to compress
00512    cppdom::NodeList& nodes(mRootNode->getChildren());
00513    unsigned total_nodes_start = nodes.size();
00514 
00515    // Create a nice little progress bar for the compression
00516    boost::progress_display compress_progress(nodes.size());
00517 
00518    cppdom::NodeList::iterator cur_node = nodes.begin();
00519    if(nodes.end() == cur_node)
00520       return;
00521 
00522    cur_node++;       // Can't remove first node
00523    //unsigned node_index(0);
00524 
00525    // Compress the data
00526    // - While nodes left
00527    //   - If node is duplicate of one before it
00528    //     - Erase the node and goto next
00529    while(cur_node != nodes.end())
00530    {
00531       cppdom::NodeList::iterator dup_node = (cur_node-1);      // Initialize dup node to check
00532 
00533       /*
00534       std::cout << "-----------------------------------------------------------------------\n"
00535                 << "-----------------------------------------------------------------------\n"
00536                 << "Comparing nodes: index: " << node_index++ << std::endl;
00537       std::cout << "------ curnode:\n";
00538       (*cur_node)->save(std::cout, 1);
00539       std::cout << "------ dupnode:\n";
00540       (*dup_node)->save(std::cout, 1);
00541       std::cout << "------ Start comparison ----\n";
00542       */
00543 
00544       if((*cur_node)->isEqual( *dup_node, mIgnoreAttribs, mIgnoreElems))
00545       {
00546          cur_node = nodes.erase(cur_node);         // Remove the node and get next one
00547       }
00548       else
00549       {
00550          cur_node++;                               // Go to the next
00551       }
00552 
00553       compress_progress += 1;       // Progress a little bit
00554    }
00555 
00556    // --- Print results --- //
00557    std::cout << std::endl
00558              << "Compression: initial/compressed:" << total_nodes_start << "/" << nodes.size() << std::endl
00559              << "      ratio: " << float(nodes.size())/float(total_nodes_start) << std::endl;
00560 }
00561 
00562 
00563 } // namespace gadget
00564 

Generated on Thu Jan 4 10:41:55 2007 for Gadgeteer by  doxygen 1.5.1