/// <summary> /// Verifies transitions in order of onEvent, onResult and if not found will return the /// default transition /// </summary> /// <param name="stateFunc"> /// The function that is invoked that will return return message from the state processing /// and use it to check against the onResult queue. /// </param> /// <param name="eventMsg">The incoming message to validate against the onEvent list</param> /// <returns>The OnEvent, OnResult or default transition</returns> private ISpStateTransition GetTransition(bool onEntry, Func <ISpEventMessage, ISpEventMessage> stateFunc, ISpEventMessage msg) { return(WrapErr.ToErrorReportException(9999, () => { // Query the OnEvent queue for a transition BEFORE calling state function (OnEntry, OnTick) ISpStateTransition tr = this.GetOnEventTransition(msg); if (tr != null) { tr.ReturnMessage = (tr.ReturnMessage == null) ? this.MsgFactory.GetResponse(msg) : this.MsgFactory.GetResponse(tr.ReturnMessage); return tr; } // Only considered entered if you do not encounter a preemptive OnEvent transition on entry. In this way // you could get a situation where you cascade down several state depths based on higher up events if (onEntry) { this.isEntered = true; } // Get the transition object from the 'OnResult' queue if ((tr = this.GetOnResultTransition(stateFunc.Invoke(msg))) != null) { tr.ReturnMessage = this.MsgFactory.GetResponse(msg, tr.ReturnMessage); return tr; } // If no transitions registered return SameState with default success message return new SpStateTransition(SpStateTransitionType.SameState, null, this.MsgFactory.GetDefaultResponse(msg)); })); }
/// <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="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; }); }
public T GetObj <T>() where T : class { T?obj = WrapErr.ToErrorReportException(9999, () => string.Format("Constructor for class {0} failed", typeof(T).Name), () => { return(this.ReturnObj <T>()); }); return(obj); }
/// <summary> /// Set the interval in for wakeup for the next Start /// </summary> /// <param name="interval">The interval in </param> public void SetInterval(TimeSpan interval) { WrapErr.ChkDisposed(this.disposed, 50002); WrapErr.ChkTrue(interval.TotalMilliseconds > 0, 50000, "The interval cannot be 0 milliseconds total"); WrapErr.ToErrorReportException(50001, () => { this.timespan = interval; }); }
/// <summary> /// Excecuted once when the state becomes the current state /// </summary> /// <param name="msg">The incoming message</param> /// <returns>A state transition object</returns> public virtual ISpStateTransition OnEntry(ISpEventMessage msg) { Log.Info(this.className, "OnEntry", String.Format("'{0}' State {1} - Event", this.FullName, this.GetCachedEventId(msg.EventId))); WrapErr.ChkFalse(this.IsEntryExcecuted, 50201, "OnEntry Cannot be Executed More Than Once Until OnExit is Called"); return(WrapErr.ToErrorReportException(9999, () => { return this.GetTransition(true, this.ExecOnEntry, msg); })); }
//private readonly static ClassLog log = new ("FileHelpers"); #endregion /// <summary>Flip all slashes back or forth for cross-platform compatibility</summary> /// <param name="pathString">The path string to convert</param> /// <returns>The converted string</returns> public static string ConvertSlashes(string pathString) { WrapErr.ChkTrue(pathString.Length > 0, 9999, "Empty path string not allowed"); return(WrapErr.ToErrorReportException(9999, "Failed to flip slashes", () => { // Cover all bases by converting forward or back slashes to OS specific string tmp = pathString.Replace('\\', Path.DirectorySeparatorChar); return tmp.Replace('/', Path.DirectorySeparatorChar); })); }
public void OnAction_NoException() { WrapErr.ToErrReport(out ErrReport err, 1111, "Validate arg", () => { WrapErr.ToErrorReportException(12345, "Unexpected Error Processing Block", () => { Console.WriteLine("This is a non exception throwing block"); }); }); Assert.AreEqual(0, err.Code); Assert.IsFalse(this.logged, "No exception - should not have been logged"); }
public void OnAction_Exception() { WrapErr.ToErrReport(out ErrReport err, 1111, "Validate arg", () => { WrapErr.ToErrorReportException(12345, "Unexpected Error Processing Block", () => { new ChkUtilsTestHelpers.OuterClass().DoNestedException(); }); }); this.Validate(err, 12345, "OnAction_Exception", "Unexpected Error Processing Block"); Assert.IsTrue(this.logged, "Exception - The log delegate should have fired"); }
/// <summary> /// Called on every other period after the first /// </summary> /// <param name="msg">The incoming message</param> /// <returns>A state transition object</returns> public virtual ISpStateTransition OnTick(ISpEventMessage msg) { //Log.Info(this.className, "OnTick", String.Format("'{0}' State - {1}", this.FullName, this.ConvertEventIdToString(msg.EventId))); WrapErr.ChkTrue(this.IsEntryExcecuted, 50205, () => { return(String.Format("OnTick for '{0}' State Cannot be Executed Before OnEntry", this.FullName)); }); return(WrapErr.ToErrorReportException(9999, () => { return this.GetTransition(false, this.ExecOnTick, msg); })); }
public void OnAction_NoException_MsgFormatInvoked() { WrapErr.ToErrReport(out ErrReport err, 1111, "Validate arg", () => { WrapErr.ToErrorReportException(12345, () => { this.msgFormated = true; return("blah"); }, () => { Console.WriteLine("This is a non exception throwing block"); }); }); Assert.AreEqual(0, err.Code); Assert.IsFalse(this.msgFormated, "The message formatter should not have been invoked"); Assert.IsFalse(this.logged, "No exception - should not have been logged"); }
/// <summary> /// Get 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> private ISpStateTransition GetTransitionFromStore(Dictionary <int, ISpStateTransition> store, ISpEventMessage eventMsg) { return(WrapErr.ToErrorReportException(50204, () => { ISpStateTransition t = SpTools.GetTransitionCloneFromStore(store, eventMsg); if (t != null) { this.LogTransition(t, eventMsg); } return t; })); }
public void OnAction_Exception_MsgFormatInvoked() { WrapErr.ToErrReport(out ErrReport err, 1111, "Validate arg", () => { WrapErr.ToErrorReportException(12345, () => { this.msgFormated = true; return("Unexpected Error Processing Block"); }, () => { new ChkUtilsTestHelpers.OuterClass().DoNestedException(); }); }); this.Validate(err, 12345, "OnAction_Exception_MsgFormatInvoked", "Unexpected Error Processing Block"); Assert.IsTrue(this.msgFormated, "The message formatter should have been invoked"); Assert.IsTrue(this.logged, "Exception - The log delegate should have fired"); }
/// <summary> /// Builds the fully resolved name by iterating through the the name based on the /// </summary> private void BuildName() { WrapErr.ToErrorReportException(9999, () => { StringBuilder sb = new StringBuilder(75); this.IdChain.ForEach((item) => { sb.Append(String.Format(".{0}", this.GetCachedStateId(item))); }); this.fullName = sb.Length > 0 ? sb.ToString(1, sb.Length - 1) : "FullNameSearchFailed"; this.name = this.idChain.Count > 0 ? this.GetCachedStateId(this.idChain[this.idChain.Count - 1]) : "NameSearchFailed"; }); }
/// <summary>Dispose resources</summary> /// <param name="disposeManagedResources"> /// If true it was called by the Dispose method rather than finalizer /// </param> private void Dispose(bool disposeManagedResources) { if (!disposed) { if (disposeManagedResources) { WrapErr.ToErrorReportException(50173, () => this.DisposeManagedResources()); } WrapErr.ToErrorReportException(50174, () => this.DisposeNativeResources()); } this.disposed = true; }
/// <summary> /// Factor out the reporting of state transitions for clarity /// </summary> /// <param name="tr">The transition object</param> /// <param name="eventMsg">The event message which pushed this transition</param> private void LogTransition(ISpStateTransition tr, ISpEventMessage eventMsg) { WrapErr.ToErrorReportException(9999, () => { Log.Debug("SpState", "LogTransition", String.Format( "{0} OnMsg({1} - {2}) - From:{3} To:{4}", tr.TransitionType, this.GetCachedMsgTypeId(eventMsg.TypeId), this.GetCachedEventId(eventMsg.EventId), this.FullName, tr.NextState == null ? "Unknown" : tr.NextState.FullName)); }); }
/// <summary> /// Start the periodic timer /// </summary> public void Start() { WrapErr.ChkDisposed(this.disposed, 50003); WrapErr.ToErrorReportException(50004, () => { lock (this.timerLock) { this.Stop(); this.timer = new Timer(this.timespan.TotalMilliseconds); this.timer.Elapsed += this.onTimerWakeup; this.timer.AutoReset = true; this.timer.Start(); } }); }
/// <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; })); }
public void OnFunction_NoException() { string s = ""; WrapErr.ToErrReport(out ErrReport err, 1111, "Validate arg", () => { s = WrapErr.ToErrorReportException(12345, "Unexpected Error Processing Block", () => { return("This is a non exception throwing block"); }); }); Assert.AreEqual(0, err.Code); Assert.AreEqual("This is a non exception throwing block", s); Assert.IsFalse(this.logged, "No exception - should not have been logged"); }
public void OnFunction_Exception() { string s = "lalala"; WrapErr.ToErrReport(out ErrReport err, 1111, "Validate arg", () => { s = WrapErr.ToErrorReportException(12345, "Unexpected Error Processing Block", () => { new ChkUtilsTestHelpers.OuterClass().DoNestedException(); return("This should not be"); }); }); this.Validate(err, 12345, "OnFunction_Exception", "Unexpected Error Processing Block"); Assert.AreEqual("lalala", s); Assert.IsTrue(this.logged, "Exception - The log delegate should have fired"); }
public void OnFunction_NoException_MsgFormatInvoked() { string s = ""; bool formatInvoked = false; WrapErr.ToErrReport(out ErrReport err, 1111, "Validate arg", () => { s = WrapErr.ToErrorReportException(12345, () => { formatInvoked = true; return("blah"); }, () => { return("This is a non exception throwing block"); }); }); Assert.AreEqual(0, err.Code); Assert.IsFalse(formatInvoked, "The message formatter should not have been invoked"); Assert.AreEqual("This is a non exception throwing block", s); Assert.IsFalse(this.logged, "No exception - should not have been logged"); }
public void OnFunction_Exception_NoLogging() { WrapErr.InitialiseOnExceptionLogDelegate(null); string s = "lalala"; WrapErr.ToErrReport(out ErrReport err, 1111, "Validate arg", () => { s = WrapErr.ToErrorReportException(12345, "Unexpected Error Processing Block", () => { new ChkUtilsTestHelpers.OuterClass().DoNestedException(); return("This should not be"); }); }); this.Validate(err, 12345, "OnFunction_Exception_NoLogging", "Unexpected Error Processing Block"); Assert.AreEqual("lalala", s); Assert.IsFalse(this.logged, "Exception - But the logger should not have been fired"); }
/// <summary> /// Initialise the state id chain from ancestors to this state /// </summary> /// <param name="parent"></param> /// <param name="id"></param> private void InitStateIds(ISpState parent, int id) { // Add any ancestor state ids to the chain WrapErr.ToErrorReportException(50207, () => { if (parent != null) { WrapErr.ChkVar(parent.IdChain, 50206, "The Parent has a Null Id Chain"); this.idChain.Clear(); parent.IdChain.ForEach((item) => this.idChain.Add(item)); } // This state id is the leaf this.idChain.Add(id); this.BuildName(); }); }
public void OnAction_Exception_FinallyInvoked() { WrapErr.ToErrReport(out ErrReport err, 1111, "Validate arg", () => { WrapErr.ToErrorReportException(12345, "Unexpected Error Processing Block", delegate { new ChkUtilsTestHelpers.OuterClass().DoNestedException(); }, delegate { Console.WriteLine("Immediately before the finally var is set to true"); this.finallyInvoked = true; }); }); this.Validate(err, 12345, "OnAction_Exception_FinallyInvoked", "Unexpected Error Processing Block"); Assert.IsTrue(this.finallyInvoked, "Finally block was not executed on exception"); Assert.IsTrue(this.logged, "Exception - The log delegate should have fired"); }
/// <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); }
public void OnAction_NoException_FinallyInvoked() { ErrReport err; WrapErr.ToErrReport(out err, 1111, "Validate arg", () => { WrapErr.ToErrorReportException(12345, "Unexpected Error Processing Block", () => { Console.WriteLine("This is a non exception throwing block"); }, delegate { this.finallyInvoked = true; }); }); Assert.AreEqual(0, err.Code); Assert.IsTrue(this.finallyInvoked, "Finally block was not executed"); Assert.IsFalse(this.logged, "No exception - should not have been logged"); }
/// <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> /// Always invoked on object exit /// </summary> public void OnExit() { Log.Info(this.className, "OnExit", String.Format("'{0}' State", this.FullName)); // TODO - check that OnEntry has happened ?? WrapErr.ToErrorReportException(9999, () => { // Only execute ExceOnExit code if the state had been entered if (this.IsEntryExcecuted) { this.ExecOnExit(); } else { Log.Warning(9999, String.Format( "ExecOnExit for State:{0} not called because OnEntry was preempted by an OnEvent Transition", this.FullName)); } }, () => { this.isEntered = false; }); }
/// <summary> /// Convert an integer to a generic enum /// </summary> /// <typeparam name="T">The enum type</typeparam> /// <param name="value">The integer value to convert from</param> /// <returns>The Enum or an exception on failure to convert</returns> public static T ToEnum <T>(this int value) where T : struct { T enumType = default(T); T ret = WrapErr.ToErrorReportException(9999, () => { return(String.Format( "Enum Conversion Failed Attempting to Convert to Type '{0}' with Value '{1}'", enumType.GetType().Name, value)); }, () => { // This will throw on non enum but not out of range return((T)Enum.Parse(typeof(T), value.ToString())); }); // Do the enum range check WrapErr.ChkTrue(Enum.IsDefined(typeof(T), ret), 9999, () => { return(String.Format( "Enum Conversion Out of Range Attempting to Convert to Type '{0}' with Value '{1}'", enumType.GetType().Name, value)); }); return(ret); }
/// <summary> /// Dispose managed resources (those with Dispose methods) /// </summary> protected virtual void DisposeManagedResources() { WrapErr.ToErrorReportException(50175, () => this.wrappedObject.Dispose()); }