If you are completely new to state machines, I've written a very basic implementation to help get you started.
Code:
unit classBaseStateMachine;
interface
uses
SysUtils, Generics.collections;
type
TStateMethod = function:integer of object;
TBaseStatemachine = class(TObject)
protected
fCurrentState : integer;
fStateList : TDictionary;
procedure addState(stateIndex:integer;aStateMethod:TStateMethod);
public
constructor create;
destructor Destroy; override;
procedure process;
procedure initialiseStates; virtual; abstract;
property currentState:integer read fCurrentState write fCurrentState;
end;
implementation
procedure TBaseStateMachine.addState(stateIndex:integer;aStateMethod:TStateMethod);
begin
if (fStateList.ContainsKey(stateIndex)) then
begin
raise exception.createFmt('%s.addState - Add failed, duplicate state %d',[self.className,stateIndex]);
end;
fStateList.Add(stateIndex,aStateMethod);
end;
constructor TBaseStateMachine.create;
begin
inherited;
fStateList:=TDictionary.create;
fCurrentState:=0;
initialiseStates;
end;
destructor TBaseStateMachine.Destroy;
begin
try
fStateList.free;
except
end;
inherited;
end;
procedure TBaseStateMachine.process;
var
nextState : integer;
begin
if (fStateList.ContainsKey(fCurrentState)) then
begin
nextState:=fStateList.Items[fCurrentState];
if (nextState<>0) then
begin
fCurrentState:=nextState;
end;
end
else
begin
raise exception.createFmt('%s.process failed - Unknown state %d',[self.className,fCurrentState]);
end;
end;
end.
You can also use initialiseStates to setup any internal variables.
To use it, you simply call process each time you want to run the current state. The appropriate function will be executed and the machine will switch to a new state if the return value of the function is not 0.
As an example, here is a very basic state machine that counts up to 100 and then back down to 0 before counting back to 100 etc. ad infinitum.
Interface
Code:
const
STATE_COUNTINGUP = 1;
STATE_COUNTINGDOWN = 2;
type
TMyStateMachine = class(TBaseStateMachine)
protected
fCounter : integer;
public
procedure initialiseStates; override;
function stateCountingUp:integer;
function stateCountingDown:integer;
property counter:integer read fCounter;
end;
Code:
procedure TMyStateMachine.initialiseStates;
begin
addState(STATE_COUNTINGUP,stateCountingUp);
addState(STATE_COUNTINGDOWN,stateCountingDown);
fCurrentState:=STATE_COUNTINGUP;
fCounter:=0;
end;
function TMyStateMachine.stateCountingUp;
begin
result:=0;
inc(fCounter);
if (fCounter>=100) then
begin
result:=STATE_COUNTINGDOWN;
end;
end;
function TMyStateMachine.stateCountingDown;
begin
result:=0;
dec(fCounter);
if (fCounter<=0) then
begin
result:=STATE_COUNTINGUP;
end;
end;
Code:
procedure TfrmBasicStateMachine.FormCreate(Sender: TObject);
begin
fMyStateMachine:=TMyStateMachine.create;
end;
procedure TfrmBasicStateMachine.FormDestroy(Sender: TObject);
begin
try
fMyStateMachine.free;
except
end;
end;
procedure TfrmBasicStateMachine.tmrTickTimer(Sender: TObject);
begin
tmrTick.enabled:=false;
fMyStateMachine.process;
lblCount.caption:=intToStr(fMyStateMachine.counter);
lblCurrentState.caption:=intToStr(fMyStateMachine.currentState);
tmrTick.enabled:=true;
end;
All the source code for this example is included in the attached ZIP file.


Menu
Categories
Content Tags







vBulletin Message