-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.cpp
More file actions
233 lines (184 loc) · 8.21 KB
/
main.cpp
File metadata and controls
233 lines (184 loc) · 8.21 KB
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
// main.cpp
// Example driver exercising the delegate-based FSM framework.
// Demonstrates synchronous, asynchronous active-object, and polling patterns.
// @see https://github.com/endurodave/StateMachine
// David Lafreniere
#include "examples/Motor.h"
#include "examples/Player.h"
#include "examples/CentrifugeTest.h"
#include "examples/TcpConnection.h"
#include "examples/AlarmPanel.h"
#include "unit-tests/StateMachineTests.h"
#include "unit-tests/StateMachineHSMTests.h"
#include "delegate-mq/DelegateMQ.h"
#include "delegate-mq/extras/util/Fault.h"
#include "delegate-mq/extras/util/Timer.h"
#include <iostream>
#include <atomic>
#include <future>
#include <thread>
#include <chrono>
using namespace std;
using namespace dmq;
std::atomic<bool> processTimerExit = false;
void ProcessTimers()
{
while (!processTimerExit.load())
{
// Process all delegate-based timers
dmq::util::Timer::ProcessTimers();
std::this_thread::sleep_for(std::chrono::milliseconds(5));
}
}
int main()
{
std::thread timerThread(ProcessTimers);
// -----------------------------------------------------------------------
// Synchronous Motor — ExternalEvent runs on the caller's thread.
// SetSpeed/Halt block until the state action completes before returning.
// -----------------------------------------------------------------------
cout << "=== Synchronous Motor ===" << endl;
Motor syncMotor;
printf("Size of StateMapRowEx: %zu bytes\n", sizeof(StateMapRowEx));
printf("Size of Motor: %zu bytes\n", sizeof(Motor));
// OnTransition fires after every completed state action.
// fromState == toState indicates a self-transition.
auto syncConn = syncMotor.OnTransition.Connect(
MakeDelegate([](uint8_t from, uint8_t to) {
cout << " [transition " << (int)from << " -> " << (int)to << "]" << endl;
}));
// OnEntry/OnExit fire on every actual state change — not on self-transitions.
// OnExit fires before the new state's entry action; OnEntry before its action.
auto entryConn = syncMotor.OnEntry.Connect(
MakeDelegate([](uint8_t state) {
cout << " [entry " << (int)state << "]" << endl;
}));
auto exitConn = syncMotor.OnExit.Connect(
MakeDelegate([](uint8_t state) {
cout << " [exit " << (int)state << "]" << endl;
}));
// OnCannotHappen fires before FaultHandler (which calls abort) when a
// CANNOT_HAPPEN transition is taken. Use this to flush logs, record the
// offending state, or trigger a controlled shutdown before the process dies.
auto faultConn = syncMotor.OnCannotHappen.Connect(
MakeDelegate([](uint8_t state) {
cerr << " [CANNOT_HAPPEN from state " << (int)state << "]" << endl;
}));
auto d1 = xmake_shared<MotorData>(); d1->speed = 100;
syncMotor.SetSpeed(d1); // blocks — state executes before returning
auto d2 = xmake_shared<MotorData>(); d2->speed = 200;
syncMotor.SetSpeed(d2);
syncMotor.Halt();
syncMotor.Halt(); // ignored — motor is already idle
// -----------------------------------------------------------------------
// Asynchronous Motor — active-object pattern.
// SetSpeed/Halt post to the SM thread and return immediately.
// All state logic, guards, entry/exit, and signals fire on that thread.
// No mutex needed inside the SM — structural thread safety via dispatch.
// -----------------------------------------------------------------------
cout << "\n=== Asynchronous Motor (Active Object) ===" << endl;
dmq::os::Thread smThread("MotorSMThread");
smThread.CreateThread();
Motor asyncMotor;
asyncMotor.SetThread(smThread);
auto asyncConn = asyncMotor.OnTransition.Connect(
MakeDelegate([](uint8_t from, uint8_t to) {
// Fires on smThread — output may interleave with main thread cout
cout << " [async transition " << (int)from << " -> " << (int)to << "]" << endl;
}));
auto a1 = xmake_shared<MotorData>(); a1->speed = 100;
cout << "Posting SetSpeed(100)..." << endl;
asyncMotor.SetSpeed(a1); // returns immediately; SM thread processes asynchronously
auto a2 = xmake_shared<MotorData>(); a2->speed = 200;
cout << "Posting SetSpeed(200)..." << endl;
asyncMotor.SetSpeed(a2);
cout << "Posting Halt()..." << endl;
asyncMotor.Halt();
cout << "Posting Halt() again (will be ignored)..." << endl;
asyncMotor.Halt();
// Drain the queue and stop the SM thread before asyncMotor goes out of scope.
// Without this the SM thread may still be processing events while the Motor
// destructor runs, causing a use-after-free.
smThread.ExitThread();
// -----------------------------------------------------------------------
// Player and CentrifugeTest (synchronous)
// -----------------------------------------------------------------------
cout << "\n=== Player ===" << endl;
Player player;
player.OpenClose();
player.OpenClose();
player.Play();
player.Pause();
player.EndPause();
player.Stop();
player.Play();
player.Play();
player.OpenClose();
cout << "\n=== CentrifugeTest ===" << endl;
CentrifugeTest test;
std::promise<void> testDone;
auto testDoneConn = test.OnComplete.Connect(
MakeDelegate([&testDone]() {
testDone.set_value();
}));
test.Cancel();
test.Start();
testDone.get_future().get(); // blocks until OnComplete fires on SM thread
// -----------------------------------------------------------------------
// TcpConnection (asynchronous)
// -----------------------------------------------------------------------
cout << "\n=== TcpConnection (Active Object) ===" << endl;
TcpConnection tcp;
auto tcpConn = tcp.OnTransition.Connect(
MakeDelegate([](uint8_t from, uint8_t to) {
cout << " [TCP transition " << (int)from << " -> " << (int)to << "]" << endl;
}));
cout << "Initiating Active Open..." << endl;
tcp.ActiveOpen();
// Simulate network packets
auto synAck = xmake_shared<TcpData>(); synAck->syn = true; synAck->ack = true;
cout << "Receiving SYN+ACK packet..." << endl;
tcp.HandlePacket(synAck);
cout << "Initiating Close..." << endl;
tcp.Close();
auto fin = xmake_shared<TcpData>(); fin->fin = true;
cout << "Receiving FIN packet..." << endl;
tcp.HandlePacket(fin);
auto ack = xmake_shared<TcpData>(); ack->ack = true;
cout << "Receiving ACK packet..." << endl;
tcp.HandlePacket(ack);
// Give some time for async transitions to finish
std::this_thread::sleep_for(std::chrono::milliseconds(100));
// -----------------------------------------------------------------------
// AlarmPanel — HSM example
// Demonstrates PROPAGATE_TO_PARENT and parent entry/exit actions.
// -----------------------------------------------------------------------
cout << "\n=== AlarmPanel (HSM) ===" << endl;
AlarmPanel alarm;
// Arm in home mode: DISARMED → ARMED (entry) → ARMED_HOME (entry) → action
cout << "\n-- ArmHome --" << endl;
alarm.ArmHome();
// Toggle between home and away: sibling transition, ARMED entry/exit not repeated
cout << "\n-- Toggle (home -> away) --" << endl;
alarm.Toggle();
cout << "\n-- Toggle (away -> home) --" << endl;
alarm.Toggle();
// Trigger from ARMED_HOME: propagates to ARMED → ALARMING
// Exit sequence: ARMED_HOME (no exit), ARMED (ExitArmed)
cout << "\n-- Trigger zone 3 (from ARMED_HOME, propagates to ARMED) --" << endl;
auto trigData = xmake_shared<TriggerData>(); trigData->zone = 3;
alarm.Trigger(trigData);
// Disarm from ALARMING: direct transition
cout << "\n-- Disarm (from ALARMING) --" << endl;
alarm.Disarm();
// Arm away, then disarm: ARMED_AWAY propagates Disarm to ARMED
cout << "\n-- ArmAway then Disarm (propagates through ARMED) --" << endl;
alarm.ArmAway();
cout << "\n-- Disarm (from ARMED_AWAY, propagates to ARMED) --" << endl;
alarm.Disarm();
processTimerExit = true;
timerThread.join();
RunStateMachineTests();
RunStateMachineHSMTests();
return 0;
}