00001 // Copyright CERN 2012 - Developed in collaboration with GSI 00002 00003 #include <fesa-core/RealTime/TimerEventSource.h> 00004 00005 #include <fesa-core/RealTime/RTEvent.h> 00006 #include <fesa-core/Synchronization/NoneContext.h> 00007 #include <fesa-core/Utilities/ParserElements.h> 00008 #include <fesa-core/Utilities/ParserElements/TimerEventElement.h> 00009 #include <fesa-core/Utilities/ParserElements/ParserElementDefs.h> 00010 #include <sstream> 00011 00012 #include <cmw-log/Logger.h> 00013 00014 #include <climits> 00015 #include <cerrno> 00016 #include <ostream> 00017 00018 namespace 00019 { 00020 CMW::Log::Logger& logger = CMW::Log::LoggerFactory::getLogger("FESA.FWK.fesa-core.RealTime.TimerEventSource"); 00021 } 00022 00023 namespace fesa 00024 { 00025 /* 00026 * \class TimerEventSource 00027 * \brief TimerEventSource is a particular event-source which requires a dedicated source 00028 * per concrete-event. This choice greatly simplifies the multiple period handling 00029 * by instantiating one thread per timer period, means per timer concrete-event. 00030 * This specific implementation is under responsability of the TimerEventSource class. 00031 * From outside point of view, only one Timer event-source is defined and accessible. 00032 * TIMER_EVENT_SOURCE_NAME/ DEFAULT_EVENT_SOURCE_KEY 00033 * 00034 * This mechanism requires to override the addRTScheduler method in order to register 00035 * the scheduler within the appropriate occurence of the timer event-source. 00036 */ 00037 00038 TimerEventSource::TimerEventSource() : 00039 AbstractEventSource(TIMER_EVENT_SOURCE_NAME, TimerSource), 00040 totalDelay_(0) 00041 { 00042 } 00043 00044 TimerEventSource::~TimerEventSource() 00045 { 00046 } 00047 00048 void TimerEventSource::connect(boost::shared_ptr<fesa::EventElement>& eventElement) 00049 { 00050 int32_t period; 00051 std::stringstream converterStream(eventElement->getSourceTypeSpecificData(TIMER_TAG_PERIOD)); 00052 converterStream >> period; 00053 00054 if (period == 0) 00055 { 00056 throw FesaBadParameterException(__FILE__, __LINE__, FesaErrorWrongTimerPeriod.c_str(), eventElement->getLogicalName().c_str()); 00057 } 00058 //add info to the vectors 00059 eventNamesCol_.push_back(eventElement->getConcreteName()); 00060 eventDelaysCol_.push_back(period); 00061 eventTotalDelaysCol_.push_back(period); 00062 eventsFired_.push_back(0); 00063 } 00064 00065 RTEvent* TimerEventSource::wait() 00066 { 00067 if (eventsToFire_.size() > 0) 00068 { 00069 return fireNextEvent(); 00070 } 00071 struct timespec thePeriod; 00072 00073 int64_t minimum = LLONG_MAX; 00074 int64_t period = 0; 00075 // find the closest event to fire 00076 for (uint32_t i = 0; i < eventTotalDelaysCol_.size(); i++) 00077 { 00078 if ((eventTotalDelaysCol_[i] - totalDelay_) < minimum) 00079 { 00080 minimum = eventTotalDelaysCol_[i] - totalDelay_; 00081 period = eventTotalDelaysCol_[i] - totalDelay_; 00082 00083 while (eventsToFire_.size() > 0) 00084 { 00085 eventsToFire_.pop_front(); 00086 } 00087 00088 eventsToFire_.push_back(i); 00089 } 00090 } 00091 00092 // update global table 00093 totalDelay_ += period; 00094 for (uint32_t i = 0; i < eventsToFire_.size(); i++) 00095 { 00096 eventTotalDelaysCol_[eventsToFire_[i]] += eventDelaysCol_[eventsToFire_[i]]; 00097 } 00098 00099 int32_t sec = static_cast<int32_t>(period) / 1000; // number of seconds since period is expressed in ms 00100 int32_t nsec = 0; 00101 if ((nsec = static_cast<int32_t>(period) % 1000)) // check remainder 00102 { 00103 nsec *= (int32_t) 1E6; // convert remainder into ns 00104 } 00105 thePeriod.tv_sec = sec; 00106 thePeriod.tv_nsec = nsec; 00107 int32_t ret; 00108 do 00109 { 00110 ret = nanosleep(&thePeriod, &thePeriod); 00111 } 00112 while ((ret == -1) && (errno == EINTR)); // check if interrupted by a signal 00113 00114 return fireNextEvent(); 00115 } 00116 00117 RTEvent* TimerEventSource::fireNextEvent() 00118 { 00119 int32_t eventIndex = eventsToFire_.front(); 00120 eventsToFire_.pop_front(); 00121 RTEvent* rtEvt = new RTEvent(eventNamesCol_[eventIndex], new NoneContext(), this); 00122 eventsFired_[eventIndex]++; 00123 if (logger.isLoggable(CMW::Log::Level::LL_TRACE)) 00124 { 00125 std::ostringstream msg; 00126 msg << "firing event " << eventNamesCol_[eventIndex]; 00127 LOG_TRACE_IF(logger, msg.str()); 00128 } 00129 return rtEvt; 00130 } 00131 00132 } // fesa