Tekkotsu Homepage
Demos
Overview
Downloads
Dev. Resources
Reference
Credits

LoadDataThread.h

Go to the documentation of this file.
00001 //-*-c++-*-
00002 #ifndef INCLUDED_LoadDataThread_h_
00003 #define INCLUDED_LoadDataThread_h_
00004 
00005 #include "IPC/PollThread.h"
00006 #include "Shared/plist.h"
00007 #include "IPC/MessageQueue.h"
00008 #include "Shared/get_time.h"
00009 #include "local/DeviceDriver.h"
00010 #include "local/DataSource.h"
00011 #include <list>
00012 #include <vector>
00013 
00014 //! Provides resources for loading time-based data from disk
00015 /*! Runs in a separate thread, preloads data into shared memory buffers, and
00016  *  then sends the buffer when the time is right.  Thread doesn't start until
00017  *  you set the source or call loadFileList(). */
00018 class LoadDataThread : public virtual PollThread, public virtual plist::Dictionary, public virtual plist::PrimitiveListener, public virtual plist::CollectionListener, public virtual DeviceDriver::SourceListener {
00019 public:
00020   //! member function pointer to call on DeviceDriver to get the data sources available for this thread
00021   typedef void (DeviceDriver::*getDataSources_t)(std::map<std::string,DataSource*>&);
00022 
00023   
00024   //! constructor
00025   /*! @param getDS a DeviceDriver member function callback, which will be used to query drivers for the data sources of they type being loaded by this thread
00026    *  @param fps frames per second, see #framerate
00027    *  @param messages the MessageQueue through which to send the data
00028    *
00029    *  For example, there are sensor sources and camera/image sources, each managed by a
00030    *  different function of DeviceDriver (DeviceDriver::getSensorSources vs. DeviceDriver::getImageSources).
00031    *  The LoadDataThread is initialized to call one of these functions to acquire data sources
00032    *  based on which driver is referenced by #source.
00033    */
00034   LoadDataThread(getDataSources_t getDS, float fps, MessageQueueBase& messages)
00035   : PollThread(), plist::Dictionary(),
00036     source(), framerate(fps), verbose(0), heartbeat(true), frozen(false),
00037     regions(), getDataSources(getDS), msgr(messages), lock(), lastSent(-1), frameSN(0), sourceSN(0), enabled(false), runloopState(INTERFRAME),
00038     curDS(NULL), dsDriver(NULL)
00039   {
00040     setLoadSavePolicy(FIXED,SYNC);
00041     addEntry("Source",source); getAvailableDataSources(); // getAvailableDataSources sets #source comment string
00042     addEntry("Framerate",framerate,"The rate at which images should be loaded.  This is passed as a hint to the source, which may be limited to multiples of its capture device frequency.");
00043     addEntry("Verbose",verbose,"Controls how much feedback to give on the console regarding progress\n  0 - none\n  1 - report when messages are dropped\n  2 - also report when a message is sent\n  3 - also report when heartbeat is sent/dropped, and when loop occurs\n  4 - also report when each message is preloaded");
00044     addEntry("Heartbeat",heartbeat,"If enabled, an empty \"heartbeat\" message is sent at the appropriate framerate, even if no data is being processed (i.e. frozen, no data loaded, or out of frames); this will cause an update event within the simulator, repeating processing on the previous data.");
00045     addEntry("Frozen",frozen,"If true, no frames will be sent, except via explicit 'advance' commands; if false, the thread will run and send messages at the requested times automatically");
00046     source.addPrimitiveListener(this); // call setDataSource when the source changes
00047     framerate.addPrimitiveListener(this); // update data source's framerate
00048     verbose.addPrimitiveListener(this); // set msgr report droppings and update data source's verbosity
00049     heartbeat.addPrimitiveListener(this); // start/stop thread if frozen
00050     frozen.addPrimitiveListener(this); // update current source, start/stop the thread
00051     DeviceDriver::getRegistry().addCollectionListener(this);
00052   }
00053   //! destructor
00054   ~LoadDataThread();
00055   
00056   //! Names the device driver from which the DataSource will be taken
00057   /*! If you simply provide a device driver name, the first entry of the appropriate data queue will be used.
00058    *  Alternatively, use drivername.entryname to specify a specific data source entry in case the driver
00059    *  has multiple data sources for the queue type. */
00060   plist::Primitive<std::string> source;
00061   
00062   //! frames per second to send -- this is only a suggestion to hardware devices, which generally use their 'native' framerate, but may use this to limit data flow
00063   plist::Primitive<float> framerate;
00064 
00065   //! Controls how much feedback to give on the console regarding progress
00066   /*! 0 - none\n
00067    *  1 - report when message is sent\n
00068    *  2 - also report when message is dropped\n
00069    *  3 - also report when heartbeat is sent/dropped, and when loop occurs\n
00070    *  4 - also report when each message is preloaded */
00071   plist::Primitive<int> verbose; 
00072 
00073   //! if enabled, an empty "heartbeat" message is sent at the appropriate framerate, even if no data is being processed (i.e. no data loaded or out of frames); this will cause an update event within the simulator, repeating processing on the previous data.
00074   plist::Primitive<bool> heartbeat;
00075   
00076   //! if true, no frames will be sent, except via explicit (external) calls to advanceFrame(); if false, the thread will run and send messages at the requested times
00077   plist::Primitive<bool> frozen;
00078   
00079   //! sends the next frame as soon as possible, blocking until sent
00080   /*! @param forceQueue pass true to force a new data frame to be sent, otherwise will send a heartbeat if frozen
00081    * @return true if there was a frame to send; will send heartbeat if configured, but returns false for heartbeats */
00082   virtual bool advanceFrame(bool forceQueue);
00083 
00084   virtual void plistValueChanged(const plist::PrimitiveBase& pl);
00085   virtual void plistCollectionEntryAdded(Collection& /*col*/, ObjectBase& /*primitive*/) { getAvailableDataSources(); }
00086   virtual void plistCollectionEntryRemoved(Collection& /*col*/, ObjectBase& /*primitive*/) { getAvailableDataSources(); }
00087   virtual void plistCollectionEntriesChanged(Collection& /*col*/) { getAvailableDataSources(); }
00088   virtual void start();
00089   virtual void stop();
00090     
00091   //! recovers data from serialized IPC message, returns beginning of payload, or NULL if there's an error
00092   /*! each parameter can be a pointer where to store the field, or NULL if you don't care */
00093   static char* deserializeHeader(char* buf, unsigned int size, unsigned int* verbose, unsigned int* sn, std::string* filename, bool* dataInQueue, unsigned int* payloadSize);
00094 
00095   virtual unsigned int nextTimestamp() { return (curDS!=NULL) ? curDS->nextTimestamp() : -1U; }
00096   virtual const std::string& nextName() { return (curDS!=NULL) ? curDS->nextName() : emptyStr(); }
00097   
00098   virtual DataSource* getDataSource() { return curDS; } //!< returns #curDS
00099   
00100   virtual void dataSourcesUpdated() { plistValueChanged(source); }
00101   
00102   //! called from constructor to build list of available data source names, resets the #source help text
00103   std::string getAvailableDataSources();
00104   
00105   virtual void loadXML(xmlNode* node) {
00106     plist::Dictionary::loadXML(node);
00107     getAvailableDataSources();
00108   }
00109   
00110 protected:
00111   //! Makes a data source eligible for providing data
00112   /*! This is protected because you shouldn't be calling it directly.  Instead, set #source to the driver instance name, which will cause this to be called via plistValueChanged */
00113   virtual void setDataSource(DeviceDriver* dd, DataSource* ds);
00114   
00115   //! monitor #msgr, send new messages when their timestamp indicates they are due, then load upcoming messages
00116   virtual unsigned int runloop();
00117 
00118   //! removes and returns first region in #regions with only one reference, or NULL if none exists
00119   virtual RCRegion* firstUnusedRegion();
00120   
00121   //! sets up some header info in the specified RCRegion, reallocating if the suggested region is not large enough (or is NULL), returns pointer to end of header info in the region
00122   virtual char* setupRegion(RCRegion*& region, const std::string& file, unsigned int payload, bool hasMoreData);
00123 
00124   //! removes our reference to a region created by loadFile()
00125   virtual void freeRegion(RCRegion* rcr) { if(rcr!=NULL) rcr->RemoveReference(); }
00126   
00127   //! sends an empty heartbeat message indicating previous data should be reused
00128   virtual void sendHeartbeat() { sendHeartbeat(get_time()); }
00129   //! sends an empty heartbeat message indicating previous data should be reused, assuming that curt is the current time
00130   virtual void sendHeartbeat(unsigned int curt);
00131   
00132   //! returns time of next heartbeat
00133   unsigned int calcNextHeartbeat(unsigned int curt) const;
00134   
00135   unsigned int calcSleepTime() { unsigned int curt=get_time(); return static_cast<unsigned int>((calcNextHeartbeat(curt)-curt)/getTimeScale()); }
00136   
00137   typedef std::list<RCRegion* > msgbuf_t; //!< type of collection of shared data regions
00138   msgbuf_t regions; //!< for efficiency, reuse old buffers -- oldest at front, most recently used at back
00139   
00140   //! member function pointer to be called on DeviceDriver to get its available data sources
00141   getDataSources_t getDataSources;
00142   
00143   MessageQueueBase& msgr; //!< the MessageQueue through which to send the data
00144   Thread::Lock lock; //!< allows mutual exclusion over this object's member variables
00145   float lastSent; //!< timestamp of most recently sent message (or heartbeat); -1 if hasn't sent any from current source
00146   unsigned int frameSN; //!< serial number of next message to send
00147   unsigned int sourceSN; //!< serial number of the last data from #curDS; 0 if hasn't received any from current source
00148   bool enabled; //!< set to true by start(), false by stop(), but reset if we call start or stop ourselves based on changes to freeze or heartbeat
00149   volatile enum {
00150     INTERFRAME, //!< runloop() is waiting between frames (usually heartbeats)
00151     GET_FRAME, //!< runloop() is getting a frame, should not be interrupted
00152     GOT_FRAME, //!< runloop() has gotten a frame and is waiting until its timestamp has arrived
00153     ADVANCE_FRAME //!< runloop() was in GOT_FRAME, but advanceFrame() was called, frame should be sent *now*
00154   } runloopState;
00155   
00156   DataSource * curDS; //!< pointer to the current data source
00157   DeviceDriver * dsDriver; //!< pointer to the data source's driver
00158   
00159 private:
00160   LoadDataThread(const LoadDataThread&); //!< do not call
00161   LoadDataThread& operator=(const LoadDataThread&); //!< do not call
00162 };
00163 
00164 /*! @file
00165  * @brief 
00166  * @author Ethan Tira-Thompson (ejt) (Creator)
00167  *
00168  * $Author: ejt $
00169  * $Name: tekkotsu-4_0 $
00170  * $Revision: 1.16 $
00171  * $State: Exp $
00172  * $Date: 2007/11/09 19:01:15 $
00173  */
00174 
00175 #endif

Tekkotsu Hardware Abstraction Layer 4.0
Generated Thu Nov 22 01:00:53 2007 by Doxygen 1.5.4