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()
| |||
| |||
//+------------------------------------------------------------------+
|