├── .gitattributes
├── .gitignore
├── .nuget
├── NuGet.Config
├── NuGet.exe
└── NuGet.targets
├── EDebugLevel.cs
├── ELogLevel.cs
├── Flow.asmdef
├── Flow.csproj
├── Flow.sln
├── Flow.sln.DotSettings
├── GlobalSuppressions.cs
├── IBarrier.cs
├── IBreak.cs
├── ICase.cs
├── IChannel.cs
├── ICoroutine.cs
├── IFactory.cs
├── IFuture.cs
├── IGenerator.cs
├── IGroup.cs
├── IKernel.cs
├── ILogger.cs
├── INamed.cs
├── INode.cs
├── IPeriodic.cs
├── ISequence.cs
├── ISteppable.cs
├── ISubroutine.cs
├── ITimeFrame.cs
├── ITimedTransients.cs
├── ITimer.cs
├── ITimesOut.cs
├── ITransient.cs
├── ITrigger.cs
├── Impl
├── Barrier.cs
├── BlockingChannel.cs
├── Break.cs
├── Case.cs
├── Channel.cs
├── Coroutine.cs
├── Create.cs
├── Detail
│ └── Detail.cs
├── Exception.cs
├── Extension.cs
├── Factory.cs
├── Future.cs
├── Generator.cs
├── Group.cs
├── Kernel.cs
├── Node.cs
├── Periodic.cs
├── Sequence.cs
├── SpinWait.cs
├── Subroutine.cs
├── TimeFrame.cs
├── TimedBarrier.cs
├── TimedFuture.cs
├── TimedTrigger.cs
├── Timer.cs
├── Transient.cs
├── Trigger.cs
└── VolatileBool.cs
├── LICENSE
├── Logger
├── ConsoleLogger.cs
├── Logger.cs
├── PrettyPrinter.cs
├── Readme.md
├── UnityLogger.cs
└── vscoloroutput.json
├── LoggerFacade.cs
├── Properties
└── AssemblyInfo.cs
├── Readme.md
├── TestFlow
├── Editor
│ ├── FlowTests.asmdef
│ ├── GetParamName.cs
│ ├── TestBarrier.cs
│ ├── TestBase.cs
│ ├── TestChannel.cs
│ ├── TestConditionals.cs
│ ├── TestEventStream.cs
│ ├── TestFlowExtra.cs
│ ├── TestKernel.cs
│ ├── TestLog.cs
│ ├── TestLoops.cs
│ ├── TestResumeAfter.cs
│ ├── TestSequenceTopology.cs
│ ├── TestTimers.cs
│ ├── TestTrigger.cs
│ ├── TimerTimedWait.cs
│ └── packages.config
├── TestFlow.asmdef
├── TestFlow.csproj
└── packages.config
├── appveyor.yml
├── flow-small.jpg
└── package.json
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
4 | # Custom for Visual Studio
5 | *.cs diff=csharp
6 | *.sln merge=union
7 | *.csproj merge=union
8 | *.vbproj merge=union
9 | *.fsproj merge=union
10 | *.dbproj merge=union
11 |
12 | # Standard to msysgit
13 | *.doc diff=astextplain
14 | *.DOC diff=astextplain
15 | *.docx diff=astextplain
16 | *.DOCX diff=astextplain
17 | *.dot diff=astextplain
18 | *.DOT diff=astextplain
19 | *.pdf diff=astextplain
20 | *.PDF diff=astextplain
21 | *.rtf diff=astextplain
22 | *.RTF diff=astextplain
23 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .current
2 | .enter
3 | .leave
4 | .leu
5 | .nuget
6 |
7 | # Unity output folders
8 | /Library/
9 | /Builds/
10 | /Logs/
11 | /Temp/
12 | /[Pp]ackages/
13 |
14 | # We need this, not everything else in Packages
15 | !/Packages/manifest.json
16 |
17 | # Visual Studio cache directory
18 | .vs/
19 |
20 | bin/
21 | obj/
22 | bin.meta
23 | obj.meta
24 | *.user
25 | .sln.meta
26 | .csproj.meta
27 | .user.meta
28 | *.meta
29 |
30 | # Autogenerated VS/MD solution and project files
31 | *.unityproj
32 | *.csproj
33 | *.sln
34 | *.suo
35 | *.tmp
36 | *.user
37 | *.userprefs
38 |
39 | # Debug info
40 | *.pidb
41 | *.pdb
42 | *.pidb.meta
43 | *.pdb.meta
44 |
45 | # Unity3D Generated File On Crash Reports
46 | sysinfo.txt
47 |
48 |
--------------------------------------------------------------------------------
/.nuget/NuGet.Config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.nuget/NuGet.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cschladetsch/Flow/d057260fe13c1e4605b854857bb4b2655d263d02/.nuget/NuGet.exe
--------------------------------------------------------------------------------
/.nuget/NuGet.targets:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | $(MSBuildProjectDirectory)\..\
5 |
6 |
7 | false
8 |
9 |
10 | false
11 |
12 |
13 | true
14 |
15 |
16 | false
17 |
18 |
19 |
20 |
21 |
22 |
26 |
27 |
28 |
29 |
30 | $([System.IO.Path]::Combine($(SolutionDir), ".nuget"))
31 | $([System.IO.Path]::Combine($(ProjectDir), "packages.config"))
32 |
33 |
34 |
35 |
36 | $(SolutionDir).nuget
37 | packages.config
38 |
39 |
40 |
41 |
42 | $(NuGetToolsPath)\NuGet.exe
43 | @(PackageSource)
44 |
45 | "$(NuGetExePath)"
46 | mono --runtime=v4.0.30319 $(NuGetExePath)
47 |
48 | $(TargetDir.Trim('\\'))
49 |
50 | -RequireConsent
51 | -NonInteractive
52 |
53 | "$(SolutionDir) "
54 | "$(SolutionDir)"
55 |
56 |
57 | $(NuGetCommand) install "$(PackagesConfig)" -source "$(PackageSources)" $(NonInteractiveSwitch) $(RequireConsentSwitch) -solutionDir $(PaddedSolutionDir)
58 | $(NuGetCommand) pack "$(ProjectPath)" -Properties "Configuration=$(Configuration);Platform=$(Platform)" $(NonInteractiveSwitch) -OutputDirectory "$(PackageOutputDir)" -symbols
59 |
60 |
61 |
62 | RestorePackages;
63 | $(BuildDependsOn);
64 |
65 |
66 |
67 |
68 | $(BuildDependsOn);
69 | BuildPackage;
70 |
71 |
72 |
73 |
74 |
75 |
76 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
91 |
92 |
95 |
96 |
97 |
98 |
100 |
101 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
133 |
134 |
135 |
136 |
--------------------------------------------------------------------------------
/EDebugLevel.cs:
--------------------------------------------------------------------------------
1 | namespace Flow {
2 | ///
3 | /// Used to control the verbosity of logging
4 | ///
5 | public enum EDebugLevel {
6 | None
7 | , Low
8 | , Medium
9 | , High
10 | , Verbose
11 | }
12 | }
--------------------------------------------------------------------------------
/ELogLevel.cs:
--------------------------------------------------------------------------------
1 | namespace Flow {
2 | public enum ELogLevel {
3 | None = 0
4 | , Info = 1
5 | , Warn = 2
6 | , Verbose = 4
7 | , Error = 8
8 | }
9 | }
--------------------------------------------------------------------------------
/Flow.asmdef:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Flow",
3 | "references": [],
4 | "optionalUnityReferences": [],
5 | "includePlatforms": [],
6 | "excludePlatforms": [],
7 | "allowUnsafeCode": false,
8 | "overrideReferences": false,
9 | "precompiledReferences": [],
10 | "autoReferenced": true,
11 | "defineConstraints": []
12 | }
13 |
--------------------------------------------------------------------------------
/Flow.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {18326C80-2CC4-417D-8FEE-EA608ED85636}
8 | Library
9 | Properties
10 | Flow
11 | Flow
12 | v4.8
13 | 512
14 | true
15 |
16 |
17 |
18 |
19 |
20 | true
21 | full
22 | false
23 | bin\Debug\
24 | DEBUG;TRACE
25 | prompt
26 | 4
27 |
28 |
29 | pdbonly
30 | true
31 | bin\Release\
32 | TRACE
33 | prompt
34 | 4
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
--------------------------------------------------------------------------------
/Flow.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 15
4 | VisualStudioVersion = 15.0.28307.572
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Flow", "Flow.csproj", "{18326C80-2CC4-417D-8FEE-EA608ED85636}"
7 | EndProject
8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestFlow", "TestFlow\TestFlow.csproj", "{12E9E3DC-61D3-42F6-B9AF-8BE0C5EAAC9F}"
9 | EndProject
10 | Global
11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
12 | Debug|Any CPU = Debug|Any CPU
13 | Release|Any CPU = Release|Any CPU
14 | EndGlobalSection
15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
16 | {18326C80-2CC4-417D-8FEE-EA608ED85636}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
17 | {18326C80-2CC4-417D-8FEE-EA608ED85636}.Debug|Any CPU.Build.0 = Debug|Any CPU
18 | {18326C80-2CC4-417D-8FEE-EA608ED85636}.Release|Any CPU.ActiveCfg = Release|Any CPU
19 | {18326C80-2CC4-417D-8FEE-EA608ED85636}.Release|Any CPU.Build.0 = Release|Any CPU
20 | {12E9E3DC-61D3-42F6-B9AF-8BE0C5EAAC9F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
21 | {12E9E3DC-61D3-42F6-B9AF-8BE0C5EAAC9F}.Debug|Any CPU.Build.0 = Debug|Any CPU
22 | {12E9E3DC-61D3-42F6-B9AF-8BE0C5EAAC9F}.Release|Any CPU.ActiveCfg = Release|Any CPU
23 | {12E9E3DC-61D3-42F6-B9AF-8BE0C5EAAC9F}.Release|Any CPU.Build.0 = Release|Any CPU
24 | EndGlobalSection
25 | GlobalSection(SolutionProperties) = preSolution
26 | HideSolutionNode = FALSE
27 | EndGlobalSection
28 | GlobalSection(ExtensibilityGlobals) = postSolution
29 | SolutionGuid = {E39A774E-3589-40AE-B148-96CF84C56F84}
30 | EndGlobalSection
31 | EndGlobal
32 |
--------------------------------------------------------------------------------
/Flow.sln.DotSettings:
--------------------------------------------------------------------------------
1 |
2 | True
3 | True
--------------------------------------------------------------------------------
/GlobalSuppressions.cs:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cschladetsch/Flow/d057260fe13c1e4605b854857bb4b2655d263d02/GlobalSuppressions.cs
--------------------------------------------------------------------------------
/IBarrier.cs:
--------------------------------------------------------------------------------
1 | // (C) 2012 christian.schladetsch@gmail.com. See https://github.com/cschladetsch/Flow.
2 |
3 | namespace Flow {
4 | ///
5 | ///
6 | /// A Barrier is a that Completes itself when all added
7 | /// s have been removed from it.
8 | ///
9 | public interface IBarrier
10 | : IGroup {
11 | new IBarrier AddTo(IGroup group);
12 | new IBarrier Named(string name);
13 | }
14 | }
--------------------------------------------------------------------------------
/IBreak.cs:
--------------------------------------------------------------------------------
1 | namespace Flow {
2 | public interface IBreak
3 | : IGenerator {
4 | }
5 | }
--------------------------------------------------------------------------------
/ICase.cs:
--------------------------------------------------------------------------------
1 | // (C) 2012 christian.schladetsch@gmail.com. See https://github.com/cschladetsch/Flow.
2 |
3 | using System;
4 |
5 | namespace Flow {
6 | public interface ICase
7 | where T
8 | : IComparable {
9 | IGenerator Body { get; }
10 | bool Matches(T val);
11 | }
12 | }
--------------------------------------------------------------------------------
/IChannel.cs:
--------------------------------------------------------------------------------
1 | // (C) 2012 christian.schladetsch@gmail.com. See https://github.com/cschladetsch/Flow.
2 |
3 | namespace Flow {
4 | ///
5 | ///
6 | /// A buffered input/output stream.
7 | ///
8 | /// If a Channel is created with a Generator, that is used as the
9 | /// source for the channel.
10 | ///
11 | ///
12 | public interface IChannel
13 | : ITransient {
14 | void Insert(T val);
15 | IFuture Extract();
16 |
17 | new IChannel AddTo(IGroup group);
18 | }
19 | }
--------------------------------------------------------------------------------
/ICoroutine.cs:
--------------------------------------------------------------------------------
1 | // (C) 2012 christian.schladetsch@gmail.com See https://github.com/cschladetsch/Flow.
2 |
3 | namespace Flow {
4 | public interface ICoroutine
5 | : IGenerator {
6 | }
7 |
8 | public interface ICoroutine
9 | : IGenerator {
10 | }
11 | }
--------------------------------------------------------------------------------
/IFactory.cs:
--------------------------------------------------------------------------------
1 | // (C) 2012 christian.schladetsch@gmail.com See https://github.com/cschladetsch/Flow.
2 |
3 | using System;
4 | using System.Collections;
5 | using System.Collections.Generic;
6 |
7 | namespace Flow {
8 | ///
9 | /// Creates Flow instances that reside within a Kernel.
10 | /// Instances created by a Factory are not automatically added to any process.
11 | ///
12 | public interface IFactory {
13 | IKernel Kernel { get; set; }
14 |
15 | ///
16 | /// Make a new Group containing the given set of s.
17 | /// When a group is Stepped, nothing happens.
18 | ///
19 | IGroup Group(IEnumerable gens);
20 |
21 | IGroup Group(params ITransient[] gens);
22 |
23 | ///
24 | /// Make a new Node containing the given s.
25 | /// When a is Stepped, it steps all contained generators.
26 | ///
27 | /// The generator to add to the new Node.
28 | /// A new prepared Node.
29 | INode Node(IEnumerable gens);
30 |
31 | INode Node(params IGenerator[] gens);
32 |
33 | ///
34 | /// Make a new timer that fires once, then Completes.
35 | ///
36 | ///
37 | /// The shortest time-span before this timer will Complete.
38 | ///
39 | ITimer OneShotTimer(TimeSpan interval);
40 |
41 | ITimer OneShotTimer(TimeSpan interval, Action onElapsed);
42 |
43 | ///
44 | /// Make a new timer that fires an event at given intervals.
45 | ///
46 | /// The shortest time between subsequent events.
47 | IPeriodic PeriodicTimer(TimeSpan interval);
48 |
49 | ///
50 | /// Make a new barrier with given args.
51 | ///
52 | IBarrier Barrier(params ITransient[] args);
53 |
54 | IBarrier Barrier(IEnumerable args);
55 |
56 | ///
57 | /// Make a new TimedBarrier with given args.
58 | ///
59 | /// The shortest time span before the Barrier Completes.
60 | ITimedBarrier TimedBarrier(TimeSpan span, params ITransient[] args);
61 |
62 | ITimedBarrier TimedBarrier(TimeSpan span, IEnumerable args);
63 |
64 | ///
65 | /// Make a new trigger with given args.
66 | ///
67 | ITrigger Trigger(params ITransient[] args);
68 |
69 | ///
70 | /// Make a new TimedTrigger with given args.
71 | ///
72 | /// The shortest time before the trigger Completes.
73 | ITimedTrigger TimedTrigger(TimeSpan span, params ITransient[] args);
74 |
75 | ///
76 | /// Make a new future value of type T.
77 | ///
78 | IFuture Future();
79 |
80 | IFuture Future(T val);
81 |
82 | ///
83 | /// Make a new timed promised future of type T.
84 | ///
85 | /// The shortest time-span before the future Completes.
86 | ITimedFuture TimedFuture(TimeSpan timeOut);
87 |
88 | ITimedFuture TimedFuture(TimeSpan timeOut, T val);
89 |
90 | ///
91 | /// Make a new
92 | ///
93 | ICoroutine Coroutine(Func fun);
94 |
95 | ICoroutine Coroutine(Func fun, T0 t0);
96 | ICoroutine Coroutine(Func fun, T0 t0, T1 t1);
97 | ICoroutine Coroutine(Func fun, T0 t0, T1 t1, T2 t2);
98 | ICoroutine Coroutine
(Func> fun);
99 | ICoroutine Coroutine
(Func> fun, T0 t0);
100 |
101 | ///
102 | /// Make a new
103 | ///
104 | ///
105 | ///
106 | ///
107 | ISubroutine Subroutine
(Func fun);
108 |
109 | ISubroutine Subroutine
(Func fun, T0 t0);
110 | ISubroutine Subroutine
(Func fun, T0 t0, T1 t1);
111 | ISubroutine Subroutine
(Func fun, T0 t0, T1 t1, T2 t2);
112 |
113 | ///
114 | /// Make a new channel with value-type T.
115 | ///
116 | /// The type of things stored in the Channel.
117 | IChannel Channel
();
118 |
119 | IChannel
Channel
(IGenerator
gen);
120 |
121 | ///
122 | /// Make a that does nothing, then Completes.
123 | ///
124 | IGenerator Nop();
125 |
126 | ///
127 | /// Do something.
128 | ///
129 | /// What to do.
130 | IGenerator Do(Action act);
131 |
132 | ///
133 | /// Make a Generator that always returns the same value.
134 | ///
135 | /// The value to always return.
136 | IGenerator Value(T val);
137 |
138 | ///
139 | /// DOC
140 | ///
141 | ///
142 | ///
143 | IGenerator Expression(Func action);
144 |
145 | ///
146 | /// Make a generator that steps the given @if generator if the given
147 | /// predicate returns true.
148 | ///
149 | /// The predicate to test.
150 | /// What to do if the predicate returns true.
151 | IGenerator If(Func pred, IGenerator @if);
152 |
153 | ///
154 | /// Make a Generator that does one of two things, depending on
155 | /// result of given predicate.
156 | ///
157 | /// The predicate to test.
158 | /// What to generate if the predicate is true.
159 | /// What to generate if the predicate is false.
160 | IGenerator IfElse(Func pred, IGenerator @if, IGenerator @else);
161 |
162 | ///
163 | /// Make a that steps zero or
164 | /// more other generators while the given predicate is true.
165 | ///
166 | IGenerator While(Func pred, params IGenerator[] body);
167 |
168 | ///
169 | /// Perform one Generator after the previous Generator Completes.
170 | ///
171 | ISequence Sequence(params IGenerator[] transients);
172 |
173 | ISequence Sequence(IEnumerable transients);
174 |
175 | ///
176 | /// DOC
177 | ///
178 | IGenerator Break();
179 |
180 | ///
181 | /// DOC
182 | ///
183 | ///
184 | ITransient Wait(TimeSpan duration);
185 |
186 | ///
187 | /// DOC
188 | ///
189 | ///
190 | ///
191 | ITransient WaitFor(ITransient trans, TimeSpan timeOut);
192 |
193 | ///
194 | /// Do meta-things.
195 | ///
196 | IGenerator SetDebugLevel(EDebugLevel level);
197 |
198 | IGenerator Log(string fmt, params object[] args);
199 | IGenerator Warn(string fmt, params object[] args);
200 | IGenerator Error(string fmt, params object[] args);
201 |
202 | T Prepare(T obj)
203 | where T : ITransient;
204 | }
205 | }
--------------------------------------------------------------------------------
/IFuture.cs:
--------------------------------------------------------------------------------
1 | // (C) 2012 Christian Schladetsch. See https://github.com/cschladetsch/Flow.
2 |
3 | using System;
4 |
5 | namespace Flow {
6 | public delegate void FutureHandler(IFuture fut);
7 |
8 | ///
9 | ///
10 | /// When its' Value property is first set, set Available property to true, and Complete.
11 | ///
12 | public interface IFuture
13 | : ITransient {
14 | bool Available { get; }
15 | T Value { get; set; }
16 | event FutureHandler Arrived;
17 |
18 | IFuture Then(Action> action);
19 | }
20 | }
--------------------------------------------------------------------------------
/IGenerator.cs:
--------------------------------------------------------------------------------
1 | // (C) 2012 Christian Schladetsch. See https://github.com/cschladetsch/Flow.
2 |
3 | using System;
4 |
5 | namespace Flow {
6 | public delegate void GeneratorHandler(IGenerator generator);
7 |
8 | ///
9 | ///
10 | /// A Generator does some work every time its Step method is called, unless it is Suspended or Completed.
11 | /// All Generators are Resumed when they are first created by a Factory
12 | ///
13 | public interface IGenerator
14 | : ITransient
15 | , ISteppable {
16 | bool Running { get; }
17 | int StepNumber { get; }
18 | object Value { get; }
19 | event GeneratorHandler Resumed;
20 | event GeneratorHandler Stepped;
21 | event GeneratorHandler Suspended;
22 |
23 | void Resume();
24 | void Pre();
25 | void Post();
26 | void Suspend();
27 |
28 | new IGenerator AddTo(IGroup group);
29 | new IGenerator Named(string name);
30 |
31 | IGenerator SuspendAfter(ITransient other);
32 | IGenerator SuspendAfter(TimeSpan span);
33 |
34 | IGenerator ResumeAfter(Func pred);
35 | IGenerator ResumeAfter(Func pred, string name);
36 | IGenerator ResumeAfter(ITransient other);
37 | IGenerator ResumeAfter(TimeSpan span);
38 | }
39 |
40 | public delegate void GeneratorHandler(IGenerator generator);
41 |
42 | public interface IGenerator
43 | : IGenerator {
44 | new T Value { get; }
45 | }
46 | }
--------------------------------------------------------------------------------
/IGroup.cs:
--------------------------------------------------------------------------------
1 | // (C) 2012 Christian Schladetsch. See https://github.com/cschladetsch/Flow.
2 |
3 | using System.Collections.Generic;
4 |
5 | namespace Flow {
6 | public delegate void GroupHandler(IGroup node, ITransient child);
7 |
8 | ///
9 | ///
10 | /// A Group contains a collection of other Transients, and fires events when the
11 | /// contents of the group changes.
12 | /// When a Group is stepped, nothing happens.
13 | /// When a Group is resumed, all child generators are also resumed.
14 | /// When a Group is suspended, all child generators are also suspended.
15 | ///
16 | public interface IGroup
17 | : IGenerator {
18 | IList Contents { get; }
19 | IEnumerable Generators { get; }
20 |
21 | bool Empty { get; }
22 |
23 | // Occurs when a transient is added to this group.
24 | event GroupHandler OnAdded;
25 |
26 | // Occurs when a transient is removed from this group.
27 | event GroupHandler OnRemoved;
28 |
29 | // Add the specified transient to this group iff it is not already a member
30 | // of this group.
31 | void Add(IEnumerable trans);
32 | void Add(params ITransient[] trans);
33 |
34 | // Remove the specified transient from this group.
35 | void Remove(ITransient trans);
36 | }
37 | }
--------------------------------------------------------------------------------
/IKernel.cs:
--------------------------------------------------------------------------------
1 | // (C) 2012 Christian Schladetsch. See https://github.com/cschladetsch/Flow.
2 |
3 | using System;
4 |
5 | namespace Flow {
6 | ///
7 | ///
8 | /// When an IKernel is Stepped, it updates its Time property,
9 | /// and steps its Root node.
10 | ///
11 | public interface IKernel
12 | : IGenerator {
13 | EDebugLevel DebugLevel { get; set; }
14 | ILogger Log { get; set; }
15 |
16 | ///
17 | /// If true, the Kernel will stop mid-senten
18 | ///
19 | bool Break { get; }
20 |
21 | ///
22 | /// The root of the process tree.
23 | ///
24 | INode Root { get; set; }
25 |
26 | IFactory Factory { get; }
27 | ITimeFrame Time { get; }
28 |
29 | void Update(float deltaSeconds);
30 | void Wait(TimeSpan duration);
31 | void BreakFlow();
32 | }
33 | }
--------------------------------------------------------------------------------
/ILogger.cs:
--------------------------------------------------------------------------------
1 | // (C) 2012 Christian Schladetsch. See https://github.com/cschladetsch/Flow.
2 |
3 | namespace Flow {
4 | ///
5 | /// DOC
6 | ///
7 | public interface ILogger {
8 | string LogPrefix { get; set; }
9 | object LogSubject { get; set; }
10 | bool ShowSource { get; set; }
11 | bool ShowStack { get; set; }
12 | int Verbosity { get; set; }
13 |
14 | void Info(string fmt, params object[] args);
15 | void Warn(string fmt, params object[] args);
16 | void Error(string fmt, params object[] args);
17 | void Verbose(int level, string fmt, params object[] args);
18 | }
19 | }
--------------------------------------------------------------------------------
/INamed.cs:
--------------------------------------------------------------------------------
1 | // (C) 2012 Christian Schladetsch. See https://github.com/cschladetsch/Flow.
2 |
3 | namespace Flow {
4 | public interface INamed {
5 | string Name { get; set; }
6 | }
7 | }
--------------------------------------------------------------------------------
/INode.cs:
--------------------------------------------------------------------------------
1 | // (C) 2012 Christian Schladetsch. See https://github.com/cschladetsch/Flow.
2 |
3 | namespace Flow {
4 | ///
5 | ///
6 | /// A Node is a Group that steps all referenced Generators when it itself is Stepped,
7 | /// and similarly for Pre and Post.
8 | ///
9 | public interface INode
10 | : IGroup {
11 | void Add(params IGenerator[] gens);
12 | new INode AddTo(IGroup group);
13 | new INode Named(string name);
14 | }
15 | }
--------------------------------------------------------------------------------
/IPeriodic.cs:
--------------------------------------------------------------------------------
1 | // (C) 2012 Christian Schladetsch. See https://github.com/cschladetsch/Flow.
2 |
3 | using System;
4 |
5 | namespace Flow {
6 | ///
7 | ///
8 | /// Periodic instances regularly fire their Elapsed event.
9 | ///
10 | /// NOTE the timer will fire at most once per Kernel Step
11 | ///
12 | ///
13 | public interface IPeriodic : IGenerator {
14 | // The time that this periodic timer was started.
15 | DateTime TimeStarted { get; }
16 |
17 | // Successive Elapsed events will have not less than this TimeSpan between being fired.
18 | TimeSpan Interval { get; }
19 |
20 | TimeSpan TimeRemaining { get; }
21 |
22 | // Periodically fires when the timer has elapsed. Fired at most once per Kernel Step
23 | event TransientHandler Elapsed;
24 | }
25 | }
--------------------------------------------------------------------------------
/ISequence.cs:
--------------------------------------------------------------------------------
1 | namespace Flow {
2 | ///
3 | ///
4 | /// A Sequence is a node that steps only the first generator until it is
5 | /// completed, then moves in to the next.
6 | /// When the sequence is empty it Completes.
7 | ///
8 | public interface ISequence
9 | : INode {
10 | }
11 | }
--------------------------------------------------------------------------------
/ISteppable.cs:
--------------------------------------------------------------------------------
1 | namespace Flow {
2 | public interface ISteppable {
3 | void Step();
4 | }
5 | }
--------------------------------------------------------------------------------
/ISubroutine.cs:
--------------------------------------------------------------------------------
1 | // (C) 2012 Christian Schladetsch. See https://github.com/cschladetsch/Flow.
2 |
3 | namespace Flow {
4 | ///
5 | ///
6 | /// An ISubroutine is-a IGenerator, implemented as a direct method call.
7 | ///
8 | public interface ISubroutine
9 | : IGenerator {
10 | }
11 |
12 | public interface ISubroutine
13 | : IGenerator {
14 | }
15 | }
--------------------------------------------------------------------------------
/ITimeFrame.cs:
--------------------------------------------------------------------------------
1 | // (C) 2012 Christian Schladetsch. See https://github.com/cschladetsch/Flow.
2 |
3 | using System;
4 |
5 | namespace Flow {
6 | ///
7 | /// Stores information about a time step.
8 | ///
9 | public interface ITimeFrame {
10 | DateTime Last { get; }
11 | DateTime Now { get; }
12 | TimeSpan Delta { get; }
13 | }
14 | }
--------------------------------------------------------------------------------
/ITimedTransients.cs:
--------------------------------------------------------------------------------
1 | // (C) 2012 Christian Schladetsch. See https://github.com/cschladetsch/Flow.
2 |
3 | using System;
4 |
5 | namespace Flow {
6 | ///
7 | ///
8 | ///
9 | ///
10 | public interface ITimedFuture
11 | : IFuture
12 | , ITimesOut {
13 | ITimedFuture Then(Action> action);
14 | }
15 |
16 | public interface ITimedBarrier
17 | : IBarrier
18 | , ITimesOut {
19 | new ITimedBarrier AddTo(IGroup group);
20 | new ITimedBarrier Named(string name);
21 | }
22 |
23 | public interface ITimedTrigger
24 | : ITrigger
25 | , ITimesOut {
26 | }
27 |
28 | public interface ITimedNode
29 | : INode
30 | , ITimesOut {
31 | }
32 | }
--------------------------------------------------------------------------------
/ITimer.cs:
--------------------------------------------------------------------------------
1 | // (C) 2012 Christian Schladetsch. See https://github.com/cschladetsch/Flow.
2 |
3 | using System;
4 |
5 | namespace Flow {
6 | ///
7 | /// A one-shot OneShotTimer that will fire its Elapsed event, and then Complete itself after a fixed time Interval.
8 | ///
9 | public interface ITimer : IPeriodic {
10 | DateTime TimeEnds { get; }
11 | }
12 | }
--------------------------------------------------------------------------------
/ITimesOut.cs:
--------------------------------------------------------------------------------
1 | // (C) 2012 Christian Schladetsch. See https://github.com/cschladetsch/Flow.
2 |
3 | namespace Flow {
4 | public delegate void TimedOutHandler(ITimesOut timed);
5 |
6 | /// After a period of time, an instance of ITimesOut will 'time-out' if not already Completed. In this case, it will:
7 | public interface ITimesOut : ITransient {
8 | ITimer Timer { get; }
9 | bool HasTimedOut { get; }
10 | event TimedOutHandler TimedOut;
11 | }
12 | }
--------------------------------------------------------------------------------
/ITransient.cs:
--------------------------------------------------------------------------------
1 | // (C) 2012 christian.schladetsch@gmail.com See https://github.com/cschladetsch/Flow.
2 |
3 | using System;
4 |
5 | namespace Flow {
6 | public delegate void TransientHandler(ITransient sender);
7 |
8 | public delegate void TransientHandlerReason(ITransient sender, ITransient reason);
9 |
10 | ///
11 | ///
12 | ///
13 | /// An ITransient object has-a IKernel and notifies observers when it
14 | /// has been Disposed.
15 | ///
16 | public interface ITransient
17 | : INamed {
18 | ///
19 | /// True if the transient has not been Completed.
20 | ///
21 | bool Active { get; }
22 |
23 | ///
24 | /// The kernel that made this transient.
25 | ///
26 | IKernel Kernel { get; /* TODO internal: */ set; }
27 |
28 | event TransientHandler Completed;
29 |
30 | void Complete();
31 |
32 | ITransient Named(string name);
33 | ITransient AddTo(IGroup group);
34 |
35 | ///
36 | /// Resume the given process after this one completes.
37 | ///
38 | ///
39 | //ITransient Then(IGenerator next);
40 | //ITransient Then(Action action);
41 | //ITransient Then(Action action);
42 | }
43 | }
--------------------------------------------------------------------------------
/ITrigger.cs:
--------------------------------------------------------------------------------
1 | // (C) 2012 christian.schladetsch@gmail.com See https://github.com/cschladetsch/Flow.
2 |
3 | using System;
4 |
5 | namespace Flow {
6 | ///
7 | ///
8 | /// A Trigger is a that completes whenever
9 | /// a child completes.
10 | ///
11 | public interface ITrigger
12 | : IGroup {
13 | ///
14 | /// What triggered this to Complete.
15 | ///
16 | ITransient Reason { get; }
17 |
18 | event Action OnTripped;
19 | }
20 | }
--------------------------------------------------------------------------------
/Impl/Barrier.cs:
--------------------------------------------------------------------------------
1 | // (C) 2012 Christian Schladetsch. See https://github.com/cschladetsch/Flow.
2 |
3 | using System.Linq;
4 |
5 | namespace Flow.Impl {
6 | internal class Barrier
7 | : Group
8 | , IBarrier {
9 | public override void Post() {
10 | base.Post();
11 |
12 | if (Contents.Any(t => t.Active)) {
13 | return;
14 | }
15 |
16 | if (_Additions.Count == 0) {
17 | Complete();
18 | }
19 | }
20 |
21 | public new IBarrier AddTo(IGroup group) {
22 | return this.AddToGroup(group);
23 | }
24 |
25 | public new IBarrier Named(string name) {
26 | return this.SetName(name);
27 | }
28 | }
29 | }
--------------------------------------------------------------------------------
/Impl/BlockingChannel.cs:
--------------------------------------------------------------------------------
1 | // (C) 2012 christian.schladetsch@gmail.com. See https://github.com/cschladetsch/Flow.
2 |
3 | using System;
4 | using System.Collections.Generic;
5 |
6 | namespace Flow.Impl {
7 | ///
8 | ///
9 | /// A thread-safe channel of values.
10 | ///
11 | internal class BlockingChannel
12 | : Subroutine
13 | , IChannel {
14 | private readonly object _mutex = new object();
15 | private readonly Queue> _requests = new Queue>();
16 | private readonly Queue _values = new Queue
();
17 |
18 | internal BlockingChannel(IKernel kernel) {
19 | Sub = StepChannel;
20 | Completed += tr => Close();
21 | }
22 |
23 | internal BlockingChannel(IKernel kernel, IGenerator
gen)
24 | : this(kernel) {
25 | gen.Stepped += g => Insert(gen.Value);
26 | CompleteAfter(gen);
27 | }
28 |
29 | public IFuture
Extract() {
30 | lock (_mutex) {
31 | var future = Factory.Future
();
32 | _requests.Enqueue(future);
33 | return future;
34 | }
35 | }
36 |
37 | public new IChannel
AddTo(IGroup group) {
38 | throw new NotImplementedException();
39 | }
40 |
41 | public void Insert(TR val) {
42 | lock (_mutex) {
43 | _values.Enqueue(val);
44 | }
45 | }
46 |
47 | public List
ExtractAll() {
48 | throw new NotImplementedException();
49 | }
50 |
51 | public void Flush() {
52 | lock (_mutex) {
53 | while (_values.Count > 0 && _requests.Count > 0) _requests.Dequeue().Value = _values.Dequeue();
54 | }
55 | }
56 |
57 | internal void Close() {
58 | lock (_mutex) {
59 | Flush();
60 |
61 | foreach (var f in _requests) ((IDisposable)f).Dispose();
62 | }
63 | }
64 |
65 | private bool StepChannel(IGenerator self) {
66 | ++StepNumber;
67 |
68 | if (!Active) {
69 | return true;
70 | }
71 |
72 | Flush();
73 |
74 | return true;
75 | }
76 | }
77 | }
--------------------------------------------------------------------------------
/Impl/Break.cs:
--------------------------------------------------------------------------------
1 | // (C) 2012 christian.schladetsch@gmail.com. See https://github.com/cschladetsch/Flow.
2 |
3 | namespace Flow.Impl {
4 | internal class Break
5 | : Generator
6 | , IBreak {
7 | public override void Step() {
8 | Kernel.BreakFlow();
9 | }
10 | }
11 | }
--------------------------------------------------------------------------------
/Impl/Case.cs:
--------------------------------------------------------------------------------
1 | // (C) 2012 christian.schladetsch@gmail.com. See https://github.com/cschladetsch/Flow.
2 |
3 | using System;
4 |
5 | namespace Flow.Impl {
6 | internal class Case
7 | : ICase where T
8 | : IComparable {
9 | private readonly T _compare;
10 |
11 | internal Case(T val, IGenerator gen) {
12 | _compare = val;
13 | Body = gen;
14 | }
15 |
16 | public IGenerator Body { get; }
17 |
18 | public bool Matches(T val) {
19 | return val.CompareTo(_compare) == 0;
20 | }
21 | }
22 | }
--------------------------------------------------------------------------------
/Impl/Channel.cs:
--------------------------------------------------------------------------------
1 | // (C) 2012 christian.schladetsch@gmail.com. See https://github.com/cschladetsch/Flow.
2 |
3 | using System.Collections.Generic;
4 |
5 | namespace Flow.Impl {
6 | internal class Channel
7 | : Subroutine
8 | , IChannel {
9 | private readonly Queue> _requests = new Queue>();
10 | private readonly Queue _values = new Queue
();
11 |
12 | internal Channel(IKernel kernel) {
13 | Sub = StepChannel;
14 | Completed += tr => Close();
15 | }
16 |
17 | internal Channel(IKernel kernel, IGenerator
gen)
18 | : this(kernel) {
19 | gen.Stepped += g => Insert(gen.Value);
20 | CompleteAfter(gen);
21 | }
22 |
23 | public void Insert(TR val) {
24 | _values.Enqueue(val);
25 | }
26 |
27 | public IFuture
Extract() {
28 | var future = Factory.Future
();
29 | _requests.Enqueue(future);
30 | return future;
31 | }
32 |
33 | public new IChannel
AddTo(IGroup group) {
34 | group.Add(this);
35 | return this;
36 | }
37 |
38 | public List
ExtractAll() {
39 | Flush();
40 |
41 | var list = new List
();
42 | while (_values.Count > 0)
43 | list.Add(_values.Dequeue());
44 |
45 | return list;
46 | }
47 |
48 | public void Flush() {
49 | while (_values.Count > 0 && _requests.Count > 0)
50 | _requests.Dequeue().Value = _values.Dequeue();
51 | }
52 |
53 | internal void Close() {
54 | Flush();
55 |
56 | foreach (var req in _requests)
57 | req.Complete();
58 | }
59 |
60 | private bool StepChannel(IGenerator self) {
61 | Flush();
62 |
63 | return true;
64 | }
65 | }
66 | }
--------------------------------------------------------------------------------
/Impl/Coroutine.cs:
--------------------------------------------------------------------------------
1 | // (C) 2012 christian.schladetsch@gmail.com. See https://github.com/cschladetsch/Flow.
2 |
3 | using System;
4 | using System.Collections;
5 | using System.Collections.Generic;
6 |
7 | namespace Flow.Impl {
8 | public class Coroutine
9 | : Generator
10 | , ICoroutine {
11 | protected IEnumerator _state;
12 | private object _value;
13 | internal Func Start;
14 |
15 | public Coroutine() {
16 | }
17 |
18 | public Coroutine(Func start)
19 | : this() {
20 | Start = start;
21 | }
22 |
23 | public override object Value => _value;
24 |
25 | public override void Step() {
26 | if (!Running || !Active) {
27 | return;
28 | }
29 |
30 | if (_state == null) {
31 | if (Start == null) {
32 | CannotStart();
33 | }
34 | else {
35 | _state = Start();
36 | }
37 |
38 | if (_state == null) {
39 | CannotStart();
40 | }
41 | }
42 |
43 | if (_state == null || !_state.MoveNext()) {
44 | Complete();
45 | return;
46 | }
47 |
48 | _value = _state.Current;
49 |
50 | base.Step();
51 | }
52 |
53 | protected static void CannotStart() {
54 | throw new Exception("Coroutine cannot start");
55 | }
56 | }
57 |
58 | internal class Coroutine
59 | : Generator
60 | , ICoroutine {
61 | private IEnumerator _state;
62 | internal Func> Start;
63 |
64 | public override void Step() {
65 | if (!Running || !Active) {
66 | return;
67 | }
68 |
69 | if (_state == null) {
70 | if (Start == null) {
71 | CannotStart();
72 | }
73 | else {
74 | _state = Start();
75 | }
76 |
77 | if (_state == null) {
78 | CannotStart();
79 | }
80 | }
81 |
82 | if (_state == null || !_state.MoveNext()) {
83 | Complete();
84 | return;
85 | }
86 |
87 | Value = _state.Current;
88 |
89 | base.Step();
90 | }
91 | }
92 | }
--------------------------------------------------------------------------------
/Impl/Create.cs:
--------------------------------------------------------------------------------
1 | // (C) 2012 christian.schladetsch@gmail.com. See https://github.com/cschladetsch/Flow.
2 |
3 | using Flow.Impl;
4 |
5 | #pragma warning disable 1685
6 |
7 | namespace Flow {
8 | ///
9 | /// Bootstrapper for the flow library using default implementations.
10 | ///
11 | public static class Create {
12 | public static IKernel Kernel() {
13 | return NewFactory().Kernel;
14 | }
15 |
16 | public static IFactory NewFactory()
17 | where TF : class
18 | , IFactory
19 | , new() {
20 | var kernel = new Kernel();
21 | var factory = new TF();
22 |
23 | kernel.Factory = factory;
24 | kernel.Kernel = kernel;
25 | factory.Kernel = kernel;
26 | kernel.Resume();
27 | kernel.Root = new Node { Kernel = kernel, Name = "Root" };
28 |
29 | return factory;
30 | }
31 | }
32 | }
--------------------------------------------------------------------------------
/Impl/Detail/Detail.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Flow.Impl.Detail
4 | {
5 | ///
6 | /// Perform an arbitrary action at every step.
7 | ///
8 | internal class EveryTime
9 | : Generator
10 | {
11 | public EveryTime(Action act)
12 | {
13 | _act = act;
14 | }
15 |
16 | public override void Step()
17 | {
18 | if (!Active)
19 | return;
20 |
21 | _act();
22 | }
23 |
24 | readonly Action _act;
25 | }
26 |
27 | ///
28 | /// Perform an arbitrary action at first step, then Complete.
29 | ///
30 | internal class OneTime
31 | : EveryTime
32 | {
33 | public OneTime(Action act) : base(act) { }
34 |
35 | public override void Step()
36 | {
37 | base.Step();
38 | Complete();
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/Impl/Exception.cs:
--------------------------------------------------------------------------------
1 | // (C) 2012 christian.schladetsch@gmail.com. See https://github.com/cschladetsch/Flow.
2 |
3 | using System;
4 |
5 | namespace Flow.Impl {
6 | public class FutureNotSetException
7 | : Exception {
8 | public FutureNotSetException()
9 | : base("Future value not arrived yet.") {
10 | }
11 | }
12 |
13 | public class FutureAlreadySetException
14 | : Exception {
15 | public FutureAlreadySetException(string name)
16 | : base($"Future {name} already set.") {
17 | }
18 | }
19 |
20 | public class ReEntranceException
21 | : Exception {
22 | public ReEntranceException()
23 | : base("Method is not re-entrant.") {
24 | }
25 | }
26 | }
--------------------------------------------------------------------------------
/Impl/Extension.cs:
--------------------------------------------------------------------------------
1 | // (C) 2012 Christian Schladetsch. See https://github.com/cschladetsch/Flow.
2 |
3 | using System.Collections.Generic;
4 | using System.Linq;
5 |
6 | namespace Flow {
7 | public static class Extension {
8 | public static T SetName(this T self, string name)
9 | where T : ITransient {
10 | self.Name = name;
11 | return self;
12 | }
13 |
14 | public static T AddToGroup(this T self, IGroup group)
15 | where T : ITransient {
16 | group.Add(self);
17 | return self;
18 | }
19 |
20 | public static bool ContainsRef(this IEnumerable list, T obj) {
21 | return list.Any(elem => ReferenceEquals(elem, obj));
22 | }
23 |
24 | public static void RemoveRef(this IList list, T obj) {
25 | for (var n = 0; n < list.Count; ++n) {
26 | if (!ReferenceEquals(list[n], obj)) {
27 | continue;
28 | }
29 |
30 | list.RemoveAt(n);
31 | return;
32 | }
33 | }
34 | }
35 | }
--------------------------------------------------------------------------------
/Impl/Factory.cs:
--------------------------------------------------------------------------------
1 | // (C) 2012 Christian Schladetsch. See https://github.com/cschladetsch/Flow.
2 |
3 | using System;
4 | using System.Collections;
5 | using System.Collections.Generic;
6 | using System.Linq;
7 |
8 | namespace Flow.Impl {
9 | ///
10 | ///
11 | /// Makes instances for the Flow library
12 | ///
13 | public class Factory
14 | : IFactory {
15 | public IKernel Kernel { get; set; }
16 |
17 | public INode Node(params IGenerator[] gens) {
18 | return Node(gens.ToList());
19 | }
20 |
21 | public INode Node(IEnumerable gens) {
22 | var node = Prepare(new Node());
23 | node.Add(gens);
24 | return node;
25 | }
26 |
27 | public IGroup Group(params ITransient[] trans) {
28 | var group = Prepare(new Group());
29 | group.Add(trans);
30 | return group;
31 | }
32 |
33 | public IGroup Group(IEnumerable trans) {
34 | var group = Prepare(new Group());
35 | group.Add(trans);
36 | return group;
37 | }
38 |
39 | public IGenerator Do(Action act) {
40 | var subroutine = Prepare(new Subroutine {
41 | Sub = self =>
42 | {
43 | act();
44 | self.Complete();
45 | }
46 | });
47 |
48 | return Prepare(subroutine);
49 | }
50 |
51 | public IGenerator If(Func pred, IGenerator body) {
52 | IEnumerator IfCoro(IGenerator self) {
53 | while (true) {
54 | if (!body.Active) {
55 | yield break;
56 | }
57 |
58 | if (pred()) {
59 | body.Step();
60 | }
61 |
62 | yield return 0;
63 | }
64 | }
65 |
66 | return Prepare(Coroutine(IfCoro));
67 | }
68 |
69 | public IGenerator IfElse(Func pred, IGenerator then, IGenerator elseBody) {
70 | IEnumerator IfElseCoro(IGenerator self) {
71 | while (true) {
72 | if (pred()) {
73 | if (!then.Active) {
74 | yield break;
75 | }
76 |
77 | then.Step();
78 | }
79 | else {
80 | if (!elseBody.Active) {
81 | yield break;
82 | }
83 |
84 | elseBody.Step();
85 | }
86 |
87 | yield return null;
88 | }
89 | }
90 |
91 | return Prepare(Coroutine(IfElseCoro));
92 | }
93 |
94 | public IGenerator While(Func pred, params IGenerator[] body) {
95 | var any = body.Any();
96 |
97 | IEnumerator WhileCoro(IGenerator self) {
98 | var node = Prepare(Node(body));
99 | while (pred()) {
100 | node.Step();
101 | if (any && (!node.Active || node.Empty)) {
102 | yield break;
103 | }
104 |
105 | yield return null;
106 | }
107 | }
108 |
109 | return Prepare(Coroutine(WhileCoro));
110 | }
111 |
112 | public IGenerator Value(T val) {
113 | return Prepare(new Generator { Value = val });
114 | }
115 |
116 | public IGenerator Expression(Func action) {
117 | return Prepare(new Subroutine { Sub = s => action() });
118 | }
119 |
120 | public ISequence Sequence(params IGenerator[] gens) {
121 | return Sequence(gens.ToList());
122 | }
123 |
124 | public ISequence Sequence(IEnumerable gens) {
125 | return Prepare(new Sequence(gens));
126 | }
127 |
128 | public ITimer OneShotTimer(TimeSpan interval, Action onElapsed) {
129 | var timer = OneShotTimer(interval);
130 | timer.Elapsed += self => onElapsed(self);
131 | return timer;
132 | }
133 |
134 | public ITimer OneShotTimer(TimeSpan interval) {
135 | return Prepare(new Timer(Kernel, interval));
136 | }
137 |
138 | public IPeriodic PeriodicTimer(TimeSpan interval) {
139 | return Prepare(new Periodic(Kernel, interval));
140 | }
141 |
142 | public IGenerator Break() {
143 | return Prepare(new Break());
144 | }
145 |
146 | public IGenerator SetDebugLevel(EDebugLevel level) {
147 | return Do(() => { Kernel.DebugLevel = level; });
148 | }
149 |
150 | public IGenerator Log(string fmt, params object[] args) {
151 | return Do(() => { Kernel.Log.Info(fmt, args); });
152 | }
153 |
154 | public IGenerator Warn(string fmt, params object[] args) {
155 | return Do(() => { Kernel.Log.Warn(fmt, args); });
156 | }
157 |
158 | public IGenerator Error(string fmt, params object[] args) {
159 | return Do(() => { Kernel.Log.Error(fmt, args); });
160 | }
161 |
162 | public IBarrier Barrier(params ITransient[] args) {
163 | return Barrier(args.ToList());
164 | }
165 |
166 | public IBarrier Barrier(IEnumerable args) {
167 | var barrier = Barrier();
168 | foreach (var tr in args) {
169 | if (tr == null) {
170 | continue;
171 | }
172 |
173 | barrier.Add(tr);
174 | }
175 |
176 | return Prepare(barrier);
177 | }
178 |
179 | public ITimedBarrier TimedBarrier(TimeSpan span, params ITransient[] args) {
180 | return TimedBarrier(span, args.ToList());
181 | }
182 |
183 | public ITimedBarrier TimedBarrier(TimeSpan span, IEnumerable args) {
184 | return Prepare(new TimedBarrier(Kernel, span, args));
185 | }
186 |
187 | public ITrigger Trigger(params ITransient[] args) {
188 | var trigger = new Trigger();
189 | trigger.Add(args);
190 | return Prepare(trigger);
191 | }
192 |
193 | public ITimedTrigger TimedTrigger(TimeSpan span, params ITransient[] args) {
194 | var timedTrigger = new TimedTrigger(Kernel, span);
195 | timedTrigger.Add(args);
196 | return Prepare(timedTrigger);
197 | }
198 |
199 | public IGenerator Nop() {
200 | return While(() => false);
201 | }
202 |
203 | public IFuture Future() {
204 | return Prepare(new Future());
205 | }
206 |
207 | public IFuture Future(T val) {
208 | var future = Prepare(new Future());
209 | future.Value = val;
210 | return future;
211 | }
212 |
213 | public ITransient Wait(TimeSpan duration) {
214 | return Do(() => Kernel.Wait(duration));
215 | }
216 |
217 | public ITransient WaitFor(ITransient trans, TimeSpan timeOut) {
218 | return Prepare(Trigger(trans, OneShotTimer(timeOut)));
219 | }
220 |
221 | public ITimedFuture TimedFuture(TimeSpan interval) {
222 | return Prepare(new TimedFuture(Kernel, interval));
223 | }
224 |
225 | public ITimedFuture TimedFuture(TimeSpan timeOut, T val) {
226 | var future = TimedFuture(timeOut);
227 | future.Value = val;
228 | return future;
229 | }
230 |
231 | public ISubroutine Subroutine
(Func fun) {
232 | var sub = new Subroutine();
233 | sub.Sub = tr => fun(sub);
234 | return Prepare(sub);
235 | }
236 |
237 | public ISubroutine
Subroutine
(Func fun, T0 t0) {
238 | var sub = new Subroutine();
239 | sub.Sub = tr => fun(sub, t0);
240 | return Prepare(sub);
241 | }
242 |
243 | public ISubroutine
Subroutine
(Func fun, T0 t0, T1 t1) {
244 | var sub = new Subroutine();
245 | sub.Sub = tr => fun(sub, t0, t1);
246 | return Prepare(sub);
247 | }
248 |
249 | public ISubroutine
Subroutine
(Func fun, T0 t0, T1 t1, T2 t2) {
250 | var sub = new Subroutine();
251 | sub.Sub = tr => fun(sub, t0, t1, t2);
252 | return Prepare(sub);
253 | }
254 |
255 | public ICoroutine
Coroutine
(Func> fun) {
256 | var coro = new Coroutine();
257 | coro.Start = () => fun(coro);
258 | return Prepare(coro);
259 | }
260 |
261 | public ICoroutine
Coroutine
(Func> fun, T0 t0) {
262 | var coro = new Coroutine();
263 | coro.Start = () => fun(coro, t0);
264 | return Prepare(coro);
265 | }
266 |
267 | public ICoroutine Coroutine(Func fun) {
268 | var coro = new Coroutine();
269 | coro.Start = () => fun(coro);
270 | return Prepare(coro);
271 | }
272 |
273 | public ICoroutine Coroutine(Func fun, T0 t0) {
274 | var coro = new Coroutine();
275 | coro.Start = () => fun(coro, t0);
276 | return Prepare(coro);
277 | }
278 |
279 | public ICoroutine Coroutine(Func fun, T0 t0, T1 t1) {
280 | var coro = new Coroutine();
281 | coro.Start = () => fun(coro, t0, t1);
282 | return Prepare(coro);
283 | }
284 |
285 | public ICoroutine Coroutine(Func fun, T0 t0, T1 t1, T2 t2) {
286 | var coro = new Coroutine();
287 | coro.Start = () => fun(coro, t0, t1, t2);
288 | return Prepare(coro);
289 | }
290 |
291 | public IChannel Channel
(IGenerator
gen) {
292 | return Prepare(new Channel
(Kernel, gen));
293 | }
294 |
295 | public IChannel
Channel
() {
296 | return Prepare(new Channel
(Kernel));
297 | }
298 |
299 | public T Prepare(T obj)
300 | where T : ITransient {
301 | obj.Kernel = Kernel;
302 | (obj as IGenerator)?.Resume();
303 | return obj;
304 | }
305 |
306 | public ITransient Transient() {
307 | return Prepare(new Transient());
308 | }
309 |
310 | public IFuture Timed
(TimeSpan span, ITransient trans) {
311 | var timed = TimedFuture
(span);
312 | timed.TimedOut += tr => trans.Complete();
313 | return Prepare(timed);
314 | }
315 |
316 | public IBarrier Barrier() {
317 | return Prepare(new Barrier());
318 | }
319 |
320 | public IGenerator Sequence(params ITransient[] gens) {
321 | var seq = new Sequence();
322 | seq.Add(gens);
323 | return Prepare(seq);
324 | }
325 |
326 | public ITransient ActionSequence(params Action[] actions) {
327 | var seq = Node();
328 | IGenerator prev = null;
329 |
330 | foreach (var act in actions) {
331 | var tr = Do(act);
332 | if (prev != null) {
333 | tr.ResumeAfter(prev);
334 | }
335 |
336 | seq.Add(tr);
337 | prev = tr;
338 | }
339 |
340 | return Prepare(seq);
341 | }
342 |
343 | public ICoroutine
TypedCoroutine
(Func> fun, T0 t0, T1 t1) {
344 | var coro = new Coroutine();
345 | coro.Start = () => fun(coro, t0, t1);
346 | return Prepare(coro);
347 | }
348 |
349 | public ICoroutine
TypedCoroutine
(Func> fun, T0 t0,
350 | T1 t1, T2 t2) {
351 | var coro = new Coroutine();
352 | coro.Start = () => fun(coro, t0, t1, t2);
353 | return Prepare(coro);
354 | }
355 | }
356 | }
--------------------------------------------------------------------------------
/Impl/Future.cs:
--------------------------------------------------------------------------------
1 | // (C) 2012 Christian Schladetsch. See https://github.com/cschladetsch/Flow.
2 |
3 | using System;
4 |
5 | namespace Flow.Impl {
6 | internal class Future
7 | : Transient
8 | , IFuture {
9 | private FutureHandler _arrived;
10 |
11 | private T _value;
12 |
13 | public event FutureHandler Arrived {
14 | add {
15 | _arrived += value;
16 | if (Available) {
17 | value(this);
18 | }
19 | }
20 | remove => _arrived -= value;
21 | }
22 |
23 | public bool Available { get; private set; }
24 |
25 | public T Value {
26 | get {
27 | if (!Available) {
28 | throw new FutureNotSetException();
29 | }
30 |
31 | return _value;
32 | }
33 | set {
34 | if (Available) {
35 | throw new FutureAlreadySetException(Name);
36 | }
37 |
38 | _value = value;
39 | Available = true;
40 |
41 | _arrived?.Invoke(this);
42 |
43 | Complete();
44 | }
45 | }
46 |
47 | public IFuture Then(Action> action) {
48 | return Then(() => action(this)) as IFuture;
49 | }
50 | }
51 | }
--------------------------------------------------------------------------------
/Impl/Generator.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Flow.Impl {
4 | public class Generator
5 | : Transient
6 | , IGenerator {
7 | public Generator() {
8 | Completed += tr => Suspend();
9 | }
10 |
11 | public event GeneratorHandler Suspended;
12 | public event GeneratorHandler Resumed;
13 | public event GeneratorHandler Stepped;
14 |
15 | public virtual object Value { get; protected set; }
16 |
17 | public bool Running { get; protected set; }
18 |
19 | public int StepNumber { get; protected set; }
20 |
21 | public new IGenerator AddTo(IGroup group) {
22 | group?.Add(this);
23 | return this;
24 | }
25 |
26 | public new IGenerator Named(string name) {
27 | Name = name;
28 | return this;
29 | }
30 |
31 | public virtual void Step() {
32 | if (!Active) {
33 | return;
34 | }
35 |
36 | ++StepNumber;
37 | Stepped?.Invoke(this);
38 | }
39 |
40 | public virtual void Pre() {
41 | }
42 |
43 | public virtual void Post() {
44 | }
45 |
46 | public void Suspend() {
47 | if (!Running) {
48 | return;
49 | }
50 |
51 | Running = false;
52 | Suspended?.Invoke(this);
53 | }
54 |
55 | public void Resume() {
56 | if (Running || !Active) {
57 | return;
58 | }
59 |
60 | Running = true;
61 | Resumed?.Invoke(this);
62 | }
63 |
64 | public IGenerator SuspendAfter(ITransient other) {
65 | if (IsNullOrInactive(other)) {
66 | Suspend();
67 | return this;
68 | }
69 |
70 | Resume();
71 |
72 | void SuspendThis(ITransient tr) {
73 | other.Completed -= SuspendThis;
74 | Suspend();
75 | }
76 |
77 | other.Completed += SuspendThis;
78 |
79 | return this;
80 | }
81 |
82 | public IGenerator ResumeAfter(Func pred, string name) {
83 | ITransient transient = Factory.While(() => !pred()).AddTo(Kernel.Root).Named(name);
84 | return ResumeAfter(transient);
85 | }
86 |
87 | public IGenerator ResumeAfter(Func pred) {
88 | return ResumeAfter(pred, pred.ToString());
89 | }
90 |
91 | public IGenerator ResumeAfter(ITransient other) {
92 | //Verbosity = 100;
93 | if (IsNullOrInactive(other)) {
94 | Verbose(10, $"Gen: {other.Name} already complete, resuming {Name}.");
95 | Resume();
96 | return this;
97 | }
98 |
99 | Verbose(10, $"Gen: Suspending {Name} until after {other.Name}.");
100 | Suspend();
101 |
102 | // thanks to https://github.com/innostory for reporting an issue
103 | // where a dangling reference to 'other' resulted in memory leaks.
104 | void OnCompleted(ITransient tr) {
105 | other.Completed -= OnCompleted;
106 | Verbose(10, $"Gen: {other.Name} completed, resuming {Name}.");
107 | Resume();
108 | }
109 |
110 | other.Completed += OnCompleted;
111 |
112 | return this;
113 | }
114 |
115 | public IGenerator ResumeAfter(TimeSpan span) {
116 | if (!Active) {
117 | return this;
118 | }
119 |
120 | var timer = Factory.OneShotTimer(span);
121 | timer.Name = $"TimeSpan ({span})";
122 | Kernel.Root.Add(timer);
123 | return ResumeAfter(timer);
124 | }
125 |
126 | public IGenerator SuspendAfter(TimeSpan span) {
127 | if (!Active) {
128 | return this;
129 | }
130 |
131 | var timer = Factory.OneShotTimer(span);
132 | Kernel.Root.Add(timer);
133 | return ResumeAfter(timer);
134 | }
135 | }
136 |
137 | public delegate void WhyTypedGeneratorCompleted(IGenerator self);
138 |
139 | public class Generator
140 | : Generator
141 | , IGenerator {
142 | public new TResult Value {
143 | get => (TResult)base.Value;
144 | set => base.Value = value;
145 | }
146 |
147 | protected static void CannotStart() {
148 | throw new Exception("Can't start typed gen");
149 | }
150 | }
151 | }
--------------------------------------------------------------------------------
/Impl/Group.cs:
--------------------------------------------------------------------------------
1 | // (C) 2012 Christian Schladetsch. See https://github.com/cschladetsch/Flow.
2 |
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 |
7 | namespace Flow.Impl {
8 | ///
9 | ///
10 | /// A flow Group contains a collection of other Transients, and fires events when the contents
11 | /// of the group changes.
12 | /// Suspending a Group suspends all contained Generators, and Resuming a Group
13 | /// Resumes all contained Generators.
14 | /// Stepping a group does nothing.
15 | ///
16 | internal class Group
17 | : Generator
18 | , IGroup {
19 | protected readonly List _Additions = new List();
20 | protected readonly List _Contents = new List();
21 | protected readonly List _Deletions = new List();
22 |
23 | internal Group() {
24 | Resumed += tr => ForEachGenerator(g => g.Resume());
25 | Suspended += tr => ForEachGenerator(g => g.Suspend());
26 | Completed += tr => Clear();
27 | }
28 |
29 | public event GroupHandler OnAdded;
30 | public event GroupHandler OnRemoved;
31 |
32 | public IList Contents => _Contents;
33 | public IEnumerable Generators => Contents.OfType();
34 | public bool Empty => !_Contents.Any();
35 |
36 | public override void Pre() {
37 | base.Pre();
38 |
39 | PerformPending();
40 |
41 | // Do we really need to copy? Answer: yes (?) because the
42 | // Pre() may change contents of this.
43 | foreach (var gen in Generators.ToArray())
44 | gen.Pre();
45 | }
46 |
47 | public override void Post() {
48 | base.Post();
49 |
50 | foreach (var gen in Generators.ToArray())
51 | gen.Post();
52 | }
53 |
54 | public void Add(IEnumerable others) {
55 | foreach (var other in others) {
56 | //Info($"Adding {other} to {Name}.");
57 | if (IsNullOrInactive(other)) {
58 | Warn($"Attempt to add null or inactive transient to Group {this}");
59 | continue;
60 | }
61 |
62 | DeferAdd(other);
63 | }
64 | }
65 |
66 | public void Add(params ITransient[] others) {
67 | Add(others.ToList());
68 | }
69 |
70 | public void Remove(ITransient other) {
71 | if (other == null) {
72 | return;
73 | }
74 |
75 | _Additions.RemoveRef(other);
76 | _Deletions.Add(other);
77 | }
78 |
79 | protected void DeferAdd(ITransient other) {
80 | if (other == null) {
81 | return;
82 | }
83 |
84 | _Deletions.RemoveRef(other);
85 | _Additions.Add(other);
86 | }
87 |
88 | public void Clear() {
89 | _Additions.Clear();
90 |
91 | foreach (var tr in Contents)
92 | _Deletions.Add(tr);
93 |
94 | PerformRemoves();
95 | }
96 |
97 | private void ForEachGenerator(Action act) {
98 | foreach (var gen in Generators)
99 | act(gen);
100 | }
101 |
102 | protected void PerformPending() {
103 | PerformAdds();
104 | PerformRemoves();
105 | }
106 |
107 | private void PerformRemoves() {
108 | if (_Deletions.Count == 0) {
109 | return;
110 | }
111 |
112 | foreach (var tr in _Deletions.ToList()) {
113 | _Contents.RemoveRef(tr);
114 | if (tr == null) {
115 | continue;
116 | }
117 |
118 | tr.Completed -= Remove;
119 |
120 | OnRemoved?.Invoke(this, tr);
121 | }
122 |
123 | _Deletions.Clear();
124 | }
125 |
126 | private void PerformAdds() {
127 | foreach (var tr in _Additions.ToList()) {
128 | _Contents.Add(tr);
129 | tr.Completed += Remove;
130 | OnAdded?.Invoke(this, tr);
131 | }
132 |
133 | _Additions.Clear();
134 | }
135 | }
136 | }
--------------------------------------------------------------------------------
/Impl/Kernel.cs:
--------------------------------------------------------------------------------
1 | // (C) 2012 Christian Schladetsch. See https://github.com/cschladetsch/Flow.
2 |
3 | using System;
4 |
5 | namespace Flow.Impl {
6 | public class Kernel
7 | : Generator
8 | , IKernel {
9 | private readonly TimeFrame _time = new TimeFrame();
10 | private DateTime _resumeTime;
11 |
12 | private bool _waiting;
13 |
14 | internal Kernel() {
15 | Log = this;
16 | Log.LogSubject = this;
17 | Log.LogPrefix = "Flow";
18 | Verbosity = 5;
19 | DebugLevel = EDebugLevel.Medium;
20 | ShowStack = false;
21 | ShowSource = true;
22 | Kernel = this;
23 |
24 | _time.Now = DateTime.Now;
25 | _time.Last = _time.Now;
26 | _time.Delta = TimeSpan.FromSeconds(0);
27 | }
28 |
29 | public EDebugLevel DebugLevel { get; set; }
30 | public ILogger Log { get; set; }
31 | public INode Root { get; set; }
32 | public new IFactory Factory { get; internal set; }
33 | public bool Break { get; private set; }
34 | public ITimeFrame Time => _time;
35 |
36 | public void BreakFlow() {
37 | Break = true;
38 | }
39 |
40 | public void Wait(TimeSpan span) {
41 | if (_waiting) {
42 | _resumeTime += span;
43 | return;
44 | }
45 |
46 | _resumeTime = _time.Now + span;
47 | _waiting = true;
48 | }
49 |
50 | public void Update(float dt) {
51 | UpdateTime(dt);
52 |
53 | Process();
54 | }
55 |
56 | public override void Step() {
57 | StepTime();
58 |
59 | if (_waiting) {
60 | if (_time.Now > _resumeTime) {
61 | _resumeTime = DateTime.MinValue;
62 | _waiting = false;
63 | }
64 | else {
65 | return;
66 | }
67 | }
68 |
69 | Process();
70 | }
71 |
72 | private void UpdateTime(float dt) {
73 | var delta = TimeSpan.FromSeconds(dt);
74 | _time.Last = _time.Now;
75 | _time.Delta = delta;
76 | _time.Now = _time.Now + delta;
77 | }
78 |
79 | private void Process() {
80 | if (Break) {
81 | return;
82 | }
83 |
84 | void Step(IGenerator node) {
85 | if (!IsNullOrInactive(node)) {
86 | node.Step();
87 | }
88 | }
89 |
90 | if (Root.Contents.Count > 0) {
91 | Verbose(10, $"Stepping kernel {Root.Contents.Count}");
92 | }
93 |
94 | Step(Root);
95 |
96 | base.Step();
97 | }
98 |
99 | public void StepTime() {
100 | var now = DateTime.Now;
101 |
102 | _time.Last = _time.Now;
103 | _time.Delta = now - _time.Last;
104 | _time.Now = now;
105 | }
106 | }
107 | }
--------------------------------------------------------------------------------
/Impl/Node.cs:
--------------------------------------------------------------------------------
1 | // (C) 2012 Christian Schladetsch. See https://github.com/cschladetsch/Flow.
2 |
3 | using System;
4 | using System.Linq;
5 |
6 | namespace Flow.Impl {
7 | internal class Node
8 | : Group
9 | , INode {
10 | protected bool _StepOne;
11 | private bool _stepping;
12 |
13 | public void Add(params IGenerator[] gens) {
14 | foreach (var gen in gens)
15 | DeferAdd(gen);
16 | }
17 |
18 | public new INode AddTo(IGroup group) {
19 | return this.AddToGroup(group);
20 | }
21 |
22 | public new INode Named(string name) {
23 | return this.SetName(name);
24 | }
25 |
26 | public override void Step() {
27 | Pre();
28 |
29 | try {
30 | if (Kernel.DebugLevel > EDebugLevel.Medium) {
31 | Kernel.Log.Info($"Stepping Node {Name}");
32 | }
33 |
34 | if (_stepping) {
35 | Kernel.Log.Error(
36 | $"Node {Name} is re-entrant. Nodes cannot directly or indirectly invoke their Step methods when stepping.");
37 | throw new ReEntranceException();
38 | }
39 |
40 | _stepping = true;
41 |
42 | base.Step();
43 |
44 | // TODO: do we really need to copy the contents? Maybe use some double-buffering if required to avoid copying.
45 | // that said, it's only creating a new list of references...
46 | // the underlying issue is that the contents of the node may be altered while stepping children of the node.
47 | foreach (var tr in Contents.ToList()) {
48 | if (tr is IGenerator gen) {
49 | if (Kernel.Break) {
50 | goto end;
51 | }
52 |
53 | try {
54 | if (!gen.Active) {
55 | Remove(gen);
56 | continue;
57 | }
58 |
59 | if (gen.Running) {
60 | gen.Step();
61 | }
62 | } catch (Exception e) {
63 | gen.Complete();
64 | Error($"Exception: {e.Message} when stepping {gen.Name}. Completing this generator.");
65 | Error($" StackTrace: {e.StackTrace}");
66 | }
67 | }
68 |
69 | if (_StepOne) {
70 | break;
71 | }
72 | }
73 | }
74 | finally {
75 | _stepping = false;
76 | }
77 |
78 | end:
79 | Post();
80 | }
81 | }
82 | }
--------------------------------------------------------------------------------
/Impl/Periodic.cs:
--------------------------------------------------------------------------------
1 | // (C) 2012 Christian Schladetsch. See https://github.com/cschladetsch/Flow.
2 |
3 | using System;
4 |
5 | namespace Flow.Impl {
6 | internal class Periodic
7 | : Subroutine
8 | , IPeriodic {
9 | private DateTime _expires;
10 |
11 | internal Periodic(IKernel kernel, TimeSpan interval) {
12 | Verbosity = 100;
13 |
14 | Interval = interval;
15 | TimeStarted = kernel.Time.Now;
16 | kernel.Root.Add(this);
17 | _expires = TimeStarted + Interval;
18 | Sub = StepTimer;
19 | }
20 |
21 | public event TransientHandler Elapsed;
22 |
23 | public TimeSpan TimeRemaining => Kernel.Time.Now - TimeStarted;
24 | public DateTime TimeStarted { get; }
25 | public TimeSpan Interval { get; }
26 |
27 | private bool StepTimer(IGenerator self) {
28 | if (Kernel.Time.Now < _expires) {
29 | return true;
30 | }
31 |
32 | Elapsed?.Invoke(this);
33 |
34 | _expires = Kernel.Time.Now + Interval;
35 |
36 | return true;
37 | }
38 | }
39 | }
--------------------------------------------------------------------------------
/Impl/Sequence.cs:
--------------------------------------------------------------------------------
1 | // (C) 2012 Christian Schladetsch. See https://github.com/cschladetsch/Flow.
2 |
3 | using System.Collections.Generic;
4 | using System.Linq;
5 |
6 | namespace Flow.Impl {
7 | ///
8 | internal class Sequence
9 | : Node
10 | , ISequence {
11 | internal Sequence() {
12 | _StepOne = true;
13 | }
14 |
15 | internal Sequence(IEnumerable gens)
16 | : this() {
17 | Add(gens);
18 | }
19 |
20 | public override void Post() {
21 | if (!Contents.Any()) {
22 | Complete();
23 | }
24 | }
25 | }
26 | }
--------------------------------------------------------------------------------
/Impl/SpinWait.cs:
--------------------------------------------------------------------------------
1 | // CJS. Nabbed from http://code.google.com/p/mono-soc-2008/source/browse/trunk/parallelfx/System.Threading/System.Threading/SpinWait.cs?r=554
2 |
3 | using System;
4 | using System.Threading;
5 |
6 | namespace Flow.Impl {
7 | public struct SpinWait {
8 | public bool NextSpinWillYield => isSingleCpu || ntime % step == 0;
9 | public int Count => ntime;
10 |
11 | public void SpinOnce() {
12 | // On a single-CPU system, spinning does no good
13 | if (isSingleCpu) {
14 | Yield();
15 | }
16 | else {
17 | if (Interlocked.Increment(ref ntime) % step == 0) {
18 | Yield();
19 | }
20 | else {
21 | // Multi-CPU system might be hyper-threaded, let other thread run
22 | Thread.SpinWait(2 * (ntime + 1));
23 | }
24 | }
25 | }
26 |
27 | public void SpinUntil(Func predicate) {
28 | while (!predicate()) SpinOnce();
29 | }
30 |
31 | private static void Yield() {
32 | // Replace sched_yield by Thread.Sleep(0) which does almost the same thing
33 | // (going back in kernel mode and yielding) but avoid the branching and unmanaged bridge
34 | Thread.Sleep(0);
35 | }
36 |
37 | public void Reset() {
38 | ntime = 0;
39 | }
40 |
41 | // The number of step until SpinOnce yield on multicore machine
42 | private const int step = 20;
43 | private static readonly bool isSingleCpu = Environment.ProcessorCount == 1;
44 | private int ntime;
45 | }
46 | }
--------------------------------------------------------------------------------
/Impl/Subroutine.cs:
--------------------------------------------------------------------------------
1 | // (C) 2012 Christian Schladetsch. See https://github.com/cschladetsch/Flow.
2 |
3 | using System;
4 |
5 | namespace Flow.Impl {
6 | internal class Subroutine
7 | : Generator
8 | , ISubroutine {
9 | internal Action Sub;
10 |
11 | public override void Step() {
12 | if (!(Active && Running)) {
13 | return;
14 | }
15 |
16 | if (Sub == null) {
17 | Complete();
18 | return;
19 | }
20 |
21 | Sub(this);
22 |
23 | base.Step();
24 | }
25 | }
26 |
27 | internal class Subroutine
28 | : Generator
29 | , ISubroutine
{
30 | private readonly bool _once;
31 | internal Func Sub;
32 |
33 | internal Subroutine(bool once = false) {
34 | _once = once;
35 | }
36 |
37 | public override void Step() {
38 | if (!Active || !Running) {
39 | return;
40 | }
41 |
42 | if (Sub == null) {
43 | Complete();
44 | return;
45 | }
46 |
47 | Value = Sub(this);
48 |
49 | base.Step();
50 |
51 | if (_once) {
52 | Complete();
53 | }
54 | }
55 | }
56 | }
--------------------------------------------------------------------------------
/Impl/TimeFrame.cs:
--------------------------------------------------------------------------------
1 | // (C) 2012 Christian Schladetsch. See https://github.com/cschladetsch/Flow.
2 |
3 | using System;
4 |
5 | namespace Flow.Impl {
6 | ///
7 | ///
8 | /// TODO: delta-capping, pausing, introduction of zulu/sim time differences
9 | ///
10 | internal class TimeFrame
11 | : ITimeFrame {
12 | public DateTime Last { get; internal set; }
13 | public DateTime Now { get; internal set; }
14 | public TimeSpan Delta { get; internal set; }
15 | }
16 | }
--------------------------------------------------------------------------------
/Impl/TimedBarrier.cs:
--------------------------------------------------------------------------------
1 | // (C) 2012 Christian Schladetsch. See https://github.com/cschladetsch/Flow.
2 |
3 | using System;
4 | using System.Collections.Generic;
5 |
6 | namespace Flow.Impl {
7 | ///
8 | /// A barrier that completes after a certain time.
9 | /// Barriers normally only complete when all of it contents are complete.
10 | ///
11 | internal class TimedBarrier
12 | : Barrier
13 | , ITimedBarrier {
14 | public TimedBarrier(IKernel kernel, TimeSpan span, IEnumerable contents) {
15 | Timer = kernel.Factory.OneShotTimer(span);
16 | if (TimeoutsEnabled) {
17 | Timer.Elapsed += Elapsed;
18 | }
19 |
20 | foreach (var tr in contents)
21 | Add(tr);
22 | }
23 |
24 | public event TimedOutHandler TimedOut;
25 | public ITimer Timer { get; }
26 | public bool HasTimedOut { get; set; }
27 |
28 | public new ITimedBarrier AddTo(IGroup group) {
29 | return this.AddToGroup(group);
30 | }
31 |
32 | public new ITimedBarrier Named(string name) {
33 | return this.SetName(name);
34 | }
35 |
36 | private void Elapsed(ITransient tr) {
37 | Timer.Elapsed -= Elapsed;
38 | HasTimedOut = true;
39 | TimedOut?.Invoke(this);
40 | Complete();
41 | }
42 | }
43 | }
--------------------------------------------------------------------------------
/Impl/TimedFuture.cs:
--------------------------------------------------------------------------------
1 | // (C) 2012 Christian Schladetsch. See https://github.com/cschladetsch/Flow.
2 |
3 | using System;
4 |
5 | namespace Flow.Impl {
6 | internal class TimedFuture
7 | : Future
8 | , ITimedFuture {
9 | internal TimedFuture(IKernel k, TimeSpan span) {
10 | Timer = k.Factory.OneShotTimer(span);
11 | if (TimeoutsEnabled) {
12 | Timer.Elapsed += HandleElapsed;
13 | }
14 |
15 | //Completed += tr => Timer.Complete();
16 | }
17 |
18 | public ITimedFuture Then(Action> action) {
19 | return Then(() => action(this)) as ITimedFuture;
20 | }
21 |
22 | public event TimedOutHandler TimedOut {
23 | add {
24 | _timedOut += value;
25 | if (HasTimedOut) {
26 | value(this);
27 | }
28 | }
29 | remove => _timedOut -= value;
30 | }
31 |
32 | public bool HasTimedOut { get; protected set; }
33 |
34 | public ITimer Timer { get; internal set; }
35 |
36 | private event TimedOutHandler _timedOut;
37 |
38 | private void HandleElapsed(ITransient sender) {
39 | if (!Active) {
40 | return;
41 | }
42 |
43 | _timedOut?.Invoke(this);
44 |
45 | HasTimedOut = true;
46 |
47 | Complete();
48 | }
49 | }
50 | }
--------------------------------------------------------------------------------
/Impl/TimedTrigger.cs:
--------------------------------------------------------------------------------
1 | // (C) 2012 Christian Schladetsch. See https://github.com/cschladetsch/Flow.
2 |
3 | using System;
4 |
5 | namespace Flow.Impl {
6 | internal class TimedTrigger
7 | : Trigger
8 | , ITimedTrigger {
9 | internal TimedTrigger(IKernel k, TimeSpan span) {
10 | Timer = k.Factory.OneShotTimer(span);
11 | k.Root.Add(Timer);
12 |
13 | if (TimeoutsEnabled) {
14 | Timer.Elapsed += HandleElapsed;
15 | }
16 | }
17 |
18 | public event TimedOutHandler TimedOut;
19 | public ITimer Timer { get; internal set; }
20 | public bool HasTimedOut { get; protected set; }
21 |
22 | private void HandleElapsed(ITransient sender) {
23 | if (!Active) {
24 | return;
25 | }
26 |
27 | TimedOut?.Invoke(this);
28 | Timer.Elapsed -= HandleElapsed;
29 | HasTimedOut = true;
30 |
31 | Complete();
32 | }
33 | }
34 | }
--------------------------------------------------------------------------------
/Impl/Timer.cs:
--------------------------------------------------------------------------------
1 | // (C) 2012 christian.schladetsch@gmail.com. See https://github.com/cschladetsch/Flow.
2 |
3 | using System;
4 |
5 | namespace Flow.Impl {
6 | internal class Timer
7 | : Periodic
8 | , ITimer {
9 | ///
10 | /// Timer based on game time, not real time.
11 | ///
12 | internal Timer(IKernel kernel, TimeSpan span)
13 | : base(kernel, span) {
14 | TimeEnds = kernel.Time.Now + span;
15 | Elapsed += TimedOutHandler;
16 | }
17 |
18 | public DateTime TimeEnds { get; }
19 |
20 | private void TimedOutHandler(ITransient sender) {
21 | Complete();
22 | }
23 | }
24 | }
--------------------------------------------------------------------------------
/Impl/Transient.cs:
--------------------------------------------------------------------------------
1 | // (C) 2012 christian.schladetsch@gmail.com. See https://github.com/cschladetsch/Flow.
2 |
3 | using System;
4 |
5 | namespace Flow.Impl {
6 | ///
7 | public class Transient
8 | : Logger
9 | , ITransient {
10 | protected static readonly bool TimeoutsEnabled = true;
11 |
12 | private TransientHandler _completed;
13 | public IFactory Factory => Kernel.Factory;
14 | public IFactory New => Factory;
15 |
16 | public event TransientHandler Completed {
17 | add {
18 | _completed += value;
19 | if (!Active) {
20 | value(this);
21 | }
22 | }
23 | remove => _completed -= value;
24 | }
25 |
26 | public bool Active { get; protected set; } = true;
27 | public IKernel Kernel { get; /*internal*/ set; }
28 |
29 | public virtual string Name { get; set; }
30 |
31 | public ITransient Named(string name) {
32 | Name = name;
33 | return this;
34 | }
35 |
36 | public void Complete() {
37 | if (!Active) {
38 | return;
39 | }
40 |
41 | Active = false;
42 |
43 | _completed?.Invoke(this);
44 | }
45 |
46 | public ITransient AddTo(IGroup group) {
47 | group.Add(this);
48 | return this;
49 | }
50 |
51 | public event TransientHandlerReason OnHowCompleted;
52 |
53 | public override string ToString() {
54 | return Print.Object(this);
55 | }
56 |
57 | public void CompleteAfter(ITransient other) {
58 | if (!Active) {
59 | return;
60 | }
61 |
62 | if (other == null) {
63 | return;
64 | }
65 |
66 | if (!other.Active) {
67 | Complete();
68 | return;
69 | }
70 |
71 | other.Completed += tr => CompletedBecause(other);
72 | }
73 |
74 | public ITransient Then(Action action) {
75 | return Then(Factory.Do(action).AddTo(Kernel.Root));
76 | }
77 |
78 | public ITransient Then(Action action) {
79 | return Then(Factory.Do(() => action(this)).AddTo(Kernel.Root));
80 | }
81 |
82 | public ITransient Then(IGenerator next) {
83 | if (next == null) {
84 | Warn("Cannot do nothing next.");
85 | return this;
86 | }
87 |
88 | if (!Active) {
89 | next.Resume();
90 | return this;
91 | }
92 |
93 | next.Suspend();
94 |
95 | void OnCompleted(ITransient self) {
96 | Completed -= OnCompleted;
97 | next.Resume();
98 | }
99 |
100 | Completed += OnCompleted;
101 |
102 | return this;
103 | }
104 |
105 | public void CompleteAfter(TimeSpan span) {
106 | CompleteAfter(Factory.OneShotTimer(span));
107 | }
108 |
109 | public static bool IsNullOrInactive(ITransient other) {
110 | return other == null || !other.Active;
111 | }
112 |
113 | private void CompletedBecause(ITransient other) {
114 | if (!Active) {
115 | return;
116 | }
117 |
118 | OnHowCompleted?.Invoke(this, other);
119 |
120 | Complete();
121 | }
122 | }
123 | }
--------------------------------------------------------------------------------
/Impl/Trigger.cs:
--------------------------------------------------------------------------------
1 | // (C) 2012 christian.schladetsch@gmail.com. See https://github.com/cschladetsch/Flow.
2 |
3 | using System;
4 |
5 | namespace Flow.Impl {
6 | internal class Trigger
7 | : Group
8 | , ITrigger {
9 | internal Trigger() {
10 | OnRemoved += Trip;
11 | }
12 |
13 | public event Action OnTripped;
14 |
15 | public ITransient Reason { get; private set; }
16 |
17 | private void Trip(IGroup self, ITransient other) {
18 | Reason = other;
19 |
20 | OnTripped?.Invoke(this, other);
21 |
22 | Complete();
23 | }
24 | }
25 | }
--------------------------------------------------------------------------------
/Impl/VolatileBool.cs:
--------------------------------------------------------------------------------
1 | // CJS: Nabbed from decompiled 4.5 .Net
2 |
3 | using System.Runtime.InteropServices;
4 |
5 | namespace Flow.Impl {
6 | [StructLayout(LayoutKind.Sequential)]
7 | internal struct VolatileBool {
8 | public volatile bool m_value;
9 |
10 | public VolatileBool(bool value) {
11 | m_value = value;
12 | }
13 | }
14 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Christian Schladetsch
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Logger/ConsoleLogger.cs:
--------------------------------------------------------------------------------
1 | #if asdasdasd //DOTNET
2 | using System;
3 |
4 | namespace Flow.Logger
5 | {
6 | public class ConsoleLogger : Impl.Logger
7 | {
8 | public ConsoleLogger() : base(ELogEntryType.Everything)
9 | {
10 | }
11 |
12 | public ConsoleLogger(ELogEntryType entryType, string name = "") : base(entryType, name)
13 | {
14 | }
15 |
16 | protected override void AddEntry(DateTime dateTime, ELogEntryType entryType, string message)
17 | {
18 | Console.WriteLine("{0}: {1}: {2}", MakeTimeString(dateTime), entryType, message);
19 | }
20 | }
21 | }
22 | #endif
--------------------------------------------------------------------------------
/Logger/Logger.cs:
--------------------------------------------------------------------------------
1 | // TODO: This whole thing is a complete mess.
2 | // This is a classic case of trying to do too much with too little.
3 | // The main problem is that the standalone unit-tests do not reference
4 | // the Unity assemblies.
5 | // But, the same code file is used for both Unity and non-Unity debugging.
6 | // This has to be fixed and soon.
7 |
8 | // ... 1 July 2019
9 | // ... 2 Sept 2019
10 | // ... 3 March 2020
11 |
12 | //#define UNITY
13 | //#undef UNITY
14 |
15 | using System;
16 | using System.Collections;
17 | using System.Diagnostics;
18 |
19 | //using NUnit.Framework;
20 |
21 | #if UNITY
22 | using Debug = UnityEngine.Debug;
23 | #endif
24 |
25 | namespace Flow.Impl {
26 | ///
27 | /// Log system used by Models.
28 | ///
29 | public class Logger
30 | : ILogger {
31 | public static string LogFileName;
32 | public static ELogLevel MaxLevel;
33 | #if !UNITY
34 | private static readonly DateTime _startTime = DateTime.Now;
35 | #endif
36 |
37 | #if !UNITY
38 | private readonly string[] _logNames = { "Info", "Warn", "Error", "Verbose" };
39 | #endif
40 |
41 | protected ELogLevel _logLevel;
42 |
43 | public Logger() {
44 | LogSubject = this;
45 | }
46 |
47 | public Logger(string pre, bool src, bool st)
48 | : this(pre) {
49 | ShowSource = src;
50 | ShowStack = st;
51 | }
52 |
53 | public Logger(string pre)
54 | : this() {
55 | LogPrefix = pre;
56 | }
57 |
58 | public string LogPrefix { get; set; }
59 | public object LogSubject { get; set; }
60 | public int Verbosity { get; set; }
61 | public bool ShowSource { get; set; } = true;
62 | public bool ShowStack { get; set; } = true;
63 |
64 | public void Info(string fmt, params object[] args) {
65 | Log(ELogLevel.Info, StringFormat(fmt, args));
66 | }
67 |
68 | public void Warn(string fmt, params object[] args) {
69 | Log(ELogLevel.Warn, StringFormat(fmt, args));
70 | }
71 |
72 | public void Error(string fmt, params object[] args) {
73 | Log(ELogLevel.Error, StringFormat(fmt, args));
74 | }
75 |
76 | public void Verbose(int level, string fmt, params object[] args) {
77 | if (level > Verbosity) {
78 | return;
79 | }
80 |
81 | Log(ELogLevel.Verbose, StringFormat(fmt, args));
82 | }
83 |
84 | public static void Initialise() {
85 | }
86 |
87 | private void OutputLine(string text) {
88 | //Console.WriteLine(text);
89 | Trace.WriteLine(text);
90 | //TestContext.Out.Write(text);
91 | }
92 |
93 | private void Output(string text) {
94 | //Console.Write(text);
95 | Trace.Write(text);
96 | //TestContext.Out.Write(text);
97 | }
98 |
99 | private static string StringFormat(string fmt, object[] args) {
100 | return fmt == null ? "Null" : string.Format(fmt, args);
101 | }
102 |
103 | private void Log(ELogLevel level, string text) {
104 | if (level == ELogLevel.None) {
105 | level = ELogLevel.Error;
106 | }
107 | #if UNITY
108 | Action log = Debug.Log;
109 | #else
110 | Action log = Console.WriteLine;
111 | #endif
112 |
113 | #if !UNITY
114 | Output(MakeEntry(level, text));
115 | var error = level == ELogLevel.Error;
116 | var showFrames = ShowStack || error;
117 |
118 | // HACK!
119 | showFrames = error;
120 |
121 | if (ShowSource || error) {
122 | OutputLine("");
123 | var lead = "\t\t";
124 | var st = new StackTrace(true);
125 | var foundTop = false;
126 | foreach (var fr in st.GetFrames()) {
127 | if (!foundTop) {
128 | var name = fr.GetMethod().Name;
129 | if (((IList)_logNames).Contains(name)) {
130 | foundTop = true;
131 | continue;
132 | }
133 | }
134 |
135 | if (!foundTop) {
136 | continue;
137 | }
138 |
139 | //if (!fr.HasSource())
140 | // break;
141 | if (string.IsNullOrEmpty(fr.GetFileName())) {
142 | break;
143 | }
144 |
145 | OutputLine(
146 | $"{lead}{fr.GetFileName()}({fr.GetFileLineNumber()},{fr.GetFileColumnNumber()}): from: {fr.GetMethod().Name}");
147 | if (!showFrames) {
148 | break;
149 | }
150 |
151 | lead += "\t";
152 | }
153 | }
154 | else {
155 | OutputLine("");
156 | }
157 | #else // TODO: use bitmasks as intended
158 | switch (level)
159 | {
160 | case ELogLevel.Info:
161 | log = Debug.Log;
162 | break;
163 | case ELogLevel.Warn:
164 | log = Debug.LogWarning;
165 | break;
166 | case ELogLevel.Error:
167 | log = Debug.LogError;
168 | break;
169 | case ELogLevel.Verbose:
170 | log = Debug.Log;
171 | break;
172 | }
173 | log(MakeEntry(level, text));
174 | #endif
175 | }
176 |
177 | private string MakeEntry(ELogLevel level, string text) {
178 | #if UNITY
179 | return text;
180 | #else
181 | text = text.Trim();
182 | var named = LogSubject as INamed;
183 | var name = named == null ? "" : named.Name;
184 | var dt = DateTime.Now - _startTime;
185 | var ms = dt.ToString(@"fff");
186 | var time = dt.ToString(@"mm\:ss\:") + ms;
187 | var prefix = string.IsNullOrEmpty(LogPrefix) ? "" : $"{LogPrefix}: ";
188 | var from = string.IsNullOrEmpty(name) ? "" : $" {name}:";
189 | var gen = LogSubject as IGenerator;
190 | var step = gen == null ? "" : $"#{gen.StepNumber}/{gen.Kernel?.StepNumber}: ";
191 | var openTick = "`";
192 | switch (level) {
193 | case ELogLevel.None:
194 | break;
195 | case ELogLevel.Info:
196 | break;
197 | case ELogLevel.Warn:
198 | openTick = "``";
199 | break;
200 | case ELogLevel.Verbose:
201 | openTick = "````";
202 | break;
203 | case ELogLevel.Error:
204 | openTick = "```";
205 | break;
206 | default:
207 | throw new ArgumentOutOfRangeException(nameof(level), level, null);
208 | }
209 |
210 | // this trace includes the source type: it's a bit "verbose"
211 | //return $"{level}: {prefix}{time} {step}{LogSubject.GetType()}{from}\n\t{openTick}{text}`";
212 | return $"{level}: {prefix}{time} {step}{from}\n\t{openTick}{text}`";
213 | #endif
214 | }
215 | }
216 | }
--------------------------------------------------------------------------------
/Logger/PrettyPrinter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using System.Text;
4 |
5 | // There are a lot of accesses to properties that could return null, but won't
6 | // ReSharper disable PossibleNullReferenceException
7 |
8 | namespace Flow {
9 | public static class Print {
10 | public static string Object(ITransient trans) {
11 | return PrettyPrinter.ToString(trans);
12 | }
13 | }
14 |
15 | public class PrettyPrinter {
16 | private readonly int _indenting = 4;
17 |
18 | private readonly StringBuilder _sb = new StringBuilder();
19 |
20 | private PrettyPrinter(ITransient trans) {
21 | _sb.Append("# ");
22 | Print(trans, 0);
23 | }
24 |
25 | public static string ToString(ITransient trans) {
26 | return new PrettyPrinter(trans)._sb.ToString();
27 | }
28 |
29 | private int Print(ITransient trans, int level) {
30 | if (trans == null) {
31 | return level;
32 | }
33 |
34 | Lead(level);
35 | Header(trans);
36 | return !(trans is IGroup group) ? level : Contents(group, level + 1);
37 | }
38 |
39 | private void Lead(int level) {
40 | _sb.Append(' ', level * _indenting);
41 | }
42 |
43 | private static bool ImplementsGenericInterface(Type given, Type iface) {
44 | return given.GetInterfaces().Any(x => x.IsGenericType &&
45 | x.GetGenericTypeDefinition() == iface);
46 | }
47 |
48 | private StringBuilder Header(ITransient trans) {
49 | var name = trans.Name ?? "";
50 | var tyName = trans.GetType().Name;
51 | var ty = trans.GetType();
52 |
53 | // test for generics
54 | if (ty.IsGenericType) {
55 | var arg = ty.GetGenericArguments()[0];
56 | if (ImplementsGenericInterface(ty, typeof(ITimedFuture<>))) {
57 | var avail = (bool)ty.GetProperty("Available")?.GetValue(trans);
58 | object val = "";
59 | if (avail) {
60 | val = ty.GetProperty("Value")?.GetValue(trans);
61 | }
62 |
63 | return _sb.AppendFormat($"TimedFuture<{arg.Name}>: {name} Available={avail}, Value={val}\n");
64 | }
65 |
66 | if (ImplementsGenericInterface(ty, typeof(IFuture<>))) {
67 | var avail = (bool)ty.GetProperty("Available")?.GetValue(trans);
68 | object val = "";
69 | if (avail) {
70 | val = ty.GetProperty("Value")?.GetValue(trans);
71 | }
72 |
73 | return _sb.AppendFormat($"Future<{arg.Name}>: {name} Available={avail}, Value={val}\n");
74 | }
75 |
76 | if (ImplementsGenericInterface(ty, typeof(IChannel<>))) {
77 | return _sb.AppendFormat($"Channel<{arg.Name}>: {name}\n");
78 | }
79 | }
80 |
81 | if (typeof(ITimer).IsAssignableFrom(ty)) {
82 | var timer = (ITimer)trans;
83 | var ends = timer.TimeEnds;
84 | return _sb.AppendFormat(
85 | $"Timer: {name} ends in {(ends - timer.Kernel.Time.Now).TotalSeconds:N2}s, {GeneratorInfo(trans)}\n");
86 | }
87 |
88 | if (typeof(IPeriodic).IsAssignableFrom(ty)) {
89 | var p = (IPeriodic)trans;
90 | return _sb.AppendFormat(
91 | $"Periodic: {name}, started={p.TimeStarted}, interval={p.Interval}, remaining={p.TimeRemaining}: {GeneratorInfo(trans)}\n");
92 | }
93 |
94 | if (typeof(ITrigger).IsAssignableFrom(ty)) {
95 | return _sb.AppendFormat($"Trigger: {name}: {GeneratorInfo(trans)}\n");
96 | }
97 |
98 | if (typeof(IBarrier).IsAssignableFrom(ty)) {
99 | return _sb.AppendFormat($"Barrier: {name}: {GeneratorInfo(trans)}\n");
100 | }
101 |
102 | if (typeof(ISequence).IsAssignableFrom(ty)) {
103 | return _sb.AppendFormat($"Sequence {name}: {GeneratorInfo(trans)}\n");
104 | }
105 |
106 | if (typeof(INode).IsAssignableFrom(ty)) {
107 | return _sb.AppendFormat($"Node {name}: {GeneratorInfo(trans)}\n");
108 | }
109 |
110 | if (typeof(IGroup).IsAssignableFrom(ty)) {
111 | return _sb.AppendFormat($"Group: {name}: {GeneratorInfo(trans)}\n");
112 | }
113 |
114 | if (typeof(ICoroutine).IsAssignableFrom(ty)) {
115 | return _sb.AppendFormat($"Coroutine {name}: {GeneratorInfo(trans)}\n");
116 | }
117 |
118 | if (typeof(ITransient).IsAssignableFrom(ty)) {
119 | return _sb.AppendFormat($"{tyName}={name}:\n");
120 | }
121 |
122 | return _sb.Append("??");
123 | }
124 |
125 | private int Contents(IGroup group, int level) {
126 | foreach (var tr in group.Contents) {
127 | Print(tr, level + 1);
128 | _sb.Append('\n');
129 | }
130 |
131 | return level;
132 | }
133 |
134 | private static string GeneratorInfo(ITransient trans) {
135 | return !(trans is IGenerator gen) ? "" : $"running={gen.Running}, step={gen.StepNumber}";
136 | }
137 | }
138 | }
--------------------------------------------------------------------------------
/Logger/Readme.md:
--------------------------------------------------------------------------------
1 | # Flow.Logging System
2 |
3 | Provides a generalised logging system, with a pretty-printer for **Flow** objects and colored Debug window output.
4 |
5 | For best effect, use the [VSColorOutput](https://marketplace.visualstudio.com/items?itemName=MikeWard-AnnArbor.VSColorOutput) plugin for **VisualStudio and put the *vscoloroutput.json* file in _AppData\Roaming\VSColorOutput_ folder.
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Logger/UnityLogger.cs:
--------------------------------------------------------------------------------
1 | #if ASDASDASD //UNITY3D
2 | using System;
3 | using UnityEngine;
4 |
5 | namespace Flow.Logger
6 | {
7 | public class UnityLogger : Logger
8 | {
9 | public UnityLogger() : base(ELogEntryType.Everything)
10 | {
11 | }
12 |
13 | public UnityLogger(ELogEntryType entryType, string name = "") : base(entryType, name)
14 | {
15 | }
16 |
17 | protected override void AddEntry(DateTime dateTime, ELogEntryType entryType, string message)
18 | {
19 | switch (entryType)
20 | {
21 | case ELogEntryType.None:
22 | break;
23 | case ELogEntryType.Log:
24 | Write(dateTime, message, UnityEngine.Debug.Log);
25 | break;
26 | case ELogEntryType.Warn:
27 | Write(dateTime, message, UnityEngine.Debug.LogWarning);
28 | break;
29 | case ELogEntryType.Error:
30 | // TODO: have to use this if running as a unit test - but there is no way to tell?
31 | //Write(dateTime, message, (str) => UnityEngine.TestTools.LogAssert.Expect(LogType.Error, str));
32 | Write(dateTime, message, UnityEngine.Debug.LogError);
33 | break;
34 | case ELogEntryType.Everything:
35 | break;
36 | }
37 | }
38 |
39 | private void Write(DateTime dateTime, string message, Action log)
40 | {
41 | log($"{dateTime.ToLongTimeString()}: #{UnityEngine.Time.frameCount}: {message}");
42 | }
43 | }
44 | }
45 | #endif
--------------------------------------------------------------------------------
/Logger/vscoloroutput.json:
--------------------------------------------------------------------------------
1 | {
2 | "EnableStopOnBuildError": false,
3 | "ShowElapsedBuildTime": false,
4 | "ShowBuildReport": false,
5 | "ShowDebugWindowOnDebug": false,
6 | "HighlightFindResults": true,
7 | "ShowTimeStamps": false,
8 | "SuppressDonation": false,
9 | "Patterns": [
10 | {
11 | "ClassificationType": 6,
12 | "IgnoreCase": false,
13 | "RegExPattern": "\\> .*"
14 | },
15 | {
16 | "ClassificationType": 5,
17 | "IgnoreCase": false,
18 | "RegExPattern": "~.*~"
19 | },
20 | {
21 | "ClassificationType": 7,
22 | "IgnoreCase": false,
23 | "RegExPattern": "`.*`"
24 | },
25 | {
26 | "ClassificationType": 8,
27 | "IgnoreCase": false,
28 | "RegExPattern": "# .*"
29 | }
30 | ],
31 | "BuildMessageColor": {
32 | "knownColor": 79,
33 | "name": null,
34 | "state": 1,
35 | "value": 0
36 | },
37 | "BuildTextColor": {
38 | "knownColor": 78,
39 | "name": null,
40 | "state": 1,
41 | "value": 0
42 | },
43 | "ErrorTextColor": {
44 | "knownColor": 141,
45 | "name": null,
46 | "state": 1,
47 | "value": 0
48 | },
49 | "WarningTextColor": {
50 | "knownColor": 125,
51 | "name": null,
52 | "state": 1,
53 | "value": 0
54 | },
55 | "InformationTextColor": {
56 | "knownColor": 49,
57 | "name": null,
58 | "state": 1,
59 | "value": 0
60 | },
61 | "CustomTextColor1": {
62 | "knownColor": 51,
63 | "name": null,
64 | "state": 1,
65 | "value": 0
66 | },
67 | "CustomTextColor2": {
68 | "knownColor": 96,
69 | "name": null,
70 | "state": 1,
71 | "value": 0
72 | },
73 | "CustomTextColor3": {
74 | "knownColor": 106,
75 | "name": null,
76 | "state": 1,
77 | "value": 0
78 | },
79 | "CustomTextColor4": {
80 | "knownColor": 73,
81 | "name": null,
82 | "state": 1,
83 | "value": 0
84 | },
85 | "FindSearchTermColor": {
86 | "knownColor": 79,
87 | "name": null,
88 | "state": 1,
89 | "value": 0
90 | },
91 | "FindFileNameColor": {
92 | "knownColor": 78,
93 | "name": null,
94 | "state": 1,
95 | "value": 0
96 | },
97 | "TimeStampColor": {
98 | "knownColor": 45,
99 | "name": null,
100 | "state": 1,
101 | "value": 0
102 | }}
--------------------------------------------------------------------------------
/LoggerFacade.cs:
--------------------------------------------------------------------------------
1 | // (C) 2012 christian.schladetsch@gmail.com See https://github.com/cschladetsch/Flow.
2 |
3 | namespace Flow {
4 | ///
5 | /// DOC
6 | ///
7 | ///
8 | public class LoggerFacade
9 | : ILogger where TLogger
10 | : class
11 | , ILogger
12 | , new() {
13 | private readonly TLogger _log = new TLogger();
14 |
15 | public LoggerFacade(string pre) {
16 | _log.LogPrefix = pre;
17 | _log.ShowStack = false;
18 | _log.ShowSource = true;
19 | }
20 |
21 | public string LogPrefix {
22 | get => _log.LogPrefix;
23 | set => _log.LogPrefix = value;
24 | }
25 |
26 | public object LogSubject {
27 | get => _log.LogSubject;
28 | set => _log.LogSubject = value;
29 | }
30 |
31 | public bool ShowSource {
32 | get => _log.ShowSource;
33 | set => _log.ShowSource = value;
34 | }
35 |
36 | public bool ShowStack {
37 | get => _log.ShowStack;
38 | set => _log.ShowStack = value;
39 | }
40 |
41 | public int Verbosity {
42 | get => _log.Verbosity;
43 | set => _log.Verbosity = value;
44 | }
45 |
46 | public void Info(string fmt, params object[] args) {
47 | _log.Info(fmt, args);
48 | }
49 |
50 | public void Warn(string fmt, params object[] args) {
51 | _log.Warn(fmt, args);
52 | }
53 |
54 | public void Error(string fmt, params object[] args) {
55 | _log.Error(fmt, args);
56 | }
57 |
58 | public void Verbose(int level, string fmt, params object[] args) {
59 | _log.Verbose(level, fmt, args);
60 | }
61 | }
62 | }
--------------------------------------------------------------------------------
/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | #define UNITY
2 | #if !UNITY
3 | using System.Reflection;
4 | using System.Runtime.CompilerServices;
5 | using System.Runtime.InteropServices;
6 |
7 | // General Information about an assembly is controlled through the following
8 | // set of attributes. Change these attribute values to modify the information
9 | // associated with an assembly.
10 | [assembly: AssemblyTitle("Flow")]
11 | [assembly: AssemblyDescription("")]
12 | [assembly: AssemblyConfiguration("")]
13 | [assembly: AssemblyCompany("")]
14 | [assembly: AssemblyProduct("Flow")]
15 | [assembly: AssemblyCopyright("Copyright © 2018")]
16 | [assembly: AssemblyTrademark("")]
17 | [assembly: AssemblyCulture("")]
18 |
19 | // Setting ComVisible to false makes the types in this assembly not visible
20 | // to COM components. If you need to access a type in this assembly from
21 | // COM, set the ComVisible attribute to true on that type.
22 | [assembly: ComVisible(false)]
23 |
24 | // The following GUID is for the ID of the typelib if this project is exposed to COM
25 | [assembly: Guid("18326c80-2cc4-417d-8fee-ea608ed85636")]
26 |
27 | // Version information for an assembly consists of the following four values:
28 | //
29 | // Major Version
30 | // Minor Version
31 | // Build Number
32 | // Revision
33 | //
34 | // You can specify all the values or you can default the Build and Revision Numbers
35 | // by using the '*' as shown below:
36 | [assembly: AssemblyVersion("1.0.*")]
37 | [assembly: AssemblyFileVersion("1.0.0.0")]
38 | #endif
--------------------------------------------------------------------------------
/Readme.md:
--------------------------------------------------------------------------------
1 | # Flow 
2 | [](https://ci.appveyor.com/project/cschladetsch/flow)
3 | [](https://www.codefactor.io/repository/github/cschladetsch/flow)
4 | [](./LICENSE.txt)
5 | 
6 |
7 | A C# coroutine-based Kernel for .Net. If you are one of the many developers using this library, I encourage you provide any feedback and/or fork.
8 |
9 | This is Unity-friendly and will work on all versions of Unity after 4.0. Please let me know otherwise.
10 |
11 | Current documentation is at [GamaSutra](http://www.gamasutra.com/view/news/177397/Indepth_Flow__A_coroutine_kernel_for_Net.php) but the formatting is a bit screwy.
12 |
13 | The original post was on [AltDevBlogADay](http://www.altdevblogaday.com/2012/09/07/flow-a-coroutine-kernel-for-net/) but that site is now lost for the ages.
14 |
15 | ## Tests
16 |
17 | The [tests](TestFlow/Editor) reside in _TestFlow/Editor_ so they can be used from Unity3d as well.
18 |
19 | These tests, along with the GamaSutra article, are the best first sources of documentation.
20 |
21 | ## Example 1
22 |
23 | Using _Flow_ for async REST communications.
24 |
25 | ```C#
26 | private void CreateHeartbeat()
27 | {
28 | New.PeriodicTimer(TimeSpan.FromMinutes(2)).Elapsed += tr =>
29 | {
30 | Get("user/alive").Then(result =>
31 | {
32 | if (result.Succeeded(out var val))
33 | {
34 | _activeUsers.Value = val.Num;
35 | Info($"{val.Num} users online.");
36 | }
37 | });
38 | };
39 | }
40 | ```
41 |
42 | ## Example 2
43 |
44 | This is example code pulled straight for a [game](https://github.com/cschladetsch/Chess2) I'm quasi-working on:
45 |
46 | ```C#
47 | public void GameLoop()
48 | {
49 | Root.Add(
50 | New.Sequence(
51 | New.Coroutine(StartGame).Named("StartGame"),
52 | New.While(() => !_gameOver),
53 | New.Coroutine(PlayerTurn).Named("Turn").Named("While"),
54 | New.Coroutine(EndGame).Named("EndGame")
55 | ).Named("GameLoop")
56 | );
57 | }
58 | ```
59 | Note the `.Named("Name")` extensions to the factory methods: these are for debugging and tracing purposes. The library comes with extensive debugging and visualisation support, so you can see in real time as the kernel changes.
60 |
61 | The main logic _flow_ for starting a turn is:
62 |
63 | ```C#
64 | private IEnumerator StartGame(IGenerator self)
65 | {
66 | var start = New.Sequence(
67 | New.Barrier(
68 | WhitePlayer.StartGame(),
69 | BlackPlayer.StartGame()
70 | ).Named("Init Game"),
71 | New.Barrier(
72 | WhitePlayer.DrawInitialCards(),
73 | BlackPlayer.DrawInitialCards()
74 | ).Named("Deal Cards"),
75 | New.Barrier(
76 | New.TimedBarrier(
77 | TimeSpan.FromSeconds(Parameters.MulliganTimer),
78 | WhitePlayer.AcceptCards(),
79 | BlackPlayer.AcceptCards()
80 | ).Named("Mulligan"),
81 | New.Sequence(
82 | WhitePlayer.PlaceKing(),
83 | BlackPlayer.PlaceKing()
84 | ).Named("Place Kings")
85 | ).Named("Preceedings")
86 | ).Named("Start Game");
87 | start.Completed += (tr) => Info("StartGame completed");
88 | yield return start;
89 | }
90 | ```
91 |
92 | And the relevant IPlayerAgent Method declaractions as being:
93 |
94 | ```C#
95 | ITimer StartGame();
96 | ITimer DrawInitialCards();
97 | ITimedFuture AcceptCards();
98 | ITimedFuture PlaceKing();
99 | ITransient ChangeMaxMana(int i);
100 | ITimedFuture DrawCard();
101 | ITimedFuture PlayCard();
102 | ITimedFuture MovePiece();
103 | ITimedFuture Pass();
104 | ```
105 |
106 | This is just a simple example on how the library is tyically used. It's a matter of chaining together sequences of _Barriers_, _Triggers_, and _Futures_ to remove the need to keep explicit track of internal state on each *Update* call.
107 |
108 | In this case, I'm using a lot of timed futures because it's a real-time card game and there are time limits.
109 |
110 | ## Notes
111 | ### Verbose Logging
112 | When using `Verbose()` be mindful that the arguments passed into the log will be evaluated even if the verbosity is set lower than would print. Be diligent when using interpolated strings or complex functions in Verbose logging.
113 | e.g.
114 | ```csharp
115 | Verbosity = 10;
116 | Verbose(15, $"Result of complex function: {ComplexFunction()}.");
117 | ```
118 |
--------------------------------------------------------------------------------
/TestFlow/Editor/FlowTests.asmdef:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Flow-Tests",
3 | "references": [],
4 | "optionalUnityReferences": [
5 | "TestAssemblies"
6 | ],
7 | "includePlatforms": [],
8 | "excludePlatforms": [],
9 | "allowUnsafeCode": false,
10 | "overrideReferences": false,
11 | "precompiledReferences": [],
12 | "autoReferenced": true,
13 | "defineConstraints": []
14 | }
--------------------------------------------------------------------------------
/TestFlow/Editor/GetParamName.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq.Expressions;
3 |
4 | public static class SymbolName
5 | {
6 | public static string Get(Expression> memberExpression)
7 | {
8 | return ((MemberExpression)memberExpression.Body).Member.Name;
9 | }
10 | }
11 |
12 | /*
13 |
14 | To get name of a variable:
15 |
16 | string testVariable = "value";
17 | string nameOfTestVariable = SymbolName.Get(() => testVariable);
18 |
19 |
20 | To get name of a parameter:
21 |
22 | public class TestClass
23 | {
24 | public void TestMethod(string param1, string param2)
25 | {
26 | string nameOfParam1 = SymbolName.Get(() => param1);
27 | }
28 | }
29 |
30 | */
31 |
--------------------------------------------------------------------------------
/TestFlow/Editor/TestBarrier.cs:
--------------------------------------------------------------------------------
1 | using System.Linq;
2 | using NUnit.Framework;
3 |
4 | namespace Flow.Test
5 | {
6 | [TestFixture]
7 | public class TestBarrier : TestBase
8 | {
9 | [Test]
10 | public void BarrierTest()
11 | {
12 | var kernel = Create.Kernel();
13 | var flow = kernel.Factory;
14 |
15 | var barrier = flow.Barrier();
16 | var future1 = flow.Future();
17 | var future2 = flow.Future();
18 |
19 | barrier.Add(future1);
20 | barrier.Add(future2);
21 |
22 | kernel.Root.Add(barrier, future1, future2);
23 | kernel.Step();
24 | kernel.Step();
25 |
26 | var rootCount = kernel.Root.Contents.Count();
27 | Assert.AreEqual(3, rootCount);
28 |
29 | Assert.IsTrue(barrier.Active);
30 | Assert.IsTrue(future1.Active);
31 | Assert.IsTrue(future2.Active);
32 |
33 | kernel.Step();
34 |
35 | future1.Value = 123;
36 |
37 | kernel.Step();
38 |
39 | Assert.IsTrue(barrier.Active);
40 | Assert.IsFalse(future1.Active);
41 | Assert.IsTrue(future2.Active);
42 |
43 | future2.Value = 456;
44 |
45 | kernel.Step();
46 |
47 | Assert.IsFalse(barrier.Active);
48 | Assert.IsFalse(future1.Active);
49 | Assert.IsFalse(future2.Active);
50 |
51 | Assert.AreEqual(123, future1.Value);
52 | Assert.AreEqual(456, future2.Value);
53 | }
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/TestFlow/Editor/TestBase.cs:
--------------------------------------------------------------------------------
1 | namespace Flow.Test
2 | {
3 | using System;
4 | using NUnit.Framework;
5 |
6 | [TestFixture]
7 | public class TestBase
8 | {
9 | protected IKernel Kernel;
10 | protected IFactory New;
11 | protected INode Root;
12 |
13 | [SetUp]
14 | public void Pre()
15 | {
16 | Kernel = Create.Kernel();
17 | New = Kernel.Factory;
18 | Root = Kernel.Root;
19 | }
20 |
21 | protected void Print(object q)
22 | => PrintFmt("{0}", q);
23 |
24 | protected void PrintFmt(string fmt, params object[] args)
25 | => Kernel.Log.Info(fmt, args);
26 |
27 | protected void Step(int steps = 1)
28 | {
29 | for (var n = 0; n < steps; ++n)
30 | Kernel.Step();
31 | }
32 |
33 | protected float RunKernel(float seconds)
34 | {
35 | Kernel.Step();
36 | var start = Kernel.Time.Now;
37 | var end = start + TimeSpan.FromSeconds(seconds);
38 |
39 | while (Kernel.Time.Now < end)
40 | {
41 | Kernel.Step();
42 | if (Kernel.Break)
43 | break;
44 | }
45 |
46 | return (float)(Kernel.Time.Now - start).TotalSeconds;
47 | }
48 | }
49 | }
50 |
51 |
--------------------------------------------------------------------------------
/TestFlow/Editor/TestChannel.cs:
--------------------------------------------------------------------------------
1 | namespace Flow.Test
2 | {
3 | using System.Collections.Generic;
4 | using NUnit.Framework;
5 |
6 | public class TestChannel
7 | : TestBase
8 | {
9 | [Test]
10 | public void TestInsertExtract()
11 | {
12 | var chan = New.Channel();
13 |
14 | chan.Insert(1);
15 | chan.Insert(2);
16 | chan.Insert(3);
17 |
18 | var f0 = chan.Extract();
19 | var f1 = chan.Extract();
20 | var f2 = chan.Extract();
21 | var f3 = chan.Extract();
22 |
23 | Root.Add(chan);
24 |
25 | Step(5);
26 |
27 | Assert.IsTrue(f0.Available);
28 | Assert.IsTrue(f1.Available);
29 | Assert.IsTrue(f2.Available);
30 | Assert.IsFalse(f3.Available);
31 |
32 | Assert.AreEqual(1, f0.Value);
33 | Assert.AreEqual(2, f1.Value);
34 | Assert.AreEqual(3, f2.Value);
35 | }
36 |
37 | [Test]
38 | public void TestExtractInsert()
39 | {
40 | var chan = New.Channel();
41 |
42 | var f0 = chan.Extract();
43 | var f1 = chan.Extract();
44 | var f2 = chan.Extract();
45 |
46 | chan.Insert(1);
47 | chan.Insert(2);
48 | chan.Insert(3);
49 |
50 | Root.Add(chan);
51 |
52 | Step(5);
53 |
54 | Assert.AreEqual(1, f0.Value);
55 | Assert.AreEqual(2, f1.Value);
56 | Assert.AreEqual(3, f2.Value);
57 | }
58 |
59 | ///
60 | /// Test one consumer using a channel, where the values are manually inserted into the channel.
61 | ///
62 | [Test()]
63 | public void TestProducerConsumer()
64 | {
65 | var channel = Kernel.Factory.Channel();
66 | var con = new Consumer(Kernel, channel);
67 |
68 | Root.Add(channel);
69 | channel.Insert(1);
70 | Step(5);
71 | Assert.AreEqual(1, con.Sum);
72 |
73 | channel.Insert(1);
74 | Step(5);
75 | Assert.AreEqual(2, con.Sum);
76 |
77 | channel.Insert(1);
78 | Step(5);
79 | Assert.AreEqual(3, con.Sum);
80 |
81 | Step(5);
82 | Assert.AreEqual(3, con.Sum);
83 | }
84 |
85 | /////
86 | ///// Test one producer and multiple consumers via a channel.
87 | ///// The producer in this case is another coroutine
88 | /////
89 | //[Test()]
90 | //public void TestCoroProducerMultipleConsumer()
91 | //{
92 | // var kernel = Create.Kernel();
93 | // var prod = kernel.Factory.NewCoroutine(Producer);
94 | // var channel = kernel.Factory.NewChannel(prod);
95 |
96 | // var con1 = new Consumer(kernel, channel);
97 | // var con2 = new Consumer(kernel, channel);
98 |
99 | // StepKernel(kernel, 50);
100 |
101 | // Assert.AreEqual(15, con1.Sum + con2.Sum);
102 | //}
103 |
104 | IEnumerator Producer(IGenerator self)
105 | {
106 | yield return 0;
107 | yield return 1;
108 | yield return 2;
109 | yield return 4;
110 | yield return 8;
111 | }
112 | }
113 |
114 | class Consumer
115 | {
116 | public int Sum;
117 |
118 | public Consumer(IKernel kernel, IChannel channel)
119 | {
120 | // this is ambiguous, but it shouldn;t be....
121 | //kernel.Root.Add(kernel.Factory.Coroutine(Step, channel));
122 | kernel.Root.Add(kernel.Factory.Coroutine>(Step, channel));
123 | }
124 |
125 | public IEnumerator Step(IGenerator self, IChannel channel)
126 | {
127 | while (true)
128 | {
129 | var next = channel.Extract();
130 | yield return self.ResumeAfter(next) != null;
131 | if (!next.Available)
132 | yield break;
133 | Sum += next.Value;
134 | }
135 | }
136 | }
137 | }
138 |
139 |
--------------------------------------------------------------------------------
/TestFlow/Editor/TestConditionals.cs:
--------------------------------------------------------------------------------
1 | namespace Flow.Test
2 | {
3 | using NUnit.Framework;
4 |
5 | internal class TestConditionals
6 | : TestBase
7 | {
8 | [Test]
9 | public void TestIf()
10 | {
11 | Kernel.DebugLevel = Flow.EDebugLevel.Verbose;
12 | var executed = false;
13 | var exp = New.If(
14 | () => true,
15 | New.Do(() => executed = true)
16 | ).Named("IF1");
17 |
18 | Root.Add(exp);
19 | Step(2);
20 | Assert.IsTrue(executed);
21 |
22 | executed = false;
23 | var exp2 = New.If(
24 | () => false,
25 | New.Do(() => executed = true)
26 | ).Named("If2");
27 |
28 | Root.Remove(exp);
29 | Root.Add(exp2);
30 |
31 | Step(2);
32 | Assert.IsFalse(executed);
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/TestFlow/Editor/TestEventStream.cs:
--------------------------------------------------------------------------------
1 | using NUnit.Framework;
2 |
3 | namespace Flow.Test
4 | {
5 | public class TestEventStream : TestBase
6 | {
7 | [Test()]
8 | public void TestCase()
9 | {
10 | //var ev = f.NewEventStream(f.Arrived);
11 | }
12 |
13 | //object NewEventStream(Action act)
14 | //{
15 | // return null;
16 | //}
17 | }
18 | }
19 |
20 |
--------------------------------------------------------------------------------
/TestFlow/Editor/TestFlowExtra.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Diagnostics;
4 |
5 | using Flow;
6 | using NUnit.Framework;
7 |
8 | namespace Flow.Test
9 | {
10 | // TODO: Move to Flow library
11 | [TestFixture]
12 | public class TestFlowExtra
13 | {
14 | [Test]
15 | public void TestWhile()
16 | {
17 | var k = Flow.Create.Kernel();
18 | var f = k.Factory;
19 | }
20 |
21 | [Test]
22 | public void TestBasicPrint()
23 | {
24 | var k = Flow.Create.Kernel();
25 | var f = k.Factory;
26 |
27 | var t0 = f.Transient().Named("T0");
28 | var t1 = f.Transient().Named("Trans1");
29 | var t2 = f.Transient().Named("Trans2");
30 | var r0 = f.Trigger(t2, t1).Named("Barrier0");
31 | var b0 = f.Barrier(t0, r0).Named("Barrier1");
32 | var f0 = f.Future().Named("Future");
33 | var g0 = f.Group(f0, b0).Named("Group");
34 |
35 | k.Root.Add(g0);
36 |
37 | // allow all objects to be placed
38 | for (var n = 0; n < 10; ++n)
39 | k.Step();
40 |
41 | Console.WriteLine(g0);
42 | }
43 |
44 | private static void Step(Flow.IGenerator gen, int steps)
45 | {
46 | for (var n = 0; n < steps; ++n)
47 | gen.Step();
48 | }
49 |
50 | //[Test]
51 | //public void TestFlowSequence()
52 | //{
53 | // var k = Flow.Create.Kernel();
54 | // var f = k.Factory;
55 | // var r = k.Root;
56 | // r.Add(f.Sequence(
57 | // f.Coroutine(StartGame).Named("GameLoop"),
58 | // f.Coroutine(PlayerTurn, EColor.White),
59 | // f.Coroutine(EndGame))
60 | // );
61 |
62 | // k.Step();
63 | // Trace.WriteLine(r);
64 | // k.Step();
65 | // Trace.WriteLine(r);
66 | // k.Step();
67 | // Trace.WriteLine(r);
68 | // k.Step();
69 | // Trace.WriteLine(r);
70 | // k.Step();
71 | // Trace.WriteLine(r);
72 | //}
73 |
74 | //static IEnumerator PlayerTurn(IGenerator self, EColor color)
75 | //{
76 | // Trace.WriteLine($"PlayerTurn: {color}");
77 | // yield return null;
78 | // Trace.WriteLine($"PlayerTurn: Again {color}");
79 | // yield return null;
80 | // Trace.WriteLine($"PlayerTurn: Again Again {color}");
81 | //}
82 |
83 | static IEnumerator EndGame(IGenerator self)
84 | {
85 | Trace.WriteLine("EndGame");
86 | yield break;
87 | }
88 |
89 | static IEnumerator StartGame(IGenerator self)
90 | {
91 | Trace.WriteLine("GameLoop");
92 | yield break;
93 | }
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/TestFlow/Editor/TestKernel.cs:
--------------------------------------------------------------------------------
1 | using System.Linq;
2 | using System.Collections.Generic;
3 | using NUnit.Framework;
4 |
5 | namespace Flow.Test
6 | {
7 | public class TestKernel : TestBase
8 | {
9 | [Test]
10 | public void TestCoroutine()
11 | {
12 | var kernel = Create.Kernel();
13 | var coro = kernel.Factory.Coroutine(Coro1);
14 | kernel.Root.Add(coro);
15 |
16 | Assert.IsTrue(coro.Active);
17 | Assert.IsTrue(coro.Running);
18 |
19 | // first coroutine is added after first step
20 | Assert.AreEqual(0, kernel.Root.Contents.Count());
21 | kernel.Step();
22 | Assert.AreEqual(1, kernel.Root.Contents.Count());
23 |
24 | kernel.Step();
25 | //Assert.AreEqual(1, coro.Value);
26 |
27 | kernel.Step();
28 | //Assert.AreEqual(2, coro.Value);
29 |
30 | kernel.Step();
31 | //Assert.AreEqual(3, coro.Value);
32 |
33 | kernel.Step();
34 | //Assert.AreEqual(3, coro.Value);
35 |
36 | Assert.IsFalse(coro.Active);
37 | Assert.IsFalse(coro.Running);
38 | }
39 |
40 | IEnumerator Coro1(IGenerator self)
41 | {
42 | yield return 1;
43 | yield return 2;
44 | yield return 3;
45 | }
46 |
47 | [Test]
48 | public void TestSubroutine()
49 | {
50 | }
51 |
52 | int Sub1(IGenerator self, int num)
53 | {
54 | return num * self.StepNumber;
55 | }
56 |
57 | [Test()]
58 | public void TestFuture()
59 | {
60 | var kernel = Create.Kernel();
61 | var spawn = kernel.Factory;
62 |
63 | var future = spawn.Future();
64 | var coro = spawn.Coroutine>(Coro2, future);
65 |
66 | _futureSet = false;
67 |
68 | kernel.Root.Add(future, coro);
69 | kernel.Step();
70 | kernel.Step();
71 |
72 | Assert.IsFalse(_futureSet);
73 |
74 | Assert.IsFalse(future.Available);
75 | future.Value = 42;
76 | Assert.IsTrue(future.Available);
77 | Assert.AreEqual(42, future.Value);
78 |
79 | kernel.Step();
80 |
81 | //Assert.IsFalse(coro.Value);
82 | Assert.IsTrue(_futureSet);
83 | }
84 |
85 | bool _futureSet;
86 |
87 | IEnumerator Coro2(IGenerator self, IFuture future)
88 | {
89 | Assert.IsFalse(!future.Active);
90 |
91 | Assert.IsFalse(future.Available);
92 |
93 | yield return self.ResumeAfter(future) != null;
94 |
95 | _futureSet = true;
96 |
97 | Assert.IsTrue(!future.Active);
98 |
99 | Assert.IsTrue(future.Available);
100 |
101 | Assert.AreEqual(future.Value, 42);
102 |
103 | yield return false;
104 | }
105 |
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/TestFlow/Editor/TestLog.cs:
--------------------------------------------------------------------------------
1 | /*
2 | using System;
3 | using NUnit.Framework;
4 | using Flow.Logger;
5 |
6 | namespace Flow.Test
7 | {
8 | [TestFixture]
9 | public class TestLog : TestBase
10 | {
11 | [Test]
12 | public void Test()
13 | {
14 | var log = new Logger.Logger(ELogEntryType.Everything, "test");
15 | #if UNITY_EDITOR
16 | log.AddLogger(new UnityLogger());
17 | #endif
18 | //log.AddFile("testlog.txt");
19 | log.Log("log1");
20 | log.Warn("warn1");
21 | log.Error("Error1");
22 | //log.Flush();
23 | }
24 | }
25 | }
26 |
27 | */
28 |
--------------------------------------------------------------------------------
/TestFlow/Editor/TestLoops.cs:
--------------------------------------------------------------------------------
1 | namespace Flow.Test
2 | {
3 | using System.Collections;
4 | using System.Linq;
5 | using NUnit.Framework;
6 |
7 | public class TestLoops
8 | : TestBase
9 | {
10 | private int _count = 0;
11 |
12 | [Test]
13 | public void TestDebugLog()
14 | {
15 | Root.Add(New.Log("Hello World"));
16 | Step(5);
17 | }
18 |
19 | [Test]
20 | public void TestCoro()
21 | {
22 | Root.Add(New.Coroutine(CountTo, 10).Named("Body"));
23 |
24 | _count = 0;
25 | Step(20);
26 | Assert.AreEqual(_count, 10);
27 | }
28 |
29 | private IEnumerator CountTo(IGenerator self, int max)
30 | {
31 | while (++_count != max)
32 | yield return 0;
33 | }
34 |
35 | [TestCase(5, 5, true)]
36 | [TestCase(3, 3, true)]
37 | [TestCase(5, 3, false)]
38 | [TestCase(3, 5, true)]
39 | [TestCase(0, 5, false)]
40 | public void TestWhile(int upper, int steps, bool shouldMatch)
41 | {
42 | _count = 0;
43 | Root.Add(
44 | New.While(() =>
45 | {
46 | ++_count;
47 | Print($"step={Kernel.StepNumber}, count={_count}");
48 | return _count < upper;
49 | })
50 | );
51 |
52 | Step(steps);
53 | Print(_count);
54 |
55 | if (shouldMatch)
56 | Assert.AreEqual(upper, _count);
57 | else
58 | Assert.AreNotEqual(upper, _count);
59 | }
60 |
61 | [Test]
62 | public void TestNop()
63 | {
64 | _count = 0;
65 |
66 | New.Nop().AddTo(Root);
67 | Assert.IsTrue(!Root.Contents.Any());
68 |
69 | Step();
70 | Assert.IsTrue(Root.Contents.Any());
71 |
72 | Step();
73 | Assert.IsTrue(!Root.Contents.Any());
74 | }
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/TestFlow/Editor/TestResumeAfter.cs:
--------------------------------------------------------------------------------
1 | namespace Flow.Test
2 | {
3 | using System.Collections;
4 | using NUnit.Framework;
5 |
6 | [TestFixture]
7 | public class TestResumeAfter
8 | : TestBase
9 | {
10 | [TestCase(10, 15, true)]
11 | [TestCase(15, 3, false)]
12 | [TestCase(3, 3, false)]
13 | public void TestResumeAfterPred(int activateStep, int numSteps, bool ran)
14 | {
15 | var hasRun = false;
16 | IEnumerator Coro(IGenerator self)
17 | {
18 | yield return null;
19 | hasRun = true;
20 | }
21 |
22 | var coro = New.Coroutine(Coro);
23 | coro.AddTo(Root);
24 | coro.ResumeAfter(() => Kernel.StepNumber > activateStep);
25 | Assert.IsFalse(hasRun);
26 |
27 | Step(numSteps);
28 | Print(Kernel.StepNumber);
29 | Print(hasRun);
30 |
31 | Assert.AreEqual(hasRun, ran);
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/TestFlow/Editor/TestSequenceTopology.cs:
--------------------------------------------------------------------------------
1 | namespace Flow.Test
2 | {
3 | using NUnit.Framework;
4 |
5 | [TestFixture]
6 | public class TestSequences
7 | : TestBase
8 | {
9 | [Test()]
10 | public void TestSequence()
11 | {
12 | var stepNum = 0;
13 | Root.Add(
14 | New.Sequence(
15 | New.Do(() => stepNum += Kernel.StepNumber),
16 | New.Do(() => stepNum += Kernel.StepNumber),
17 | New.Do(() => stepNum += Kernel.StepNumber),
18 | New.Do(() => stepNum += Kernel.StepNumber),
19 | New.Do(() => stepNum += Kernel.StepNumber)
20 | )
21 | );
22 |
23 | Step(5);
24 |
25 | Assert.AreEqual(stepNum, 0 + 1 + 2 + 3 + 4);
26 | }
27 |
28 | [Test]
29 | public void TestEmptySequenceCompletes()
30 | {
31 | var completed = false;
32 | var seq = New.Sequence().AddTo(Root);
33 | seq.Completed += tr => completed = true;
34 |
35 | Step(4);
36 |
37 | Assert.IsTrue(completed);
38 | }
39 |
40 | [Test]
41 | public void TestParallel()
42 | {
43 | var stepNum = 0;
44 | New.Node(
45 | New.Do(() => stepNum += 1).Named("Do 1"),
46 | New.Do(() => stepNum += 1).Named("Do 2"),
47 | New.Do(() => stepNum += 1).Named("Do 3")
48 | ).Named("Parallel Node").AddTo(Root);
49 |
50 | Step(3);
51 |
52 | Assert.AreEqual(3, stepNum);
53 | }
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/TestFlow/Editor/TestTimers.cs:
--------------------------------------------------------------------------------
1 | namespace Flow.Test
2 | {
3 | using System;
4 | using NUnit.Framework;
5 |
6 | public class TestTimers
7 | : TestBase
8 | {
9 | [TestCase(0.4f, 0.5f, true)]
10 | [TestCase(0.4f, 0.2f, false)]
11 | public void TestOneShot(float span, float runTime, bool shouldBeCompleted)
12 | {
13 | var elapsed = false;
14 | var oneShotTimer = New.OneShotTimer(TimeSpan.FromSeconds(span), (timer) => elapsed = true);
15 | Root.Add(oneShotTimer);
16 | RunKernel(runTime);
17 |
18 | if (shouldBeCompleted)
19 | {
20 | Assert.IsTrue(!oneShotTimer.Active);
21 | Assert.IsTrue(elapsed);
22 | }
23 | else
24 | {
25 | Assert.IsTrue(oneShotTimer.Active);
26 | Assert.IsFalse(elapsed);
27 | }
28 | }
29 |
30 | [TestCase(0.1f, 0.25f, 2)]
31 | [TestCase(0.1f, 0.45f, 4)]
32 | [TestCase(0.1f, 0.0f, 0)]
33 | public void TestPeriodic(float interval, float runTime, int numElapsed)
34 | {
35 | var timer = New.PeriodicTimer(TimeSpan.FromSeconds(interval));
36 | var elapsed = 0;
37 |
38 | timer.Elapsed += tr => ++elapsed;
39 |
40 | Root.Add(timer);
41 | RunKernel(runTime);
42 |
43 | Assert.AreEqual(numElapsed, elapsed);
44 | }
45 |
46 | [TestCase(0.1f, 0.2f, true)]
47 | [TestCase(0.2f, 0.1f, false)]
48 | public void TestTimedFuture(float futureLifetime, float runTime, bool result)
49 | {
50 | var future = New.TimedFuture(TimeSpan.FromSeconds(futureLifetime));
51 |
52 | Assert.IsFalse(future.Available);
53 |
54 | Root.Add(future);
55 | RunKernel(runTime);
56 |
57 | Step();
58 |
59 | Assert.AreEqual(result, future.HasTimedOut);
60 | }
61 | }
62 | }
63 |
64 |
--------------------------------------------------------------------------------
/TestFlow/Editor/TestTrigger.cs:
--------------------------------------------------------------------------------
1 | using NUnit.Framework;
2 |
3 | namespace Flow.Test
4 | {
5 | [TestFixture]
6 | public class TestTrigger : TestBase
7 | {
8 | [Test]
9 | public void TriggerTest()
10 | {
11 | var trans1 = New.Transient();
12 | var trans2 = New.Transient();
13 | var trigger = New.Trigger(trans1, trans2);
14 |
15 | Root.Add(trigger);
16 |
17 | Step();
18 |
19 | Assert.IsTrue(trigger.Active);
20 | Assert.IsTrue(trans1.Active);
21 | Assert.IsTrue(trans2.Active);
22 |
23 | trans1.Complete();
24 |
25 | Step();
26 |
27 | Assert.IsFalse(trigger.Active);
28 | Assert.IsFalse(trans1.Active);
29 | Assert.IsTrue(trans2.Active);
30 | }
31 |
32 | [Test]
33 | public void TriggerFutureTest()
34 | {
35 | var trigger = New.Trigger();
36 | var future1 = New.Future();
37 | var future2 = New.Future();
38 |
39 | trigger.Add(future1);
40 | trigger.Add(future2);
41 |
42 | Root.Add(trigger, future1, future2);
43 | Step(2);
44 |
45 | Assert.IsTrue(trigger.Active);
46 | Assert.IsTrue(future1.Active);
47 | Assert.IsTrue(future2.Active);
48 |
49 | Step();
50 |
51 | future1.Value = 123;
52 |
53 | Step();
54 |
55 | Assert.AreEqual(123, future1.Value);
56 | Assert.IsFalse(trigger.Active);
57 | Assert.IsFalse(future1.Active);
58 | Assert.IsTrue(future2.Active);
59 |
60 | future2.Value = "foo";
61 | Step();
62 | Assert.IsFalse(future2.Active);
63 | Assert.AreEqual("foo", future2.Value);
64 | }
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/TestFlow/Editor/TimerTimedWait.cs:
--------------------------------------------------------------------------------
1 | //using System;
2 | //using NUnit.Framework;
3 |
4 | //namespace Flow.Test
5 | //{
6 | // [TestFixture]
7 | // class TimerWaitTest : TestBase
8 | // {
9 | // // Broke this test when I removed IFactory.Parallel
10 | // // I thought Node did the same thing...
11 | // //[TestCase(0.5f)]
12 | // //[TestCase(0.2f)]
13 | // //public void TestBreak(float timeOut)
14 | // //{
15 | // // _root.Add(
16 | // // New.Node(
17 | // // New.While(() => true,
18 | // // New.OneShotTimer(TimeSpan.FromSeconds(timeOut), (self) => self.Kernel.BreakFlow())
19 | // // )
20 | // // )
21 | // // );
22 |
23 | // // Print(_root);
24 |
25 | // // var delta = RunKernel(2) - timeOut;
26 | // // Assert.IsTrue(Math.Abs(delta) < 0.1f);
27 | // //}
28 |
29 | // [TestCase(0.5f, 1.0f, 0.5f)]
30 | // [TestCase(1.5f, 1.0f, 1.0f)]
31 | // public void TestTimedWait(float timerLen, float timeOut, float kernelRunTime)
32 | // {
33 | // Root.Add(
34 | // New.WaitFor(
35 | // New.Trigger(New.OneShotTimer(TimeSpan.FromSeconds(timerLen))),
36 | // TimeSpan.FromSeconds(timeOut)
37 | // ),
38 | // New.Break()
39 | // );
40 |
41 | // var delta = timerLen - RunKernel(kernelRunTime);
42 | // Assert.IsTrue(Math.Abs(delta) < 0.1f);
43 | // }
44 | // }
45 | //}
46 |
--------------------------------------------------------------------------------
/TestFlow/Editor/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/TestFlow/TestFlow.asmdef:
--------------------------------------------------------------------------------
1 | {
2 | "name": "TestFlow",
3 | "references": [],
4 | "optionalUnityReferences": [
5 | "TestAssemblies"
6 | ],
7 | "includePlatforms": [],
8 | "excludePlatforms": [],
9 | "allowUnsafeCode": false,
10 | "overrideReferences": false,
11 | "precompiledReferences": [],
12 | "autoReferenced": true,
13 | "defineConstraints": []
14 | }
--------------------------------------------------------------------------------
/TestFlow/TestFlow.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {12E9E3DC-61D3-42F6-B9AF-8BE0C5EAAC9F}
8 | Library
9 | TestFlow
10 | TestFlow
11 | v4.5
12 |
13 |
14 |
15 |
16 | true
17 | full
18 | false
19 | bin\Debug
20 | DEBUG;DOTNET
21 | prompt
22 | 4
23 |
24 |
25 | true
26 | bin\Release
27 | prompt
28 | 4
29 |
30 |
31 |
32 | ..\packages\NUnit.3.12.0\lib\net45\nunit.framework.dll
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 | {18326c80-2cc4-417d-8fee-ea608ed85636}
55 | Flow2
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 | This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
65 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/TestFlow/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/appveyor.yml:
--------------------------------------------------------------------------------
1 | version: 1.0.{build}
2 | before_build:
3 | - nuget restore
4 | image: Visual Studio 2017
5 | build:
6 | project: Flow.sln
7 | verbosity: minimal
8 |
--------------------------------------------------------------------------------
/flow-small.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cschladetsch/Flow/d057260fe13c1e4605b854857bb4b2655d263d02/flow-small.jpg
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "com.cschladetsch.flow",
3 | "displayName": "Flow",
4 | "version": "0.0.1",
5 | "unity": "2018.3",
6 | "description": "A co-routine Kernel for program flow-control",
7 | "keywords": [
8 | "flow"
9 | ],
10 | "category": "Unity"
11 | }
12 |
--------------------------------------------------------------------------------