OOSMOS Control Example
1 Introduction
2 Classes
2.1 Control
2.1.1 control Requirements
2.1.2 control Statechart
2.1.3 control Code
2.2 Pump
2.2.1 pump Requirements
2.2.2 pump Statechart
2.2.3 pump Code
2.3 Motor
2.3.1 motor Requirements
2.3.2 motor Statechart
2.3.3 motor Code
2.4 Key
2.4.1 key Requirements
2.4.2 key Statechart
2.4.3 key Code
2.5 Main
3 Execution Diagram
1 Introduction
2 Classes
2.1 Control
2.1.1 control Require...
2.1.2 control Statech...
2.1.3 control Code
2.2 Pump
2.2.1 pump Requiremen...
2.2.2 pump Statechart
2.2.3 pump Code
2.3 Motor
2.3.1 motor Requireme...
2.3.2 motor Statechar...
2.3.3 motor Code
2.4 Key
2.4.1 key Requirement...
2.4.2 key Statechart
2.4.3 key Code
2.5 Main
3 Execution Diagr...
1 Introduction
2 Classes
2.1 Control
2.1.1 control Requ...
2.1.2 control Stat...
2.1.3 control Code
2.2 Pump
2.2.1 pump Require...
2.2.2 pump Statech...
2.2.3 pump Code
2.3 Motor
2.3.1 motor Requir...
2.3.2 motor Statec...
2.3.3 motor Code
2.4 Key
2.4.1 key Requirem...
2.4.2 key Statecha...
2.4.3 key Code
2.5 Main
3 Execution Di...

1 Introduction

This example demonstrates:
  • The use of "and" states (AKA "orthogonal" states).
  • The use of events, including Publish/Subscribe events.
  • Both the Active and State Machine types of OOSMOS objects.
  • Execution on the Windows platform. It requires no external hardware to run and you can use source level debugging to step through the code which will help you learn OOSMOS quicker.
EventDemo is an implementation of a controller for a simulated pump and a motor. The pump and motor objects are independent and can be started and stopped individually by a control object. Events are triggered by pressing and releasing keys on the Windows PC keyboard which are detected and announced by key objects. The speed of the pump can be adjusted by up and down key events.
This example shows a nifty trick for setting options. Options can only be adjusted when both motor and pump are off. We guarantee this by requiring the 's' key to be depressed while the option is being set. (By pressing the '1' or '2' key.) When 's' is pressed, both the motor and pump are stopped. (See the statechart, below.)
It is Windows-specific because the key class uses OS calls to react to keystrokes, turning them into OOSMOS events.
You can build the example from the command line with any of Microsoft's compilers. We use the free Visual C++ 2008 Express Edition. Once you have the command line compiler installed, building it is simple. Open a command line window that has the necessary environment variables set up to access the CL compiler. Locate and run bld.py to compile and link the example. Once built, you can run control.exe.

2 Classes

This application is made up of four principle classes: control, pump, motor, and key. This section details each object, including its requirements, statechart and code.

2.1 Control

control is the main orchestration object that reacts to keystroke events and directs the pump and motor objects.
Refer to control's statechart in Figure 1.

2.1.1 control Requirements

  • The 'm' key acts as a toggle. If the motor is Off when this key is pressed, the motor will turn On. If the motor is On when this key is pressed, the motor will turn Off.
  • The 'p' key acts as a toggle. If the pump is Off when this key is pressed, the pump will turn On. If the pump is off when this key is pressed, the pump will turn Off.
  • If the 's' key is pressed, the Operational state is exited (also exiting any nested states). When the 's' key is released, the Operational state is entered, and then the MotionControl/Idle and PumpControl/Idle states will be entered.
  • If the '1' key is pressed while the 's' key is depressed, toggle option 1.
  • If the '2' key is pressed while the 's' key is depressed, toggle option 2.

2.1.2 control Statechart

Figure 1. control State Machine

2.1.3 control Code

1
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
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
[GPLv2]
 
#include <stdlib.h>
#include <stdio.h>
 
#include "oosmos.h"
#include "control.h"
#include "key.h"
#include "motor.h"
#include "pump.h"
 
typedef enum
{
StopPressedEvent = 1,
StopReleasedEvent,
MovePressedEvent,
PumpPressedEvent,
Option1PressedEvent,
Option2PressedEvent,
QuitPressedEvent,
} eEvents;
 
