Tekkotsu Homepage
Demos
Overview
Downloads
Dev. Resources
Reference
Credits

SharedGlobals.h

Go to the documentation of this file.
00001 //-*-c++-*-
00002 #ifndef INCLUDED_CLASSNAME_h_
00003 #define INCLUDED_CLASSNAME_h_
00004 
00005 #include "IPC/MutexLock.h"
00006 #include "IPC/SemaphoreManager.h"
00007 #include "IPC/ProcessID.h"
00008 #include "Shared/plist.h"
00009 #include "Shared/TimeET.h"
00010 #include "Shared/RobotInfo.h"
00011 #include "local/DataSource.h"
00012 
00013 //! A class to hold various simulator parameters which need to be accessed from multiple processes
00014 class SharedGlobals {
00015 public:
00016   //! constructor
00017   SharedGlobals()
00018     : waitForSensors(false), simulatorTime(0), timeScale(1), motion(), sensors(), vision(), lock(), sensorState(), 
00019     nextTimer(-1U), nextMotion(-1U), nextSensorUpdate(-1U), bootTime(), timeOffset(0), lastTimeScale(0), autoPauseTime(-1U),
00020       semgr(2), running(semgr.getSemaphore()), sensorValid(semgr.getSemaphore())
00021   {
00022     sensorState.motionOverride = &motion.override;
00023     for(unsigned int i=0; i<NUM_RUNLEVELS; i++)
00024       level_count[i]=0;
00025     semgr.raise(running,1);
00026     semgr.raise(sensorValid,1);
00027   }
00028   //! destructor
00029   ~SharedGlobals() {
00030     semgr.releaseSemaphore(running);
00031     semgr.releaseSemaphore(sensorValid);
00032   }
00033   
00034   //       ****************
00035   //!@name Startup Control
00036   //       ****************
00037   
00038   //! Controls whether to wait for initial sensor readings before triggering the startup behavior or starting the motion polling thread.
00039   /*! This can avoid jumping to the 0-point on simulator launch.  Changes after initial launch are ignored. */
00040   plist::Primitive<bool> waitForSensors;
00041   
00042   //! Called by Main when the first sensorEGID event is generated.
00043   /*! When the waitForSensors setting is enabled, the startupBehavior is not activated until this occurs */
00044   void signalHaveSensors() {
00045     semgr.lower(sensorValid,1,false);
00046   }
00047   //! test to see if the initial sensor event has been generated
00048   bool haveSensors() const {
00049     return semgr.testZero(sensorValid,false);
00050   }
00051   //! blocks until the initial sensor event has been generated
00052   void waitSensors() {
00053     semgr.testZero(sensorValid,true);
00054   }
00055   
00056   //@}
00057   
00058   
00059   //       ****************
00060   //!@name Shutdown Control
00061   //       ****************
00062 
00063   //! call this to cause "system shutdown" -- clean halt of the simulator (not actually the host system)
00064   void signalShutdown() {
00065     semgr.setValue(running,0);
00066     if(waitForSensors && !haveSensors())
00067       signalHaveSensors(); // break out of deadlock in Main and Motion if we were waiting for the first sensor
00068   }
00069   //! test to see if the shutdown flag has been set (non-blocking)
00070   bool isShutdown() const {
00071     return semgr.testZero(running,false);
00072   }
00073   //! blocks until shutdown flag has been set
00074   bool waitShutdown() {
00075     return semgr.testZero(running,true);
00076   }
00077   
00078   //! access to #semgr, returns SemaphoreManager::hadFault()
00079   bool hadFault() const { return semgr.hadFault(); }
00080   
00081   //! access to #semgr's SemaphoreManager::faultShutdown() -- call this *after* a fault has occured from the signal handler; doesn't signal the fault itself
00082   void faultShutdown() { semgr.faultShutdown(); }
00083   
00084   //@}
00085 
00086 
00087   //       ************
00088   //!@name Time Control
00089   //       ************
00090 
00091   //! returns the current simulator time, in milliseconds since startup
00092   /*! the simulator should set project_get_time::get_time_callback to call this,
00093    *  so calls to ::get_time() will be forwarded here.  That wall all processes
00094    *  will share the same time */
00095   unsigned int get_time();
00096   
00097   //! returns the current simulator #timeScale (speed factor), as a ratio of real time (e.g. '2' means simulation is running two times wall clock)
00098   /*! the simulator should set project_get_time::get_timeScale_callback to call this,
00099    *  so calls to ::getTimeScale() will be forwarded here. */
00100   float getTimeScale() const;
00101   
00102   //! the current time within the simulation, only applicable when timeScale is negative (non-realtime)
00103   unsigned int simulatorTime;
00104   
00105   //! Controls the speed at which time from get_time() will move
00106   /*! You can use this to pretend your hardware is faster or slower
00107    *  than it actually is.  For instance, a value of .5 means time
00108    *  will move at half speed (pretending your hardware is twice as
00109    *  fast)  This can be useful for "slow motion" analysis, or you
00110    *  can speed up time to simulate a more processor-restrictive platform.
00111    *
00112    *  Negative values indicate full-speed processing -- time will be
00113    *  incremented only as quickly as it can be without dropping any
00114    *  video or sensor frames. (may be faster or slower than realtime)
00115    *  in this case, #simulatorTime is used by calls to get_time()
00116    *
00117    *  A value of zero halts time. */
00118   plist::Primitive<double> timeScale;
00119 
00120   //! sets #autoPauseTime
00121   void setAutoPauseTime(unsigned int t) { autoPauseTime=t; }
00122   //! returns #autoPauseTime
00123   unsigned int getAutoPauseTime() const { return autoPauseTime; }
00124   
00125   //@}
00126 
00127 
00128   //       **********************
00129   //!@name Runlevel Communication
00130   //       **********************
00131 
00132   //! defines the runlevels that each process passes through; runlevels should monotonically increase (can't go backwards)
00133   enum runlevel_t {
00134     CREATED=0,    //!< corresponding element of #level_count is incremented prior to each fork -- not strictly a runlevel per se
00135     CONSTRUCTING, //!< currently initializing
00136     STARTING,     //!< setting up shared memory regions with other processes
00137     RUNNING,      //!< full activity, stay here until the #running semaphore is set to 0
00138     STOPPING,     //!< dereferencing shared regions, waiting for threads to finish
00139     DESTRUCTING,  //!< destructors are in progress
00140     DESTRUCTED,   //!< destruction has completed, corresponding element of #level_count is incremented immediately prior to process completion
00141   };
00142   static const unsigned int NUM_RUNLEVELS=DESTRUCTED+1; //!< symbolic access to the total number of runlevel stages
00143 
00144   //! string versions of runlevel_t for runtime user-feedback
00145   static const char * const runlevel_names[NUM_RUNLEVELS+1];
00146 
00147   //! a count of the number of processes which have passed through each runlevel
00148   unsigned int level_count[NUM_RUNLEVELS];
00149 
00150   //@}
00151 
00152 
00153   //       **********************
00154   //!@name Configuration Parameters
00155   //       **********************
00156 
00157   //! holds configuration parameters for the motion process
00158   class MotionSimConfig : public virtual plist::Dictionary {
00159   public:
00160     //! constructor
00161     MotionSimConfig() : plist::Dictionary(), verbose(1), feedbackDelay(0),
00162       zeroPIDFeedback(false), /*speedLimit(0),*/ feedbackRangeLimits(true), override(false), startPose(), frameNumber(-1U)
00163     {
00164       setLoadSavePolicy(FIXED,SYNC);
00165       addEntry("Verbose",verbose,"Report whenever motion commands are being processed or joints are updated\n0 - nothing, 1 - errors, 2 - warnings (e.g. dropped frames), 3 - notification every frame");
00166       addEntry("FeedbackDelay",feedbackDelay,"Delay (in milliseconds) to apply to motion output before feeding back to sensor values (simulates (very roughly) inertia and system response time); 0 indicates instantaneous/perfect joint control, negative values indicate no feedback (only sensor data sets joint positions)");
00167       addEntry("ZeroPIDFeedback",zeroPIDFeedback,"When set to false, if PIDs are set to zero, then sensor values are used to set joint positions; otherwise joint position sensors would only be used if FeedbackDelay is negative");
00168       //addEntry("EnforceSpeedLimit",speedLimit,"The simulated motion of joints is limited to this factor of model's recommended speed limits.  0 (or negative) disables speed limit altogether.");
00169       addEntry("FeedbackRangeLimits",feedbackRangeLimits,"If true, feedback will be limited to the RobotInfo::mechnicalLimits values");
00170       addEntry("OverrideSensors",override,"Allows motion feedback to override position values from sensor data loaded from disk.\nIf false, feedback is only provided when no other sensor data is being provided");
00171       addEntry("StartPose",startPose,"Name of a posture file to load as the initial output values before MotionHooks or Behaviors are activated (if empty, everything will be 0)");
00172     }
00173     plist::Primitive<int> verbose; //!< Report whenever motion commands are being processed or joints are updated; 0 - nothing, 1 - errors, 2 - warnings (e.g. dropped frames), 3 - notification every frame
00174     plist::Primitive<int> feedbackDelay; //!< Delay (in milliseconds) to apply to motion output before feeding back to sensor values (simulates (very roughly) inertia and system response time); 0 indicates instantaneous/perfect joint control, negative values indicate no feedback (only sensor data sets joint positions)
00175     plist::Primitive<bool> zeroPIDFeedback; //!< When set to false, if PIDs are set to zero, then sensor values are used to set joint positions; otherwise joint position sensors would only be used if FeedbackDelay is negative
00176     //plist::Primitive<float> speedLimit; //!< The simulated motion of joints is limited to this factor of model's recommended speed limits.  0 (or negative) disables speed limit altogether.
00177     plist::Primitive<bool> feedbackRangeLimits; //!< If true, feedback will be limited to the RobotInfo::mechnicalLimits values
00178     plist::Primitive<bool> override; //!< Allows motion feedback to override position values from sensor data loaded from disk; if false, feedback is only provided when no other sensor data is being provided
00179     plist::Primitive<std::string> startPose; //!< Name of a posture file to load as the initial output values before MotionHooks or Behaviors are activated (if empty, everything will be 0)
00180     unsigned int frameNumber; //!< a monotonically increasing count of the number of sensor frames which have been "completed".  Needed to allow coordination between sensor loading from disk and feedback from motion.  Count is increased by the simulator process, which will send a heartbeat message over Simulator::sensorQueue when it does so.
00181   } motion;
00182   
00183   class StreamSimConfig : public virtual plist::Dictionary {
00184   public:
00185     StreamSimConfig() : plist::Dictionary(),
00186       framerate(1000.f/(FrameTime*NumFrames)), verbose(0), heartbeat(false), sources()
00187     {
00188       addEntry("Framerate",framerate,"The rate at which data should be loaded.  This is a hint to the hardware devices, which generally use their 'native' framerate, but may use this to limit data flow.");
00189       addEntry("Verbose",verbose,"Controls how much feedback to give on the console regarding progress\n  0 - none\n  1 - report when frames are dropped\n  2 - also report when a frame is sent\n  3 - also report when heartbeat is sent/dropped\n  4 - also report when each frame is received and processed");
00190       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.");
00191       addEntry("Sources",sources,"Indicates which data sources should be activated at launch");
00192       setLoadSavePolicy(FIXED,SYNC);
00193     }
00194     
00195     //! 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
00196     plist::Primitive<float> framerate;
00197     
00198     //! Controls how much feedback to give on the console regarding progress
00199     /*! 0 - none\n
00200      *  1 - report when frames are dropped\n
00201      *  2 - also report when a frame is sent\n
00202      *  3 - also report when heartbeat is sent/dropped\n
00203      *  4 - also report when each frame is received and processed */
00204      plist::Primitive<int> verbose;
00205     
00206     //! 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.
00207     plist::Primitive<bool> heartbeat;
00208 
00209     //! list of names of DataSources which are to be activated at launch
00210     plist::ArrayOf<plist::Primitive<std::string> > sources;
00211   };
00212   StreamSimConfig sensors;
00213   StreamSimConfig vision;
00214   
00215   //@}
00216   
00217   //! allows mutually exclusive access to the fields of SharedObject
00218   MutexLock<ProcessID::NumProcesses> lock;
00219 
00220   //! holds the host system's process ID for each simulator process
00221   pid_t pids[ProcessID::NumProcesses];
00222 
00223   //! maximum storage size of strings in #processNames
00224   static const unsigned int MAX_PROCESS_NAME_LEN=32;
00225 
00226   //! each process should set a string version of its name for user feedback
00227   char processNames[ProcessID::NumProcesses][MAX_PROCESS_NAME_LEN];
00228   
00229   bool setNextTimer(unsigned int t) { if(nextTimer==t) return false; nextTimer=t; return true; } //!< sets #nextTimer, returns true if the new value differs from previous value
00230   unsigned int getNextTimer() { return nextTimer; } //!< gets #nextTimer
00231   
00232   void setNextMotion(unsigned int t) { nextMotion=t; } //!< sets #nextMotion
00233   unsigned int getNextMotion() { return nextMotion; } //!< gets #nextMotion
00234   
00235   void setNextSensorUpdate(unsigned int t) { nextSensorUpdate=t; } //!< sets #nextSensorUpdate
00236   unsigned int getNextSensorUpdate() { return nextSensorUpdate; } //!< gets #nextSensorUpdate
00237   
00238   void resetBootTime() { timeOffset=bootTime.Age().Value()*timeScale*1000; simulatorTime=0; }
00239   
00240   SensorState sensorState;
00241   
00242 protected:
00243   //! this returns time since boot (#bootTime), scaled by @a scale, relative to #timeOffset
00244   unsigned int get_real_time(double scale) const {
00245     return static_cast<unsigned int>(bootTime.Age().Value()*scale*1000-timeOffset);
00246   }
00247 
00248   //! set by setNextTimer, called with the current value of EventRouter::getNextTimer() after each user code section, indicates time of next timer event
00249   unsigned int nextTimer;
00250   
00251   //! updated by Motion process after each motion update
00252   unsigned int nextMotion;
00253   
00254   //! updated by Main process after each sensor update
00255   unsigned int nextSensorUpdate;
00256   
00257   //! real time since simulator startup (or, at least, since SharedGlobals was constructed... close enough)
00258   TimeET bootTime; 
00259 
00260   //! the scaled value of #bootTime at which isRealTime was last activated, allows you to start and stop realtime fluidly
00261   double timeOffset; 
00262   
00263   //! updated by each call to get_time(), if timeScale differs, allows timeOffset to be updated fluidly
00264   double lastTimeScale;
00265   
00266   //! if simulatorTime is about to move past this value, timeScale is set to 0 instead, and simulatorTime is set to this
00267   unsigned int autoPauseTime;
00268   
00269   SemaphoreManager semgr; //!< a semaphore set, only used for #running and #sensorValid
00270   SemaphoreManager::semid_t running; //!< the semaphore within #semgr to communicate shutdown status between processes -- when the semaphore is set to 0, shutdown is requested
00271   SemaphoreManager::semid_t sensorValid; //!< the semaphore within #semgr to notify processes when the first sensor frame is available
00272 };
00273 
00274 const unsigned int MAX_SUBJECTS=50; //!< maximum number of message queues the simulator can maintain
00275 const unsigned int MAX_SUBJECT_NAME=50; //!< maximum storage capacity of subject names
00276 
00277 // just a forward definition of RegionRegistry
00278 template<unsigned int MAX_SUBJECTS, unsigned int MAX_SUBJECT_NAME> class RegionRegistry;
00279 //! the type to use for the inter-process communication registry
00280 typedef RegionRegistry<MAX_SUBJECTS,MAX_SUBJECT_NAME> ipc_setup_t;
00281 
00282 extern ipc_setup_t * ipc_setup; //!< a global pointer to the inter-process message queue registry (a RegionRegistry)
00283 extern SharedGlobals * globals; //!< a global pointer to the SharedGlobals instance
00284 extern float getTimeScale(); //!< a prototype for accessing current time scale without referencing ::globals directly
00285 
00286 /*! @file
00287  * @brief A class to hold various simulator parameters which need to be accessed from multiple processes
00288  * @author ejt (Creator)
00289  */
00290 
00291 #endif

Tekkotsu Hardware Abstraction Layer 5.1CVS
Generated Mon May 9 05:01:39 2016 by Doxygen 1.6.3