├── .gitignore
├── CODE-OF-CONDUCT.md
├── Documentation
└── FlowChart.odg
├── Examples
├── NStateManager.Example.Sale.Console
│ ├── App.config
│ ├── NStateManager.Example.Sale.Console.csproj
│ ├── POSv1.dia
│ ├── POSv1.png
│ ├── POSv2.dia
│ ├── POSv2.png
│ ├── Payment.cs
│ ├── Phone.dia
│ ├── Phone.pdf
│ ├── Phone.png
│ ├── Program.cs
│ ├── Properties
│ │ └── AssemblyInfo.cs
│ ├── Sale.cs
│ ├── SaleItem.cs
│ └── SaleStateMachine.cs
└── NStateManager.Example.Sale
│ ├── ChangeTransaction.cs
│ ├── NStateManager.Example.Sale.csproj
│ ├── Payment.cs
│ ├── Properties
│ └── AssemblyInfo.cs
│ ├── Sale.cs
│ ├── SaleEvent.cs
│ ├── SaleItem.cs
│ ├── SaleItemState.cs
│ ├── SaleState.cs
│ ├── SaleStateManager.cs
│ ├── Tests.cs
│ └── packages.config
├── LICENSE
├── NSM Individual Contributor License Agreement.docx
├── README.md
├── Source
├── NStateManager.sln
├── NStateManager.sln.DotSettings
├── NStateManager.sln.licenseheader
└── NStateManager
│ ├── AssemblyInfo.cs
│ ├── Async
│ ├── IStateConfiguration.cs
│ ├── IStateConfigurationInternal.cs
│ ├── IStateMachine.cs
│ ├── StateConfiguration.cs
│ ├── StateMachine.cs
│ ├── StateTransition.cs
│ ├── StateTransitionAutoDynamic.cs
│ ├── StateTransitionAutoDynamicParameterized.cs
│ ├── StateTransitionAutoFallback.cs
│ ├── StateTransitionAutoFallbackParameterized.cs
│ ├── StateTransitionAutoForward.cs
│ ├── StateTransitionAutoForwardParameterized.cs
│ └── StateTransitionParameterized.cs
│ ├── ExecutionParameters.cs
│ ├── Export
│ ├── ConfigurationSummary.cs
│ ├── CsvExporter.cs
│ ├── DotGraphExporter.cs
│ ├── IStateMachine.cs
│ ├── IStateMachineAsync.cs
│ ├── StateDetails.cs
│ ├── StateMachine.cs
│ ├── StateMachineAsync.cs
│ └── TransitionDetails.cs
│ ├── Extensions.cs
│ ├── FunctionAction.cs
│ ├── FunctionActionBase.cs
│ ├── FunctionActionParameterized.cs
│ ├── NStateManager.csproj
│ ├── NStateManager.nuspec
│ ├── StateConfigurationBase.cs
│ ├── StateTransitionBase.cs
│ ├── StateTransitionDynamic.cs
│ ├── StateTransitionDynamicAsync.cs
│ ├── StateTransitionDynamicBase.cs
│ ├── StateTransitionDynamicParameterized.cs
│ ├── StateTransitionDynamicParameterizedAsync.cs
│ ├── StateTransitionFactory.cs
│ ├── StateTransitionNonDynamic.cs
│ ├── StateTransitionResult.cs
│ ├── Sync
│ ├── IStateConfiguration.cs
│ ├── IStateConfigurationInternal.cs
│ ├── IStateMachine.cs
│ ├── StateConfiguration.cs
│ ├── StateMachine.cs
│ ├── StateTransition.cs
│ ├── StateTransitionAutoDynamic.cs
│ ├── StateTransitionAutoDynamicParameterized.cs
│ ├── StateTransitionAutoFallback.cs
│ ├── StateTransitionAutoFallbackParameterized.cs
│ ├── StateTransitionAutoForward.cs
│ ├── StateTransitionAutoForwardParameterized.cs
│ └── StateTransitionParameterized.cs
│ ├── TransitionEventArgs.cs
│ ├── TriggerAction.cs
│ ├── TriggerActionBase.cs
│ ├── TriggerActionFactory.cs
│ └── TriggerActionParameterized.cs
├── Tests
└── NStateManager.Tests
│ ├── Async
│ ├── StateConfigurationTests.cs
│ ├── StateMachineTests.cs
│ ├── StateTransitionAutoDynamicParameterizedTests.cs
│ ├── StateTransitionAutoDynamicTests.cs
│ ├── StateTransitionAutoFallbackParameterizedTests.cs
│ ├── StateTransitionAutoFallbackTests.cs
│ ├── StateTransitionAutoForwardParameterizedTests.cs
│ ├── StateTransitionAutoForwardTests.cs
│ ├── StateTransitionParameterizedTests.cs
│ └── StateTransitionTests.cs
│ ├── AutoFallbackTest.cs
│ ├── ExecutionParametersTests.cs
│ ├── Export
│ ├── ConfigurationSummaryTests.cs
│ ├── CsvExporterTests.cs
│ ├── DotGraphExporterTests.cs
│ ├── StateDetailsTests.cs
│ ├── StateMachineAsyncTests.cs
│ ├── StateMachineTests.cs
│ └── StateTransitionDetailsTests.cs
│ ├── FunctionActionParameterizedTests.cs
│ ├── FunctionActionTests.cs
│ ├── NStateManager.Tests.csproj
│ ├── Phone.cs
│ ├── Properties
│ └── AssemblyInfo.cs
│ ├── Sale.cs
│ ├── SaleComplex.cs
│ ├── SaleComplexEvent.cs
│ ├── SaleComplexState.cs
│ ├── SimpleTests.cs
│ ├── StateConfigurationBaseTests.cs
│ ├── StateTransitionBaseTests.cs
│ ├── StateTransitionDynamicAsyncTests.cs
│ ├── StateTransitionDynamicParameterizedAsyncTests.cs
│ ├── StateTransitionFactoryTests.cs
│ ├── StateTransitionParameterizedTests.cs
│ ├── StateTransitionTests.cs
│ ├── Sync
│ ├── PhoneCall.cs
│ ├── StateConfigurationTests.cs
│ ├── StateMachineTests.cs
│ ├── StateTransitionAutoDynamicParameterizedTests.cs
│ ├── StateTransitionAutoDynamicTests.cs
│ ├── StateTransitionAutoFallbackParameterizedTests.cs
│ ├── StateTransitionAutoFallbackTests.cs
│ ├── StateTransitionAutoForwardParameterizedTests.cs
│ ├── StateTransitionAutoForwardTests.cs
│ ├── StateTransitionDynamicParameterizedTests.cs
│ ├── StateTransitionDynamicTests.cs
│ └── SubStateTest.cs
│ ├── TestChange.cs
│ ├── TestPayment.cs
│ ├── TestProduct.cs
│ ├── TestSale.cs
│ ├── TestSaleEvent.cs
│ ├── TestSaleItem.cs
│ ├── TestSaleState.cs
│ ├── TriggerActionParameterizedTests.cs
│ └── packages.config
└── appveyor.yml
/CODE-OF-CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as
6 | contributors and maintainers pledge to making participation in our project and
7 | our community a harassment-free experience for everyone, regardless of age, body
8 | size, disability, ethnicity, gender identity and expression, level of experience,
9 | nationality, personal appearance, race, religion, or sexual identity and
10 | orientation.
11 |
12 | ## Our Standards
13 |
14 | Examples of behavior that contributes to creating a positive environment
15 | include:
16 |
17 | * Using welcoming and inclusive language
18 | * Being respectful of differing viewpoints and experiences
19 | * Gracefully accepting constructive criticism
20 | * Focusing on what is best for the community
21 | * Showing empathy towards other community members
22 |
23 | Examples of unacceptable behavior by participants include:
24 |
25 | * The use of sexualized language or imagery and unwelcome sexual attention or
26 | advances
27 | * Trolling, insulting/derogatory comments, and personal or political attacks
28 | * Public or private harassment
29 | * Publishing others' private information, such as a physical or electronic
30 | address, without explicit permission
31 | * Other conduct which could reasonably be considered inappropriate in a
32 | professional setting
33 |
34 | ## Our Responsibilities
35 |
36 | Project maintainers are responsible for clarifying the standards of acceptable
37 | behavior and are expected to take appropriate and fair corrective action in
38 | response to any instances of unacceptable behavior.
39 |
40 | Project maintainers have the right and responsibility to remove, edit, or
41 | reject comments, commits, code, wiki edits, issues, and other contributions
42 | that are not aligned to this Code of Conduct, or to ban temporarily or
43 | permanently any contributor for other behaviors that they deem inappropriate,
44 | threatening, offensive, or harmful.
45 |
46 | ## Scope
47 |
48 | This Code of Conduct applies both within project spaces and in public spaces
49 | when an individual is representing the project or its community. Examples of
50 | representing a project or community include using an official project email
51 | address, posting via an official social media account, or acting as an appointed
52 | representative at an online or offline event. Representation of a project may be
53 | further defined and clarified by project maintainers.
54 |
55 | ## Enforcement
56 |
57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
58 | reported by contacting the project team at [scottctr@gmail.com][email]. All
59 | complaints will be reviewed and investigated and will result in a response that
60 | is deemed necessary and appropriate to the circumstances. The project team is
61 | obligated to maintain confidentiality with regard to the reporter of an incident.
62 | Further details of specific enforcement policies may be posted separately.
63 |
64 | Project maintainers who do not follow or enforce the Code of Conduct in good
65 | faith may face temporary or permanent repercussions as determined by other
66 | members of the project's leadership.
67 |
68 | ## Attribution
69 |
70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71 | available at [http://contributor-covenant.org/version/1/4][version]
72 |
73 | [homepage]: http://contributor-covenant.org
74 | [version]: http://contributor-covenant.org/version/1/4/
75 | [email]: mailto:scottctr@gmail.com
76 |
--------------------------------------------------------------------------------
/Documentation/FlowChart.odg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scottctr/NStateManager/6b26ae8a221a029120088eebf9a08d785ce3750d/Documentation/FlowChart.odg
--------------------------------------------------------------------------------
/Examples/NStateManager.Example.Sale.Console/App.config:
--------------------------------------------------------------------------------
1 |
2 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/Examples/NStateManager.Example.Sale.Console/NStateManager.Example.Sale.Console.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | net472
4 | Exe
5 | true
6 | false
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/Examples/NStateManager.Example.Sale.Console/POSv1.dia:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scottctr/NStateManager/6b26ae8a221a029120088eebf9a08d785ce3750d/Examples/NStateManager.Example.Sale.Console/POSv1.dia
--------------------------------------------------------------------------------
/Examples/NStateManager.Example.Sale.Console/POSv1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scottctr/NStateManager/6b26ae8a221a029120088eebf9a08d785ce3750d/Examples/NStateManager.Example.Sale.Console/POSv1.png
--------------------------------------------------------------------------------
/Examples/NStateManager.Example.Sale.Console/POSv2.dia:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scottctr/NStateManager/6b26ae8a221a029120088eebf9a08d785ce3750d/Examples/NStateManager.Example.Sale.Console/POSv2.dia
--------------------------------------------------------------------------------
/Examples/NStateManager.Example.Sale.Console/POSv2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scottctr/NStateManager/6b26ae8a221a029120088eebf9a08d785ce3750d/Examples/NStateManager.Example.Sale.Console/POSv2.png
--------------------------------------------------------------------------------
/Examples/NStateManager.Example.Sale.Console/Payment.cs:
--------------------------------------------------------------------------------
1 | #region Copyright (c) 2018 Scott L. Carter
2 | //
3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in
4 | //compliance with the License. You may obtain a copy of the License at
5 | //http://www.apache.org/licenses/LICENSE-2.0
6 | //
7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is
8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | //See the License for the specific language governing permissions and limitations under the License.
10 | #endregion
11 | namespace NStateManager.Example.Sale.Console
12 | {
13 | public class Payment
14 | {
15 | public double Amount { get; }
16 |
17 | public Payment(double amount)
18 | {
19 | Amount = amount;
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/Examples/NStateManager.Example.Sale.Console/Phone.dia:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scottctr/NStateManager/6b26ae8a221a029120088eebf9a08d785ce3750d/Examples/NStateManager.Example.Sale.Console/Phone.dia
--------------------------------------------------------------------------------
/Examples/NStateManager.Example.Sale.Console/Phone.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scottctr/NStateManager/6b26ae8a221a029120088eebf9a08d785ce3750d/Examples/NStateManager.Example.Sale.Console/Phone.pdf
--------------------------------------------------------------------------------
/Examples/NStateManager.Example.Sale.Console/Phone.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scottctr/NStateManager/6b26ae8a221a029120088eebf9a08d785ce3750d/Examples/NStateManager.Example.Sale.Console/Phone.png
--------------------------------------------------------------------------------
/Examples/NStateManager.Example.Sale.Console/Program.cs:
--------------------------------------------------------------------------------
1 | #region Copyright (c) 2018 Scott L. Carter
2 | //
3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in
4 | //compliance with the License. You may obtain a copy of the License at
5 | //http://www.apache.org/licenses/LICENSE-2.0
6 | //
7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is
8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | //See the License for the specific language governing permissions and limitations under the License.
10 | #endregion
11 | namespace NStateManager.Example.Sale.Console
12 | {
13 | internal class Program
14 | {
15 | private static void Main(string[] args)
16 | {
17 | var sale = new Sale(saleId: 1);
18 | SaleStateMachine.AddItem(sale, new SaleItem("Magazine", price: 3.00));
19 | SaleStateMachine.AddItem(sale, new SaleItem("Fuel", price: 10.00));
20 | SaleStateMachine.AddPayment(sale, new Payment(amount: 10.00));
21 | SaleStateMachine.AddPayment(sale, new Payment(amount: 10.00));
22 | SaleStateMachine.ReturnChange(sale, new Payment(amount: -7.00));
23 | //We should be in Complete state here, so the following actions should be ignored
24 | SaleStateMachine.AddItem(sale, new SaleItem("Magazine", 3.00));
25 | SaleStateMachine.AddItem(sale, new SaleItem("Fuel", 10.00));
26 |
27 | System.Console.Read();
28 | }
29 | }
30 | }
31 |
32 |
--------------------------------------------------------------------------------
/Examples/NStateManager.Example.Sale.Console/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | #region Copyright (c) 2018 Scott L. Carter
2 | //
3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in
4 | //compliance with the License. You may obtain a copy of the License at
5 | //http://www.apache.org/licenses/LICENSE-2.0
6 | //
7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is
8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | //See the License for the specific language governing permissions and limitations under the License.
10 | #endregion
11 | using System.Reflection;
12 | using System.Runtime.InteropServices;
13 |
14 | // General Information about an assembly is controlled through the following
15 | // set of attributes. Change these attribute values to modify the information
16 | // associated with an assembly.
17 | [assembly: AssemblyTitle("NStateManager.Sale.Console")]
18 | [assembly: AssemblyDescription("")]
19 | [assembly: AssemblyConfiguration("")]
20 | [assembly: AssemblyCompany("")]
21 | [assembly: AssemblyProduct("NStateManager.Sale.Console")]
22 | [assembly: AssemblyCopyright("Copyright © 2018")]
23 | [assembly: AssemblyTrademark("")]
24 | [assembly: AssemblyCulture("")]
25 |
26 | // Setting ComVisible to false makes the types in this assembly not visible
27 | // to COM components. If you need to access a type in this assembly from
28 | // COM, set the ComVisible attribute to true on that type.
29 | [assembly: ComVisible(false)]
30 |
31 | // The following GUID is for the ID of the typelib if this project is exposed to COM
32 | [assembly: Guid("04c3f5c3-375f-4607-b9c5-44f4a7bf7833")]
33 |
34 | // Version information for an assembly consists of the following four values:
35 | //
36 | // Major Version
37 | // Minor Version
38 | // Build Number
39 | // Revision
40 | //
41 | // You can specify all the values or you can default the Build and Revision Numbers
42 | // by using the '*' as shown below:
43 | // [assembly: AssemblyVersion("1.0.*")]
44 | [assembly: AssemblyVersion("1.0.0.0")]
45 | [assembly: AssemblyFileVersion("1.0.0.0")]
46 |
--------------------------------------------------------------------------------
/Examples/NStateManager.Example.Sale.Console/Sale.cs:
--------------------------------------------------------------------------------
1 | #region Copyright (c) 2018 Scott L. Carter
2 | //
3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in
4 | //compliance with the License. You may obtain a copy of the License at
5 | //http://www.apache.org/licenses/LICENSE-2.0
6 | //
7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is
8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | //See the License for the specific language governing permissions and limitations under the License.
10 | #endregion
11 | using System.Collections.Generic;
12 | using System.Linq;
13 |
14 | namespace NStateManager.Example.Sale.Console
15 | {
16 | public enum SaleState
17 | {
18 | Open,
19 | ChangeDue,
20 | Complete
21 | }
22 |
23 | public enum SaleEvent
24 | {
25 | AddItem,
26 | Pay,
27 | ChangeGiven,
28 | SaleCancelled
29 | }
30 |
31 | public class Sale
32 | {
33 | public double Balance { get; private set; }
34 | public List Items { get; } = new List();
35 | public List Payments { get; } = new List();
36 | public SaleState State { get; set; }
37 | public int SaleId { get; }
38 |
39 | public Sale(int saleId)
40 | {
41 | SaleId = saleId;
42 | }
43 |
44 | public void AddItem(SaleItem item)
45 | {
46 | Items.Add(item);
47 | updateBalance();
48 | }
49 |
50 | public void AddPayment(Payment payment)
51 | {
52 | Payments.Add(payment);
53 | updateBalance();
54 | }
55 |
56 | public void ReturnChange()
57 | {
58 | AddPayment(new Payment(Balance));
59 | }
60 |
61 | private void updateBalance()
62 | {
63 | Balance = Items.Sum(item => item.Price) - Payments.Sum(payment => payment.Amount);
64 | }
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/Examples/NStateManager.Example.Sale.Console/SaleItem.cs:
--------------------------------------------------------------------------------
1 | #region Copyright (c) 2018 Scott L. Carter
2 | //
3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in
4 | //compliance with the License. You may obtain a copy of the License at
5 | //http://www.apache.org/licenses/LICENSE-2.0
6 | //
7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is
8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | //See the License for the specific language governing permissions and limitations under the License.
10 | #endregion
11 | namespace NStateManager.Example.Sale.Console
12 | {
13 | public class SaleItem
14 | {
15 | public string Name { get; }
16 | public double Price { get; }
17 |
18 | public SaleItem(string name, double price)
19 | {
20 | Name = name;
21 | Price = price;
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/Examples/NStateManager.Example.Sale.Console/SaleStateMachine.cs:
--------------------------------------------------------------------------------
1 | #region Copyright (c) 2018 Scott L. Carter
2 | //
3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in
4 | //compliance with the License. You may obtain a copy of the License at
5 | //http://www.apache.org/licenses/LICENSE-2.0
6 | //
7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is
8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | //See the License for the specific language governing permissions and limitations under the License.
10 | #endregion
11 | using NStateManager.Sync;
12 | using System;
13 | using Output = System.Console;
14 |
15 | namespace NStateManager.Example.Sale.Console
16 | {
17 | public static class SaleStateMachine
18 | {
19 | private static readonly StateMachine StateMachine;
20 |
21 | static SaleStateMachine()
22 | {
23 | //State machine to manage sales for a point of sale system
24 | StateMachine = new StateMachine(
25 | stateAccessor: (sale) => sale.State
26 | ,stateMutator: (sale, state) => sale.State = state);
27 |
28 | //Log each time a sale changes state regardless of to/from state
29 | StateMachine.OnTransition += (sender, args) => Output.WriteLine($"Sale {args.Parameters.Context.SaleId} transitioned from {args.TransitionResult.PreviousState} to {args.TransitionResult.CurrentState}.");
30 |
31 | //Configure the Open state
32 | StateMachine.ConfigureState(SaleState.Open)
33 | //Process the new item on the AddItem event
34 | .AddTriggerAction(SaleEvent.AddItem, (sale, saleItem) =>
35 | {
36 | sale.AddItem(saleItem);
37 | Output.WriteLine($"Adding {saleItem.Name} for {saleItem.Price:C}. {getSaleStatus(sale)}");
38 | })
39 | //Process the payment on the Pay event
40 | .AddTriggerAction(SaleEvent.Pay, (sale, payment) =>
41 | {
42 | sale.AddPayment(payment);
43 | Output.WriteLine($"Adding payment of {payment.Amount:C}. {getSaleStatus(sale)}");
44 | })
45 | //Transition to the ChangeDue state if customer over pays
46 | .AddTransition(SaleEvent.Pay, SaleState.ChangeDue, condition: sale => sale.Balance < 0, name: "Open2ChangeDue", priority: 1)
47 | //Transition to the Completed state if customer pays exact amount
48 | .AddTransition(SaleEvent.Pay, SaleState.Complete, condition: sale => Math.Abs(sale.Balance) < .005, name: "Open2Complete", priority: 2);
49 |
50 | //Configure the ChangeDue state
51 | StateMachine.ConfigureState(SaleState.ChangeDue)
52 | //Process the returned change on the ChangeGiven event
53 | .AddTriggerAction(SaleEvent.ChangeGiven, (sale, payment) =>
54 | {
55 | sale.ReturnChange();
56 | Output.WriteLine($"Returning change of {payment.Amount:C}. {getSaleStatus(sale)}");
57 | })
58 | //Transition to the Complete state on ChangeGiven -- no conditions
59 | .AddTransition(SaleEvent.ChangeGiven, SaleState.Complete);
60 |
61 | //No configuration required for Complete state since it's a final state and
62 | //transitions or actions are taken at this point
63 | }
64 |
65 | private static string getSaleStatus(Sale sale)
66 | {
67 | return $"\r\n--> SaleState: {sale.State} \tBalance: {sale.Balance:$#,##0.00}.";
68 | }
69 |
70 | public static void AddItem(Sale sale, SaleItem saleItem)
71 | {
72 | StateMachine.FireTrigger(sale, SaleEvent.AddItem, saleItem);
73 | }
74 |
75 | public static void AddPayment(Sale sale, Payment payment)
76 | {
77 | StateMachine.FireTrigger(sale, SaleEvent.Pay, payment);
78 | }
79 |
80 | public static void ReturnChange(Sale sale, Payment payment)
81 | {
82 | StateMachine.FireTrigger(sale, SaleEvent.ChangeGiven, payment);
83 | }
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/Examples/NStateManager.Example.Sale/ChangeTransaction.cs:
--------------------------------------------------------------------------------
1 | #region Copyright (c) 2018 Scott L. Carter
2 | //
3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in
4 | //compliance with the License. You may obtain a copy of the License at
5 | //http://www.apache.org/licenses/LICENSE-2.0
6 | //
7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is
8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | //See the License for the specific language governing permissions and limitations under the License.
10 | #endregion
11 | namespace NStateManager.Example.Sale
12 | {
13 | public class ChangeTransaction
14 | {
15 | public decimal Amount { get; }
16 |
17 | public ChangeTransaction(decimal amount)
18 | {
19 | Amount = amount;
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/Examples/NStateManager.Example.Sale/NStateManager.Example.Sale.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | net5.0-windows
4 | Exe
5 | true
6 | true
7 | false
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 | all
28 | runtime; build; native; contentfiles; analyzers; buildtransitive
29 |
30 |
31 |
--------------------------------------------------------------------------------
/Examples/NStateManager.Example.Sale/Payment.cs:
--------------------------------------------------------------------------------
1 | #region Copyright (c) 2018 Scott L. Carter
2 | //
3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in
4 | //compliance with the License. You may obtain a copy of the License at
5 | //http://www.apache.org/licenses/LICENSE-2.0
6 | //
7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is
8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | //See the License for the specific language governing permissions and limitations under the License.
10 | #endregion
11 | namespace NStateManager.Example.Sale
12 | {
13 | public class Payment
14 | {
15 | public decimal Amount { get; }
16 |
17 | public Payment(decimal amount)
18 | {
19 | Amount = amount;
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/Examples/NStateManager.Example.Sale/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | #region Copyright (c) 2018 Scott L. Carter
2 | //
3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in
4 | //compliance with the License. You may obtain a copy of the License at
5 | //http://www.apache.org/licenses/LICENSE-2.0
6 | //
7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is
8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | //See the License for the specific language governing permissions and limitations under the License.
10 | #endregion
11 | using System.Reflection;
12 | using System.Runtime.InteropServices;
13 |
14 | // General Information about an assembly is controlled through the following
15 | // set of attributes. Change these attribute values to modify the information
16 | // associated with an assembly.
17 | [assembly: AssemblyTitle("NStateManager.Example.Sale")]
18 | [assembly: AssemblyDescription("")]
19 | [assembly: AssemblyConfiguration("")]
20 | [assembly: AssemblyCompany("")]
21 | [assembly: AssemblyProduct("NStateManager.Example.Sale")]
22 | [assembly: AssemblyCopyright("Copyright © 2018")]
23 | [assembly: AssemblyTrademark("")]
24 | [assembly: AssemblyCulture("")]
25 |
26 | // Setting ComVisible to false makes the types in this assembly not visible
27 | // to COM components. If you need to access a type in this assembly from
28 | // COM, set the ComVisible attribute to true on that type.
29 | [assembly: ComVisible(false)]
30 |
31 | // The following GUID is for the ID of the typelib if this project is exposed to COM
32 | [assembly: Guid("43520112-f273-4bd7-8bf8-5c5f1d474556")]
33 |
34 | // Version information for an assembly consists of the following four values:
35 | //
36 | // Major Version
37 | // Minor Version
38 | // Build Number
39 | // Revision
40 | //
41 | // You can specify all the values or you can default the Build and Revision Numbers
42 | // by using the '*' as shown below:
43 | // [assembly: AssemblyVersion("1.0.*")]
44 | [assembly: AssemblyVersion("1.0.0.0")]
45 | [assembly: AssemblyFileVersion("1.0.0.0")]
46 |
--------------------------------------------------------------------------------
/Examples/NStateManager.Example.Sale/SaleEvent.cs:
--------------------------------------------------------------------------------
1 | #region Copyright (c) 2018 Scott L. Carter
2 | //
3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in
4 | //compliance with the License. You may obtain a copy of the License at
5 | //http://www.apache.org/licenses/LICENSE-2.0
6 | //
7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is
8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | //See the License for the specific language governing permissions and limitations under the License.
10 | #endregion
11 | namespace NStateManager.Example.Sale
12 | {
13 | public enum SaleEvent
14 | {
15 | AddItem,
16 | AddPayment,
17 | ItemDelivered,
18 | ChangeGiven,
19 | Cancel
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Examples/NStateManager.Example.Sale/SaleItem.cs:
--------------------------------------------------------------------------------
1 | #region Copyright (c) 2018 Scott L. Carter
2 | //
3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in
4 | //compliance with the License. You may obtain a copy of the License at
5 | //http://www.apache.org/licenses/LICENSE-2.0
6 | //
7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is
8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | //See the License for the specific language governing permissions and limitations under the License.
10 | #endregion
11 | namespace NStateManager.Example.Sale
12 | {
13 | public class SaleItem
14 | {
15 | public decimal Price { get; }
16 |
17 | public bool IsAuthorized => !IsCancelled && State >= SaleItemState.Authorized;
18 |
19 | public bool IsCancelled => State == SaleItemState.Cancelled;
20 |
21 | public bool IsDelivered => State == SaleItemState.Delivered;
22 |
23 | public bool IsRemovableAfterDelivery { get; internal set; } = true;
24 |
25 | public bool RequiresDelivery { get; internal set; } = false;
26 |
27 | public SaleItemState State { get; internal set; }
28 |
29 | public SaleItem(decimal price, SaleItemState state = SaleItemState.NotDelivered)
30 | {
31 | Price = price;
32 | State = state;
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/Examples/NStateManager.Example.Sale/SaleItemState.cs:
--------------------------------------------------------------------------------
1 | #region Copyright (c) 2018 Scott L. Carter
2 | //
3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in
4 | //compliance with the License. You may obtain a copy of the License at
5 | //http://www.apache.org/licenses/LICENSE-2.0
6 | //
7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is
8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | //See the License for the specific language governing permissions and limitations under the License.
10 | #endregion
11 | namespace NStateManager.Example.Sale
12 | {
13 | public enum SaleItemState
14 | {
15 | NotDelivered,
16 | Authorized,
17 | Delivered,
18 | Cancelled
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/Examples/NStateManager.Example.Sale/SaleState.cs:
--------------------------------------------------------------------------------
1 | #region Copyright (c) 2018 Scott L. Carter
2 | //
3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in
4 | //compliance with the License. You may obtain a copy of the License at
5 | //http://www.apache.org/licenses/LICENSE-2.0
6 | //
7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is
8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | //See the License for the specific language governing permissions and limitations under the License.
10 | #endregion
11 | namespace NStateManager.Example.Sale
12 | {
13 | public enum SaleState
14 | {
15 | Opened,
16 | Authorized,
17 | WaitingForDelivery,
18 | ChangeDue,
19 | PartiallyAuthorized,
20 | Cancelled,
21 | Completed
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/Examples/NStateManager.Example.Sale/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/NSM Individual Contributor License Agreement.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scottctr/NStateManager/6b26ae8a221a029120088eebf9a08d785ce3750d/NSM Individual Contributor License Agreement.docx
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # NStateManager [](https://ci.appveyor.com/project/ScottCarter/nstatemanager) [](https://www.nuget.org/packages/NStateManager) [](https://sonarcloud.io/dashboard?id=NStateManager) [](https://sonarcloud.io/dashboard?id=NStateManager) [](https://sonarcloud.io/dashboard?id=NStateManager)
2 | # Background and Goals
3 | **NStateManager** is a .Net library for managing:
4 | - [definition of states](https://github.com/scottctr/NStateManager/wiki/Quick-Start#states) for a type
5 | - [rules for transitioning](https://github.com/scottctr/NStateManager/wiki/Changing-States) between states
6 | - additional [actions to take](https://github.com/scottctr/NStateManager/wiki/Event-Actions) based on trigger events and current state
7 |
8 | This project was created while developing cloud-based solutions. We needed a state management solution made for our stateless services.
9 | - **Stateless** so it's thread safe and a single state machine can be used to manage state for all managed instances
10 | - **Intuitive configuration** of [state change conditions](https://github.com/scottctr/NStateManager/wiki/Changing-States), [behaviors based on current state](https://github.com/scottctr/NStateManager/wiki/Event-Actions), and [actions when changing states](https://github.com/scottctr/NStateManager/wiki/State-Change-Actions)
11 | - **[async/await support](https://github.com/scottctr/NStateManager/wiki/Async-Await-Support)**, including cancellation and ConfigureAwait
12 | # Quick Example
13 | Managing state of a Sale for a simple point-of-sale system.
14 |
15 | 
16 | ### Configuring the state machine
17 | ```C#
18 | //State machine to manage sales for a point of sale system
19 | stateMachine = new StateMachine(
20 | stateAccessor: (sale) => sale.State, //stateAccessor is used to retrieve the current state
21 | stateMutator: (sale, state) => sale.State = state); //stateMutator updates state based on transition rule below
22 |
23 | //Log each time a sale changes state regardless of to/from state
24 | stateMachine.RegisterOnTransitionedAction((sale, transitionDetails)
25 | => Output.WriteLine($"Sale {sale.SaleID} transitioned from {transitionDetails.PreviousState} to {transitionDetails.CurrentState}."));
26 |
27 | //Configure the Open state
28 | stateMachine.ConfigureState(SaleState.Open)
29 | //Process the new item on the AddItem event
30 | .AddTriggerAction(SaleEvent.AddItem, (sale, saleItem) => { sale.AddItem(saleItem); })
31 | //Process the payment on the Pay event
32 | .AddTriggerAction(SaleEvent.Pay, (sale, payment) => { sale.AddPayment(payment); })
33 | //Transition to the ChangeDue state on Pay event if customer over pays
34 | .AddTransition(SaleEvent.Pay, SaleState.ChangeDue, condition: sale => sale.Balance < 0, name: "Open2ChangeDue", priority: 1)
35 | //Transition to the Completed state on Pay event if customer pays exact amount
36 | .AddTransition(SaleEvent.Pay, SaleState.Complete, condition: sale => sale.Balance == 0, name: "Open2Complete", priority: 2);
37 |
38 | //Configure the ChangeDue state
39 | stateMachine.ConfigureState(SaleState.ChangeDue)
40 | //Process the returned change on the ChangeGiven event
41 | .AddTriggerAction(SaleEvent.ChangeGiven, (sale, payment) => { sale.ReturnChange(); })
42 | //Transition to the Complete state on ChangeGiven -- no conditions
43 | .AddTransition(SaleEvent.ChangeGiven, SaleState.Complete);
44 |
45 | //No configuration required for Complete state since it's a final state and
46 | //no state transitions or actions are allowed at this point
47 | ```
48 | ### Using the state machine
49 | ```C#
50 | //Add an item to a sale
51 | stateMachine.FireTrigger(sale, SaleEvent.AddItem, saleItem);
52 |
53 | //Add a payment to a sale
54 | stateMachine.FireTrigger(sale, SaleEvent.Pay, payment);
55 |
56 | //Give the customer their change
57 | stateMachine.FireTrigger(sale, SaleEvent.ChangeGiven, payment);
58 | ```
59 | For a walkthough of the above code, go to the [Quick Start](https://github.com/scottctr/NStateManager/wiki/Quick-Start). You can also look at the [Wiki](https://github.com/scottctr/NStateManager/wiki) for additional details.
60 | # Feedback
61 | Feedback, questions, advice, and contributions are always welcomed so feel free to leave your thoughts in [Issues](https://github.com/scottctr/NStateManager/issues).
62 |
--------------------------------------------------------------------------------
/Source/NStateManager.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 16
4 | VisualStudioVersion = 16.0.30717.126
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NStateManager", "NStateManager\NStateManager.csproj", "{32ABF9D2-580A-4D63-BC38-8EAD7E8B405A}"
7 | EndProject
8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NStateManager.Example.Sale.Console", "..\Examples\NStateManager.Example.Sale.Console\NStateManager.Example.Sale.Console.csproj", "{04C3F5C3-375F-4607-B9C5-44F4A7BF7833}"
9 | EndProject
10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NStateManager.Example.Sale", "..\Examples\NStateManager.Example.Sale\NStateManager.Example.Sale.csproj", "{43520112-F273-4BD7-8BF8-5C5F1D474556}"
11 | EndProject
12 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NStateManager.Tests", "..\Tests\NStateManager.Tests\NStateManager.Tests.csproj", "{2ADDF230-A48B-462B-A1D4-CD91D9EC5F6F}"
13 | EndProject
14 | Global
15 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
16 | Debug|Any CPU = Debug|Any CPU
17 | Release|Any CPU = Release|Any CPU
18 | EndGlobalSection
19 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
20 | {32ABF9D2-580A-4D63-BC38-8EAD7E8B405A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
21 | {32ABF9D2-580A-4D63-BC38-8EAD7E8B405A}.Debug|Any CPU.Build.0 = Debug|Any CPU
22 | {32ABF9D2-580A-4D63-BC38-8EAD7E8B405A}.Release|Any CPU.ActiveCfg = Release|Any CPU
23 | {32ABF9D2-580A-4D63-BC38-8EAD7E8B405A}.Release|Any CPU.Build.0 = Release|Any CPU
24 | {04C3F5C3-375F-4607-B9C5-44F4A7BF7833}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
25 | {04C3F5C3-375F-4607-B9C5-44F4A7BF7833}.Debug|Any CPU.Build.0 = Debug|Any CPU
26 | {04C3F5C3-375F-4607-B9C5-44F4A7BF7833}.Release|Any CPU.ActiveCfg = Release|Any CPU
27 | {04C3F5C3-375F-4607-B9C5-44F4A7BF7833}.Release|Any CPU.Build.0 = Release|Any CPU
28 | {43520112-F273-4BD7-8BF8-5C5F1D474556}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
29 | {43520112-F273-4BD7-8BF8-5C5F1D474556}.Debug|Any CPU.Build.0 = Debug|Any CPU
30 | {43520112-F273-4BD7-8BF8-5C5F1D474556}.Release|Any CPU.ActiveCfg = Release|Any CPU
31 | {43520112-F273-4BD7-8BF8-5C5F1D474556}.Release|Any CPU.Build.0 = Release|Any CPU
32 | {2ADDF230-A48B-462B-A1D4-CD91D9EC5F6F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
33 | {2ADDF230-A48B-462B-A1D4-CD91D9EC5F6F}.Debug|Any CPU.Build.0 = Debug|Any CPU
34 | {2ADDF230-A48B-462B-A1D4-CD91D9EC5F6F}.Release|Any CPU.ActiveCfg = Release|Any CPU
35 | {2ADDF230-A48B-462B-A1D4-CD91D9EC5F6F}.Release|Any CPU.Build.0 = Release|Any CPU
36 | EndGlobalSection
37 | GlobalSection(SolutionProperties) = preSolution
38 | HideSolutionNode = FALSE
39 | EndGlobalSection
40 | GlobalSection(ExtensibilityGlobals) = postSolution
41 | SolutionGuid = {BDB508A4-9C98-4F2D-9E3A-A0270F42F3DB}
42 | EndGlobalSection
43 | EndGlobal
44 |
--------------------------------------------------------------------------------
/Source/NStateManager.sln.DotSettings:
--------------------------------------------------------------------------------
1 |
2 | <data><IncludeFilters /><ExcludeFilters><Filter ModuleMask="NStateManager.Example.Sale.Console" ModuleVersionMask="*" ClassMask="*" FunctionMask="*" IsEnabled="True" /><Filter ModuleMask="*.Tests" ModuleVersionMask="*" ClassMask="*" FunctionMask="*" IsEnabled="True" /></ExcludeFilters></data>
3 | <data />
--------------------------------------------------------------------------------
/Source/NStateManager.sln.licenseheader:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scottctr/NStateManager/6b26ae8a221a029120088eebf9a08d785ce3750d/Source/NStateManager.sln.licenseheader
--------------------------------------------------------------------------------
/Source/NStateManager/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | #region Copyright (c) 2018 Scott L. Carter
2 | //
3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in
4 | //compliance with the License. You may obtain a copy of the License at
5 | //http://www.apache.org/licenses/LICENSE-2.0
6 | //
7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is
8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | //See the License for the specific language governing permissions and limitations under the License.
10 | #endregion
11 | using System.Reflection;
12 | using System.Runtime.CompilerServices;
13 | using System.Runtime.InteropServices;
14 |
15 | // General Information about an assembly is controlled through the following
16 | // set of attributes. Change these attribute values to modify the information
17 | // associated with an assembly.
18 | [assembly: AssemblyTitle("NStateManager")]
19 | [assembly: AssemblyDescription("")]
20 | [assembly: AssemblyConfiguration("")]
21 | [assembly: AssemblyCompany("Scott L Carter")]
22 | [assembly: AssemblyProduct("NStateManager")]
23 | [assembly: AssemblyCopyright("Copyright © 2019")]
24 | [assembly: AssemblyTrademark("")]
25 | [assembly: AssemblyCulture("")]
26 | [assembly: InternalsVisibleTo("NStateManager.Tests")]
27 |
28 | // Setting ComVisible to false makes the types in this assembly not visible
29 | // to COM components. If you need to access a type in this assembly from
30 | // COM, set the ComVisible attribute to true on that type.
31 | [assembly: ComVisible(false)]
32 |
33 | // The following GUID is for the ID of the typelib if this project is exposed to COM
34 | [assembly: Guid("38251772-c63b-4815-af8f-1920ac0f12bb")]
35 |
36 | // Version information for an assembly consists of the following four values:
37 | //
38 | // Major Version
39 | // Minor Version
40 | // Build Number
41 | // Revision
42 | //
43 | [assembly: AssemblyVersion("4.0.1")]
44 | [assembly: AssemblyFileVersion("4.0.1")]
45 |
--------------------------------------------------------------------------------
/Source/NStateManager/Async/IStateConfigurationInternal.cs:
--------------------------------------------------------------------------------
1 | #region Copyright (c) 2018 Scott L. Carter
2 | //
3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in
4 | //compliance with the License. You may obtain a copy of the License at
5 | //http://www.apache.org/licenses/LICENSE-2.0
6 | //
7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is
8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | //See the License for the specific language governing permissions and limitations under the License.
10 | #endregion
11 | using System.Threading.Tasks;
12 |
13 | namespace NStateManager.Async
14 | {
15 | public interface IStateConfigurationInternal : IStateConfiguration
16 | {
17 | void AddAutoTransition(TTrigger trigger, StateTransitionBase transition);
18 | void AddSuperstate(IStateConfigurationInternal superStateConfiguration);
19 | void AddTransition(TTrigger trigger, StateTransitionBase transition);
20 | Task> ExecuteAutoTransitionAsync(ExecutionParameters parameters
21 | , StateTransitionResult currentResult);
22 | Task ExecuteEntryActionAsync(ExecutionParameters parameters, StateTransitionResult currentResult);
23 | Task ExecuteExitActionAsync(ExecutionParameters parameters, StateTransitionResult currentResult);
24 | Task ExecuteReentryActionAsync(ExecutionParameters parameters, StateTransitionResult currentResult);
25 | Task> FireTriggerAsync(ExecutionParameters parameters);
26 |
27 | bool IsSubState();
28 |
29 | ///
30 | /// Returns true if the given state is the same as the current state or the current state is a subset of the given state; otherwise false.
31 | ///
32 | /// The state to compare against.
33 | ///
34 | bool IsSubStateOf(TState state);
35 | IStateConfigurationInternal SuperStateConfig { get; }
36 | TState State { get; }
37 | }
38 | }
--------------------------------------------------------------------------------
/Source/NStateManager/Async/IStateMachine.cs:
--------------------------------------------------------------------------------
1 | #region Copyright (c) 2018 Scott L. Carter
2 | //
3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in
4 | //compliance with the License. You may obtain a copy of the License at
5 | //http://www.apache.org/licenses/LICENSE-2.0
6 | //
7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is
8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | //See the License for the specific language governing permissions and limitations under the License.
10 | #endregion
11 | using System;
12 | using System.Threading;
13 | using System.Threading.Tasks;
14 |
15 | namespace NStateManager.Async
16 | {
17 | public partial interface IStateMachine
18 | where TState : IComparable
19 | {
20 | ///
21 | /// Defines an action to take any time occurs.
22 | ///
23 | /// The for the action.
24 | /// The action to execute.
25 | /// also has trigger actions that should only occur while T is in a specific state.
26 | ///
27 | IStateMachine AddTriggerAction(TTrigger trigger, Func action);
28 |
29 | ///
30 | /// Defines an action to take any time occurs.
31 | ///
32 | /// Parameter to be passed in from FireTrigger.
33 | /// The for the action.
34 | /// The action to execute.
35 | /// also has trigger actions that should only occur while T is in a specific state.
36 | ///
37 | IStateMachine AddTriggerAction(TTrigger trigger, Func action);
38 |
39 | ///
40 | /// Configures a specified .
41 | ///
42 | /// The to configure.
43 | ///
44 | IStateConfiguration ConfigureState(TState state);
45 |
46 | ///
47 | /// Executes trigger asynchronously with a parameter.
48 | ///
49 | /// The items whose state is being managed.
50 | /// The event that has occurred that may affect the state.
51 | /// The details of the event that's occurring.
52 | /// Provides the ability to cancel an asynchronous operation.
53 | ///
54 | Task> FireTriggerAsync(T context, TTrigger trigger, TRequest request, CancellationToken cancellationToken = default)
55 | where TRequest : class;
56 |
57 | ///
58 | /// Executes trigger.
59 | ///
60 | /// The items whose state is being managed.
61 | /// The event that has occurred that may affect the state.
62 | /// Provides the ability to cancel an asynchronous operation.
63 | ///
64 | Task> FireTriggerAsync(T context, TTrigger trigger, CancellationToken cancellationToken = default);
65 |
66 | bool IsInState(T context, TState state);
67 |
68 | Func StateAccessor { get; }
69 | Action StateMutator { get; }
70 |
71 | ///
72 | /// Event raised when the context transitions to a new state when FireTrigger is called.
73 | ///
74 | event EventHandler> OnTransition;
75 |
76 | ///
77 | /// Event raised when the context doesn't transition to a new state when FireTrigger is called.
78 | ///
79 | event EventHandler> OnNoTransition;
80 |
81 | ///
82 | /// Event raised when the context's current state isn't configured for the trigger passed to FireTrigger.
83 | ///
84 | event EventHandler> OnTriggerNotConfigured;
85 | }
86 | }
--------------------------------------------------------------------------------
/Source/NStateManager/Async/StateTransition.cs:
--------------------------------------------------------------------------------
1 | #region Copyright (c) 2018 Scott L. Carter
2 | //
3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in
4 | //compliance with the License. You may obtain a copy of the License at
5 | //http://www.apache.org/licenses/LICENSE-2.0
6 | //
7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is
8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | //See the License for the specific language governing permissions and limitations under the License.
10 | #endregion
11 | using System;
12 | using System.Threading;
13 | using System.Threading.Tasks;
14 |
15 | namespace NStateManager.Async
16 | {
17 | internal class StateTransition : StateTransitionNonDynamic
18 | {
19 | public Func> ConditionAsync { get; }
20 | public bool HasCondition { get; }
21 |
22 | public StateTransition(Func stateAccessor, Action stateMutator, TState toState, Func> conditionAsync, string name, uint priority)
23 | : base(stateAccessor, stateMutator, toState, name, priority)
24 | {
25 | if (conditionAsync == null)
26 | { return; }
27 |
28 | ConditionAsync = conditionAsync;
29 | HasCondition = true;
30 | }
31 |
32 | public override async Task> ExecuteAsync(ExecutionParameters parameters
33 | , StateTransitionResult currentResult = null)
34 | {
35 | var startState = currentResult != null ? currentResult.StartingState : StateAccessor(parameters.Context);
36 |
37 | if (parameters.CancellationToken.IsCancellationRequested)
38 | { return GetFreshResult(parameters, currentResult, startState, wasCancelled: true, transitionDefined: true, conditionMet: false); }
39 |
40 | if (HasCondition && !await ConditionAsync(parameters.Context, parameters.CancellationToken)
41 | .ConfigureAwait(continueOnCapturedContext: false))
42 | { return GetFreshResult(parameters, currentResult, startState, wasCancelled: parameters.CancellationToken.IsCancellationRequested, transitionDefined: true, conditionMet: false); }
43 |
44 | StateMutator(parameters.Context, ToState);
45 | var transitionResult = GetFreshResult(parameters, currentResult, startState, wasCancelled: false, conditionMet: true, transitionDefined: true);
46 |
47 | return transitionResult;
48 | }
49 | }
50 | }
--------------------------------------------------------------------------------
/Source/NStateManager/Async/StateTransitionAutoDynamic.cs:
--------------------------------------------------------------------------------
1 | #region Copyright (c) 2018 Scott L. Carter
2 | //
3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in
4 | //compliance with the License. You may obtain a copy of the License at
5 | //http://www.apache.org/licenses/LICENSE-2.0
6 | //
7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is
8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | //See the License for the specific language governing permissions and limitations under the License.
10 | #endregion
11 | using System;
12 | using System.Threading.Tasks;
13 |
14 | namespace NStateManager.Async
15 | {
16 | internal class StateTransitionAutoDynamic : StateTransitionDynamicBase
17 | where TState : IComparable
18 | {
19 | private readonly IStateMachine _stateMachine;
20 | private readonly TState _startState;
21 | private readonly TState _triggerState;
22 | private readonly Func _stateFunction;
23 |
24 | public StateTransitionAutoDynamic(IStateMachine stateMachine
25 | , TState startStartState
26 | , Func targetStateFunction
27 | , TState triggerState
28 | , string name
29 | , uint priority)
30 | : base(stateMachine.StateAccessor, stateMachine.StateMutator, name, priority)
31 | {
32 | _stateMachine = stateMachine;
33 | _startState = startStartState;
34 | _triggerState = triggerState;
35 | _stateFunction = targetStateFunction;
36 | }
37 |
38 | public override Task> ExecuteAsync(ExecutionParameters parameters
39 | , StateTransitionResult currentResult = null)
40 | {
41 | if (currentResult != null
42 | && !parameters.CancellationToken.IsCancellationRequested
43 | && _startState.IsEqual(currentResult.PreviousState)
44 | && (_triggerState.IsEqual(currentResult.CurrentState)
45 | || _stateMachine.IsInState(parameters.Context, _triggerState)))
46 | {
47 | StateMutator(parameters.Context, _stateFunction.Invoke(parameters.Context));
48 |
49 | var transitioned = !StateAccessor(parameters.Context).IsEqual(_triggerState);
50 | var result = GetFreshResult(parameters
51 | , currentResult
52 | , currentResult.StartingState
53 | , wasCancelled: parameters.CancellationToken.IsCancellationRequested
54 | , transitionDefined: true
55 | , conditionMet: transitioned);
56 |
57 | return Task.FromResult(result);
58 | }
59 |
60 | return Task.FromResult(GetFreshResult(parameters
61 | , currentResult
62 | , StateAccessor(parameters.Context)
63 | , wasCancelled: parameters.CancellationToken.IsCancellationRequested
64 | , transitionDefined: true
65 | , conditionMet: false));
66 | }
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/Source/NStateManager/Async/StateTransitionAutoDynamicParameterized.cs:
--------------------------------------------------------------------------------
1 | #region Copyright (c) 2018 Scott L. Carter
2 | //
3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in
4 | //compliance with the License. You may obtain a copy of the License at
5 | //http://www.apache.org/licenses/LICENSE-2.0
6 | //
7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is
8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | //See the License for the specific language governing permissions and limitations under the License.
10 | #endregion
11 | using System;
12 |
13 | namespace NStateManager.Async
14 | {
15 | internal class StateTransitionAutoDynamicParameterized : StateTransitionDynamicBase
16 | where TState : IComparable
17 | where TRequest : class
18 | {
19 | private readonly IStateMachine _stateMachine;
20 | private readonly TState _startState;
21 | private readonly TState _triggerState;
22 | private readonly Func _stateFunction;
23 |
24 | public StateTransitionAutoDynamicParameterized(IStateMachine stateMachine
25 | , TState startState
26 | , Func targetStateFunction
27 | , TState triggerState
28 | , string name
29 | , uint priority)
30 | : base(stateMachine.StateAccessor, stateMachine.StateMutator, name, priority)
31 | {
32 | _stateMachine = stateMachine;
33 | _startState = startState;
34 | _triggerState = triggerState;
35 | _stateFunction = targetStateFunction;
36 | }
37 |
38 | public override StateTransitionResult Execute(ExecutionParameters parameters
39 | , StateTransitionResult currentResult = null)
40 | {
41 | if (!(parameters.Request is TRequest typeSafeParam))
42 | { throw new ArgumentException($"Expected a {typeof(TRequest).Name} parameter, but received a {parameters.Request?.GetType().Name ?? "null"}."); }
43 |
44 | if (currentResult != null
45 | && !parameters.CancellationToken.IsCancellationRequested
46 | && _startState.IsEqual(currentResult.PreviousState)
47 | && (_triggerState.IsEqual(currentResult.CurrentState)
48 | || _stateMachine.IsInState(parameters.Context, _triggerState)))
49 | {
50 | StateMutator(parameters.Context, _stateFunction(parameters.Context, typeSafeParam));
51 |
52 | var transitioned = !StateAccessor(parameters.Context).IsEqual(_triggerState);
53 | var result = GetFreshResult(parameters
54 | , currentResult
55 | , currentResult.StartingState
56 | , wasCancelled: false
57 | , transitionDefined: true
58 | , conditionMet: transitioned);
59 |
60 | return result;
61 | }
62 |
63 | return GetFreshResult(parameters
64 | , currentResult
65 | , StateAccessor(parameters.Context)
66 | , wasCancelled: parameters.CancellationToken.IsCancellationRequested
67 | , transitionDefined: true
68 | , conditionMet: false);
69 | }
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/Source/NStateManager/Async/StateTransitionAutoFallback.cs:
--------------------------------------------------------------------------------
1 | #region Copyright (c) 2018 Scott L. Carter
2 | //
3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in
4 | //compliance with the License. You may obtain a copy of the License at
5 | //http://www.apache.org/licenses/LICENSE-2.0
6 | //
7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is
8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | //See the License for the specific language governing permissions and limitations under the License.
10 | #endregion
11 | using System;
12 | using System.Threading;
13 | using System.Threading.Tasks;
14 |
15 | namespace NStateManager.Async
16 | {
17 | internal class StateTransitionAutoFallback : StateTransition
18 | where TState : IComparable
19 | {
20 | private readonly IStateMachine _stateMachine;
21 | private readonly TState _startState;
22 | private readonly TState _triggerState;
23 |
24 | public StateTransitionAutoFallback(IStateMachine stateMachine, TState startState, TState triggerState, TState toState, Func> conditionAsync, string name, uint priority)
25 | : base(stateMachine.StateAccessor, stateMachine.StateMutator, toState, conditionAsync, name, priority)
26 | {
27 | _stateMachine = stateMachine;
28 | _startState = startState;
29 | _triggerState = triggerState;
30 | }
31 |
32 | public override async Task> ExecuteAsync(ExecutionParameters parameters
33 | , StateTransitionResult currentResult = null)
34 | {
35 | if (currentResult != null
36 | && !parameters.CancellationToken.IsCancellationRequested
37 | && _startState.IsEqual(currentResult.PreviousState)
38 | && (_triggerState.IsEqual(currentResult.CurrentState)
39 | || _stateMachine.IsInState(parameters.Context, _triggerState)))
40 | { return await base.ExecuteAsync(parameters, currentResult); }
41 |
42 | return GetFreshResult(parameters
43 | , currentResult
44 | , StateAccessor(parameters.Context)
45 | , wasCancelled: parameters.CancellationToken.IsCancellationRequested
46 | , transitionDefined: true
47 | , conditionMet: false);
48 | }
49 | }
50 | }
--------------------------------------------------------------------------------
/Source/NStateManager/Async/StateTransitionAutoFallbackParameterized.cs:
--------------------------------------------------------------------------------
1 | #region Copyright (c) 2018 Scott L. Carter
2 | //
3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in
4 | //compliance with the License. You may obtain a copy of the License at
5 | //http://www.apache.org/licenses/LICENSE-2.0
6 | //
7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is
8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | //See the License for the specific language governing permissions and limitations under the License.
10 | #endregion
11 | using System;
12 | using System.Threading;
13 | using System.Threading.Tasks;
14 |
15 | namespace NStateManager.Async
16 | {
17 | internal class StateTransitionAutoFallbackParameterized : StateTransitionParameterized
18 | where TParam : class
19 | where TState : IComparable
20 | {
21 | private readonly IStateMachine _stateMachine;
22 | private readonly TState _startState;
23 | private readonly TState _triggerState;
24 |
25 | public StateTransitionAutoFallbackParameterized(IStateMachine stateMachine, TState startState, TState toState, TState triggerState, Func> conditionAsync, string name, uint priority)
26 | : base(stateMachine.StateAccessor, stateMachine.StateMutator, toState, conditionAsync, name, priority)
27 | {
28 | _stateMachine = stateMachine;
29 | _startState = startState;
30 | _triggerState = triggerState;
31 | }
32 |
33 | public override async Task> ExecuteAsync(ExecutionParameters parameters
34 | , StateTransitionResult currentResult = null)
35 | {
36 | if (currentResult != null
37 | && !parameters.CancellationToken.IsCancellationRequested
38 | && _startState.IsEqual(currentResult.PreviousState)
39 | && (_triggerState.IsEqual(currentResult.CurrentState)
40 | || _stateMachine.IsInState(parameters.Context, _triggerState)))
41 | { return await base.ExecuteAsync(parameters, currentResult); }
42 |
43 | return GetFreshResult(parameters
44 | , currentResult
45 | , StateAccessor(parameters.Context)
46 | , wasCancelled: parameters.CancellationToken.IsCancellationRequested
47 | , transitionDefined: true
48 | , conditionMet: false);
49 | }
50 | }
51 | }
--------------------------------------------------------------------------------
/Source/NStateManager/Async/StateTransitionAutoForward.cs:
--------------------------------------------------------------------------------
1 | #region Copyright (c) 2018 Scott L. Carter
2 | //
3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in
4 | //compliance with the License. You may obtain a copy of the License at
5 | //http://www.apache.org/licenses/LICENSE-2.0
6 | //
7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is
8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | //See the License for the specific language governing permissions and limitations under the License.
10 | #endregion
11 | using System;
12 | using System.Threading;
13 | using System.Threading.Tasks;
14 |
15 | namespace NStateManager.Async
16 | {
17 | internal class StateTransitionAutoForward : StateTransition
18 | where TState : IComparable
19 | {
20 | private readonly IStateMachine _stateMachine;
21 | private readonly TState _triggerState;
22 |
23 | public StateTransitionAutoForward(IStateMachine stateMachine, TState triggerState, TState toState, Func> conditionAsync, string name, uint priority)
24 | : base(stateMachine.StateAccessor, stateMachine.StateMutator, toState, conditionAsync, name, priority)
25 | {
26 | _stateMachine = stateMachine;
27 | _triggerState = triggerState;
28 | }
29 |
30 | public override async Task> ExecuteAsync(ExecutionParameters parameters
31 | , StateTransitionResult currentResult = null)
32 | {
33 | if (currentResult != null
34 | && !parameters.CancellationToken.IsCancellationRequested
35 | && (_triggerState.IsEqual(currentResult.CurrentState)
36 | || _stateMachine.IsInState(parameters.Context, _triggerState)))
37 | { return await base.ExecuteAsync(parameters, currentResult); }
38 |
39 | return GetFreshResult(parameters
40 | , currentResult
41 | , StateAccessor(parameters.Context)
42 | , wasCancelled: parameters.CancellationToken.IsCancellationRequested
43 | , transitionDefined: true
44 | , conditionMet: false);
45 | }
46 | }
47 | }
--------------------------------------------------------------------------------
/Source/NStateManager/Async/StateTransitionAutoForwardParameterized.cs:
--------------------------------------------------------------------------------
1 | #region Copyright (c) 2018 Scott L. Carter
2 | //
3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in
4 | //compliance with the License. You may obtain a copy of the License at
5 | //http://www.apache.org/licenses/LICENSE-2.0
6 | //
7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is
8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | //See the License for the specific language governing permissions and limitations under the License.
10 | #endregion
11 | using System;
12 | using System.Threading;
13 | using System.Threading.Tasks;
14 |
15 | namespace NStateManager.Async
16 | {
17 | internal class StateTransitionAutoForwardParameterized : StateTransitionParameterized
18 | where TParam : class
19 | where TState : IComparable
20 | {
21 | private readonly IStateMachine _stateMachine;
22 | private readonly TState _triggerState;
23 |
24 | public StateTransitionAutoForwardParameterized(IStateMachine stateMachine, TState toState, TState triggerState, Func> conditionAsync, string name, uint priority)
25 | : base(stateMachine.StateAccessor, stateMachine.StateMutator, toState, conditionAsync, name, priority)
26 | {
27 | _stateMachine = stateMachine;
28 | _triggerState = triggerState;
29 | }
30 |
31 | public override async Task> ExecuteAsync(ExecutionParameters parameters
32 | , StateTransitionResult currentResult = null)
33 | {
34 | if (currentResult != null //Auto transitions must have a previously successful transition
35 | && !parameters.CancellationToken.IsCancellationRequested
36 | && (_triggerState.IsEqual(currentResult.CurrentState)
37 | || _stateMachine.IsInState(parameters.Context, _triggerState)))
38 | { return await base.ExecuteAsync(parameters, currentResult); }
39 |
40 | return GetFreshResult(parameters
41 | , currentResult
42 | , StateAccessor(parameters.Context)
43 | , wasCancelled: parameters.CancellationToken.IsCancellationRequested
44 | , transitionDefined: true
45 | , conditionMet: false);
46 | }
47 | }
48 | }
--------------------------------------------------------------------------------
/Source/NStateManager/Async/StateTransitionParameterized.cs:
--------------------------------------------------------------------------------
1 | #region Copyright (c) 2018 Scott L. Carter
2 | //
3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in
4 | //compliance with the License. You may obtain a copy of the License at
5 | //http://www.apache.org/licenses/LICENSE-2.0
6 | //
7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is
8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | //See the License for the specific language governing permissions and limitations under the License.
10 | #endregion
11 | using System;
12 | using System.Threading;
13 | using System.Threading.Tasks;
14 |
15 | namespace NStateManager.Async
16 | {
17 | internal class StateTransitionParameterized : StateTransitionNonDynamic
18 | where TParam : class
19 | {
20 | public Func> ConditionAsync { get; }
21 |
22 | public StateTransitionParameterized(Func stateAccessor, Action stateMutator, TState toState, Func> conditionAsync, string name, uint priority)
23 | : base(stateAccessor, stateMutator, toState, name, priority)
24 | {
25 | ConditionAsync = conditionAsync ?? throw new ArgumentNullException(nameof(conditionAsync));
26 | }
27 |
28 | public override async Task> ExecuteAsync(ExecutionParameters parameters
29 | , StateTransitionResult currentResult = null)
30 | {
31 | if (!(parameters.Request is TParam typeSafeParam))
32 | { throw new ArgumentException($"Expected a {typeof(TParam).Name} parameter, but received a {parameters.Request?.GetType().Name ?? "null"}."); }
33 |
34 | var startState = currentResult != null ? currentResult.StartingState : StateAccessor(parameters.Context);
35 |
36 | if (parameters.CancellationToken.IsCancellationRequested)
37 | {
38 | return GetFreshResult(parameters
39 | , currentResult
40 | , startState
41 | , transitionDefined: true
42 | , conditionMet: false
43 | , wasCancelled: true); }
44 |
45 | if (!await ConditionAsync(parameters.Context, typeSafeParam, parameters.CancellationToken)
46 | .ConfigureAwait(continueOnCapturedContext: false))
47 | { return GetFreshResult(parameters, currentResult, startState, transitionDefined: true, conditionMet: false, wasCancelled: parameters.CancellationToken.IsCancellationRequested); }
48 |
49 | StateMutator(parameters.Context, ToState);
50 | var transitionResult = currentResult == null
51 | ? new StateTransitionResult(parameters.Trigger, startState, startState, ToState, Name)
52 | : new StateTransitionResult(parameters.Trigger, startState, currentResult.CurrentState, ToState, Name);
53 |
54 | return transitionResult;
55 | }
56 | }
57 | }
--------------------------------------------------------------------------------
/Source/NStateManager/ExecutionParameters.cs:
--------------------------------------------------------------------------------
1 | #region Copyright (c) 2018 Scott L. Carter
2 | //
3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in
4 | //compliance with the License. You may obtain a copy of the License at
5 | //http://www.apache.org/licenses/LICENSE-2.0
6 | //
7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is
8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | //See the License for the specific language governing permissions and limitations under the License.
10 | #endregion
11 | using System.Threading;
12 |
13 | namespace NStateManager
14 | {
15 | public class ExecutionParameters
16 | {
17 | public TTrigger Trigger { get; }
18 | public T Context { get; }
19 | public object Request { get; }
20 | public CancellationToken CancellationToken { get; }
21 |
22 | public ExecutionParameters(TTrigger trigger
23 | , T context
24 | , CancellationToken cancellationToken = default
25 | , object request = null)
26 | {
27 | Trigger = trigger;
28 | Context = context;
29 | Request = request;
30 | CancellationToken = cancellationToken;
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/Source/NStateManager/Export/ConfigurationSummary.cs:
--------------------------------------------------------------------------------
1 | #region Copyright (c) 2018 Scott L. Carter
2 | //
3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in
4 | //compliance with the License. You may obtain a copy of the License at
5 | //http://www.apache.org/licenses/LICENSE-2.0
6 | //
7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is
8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | //See the License for the specific language governing permissions and limitations under the License.
10 | #endregion
11 | using System;
12 | using System.Collections.Generic;
13 | using System.Linq;
14 |
15 | namespace NStateManager.Export
16 | {
17 | public class ConfigurationSummary where TState : IComparable
18 | {
19 | private readonly Dictionary> _stateDetails = new Dictionary>();
20 | public IReadOnlyList> StateDetails => _stateDetails.Values.ToList();
21 |
22 | public IReadOnlyList> FinalStates => Transitions
23 | .Where(t => t.ToState.IsFinalState)
24 | .Select(t => t.ToState)
25 | .Distinct()
26 | .ToList();
27 |
28 | public bool HasSubStates => StateDetails.Any(s => s.IsSuperState);
29 |
30 | public IReadOnlyList> StartingStates => Transitions
31 | .Where(t => t.FromState.IsStartingState)
32 | .Select(t => t.FromState)
33 | .Distinct()
34 | .ToList();
35 |
36 | public IReadOnlyList> Transitions => _stateDetails.SelectMany(s => s.Value.TransitionsFrom).Distinct().ToList();
37 |
38 | public StateDetails AddState(TState state)
39 | {
40 | if (!_stateDetails.ContainsKey(state))
41 | { _stateDetails.Add(state, new StateDetails(state)); }
42 |
43 | return _stateDetails[state];
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/Source/NStateManager/Export/CsvExporter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using System.Text;
4 |
5 | namespace NStateManager.Export
6 | {
7 | public class CsvExporter where TState : IComparable
8 | {
9 | public static string Export(ConfigurationSummary summary)
10 | {
11 | var csv = new StringBuilder();
12 | csv.AppendLine("FromState, TriggerEvent, ToState, Condition");
13 |
14 | foreach (var transition in summary.Transitions.OrderByDescending(t => t.FromState.IsStartingState).ThenByDescending(t => t.ToState.IsFinalState))
15 | { csv.AppendLine($"{transition.FromState.State}, {transition.Trigger}, {transition.ToState.State}{(transition.HasCondition ? ", *" : string.Empty)}"); }
16 |
17 | var unusedStates = summary.StateDetails.Where(s => s.IsStartingState && s.IsFinalState);
18 | foreach (var state in unusedStates)
19 | { csv.AppendLine(state.State.ToString()); }
20 |
21 | return csv.ToString();
22 | }
23 | }
24 | }
--------------------------------------------------------------------------------
/Source/NStateManager/Export/DotGraphExporter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 |
6 | namespace NStateManager.Export
7 | {
8 | public class DotGraphExporter where TState : IComparable
9 | {
10 | public static string Export(ConfigurationSummary summary)
11 | {
12 | var dotGraph = new StringBuilder();
13 | dotGraph.AppendLine("digraph NStateManagerGraph {");
14 | if (summary.HasSubStates)
15 | { dotGraph.AppendLine("\tcompound=true;"); }
16 |
17 | // ReSharper disable once StringLiteralTypo
18 | dotGraph.AppendLine("\trankdir=LR;");
19 | dotGraph.AppendLine();
20 |
21 | exportNonSuperStates(summary.StateDetails.Where(s => s.StateLevel == 0) , dotGraph, level: 0);
22 | exportSuperStates(summary.StateDetails.Where(s => s.StateLevel == 1), dotGraph, level: 0);
23 |
24 | //List all the transitions.
25 | if (summary.Transitions.Any())
26 | {
27 | dotGraph.AppendLine();
28 |
29 | foreach (var transition in summary.Transitions)
30 | {
31 | dotGraph.Append($"\t{transition.FromState.State} -> {transition.ToState.State}");
32 | dotGraph.Append(" [label=\"");
33 |
34 | if (!string.IsNullOrWhiteSpace(transition.Name))
35 | { dotGraph.Append(transition.Trigger); }
36 |
37 | if (transition.HasCondition)
38 | { dotGraph.Append("(*)"); }
39 | dotGraph.Append("\"");
40 |
41 | if (transition.FromState.IsSuperState)
42 | { dotGraph.Append($" ltail=cluster_{transition.FromState.State}"); }
43 |
44 | if (transition.ToState.IsSuperState)
45 | { dotGraph.Append($" lhead=cluster_{transition.ToState.State}"); }
46 |
47 | dotGraph.AppendLine("];");
48 | }
49 | }
50 |
51 | dotGraph.AppendLine("}");
52 |
53 | return dotGraph.ToString();
54 | }
55 |
56 | private static void exportSuperStates(IEnumerable> stateDetails, StringBuilder dotGraph, int level)
57 | {
58 | foreach (var state in stateDetails)
59 | {
60 | dotGraph.AppendLine();
61 | dotGraph.AppendLine($"{createIndent(level + 1)}subgraph cluster_{state.State} {{");
62 | dotGraph.AppendLine($"{createIndent(level + 2)}label={state.State};");
63 | exportSubGraphNode(dotGraph, state);
64 | exportNonSuperStates(state.SubStates.Where(s => s.IsSuperState == false), dotGraph, level: level + 1);
65 | exportSuperStates(state.SubStates.Where(s => s.IsSuperState), dotGraph, level + 1);
66 | dotGraph.AppendLine($"{createIndent(level + 1)}}}");
67 | }
68 | }
69 |
70 | private static void exportSubGraphNode(StringBuilder dotGraph, StateDetails state)
71 | {
72 | dotGraph.Append($"{createIndent(state.StateLevel + 1)}{state.State} ");
73 | dotGraph.AppendLine(state.TransitionsFrom.Any(t => t.ToState?.SuperState == state)
74 | ? "[shape=circle label=\"\" style=filled color=black]"
75 | : "[shape=point color=white];");
76 | }
77 |
78 | private static string createIndent(int level)
79 | {
80 | var indent = string.Empty;
81 | for(var i = 0; i < level; i++)
82 | { indent += "\t"; }
83 |
84 | return indent;
85 | }
86 |
87 | private static void exportNonSuperStates(IEnumerable> stateDetails, StringBuilder dotGraph, int level)
88 | {
89 | var stateDetailsArray = stateDetails as StateDetails[] ?? stateDetails.ToArray();
90 |
91 | if (!stateDetailsArray.Any())
92 | { return; }
93 |
94 | dotGraph.AppendLine();
95 |
96 | foreach (var state in stateDetailsArray)
97 | { dotGraph.AppendLine($"{createIndent(level + 1)}{state.State};"); }
98 | }
99 | }
100 | }
--------------------------------------------------------------------------------
/Source/NStateManager/Export/IStateMachine.cs:
--------------------------------------------------------------------------------
1 | #region Copyright (c) 2018 Scott L. Carter
2 | //
3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in
4 | //compliance with the License. You may obtain a copy of the License at
5 | //http://www.apache.org/licenses/LICENSE-2.0
6 | //
7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is
8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | //See the License for the specific language governing permissions and limitations under the License.
10 | #endregion
11 | using NStateManager.Export;
12 | using System;
13 |
14 | namespace NStateManager.Sync
15 | {
16 | public partial interface IStateMachine
17 | where TState : IComparable
18 | {
19 | ConfigurationSummary GetSummary();
20 | }
21 | }
--------------------------------------------------------------------------------
/Source/NStateManager/Export/IStateMachineAsync.cs:
--------------------------------------------------------------------------------
1 | #region Copyright (c) 2018 Scott L. Carter
2 | //
3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in
4 | //compliance with the License. You may obtain a copy of the License at
5 | //http://www.apache.org/licenses/LICENSE-2.0
6 | //
7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is
8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | //See the License for the specific language governing permissions and limitations under the License.
10 | #endregion
11 | using NStateManager.Export;
12 | using System;
13 |
14 | // ReSharper disable once CheckNamespace
15 | namespace NStateManager.Async
16 | {
17 | public partial interface IStateMachine
18 | where TState : IComparable
19 | {
20 | ConfigurationSummary GetSummary();
21 | }
22 | }
--------------------------------------------------------------------------------
/Source/NStateManager/Export/TransitionDetails.cs:
--------------------------------------------------------------------------------
1 | #region Copyright (c) 2018 Scott L. Carter
2 | //
3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in
4 | //compliance with the License. You may obtain a copy of the License at
5 | //http://www.apache.org/licenses/LICENSE-2.0
6 | //
7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is
8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | //See the License for the specific language governing permissions and limitations under the License.
10 | #endregion
11 | using System;
12 |
13 | namespace NStateManager.Export
14 | {
15 | public class TransitionDetails where TState : IComparable
16 | {
17 | public TransitionDetails(string name
18 | , StateDetails fromState
19 | , TTrigger trigger
20 | , StateDetails toState = null
21 | , bool hasCondition = false
22 | , uint priority = 1
23 | , bool isDynamic = false)
24 | {
25 | Name = name;
26 | FromState = fromState;
27 | Trigger = trigger;
28 | ToState = toState;
29 | HasCondition = hasCondition;
30 | Priority = priority;
31 | IsDynamic = isDynamic;
32 | }
33 |
34 | public string Name { get; }
35 | public StateDetails FromState { get; }
36 | public TTrigger Trigger { get; }
37 | public StateDetails ToState { get; }
38 | public bool HasCondition { get; }
39 | public uint Priority { get; }
40 | public bool IsDynamic { get; }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/Source/NStateManager/Extensions.cs:
--------------------------------------------------------------------------------
1 | #region Copyright (c) 2018 Scott L. Carter
2 | //
3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in
4 | //compliance with the License. You may obtain a copy of the License at
5 | //http://www.apache.org/licenses/LICENSE-2.0
6 | //
7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is
8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | //See the License for the specific language governing permissions and limitations under the License.
10 | #endregion
11 | using System;
12 |
13 | namespace NStateManager
14 | {
15 | public static class Extensions
16 | {
17 | public static bool IsEqual(this TState first, TState second) where TState : IComparable
18 | {
19 | return first.CompareTo(second) == 0;
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/Source/NStateManager/FunctionAction.cs:
--------------------------------------------------------------------------------
1 | #region Copyright (c) 2018 Scott L. Carter
2 | //
3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in
4 | //compliance with the License. You may obtain a copy of the License at
5 | //http://www.apache.org/licenses/LICENSE-2.0
6 | //
7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is
8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | //See the License for the specific language governing permissions and limitations under the License.
10 | #endregion
11 | using System;
12 | using System.Threading;
13 | using System.Threading.Tasks;
14 |
15 | namespace NStateManager
16 | {
17 | internal class FunctionAction : FunctionActionBase
18 | {
19 | internal Func Action { get; }
20 |
21 | internal FunctionAction(Func action)
22 | {
23 | Action = action ?? throw new ArgumentNullException(nameof(action));
24 | }
25 |
26 | internal override async Task ExecuteAsync(T context, CancellationToken cancellationToken, object request)
27 | {
28 | await Action.Invoke(context, cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
29 | }
30 | }
31 | }
--------------------------------------------------------------------------------
/Source/NStateManager/FunctionActionBase.cs:
--------------------------------------------------------------------------------
1 | #region Copyright (c) 2018 Scott L. Carter
2 | //
3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in
4 | //compliance with the License. You may obtain a copy of the License at
5 | //http://www.apache.org/licenses/LICENSE-2.0
6 | //
7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is
8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | //See the License for the specific language governing permissions and limitations under the License.
10 | #endregion
11 | using System.Threading;
12 | using System.Threading.Tasks;
13 |
14 | namespace NStateManager
15 | {
16 | public abstract class FunctionActionBase
17 | {
18 | internal abstract Task ExecuteAsync(T context, CancellationToken cancellationToken, object request);
19 | }
20 | }
--------------------------------------------------------------------------------
/Source/NStateManager/FunctionActionParameterized.cs:
--------------------------------------------------------------------------------
1 | #region Copyright (c) 2018 Scott L. Carter
2 | //
3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in
4 | //compliance with the License. You may obtain a copy of the License at
5 | //http://www.apache.org/licenses/LICENSE-2.0
6 | //
7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is
8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | //See the License for the specific language governing permissions and limitations under the License.
10 | #endregion
11 | using System;
12 | using System.Threading;
13 | using System.Threading.Tasks;
14 |
15 | namespace NStateManager
16 | {
17 | internal class FunctionActionParameterized : FunctionActionBase
18 | {
19 | internal Func Action { get; }
20 |
21 | internal FunctionActionParameterized(Func action)
22 | {
23 | Action = action ?? throw new ArgumentNullException(nameof(action));
24 | }
25 |
26 | internal override async Task ExecuteAsync(T context, CancellationToken cancellationToken, object request)
27 | {
28 | if (!(request is TRequest typedRequestType))
29 | { throw new ArgumentException($"{nameof(request)} must be of type {typeof(TRequest).Name}.");}
30 |
31 | await Action.Invoke(context, typedRequestType, cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
32 | }
33 | }
34 | }
--------------------------------------------------------------------------------
/Source/NStateManager/NStateManager.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0
5 | 5.0
6 | false
7 | true
8 | true
9 | 4.0.2
10 | Scott L Carter
11 | Scott L Carter
12 | Copyright (c) 2019 Scott L. Carter
13 | https://github.com/scottctr/NStateManager/blob/master/LICENSE
14 | https://github.com/scottctr/NStateManager
15 | https://github.com/scottctr/NStateManager
16 | NStateManager
17 | 4.0.2
18 | 4.0.2
19 |
20 | Easy to use and very flexible state manager for .Net.
21 | latest
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/Source/NStateManager/NStateManager.nuspec:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | $id$
5 | $version$
6 | $title$
7 | $author$
8 | $author$
9 | http://LICENSE_URL_HERE_OR_DELETE_THIS_LINE
10 | http://PROJECT_URL_HERE_OR_DELETE_THIS_LINE
11 | http://ICON_URL_HERE_OR_DELETE_THIS_LINE
12 | false
13 | $description$
14 | Summary of changes made in this release of the package.
15 | Copyright 2018
16 | Tag1 Tag2
17 |
18 |
--------------------------------------------------------------------------------
/Source/NStateManager/StateTransitionBase.cs:
--------------------------------------------------------------------------------
1 | #region Copyright (c) 2018 Scott L. Carter
2 | //
3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in
4 | //compliance with the License. You may obtain a copy of the License at
5 | //http://www.apache.org/licenses/LICENSE-2.0
6 | //
7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is
8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | //See the License for the specific language governing permissions and limitations under the License.
10 | #endregion
11 | using System;
12 | using System.Threading.Tasks;
13 |
14 | namespace NStateManager
15 | {
16 | public abstract class StateTransitionBase
17 | {
18 | protected internal string Name;
19 | protected internal uint Priority;
20 | protected Func StateAccessor { get; }
21 | protected Action StateMutator { get; }
22 |
23 | protected StateTransitionBase(Func stateAccessor
24 | , Action stateMutator
25 | , string name
26 | , uint priority)
27 | {
28 | StateAccessor = stateAccessor ?? throw new ArgumentNullException(nameof(stateAccessor));
29 | StateMutator = stateMutator ?? throw new ArgumentNullException(nameof(stateMutator));
30 | Name = name ?? "";
31 | Priority = priority;
32 | }
33 |
34 | public virtual StateTransitionResult Execute(ExecutionParameters parameters
35 | , StateTransitionResult currentResult = null)
36 | {
37 | throw new NotImplementedException("Inherited classes must override this method. Ensure you're calling the correct overloaded version.");
38 | }
39 |
40 | public virtual Task> ExecuteAsync(ExecutionParameters parameters
41 | , StateTransitionResult currentResult = null)
42 | {
43 | throw new NotImplementedException("Inherited classes must override this method. Ensure you're calling the correct overloaded version.");
44 | }
45 |
46 | protected StateTransitionResult GetFreshResult(ExecutionParameters parameters
47 | , StateTransitionResult previousResult
48 | , TState startState
49 | , bool transitionDefined
50 | , bool conditionMet
51 | , bool wasCancelled)
52 | {
53 | var wasSuccessful = (transitionDefined && conditionMet && !wasCancelled);
54 |
55 | return new StateTransitionResult(parameters.Trigger
56 | , startState
57 | , (!wasSuccessful || previousResult == null) ? startState : previousResult.CurrentState
58 | , wasSuccessful ? StateAccessor(parameters.Context) : previousResult == null ? startState : previousResult.CurrentState
59 | , wasSuccessful ? Name : string.Empty
60 | , wasCancelled: wasCancelled
61 | , conditionMet: conditionMet
62 | , transitionDefined: transitionDefined);
63 | }
64 | }
65 | }
--------------------------------------------------------------------------------
/Source/NStateManager/StateTransitionDynamic.cs:
--------------------------------------------------------------------------------
1 | #region Copyright (c) 2018 Scott L. Carter
2 | //
3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in
4 | //compliance with the License. You may obtain a copy of the License at
5 | //http://www.apache.org/licenses/LICENSE-2.0
6 | //
7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is
8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | //See the License for the specific language governing permissions and limitations under the License.
10 | #endregion
11 | using System;
12 |
13 | namespace NStateManager
14 | {
15 | internal class StateTransitionDynamic : StateTransitionDynamicBase
16 | where TState : IComparable
17 | {
18 | public Func StateFunc { get; }
19 |
20 | public StateTransitionDynamic(Func stateAccessor
21 | , Action stateMutator
22 | , Func stateFunc
23 | , string name
24 | , uint priority)
25 | : base(stateAccessor, stateMutator, name, priority)
26 | {
27 | StateFunc = stateFunc ?? throw new ArgumentNullException(nameof(stateFunc));
28 | }
29 |
30 | public override StateTransitionResult Execute(ExecutionParameters parameters, StateTransitionResult currentResult = null)
31 | {
32 | var startState = currentResult != null ? currentResult.StartingState : StateAccessor(parameters.Context);
33 | var toState = StateFunc(parameters.Context);
34 |
35 | StateMutator(parameters.Context, toState);
36 |
37 | var transitioned = !StateAccessor(parameters.Context).IsEqual(startState);
38 | var transitionResult = GetFreshResult(parameters, currentResult, startState, wasCancelled: false, transitionDefined: true, conditionMet: transitioned);
39 |
40 | return transitionResult;
41 | }
42 | }
43 | }
--------------------------------------------------------------------------------
/Source/NStateManager/StateTransitionDynamicAsync.cs:
--------------------------------------------------------------------------------
1 | #region Copyright (c) 2018 Scott L. Carter
2 | //
3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in
4 | //compliance with the License. You may obtain a copy of the License at
5 | //http://www.apache.org/licenses/LICENSE-2.0
6 | //
7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is
8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | //See the License for the specific language governing permissions and limitations under the License.
10 | #endregion
11 | using System;
12 | using System.Threading;
13 | using System.Threading.Tasks;
14 |
15 | namespace NStateManager
16 | {
17 | internal class StateTransitionDynamicAsync : StateTransitionDynamicBase
18 | where TState : IComparable
19 | {
20 | public Func> StateFunctionAsync { get; }
21 |
22 | public StateTransitionDynamicAsync(Func stateAccessor
23 | , Action stateMutator
24 | , Func> stateFuncAsync
25 | , string name
26 | , uint priority)
27 | : base(stateAccessor, stateMutator, name, priority)
28 | {
29 | StateFunctionAsync = stateFuncAsync ?? throw new ArgumentNullException(nameof(stateFuncAsync));
30 | }
31 |
32 | public override async Task> ExecuteAsync(ExecutionParameters parameters
33 | , StateTransitionResult currentResult = null)
34 | {
35 | var startState = currentResult != null ? currentResult.StartingState : StateAccessor(parameters.Context);
36 |
37 | if (parameters.CancellationToken.IsCancellationRequested)
38 | { return GetFreshResult(parameters, currentResult, startState, wasCancelled: true, transitionDefined: true, conditionMet: false); }
39 |
40 | var toState = await StateFunctionAsync(parameters.Context, parameters.CancellationToken)
41 | .ConfigureAwait(continueOnCapturedContext: false);
42 |
43 | return ExecuteFinalizeAsync(parameters, currentResult, startState, toState);
44 | }
45 | }
46 | }
--------------------------------------------------------------------------------
/Source/NStateManager/StateTransitionDynamicBase.cs:
--------------------------------------------------------------------------------
1 | #region Copyright (c) 2018 Scott L. Carter
2 | //
3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in
4 | //compliance with the License. You may obtain a copy of the License at
5 | //http://www.apache.org/licenses/LICENSE-2.0
6 | //
7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is
8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | //See the License for the specific language governing permissions and limitations under the License.
10 | #endregion
11 | using System;
12 |
13 | namespace NStateManager
14 | {
15 | internal abstract class StateTransitionDynamicBase : StateTransitionBase
16 | where TState : IComparable
17 | {
18 | protected StateTransitionDynamicBase(Func stateAccessor, Action stateMutator, string name, uint priority)
19 | : base(stateAccessor, stateMutator, name, priority)
20 | { }
21 | }
22 | }
--------------------------------------------------------------------------------
/Source/NStateManager/StateTransitionDynamicParameterized.cs:
--------------------------------------------------------------------------------
1 | #region Copyright (c) 2018 Scott L. Carter
2 | //
3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in
4 | //compliance with the License. You may obtain a copy of the License at
5 | //http://www.apache.org/licenses/LICENSE-2.0
6 | //
7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is
8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | //See the License for the specific language governing permissions and limitations under the License.
10 | #endregion
11 | using System;
12 |
13 | namespace NStateManager
14 | {
15 | internal class StateTransitionDynamicParameterized : StateTransitionDynamicBase
16 | where TParam : class
17 | where TState : IComparable
18 | {
19 | public Func StateFunc { get; }
20 |
21 | public StateTransitionDynamicParameterized(Func stateAccessor, Action stateMutator, Func stateFunc, string name, uint priority)
22 | : base(stateAccessor, stateMutator, name, priority)
23 | {
24 | StateFunc = stateFunc ?? throw new ArgumentNullException(nameof(stateFunc));
25 | }
26 |
27 | public override StateTransitionResult Execute(ExecutionParameters parameters, StateTransitionResult currentResult = null)
28 | {
29 | if (!(parameters.Request is TParam typeSafeParam))
30 | { throw new ArgumentException($"Expected a {typeof(TParam).Name} parameter, but received a {parameters.Request?.GetType().Name ?? "null"}."); }
31 |
32 | var startState = currentResult != null ? currentResult.StartingState : StateAccessor(parameters.Context);
33 | var toState = StateFunc(parameters.Context, typeSafeParam);
34 |
35 | var transitioned = !toState.IsEqual(startState);
36 |
37 | if (transitioned)
38 | { StateMutator(parameters.Context, toState); }
39 |
40 | var transitionResult = GetFreshResult(parameters, currentResult, startState, transitionDefined: true, wasCancelled: false, conditionMet: transitioned);
41 |
42 | return transitionResult;
43 | }
44 | }
45 | }
--------------------------------------------------------------------------------
/Source/NStateManager/StateTransitionDynamicParameterizedAsync.cs:
--------------------------------------------------------------------------------
1 | #region Copyright (c) 2018 Scott L. Carter
2 | //
3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in
4 | //compliance with the License. You may obtain a copy of the License at
5 | //http://www.apache.org/licenses/LICENSE-2.0
6 | //
7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is
8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | //See the License for the specific language governing permissions and limitations under the License.
10 | #endregion
11 | using System;
12 | using System.Threading;
13 | using System.Threading.Tasks;
14 |
15 | namespace NStateManager
16 | {
17 | internal class StateTransitionDynamicParameterizedAsync
18 | : StateTransitionDynamicBase
19 | where TParam : class
20 | where TState : IComparable
21 | {
22 | public Func> StateFuncAsync { get; }
23 |
24 | public StateTransitionDynamicParameterizedAsync(Func stateAccessor
25 | , Action stateMutator
26 | , Func> stateFuncAsync
27 | , string name
28 | , uint priority)
29 | : base(stateAccessor, stateMutator, name, priority)
30 | {
31 | StateFuncAsync = stateFuncAsync ?? throw new ArgumentNullException(nameof(stateFuncAsync));
32 | }
33 |
34 | public override async Task> ExecuteAsync(ExecutionParameters parameters
35 | , StateTransitionResult currentResult = null)
36 | {
37 | if (!(parameters.Request is TParam typeSafeParam))
38 | { throw new ArgumentException($"Expected a {typeof(TParam).Name} parameter, but received a {parameters.Request?.GetType().Name ?? "null"}."); }
39 |
40 | var startState = currentResult != null ? currentResult.StartingState : StateAccessor(parameters.Context);
41 |
42 | if (parameters.CancellationToken.IsCancellationRequested)
43 | {
44 | if (currentResult != null)
45 | { return currentResult; }
46 |
47 | return new StateTransitionResult(parameters.Trigger
48 | , startState
49 | , startState
50 | , startState
51 | , lastTransitionName: string.Empty
52 | , wasCancelled: true);
53 | }
54 |
55 | var toState = await StateFuncAsync(parameters.Context, typeSafeParam, parameters.CancellationToken)
56 | .ConfigureAwait(continueOnCapturedContext: false);
57 |
58 | return ExecuteFinalizeAsync(parameters, currentResult, startState, toState);
59 | }
60 | }
61 | }
--------------------------------------------------------------------------------
/Source/NStateManager/StateTransitionNonDynamic.cs:
--------------------------------------------------------------------------------
1 | #region Copyright (c) 2018 Scott L. Carter
2 | //
3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in
4 | //compliance with the License. You may obtain a copy of the License at
5 | //http://www.apache.org/licenses/LICENSE-2.0
6 | //
7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is
8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | //See the License for the specific language governing permissions and limitations under the License.
10 | #endregion
11 | using System;
12 |
13 | namespace NStateManager
14 | {
15 | public abstract class StateTransitionNonDynamic : StateTransitionBase
16 | {
17 | public TState ToState { get; protected set; }
18 |
19 | protected StateTransitionNonDynamic(Func stateAccessor
20 | , Action stateMutator
21 | , TState toState
22 | , string name
23 | , uint priority)
24 | : base(stateAccessor, stateMutator, name, priority)
25 | {
26 | ToState = toState;
27 | }
28 |
29 | protected StateTransitionResult ExecutePrim(ExecutionParameters parameters
30 | , StateTransitionResult currentResult
31 | , bool conditionMet)
32 | {
33 | var startState = currentResult != null ? currentResult.StartingState : StateAccessor(parameters.Context);
34 |
35 | if (!conditionMet)
36 | { return GetFreshResult(parameters, currentResult, startState, wasCancelled: false, conditionMet: false, transitionDefined: true); }
37 |
38 | StateMutator(parameters.Context, ToState);
39 | var transitionResult = GetFreshResult(parameters, currentResult, startState, wasCancelled: false, conditionMet: true, transitionDefined: true);
40 |
41 | return transitionResult;
42 | }
43 | }
44 | }
--------------------------------------------------------------------------------
/Source/NStateManager/StateTransitionResult.cs:
--------------------------------------------------------------------------------
1 | #region Copyright (c) 2018 Scott L. Carter
2 | //
3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in
4 | //compliance with the License. You may obtain a copy of the License at
5 | //http://www.apache.org/licenses/LICENSE-2.0
6 | //
7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is
8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | //See the License for the specific language governing permissions and limitations under the License.
10 | #endregion
11 |
12 | namespace NStateManager
13 | {
14 | ///
15 | /// The result of any state transition that occurred from executing a request or transition on the ./>
16 | ///
17 | /// Represents the type of state used by the transition.
18 | /// Represents the type of trigger used by the transition.
19 | public sealed class StateTransitionResult
20 | {
21 | ///
22 | /// Boolean result of the condition, if any, for the transition.
23 | ///
24 | /// Will only be False if there was a transition defined (i.e. TransitionDefined is True) and it returned False.
25 | public bool ConditionMet { get; }
26 |
27 | ///
28 | /// Current state -- after all transitions and actions have been executed.
29 | ///
30 | public TState CurrentState { get; internal set; }
31 |
32 | ///
33 | /// Name of the last transition executed.
34 | ///
35 | public string LastTransitionName { get; internal set; }
36 |
37 | ///
38 | /// State the context was in just before the current state.
39 | ///
40 | public TState PreviousState { get; internal set; }
41 |
42 | ///
43 | /// State when execution of the request or transition began.
44 | ///
45 | public TState StartingState { get; }
46 |
47 | ///
48 | /// True if there was a transition defined for the request or transition; otherwise False.
49 | ///
50 | public bool TransitionDefined { get; }
51 |
52 | ///
53 | /// Trigger that initiated the results.
54 | ///
55 | public TTrigger Trigger { get; }
56 |
57 | ///
58 | /// True if the action was cancelled; otherwise False.
59 | ///
60 | public bool WasCancelled { get; }
61 |
62 | ///
63 | /// True if the transition was successful; otherwise false.
64 | ///
65 | /// Will only be True if there was a transition defined (i.e. TransitionDefined is True) and any conditions were met.
66 | public bool WasTransitioned { get; }
67 |
68 | ///
69 | /// Constructor.
70 | ///
71 | /// The trigger that initiated the results.
72 | /// State when execution of the request or transition.
73 | /// State previous to the current state.
74 | /// Current state -- after all transitions and actions have been executed.
75 | ///
76 | /// True if there was a transition defined for the request or transition; otherwise False.
77 | /// Boolean result of the condition, if any, for the transition.
78 | /// True if the action was cancelled; otherwise False.
79 | public StateTransitionResult(TTrigger trigger
80 | , TState startingState
81 | , TState previousState
82 | , TState currentState
83 | , string lastTransitionName
84 | , bool transitionDefined = true
85 | , bool conditionMet = true
86 | , bool wasCancelled = false)
87 | {
88 | Trigger = trigger;
89 | StartingState = startingState;
90 | PreviousState = previousState;
91 | CurrentState = currentState;
92 | LastTransitionName = lastTransitionName;
93 | WasTransitioned = transitionDefined && conditionMet && !wasCancelled;
94 | TransitionDefined = transitionDefined;
95 | ConditionMet = transitionDefined && conditionMet;
96 | WasCancelled = wasCancelled;
97 | }
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/Source/NStateManager/Sync/IStateConfigurationInternal.cs:
--------------------------------------------------------------------------------
1 | #region Copyright (c) 2018 Scott L. Carter
2 | //
3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in
4 | //compliance with the License. You may obtain a copy of the License at
5 | //http://www.apache.org/licenses/LICENSE-2.0
6 | //
7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is
8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | //See the License for the specific language governing permissions and limitations under the License.
10 | #endregion
11 |
12 | namespace NStateManager.Sync
13 | {
14 | public interface IStateConfigurationInternal : IStateConfiguration
15 | {
16 | void AddAutoTransition(TTrigger trigger, StateTransitionBase transition);
17 | void AddSuperstate(IStateConfigurationInternal superStateConfiguration);
18 | void AddTransition(TTrigger trigger, StateTransitionBase transition);
19 | StateTransitionResult ExecuteAutoTransition(ExecutionParameters parameters, StateTransitionResult currentResult);
20 | void ExecuteEntryAction(T context, StateTransitionResult currentResult);
21 | void ExecuteExitAction(T context, StateTransitionResult currentResult);
22 | void ExecuteReentryAction(T context, StateTransitionResult currentResult);
23 | StateTransitionResult FireTrigger(ExecutionParameters parameters);
24 | bool IsSubState();
25 | bool IsSubStateOf(TState state);
26 | IStateConfigurationInternal SuperStateConfig { get; }
27 | TState State { get; }
28 | }
29 | }
--------------------------------------------------------------------------------
/Source/NStateManager/Sync/IStateMachine.cs:
--------------------------------------------------------------------------------
1 | #region Copyright (c) 2018 Scott L. Carter
2 | //
3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in
4 | //compliance with the License. You may obtain a copy of the License at
5 | //http://www.apache.org/licenses/LICENSE-2.0
6 | //
7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is
8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | //See the License for the specific language governing permissions and limitations under the License.
10 | #endregion
11 | using System;
12 |
13 | namespace NStateManager.Sync
14 | {
15 | public partial interface IStateMachine
16 | {
17 | IStateMachine AddTriggerAction(TTrigger trigger, Action action);
18 | IStateMachine AddTriggerAction(TTrigger trigger, Action action);
19 | IStateConfiguration ConfigureState(TState state);
20 | StateTransitionResult FireTrigger(T context, TTrigger trigger);
21 | StateTransitionResult FireTrigger(T context, TTrigger trigger, TRequest request) where TRequest : class;
22 | bool IsInState(T context, TState state);
23 | Func StateAccessor { get; }
24 | Action StateMutator { get; }
25 |
26 | ///
27 | /// Event raised when the context transitions to a new state when FireTrigger is called.
28 | ///
29 | event EventHandler> OnTransition;
30 |
31 | ///
32 | /// Event raised when the context doesn't transition to a new state when FireTrigger is called.
33 | ///
34 | event EventHandler> OnNoTransition;
35 |
36 | ///
37 | /// Event raised when the context's current state isn't configured for the trigger passed to FireTrigger.
38 | ///
39 | event EventHandler> OnTriggerNotConfigured;
40 | }
41 | }
--------------------------------------------------------------------------------
/Source/NStateManager/Sync/StateTransition.cs:
--------------------------------------------------------------------------------
1 | #region Copyright (c) 2018 Scott L. Carter
2 | //
3 | //Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in
4 | //compliance with the License. You may obtain a copy of the License at
5 | //http://www.apache.org/licenses/LICENSE-2.0
6 | //
7 | //Unless required by applicable law or agreed to in writing, software distributed under the License is
8 | //distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | //See the License for the specific language governing permissions and limitations under the License.
10 | #endregion
11 | using System;
12 |
13 | namespace NStateManager.Sync
14 | {
15 | internal class StateTransition : StateTransitionNonDynamic
16 | {
17 | public Func Condition { get; }
18 | public bool HasCondition { get; }
19 |
20 | public StateTransition(Func stateAccessor, Action stateMutator, TState toState, string name, uint priority, Func condition = null)
21 | : base(stateAccessor, stateMutator, toState, name, priority)
22 | {
23 | Condition = condition;
24 | HasCondition = Condition != null;
25 | }
26 |
27 | public override StateTransitionResult Execute(ExecutionParameters parameters
28 | , StateTransitionResult