struct controlTag
{
motor * m_pMotor;
pump * m_pPump;
bool m_Option1;
bool m_Option2;
 
oosmos_sStateMachine (StateMachine, oosmos_sEvent, 3);
oosmos_sLeaf StartingUp_State;
oosmos_sOrtho Operational_State;
oosmos_sOrthoRegion Operational_MotorControl_State;
oosmos_sLeaf Operational_MotorControl_Idle_State;
oosmos_sLeaf Operational_MotorControl_Moving_State;
oosmos_sOrthoRegion Operational_PumpControl_State;
oosmos_sLeaf Operational_PumpControl_Idle_State;
oosmos_sLeaf Operational_PumpControl_Pumping_State;
oosmos_sLeaf StopPressed_State;
oosmos_sLeaf Termination_State;
};
 
#ifdef oosmos_DEBUG
#define NameCase(Name) case Name: return #Name;
 
static const char * EventNames(int EventCode)
{
switch (EventCode) {
NameCase(StopPressedEvent)
NameCase(StopReleasedEvent)
NameCase(MovePressedEvent)
NameCase(PumpPressedEvent)
NameCase(QuitPressedEvent)
NameCase(Option1PressedEvent)
NameCase(Option2PressedEvent)
default: return "--No Event Name--";
}
}
#endif
 
static void ToggleOption1(control * pControl)
{
pControl->m_Option1 = !(pControl->m_Option1);
}
 
static void ToggleOption2(control * pControl)
{
pControl->m_Option2 = !(pControl->m_Option2);
}
 
static bool StartingUp_State_Code(void * pObject, oosmos_sRegion * pRegion, const oosmos_sEvent * pEvent)
{
control * pControl = (control *) pObject;
 
switch (pEvent->Code) {
case oosmos_ENTER:
return oosmos_StateTimeoutSeconds(pRegion, 1);
case oosmos_TIMEOUT:
return oosmos_Transition(pRegion, &pControl->Operational_State);
}
 
return false;
}
 
static bool Operational_State_Code(void * pObject, oosmos_sRegion * pRegion, const oosmos_sEvent * pEvent)
{
control * pControl = (control *) pObject;
 
switch (pEvent->Code) {
case StopPressedEvent:
return oosmos_Transition(pRegion, &pControl->StopPressed_State);
case QuitPressedEvent:
return oosmos_Transition(pRegion, &pControl->Termination_State);
}
 
return false;
}
 
static bool Operational_MotorControl_Idle_State_Code(void * pObject, oosmos_sRegion * pRegion, const oosmos_sEvent * pEvent)
{
control * pControl = (control *) pObject;
 
switch (pEvent->Code) {
case MovePressedEvent:
return oosmos_Transition(pRegion, &pControl->Operational_MotorControl_Moving_State);
}
 
return false;
}
 
static bool Operational_MotorControl_Moving_State_Code(void * pObject, oosmos_sRegion * pRegion, const oosmos_sEvent * pEvent)
{
control * pControl = (control *) pObject;
 
switch (pEvent->Code) {
case oosmos_ENTER:
motorOn(pControl->m_pMotor);
return true;
case oosmos_EXIT:
motorOff(pControl->m_pMotor);
return true;
case MovePressedEvent:
return oosmos_Transition(pRegion, &pControl->Operational_MotorControl_State);
}
 
return false;
}
 
static bool Operational_PumpControl_Idle_State_Code(void * pObject, oosmos_sRegion * pRegion, const oosmos_sEvent * pEvent)
{
control * pControl = (control *) pObject;
 
switch (pEvent->Code) {
case PumpPressedEvent:
return oosmos_Transition(pRegion, &pControl->Operational_PumpControl_Pumping_State);
}
 
return false;
}
 
static bool Operational_PumpControl_Pumping_State_Code(void * pObject, oosmos_sRegion * pRegion, const oosmos_sEvent * pEvent)
{
control * pControl = (control *) pObject;
 
switch (pEvent->Code) {
case oosmos_ENTER:
pumpOn(pControl->m_pPump);
return true;
case oosmos_EXIT:
pumpOff(pControl->m_pPump);
return true;
case PumpPressedEvent:
return oosmos_Transition(pRegion, &pControl->Operational_PumpControl_State);
}
 
return false;
}
 
