Using Stateless library for .NET I wrote a state machine that does a few things:
- Read a barcode ticket
- do some validation
- check for some more validation
- do some digital I/O(turn a light green and open a gate)
- frame a transaction and commit it
My StateMachine code is as follows:
public class MainStateMachine
{
public delegate void tvOnTransitioned(StateMachine<State, Trigger>.Transition otransition);
public delegate void tvUnhandledTrigger(State estate, Trigger etrigger);
public delegate void tvEntryExit();
public delegate bool bnGuardClause();
public delegate void vLogMsg(string sstring);
public delegate void tvOnTransition_Barcode(sbarcodeserial);
StateMachine<State, Trigger>.TriggerWithParameters<string> oBarcode_Trigger_With_Parameters;
public tvOnTransitioned OnTransitioned = null;
public tvUnhandledTrigger OnUnhandledTrigger = null;
#region SuperState
public tvEntryExit OnEntry_Input = null;
public tvEntryExit OnExit_Input = null;
#endregion
#region Substates_Entry_Exit
public tvEntryExit OnEntry_CSC_BarcodeValidation = null;
public tvEntryExit OnEntry_PaymentSatisfied = null;
public tvEntryExit OnEntry_FrameTransaction = null;
#endregion
#region Transitions
public tvEntryExit OnTransition_BarcodeValid = null;
public tvEntryExit OnTransition_OCR_Validation_OK = null;
public tvEntryExit OnTransition_LD_RisingEdge = null;
public tvEntryExit OnTransition_BarcodeError = null;
public tvEntryExit OnTransition_BarcodeInvalid = null;
public tvEntryExit OnTransition_OCR_ValidationNot_OK = null;
#endregion
public tvOnTransition_Barcode OnTransition_DO_CSCBarcodeValidation = null;
public enum State
{
//Super states
Input,
//sub states
CSC_Barcode_Validation, OCR_BarcodeValidation, PaymentSatisfied, Transaction
}
public enum Trigger
{
OpenLane,
Trg_CloseLane,
Trg_BC,
Trg_BarCodeRead,
Trg_BarcodeError,
Trg_BarcodeInvalid,
Trg_BarcodeValid,
Trg_OCR_ValidationNot_OK,
Trg_OCR_Validation_OK,
Trg_LDRisingEdge,
Trg_LDFallingEdge
}
State _state = State.Input;
StateMachine<State, Trigger> _sMachine;
object oLock;
public MainStateMachine()
{
this.oLock = new object();
_sMachine = new StateMachine<State, Trigger>(() => _state, s => _state = s);
this._sMachine.OnTransitioned((otransition) => { if (this.OnTransitioned != null) this.OnTransitioned(otransition); });
this._sMachine.OnUnhandledTrigger((estate, etrigger) => { if (this.OnUnhandledTrigger != null) this.OnUnhandledTrigger(estate, etrigger); });
this.oBarcode_Trigger_With_Parameters = this._sMachine.SetTriggerParameters<string>(Trigger.Trg_BC);
//Tag/Barcode Input state
_sMachine.Configure(State.Input)
.Permit(Trigger.Trg_BC, State.CSC_Barcode_Validation)
.OnEntry(() => { if (this.OnEntry_Input != null) this.OnEntry_Input(); })
.OnExit(() => { if (this.OnExit_Input != null) this.OnExit_Input(); });
_sMachine.Configure(State.CSC_Barcode_Validation)
.SubstateOf(State.Input)
.OnEntryFrom(this.oBarcode_Trigger_With_Parameters, (sbarcodeserial) => { if (this.OnTransition_DO_CSCBarcodeValidation != null) this.OnTransition_DO_CSCBarcodeValidation( sbarcodeserial); })
.Permit(Trigger.Trg_BarcodeError, State.Transaction)
.Permit(Trigger.Trg_BarcodeValid, State.OCR_BarcodeValidation)
.OnEntry(() => { if (OnEntry_CSC_BarcodeValidation != null) OnEntry_CSC_BarcodeValidation(); });
_sMachine.Configure(State.OCR_BarcodeValidation)
.SubstateOf(State.Input)
.OnEntryFrom(Trigger.Trg_BarcodeValid, () =>
{
if(this.OnTransition_BarcodeValid != null) this.OnTransition_BarcodeValid();
})
.Permit(Trigger.Trg_OCR_ValidationNot_OK, State.Transaction)
.Permit(Trigger.Trg_OCR_Validation_OK, State.PaymentSatisfied);
_sMachine.Configure(State.PaymentSatisfied)
.Permit(Trigger.Trg_LDRisingEdge, State.Transaction)
.OnEntryFrom(Trigger.Trg_OCR_Validation_OK, () =>
{
if (this.OnTransition_OCR_Validation_OK != null) this.OnTransition_OCR_Validation_OK();
})
.OnEntry(() => { if (this.OnEntry_PaymentSatisfied != null) OnEntry_PaymentSatisfied(); }); //.OnExit(() => { if (this.OnExit_PaymentSatisfied != null) OnExit_PaymentSatisfied(); });
_sMachine.Configure(State.Transaction)
.OnEntryFrom(Trigger.Trg_LDRisingEdge, () =>
{
if (this.OnTransition_LD_RisingEdge != null) this.OnTransition_LD_RisingEdge();
})
.OnEntryFrom(Trigger.Trg_BarcodeError, () => { if (this.OnTransition_BarcodeError != null) this.OnTransition_BarcodeError(); })
.OnEntryFrom(Trigger.Trg_BarcodeInvalid, () => { if (this.OnTransition_BarcodeInvalid != null) this.OnTransition_BarcodeInvalid(); })
.OnEntryFrom(Trigger.Trg_OCR_ValidationNot_OK, () => { if (this.OnTransition_OCR_ValidationNot_OK != null) this.OnTransition_OCR_ValidationNot_OK(); })
.OnEntry(() => { if (this.OnEntry_FrameTransaction != null) OnEntry_FrameTransaction(); }); // on entry into this State, I have to frame a Transaction and commit it
}
public bool TryFireTrigger(Trigger etrigger)
{
bool bReturn = false;
lock (this.oLock)
{
if (this._sMachine.CanFire(etrigger))
{
try
{
this._sMachine.Fire(etrigger);
bReturn = true;
}
catch (Exception oException)
{
bReturn = false;
Console.WriteLine(oException.StackTrace);
}
}
}
return bReturn;
}
}
This State machine is for a vehicle when it enters an unattended booth in a parking lot. I can't figure out as to how to create a transaction object, because it's properties change as and how it undergoes a state change. Do I pass the Transaction object to each state and get it's modified state back and pass it to next state? I'm not sure how to go about it. Any help is appreciated.
Edit: Another question. What is a substate?
Substates inherit the allowed transitions of their superstate. When entering directly into a substate from outside of the superstate, entry actions for the superstate are executed. Likewise when leaving from the substate to outside the superstate, exit actions for the superstate will execute.