├── Lightswitch ├── .DS_Store ├── LightSwitch.csproj ├── TraditionalLightSwitch.cs ├── StatelessLightSwitch.cs ├── Program.cs └── ALightSwitch.cs ├── State Machine Diagrams ├── AssetWorkflow.png └── Lightswitch.jpg ├── AssetWorkflow ├── AssetWorkflow.csproj ├── AssetInformation.cs ├── Person.cs ├── Program.cs └── Asset.cs ├── Stateless.Tests ├── Stateless.Tests.csproj ├── LightswitchTests.cs └── AssetWorkflowTests.cs ├── README.md ├── Tutorial_UsingStateless.sln └── .gitignore /Lightswitch/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trashvin/Tutorial_UsingStateless/HEAD/Lightswitch/.DS_Store -------------------------------------------------------------------------------- /State Machine Diagrams/AssetWorkflow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trashvin/Tutorial_UsingStateless/HEAD/State Machine Diagrams/AssetWorkflow.png -------------------------------------------------------------------------------- /State Machine Diagrams/Lightswitch.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trashvin/Tutorial_UsingStateless/HEAD/State Machine Diagrams/Lightswitch.jpg -------------------------------------------------------------------------------- /AssetWorkflow/AssetWorkflow.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | netcoreapp1.1 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /Lightswitch/LightSwitch.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | netcoreapp1.1 6 | Lightswitch 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /AssetWorkflow/AssetInformation.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | namespace AssetWorkflow 3 | { 4 | public class AssetInformation 5 | { 6 | public AssetInformation(int assetID, string assetName = "") 7 | { 8 | AssetID = assetID; 9 | AssetName = assetName; 10 | } 11 | 12 | public int AssetID { get; set; } 13 | public string AssetName { get; set; } 14 | public Person Owner { get; set; } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /AssetWorkflow/Person.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | namespace AssetWorkflow 3 | { 4 | public class Person 5 | { 6 | public Person(int personID, string personName, string emailAddress) 7 | { 8 | PersonID = personID; 9 | PersonName = personName; 10 | EmailAddress = emailAddress; 11 | } 12 | 13 | public int PersonID { get; set; } 14 | public string PersonName { get; set; } 15 | public string EmailAddress { get; set; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Stateless.Tests/Stateless.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp1.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # Tutorial Using Stateless 3 | [![Build status](https://ci.appveyor.com/api/projects/status/yfj1xf6qas5w614r?svg=true)](https://ci.appveyor.com/project/trashvin/tutorial-usingstateless) [![Codacy Badge](https://api.codacy.com/project/badge/Grade/82b95eb7915d441dbde3ba48caef79b4)](https://app.codacy.com/app/m3lles/Tutorial_UsingStateless?utm_source=github.com&utm_medium=referral&utm_content=trashvin/Tutorial_UsingStateless&utm_campaign=Badge_Grade_Dashboard) 4 | 5 | Sample C# projects using the [Stateless library](https://github.com/dotnet-state-machine/stateless) 6 | 1. Lightswitch - a demo project that shows 2 lightswitch implementation : 1. Traditional ( using if-else) 2. Stateless ( using Stateless) 7 | 2. AssetWorkflow - a demo project for a simple asset management workflow. 8 | 9 | ## Blog 10 | Read more about this tutorial in my [blog post](https://github.com/trashvin/angular-material-dialog-boxes-sample.git) 11 | 12 | ## Additinal Notes 13 | Project created using VS2017 for Mac. 14 | -------------------------------------------------------------------------------- /Lightswitch/TraditionalLightSwitch.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Stateless; 3 | 4 | namespace Lightswitch 5 | { 6 | public class TraditionalLightSwitch : ALightSwitch 7 | { 8 | public TraditionalLightSwitch(string name, bool initialState = false, bool enforceTimeConstraint = false) 9 | :base(name,initialState, enforceTimeConstraint) 10 | { 11 | _time = new TimeSpan(0, 0, 0); 12 | } 13 | 14 | public override void ToggleSwitch() 15 | { 16 | 17 | if (CurrentState == State.ON) 18 | { 19 | CurrentState = State.OFF; 20 | } 21 | else 22 | { 23 | if(_enforceTimeConstraint) 24 | { 25 | if (IsLightNeeded()) CurrentState = State.ON; 26 | } 27 | else 28 | { 29 | CurrentState = State.ON; 30 | } 31 | 32 | } 33 | 34 | Console.WriteLine("Light is " + CurrentState.ToString()); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Lightswitch/StatelessLightSwitch.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Stateless; 3 | 4 | namespace Lightswitch 5 | { 6 | public class StatelessLightSwitch : ALightSwitch 7 | { 8 | 9 | enum Trigger { TOGGLE }; 10 | 11 | StateMachine _machine; 12 | 13 | public StatelessLightSwitch(string name, bool initialState = false, bool enforceTimeConstraint = false) 14 | : base(name, initialState, enforceTimeConstraint) 15 | { 16 | _time = new TimeSpan(0, 0, 0); 17 | 18 | _machine = new StateMachine(() => CurrentState, s => CurrentState = s); 19 | 20 | _machine.Configure(State.ON) 21 | .Permit(Trigger.TOGGLE, State.OFF); 22 | 23 | _machine.Configure(State.OFF) 24 | .PermitIf(Trigger.TOGGLE, State.ON, () => IsLightNeeded(), "Toggle allowed") 25 | .PermitReentryIf(Trigger.TOGGLE, () => !IsLightNeeded(), "Toggle not allowed"); 26 | 27 | 28 | } 29 | 30 | public override void ToggleSwitch() 31 | { 32 | _machine.Fire(Trigger.TOGGLE); 33 | 34 | Console.WriteLine("Switch is " + CurrentState.ToString()); 35 | } 36 | 37 | public string ShowStateMachine() 38 | { 39 | return _machine.ToDotGraph(); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Lightswitch/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Lightswitch 4 | { 5 | class Program 6 | { 7 | static void Main(string[] args) 8 | { 9 | TraditionalLightSwitch mySwitch1 = new TraditionalLightSwitch("Traditional Switch",true,true); 10 | StatelessLightSwitch stateSwitch = new StatelessLightSwitch("Stateless Switch",true,true); 11 | 12 | Console.WriteLine("\n Toggling traditional switch at predefined time....\n"); 13 | StartSwitchAtDefinedTime(mySwitch1); 14 | 15 | Console.WriteLine("\n Toggling stateless switch at predefined time...\n"); 16 | StartSwitchAtDefinedTime(stateSwitch); 17 | Console.WriteLine("Graph >>>>>>>>>>>."); 18 | Console.WriteLine(stateSwitch.ShowStateMachine()); 19 | 20 | 21 | Console.Read(); 22 | } 23 | 24 | static void StartSwitchAtDefinedTime(ALightSwitch mySwitch) 25 | { 26 | TimeSpan[] times = 27 | { 28 | new TimeSpan(4,30,30), 29 | new TimeSpan(9,45,0), 30 | new TimeSpan(14,2,6), 31 | new TimeSpan(19,21,34), 32 | new TimeSpan(23,46,0) 33 | }; 34 | 35 | foreach(var time in times) 36 | { 37 | Console.WriteLine("Toggling switch at " + time.ToString()); 38 | mySwitch.SetTimeOfDay(time); 39 | mySwitch.ToggleSwitch(); 40 | } 41 | } 42 | 43 | static void StartSwitchNow(ALightSwitch mySwitch) 44 | { 45 | for (int i = 0; i < 3; i++ ) 46 | { 47 | Console.WriteLine("Toggling switch now : "+ DateTime.Now.TimeOfDay.ToString()); 48 | mySwitch.ToggleSwitch(); 49 | } 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Tutorial_UsingStateless.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2012 4 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AssetWorkflow", "AssetWorkflow\AssetWorkflow.csproj", "{0FEB747F-4BE3-472E-A00B-8D014AE71996}" 5 | EndProject 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LightSwitch", "Lightswitch\LightSwitch.csproj", "{461985E7-D8AB-4D00-B904-B63EA2F09CF4}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Stateless.Tests", "Stateless.Tests\Stateless.Tests.csproj", "{AD57A4CB-048D-4DEF-B3BE-310935285C35}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {0FEB747F-4BE3-472E-A00B-8D014AE71996}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {0FEB747F-4BE3-472E-A00B-8D014AE71996}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {0FEB747F-4BE3-472E-A00B-8D014AE71996}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {0FEB747F-4BE3-472E-A00B-8D014AE71996}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {461985E7-D8AB-4D00-B904-B63EA2F09CF4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {461985E7-D8AB-4D00-B904-B63EA2F09CF4}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {461985E7-D8AB-4D00-B904-B63EA2F09CF4}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {461985E7-D8AB-4D00-B904-B63EA2F09CF4}.Release|Any CPU.Build.0 = Release|Any CPU 24 | {AD57A4CB-048D-4DEF-B3BE-310935285C35}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 25 | {AD57A4CB-048D-4DEF-B3BE-310935285C35}.Debug|Any CPU.Build.0 = Debug|Any CPU 26 | {AD57A4CB-048D-4DEF-B3BE-310935285C35}.Release|Any CPU.ActiveCfg = Release|Any CPU 27 | {AD57A4CB-048D-4DEF-B3BE-310935285C35}.Release|Any CPU.Build.0 = Release|Any CPU 28 | EndGlobalSection 29 | EndGlobal 30 | -------------------------------------------------------------------------------- /Lightswitch/ALightSwitch.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Lightswitch 4 | { 5 | public abstract class ALightSwitch 6 | { 7 | protected TimeSpan _time; 8 | protected bool _enforceTimeConstraint; 9 | protected string _name; 10 | 11 | public State CurrentState = State.OFF; 12 | public enum State { ON, OFF }; 13 | 14 | public ALightSwitch(string name,bool initialState = false, bool enableTimeConstraint = false) 15 | { 16 | if (initialState) CurrentState = State.ON; 17 | _enforceTimeConstraint = enableTimeConstraint; 18 | 19 | Console.WriteLine("--------------------------------------"); 20 | Console.WriteLine(name + " switch initial state : " + CurrentState.ToString()); 21 | Console.WriteLine("Enable Time Constraint : " + enableTimeConstraint.ToString()); 22 | 23 | } 24 | 25 | private TimeSpan GetCurrentTime() 26 | { 27 | if (_time == new TimeSpan(0, 0, 0)) 28 | { 29 | return DateTime.Now.TimeOfDay; 30 | } 31 | else 32 | { 33 | return _time; 34 | } 35 | } 36 | 37 | private bool IsDaylight(TimeSpan time) 38 | { 39 | if (time.Hours >= 6 && time.Hours <= 18) 40 | { 41 | return true; 42 | } 43 | else 44 | { 45 | return false; 46 | } 47 | } 48 | 49 | public void SetTimeOfDay(TimeSpan time) 50 | { 51 | _time = time; 52 | 53 | } 54 | 55 | public abstract void ToggleSwitch(); 56 | 57 | protected bool IsLightNeeded() 58 | { 59 | bool result = false; 60 | if (_enforceTimeConstraint) 61 | { 62 | result = !IsDaylight(GetCurrentTime()); 63 | } 64 | else result = true; 65 | 66 | return result; 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /Stateless.Tests/LightswitchTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Xunit; 3 | 4 | using Lightswitch; 5 | using System.ComponentModel; 6 | 7 | namespace Stateless.Tests 8 | { 9 | public class LightswitchTests 10 | { 11 | [Theory] 12 | [InlineData(false, ALightSwitch.State.ON)] 13 | [InlineData(true, ALightSwitch.State.OFF)] 14 | public void TraditionalSwitchWithoutConstraintTest(bool initialState, 15 | Lightswitch.ALightSwitch.State expected) 16 | { 17 | TraditionalLightSwitch testSwitch = new TraditionalLightSwitch("Traditional Switch", initialState); 18 | 19 | testSwitch.ToggleSwitch(); 20 | 21 | Assert.Equal(expected, testSwitch.CurrentState); 22 | } 23 | 24 | [Theory] 25 | [InlineData(false, ALightSwitch.State.ON)] 26 | [InlineData(true, ALightSwitch.State.OFF)] 27 | public void StatelessSwitchWithoutConstraintTest(bool initialState, ALightSwitch.State expected) 28 | { 29 | StatelessLightSwitch testSwitch = new StatelessLightSwitch("Stateless Switch", initialState); 30 | 31 | testSwitch.ToggleSwitch(); 32 | 33 | Assert.Equal(expected, testSwitch.CurrentState); 34 | } 35 | 36 | [Theory] 37 | [InlineData(false, "4:0:0", ALightSwitch.State.ON)] 38 | [InlineData(false, "9:0:0", ALightSwitch.State.OFF)] 39 | [InlineData(true, "3:0:0", ALightSwitch.State.OFF)] 40 | [InlineData(true, "11:0:0", ALightSwitch.State.OFF)] 41 | [InlineData(true, "19:0:0", ALightSwitch.State.OFF)] 42 | [InlineData(false, "22:0:0", ALightSwitch.State.ON)] 43 | public void TraditionalSwitchWithConstraintTest(bool initialState, string testTime, ALightSwitch.State expected) 44 | { 45 | TraditionalLightSwitch testSwitch = new TraditionalLightSwitch("Traditinal Switch", initialState, true); 46 | TimeSpan time = TimeSpan.Parse(testTime); 47 | 48 | testSwitch.SetTimeOfDay(time); 49 | testSwitch.ToggleSwitch(); 50 | 51 | Assert.Equal(expected, testSwitch.CurrentState); 52 | 53 | } 54 | 55 | [Theory] 56 | [InlineData(false, "4:0:0", ALightSwitch.State.ON)] 57 | [InlineData(false, "9:0:0", ALightSwitch.State.OFF)] 58 | [InlineData(true, "3:0:0", ALightSwitch.State.OFF)] 59 | [InlineData(true, "11:0:0", ALightSwitch.State.OFF)] 60 | [InlineData(true, "19:0:0", ALightSwitch.State.OFF)] 61 | [InlineData(false, "22:0:0", ALightSwitch.State.ON)] 62 | public void StatelessSwitchWithConstraintTest(bool initialState, string testTime, ALightSwitch.State expected) 63 | { 64 | StatelessLightSwitch testSwitch = new StatelessLightSwitch("Traditinal Switch", initialState, true); 65 | TimeSpan time = TimeSpan.Parse(testTime); 66 | 67 | testSwitch.SetTimeOfDay(time); 68 | testSwitch.ToggleSwitch(); 69 | 70 | Assert.Equal(expected, testSwitch.CurrentState); 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /Stateless.Tests/AssetWorkflowTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Xunit; 3 | 4 | using AssetWorkflow; 5 | using System.Collections.Generic; 6 | 7 | 8 | 9 | namespace Stateless.Tests 10 | { 11 | 12 | public class AssetWorkflowTests : IDisposable 13 | { 14 | private List _owners; 15 | private List _assets; 16 | 17 | public AssetWorkflowTests() 18 | { 19 | LoadTestData(); 20 | } 21 | 22 | private void LoadTestData() 23 | { 24 | Asset testAsset1 = new Asset(new AssetInformation(1, "Test 1")); 25 | Asset testAsset2 = new Asset(new AssetInformation(2, "Test 2")); 26 | Asset testAsset3 = new Asset(new AssetInformation(3, "Test 3")); 27 | Asset testAsset4 = new Asset(new AssetInformation(4, "Test 4")); 28 | 29 | Person owner1 = new Person(1, "Test", "test@test.com"); 30 | Person owner2 = new Person(2, "QA", "qa@test.com"); 31 | 32 | _owners = new List(); 33 | _assets = new List(); 34 | 35 | _owners.Add(owner1); 36 | _owners.Add(owner2); 37 | 38 | _assets.Add(testAsset1); 39 | _assets.Add(testAsset2); 40 | _assets.Add(testAsset3); 41 | _assets.Add(testAsset4); 42 | } 43 | 44 | public void Dispose() 45 | { 46 | } 47 | 48 | [Fact] 49 | public void FinishedTestingTriggerTest() 50 | { 51 | LoadTestData(); 52 | 53 | Asset toTest = _assets[0]; 54 | 55 | toTest.AssetState = Asset.State.New; 56 | toTest.FinishedTesting(); 57 | 58 | Assert.Equal(Asset.State.Available, toTest.AssetState); 59 | } 60 | 61 | [Theory] 62 | [InlineData(0, 1)] 63 | [InlineData(1, 2)] 64 | public void AssignTriggerTest(int ownerIndex, int expectedID) 65 | { 66 | LoadTestData(); 67 | 68 | Asset toTest = _assets[0]; 69 | 70 | toTest.AssetState = Asset.State.Available; 71 | toTest.Assign(_owners[ownerIndex]); 72 | 73 | Assert.Equal(Asset.State.Allocated, toTest.AssetState); 74 | Assert.Equal(expectedID, toTest.AssetData.Owner.PersonID); 75 | } 76 | 77 | [Theory] 78 | [InlineData(0, 1, 2)] 79 | [InlineData(1, 0, 1)] 80 | public void TransferTriggerTest(int oldOwner, int newOwner, int expectedID) 81 | { 82 | LoadTestData(); 83 | 84 | Asset toTest = _assets[0]; 85 | toTest.AssetState = Asset.State.Available; 86 | 87 | toTest.Assign(_owners[oldOwner]); 88 | toTest.Transfer(_owners[newOwner]); 89 | 90 | Assert.Equal(Asset.State.Allocated, toTest.AssetState); 91 | Assert.Equal(expectedID, toTest.AssetData.Owner.PersonID); 92 | } 93 | 94 | 95 | //TODO : Add more test scenario 96 | [Theory] 97 | [InlineData(Asset.State.New, Asset.Trigger.Released)] 98 | [InlineData(Asset.State.New, Asset.Trigger.Discarded)] 99 | [InlineData(Asset.State.New, Asset.Trigger.Found)] 100 | [InlineData(Asset.State.New, Asset.Trigger.Repaired)] 101 | [InlineData(Asset.State.New, Asset.Trigger.RequestRepair)] 102 | [InlineData(Asset.State.New, Asset.Trigger.RequestUpdate)] 103 | [InlineData(Asset.State.New, Asset.Trigger.Transferred)] 104 | [InlineData(Asset.State.Unavailable, Asset.Trigger.Assigned)] 105 | [InlineData(Asset.State.Unavailable, Asset.Trigger.Lost)] 106 | [InlineData(Asset.State.Unavailable, Asset.Trigger.RequestRepair)] 107 | [InlineData(Asset.State.Unavailable, Asset.Trigger.RequestUpdate)] 108 | [InlineData(Asset.State.Unavailable, Asset.Trigger.Tested)] 109 | public void InvalidTriggerTest(Asset.State initialState, Asset.Trigger trigger) 110 | { 111 | LoadTestData(); 112 | 113 | Asset toTest = _assets[0]; 114 | toTest.AssetState = initialState; 115 | toTest.Fire(trigger); 116 | 117 | Assert.True(toTest.AssetState == initialState); 118 | 119 | 120 | } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | [Xx]64/ 19 | [Xx]86/ 20 | [Bb]uild/ 21 | bld/ 22 | [Bb]in/ 23 | [Oo]bj/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | # NUNIT 35 | *.VisualState.xml 36 | TestResult.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | artifacts/ 46 | 47 | *_i.c 48 | *_p.c 49 | *_i.h 50 | *.ilk 51 | *.meta 52 | *.obj 53 | *.pch 54 | *.pdb 55 | *.pgc 56 | *.pgd 57 | *.rsp 58 | *.sbr 59 | *.tlb 60 | *.tli 61 | *.tlh 62 | *.tmp 63 | *.tmp_proj 64 | *.log 65 | *.vspscc 66 | *.vssscc 67 | .builds 68 | *.pidb 69 | *.svclog 70 | *.scc 71 | 72 | # Chutzpah Test files 73 | _Chutzpah* 74 | 75 | # Visual C++ cache files 76 | ipch/ 77 | *.aps 78 | *.ncb 79 | *.opendb 80 | *.opensdf 81 | *.sdf 82 | *.cachefile 83 | *.VC.db 84 | 85 | # Visual Studio profiler 86 | *.psess 87 | *.vsp 88 | *.vspx 89 | *.sap 90 | 91 | # TFS 2012 Local Workspace 92 | $tf/ 93 | 94 | # Guidance Automation Toolkit 95 | *.gpState 96 | 97 | # ReSharper is a .NET coding add-in 98 | _ReSharper*/ 99 | *.[Rr]e[Ss]harper 100 | *.DotSettings.user 101 | 102 | # JustCode is a .NET coding add-in 103 | .JustCode 104 | 105 | # TeamCity is a build add-in 106 | _TeamCity* 107 | 108 | # DotCover is a Code Coverage Tool 109 | *.dotCover 110 | 111 | # NCrunch 112 | _NCrunch_* 113 | .*crunch*.local.xml 114 | nCrunchTemp_* 115 | 116 | # MightyMoose 117 | *.mm.* 118 | AutoTest.Net/ 119 | 120 | # Web workbench (sass) 121 | .sass-cache/ 122 | 123 | # Installshield output folder 124 | [Ee]xpress/ 125 | 126 | # DocProject is a documentation generator add-in 127 | DocProject/buildhelp/ 128 | DocProject/Help/*.HxT 129 | DocProject/Help/*.HxC 130 | DocProject/Help/*.hhc 131 | DocProject/Help/*.hhk 132 | DocProject/Help/*.hhp 133 | DocProject/Help/Html2 134 | DocProject/Help/html 135 | 136 | # Click-Once directory 137 | publish/ 138 | 139 | # Publish Web Output 140 | *.[Pp]ublish.xml 141 | *.azurePubxml 142 | 143 | # TODO: Un-comment the next line if you do not want to checkin 144 | # your web deploy settings because they may include unencrypted 145 | # passwords 146 | #*.pubxml 147 | *.publishproj 148 | 149 | # NuGet Packages 150 | *.nupkg 151 | # The packages folder can be ignored because of Package Restore 152 | **/packages/* 153 | # except build/, which is used as an MSBuild target. 154 | !**/packages/build/ 155 | # Uncomment if necessary however generally it will be regenerated when needed 156 | #!**/packages/repositories.config 157 | # NuGet v3's project.json files produces more ignoreable files 158 | *.nuget.props 159 | *.nuget.targets 160 | 161 | # Microsoft Azure Build Output 162 | csx/ 163 | *.build.csdef 164 | 165 | # Microsoft Azure Emulator 166 | ecf/ 167 | rcf/ 168 | 169 | # Windows Store app package directory 170 | AppPackages/ 171 | BundleArtifacts/ 172 | 173 | # Visual Studio cache files 174 | # files ending in .cache can be ignored 175 | *.[Cc]ache 176 | # but keep track of directories ending in .cache 177 | !*.[Cc]ache/ 178 | 179 | # Others 180 | ClientBin/ 181 | [Ss]tyle[Cc]op.* 182 | ~$* 183 | *~ 184 | *.dbmdl 185 | *.dbproj.schemaview 186 | *.pfx 187 | *.publishsettings 188 | node_modules/ 189 | orleans.codegen.cs 190 | 191 | # RIA/Silverlight projects 192 | Generated_Code/ 193 | 194 | # Backup & report files from converting an old project file 195 | # to a newer Visual Studio version. Backup files are not needed, 196 | # because we have git ;-) 197 | _UpgradeReport_Files/ 198 | Backup*/ 199 | UpgradeLog*.XML 200 | UpgradeLog*.htm 201 | 202 | # SQL Server files 203 | *.mdf 204 | *.ldf 205 | 206 | # Business Intelligence projects 207 | *.rdl.data 208 | *.bim.layout 209 | *.bim_*.settings 210 | 211 | # Microsoft Fakes 212 | FakesAssemblies/ 213 | 214 | # GhostDoc plugin setting file 215 | *.GhostDoc.xml 216 | 217 | # Node.js Tools for Visual Studio 218 | .ntvs_analysis.dat 219 | 220 | # Visual Studio 6 build log 221 | *.plg 222 | 223 | # Visual Studio 6 workspace options file 224 | *.opt 225 | 226 | # Visual Studio LightSwitch build output 227 | **/*.HTMLClient/GeneratedArtifacts 228 | **/*.DesktopClient/GeneratedArtifacts 229 | **/*.DesktopClient/ModelManifest.xml 230 | **/*.Server/GeneratedArtifacts 231 | **/*.Server/ModelManifest.xml 232 | _Pvt_Extensions 233 | 234 | # LightSwitch generated files 235 | GeneratedArtifacts/ 236 | ModelManifest.xml 237 | 238 | # Paket dependency manager 239 | .paket/paket.exe 240 | 241 | # FAKE - F# Make 242 | .fake/ 243 | 244 | # Mac 245 | .DS_Store 246 | -------------------------------------------------------------------------------- /AssetWorkflow/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | 4 | using static System.Console; 5 | using System.Collections.Generic; 6 | using System.Text; 7 | 8 | namespace AssetWorkflow 9 | { 10 | class Program 11 | { 12 | private static List assetList = new List(); 13 | public static int counter = 1; 14 | 15 | static void Main() 16 | { 17 | 18 | int choice =0; 19 | while ( choice != 4) 20 | { 21 | choice = ShowMainMenu(); 22 | switch (choice) 23 | { 24 | case 1: 25 | WriteLine("Creating asset.."); 26 | CreateAsset(); 27 | break; 28 | case 2: 29 | WriteLine("Managing assets..."); 30 | ManageAssets(); 31 | break; 32 | case 3: 33 | WriteLine("Listing assets..."); 34 | ListAssets(); 35 | break; 36 | case 4: 37 | WriteLine("Exiting....."); 38 | break; 39 | default: 40 | break; 41 | } 42 | } 43 | 44 | 45 | } 46 | 47 | private static void CreateAsset() 48 | { 49 | AssetInformation assInfo = new AssetInformation(counter); 50 | counter++; 51 | assInfo.AssetName = "Asset " + assInfo.AssetID.ToString(); 52 | 53 | Asset tempAsset = new Asset(assInfo); 54 | assetList.Add(tempAsset); 55 | } 56 | 57 | private static void ListAssets() 58 | { 59 | foreach(Asset asset in assetList) 60 | { 61 | StringBuilder data = new StringBuilder(); 62 | data.Append(asset.AssetData.AssetID.ToString() + " : "); 63 | data.Append(asset.AssetData.AssetName + " : "); 64 | 65 | if (asset.AssetData.Owner != null) 66 | { 67 | data.Append(asset.AssetData.Owner.EmailAddress + " : "); 68 | } 69 | 70 | data.Append(asset.AssetState.ToString()); 71 | 72 | WriteLine(data.ToString()); 73 | } 74 | } 75 | 76 | private static int ShowMainMenu() 77 | { 78 | int choice = 0; 79 | WriteLine("Asset Manager v1"); 80 | 81 | do 82 | { 83 | WriteLine("Menu : (1) Create Asset (2) Manage Asset (3) List Assets (4)Exit"); 84 | 85 | string ch = ReadLine(); 86 | Int32.TryParse(ch,out choice); 87 | } while (choice < 1 || choice > 4); 88 | 89 | return choice; 90 | } 91 | 92 | private static void ManageAssets() 93 | { 94 | string assetID; 95 | ListAssets(); 96 | 97 | WriteLine("Select asset to manage."); 98 | Write("Asset ID :"); 99 | assetID = ReadLine(); 100 | 101 | string valid = "ABCDEFGHIJK"; 102 | 103 | if (IsAssetExist(assetID)) 104 | { 105 | string choice = "X"; 106 | WriteLine("Asset Management"); 107 | 108 | do 109 | { 110 | WriteLine("Menu : (A) Test (B) Assign (C) Repair (D) Upgrade (E) Release (F) Transfer (G) Repaired (H) Discard (I) Lost (J) Found (K) Exit"); 111 | 112 | choice = ReadLine().ToUpper(); 113 | 114 | 115 | } while (!valid.Contains(choice)); 116 | 117 | Asset asset = assetList.Find(i => i.AssetData.AssetID.ToString() == assetID); 118 | 119 | if ( choice == "K") 120 | { 121 | WriteLine("Exiting asset management."); 122 | Console.WriteLine(asset.GetDOTGraph()); 123 | 124 | } 125 | else 126 | { 127 | ManageAsset(asset, choice); 128 | } 129 | } 130 | else 131 | { 132 | WriteLine("Asset not found."); 133 | } 134 | } 135 | 136 | private static void ManageAsset(Asset asset,string action) 137 | { 138 | switch(action) 139 | { 140 | case "A": 141 | asset.FinishedTesting(); 142 | break; 143 | case "B": 144 | asset.Assign(GetOwner()); 145 | break; 146 | case "C": 147 | asset.RequestRepair(); 148 | break; 149 | case "D": 150 | asset.RequestUpdate(); 151 | break; 152 | case "E": 153 | asset.Release(); 154 | break; 155 | case "F": 156 | asset.Transfer(GetOwner()); 157 | break; 158 | case "G": 159 | asset.Repaired(); 160 | break; 161 | case "H": 162 | asset.Discard(); 163 | break; 164 | case "I": 165 | asset.Lost(); 166 | break; 167 | case "J": 168 | asset.Found(); 169 | break; 170 | default: 171 | break; 172 | } 173 | 174 | WriteLine($"Success : {asset.IsSuccessful().ToString()}"); 175 | } 176 | private static Person GetOwner() 177 | { 178 | int id = counter; 179 | Console.WriteLine("Enter name : "); 180 | 181 | string name = Console.ReadLine(); 182 | string email = name.Replace(" ", "") + "@email.com"; 183 | counter++; 184 | 185 | return new Person(id, name, email); 186 | } 187 | 188 | private static bool IsAssetExist(string assetID) 189 | { 190 | int id = 0; 191 | Int32.TryParse(assetID,out id); 192 | 193 | return assetList.Exists(i => i.AssetData.AssetID == id); 194 | } 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /AssetWorkflow/Asset.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Stateless; 3 | using System.Runtime.ExceptionServices; 4 | using System.Runtime.InteropServices; 5 | 6 | namespace AssetWorkflow 7 | { 8 | public class Asset 9 | { 10 | public enum State { New, Available, Allocated, UnderMaintenance,Unavailable, Decommissioned }; 11 | public enum Trigger { Tested, Assigned, Released, RequestRepair, RequestUpdate,Transferred, Repaired, Lost, Discarded, Found }; 12 | 13 | protected State _state; 14 | protected StateMachine _machine; 15 | protected State _previousState; 16 | protected bool _isSuccesful; 17 | 18 | public AssetInformation AssetData { get; set; } 19 | public Person OwnerData { get; set; } 20 | 21 | protected StateMachine.TriggerWithParameters _assignTrigger; 22 | protected StateMachine.TriggerWithParameters _transferTrigger; 23 | 24 | 25 | public State AssetState 26 | { 27 | get 28 | { 29 | return _state; 30 | } 31 | set 32 | { 33 | _previousState = _state; 34 | _state = value; 35 | Console.WriteLine("------------------------"); 36 | Console.WriteLine($"Asset No : {AssetData.AssetID.ToString()}"); 37 | Console.WriteLine($"Previous asset state : {_previousState.ToString()}"); 38 | Console.WriteLine($"New asset state : {_state.ToString()}" ); 39 | } 40 | } 41 | 42 | public Asset(AssetInformation data) 43 | { 44 | InitializeStateMachine(); 45 | AssetData = data; 46 | 47 | } 48 | 49 | private void InitializeStateMachine() 50 | { 51 | _state = State.New; 52 | 53 | 54 | _machine = new StateMachine(() => AssetState, s => AssetState = s); 55 | 56 | _assignTrigger = _machine.SetTriggerParameters(Trigger.Assigned); 57 | _transferTrigger = _machine.SetTriggerParameters(Trigger.Transferred); 58 | 59 | 60 | _machine.Configure(State.New) 61 | .Permit(Trigger.Tested, State.Available) 62 | .OnEntry(()=>OnEntry()) 63 | .OnActivate(() => OnActivate()) 64 | .Permit(Trigger.Lost, State.Unavailable) 65 | .OnDeactivate(()=>OnDeactivate()) 66 | .OnExit(() => OnExit()); 67 | 68 | _machine.Configure(State.Available) 69 | .OnEntry(() => OnEntry()) 70 | .OnActivate(() => OnActivate()) 71 | .Permit(Trigger.Assigned, State.Allocated) 72 | .Permit(Trigger.Lost, State.Unavailable) 73 | .OnExit(() => OnExit()) 74 | .OnEntryFrom(Trigger.Found,()=> ProcessFound()) 75 | .OnEntryFrom(Trigger.Released,() => ProcessDecommission()) 76 | .OnDeactivate(() => OnDeactivate()); 77 | 78 | 79 | _machine.Configure(State.Allocated) 80 | .OnEntry(()=>OnEntry()) 81 | .OnEntryFrom(_assignTrigger, owner => SetOwner(owner)) 82 | .OnEntryFrom(_transferTrigger, owner => SetOwner(owner)) 83 | .OnActivate(()=> OnActivate()) 84 | .OnExit(()=>OnExit()) 85 | .OnDeactivate(()=>OnDeactivate()) 86 | .PermitReentry(Trigger.Transferred) 87 | .Permit(Trigger.Released, State.Available) 88 | .Permit(Trigger.RequestRepair, State.UnderMaintenance) 89 | .Permit(Trigger.RequestUpdate, State.UnderMaintenance) 90 | .Permit(Trigger.Lost, State.Unavailable); 91 | 92 | _machine.Configure(State.UnderMaintenance) 93 | .OnEntry(() => OnEntry()) 94 | .OnActivate(() => OnActivate()) 95 | .OnExit(() => OnExit()) 96 | .OnDeactivate(() => OnDeactivate()) 97 | .Permit(Trigger.Repaired, State.Allocated) 98 | .Permit(Trigger.Lost,State.Unavailable) 99 | .Permit(Trigger.Discarded, State.Decommissioned); 100 | 101 | 102 | _machine.Configure(State.Unavailable) 103 | .OnEntry(() => OnEntry()) 104 | .OnActivate(() => OnActivate()) 105 | .OnExit(() => OnExit()) 106 | .OnDeactivate(() => OnDeactivate()) 107 | .PermitIf(Trigger.Found, State.Available,()=>(_previousState != State.New)) 108 | .PermitIf(Trigger.Found,State.New,()=>(_previousState == State.New)); 109 | 110 | _machine.Configure(State.Decommissioned) 111 | .OnEntry(() => ProcessDecommission()) 112 | .OnActivate(() => OnActivate()) 113 | .OnExit(() => OnExit()) 114 | .OnDeactivate(() => OnDeactivate()); 115 | 116 | 117 | 118 | 119 | } 120 | 121 | private void SetOwner(Person owner) 122 | { 123 | AssetData.Owner = owner; 124 | } 125 | 126 | private void ProcessDecommission() 127 | { 128 | Console.WriteLine("Clearing owner date.."); 129 | AssetData.Owner = null; 130 | OnEntry(); 131 | } 132 | 133 | private void ProcessFound() 134 | { 135 | if (AssetData.Owner != null) 136 | { 137 | Console.WriteLine("Clearing the owner data..."); 138 | AssetData.Owner = null; 139 | } 140 | } 141 | 142 | private void OnEntry() 143 | { 144 | Console.WriteLine($"Entering {_state.ToString()} ..."); 145 | } 146 | 147 | private void OnActivate() 148 | { 149 | Console.WriteLine($"Activating {_state.ToString()} ..."); 150 | } 151 | 152 | private void OnDeactivate() 153 | { 154 | Console.WriteLine($"Deactivating {_state.ToString()} ..."); 155 | } 156 | 157 | private void OnExit() 158 | { 159 | Console.WriteLine($"Exiting {_state.ToString()} ..."); 160 | } 161 | 162 | public void Fire(Trigger trigger) 163 | { 164 | _isSuccesful = false; 165 | try 166 | { 167 | _machine.Fire(trigger); 168 | _isSuccesful = true; 169 | } 170 | catch 171 | { 172 | Console.WriteLine("Error during state transition."); 173 | _isSuccesful = false; 174 | } 175 | } 176 | 177 | public void FinishedTesting() 178 | { 179 | Fire(Trigger.Tested); 180 | } 181 | 182 | public void Assign(Person owner) 183 | { 184 | _isSuccesful = false; 185 | try 186 | { 187 | _machine.Fire(_assignTrigger, owner); 188 | _isSuccesful = true; 189 | } 190 | catch 191 | { 192 | Console.WriteLine("Error during state transition."); 193 | _isSuccesful = false; 194 | } 195 | } 196 | 197 | public void Release() 198 | { 199 | Fire(Trigger.Released); 200 | } 201 | 202 | public void Repaired() 203 | { 204 | Fire(Trigger.Repaired); 205 | } 206 | 207 | public void RequestRepair() 208 | { 209 | Fire(Trigger.RequestRepair); 210 | } 211 | 212 | public void RequestUpdate() 213 | { 214 | Fire(Trigger.RequestUpdate); 215 | } 216 | 217 | public void Transfer(Person owner) 218 | { 219 | _isSuccesful = false; 220 | try 221 | { 222 | _machine.Fire(_transferTrigger,owner); 223 | _isSuccesful = true; 224 | } 225 | catch 226 | { 227 | Console.WriteLine("Error during state transition."); 228 | _isSuccesful = false; 229 | } 230 | } 231 | 232 | public void Lost() 233 | { 234 | Fire(Trigger.Lost); 235 | } 236 | 237 | public void Found() 238 | { 239 | Fire(Trigger.Found); 240 | } 241 | 242 | public void Discard() 243 | { 244 | Fire(Trigger.Discarded); 245 | } 246 | 247 | public string GetDOTGraph() 248 | { 249 | return _machine.ToDotGraph(); 250 | } 251 | 252 | public bool IsSuccessful() 253 | { 254 | return _isSuccesful; 255 | } 256 | 257 | } 258 | } 259 | --------------------------------------------------------------------------------