#define ulong unsigned long
class EngineManager;
// Base class for type-erased Engine instances
class EngineBase {
public:
EngineBase* next;
EngineManager* manager;
ulong wait;
int state;
virtual void tick() = 0; // Pure virtual function
void delay(ulong d){ wait += d; }
void setState(int s){state = s;}
void setWait(ulong w){wait = w;}
};
template <typename T>
class Engine: public EngineBase{
public:
void (T::*method)(EngineBase&, int);
T* instance;
Engine(T* i, void (T::*m)(EngineBase&, int)){
method = m;
instance = i;
}
void tick(){ (instance->*method)(*this, state); }
};
class EngineManager{
private:
EngineBase* baseEngine;
EngineBase* lastEngine;
ulong lastTime = 0;
public:
ulong currentDelay;
void AddEngine(EngineBase* a){
a->manager = this;
if(baseEngine == nullptr) {
baseEngine = a;
lastEngine = a;
}else{
lastEngine->next = a;
lastEngine = a;
}
}
void tick(ulong t){
if(lastTime != t){
lastTime = t;
if(currentDelay > 0) {
currentDelay--;
return;
}
EngineBase* a = baseEngine;
unsigned long minDelay = 0xffffffff; //2147483647
while(a != nullptr){
int v = a->wait;
if(v < minDelay) minDelay = v;
if(v <= 0) a->tick();
a = a->next;
}
a = baseEngine;
if(minDelay <= 0) return;
while(a != nullptr){
if(a->wait > 0) a->delay(-minDelay);
a = a->next;
}
currentDelay = minDelay;
}
}
};
class Cars{
public:
int red;
int yellow;
int green;
Cars(byte r, byte y, byte g){
red = r;
yellow = y;
green = g;
}
void process(EngineBase& a, int state){
a.setState(state + 1);
switch(state%4){
case 0:
digitalWrite(yellow, 0);
digitalWrite(red, 1);
return a.delay(500 * 7);
case 1:
digitalWrite(yellow, 1);
return a.delay(500);
case 2:
digitalWrite(red, 0);
digitalWrite(yellow, 0);
digitalWrite(green, 1);
return a.delay(500 * 4);
case 3:
digitalWrite(green, 0);
digitalWrite(yellow, 1);
return a.delay(500);
}
}
};
class Ppls{
public:
int red;
int green;
Ppls(byte r, byte g){
red = r;
green = g;
}
void process(EngineBase& a, int state){
a.setState(state + 1);
switch(state&1){
case 0:
digitalWrite(green, 0);
digitalWrite(red, 1);
return a.delay(250 * 14);
case 1:
digitalWrite(red, 0);
digitalWrite(green, 1);
return a.delay(250 * 12);
}
}
};
EngineManager manager;
Engine<Cars>* mainC = nullptr;
void setup(){
//Cars
manager.AddEngine(new Engine<Cars>(new Cars(13,12,11),&Cars::process));
Engine<Cars>* a = new Engine<Cars>(new Cars(10,9,8),&Cars::process);
a->setState(1);
a->delay(250);
mainC = a;
manager.AddEngine(a);
//Ppls
manager.AddEngine(new Engine<Ppls>(new Ppls(7,6),&Ppls::process));
//Ppls
Engine<Ppls>* b = new Engine<Ppls>(new Ppls(5,4),&Ppls::process);
b->setState(1);
b->delay(250 * 3);
manager.AddEngine(b);
attachInterrupt(digitalPinToInterrupt(2), handleClick, RISING );
}
void loop(){
manager.tick(millis());
}
void handleClick(){
if((mainC->state % 4) == 3){
manager.currentDelay = 0;
}
}