blob: 0e2d790299a5c5b1fa6be74f244e20d41ebd1f4c [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2005 - 2015 ACIN, Profactor GmbH, fortiss GmbH
* 2020 Johannes Kepler University Linz
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Alois Zoitl, Rene Smodic, Thomas Strasser, Ingo Hegny
* - initial API and implementation and/or initial documentation
* Alois Zoitl - worked on reducing the jitter and overhead of timer handler
* Bug #568902
*******************************************************************************/
#include "timerha.h"
#include "../core/datatypes/forte_time.h"
#include "../core/devexec.h"
#include "../core/esfb.h"
#include "../core/utils/criticalregion.h"
#include <algorithm>
#include <functional>
DEFINE_HANDLER(CTimerHandler)
CTimerHandler::CTimerHandler(CDeviceExecution& paDeviceExecution) : CExternalEventHandler(paDeviceExecution),
mForteTime(0), mTimedFBList(nullptr), mAddFBList(nullptr){
}
CTimerHandler::~CTimerHandler(){
}
void CTimerHandler::registerTimedFB(STimedFBListEntry *paTimerListEntry, const CIEC_TIME &paTimeInterval) {
//calculate the correct interval based on time-base and timer ticks per seconds
paTimerListEntry->mInterval = static_cast<TForteUInt32>((paTimeInterval * getTicksPerSecond()) / cgForteTimeBaseUnitsPerSecond);
// Correct null intervals that can lead to event queue overflow to at least 1 timer tick
if(0 == paTimerListEntry->mInterval) {
paTimerListEntry->mInterval = 1;
}
// set the first next activation time right here to reduce jitter, see Bug #568902 for details
paTimerListEntry->mTimeOut = mForteTime + paTimerListEntry->mInterval;
{
CCriticalRegion criticalRegion(mAddListSync);
paTimerListEntry->mNext = mAddFBList;
mAddFBList = paTimerListEntry;
}
}
void CTimerHandler::addTimedFBEntry(STimedFBListEntry *paTimerListEntry) {
paTimerListEntry->mNext = 0;
if (0 == mTimedFBList) {
mTimedFBList = paTimerListEntry;
} else {
if (mTimedFBList->mTimeOut > paTimerListEntry->mTimeOut) {
paTimerListEntry->mNext = mTimedFBList;
mTimedFBList = paTimerListEntry;
} else {
STimedFBListEntry *runner = mTimedFBList;
while (0 != runner->mNext) {
if (runner->mNext->mTimeOut > paTimerListEntry->mTimeOut) {
paTimerListEntry->mNext = runner->mNext;
runner->mNext = paTimerListEntry;
break;
}
runner = runner->mNext;
}
runner->mNext = paTimerListEntry;
}
}
}
void CTimerHandler::unregisterTimedFB(CEventSourceFB *paTimedFB) {
CCriticalRegion criticalRegion(mRemoveListSync);
mRemoveFBList.push_back(paTimedFB);
}
void CTimerHandler::removeTimedFB(CEventSourceFB *paTimedFB) {
if (0 != mTimedFBList) {
STimedFBListEntry *buffer = 0;
if (mTimedFBList->mTimedFB == paTimedFB) {
buffer = mTimedFBList;
mTimedFBList = mTimedFBList->mNext;
buffer->mNext = 0;
buffer->mTimeOut = 0;
} else {
STimedFBListEntry *runner = mTimedFBList;
while (0 != runner->mNext) {
if (runner->mNext->mTimedFB == paTimedFB) {
buffer = runner->mNext;
runner->mNext = runner->mNext->mNext;
buffer->mNext = 0;
buffer->mTimeOut = 0;
break;
}
runner = runner->mNext;
}
}
}
}
void CTimerHandler::nextTick(void) {
++mForteTime;
mDeviceExecution.notifyTime(mForteTime); //notify the device execution that one tick passed by.
if(!mRemoveFBList.empty()){
processRemoveList();
}
processTimedFBList();
if(0 != mAddFBList){
processAddList();
}
}
void CTimerHandler::processTimedFBList(){
while (0 != mTimedFBList) {
if (mTimedFBList->mTimeOut > mForteTime) {
break;
}
STimedFBListEntry *buffer = mTimedFBList;
mTimedFBList = buffer->mNext; //remove buffer from the list
triggerTimedFB(buffer);
}
}
void CTimerHandler::triggerTimedFB(STimedFBListEntry *paTimerListEntry){
mDeviceExecution.startNewEventChain(paTimerListEntry->mTimedFB);
switch (paTimerListEntry->mType) {
case e_Periodic:
paTimerListEntry->mTimeOut = mForteTime + paTimerListEntry->mInterval; // the next activation time of this FB
addTimedFBEntry(paTimerListEntry); //re-register the timed FB
break;
case e_SingleShot:
// nothing special is to do up to now, therefore go to default
default:
paTimerListEntry->mNext = 0;
paTimerListEntry->mTimeOut = 0;
break;
}
}
void CTimerHandler::processAddList(){
CCriticalRegion criticalRegion(mAddListSync);
while(0 != mAddFBList){
STimedFBListEntry *buffer = mAddFBList;
mAddFBList = buffer->mNext; //remove buffer from the list
if(buffer->mTimeOut < mForteTime){
// the time already passed trigger the fb
triggerTimedFB(buffer);
}
else{
addTimedFBEntry(buffer);
}
}
}
void CTimerHandler::processRemoveList(){
CCriticalRegion criticalRegion(mRemoveListSync);
std::for_each(mRemoveFBList.begin(), mRemoveFBList.end(), std::bind1st(std::mem_fun(&CTimerHandler::removeTimedFB), this));
mRemoveFBList.clear();
}