-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathStateMachinesController.cs
235 lines (186 loc) · 6.75 KB
/
StateMachinesController.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
using ValkyrieFSMCore;
using System.Diagnostics;
namespace Valkyrie_Server
{
/// <summary>
/// Executes the state machines.
/// </summary>
public sealed class StateMachinesController
{
/// <summary>
/// The list of machines that are being handled by this task handler.
/// </summary>
private Dictionary<string, (StateMachine machine, DateTime time)> Machines { get; set; } = new Dictionary<string, (StateMachine machine, DateTime time)>();
/// <summary>
/// get the active machines
/// </summary>
public (StateMachine machine, DateTime time)[] GetMachines => Machines.Values.ToArray();
/// <summary>
/// Is the state machine task handler running?
/// </summary>
public bool Running => TickThread != null && TickThread.IsAlive;
/// <summary>
/// The tick thread.
/// </summary>
private Thread? TickThread { get; set; }
/// <summary>
/// Is the state machine ticking?
/// </summary>
public bool IsTicking => TickThread != null && TickThread.IsAlive;
public float TimoutSeconds { get; set; } = 60f;
/// <summary>
/// Default constructor.
/// </summary>
public StateMachinesController(float timeoutSeconds)
{
TimoutSeconds = timeoutSeconds;
}
/// <summary>
/// Start the state machine.
/// </summary>
/// <param name="id">the id of the machine</param>
/// <param name="machine">the state machine</param>
public StateMachine AddMachine(string id, string instructions)
{
if (string.IsNullOrEmpty(id))
throw new ArgumentNullException("id");
if (string.IsNullOrEmpty(instructions))
throw new ArgumentNullException("instructions");
StateMachineBuilder builder = new StateMachineBuilder();
var machine = builder.ParseInstructionsJSON(instructions);
machine.IsRunning = true;
Machines.Add(id, (machine, DateTime.UtcNow));
if (!IsTicking)
Boot();
return machine;
}
/// <summary>
/// Evaluate each state machine once.
/// </summary>
private void Tick()
{
//Debug.WriteLine("tick " + Machines.Count);
foreach (var x in Machines)
{
if (!x.Value.machine.Completed)
{
if ((DateTime.UtcNow - x.Value.time).TotalSeconds > TimoutSeconds)
{
Debug.WriteLine("Killing state machine " + x.Key + " due to timeout");
KillStateMachineProcess(x.Key);
continue;
}
try
{
Debug.WriteLine("Evaluating");
x.Value.machine.Evaluate();
}
catch (Exception ex)
{
x.Value.machine.IsRunning = false;
x.Value.machine.CurrentState = x.Value.machine.FallbackState;
try
{
x.Value.machine.Evaluate();
}
catch (Exception ex2)
{
x.Value.machine.Result = "bru - Error running: " + ex2.Message;
}
x.Value.machine.Result = "Error running: " + ex.Message;
}
}
}
try
{
Thread.Sleep(0); //force the thread to sleep so other threads can execute (kinda like a video game serverside tick).
Tick();
}
catch (Exception ex)
{
Debug.WriteLine("Error ticking: " + ex.Message);
}
}
/// <summary>
/// Check if the machine is complete.
/// </summary>
/// <param name="id">the id to check</param>
/// <returns>returns a tuple with the first value being a bool indicating if the machine is complete and the second value being the result of the machine</returns>
public (bool complete, string result) HandleStatus(string id)
{
if (!Machines.ContainsKey(id))
{
return (false, "does not contain the key");
}
if (Machines[id].machine.Completed)
{
Machines[id].machine.IsRunning = false;
string res = Machines[id].machine.Result;
Machines.Remove(id);
if (Machines.Count == 0)
{
Kill();
}
return (true, res);
}
return (false, "not complete");
}
/// <summary>
/// Kill the state machine if it took too long..
/// </summary>
/// <param name="id">the id of the state machine..</param>
/// <returns></returns>
public State? KillStateMachineProcess(string id)
{
Debug.WriteLine("removing state machine " + id);
if (Machines.ContainsKey(id))
{
Machines[id].machine.IsRunning = false;
var machine = Machines[id];
//could be dangerous...
machine.machine.CurrentState = machine.machine.FallbackState;
machine.machine.Evaluate();
//
Machines.Remove(id);
Debug.WriteLine("state machine removed");
Kill();
if (Machines.Count > 0)
Boot();
return machine.machine.CurrentState;
}
return null;
}
/// <summary>
/// Kill the ticking thread.
/// </summary>
public void Kill()
{
if (TickThread != null && TickThread.IsAlive)
{
//KILL the thread...
try
{
TickThread.Interrupt();
}
catch (Exception ex)
{
Debug.WriteLine("Error killing thread: " + ex.Message);
}
TickThread.Join();
TickThread = null;
Debug.WriteLine("killed thread");
}
}
/// <summary>
/// Start the tick thread.
/// </summary>
public void Boot()
{
Debug.WriteLine("starting thread");
//boot
TickThread = new Thread(Tick);
TickThread.IsBackground = true;
TickThread.Start();
}
}
}