Сейчас сделано как-то так.
Прекрасно работает, расширяется. Всем доволен, но хотелось бы сравнить с тру 8ми битным подходом. Собственно какую-нибудь хорошую реализацию конечного автомата и хотелось бы где-то посмотреть.
Код
enum DeviceEvents
{
DEVEV_HEARTBEAT, //this event occures every ~100ms
DEVEV_STARTBTN_PRESSED,
//etc...
DEVEV_NO_EVENT = 0xff,
}
enum DeviceState_enum
{
DEVSTATE_OFF,
DEVSTATE_LOCKED,
//etc....
DEVSTATE_INVALID = 0xff,
}
//state interface class
struct DeviceStateImplementation
{
virtual void EnterThisStateFrom( const DeviceState_enum &prevState ) = 0;
//Handles event and returns new device state in response of event
virtual DeviceState_enum HandleEvent( const DeviceEvents &event ) = 0;
};
//Implementation of DEVSTATE_OFF state
class StateImpl_OFF: public DeviceStateImplementation
{
timestamp_t MillisecondsCounter;
uint8_t MinutesCounter;
bool ReactOnStartBTNRelease;
void ResetMillisecondsCounter();
public:
virtual void EnterThisStateFrom( const DeviceState_enum &prevState );
virtual DeviceState_enum HandleEvent( const DeviceEvents &event );
};
//Implementation of DEVSTATE_OFF state
void StateImpl_OFF::EnterThisStateFrom( const DeviceState_enum &prevState )
{
TurnOffAllOutputs();
DisableRPMMeasure();
G_SettingsStateSelector.ResetPressCounter();
ReactOnStartBTNRelease = false;
ResetMillisecondsCounter();
}
void StateImpl_OFF::ResetMillisecondsCounter()
{
MillisecondsCounter = GetTickCount();
MinutesCounter = 0;
}
DeviceState_enum StateImpl_OFF::HandleEvent( const DeviceEvents &event )
{
DeviceState_enum retval = DEVSTATE_OFF;
switch(event)
{
case DEVEV_HEARTBEAT:
ACC_LEDBlinker.DoTheJob();
if( G_SettingsStateSelector.GetSelectedState() != DEVSTATE_INVALID )
{
ACC_LEDBlinker.StartEndlessBlinkSequence(100, 100);//fast blinking indicating some settings mode was selected
}
break;
case DEVEV_STARTBTN_PRESSED:
ReactOnStartBTNRelease = true;
break;
case DEVEV_STARTBTN_RELEASED:
//react on release only if press also was made in this state
if( ReactOnStartBTNRelease )
{
ResetMillisecondsCounter();
if( GlobalVarsRef.BrakePedal.GetButtonState() == GPIOBTNSTATE_PUSHED )
{
DeviceState_enum SelectedSettingsModeState = G_SettingsStateSelector.GetSelectedState();
if( SelectedSettingsModeState != DEVSTATE_INVALID )
retval = SelectedSettingsModeState;
else
retval = DEVSTATE_CRANKING;
}
else
{
retval = DEVSTATE_IGNON;
}
}
break;
case DEVEV_STARTBTN_LONGPRESS:
if( GlobalVarsRef.BrakePedal.GetButtonState() == GPIOBTNSTATE_PUSHED )
retval = DEVSTATE_FORCEDCRANKING;
else
retval = DEVSTATE_ACCON;
break;
case DEVEV_BREAKPEDAL_PRESSED:
ResetMillisecondsCounter();
G_SettingsStateSelector.SelectorWasPressed();
ACC_LED::Clear();
ACC_LEDBlinker.StartBlinkSequence(2, 50, 100);
break;
case DEVEV_BREAKPEDAL_RELEASED:
ACC_LED::Clear();
ACC_LEDBlinker.AbortBlinkSequence();
break;
case DEVEV_ENGINE_STARTED:
retval = DEVSTATE_RUNNING;
break;
case DEVEV_ENGINE_STALLED:
break;
}
return retval;
}
/////main state machine implementation
class MainStateMachine
{
public:
void EnterStateForced( const DeviceState_enum &state );
void ProcessEvent( const DeviceEvents &evnt );
private:
DeviceStateImplementation *CurrentDevStateImplementation; //<----pointer to state interface class
DeviceState_enum CurrentDevState;
void SetNewDeviceState( const DeviceState_enum &newDevState );
};
void MainStateMachine::ProcessEvent( const DeviceEvents &evnt )
{
DeviceState_enum newDevState = CurrentDevStateImplementation->HandleEvent( evnt );
if( newDevState != CurrentDevState )
SetNewDeviceState( newDevState );
}
void MainStateMachine::SetNewDeviceState( const DeviceState_enum &newDevState )
{
switch( newDevState )
{
case DEVSTATE_OFF:
CurrentDevStateImplementation = &OFFState;
break;
case DEVSTATE_LOCKED:
CurrentDevStateImplementation = &LockedState;
break;
}
CurrentDevStateImplementation->EnterThisStateFrom( CurrentDevState );
CurrentDevState = newDevState;
}
//main loop simplifiyed
int main()
{
while(1)
{
event = DEVEV_HEARTBEAT;
while( event != DEVEV_NO_EVENT )
{
MainStateMachine.ProcessEvent( event );
event = DetectOneMoreEvents();
}
}
}
{
DEVEV_HEARTBEAT, //this event occures every ~100ms
DEVEV_STARTBTN_PRESSED,
//etc...
DEVEV_NO_EVENT = 0xff,
}
enum DeviceState_enum
{
DEVSTATE_OFF,
DEVSTATE_LOCKED,
//etc....
DEVSTATE_INVALID = 0xff,
}
//state interface class
struct DeviceStateImplementation
{
virtual void EnterThisStateFrom( const DeviceState_enum &prevState ) = 0;
//Handles event and returns new device state in response of event
virtual DeviceState_enum HandleEvent( const DeviceEvents &event ) = 0;
};
//Implementation of DEVSTATE_OFF state
class StateImpl_OFF: public DeviceStateImplementation
{
timestamp_t MillisecondsCounter;
uint8_t MinutesCounter;
bool ReactOnStartBTNRelease;
void ResetMillisecondsCounter();
public:
virtual void EnterThisStateFrom( const DeviceState_enum &prevState );
virtual DeviceState_enum HandleEvent( const DeviceEvents &event );
};
//Implementation of DEVSTATE_OFF state
void StateImpl_OFF::EnterThisStateFrom( const DeviceState_enum &prevState )
{
TurnOffAllOutputs();
DisableRPMMeasure();
G_SettingsStateSelector.ResetPressCounter();
ReactOnStartBTNRelease = false;
ResetMillisecondsCounter();
}
void StateImpl_OFF::ResetMillisecondsCounter()
{
MillisecondsCounter = GetTickCount();
MinutesCounter = 0;
}
DeviceState_enum StateImpl_OFF::HandleEvent( const DeviceEvents &event )
{
DeviceState_enum retval = DEVSTATE_OFF;
switch(event)
{
case DEVEV_HEARTBEAT:
ACC_LEDBlinker.DoTheJob();
if( G_SettingsStateSelector.GetSelectedState() != DEVSTATE_INVALID )
{
ACC_LEDBlinker.StartEndlessBlinkSequence(100, 100);//fast blinking indicating some settings mode was selected
}
break;
case DEVEV_STARTBTN_PRESSED:
ReactOnStartBTNRelease = true;
break;
case DEVEV_STARTBTN_RELEASED:
//react on release only if press also was made in this state
if( ReactOnStartBTNRelease )
{
ResetMillisecondsCounter();
if( GlobalVarsRef.BrakePedal.GetButtonState() == GPIOBTNSTATE_PUSHED )
{
DeviceState_enum SelectedSettingsModeState = G_SettingsStateSelector.GetSelectedState();
if( SelectedSettingsModeState != DEVSTATE_INVALID )
retval = SelectedSettingsModeState;
else
retval = DEVSTATE_CRANKING;
}
else
{
retval = DEVSTATE_IGNON;
}
}
break;
case DEVEV_STARTBTN_LONGPRESS:
if( GlobalVarsRef.BrakePedal.GetButtonState() == GPIOBTNSTATE_PUSHED )
retval = DEVSTATE_FORCEDCRANKING;
else
retval = DEVSTATE_ACCON;
break;
case DEVEV_BREAKPEDAL_PRESSED:
ResetMillisecondsCounter();
G_SettingsStateSelector.SelectorWasPressed();
ACC_LED::Clear();
ACC_LEDBlinker.StartBlinkSequence(2, 50, 100);
break;
case DEVEV_BREAKPEDAL_RELEASED:
ACC_LED::Clear();
ACC_LEDBlinker.AbortBlinkSequence();
break;
case DEVEV_ENGINE_STARTED:
retval = DEVSTATE_RUNNING;
break;
case DEVEV_ENGINE_STALLED:
break;
}
return retval;
}
/////main state machine implementation
class MainStateMachine
{
public:
void EnterStateForced( const DeviceState_enum &state );
void ProcessEvent( const DeviceEvents &evnt );
private:
DeviceStateImplementation *CurrentDevStateImplementation; //<----pointer to state interface class
DeviceState_enum CurrentDevState;
void SetNewDeviceState( const DeviceState_enum &newDevState );
};
void MainStateMachine::ProcessEvent( const DeviceEvents &evnt )
{
DeviceState_enum newDevState = CurrentDevStateImplementation->HandleEvent( evnt );
if( newDevState != CurrentDevState )
SetNewDeviceState( newDevState );
}
void MainStateMachine::SetNewDeviceState( const DeviceState_enum &newDevState )
{
switch( newDevState )
{
case DEVSTATE_OFF:
CurrentDevStateImplementation = &OFFState;
break;
case DEVSTATE_LOCKED:
CurrentDevStateImplementation = &LockedState;
break;
}
CurrentDevStateImplementation->EnterThisStateFrom( CurrentDevState );
CurrentDevState = newDevState;
}
//main loop simplifiyed
int main()
{
while(1)
{
event = DEVEV_HEARTBEAT;
while( event != DEVEV_NO_EVENT )
{
MainStateMachine.ProcessEvent( event );
event = DetectOneMoreEvents();
}
}
}