PaulH PaulH - 3 years ago 142
C++ Question

State machine implementation

I have a state machine as described below.

We can start in one of two starting states, but we must hit all 4 states of the handshake. From there, we can either transfer a payload of data or receive a payload of data. Then, we return to our original starting state.

Handshake:

-> StartingState1 -> FinalState1 -> StartingState2 -> FinalState2

-> StartingState2 -> FinalState2 -> StartingState1 -> FinalState1

Payload Transfer:

-> SendPayload -> SendEnd -> StartingState?

-> ReceivePayload -> ReceiveEnd -> StartingState?

The code below represents my current architecture. Unfortunately, at the end of each process, I don't have enough information from within the states to know what the next state is I should hit.

Does anybody have any suggestions on how to improve this architecture based on my requirements?

Thanks,
PaulH

class MyMachine;
class Payload;

class IState
{
MyMachine* context_;

IState( MyMachine* context ) : context_( context) {};

virtual void Consume( byte data );

void ChangeState( IState* state )
{
context_->SetState( state );
}
}

class FinalState1 : IState
{
void Consume( byte data )
{
// Either go to StartingState1, SendPayload, or ReceivePayload.
// How can I tell from within the context of this state where I
// should go?
}
}

class StartingState1 : IState
{
void Consume( byte data )
{
if ( /*some condition*/ )
{
ChangeState( new FinalState1( context_ ) );
}
}
}

class MyMachine
{
IState* state_;
Payload* payload_;

void Start1( Mode mode )
{
state_ = new StartingState1( this );
}

void Start2( Mode mode )
{
state_ = new StartingState2( this );
}

void Consume( byte data )
{
state_->Consume( data );
}

void SetPayload( const Payload* payload )
{
payload_ = payload;
}

const Payload* GetPayload()
{
return payload_;
}

void SetState( State* state )
{
delete state_;
state_ = state;
}
}

// get a byte of data from some source
byte GetData();

void main()
{
MyMachine machine;
Payload payload;
machine.SetPayload( payload );
machine.Start1( Mode::SendPayload );

// could also call:
// machine.Start1( Mode::ReceivePayload );
// machine.Start2( Mode::SendPayload );
// machine.Start2( Mode::ReceivePayload );

for(;;)
{
machine.Consume( GetData() );
}
}

Answer Source

What you have doesn't represent the possible states of your system completely, but it's easy to transform it so that it does. You need additional states to represent the difference between being in state 1 and not having been in state 2, and being in state 1, whilst having been in state 2 (and the same for state 2). So you need:

S1 S2 F1 F2 S12 F12 S21 F21
SP SE
RP RE

with transitions

S1 --> F1
F1 --> S12
S12 --> F12
F12 --> SP or F12 --> RP

S2 --> F2
F2 --> S21
S21 --> F21
F21 --> SP or F21 --> RP

SP --> SE
RP --> RE
SE --> S1 or SE --> S2
RE --> S1 or RE --> S2

The key difference is the introduction of new states S12, F12, S21 and F21. In terms of implementation you could almost certainly just derive S12 from S2, F12 from F2, S21 from S1 and F21 from F2 and override the transition function to go to the correct state.

(Apologies for acronymising all your states).

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download