/// <summary> /// Constructor /// </summary> /// <param name="msgListner">The event listner object that receives events</param> /// <param name="msgStore">The object that stores the messages</param> /// <param name="eventBehavior">The behavior object that interacts with incoming events</param> /// <param name="stateMachine">The state machine that interprets the events</param> /// <param name="timer">The periodic timer</param> public SpStateMachineEngine(ISpEventListner msgListner, ISpEventStore msgStore, ISpBehaviorOnEvent eventBehavior, ISpStateMachine stateMachine, ISpPeriodicTimer timer) { WrapErr.ChkParam(msgListner, "msgListner", 50050); WrapErr.ChkParam(msgStore, "msgStore", 50051); WrapErr.ChkParam(eventBehavior, "eventBehavior", 50052); WrapErr.ChkParam(stateMachine, "stateMachine", 50053); WrapErr.ChkParam(timer, "timer", 50054); WrapErr.ToErrorReportException(50055, () => { this.wakeUpAction = new Action(timer_OnWakeup); this.msgReceivedAction = new Action <ISpEventMessage>(this.eventListner_MsgReceived); this.msgListner = msgListner; this.msgStore = msgStore; this.eventBehavior = eventBehavior; this.stateMachine = stateMachine; this.timer = timer; this.driverThread = new Thread(new ThreadStart(this.DriverThread)); this.driverThread.Start(); this.msgListner.MsgReceived += this.msgReceivedAction; this.timer.OnWakeup += this.wakeUpAction; }); }
/// <summary>Tick current state to execute the action based on the inputed message</summary> /// <param name="msg">The inputed message</param> /// <returns>The return message from the action</returns> public ISpEventMessage Tick([NotNull] ISpEventMessage?msg) { WrapErr.ChkDisposed(this.disposed, 50176); WrapErr.ChkParam(msg, "msg", 50172); // First tick to drive it from entry to regular ISpStateTransition <TMsgId>?tr = null; bool tmpIsStarted = this.isStarted; //Log.Debug("SpMachine", "Tick", String.Format("isStarted:{0}", this.isStarted)); if (!this.isStarted) { // The OnEntry must be called directly from the state machine for the first state. Subsequent // state transitions will insure that the OnEntry for the new state is called this.isStarted = true; tr = this.state.OnEntry(msg); } else { tr = this.state.OnTick(msg); } WrapErr.ChkVar(tr, 50177, () => String.Format( "The State '{0}' {1} Returned a Null Transition", this.state.FullName, tmpIsStarted ? "OnTick" : "OnEntry")); WrapErr.ChkVar(tr.ReturnMessage, 9999, "Null ReturnMessage"); return(tr.ReturnMessage); }
/// <summary> /// Get a clone of the transition object from the store or null if not found /// </summary> /// <param name="store">The store to search</param> /// <param name="eventMsg">The message to insert in the transition object</param> /// <returns>The transition object from the store or null if not found</returns> public static ISpStateTransition GetTransitionCloneFromStore(Dictionary <int, ISpStateTransition> store, ISpEventMessage eventMsg) { WrapErr.ChkParam(store, "store", 51009); WrapErr.ChkParam(eventMsg, "eventMsg", 51010); return(WrapErr.ToErrorReportException(51011, () => { if (store.Keys.Contains(eventMsg.EventId)) { // Clone Transition object from store since its pointers get reset later ISpStateTransition tr = (ISpStateTransition)store[eventMsg.EventId].Clone(); if (tr.ReturnMessage == null) { tr.ReturnMessage = eventMsg; } else { //Log.Info("SpTools", "GetTransitionCloneFromStore", "Found a transition AND - Held msg - transfering GUID"); } // TODO - Look at transfering the GUID here tr.ReturnMessage.Uid = eventMsg.Uid; //tr.ReturnMessage = eventMsg; return tr; } return null; })); }
/// <summary>Constructor</summary> /// <param name="wrappedObject">Has common data and functions for all states</param> /// <param name="state">The state machine's main state</param> /// <remarks> /// The main state will be a super state or parallel super state implementation. You /// can use a single state implementation if you only want the wrapped object to be /// driven by periodic timer and have access to the messaging architecture /// </remarks> public SpMachine([NotNull] TMachine?wrappedObject, [NotNull] ISpState <TMsgId>?state) { WrapErr.ChkParam(wrappedObject, "wrappedObject", 50170); WrapErr.ChkParam(state, "state", 50171); this.wrappedObject = wrappedObject; this.state = state; }
/// <summary> /// Constructor with DI injectable main state /// </summary> /// <param name="wrappedObject"></param> /// <param name="state">The state machine's main state</param> /// <remarks> /// The main state will be a super state or parallel super state implementation. You /// can use a single state implementation if you only want the wrapped object to be /// driven by periodic timer and have access to the messaging architecture /// </remarks> public SpMachine(T wrappedObject, ISpState state) { WrapErr.ChkParam(wrappedObject, "wrappedObject", 50170); WrapErr.ChkParam(state, "state", 50171); this.wrappedObject = wrappedObject; this.state = state; }
public void Param_NullArg_FaultException() { WrapErr.ToErrReport(out ErrReport err, 1111, "Validate arg", () => { WrapErr.ChkParam(null, "zork", 8888); }); this.Validate(err, 8888, "Param_NullArg_FaultException", "Null zork Argument"); }
/// <summary> /// Add and event object to the store /// </summary> /// <param name="msg">The message event</param> public void Add(ISpEventMessage msg) { WrapErr.ChkDisposed(this.disposed, 50111); WrapErr.ChkParam(msg, "msg", 50112); lock (this.queueLock) { this.AddEvent(msg); } }
/// <summary>Constructor</summary> /// <param name="parent">The parent state</param> /// <param name="msgFactory">Message Factory</param> /// <param name="idConverter">The integer id to string converter</param> /// <param name="id">Unique state id converter</param> /// <param name="wrappedObject">The generic object that the states represent</param> public SpStateBase(ISpState parent, ISpMsgFactory msgFactory, ISpIdConverter idConverter, ISpToInt id, T wrappedObject) { WrapErr.ChkParam(msgFactory, "msgFactory", 9999); WrapErr.ChkParam(wrappedObject, "wrappedObject", 50200); this.msgFactory = msgFactory; this.idConverter = idConverter; this.InitStateIds(parent, id.ToInt()); this.wrappedObject = wrappedObject; }
public void Param_ValidArg_FaultException() { string zork = "Zorker"; WrapErr.ToErrReport(out ErrReport err, 1111, "Validate arg", () => { WrapErr.ChkParam(zork, "zork", 8888); }); Assert.AreEqual(0, err.Code, "Should not have been an error"); }
/// <summary>Safely execute function and transfer the GUID to the response</summary> /// <param name="msg">The incoming message</param> /// <param name="func">The function to invoke to generate the response message</param> /// <returns>A correlated response message</returns> private ISpEventMessage GetMsg(ISpEventMessage msg, Func <ISpEventMessage> func) { WrapErr.ChkParam(msg, "msg", 9999); return(WrapErr.ToErrorReportException(9999, () => { ISpEventMessage ret = func.Invoke(); WrapErr.ChkVar(ret, 9999, "The Provider Returned a Null Message"); // Transfer the GUID for correlation ret.Uid = msg.Uid; return ret; })); }
/// <summary> /// Register a state transition for an event /// </summary> /// <param name="eventId">The id converter of the event type</param> /// <param name="transition">The transition object</param> public static void RegisterTransition(string type, ISpToInt eventId, ISpStateTransition transition, Dictionary <int, ISpStateTransition> store) { WrapErr.ChkParam(eventId, "eventId", 51004); WrapErr.ChkParam(transition, "transition", 51005); WrapErr.ChkParam(store, "store", 51006); // Wrap the id converter separately int tmp = WrapErr.ToErrorReportException(51007, () => { return(String.Format("Error on Event Id Converter for '{0}' Event Type", type)); }, () => { return(eventId.ToInt()); }); // Duplicate transitions on same Event is a no no. WrapErr.ChkFalse(store.Keys.Contains(tmp), 51008, () => { return(String.Format("Already Contain a '{0}' Transition for Id:{1}", type, tmp)); }); store.Add(tmp, transition); }
/// <summary> /// Search through a dictionary for a string equivalent and add if not found /// </summary> /// <param name="key">The key for the string requested</param> /// <param name="currentStrings">The dictionary of strings to query and add to</param> /// <param name="converterFunc">The converted to convert the key to a string value if not in the Dictionary</param> /// <returns></returns> public static string GetIdString(int key, Dictionary <int, string> currentStrings, Func <int, string> converterFunc) { WrapErr.ChkParam(currentStrings, "currentStrings", 51000); WrapErr.ChkParam(converterFunc, "converterFunc", 51001); return(WrapErr.ToErrorReportException(51002, () => { if (currentStrings.Keys.Contains(key)) { return currentStrings[key]; } // Do another wrap level to isolate the user defined conversion failure string ret = WrapErr.ToErrorReportException(51003, "Error in Calling Id to String Converter Method", () => { return converterFunc.Invoke(key); }); currentStrings.Add(key, ret); return ret; })); }
/// <summary>Register a transition for a state</summary> /// <param name="type">string of transition type</param> /// <typeparam name="TMsgId">Event id</typeparam> /// <param name="msgId">The event message id</param> /// <param name="transition">Transition object</param> /// <param name="store">Transition store</param> public static void RegisterTransition <TMsgId>(string type, TMsgId msgId, ISpStateTransition <TMsgId>?transition, Dictionary <int, ISpStateTransition <TMsgId> >?store) where TMsgId : struct { //WrapErr.ChkParam(eventId, "msgId", 51004); WrapErr.ChkParam(transition, "transition", 51005); WrapErr.ChkParam(store, "store", 51006); WrapErr.ChkTrue(typeof(TMsgId).IsEnum, 9999, () => string.Format("Transition type {0} must be Enum", msgId.GetType().Name)); WrapErr.ChkTrue(typeof(TMsgId).GetEnumUnderlyingType() == typeof(Int32), 9999, () => string.Format("Transition type enum {0} must be derived from int", msgId.GetType().Name)); int tmp = Convert.ToInt32(msgId); // 51007 - failure of conversion number // Duplicate transitions on same Event is a no no. WrapErr.ChkFalse(store.Keys.Contains(tmp), 51008, () => { return(String.Format("Already Contain a '{0}' Transition for Id:{1}", type, tmp)); }); store.Add(tmp, transition); }
public void ExceptionType_Regular_Param() { CheckExceptionType(() => { WrapErr.ChkParam(null, "zork", 8888); }); }
/// <summary> /// Constructor /// </summary> /// <param name="defaultTick"> /// The default tick event if to provide if there are no queued event objects /// </param> public BaseEventStore(ISpEventMessage defaultTick) { WrapErr.ChkParam(defaultTick, "defaultTick", 50110); this.defaultTick = defaultTick; }
/// <summary>Constructor</summary> /// <param name="provider">The message provider</param> public SpMsgFactory(ISpMsgProvider provider) { WrapErr.ChkParam(provider, "provider", 9999); this.provider = provider; }