├── 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 | [](https://ci.appveyor.com/project/trashvin/tutorial-usingstateless) [](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 |
--------------------------------------------------------------------------------