forked from rosh/MQL5Book
253 lines
6.8 KiB
MQL5
253 lines
6.8 KiB
MQL5
//+------------------------------------------------------------------+
| |
//| MultiTimer.mqh |
| |
//| Copyright © 2016-2021, Marketeer |
| |
//| https://www.mql5.com/en/users/marketeer |
| |
//| Multiple timers with publisher/subscriber design pattern |
| |
//+------------------------------------------------------------------+
| |
| |
//+------------------------------------------------------------------+
| |
//| Base 'interface' for processing timer events |
| |
//| "Subscriber" |
| |
//| (abstract, can not be instantiated) |
| |
//+------------------------------------------------------------------+
| |
class TimerNotification
| |
{
| |
protected:
| |
int chronometer;
| |
| |
public:
| |
TimerNotification(): chronometer(0)
| |
{
| |
}
| |
| |
// timer event
| |
// pure virtual method, must be implemented in descendants
| |
virtual void notify() = 0;
| |
| |
// return current timer period (can be changed on the go)
| |
// pure virtual method, must be implemented in descendants
| |
virtual int getInterval() = 0;
| |
| |
// check if timer should fire now
| |
virtual bool isTimeCome()
| |
{
| |
if(chronometer >= getInterval() - 1)
| |
{
| |
chronometer = 0;
| |
notify();
| |
return true;
| |
}
| |
| |
++chronometer;
| |
return false;
| |
}
| |
};
| |
| |
//+------------------------------------------------------------------+
| |
//| Main and single processor of timer events coming from terminal |
| |
//| "Publisher" |
| |
//+------------------------------------------------------------------+
| |
class MultiTimer
| |
{
| |
protected:
| |
TimerNotification *subscribers[];
| |
bool enabled;
| |
| |
// single instance of main timer
| |
static MultiTimer _mainTimer;
| |
| |
// protected constructor prevents creation of more instances than our static one
| |
MultiTimer(): enabled(true)
| |
{
| |
}
| |
| |
// enable/disable all notifications of subscribers
| |
void setEnabled(const bool b)
| |
{
| |
enabled = b;
| |
}
| |
| |
// method to be called from global OnTimer event handler
| |
void checkTimers()
| |
{
| |
if(!enabled) return;
| |
int n = ArraySize(subscribers);
| |
for(int i = 0; i < n; ++i)
| |
{
| |
if(CheckPointer(subscribers[i]) != POINTER_INVALID)
| |
{
| |
subscribers[i].isTimeCome();
| |
}
| |
}
| |
}
| |
| |
public:
| |
// attach new subscriber to get timer notifications
| |
void bind(TimerNotification &tn)
| |
{
| |
int i, n = ArraySize(subscribers);
| |
for(i = 0; i < n; ++i)
| |
{
| |
if(subscribers[i] == &tn) return;
| |
if(subscribers[i] == NULL) break;
| |
}
| |
| |
if(i == n)
| |
{
| |
ArrayResize(subscribers, n + 1);
| |
}
| |
else
| |
{
| |
n = i;
| |
}
| |
subscribers[n] = &tn;
| |
}
| |
| |
// remove subscriber from timer notifications list
| |
void unbind(TimerNotification &tn)
| |
{
| |
const int n = ArraySize(subscribers);
| |
for(int i = 0; i < n; ++i)
| |
{
| |
if(subscribers[i] == &tn)
| |
{
| |
subscribers[i] = NULL;
| |
return;
| |
}
| |
}
| |
}
| |
| |
// method to call from the global OnTimer event handler
| |
static void onTimer()
| |
{
| |
_mainTimer.checkTimers();
| |
}
| |
| |
// global enable/disable for all custom timers
| |
static void enable(const bool b)
| |
{
| |
_mainTimer.setEnabled(b);
| |
}
| |
| |
//+------------------------------------------------------------------+
| |
//| Base timer class (still abstract cause notify is not implemented)|
| |
//+------------------------------------------------------------------+
| |
class SingleTimer: public TimerNotification
| |
{
| |
protected:
| |
int multiplier;
| |
MultiTimer *owner;
| |
| |
public:
| |
// create a timer with given multiplier of base period, optionally paused,
| |
// register this object in MultiTimer
| |
SingleTimer(const int m, const bool paused = false): multiplier(m)
| |
{
| |
owner = &MultiTimer::_mainTimer;
| |
if(!paused) owner.bind(this);
| |
}
| |
| |
// unregister this object in MultiTimer
| |
~SingleTimer()
| |
{
| |
owner.unbind(this);
| |
}
| |
| |
// specify timer period
| |
virtual int getInterval() override
| |
{
| |
return multiplier;
| |
}
| |
| |
// pause this timer
| |
virtual void stop()
| |
{
| |
owner.unbind(this);
| |
}
| |
| |
// resume this timer
| |
virtual void start()
| |
{
| |
owner.bind(this);
| |
}
| |
};
| |
};
| |
| |
static MultiTimer MultiTimer::_mainTimer;
| |
| |
//+------------------------------------------------------------------+
| |
//| Concrete worker class for timers with optional counter |
| |
//+------------------------------------------------------------------+
| |
class CountableTimer: public MultiTimer::SingleTimer
| |
{
| |
protected:
| |
const uint repeat;
| |
uint count;
| |
| |
public:
| |
CountableTimer(const int m, const uint r = UINT_MAX, const bool paused = false):
| |
SingleTimer(m, paused), repeat(r), count(0) { }
| |
| |
virtual bool isTimeCome() override
| |
{
| |
if(count >= repeat && repeat != UINT_MAX)
| |
{
| |
stop();
| |
return false;
| |
}
| |
// delegate timing checkup to the parent class,
| |
// increase own count only if timer is triggered right now (got true)
| |
return SingleTimer::isTimeCome() && (bool)++count;
| |
}
| |
| |
// updated method is additionally dropping the count
| |
virtual void stop() override
| |
{
| |
SingleTimer::stop();
| |
count = 0;
| |
}
| |
| |
uint getCount() const
| |
{
| |
return count;
| |
}
| |
| |
uint getRepeat() const
| |
{
| |
return repeat;
| |
}
| |
};
| |
| |
// pointer to a function of custom timer handler (return false to stop)
| |
typedef bool (*TimerHandler)(void);
| |
| |
//+------------------------------------------------------------------+
| |
//| Class for simple timers with pseudo-handler functions in defines |
| |
//+------------------------------------------------------------------+
| |
class FunctionalTimer: public MultiTimer::SingleTimer
| |
{
| |
TimerHandler func;
| |
public:
| |
FunctionalTimer(const int m, TimerHandler f):
| |
SingleTimer(m), func(f) { }
| |
| |
virtual void notify() override
| |
{
| |
if(func != NULL)
| |
{
| |
if(!func())
| |
{
| |
stop();
| |
}
| |
}
| |
}
| |
};
| |
| |
//+------------------------------------------------------------------+
| |
//| Pseudo-handler function define based on FunctionalTimer class |
| |
//+------------------------------------------------------------------+
| |
#define OnTimerCustom(P) OnTimer##P(); \
| |
FunctionalTimer ft##P(P, OnTimer##P); \
| |
bool OnTimer##P()
| |
| |
//+------------------------------------------------------------------+
|