I am using Stateless to implement FSM's in multiple classes. (http://code.google.com/p/stateless/)
I want to use a base class for firing triggers and logging, etc.. I also want to enforce that any class inheriting my baseFSM class implements a StateMachine with their own local States and Triggers.
However my problem is, enum's cannot be abstracted or passed to functions.
By the way, Stateless says "Generic support for states and triggers of any .NET type (numbers, strings, enums, etc.)" so if there is a better way to go about this let me know.
Ideally this is what I would like to implement (or something that can work the same way).
BaseFSM class:
public abstract class BaseFSM : IStateMachine
{
#region Implementation of IStateMachine
public ICall LocalCall { get; set; }
#endregion
internal abstract enum State {}
internal abstract enum Trigger {}
internal abstract StateMachine<State, Trigger> fsm { get; set; }
public abstract void Fire(Enum trigger);
}
A class that implements BaseFSM:
class Incoming_Initial : BaseFSM
{
private enum State
{
WaitForCallToBeAnswered,
CallConnected,
CallNeverConnected,
CheckForCustomIntro,
PlayIntro,
PlayPleaseEnterPin,
ReadLanguageSettings,
ChooseLanguage,
ValidatePIN,
PINWasInvalid,
IdentifyUser
}
private enum Trigger
{
Yes,
No,
DigitPressed,
PromptDonePlaying,
PromptTimerElapse,
Done
}
public Incoming_Initial(ICall call)
{
LocalCall = call;
fsm = new StateMachine<this.State, this.Trigger>(State.WaitForCallToBeAnswered);
....
OR I would even take something like this:
public class myStateMachine
{
private enum State{}
private enum Trigger{}
private StateMachine<State, Trigger> stateMachine;
public myStateMachine (Enum _states, Enum _triggers, Enum _startState)
{
State = _states;
Trigger = _triggers;
stateMachine = new StateMachine<State, Trigger>(_startState);
}
}
Any insight on how I can go about implementing this would be greatly appreciated!
Edit: My ultimate goal is using Stateless to implement an IVR (IVR) system that has ~40 different FSM's. The state machines will be responsible for call flow and how the user interacts with the system. I already have a demo state machine working, but the States and Triggers are local to that class.
I am just trying to see if I can pull the state machine out to a base class so I don't have to pass the state machine to helper functions.
If I can put the state machine in a base class, I think I could use one set of Triggers (these would be events from the phone call like CallConnected, UserPressedDigit, CallDisconnected, PromptDonePlaying, etc) and only have to implement States for each FSM.
ANSWER (at least how I am using this) thanks to @phoog:
public abstract class BaseFSM <TState> : IStateMachine
{
#region Implementation of IStateMachine
public ICall LocalCall { get; set; }
#endregion
public enum Triggers
{
Yes = 0,
No,
DigitPressed,
PromptDonePlaying,
PromptTimerElapse,
Done
}
protected IList<TState> States { get; set; }
protected StateMachine<TState, Triggers> fsm { get; set; }
...
class Incoming_Initial : BaseFSM<Incoming_Initial.State>
{
internal enum State
{
WaitForCallToBeAnswered,
CallConnected,
CallNeverConnected,
CheckForCustomIntro,
PlayIntro,
PlayPleaseEnterPin,
ReadLanguageSettings,
ChooseLanguage,
ValidatePIN,
PINWasInvalid,
IdentifyUser
}
public Incoming_Initial(ICall call)
{
LocalCall = call;
LocalCall.CallEventHandler += new CallEventHandler(LocalCall_CallEventHandler);
States = (State[]) Enum.GetValues(typeof (State));
fsm = new StateMachine<State, Triggers>(State.WaitForCallToBeAnswered);
Note that the Enum
type represents a reference to a boxed value of an enum; it doesn't refer to the entire enum type. So, for example, this code is valid:
enum Something { Value0, Value1, Value2, Value3 }
void ProcessAnEnumValue(Enum value)
{
//...whatever
}
void CallTheMethod()
{
ProcessAnEnumValue(Something.Value2);
}
You are trying to parameterize the entire enum type; the tool for parameterizing types is generics. With that in mind, your code could be made to work with some modifications:
public abstract class BaseFSM<TState, TTrigger> : IStateMachine
{
#region Implementation of IStateMachine
public ICall LocalCall { get; set; }
#endregion
protected IList<TState> States { get; set; }
protected IList<TTrigger> Triggers { get; set; }
protected StateMachine<TState, TTrigger> fsm { get; set; }
public abstract void Fire(TTrigger trigger);
}
class Incoming_Initial : BaseFSM<Incoming_Initial.State, Incoming_Initial.Trigger>
{
public enum State
{
WaitForCallToBeAnswered,
CallConnected,
CallNeverConnected,
CheckForCustomIntro,
PlayIntro,
PlayPleaseEnterPin,
ReadLanguageSettings,
ChooseLanguage,
ValidatePIN,
PINWasInvalid,
IdentifyUser
}
public enum Trigger
{
Yes,
No,
DigitPressed,
PromptDonePlaying,
PromptTimerElapse,
Done
}
public Incoming_Initial(ICall call)
{
States = (State[])Enum.GetValues(typeof(State));
Triggers = (Trigger[])Enum.GetValues(typeof(Trigger));
LocalCall = call;
fsm = new StateMachine<State, Trigger>(State.WaitForCallToBeAnswered);
....