| Tekkotsu Homepage | Demos | Overview | Downloads | Dev. Resources | Reference | Credits |
EventBase.hGo to the documentation of this file.00001 //-*-c++-*- 00002 #ifndef INCLUDED_EventBase_h 00003 #define INCLUDED_EventBase_h 00004 00005 #include "Shared/XMLLoadSave.h" 00006 #include "Shared/FamilyFactory.h" 00007 #include <string> 00008 00009 //! Forms the basis of communication between modules/behaviors in the framework 00010 /*! 00011 * Events are defined by a 3-tuple: 00012 * - The @b generator indicates the class of the creator of the event, and in practice also generally implies the type (i.e. subclass) of the event itself. 00013 * - The @b source indicates the instance of the creator of the event, which differentiates when more than one generator is available. 00014 * - The @b type indicates the role of the event among other events from the source. 00015 * 00016 * Each of these is represented by an ID, the #EventGeneratorID_t (EGID) and #EventTypeID_t (ETID) are 00017 * defined here in EventBase. Source IDs (SID) are defined separately by each generator, so the 00018 * documentation in each entry of EventGeneratorID_t describes how to interpret the source field. 00019 * 00020 * So for example, the button event which results from initially pressing the head button on an ERS-7 00021 * robot would be: 00022 * <center>(EventBase::buttonEGID, ERS7Info::HeadButOffset, EventBase::activateETID)</center> 00023 * 00024 * While the button is held down, additional #statusETID events are used to report varying pressure values (if the button 00025 * is pressure sensitive), and an event with #deactivateETID is sent when the button is released. 00026 * Alternatively, an SID value from a vision detector (say #visObjEGID) refers to seeing a particular object, a completely 00027 * different domain, values of which may overlap with other generators' source IDs. 00028 * Thus, interpreting the source field requires knowing the generator as well. 00029 * 00030 * When the generator doesn't have groups of activity with a 'begin' or 'end', it will use #statusETID 00031 * for all of its events. (in other words, not all generators necessarily have to use activate or deactivate) 00032 * For example, sensor updates are continuously occuring, so you will only ever see 00033 * <center>(EventBase::sensorEGID,SensorSrcID::UpdatedSID,EventBase::statusETID)</center> 00034 * 00035 * The #duration field is also generator specific - some may refer to 00036 * the time since the last activation event (e.g. button events) 00037 * where as others refer to time since last status (e.g. sensors 00038 * updates) 00039 * 00040 * If you want to make a new generator, all you have to do is add a new entry 00041 * to the ID list (#EventGeneratorID_t) and then put its name in the 00042 * #EventGeneratorNames[] array. Alternatively, there is an 'unlicensed spectrum' available under 00043 * #unknownEGID. You can send out events from that generator just 00044 * like any other, but it should only be used for quick tests and hacking 00045 * around... 00046 * 00047 * The generator ID number is only that -- an ID number. 00048 * Events can be posted to the EventRouter anywhere, anytime, 00049 * and do not require anything of the sender. However, there is an EventGeneratorBase which 00050 * can simplify some aspects of behaviors whose sole purpose is processing information 00051 * and generating events. 00052 * 00053 * If more information needs to be sent along with the event, the 00054 * cleanest solution is to create a subclass of EventBase to hold the 00055 * additional information. For example, you can see the existing 00056 * subclasses in the inheritance diagram above. If you want to use a 00057 * quick hack however, you could just pass a pointer to data as the SID if you 00058 * don't need that field for something else, or use a DataEvent. 00059 * 00060 * @note All subclasses must override getClassTypeID() and provide 00061 * a unique value to allow fast polymorphic serialization for inter-process 00062 * communication. Implementing the load/save functions 00063 * ({load,save}BinaryBuffer and {load,save}XML) for your addtional data fields 00064 * would also be necessary in order to allow your event to be sent between 00065 * processes, or eventually, between robots. 00066 * 00067 * @see EventRouter for discussion on sending and receiving of events 00068 * @see Tutorials: 00069 * - <a href="../FirstBehavior2.html">Steps 3, 4, & 5 of Tekkotsu's First Behavior Tutorial</a> 00070 * - <a href="http://www.cs.cmu.edu/~dst/Tekkotsu/Tutorial/events.shtml">David Touretzky's Events Chapter</a> 00071 * - <a href="http://www.cs.cmu.edu/afs/cs/academic/class/15494-s06/www/lectures/behaviors.pdf">CMU's Cognitive Robotics course slides</a> 00072 * - <a href="../media/TekkotsuQuickReference_ERS7.pdf">ERS-7 Quick Reference Sheet</a> 00073 */ 00074 class EventBase : public XMLLoadSave { 00075 public: 00076 //! Lists all possible event generator ids 00077 /*! An event generator is a abstract source of events, used for listening to and parsing certain classes of events 00078 * 00079 * IF YOU ADD AN EVENT GENERATOR, DON'T FORGET TO NAME IT (EventBase::EventGeneratorNames, actual names are in EventBase.cc)*/ 00080 enum EventGeneratorID_t { 00081 unknownEGID=0, //!< default EGID, used if you forget to set it, probably not a good idea to use this for anything except errors or testing quick hacks 00082 aiEGID, //!< not being used, yet (might use this when AI makes decisions?) 00083 audioEGID, //!< Sends an event when a sound starts/ends playback, status events as chained sounds end; SID is SoundManager::Play_ID; duration is playtime 00084 buttonEGID, //!< Sends activate event for button down, deactivate for button up. Status events only for when pressure sensitive buttons' reading changes. (on sensorEGID updates); SIDs are from ButtonOffset_t in the namespace of the target model (e.g. ERS210Info::ButtonOffset_t); duration is button down time 00085 cameraResolutionEGID, //!< Sends a status event whenever the camera's resolution changes, such as a different camera than predicted by RobotInfo is being used, or a camera is hotswapped. SID corresponds to the visOFbkEGID for that camera. 00086 erouterEGID, //!< Sends activate event on first listener, deactivate on last listener, and status on other listener changes.; SID is the generator ID affected 00087 estopEGID, //!< Sends an event when the estop is turned on or off; SID is the MotionManager::MC_ID of the EmergencyStopMC; duration is length of estop activation 00088 grasperEGID, //!< Sends events when the Grasper processes a user request 00089 locomotionEGID, //!< Sends events regarding transportation in the world; you can/should assume these will all be LocomotionEvent classes; SID is MotionManager::MC_ID of posting MotionCommand; duration is the time since last velocity change of that MC. (You could generate these for things other than walking...) 00090 lookoutEGID, //!< Sends an event when Lookout request is complete; SID is the id for lookout request 00091 mapbuilderEGID, //!< Sends a status event when map is completed 00092 micOSndEGID, //!< Sends a DataEvent<OSoundVectorData> for every audio buffer received from the system; SID and duration are always 0 (This is generated by the MainObj instantiation of MMCombo) 00093 micRawEGID, //!< reserved for future use 00094 micFFTEGID, //!< reserved for future use 00095 micPitchEGID, //!< Sends a PitchEvent when a particular frequency is detected; SID is a pointer to the PitchDetector, magnitude is product of amplitude and confidence 00096 mocapEGID, //!< Sends a MoCapEvent for "motion capture" or GPS type updates from an external localization source. Used for position/orientation feedback from simulation as well. 00097 motmanEGID, //!< Sends events when a MotionCommand is added or removed, SID is is the MotionManager::MC_ID, duration is always 0; individual MotionCommands may throw status events to signal intermediary status 00098 pilotEGID, //!< Sends events when position of agent is updated or pilot request is completed 00099 powerEGID, //!< Sends events for low power warnings, temperature, etc. see PowerSrcID::PowerSourceID_t 00100 remoteStateEGID, //!< Sent when remote state is updated 00101 runtimeEGID, //!< Sends an activate upon completion of startup, and a deactivate event upon shutdown; source ID undefined (currently 0) 00102 sensorEGID, //!< Sends a status event when new sensor readings are available. see SensorSrcID::SensorSourceID_t 00103 servoEGID, //!< Sends notifications of servo errors; the source id is the Tekkotsu output offset 00104 stateMachineEGID, //!< Sends an event upon entering and leaving a StateNode; SID is pointer to the StateNode; duration is always 0; some state will throw a status event when they have completed their task and are now idling 00105 stateSignalEGID, //!< Sends a DataEvent that can be monitored by a SignalTrans for triggering a state transition 00106 stateTransitionEGID, //!< Sends an event each time a transition is triggered; SID is a pointer to the transition; type is always status, duration is always 0; guaranteed to occur immediately *before* the transition actually occurs 00107 textmsgEGID, //!< Sends status events when a text msg is received on console; generated by the Controller, SID is 0 if broadcast from ControllerGUI, 1 if "private" from BehaviorSwitchControl; duration is always 0 (see Controller for more information) 00108 timerEGID, //!< Sends timer events; you set timers explicitly, you don't have to listen as well. (See EventRouter::addTimer()) There's no cross-talk, only the listener which requested the timer will receive it; SID is whatever you requested it to be; duration is the time (since boot, in ms) that the timer was supposed to go off; these are always status 00109 userEGID, //!< Reserved for user-generated events 00110 visInterleaveEGID,//!< Sends a FilterBankEvent when new interleaved images are available; SID is whatever value you gave during setup (typically in StartupBehavior_SetupVision.cc), duration is always 0 00111 visJPEGEGID, //!< Sends a FilterBankEvent when JPEG compressed images are available; SID is whatever value you gave during setup (typically in StartupBehavior_SetupVision.cc), duration is always 0 00112 visObjEGID, //!< Sends VisionObjectEvents for objects detected in camera images; SID is whatever value you gave during setup (typically in StartupBehavior_SetupVision.cc), duration is always 0 00113 visOFbkEGID, //!< Sends a DataEvent < OFbkImageVectorData > for every camera image received from the system; SID and duration are always 0 (This is generated by the MainObj instantiation of MMCombo) 00114 visPNGEGID, //!< Sends a FilterBankEvent when PNG compressed images are available; SID is whatever value you gave during setup (typically in StartupBehavior_SetupVision.cc), duration is always 0 00115 visRawCameraEGID, //!< Sends a FilterBankEvent when new raw camera images are available; SID is whatever value you gave during setup (typically in StartupBehavior_SetupVision.cc), duration is always 0 00116 visRawDepthEGID, //!< Sends FilterBankEvent when new depth images are available 00117 visRegionEGID, //!< Sends a SegmentedColorFilterBankEvent when color regions are available; SID is whatever value you gave during setup (typically in StartupBehavior_SetupVision.cc), duration is always 0 00118 visRLEEGID, //!< Sends a SegmentedColorFilterBankEvent when RLE encoded color segmentated images are available; SID is whatever value you gave during setup (typically in StartupBehavior_SetupVision.cc), duration is always 0 00119 visSegmentEGID, //!< Sends a SegmentedColorFilterBankEvent when color segmentated images are available; SID is whatever value you gave during setup (typically in StartupBehavior_SetupVision.cc), duration is always 0 00120 wmVarEGID, //!< Sends an event when a watched memory is changed; source id is pointer to WMEntry 00121 worldModelEGID, //!< not being used, yet (for when objects are detected/lost?) 00122 numEGIDs //!< the number of generators available 00123 }; 00124 00125 //! Holds string versions of each of the generator's names, handy for debugging so you can output the events as readable strings (you'll find this in EventBase.cc since it can't go in the header or we get multiply-defined errors during linking) 00126 static const char* const EventGeneratorNames[numEGIDs+1]; 00127 00128 //! an event type id is used to denote whether it's the first in a sequence (button down), in a sequence (button still down), or last (button up) 00129 enum EventTypeID_t { 00130 activateETID, //!< Start of an event sequence, e.g. button down 00131 statusETID, //!< Indicates a value has changed, e.g. new sensor readings 00132 deactivateETID, //!< Last of a series of events, e.g. button up 00133 numETIDs //!< the number of different event types 00134 }; 00135 00136 //! holds string versions of EventTypeID_t 00137 static const char* const EventTypeNames[numETIDs+1]; 00138 00139 //! holds abbreviated string versions of EventTypeID_t 00140 static const char* const EventTypeAbbr[numETIDs]; 00141 00142 //! type used for class ids in the type registry (see getTypeRegistry()) 00143 typedef unsigned int classTypeID_t; 00144 00145 //! type used for the type registry 00146 typedef FamilyFactory<EventBase,classTypeID_t> registry_t; 00147 00148 //! returns a FamilyFactory with which you can look up classTypeID_t's to make new instances from serialized data 00149 static registry_t& getTypeRegistry(); 00150 00151 /*! @name Constructors/Destructors */ 00152 //! constructor 00153 /*! @see EventRouter::postEvent() */ 00154 EventBase(); 00155 EventBase(EventGeneratorID_t gid, size_t sid, EventTypeID_t tid, unsigned int dur=0); 00156 EventBase(EventGeneratorID_t gid, size_t sid, EventTypeID_t tid, unsigned int dur, const std::string& n); 00157 EventBase(EventGeneratorID_t gid, size_t sid, EventTypeID_t tid, unsigned int dur, const std::string& n, float mag); 00158 virtual ~EventBase() {} //!< destructor 00159 00160 //! allows a copy to be made of an event, supporting polymorphism 00161 /*! Must be overridden by all subclasses to allow this to happen 00162 * 00163 * I would like to switch this over to the cloneable interface once 00164 * the compiler gets updated out of the 3.3 branch... see 00165 * Cloneable::clone() for a discussion of the issue and 00166 * implementation notes. */ 00167 virtual EventBase* clone() const { return new EventBase(*this); } 00168 //@} 00169 00170 template<typename T> 00171 operator const T&() const { 00172 const T* recastEvent = dynamic_cast<const T*>(this); 00173 if ( recastEvent == NULL ) 00174 std::cerr << "Attempt to dynamic_cast an EventBase to the wrong type\n"; 00175 return *recastEvent; 00176 } 00177 00178 /*! @name Methods */ 00179 virtual const std::string& getName() const { return stim_id; } //!< gets the name of the event - useful for debugging output, see also getDescription() 00180 virtual EventBase& setName(const std::string& n); //!< sets name to a given string, prevents overwriting by generated names 00181 00182 virtual float getMagnitude() const { return magnitude; } //!< gets "strength" of event - by default 1 for activate and status events, 0 for deactivate events 00183 virtual EventBase& setMagnitude(float m) { magnitude=m; return *this; }//!< sets "strength" of event - you may want to override the default values (see getMagnitude()) 00184 00185 virtual unsigned int getTimeStamp() const { return timestamp; } //!< time event was created 00186 virtual void setTimeStamp(unsigned int t) { timestamp=t; } //!< resets time event was created 00187 00188 virtual EventGeneratorID_t getGeneratorID() const { return genID; } /*!< @brief gets the generator ID for this event @see EventGeneratorID_t */ 00189 virtual EventBase& setGeneratorID(EventGeneratorID_t gid) { genID=gid; genName(); return *this; } /*!< @brief sets the generator ID for this event @see EventGeneratorID_t */ 00190 00191 virtual size_t getSourceID() const { return sourceID; } /*!< @brief gets the source ID for this event @see sourceID */ 00192 virtual EventBase& setSourceID(size_t sid) { sourceID=sid; genName(); return *this; } /*!< @brief sets the source ID for this event @see sourceID */ 00193 00194 virtual EventTypeID_t getTypeID() const { return typeID; } /*!< @brief gets the type ID @see EventTypeID_t */ 00195 virtual EventBase& setTypeID(EventTypeID_t tid) { typeID=tid; unsigned int n=strlen(EventTypeAbbr[typeID]); stim_id.replace(stim_id.size()-n-1,n,EventTypeAbbr[typeID]); return *this; } /*!< @brief sets the type ID @see EventTypeID_t */ 00196 00197 00198 virtual int getHostID() const { return hostID; } //!< ID of the host that generated this event (-1U for localhost) 00199 virtual EventBase& setHostID(int host) { hostID = host; genName(); return *this; } //!< sets the ID of the host associated with this event 00200 00201 virtual unsigned int getDuration() const { return duration; } /*!< @brief gets the time since the beginning of this sequence (the timestamp of the activate event) @see duration */ 00202 virtual EventBase& setDuration(unsigned int d) { duration = d; return *this; }/*!< @brief sets the time since the beginning of this sequence (the timestamp of the activate event) @see duration */ 00203 00204 virtual const std::string& resetName() { nameisgen=true; genName(); return stim_id; } //!< resets name to generated form, overwriting any previous name 00205 virtual bool isCustomName() const { return !nameisgen; } //!< returns true if not using the generated name 00206 00207 //! generates a description of the event with variable verbosity 00208 /*! @param showTypeSpecific should be read by subclasses to add additional information 00209 * @param verbosity can be one of the following values: 00210 * - 0 - Basic: <i>event_name</i> \\t <i>generator_id</i> \\t <i>source_id</i> \\t <i>type_id</i> 00211 * - 1 - Numerics: <i>event_name</i> \\t <i>generator_id</i> \\t <i>source_id</i> \\t <i>type_id</i> 00212 * - 2 - Timing: <i>event_name</i> \\t <i>generator_id</i> \\t <i>source_id</i> \\t <i>type_id</i> \\t <i>duration</i> \\t <i>timestamp</i> 00213 * - 3 and above - Full: <i>event_name</i> \\t <i>generator_id</i> \\t <i>source_id</i> \\t <i>type_id</i> \\t <i>duration</i> \\t <i>timestamp</i> \\t <i>magnitude</i> 00214 * if showTypeSpecific, additional fields will be added after the common fields listed above. */ 00215 virtual std::string getDescription(bool showTypeSpecific=true, unsigned int verbosity=0) const; 00216 00217 inline bool operator<(const EventBase& e) const { return timestamp<e.timestamp; } 00218 00219 //! is true if the genID, typeID, and sourceID's all match 00220 virtual bool operator==(const EventBase& eb) const { 00221 return (sourceID==eb.sourceID && genID==eb.genID && typeID==eb.typeID); 00222 } 00223 //!tests to see if events have the same generator and source IDs 00224 bool sameGenSource(const EventBase& eb) const { return genID==eb.genID && sourceID==eb.sourceID; } 00225 00226 bool longerThan(const EventBase& eb) const { return duration>eb.duration && *this==eb; } //!< compares event duration and ensures same event generator, source, and type - useful for event masks 00227 bool shorterThan(const EventBase& eb) const { return duration<eb.duration && *this==eb; }//!< compares event duration and ensures same event generator, source, and type - useful for event masks 00228 bool equalOrLongerThan(const EventBase& eb) const { return duration>=eb.duration && *this==eb; }//!< compares event duration and ensures same event generator, source, and type - useful for event masks 00229 bool equalOrShorterThan(const EventBase& eb) const { return duration<=eb.duration && *this==eb; }//!< compares event duration and ensures same event generator, source, and type - useful for event masks 00230 00231 static bool isValidGeneratorID(unsigned int egid) { return egid<numEGIDs; } 00232 //@} 00233 00234 //! Useful for serializing events to send between processes 00235 /*! @name LoadSave interface */ 00236 00237 //! All subclasses should override this and return a unique ID for their class. 00238 /*! All IDs corresponding to all-capital letters are reserved for future 00239 * framework expansion. (Thus, user subclasses should contain at least one 00240 * lower-case letter.) This code can be used when serializing to allow quick 00241 * identification of the class type by the receiver. */ 00242 virtual classTypeID_t getClassTypeID() const { return autoRegisterEventBase; } 00243 00244 virtual unsigned int getBinSize() const; //!< should return the minimum size needed if using binary format (i.e. not XML) 00245 virtual unsigned int loadBinaryBuffer(const char buf[], unsigned int len); //!< load from binary format 00246 virtual unsigned int saveBinaryBuffer(char buf[], unsigned int len) const; //!< save to binary format 00247 virtual void loadXML(xmlNode* node); //!< load from XML format 00248 virtual void saveXML(xmlNode * node) const; //!< save to XML format 00249 00250 //! no longer need to override this -- will automatically call either loadXML() or loadBinaryBuffer() based on #saveFormat 00251 /*! tries to be smart so if the load based on the current #saveFormat fails, retries with the alternative format */ 00252 virtual unsigned int loadBuffer(const char buf[], unsigned int len, const char* filename=NULL) { 00253 unsigned int test = saveFormat!=BINARY ? XMLLoadSave::loadBuffer(buf,len,filename) : loadBinaryBuffer(buf,len); 00254 if(test!=0) //if the default didn't work, try the other format too... 00255 return test; 00256 return saveFormat!=BINARY ? loadBinaryBuffer(buf,len) : XMLLoadSave::loadBuffer(buf,len,filename); 00257 } 00258 //! no longer need to override this -- will automatically call either saveXML() or saveBinaryBuffer() based on #saveFormat 00259 virtual unsigned int saveBuffer(char buf[], unsigned int len) const { return saveFormat!=BINARY ? XMLLoadSave::saveBuffer(buf,len) : saveBinaryBuffer(buf,len); } 00260 00261 //! automatically calls either XMLLoadSave::loadFile or LoadSave::loadFile based on #saveFormat 00262 /*! tries to be smart so if the load based on the current #saveFormat fails, retries with the alternative format */ 00263 virtual unsigned int loadFile(const char* filename) { 00264 unsigned int test = saveFormat!=BINARY ? XMLLoadSave::loadFile(filename) : LoadSave::loadFile(filename); 00265 if(test!=0) //if the default didn't work, try the other format too... 00266 return test; 00267 return saveFormat!=BINARY ? LoadSave::loadFile(filename) : XMLLoadSave::loadFile(filename); 00268 } 00269 //! automatically calls either XMLLoadSave::saveFile or LoadSave::saveFile based on #saveFormat 00270 virtual unsigned int saveFile(const char* filename) const { return saveFormat!=BINARY ? XMLLoadSave::saveFile(filename) : LoadSave::saveFile(filename); } 00271 00272 //! automatically calls either XMLLoadSave::loadFileStream or LoadSave::loadFileStream based on #saveFormat 00273 virtual unsigned int loadFileStream(FILE* f, const char* filename=NULL) { return saveFormat!=BINARY ? XMLLoadSave::loadFileStream(f,filename) : LoadSave::loadFileStream(f,filename); } 00274 //! automatically calls either XMLLoadSave::loadFileStream or LoadSave::loadFileStream based on #saveFormat 00275 virtual unsigned int saveFileStream(FILE* f) const { return saveFormat!=BINARY ? XMLLoadSave::saveFileStream(f) : LoadSave::saveFileStream(f); } 00276 00277 //! values to pass to setSaveFormat() 00278 enum SaveFormat { 00279 BINARY, //!< saves will be in packed binary, loads will try binary first 00280 XML //!< saves will be in xml, loads will try xml first 00281 }; 00282 virtual void setSaveFormat(SaveFormat sf) const { saveFormat=sf; } //!< set #saveFormat 00283 virtual SaveFormat getSaveFormat() const { return saveFormat; } //!< return #saveFormat 00284 //@} 00285 00286 protected: 00287 //! converts the first 4 characters of @a str to an unsigned int, should ensure consistent byte ordering across platforms 00288 static unsigned int makeClassTypeID(const char* str) { 00289 #if LOADSAVE_SWAPBYTES 00290 unsigned int x; 00291 byteswap(x,*reinterpret_cast<const unsigned int*>(str)); 00292 return x; 00293 #else 00294 return *reinterpret_cast<const unsigned int*>(str); 00295 #endif 00296 } 00297 00298 std::string stim_id; //!< the name of the event, use the same name consistently or else will be seen as different stimuli 00299 float magnitude; //!< the current "strength" of the event/stimuli... MAKE SURE this gets set to ZERO IF event is DEACTIVATE 00300 unsigned int timestamp; //!< the time the event was created - set automatically by constructor 00301 00302 mutable SaveFormat saveFormat; //!< controls the format used during the next call to saveBuffer() (packed binary or XML) 00303 00304 bool nameisgen; //!< tracks whether the current name (stim_id) was generated by genName() (true) or setName() (false) 00305 virtual void genName(); //!< calls setName() with a string version of sourceID, decimal notation 00306 00307 EventGeneratorID_t genID; //!< generator ID, see EventGeneratorID_t 00308 EventTypeID_t typeID; //!< type ID, see EventTypeID_t 00309 size_t sourceID; /*!< @brief the source ID for this event 00310 * Source IDs are defined by the generator that made it. This should 00311 * give authors flexibility to design their modules without having to 00312 * worry about ID space collision */ 00313 int hostID; 00314 unsigned int duration; /*!< @brief the time since this sequence started (like, how long the 00315 * button has been pressed); not all generators will set this; 00316 * Typically, this would be 0 for activate, 00317 * (activate.timestamp-::get_time()) for status and deactivate */ 00318 00319 //! causes class type id to automatically be regsitered with EventBase's FamilyFactory (getTypeRegistry()) 00320 static const EventBase::classTypeID_t autoRegisterEventBase; 00321 }; 00322 00323 /*! @file 00324 * @brief Describes EventBase, the basic class for sending events around the system 00325 * @author ejt (Creator) 00326 */ 00327 00328 #endif |
|
Tekkotsu v5.1CVS |
Generated Tue Jan 31 04:31:50 2012 by Doxygen 1.6.3 |