-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.cpp
More file actions
160 lines (137 loc) · 5.01 KB
/
main.cpp
File metadata and controls
160 lines (137 loc) · 5.01 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
/**
* @file main.cpp
* @brief Self-Test Engine Example Entry Point utilizing Signal-Slot architecture.
*
* @details
* This example demonstrates a robust, multi-threaded architecture for a
* "Self-Test Engine" using the DelegateMQ library and an Asynchronous State Machine.
*
* **System Architecture:**
* - **SelfTestEngine (Master):** A Singleton State Machine running on its own
* dedicated worker thread. It coordinates the execution of sub-tests.
* - **Sub-Tests:** Independent State Machines (e.g., CentrifugeTest) that inherit
* from a common AsyncStateMachine-based SelfTest class.
* - **User Interface:** A simulated UI thread that receives status updates
* asynchronously via Signals.
*
* **Key DelegateMQ & Signal-Slot Features:**
* 1. **Thread-Safe Signals (SignalPtr):**
* - Uses dmq::SignalPtr to provide a publisher/subscriber mechanism for
* task completion and status updates.
* 2. **RAII Connection Management:**
* - Uses dmq::ScopedConnection to manage signal lifetimes. Connections are
* automatically severed when the ScopedConnection object is destroyed.
* 3. **Asynchronous Marshaling:**
* - MakeDelegate() transparently marshals callbacks from worker threads to
* the userInterfaceThread, ensuring thread-safe UI updates.
* 4. **Asynchronous Invocation:**
* - Utilizes ASYNC_INVOKE to ensure state transitions occur on the
* appropriate thread of control.
*
* @see https://github.com/endurodave/cpp-signal-slot-fsm
* David Lafreniere
*/
#include "DelegateMQ.h"
#include "SelfTestEngine.h"
#include <iostream>
#include "DataTypes.h"
#include "Motor.h"
#include <atomic>
#include <chrono>
// @see https://github.com/endurodave/cpp-signal-slot-fsm
// David Lafreniere
using namespace std;
using namespace dmq;
// A thread to capture self-test status callbacks for output to the "user interface"
Thread userInterfaceThread("UserInterface");
// Simple flag to exit main loop (Atomic for thread safety)
std::atomic<bool> selfTestEngineCompleted(false);
std::atomic<bool> processTimerExit(false);
static void ProcessTimers()
{
while (!processTimerExit.load())
{
// Process all delegate-based timers
Timer::ProcessTimers();
std::this_thread::sleep_for(std::chrono::microseconds(50));
}
}
//------------------------------------------------------------------------------
// OnSelfTestEngineStatus
//------------------------------------------------------------------------------
void OnSelfTestEngineStatus(const SelfTestStatus& status)
{
// Output status message to the console "user interface"
cout << status.message.c_str() << endl;
}
//------------------------------------------------------------------------------
// OnSelfTestEngineComplete
//------------------------------------------------------------------------------
void OnSelfTestEngineComplete()
{
selfTestEngineCompleted = true;
}
//------------------------------------------------------------------------------
// main
//------------------------------------------------------------------------------
int main(void)
{
// Start the thread that will run ProcessTimers
std::thread timerThread(ProcessTimers);
try
{
// Create the worker thread
userInterfaceThread.CreateThread();
// *** Begin async Motor test ***
Motor motor;
auto data = new MotorData();
data->speed = 100;
motor.SetSpeed(data);
data = new MotorData();
data->speed = 200;
motor.SetSpeed(data);
motor.Halt();
// *** End async Motor test ***
// *** Begin async self test ***
// -------------------------------------------------------------------------
// CONNECT SIGNALS (RAII)
// -------------------------------------------------------------------------
// We must store the connection handles!
// If these fall out of scope, they automatically disconnect.
ScopedConnection statusConn;
ScopedConnection completeConn;
// Register for status updates (Static Signal)
statusConn = SelfTestEngine::OnStatus.Connect(
MakeDelegate(&OnSelfTestEngineStatus, userInterfaceThread)
);
// Register for completion (Instance Signal from base class)
completeConn = SelfTestEngine::GetInstance().OnCompleted.Connect(
MakeDelegate(&OnSelfTestEngineComplete, userInterfaceThread)
);
// Start self-test engine
StartData startData;
startData.shortSelfTest = TRUE;
SelfTestEngine::GetInstance().Start(&startData);
// Wait for self-test engine to complete
while (!selfTestEngineCompleted)
std::this_thread::sleep_for(std::chrono::milliseconds(10));
// -------------------------------------------------------------------------
// DISCONNECT
// -------------------------------------------------------------------------
// Explicitly disconnect (optional, as destructors handle this automatically)
statusConn.Disconnect();
completeConn.Disconnect();
// *** End async self test **
// Exit the worker thread
userInterfaceThread.ExitThread();
}
catch (...)
{
std::cerr << "Exception!" << std::endl;
}
// Ensure the timer thread completes before main exits
processTimerExit.store(true);
if (timerThread.joinable())
timerThread.join();
return 0;
}