static bool StopPressed_State_Code(void * pObject, oosmos_sRegion * pRegion, const oosmos_sEvent * pEvent)
{
control * pControl = (control *) pObject;
 
switch (pEvent->Code) {
case StopReleasedEvent:
return oosmos_Transition(pRegion, &pControl->Operational_State);
case Option1PressedEvent:
ToggleOption1(pControl);
 
printf("control: Option1: %s\n", pControl->m_Option1 ? "true" : "false");
return true;
case Option2PressedEvent:
ToggleOption2(pControl);
 
printf("control: Option2: %s\n", pControl->m_Option2 ? "true" : "false");
return true;
}
 
return false;
}
 
static bool Termination_State_Code(void * pObject, oosmos_sRegion * pRegion, const oosmos_sEvent * pEvent)
{
switch (pEvent->Code) {
case oosmos_ENTER:
exit(1);
}
 
return false;
}
 
extern control * controlNew(void)
{
key * pStopKey;
key * pMoveKey;
key * pQuitKey;
 
key * pPumpKey;
key * pUpKey;
key * pDownKey;
key * pOption1Key;
key * pOption2Key;
 
oosmos_Allocate(pControl, control, 1, NULL);
 
pStopKey = keyNew('s');
pMoveKey = keyNew('m');
pQuitKey = keyNew('q');
 
pPumpKey = keyNew('p');
pUpKey = keyNew('u');
pDownKey = keyNew('d');
pOption1Key = keyNew('1');
pOption2Key = keyNew('2');
 
{
oosmos_sQueue * const pControlEventQueue = &pControl->EventQueue;
 
keySubscribePressedEvent(pStopKey, pControlEventQueue, StopPressedEvent);
keySubscribeReleasedEvent(pStopKey, pControlEventQueue, StopReleasedEvent);
keySubscribePressedEvent(pMoveKey, pControlEventQueue, MovePressedEvent);
keySubscribePressedEvent(pPumpKey, pControlEventQueue, PumpPressedEvent);
keySubscribePressedEvent(pQuitKey, pControlEventQueue, QuitPressedEvent);
keySubscribePressedEvent(pOption1Key, pControlEventQueue, Option1PressedEvent);
keySubscribePressedEvent(pOption2Key, pControlEventQueue, Option2PressedEvent);
}
 
pControl->m_pMotor = motorNew();
pControl->m_pPump = pumpNew(pUpKey, pDownKey);
pControl->m_Option1 = false;
pControl->m_Option2 = false;
 
// StateName Parent Default
// ======================================================================================================================
oosmos_StateMachineInit (pControl, StateMachine, NULL, StartingUp_State );
oosmos_LeafInit (pControl, StartingUp_State, StateMachine );
oosmos_OrthoInit (pControl, Operational_State, StateMachine );
oosmos_OrthoRegionInitNoCode(pControl, Operational_MotorControl_State, Operational_State, Operational_MotorControl_Idle_State);
oosmos_LeafInit (pControl, Operational_MotorControl_Idle_State, Operational_MotorControl_State );
oosmos_LeafInit (pControl, Operational_MotorControl_Moving_State, Operational_MotorControl_State );
oosmos_OrthoRegionInitNoCode(pControl, Operational_PumpControl_State, Operational_State, Operational_PumpControl_Idle_State );
oosmos_LeafInit (pControl, Operational_PumpControl_Idle_State, Operational_PumpControl_State );
oosmos_LeafInit (pControl, Operational_PumpControl_Pumping_State, Operational_PumpControl_State );
oosmos_LeafInit (pControl, StopPressed_State, StateMachine );
oosmos_LeafInit (pControl, Termination_State, StateMachine );
 
oosmos_DebugCode(
oosmos_Debug(&pControl->StateMachine, true, EventNames);
)
 
return pControl;
}
control.c

2.2 Pump

The pump object controls a pumping device that can be started and stopped by a controller but it can also react to keystrokes to pump faster or slower.

2.2.1 pump Requirements

  • The pump object shall startup in an idle, non-pumping state.
  • The pump object shall implement pumpOn which will turn the pump on.
  • The pump object shall implement pumpOff which will turn the pump off.
  • If the 'u' is pressed when the pump is On, the speed of the pump will increase.
  • If the 'u' key is pressed when the pump is Off, no speed adjustment is made.
  • If the 'd' is pressed when the pump is On, the speed of the pump will decrease.
  • If the 'd' key is pressed when the pump is Off, no speed adjustment is made.
  • If the pump is pumping, the message "Pumping..." shall be displayed more often if the pump speed is high and less often if the pump speed is low.

