Tekkotsu Homepage | Demos | Overview | Downloads | Dev. Resources | Reference | Credits |
DataSource.hGo to the documentation of this file.00001 //-*-c++-*- 00002 #ifndef INCLUDED_DataSource_h_ 00003 #define INCLUDED_DataSource_h_ 00004 00005 //#include "Shared/RobotInfo.h" // only needed for some debugging output below 00006 #include "IPC/Thread.h" 00007 #include "IPC/MessageQueue.h" 00008 #include "Shared/debuget.h" 00009 #include "Shared/get_time.h" 00010 #include "Shared/RobotInfo.h" 00011 #include "Shared/plistPrimitives.h" 00012 #include <string> 00013 #include <iostream> 00014 #include <list> 00015 00016 class RCRegion; 00017 struct SensorState; 00018 00019 //! abstract base class for simulator data sources 00020 /*! Each subclass will implement loading data from some piece of hardware or network protocol. 00021 * 00022 * The general flow of calls is: 00023 * - constructor - user creates an instance of a DeviceDriver (which creates the data source) 00024 * - registerSource() - the user selects the data source via SharedGlobals::StreamSimConfig::sources 00025 * - enteringRealtime() - if the simulator enters real time mode 00026 * - leavingRealtime() - if the simulator is paused, set to full-speed mode, or begins to shut down 00027 * - deregisterSource() - simulator is shutting down or otherwise deleting associated DeviceDriver 00028 * - destructor - deleting associated DeviceDriver 00029 * 00030 * If your data source provides sensor data for current output values, you should call 00031 * providingOutput() for those outputs when registerSource() is called, and ignoringOutput() 00032 * when you are no longer active (deregisterSource() or destructor is called). 00033 * This prevents the Motion process from clobbering your readings with its own feedback. 00034 */ 00035 class DataSource { 00036 public: 00037 DataSource() : regions(), frozen(false), imageQueue(NULL) {} //!< constructor 00038 DataSource(const DataSource& ds) : regions(ds.regions), frozen(ds.frozen), imageQueue(ds.imageQueue) {} //!< copy constructor, just in case your subclass wants it 00039 DataSource& operator=(const DataSource&) { return *this; } //!< assignment operator, just in case your subclass wants it 00040 virtual ~DataSource(); //!< destructor 00041 00042 //! Returns the simulator time of the next data segment. 00043 /*! Should be in the future if nothing new since last data segment, otherwise should be the 00044 * timestamp of the most recent data segment (older segments are skipped), return -1U if there is no more data */ 00045 virtual unsigned int nextTimestamp()=0; 00046 00047 //! Returns a descriptive name of the next data segment for user feedback (e.g. a filename for logged data). 00048 /*! Just use your class name if you don't have a useful name for individual samples. */ 00049 virtual const std::string& nextName()=0; 00050 00051 //! Called when the simulator is stepping while paused or advancing a frozen data source, return true if successful, or false if no more data is available 00052 virtual bool advance()=0; 00053 00054 //! Called by simulator if the user pauses a data source; calls doFreeze() or doUnfreeze() as appropriate 00055 /*! You probably don't want to override this function -- that's what doFreeze() doUnfreeze are for! */ 00056 virtual void setFrozen(bool fr) { if(fr==frozen) return; frozen=fr; if(getTimeScale()>0) { if(frozen) doFreeze(); else doUnfreeze(); } } 00057 virtual bool getFrozen() const { return frozen; } //!< returns #frozen status 00058 00059 //! if called, indicates a request to restart/reinitialize the data source 00060 /*! For example, a FileSystemDataSource would go back to the beginning of its list, 00061 * and a network-based source would close and reopen the connection */ 00062 virtual void reset() {} 00063 00064 //! User hook, called when the data source should claim which outputs it provides feedback (providingOuput()) 00065 /*! Does not indicate the data source should start sending updates yet — wait for enteringRealtime() or advance() to be called */ 00066 virtual void registerSource() {} 00067 00068 //! User hook, called when the data source should release its claim on outputs with feedback (ignoringOuput()). 00069 /*! It would be wise to call this from your destructor as well. */ 00070 virtual void deregisterSource() {} 00071 00072 //! User hook, called when the controller is going to be running in realtime mode, which is probably the normal mode you'd expect. 00073 /*! You might be in realtime mode, but a debugger breakpoint will still pause things, or thread scheduling could hiccup, so try to be robust.\n 00074 * The argument is a reference to SharedGlobals::timeScale, so the data source can subscribe to changes in 00075 * simulation speed if it can use that information. (We avoid direct dependency on the tekkotsu simulator 00076 * so this code can be reused for other tools too.) */ 00077 virtual void enteringRealtime(const plist::Primitive<double>& /*simTimeScale*/) { if(!frozen) doUnfreeze(); } 00078 00079 //! User hook, called when leaving realtime mode, which means you have no idea when motionCheck() is going to be called in terms of wall-clock time. 00080 /*! Argument set to true if entering full speed mode, which indicates everything should run 00081 * at full native "frame rate", and may indicate more data will be processed than normal, CPU speed permitting. 00082 * However, if false, almost certainly indicates updates will be sparse, trigger by user 'step' commands. 00083 * May be called multiple times if changing between full-speed mode and paused 00084 * 00085 * A non-realtime mode might be triggered if the user wants to pause the simulator/controller to step through something... 00086 * No guarantees though! The debugger might catch a breakpoint and stop things, and this won't be called! */ 00087 virtual void leavingRealtime(bool /*isFullSpeed*/) { if(!frozen) doFreeze(); } 00088 00089 //! Called by simulator during initialization to tell DataSources where the array of sensor values are stored (see #sensorState) 00090 /*! This would need to point into a shared memory region if using multi-process model, hence we can't just 00091 * use process-local static allocation. */ 00092 static void setSensorState(SensorState* senState) { sensorState=senState; } 00093 00094 //! Called by simulator during initialization to tell the DataSource where it should send image data (if this is selected as an active image data source). 00095 /*! Each image data source (if we get around to supporting such concurrent sources) will have a separate message queue */ 00096 virtual void setImageQueue(MessageQueueBase* mq) { imageQueue=mq; } 00097 00098 //! will be called by initialization code prior to first getData() if client code is going to block on getting the first sensor reading 00099 static void setNeedsSensor(bool waiting) { requiresFirstSensor=waiting; } 00100 00101 //! will be called by initialization code to provide a pointer to the sensor synchronization framerate #sensorFramerate 00102 static void setSensorFramerate(const plist::Primitive<float>* senFR) { sensorFramerate = senFR; } 00103 00104 //! This structure should be written into the beginning of each image buffer sent via setImage(). 00105 /*! Using "placement new" avoids doing a memcpy: <code>new (buffer) ImageHeader(...);</code>\n 00106 * If you are storing a visual image to be processed by the pipeline (vs. a laser rangefinder sweep or such), then 00107 * values should be interleaved YUV samples. */ 00108 struct ImageHeader { 00109 //! full constructor, see corresponding members for documentation on arguments 00110 ImageHeader(unsigned int sourceID_, int layer_, unsigned int width_, unsigned int height_, unsigned int components_, unsigned int frameNumber_, unsigned int timestamp_, const std::string& name_) 00111 : sourceID(sourceID_), layer(layer_), width(width_), height(height_), components(components_), frameNumber(frameNumber_), timestamp(timestamp_) { strncpy(name,name_.c_str(),MAX_NAME_LEN); } 00112 00113 unsigned int sourceID; //!< In order to support multiple cameras, this tracks how to identify the cameras within Tekkotsu. You should allow users to configure the value you store here. 00114 int layer; //!< the resolution layer for this image, if 0 then automatically chooses the closest in size based on expected values in RobotInfo namespace. Otherwise should be presented as a configuration item. 00115 unsigned int width; //!< pixels per row of image 00116 unsigned int height; //!< pixels per column of image 00117 unsigned int components; //!< number of color channels, vision pipeline expects 3 for YUV images. 00118 00119 unsigned int frameNumber; //!< serial number per image sent, provides user notification for dropped images if the SharedGlobals::StreamSimConfig::verbose flag is set 00120 unsigned int timestamp; //!< timestamp image was recorded 00121 static const unsigned int MAX_NAME_LEN=64; //!< maximum length of #name 00122 char name[MAX_NAME_LEN]; //!< a user identifiable name for the image... see DataSource::nextName() 00123 }; 00124 00125 //! returns the pending output value, which may not have been processed by the framework yet 00126 static float getOutputValue(unsigned int i); 00127 00128 //! returns the pending sensor value, which may not have been processed by the framework yet 00129 static float getSensorValue(unsigned int i); 00130 00131 //! returns the pending button value, which may not have been processed by the framework yet 00132 static float getButtonValue(unsigned int i); 00133 00134 //! returns the pending duty cycle value, which may not have been processed by the framework yet 00135 static float getPIDDutyValue(unsigned int i); 00136 00137 protected: 00138 //! subclasses should pass this to a MarkScope instance before they begin calling the static setXXXValue functions to prevent concurrent or partial updates 00139 static Resource& getSensorWriteLock(); 00140 00141 //! subclasses should call this if they provide sensor updates which will contain a measurement of the current position of output @a i. 00142 /* A DataSource should consider itself providing an output if it will be sending some kind of measurement of 00143 * the current value of an output, and has been assigned to a LoadDataThread 00144 * (i.e. setDataSourceThread() has been called and #thread is non-NULL).\n 00145 * This prevents the motion process from clobbering your readings with its own feedback. */ 00146 static void providingOutput(unsigned int i); 00147 00148 //! subclasses should call this if they provided sensor updates containing the current position of output @a i, but are not going to do so any longer. 00149 /*! You don't need to call this if you didn't previously call providingOutput(). */ 00150 static void ignoringOutput(unsigned int i); 00151 00152 //! sets the current value for an output, be sure to register with providingOutput() (and later ignoringOutput()) in order to avoid being clobbered by "blind" feedback from the Motion process 00153 static void setOutputValue(unsigned int i, float v); 00154 00155 //! sets the current value for a sensor 00156 static void setSensorValue(unsigned int i, float v); 00157 00158 //! sets the current value for a button 00159 static void setButtonValue(unsigned int i, float v); 00160 00161 //! sets the current duty cycle value for a servo joint, @a i should be relative to PIDJointOffset 00162 static void setPIDDutyValue(unsigned int i, float v); 00163 00164 //! Returns a counter which is incremented each time the sensor data is copied into the framework 00165 /*! You could use this to determine if user code has gotten a chance to see a detection flag of some sort before clearing it again */ 00166 static unsigned int getSensorFrameNumber(); 00167 00168 //! Sets the current image, for bulk data sources like cameras. 00169 /*! You should not modify data in @a region until you pass a new @a region here. 00170 * However even then, the previous region may still be in processing. Check 00171 * region reference counts to determine when an old region is available for 00172 * recycling, or otherwise dereference them after calling this so they will 00173 * be deallocated after use. 00174 * 00175 * Image data should be in 8-bit per channel interleaved format and the data 00176 * should be prepended by an ImageHeader structure so the receiver knows how to 00177 * interpret the data. 00178 * 00179 * Recommended to just use getUnusedRegion() to handle region recycling for you. */ 00180 void setImage(RCRegion* region) { 00181 ASSERTRET(imageQueue!=NULL,"DataSource::setImage called without imageQueue"); 00182 imageQueue->sendMessage(region); 00183 } 00184 00185 //! sets the current image, for bulk data sources like cameras 00186 /*! This version copies the data into an RCRegion and calls setImage(RCRegion*). For efficiency with 00187 * large blocks of data, if possible you should use getUnusedRegion and write your results directly 00188 * there to call setImage(RCRegion*) instead and save a copy if possible. 00189 * 00190 * Image @a data should be in 8-bit per channel interleaved format. */ 00191 void setImage(const ImageHeader& header, const void * data); 00192 00193 //! Finds a one-reference entry in #regions with capacity at least @a minSize, creating a new one of size @a minSize + @a padding if necessary. 00194 /*! If the first one-reference entry is too small, it will be freed and a new one created instead. */ 00195 virtual RCRegion* getUnusedRegion(size_t minSize, size_t padding); 00196 00197 std::list<RCRegion*> regions; //!< for efficiency, reuse old buffers via getUnusedRegion() -- oldest at front, most recently used at back 00198 00199 virtual void doFreeze() {} //!< user hook for when #frozen is set to true; advance() will be called by simulator at user discretion. 00200 virtual void doUnfreeze() {} //!< user hook for when #frozen is set to false; if enteringRealtime() has been called then you should resume sending data. 00201 bool frozen; //!< indicates that data is going to be requested "sparsely"; advance() will be called by simulator at user discretion 00202 00203 //! if true, indicates that client code is going to wait for sensor readings returned by getData before executing 00204 static bool requiresFirstSensor; 00205 00206 //! Assigned by setSensorFramerate() during initialization, points to the synchronization framerate for sensors. 00207 /*! It is pointless to exceed this framerate for sensor updates, so if you have control over the polling rate, set it to this value. */ 00208 static const plist::Primitive<float>* sensorFramerate; 00209 00210 private: 00211 static SensorState * sensorState; 00212 00213 //! assigned by setImageQueue(), calls to setImage() will send messages into this queue 00214 MessageQueueBase* imageQueue; 00215 }; 00216 00217 00218 //! A global instance of this structure is registered via DataSource::setSensorState() during launch, so that DataSources know where to put their results 00219 /*! Subclasses of DataSource don't access this structure directly, they should call the static DataSource functions to write its fields. 00220 * Image based data sources like cameras will send raw buffers of data via DataSource::setImage, ignoring this structure. */ 00221 struct SensorState : public Resource { 00222 //! constructor 00223 SensorState(); 00224 00225 //! returns true if any of #providedOutputs is greater than zero 00226 bool hasProvidedOutput() const { for(unsigned int i=0; i<NumOutputs; i++) if(providedOutputs[i]>0) return true; return false; } 00227 //! returns true if any of #providedOutputs is zero 00228 bool hasUnprovidedOutput() const { for(unsigned int i=0; i<NumOutputs; i++) if(providedOutputs[i]==0) return true; return false; } 00229 00230 //! Counts the number of sensor data sources which are providing readings for each output 00231 /*! This isn't a configuration setting per se, but needed so motion process can tell if it 00232 * should provide feedback for each output. If an output doesn't have any sensor feedback 00233 * (or #override is true), then motion should provide feedback. If more than one 00234 * sensor is providing the same output, that could be a problem, but not dealt with here. 00235 * 00236 * The simulator's initialization routines will pass this to DataSource::setOutputTracker(). */ 00237 unsigned int providedOutputs[NumOutputs]; 00238 00239 float outputs[NumOutputs]; //!< maps to WorldState::outputs, assign via DataSource::setOutputValue 00240 float buttons[NumButtons]; //!< maps to WorldState::buttons, assign via DataSource::setButtonValue 00241 float sensors[NumSensors]; //!< maps to WorldState::sensors, assign via DataSource::setSensorValue 00242 float pids[NumPIDJoints][3]; //!< maps to WorldState::pids, only assigned via MotionExecThread 00243 float pidduties[NumPIDJoints]; //!< maps to WorldState::pidduties, assign via DataSource::setPIDDutyValue 00244 00245 unsigned int timestamp; //!< simulator time of last update to one of the value arrays 00246 00247 //! Serial number for the update, incremented each time Simulator sends an update notification. 00248 /*! This is not incremented for each DataSource modification because there might be multiple data sources, 00249 * or a data source might make multiple partial updates within each Simulator frame. */ 00250 unsigned int frameNumber; 00251 00252 //! this flag should be set when any of the fields are updated, cleared when releaseResource is called (when timestamp is set instead) 00253 bool dirty; 00254 00255 //! This may be set to point to the Motion.OverrideSensors configuration setting, causing sensor updates for outputs to be ignored (forcing open loop) 00256 /*! This is useful if loading sensor data from log, where we want to compute new output positions in simulation, but using the "pure" sensors for input */ 00257 plist::Primitive<bool>* motionOverride; 00258 00259 // ! signals when a sensor field is updated so things like full speed mode and waiting for the first sensor update can work 00260 /*Thread::Condition updateSignal;*/ 00261 00262 //! a function to be called whenever releaseResource() is called and dirty flag is set; called at the end once the resource is immediately available 00263 void (*resourceSync)(); 00264 00265 protected: 00266 virtual void useResource(Data& d) { 00267 static_cast<Resource&>(lock).useResource(d); 00268 } 00269 virtual void releaseResource(Data& d); 00270 00271 //! lock to prevent concurrent access to the structure, but should use the SensorState with MarkScope to set the dirty flag on unlock 00272 Thread::Lock lock; 00273 00274 private: 00275 SensorState(const SensorState&); //!< don't call 00276 SensorState& operator=(const SensorState&); //!< don't call 00277 }; 00278 00279 inline Resource& DataSource::getSensorWriteLock() { return *sensorState; } 00280 inline void DataSource::setOutputValue(unsigned int i, float v) { 00281 if(sensorState->motionOverride!=NULL && *sensorState->motionOverride) 00282 return; 00283 sensorState->outputs[i]=v; 00284 sensorState->dirty=true; 00285 } 00286 inline float DataSource::getOutputValue(unsigned int i) { return sensorState->outputs[i]; } 00287 00288 inline void DataSource::setSensorValue(unsigned int i, float v) { sensorState->sensors[i]=v; sensorState->dirty=true; } 00289 inline float DataSource::getSensorValue(unsigned int i) { return sensorState->sensors[i]; } 00290 00291 inline void DataSource::setButtonValue(unsigned int i, float v) { sensorState->buttons[i]=v; sensorState->dirty=true; } 00292 inline float DataSource::getButtonValue(unsigned int i) { return sensorState->buttons[i]; } 00293 00294 inline void DataSource::setPIDDutyValue(unsigned int i, float v) { sensorState->pidduties[i]=v; sensorState->dirty=true; } 00295 inline float DataSource::getPIDDutyValue(unsigned int i) { return sensorState->pidduties[i]; } 00296 00297 inline unsigned int DataSource::getSensorFrameNumber() { return sensorState->frameNumber; } 00298 00299 /*! @file 00300 * @brief Defines DataSource, an abstract base class for simulator data sources 00301 * @author Ethan Tira-Thompson (ejt) (Creator) 00302 */ 00303 00304 #endif |
Tekkotsu Hardware Abstraction Layer 5.1CVS |
Generated Mon May 9 05:01:38 2016 by Doxygen 1.6.3 |