/* Classe d'objets avec une methode (DoPeriodic()) appelee periodiquement */
/*                                  Eric Aubourg     04/96                */
/*                                  Reza Ansari      06/96                */
/*                      LAL/IN2P3 (Orsay)  DAPNIA/CEA (Saclay)            */

#include "sopnamsp.h"
#include "machdefs.h"
#include <signal.h>
#include <unistd.h>

#include "periodic.h"
/*!
  \class SOPHYA::Periodic
  \ingroup SysTools
  This class provide the service of calling a given function at 
  regular intervals. It can also be subclassed with the method 
  \b DoPeriodic redefined to perform a periodic action.
*/

//++
// Class	Periodic
// Lib		SysTools
// include	periodic.h
//
//	Classe d'objets avec une méthode "DoPeriodic()" appelée périodiquement
//	à intervalle régulier.
//--

//++
// Titre	Constructeur, méthodes
//--
//++
//  Periodic(int dt, UsPeriodicAction act=NULL, void * usp=NULL)
//	Constructeur avec un intervalle de temps en secondes "dt".
//	Il est possible de spécifier une fonction "UsPeriodicAction" avec
//	un argument "usp" qui sera appelée régulierement.
//|	typedef void (* UsPeriodicAction) (void *);
//--

//++
//  void  Start(int dt=-1)
//	Activation de l'objet périodique. 
//	On peut spécifier l'intervalle en temps (en secondes) si "dt > 0".
//  void  Stop()
//	Désactivation de l'objet périodique.
//  void  DoPeriodic()
//	Méthode virtuelle qui peut être surchargée dans les classes dérivées.
//	Elle est appelée lorsque l'intervalle de temps est écoulé. Par défaut,
//	elle appelle la fonction "UsPeriodicAction" associée si "!= NULL"
//--


/* --Methode-- */
/*! Constructor with the definition of the interval (in seconds)
  and an optional user action */
Periodic::Periodic(int dt, UsPeriodicAction act, void * usp)
{
mDt = dt;
mAct = act;
mUsp = usp;
if (mDt < 1)  mDt = 5;
mDtms = 1000*mDt;
mFgact = false;
it = -1;
}

/* --Methode-- */
Periodic::~Periodic()
{
if (mFgact) Stop();
}

/* --Methode-- */
/*! Sets the user action function and its data \c usp */
void Periodic::SetAction(UsPeriodicAction act, void * usp)
{
mAct = act;
mUsp = usp;
}

/* --Methode-- */
/*! Changes the interval \c dt specified in seconds */
void Periodic::SetInterval(int dt)
{
mDt = dt;
if (mDt < 1)  mDt = 5;
mDtms = 1000*mDt;
}

/* --Methode-- */
/*! Changes the interval \c dtms specified in milli-seconds */
void Periodic::SetIntervalms(int dtms)
{
if (dtms < 1) dtms = 5;
mDtms = dtms;
mDt = mDtms/1000;
if (mDt < 1)  mDt = 1;
}

/* --Methode-- */
/*! Activate the object */
void Periodic::Start(int dt)
{
if (mFgact)  return;
if (dt > 0)  { mDt = dt;  mDtms = dt*1000; }
if (!actifs) actifs = new PeriodicList;
it = 0;
if (actifs->size() == 0) {
  signal(SIGALRM, CallBack);
  alarm(1);
  }
actifs->push_back(this);
mFgact = true;
return;
}

/* --Methode-- */
/*! Deactivate the object */
void Periodic::Stop()
{
if (!mFgact)  return;

//remove(actifs->begin(), actifs->end(), this);  // $CHECK$ - Reza
actifs->remove(this);    // $CHECK$ 
if (actifs->size() == 0) {
  signal(SIGALRM, SIG_IGN);
  alarm(0);
  delete actifs;
  actifs = NULL;
  }
it = -1;  mFgact = false;
return;
}

/* --Methode-- */
/*! This method should be redefined for sub-classes of Periodic 
  The default implementation calls the user action function */
void Periodic::DoPeriodic()
{
if (mAct)   mAct(mUsp);
}

PeriodicList* Periodic::actifs = NULL;

void Periodic::CallBack(int)
{
  if (!actifs) return;

  for (PeriodicList::iterator i = actifs->begin(); i != actifs->end(); i++) {
    Periodic* p = (Periodic*) *i;
    p->it ++;
    if (p->it >= p->mDt) {
      p->it = 0;
      p->DoPeriodic();
    }
  }
  alarm(1);
}