2.2.2 pump Statechart

Figure 2. pump State Machine

2.2.3 pump Code

1
22
23
24
25
26
27
28
29
30
31
32
33
34
[GPLv2]
 
#ifndef _pump_h
#define _pump_h
 
#include "key.h"
 
typedef struct pumpTag pump;
 
extern pump * pumpNew(key * pUpKey, key * pDownKey);
extern void pumpOn(pump * pPump);
extern void pumpOff(pump * pPump);
 
#endif
pump.h
1
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
[GPLv2]
 
#include <stddef.h>
#include <stdio.h>
#include "oosmos.h"
#include "pump.h"
 
typedef enum
{
StartEvent = 1,
StopEvent,
 
UpPressedEvent,
DownPressedEvent,
} eEvents;
 
struct pumpTag
{
int PumpSpeed;
 
oosmos_sStateMachine(StateMachine, oosmos_sEvent, 3);
oosmos_sLeaf Idle_State;
oosmos_sLeaf Pumping_State;
};
 
#ifndef pumpMAX
#define pumpMAX 3
#endif
 
#ifdef oosmos_DEBUG
#define NameCase(Name) case Name: return #Name;
 
static const char * EventNames(int EventCode)
{
switch (EventCode) {
NameCase(StartEvent)
NameCase(StopEvent)
NameCase(UpPressedEvent)
NameCase(DownPressedEvent)
default: return "--No Event Name--";
}
}
#endif
 
static bool Idle_State_Code(void * pObject, oosmos_sRegion * pRegion, const oosmos_sEvent * pEvent)
{
pump * pPump = (pump *) pObject;
 
switch (pEvent->Code) {
case StartEvent:
return oosmos_Transition(pRegion, &pPump->Pumping_State);
}
 
return false;
}
 
static bool Pumping_State_Code(void * pObject, oosmos_sRegion * pRegion, const oosmos_sEvent * pEvent)
{
pump * pPump = (pump *) pObject;
 
switch (pEvent->Code) {
case StopEvent:
return oosmos_Transition(pRegion, &pPump->Idle_State);
 
case UpPressedEvent:
pPump->PumpSpeed = oosmos_Min(10, pPump->PumpSpeed+1);
printf("pump: PUMPING SPEED %d...\n", pPump->PumpSpeed);
return true;
 
case DownPressedEvent:
pPump->PumpSpeed = oosmos_Max(1, pPump->PumpSpeed-1);
printf("pump: PUMPING SPEED %d...\n", pPump->PumpSpeed);
return true;
 
case oosmos_INSTATE:
oosmos_AyncBegin(pRegion);
while (true) {
printf("pump: PUMPING...\n");
oosmos_AyncDelayMS(pRegion, (10-pPump->PumpSpeed) * 200);
}
oosmos_AyncEnd(pRegion);
return true;
}
 
return false;
}
 
extern pump * pumpNew(key * pUpKey, key * pDownKey)
{
oosmos_Allocate(pPump, pump, pumpMAX, NULL);
 
pPump->PumpSpeed = 5;
 
keySubscribePressedEvent(pUpKey, &pPump->EventQueue, UpPressedEvent);
keySubscribePressedEvent(pDownKey, &pPump->EventQueue, DownPressedEvent);
 
// StateName Parent Default
// ================================================
oosmos_StateMachineInit(pPump, StateMachine, NULL, Idle_State);
oosmos_LeafInit (pPump, Idle_State, StateMachine );
oosmos_LeafInit (pPump, Pumping_State, StateMachine );
 
oosmos_DebugCode(
oosmos_Debug(&pPump->StateMachine, true, EventNames);
)
 
return pPump;
}
 
extern void pumpOn(pump * pMotor)
{
oosmos_SendEvent(pMotor, StartEvent);
}
 
extern void pumpOff(pump * pMotor)
{
oosmos_SendEvent(pMotor, StopEvent);
}
pump.c

2.3 Motor

The motor object simulates a motor that can turned On or Off.

2.3.1 motor Requirements

  • The motor object shall startup in an idle, motionless state.
  • The motor object shall implement motorOn which will turn the motor on.
  • The motor object shall implement motorOff which will turn the motor off.
  • Any time the motor is moving, the message "Moving..." shall be displayed every 2 seconds.

2.3.2 motor Statechart

Figure 3. motor State Machine

2.3.3 motor Code

1
22
23
24
25
26
27
28
29
30
31
32
[GPLv2]
 
#ifndef _motor_h
#define _motor_h
 
typedef struct motorTag motor;
 
extern motor * motorNew(void);
extern void motorOn(motor * pMotor);
extern void motorOff(motor * pMotor);
 
#endif
motor.h
1
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
[GPLv2]
 
#include <stdio.h>
#include "oosmos.h"
#include "motor.h"
 
typedef enum
{
StartEvent = 1,
StopEvent,
} eEvents;
 
struct motorTag
{
oosmos_sStateMachine(StateMachine, oosmos_sEvent, 3);
oosmos_sLeaf Idle_State;
oosmos_sLeaf Moving_State;
};
 
#ifndef motorMAX
#define motorMAX 3
#endif
 
#ifdef oosmos_DEBUG
#define NameCase(Name) case Name: return #Name;
 
static const char * EventNames(int EventCode)
{
switch (EventCode) {
NameCase(StartEvent)
NameCase(StopEvent)
default: return "--No Event Name--";
}
}
#endif
 
static bool Idle_State_Code(void * pObject, oosmos_sRegion * pRegion, const oosmos_sEvent * pEvent)
{
motor * pMotor = (motor *) pObject;
 
switch (pEvent->Code) {
case StartEvent:
return oosmos_Transition(pRegion, &pMotor->Moving_State);
}
 
return false;
}
 
static bool Moving_State_Code(void * pObject, oosmos_sRegion * pRegion, const oosmos_sEvent * pEvent)
{
motor * pMotor = (motor *) pObject;
 
switch (pEvent->Code) {
case oosmos_INSTATE:
oosmos_AyncBegin(pRegion);
while (true) {
printf("motor: MOVING...\n");
oosmos_AyncDelayMS(pRegion, 500);
}
oosmos_AyncEnd(pRegion);
return true;
 
case StopEvent:
return oosmos_Transition(pRegion, &pMotor->Idle_State);
}
 
return false;
}
 
extern motor * motorNew(void)
{
oosmos_Allocate(pMotor, motor, motorMAX, NULL);
 
// StateName Parent Default
// ================================================
oosmos_StateMachineInit(pMotor, StateMachine, NULL, Idle_State);
oosmos_LeafInit (pMotor, Idle_State, StateMachine );
oosmos_LeafInit (pMotor, Moving_State, StateMachine );
 
oosmos_DebugCode(
oosmos_Debug(&pMotor->StateMachine, true, EventNames);
)
 
return pMotor;
}
 
extern void motorOn(motor * pMotor)
{
oosmos_SendEvent(pMotor, StartEvent);
}
 
extern void motorOff(motor * pMotor)
{
oosmos_SendEvent(pMotor, StopEvent);
}
motor.c

2.4 Key

A key object looks for and reacts to a single, specified keystroke. For example, newKey('u') creates an object that will react to the pressing and releasing of the u key only. An application will create a key object for every key that it finds interesting.

2.4.1 key Requirements

  • The key object shall allow users to subscribe to pressed and released keystroke events for a specified key. The subscriber specifies the event codes to publish.
  • When the specified key is pressed or released, the key object will post the corresponding event code to event queue of the subscriber.

2.4.2 key Statechart

Figure 4. key State Machine

2.4.3 key Code

1
22
23
24
25
26
27
28
29
30
31
32
33
34
35
[GPLv2]
 
#ifndef key_h
#define key_h
 
#include "oosmos.h"
 
typedef struct keyTag key;
 
extern key * keyNew(char Char);
 
extern void keySubscribePressedEvent(key * pKey, oosmos_sQueue * pQueue, int PressedEventCode);
extern void keySubscribeReleasedEvent(key * pKey, oosmos_sQueue * pQueue, int ReleasedEventCode);
 
#endif
key.h
1
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
[GPLv2]
 
#include <windows.h>
 
#include <stdio.h>
#include "oosmos.h"
#include "key.h"
 
typedef enum {
Up_State = 1,
Down_State,
} eStates;
 
struct keyTag
{
char m_Char;
eStates m_State;
 
oosmos_sActiveObject m_ActiveObject;
 
oosmos_sSubscriberList m_PressedEvent[1];
oosmos_sSubscriberList m_ReleasedEvent[1];
};
 
#ifndef keyMAX
#define keyMAX 10
#endif
 
static HANDLE hStdin;
 
//
// Poll the console for this object's character.
//
// Peek at the buffer. If it does not contain this object's character, return.
//
// If the buffer does contain this object's character, determine if the key is up
// or down and store it in the by-reference pKeyState argument and clear the
// buffer.
//
static bool IsMyChar(key * pKey, eStates * pKeyState)
{
DWORD NumRead;
INPUT_RECORD irInBuf[128];
DWORD I;
 
if (PeekConsoleInput(hStdin, irInBuf, 128, &NumRead) && NumRead == 0)
return false;
 
for (I = 0; I < NumRead; I++) {
KEY_EVENT_RECORD KER = irInBuf[I].Event.KeyEvent;
 
if (KER.uChar.AsciiChar != pKey->m_Char)
continue;
 
*pKeyState = KER.bKeyDown ? Down_State : Up_State;
 
ReadConsoleInput(hStdin, irInBuf, 128, &NumRead);
return true;
}
 
//
// Purge accumulated unrecognized characters.
//
if (NumRead > 10)
ReadConsoleInput(hStdin, irInBuf, 128, &NumRead);
 
return false;
}
 
static void StateMachine(void * pObject)
{
key * pKey = (key *) pObject;
eStates KeyState;
 
switch (pKey->m_State) {
case Up_State:
if (!IsMyChar(pKey, &KeyState))
return;
 
if (KeyState == Down_State) {
printf("key: ****************** %c pressed\n", pKey->m_Char);
oosmos_SubscriberListNotify(pKey->m_PressedEvent);
pKey->m_State = Down_State;
}
 
break;
 
case Down_State:
if (!IsMyChar(pKey, &KeyState))
return;
 
if (KeyState == Up_State) {
printf("key: ****************** %c released\n", pKey->m_Char);
oosmos_SubscriberListNotify(pKey->m_ReleasedEvent);
pKey->m_State = Up_State;
}
 
break;
}
}
 
extern key * keyNew(char Char)
{
static bool Initialized = false;
 
oosmos_Allocate(pKey, key, keyMAX, NULL);
 
if (!Initialized) {
hStdin = GetStdHandle(STD_INPUT_HANDLE);
Initialized = true;
}
 
oosmos_RegisterActiveObject(pKey, StateMachine, &pKey->m_ActiveObject);
 
pKey->m_Char = Char;
pKey->m_State = Up_State;
 
oosmos_SubscriberListInit(pKey->m_PressedEvent);
oosmos_SubscriberListInit(pKey->m_ReleasedEvent);
 
return pKey;
}
 
extern void keySubscribePressedEvent(key * pKey, oosmos_sQueue * pQueue, int PressedEventCode)
{
oosmos_SubscriberListAdd(pKey->m_PressedEvent, pQueue, PressedEventCode, NULL);
}
 
extern void keySubscribeReleasedEvent(key * pKey, oosmos_sQueue * pQueue, int ReleasedEventCode)
{
oosmos_SubscriberListAdd(pKey->m_ReleasedEvent, pQueue, ReleasedEventCode, NULL);
}
 
key.c

2.5 Main

Because this application runs on Windows, we don't want to consume 100% of the CPU, so we do a delay between calls to oosmos_RunStateMachines. This delay makes sure that we only take small sips from the processor, but it is often enough that we are sufficiently reactive to keystrokes.
1
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
[GPLv2]
 
#include "oosmos.h"
#include "control.h"
 
extern int main(void)
{
controlNew();
 
while (true) {
oosmos_RunStateMachines();
oosmos_DelayMS(50);
}
 
return 0;
}
main.c

3 Execution Diagram

Figure 5 depicts the running application, with instantiated objects and indications of events traveling from their publishers to their subscribers.
Note that there are two event models here. Events that flow from one object to another are publish/subscribe events. The events that flow from the pump object to itself and from the motor object to itself are examples of a method enqueuing work to itself, effectively implementing an asynchronous call.
Figure 5. Event Flow
Copyright © 2014-2016  OOSMOS, LLC