OOSMOSjs - Hierarchical State Machine Class for JavaScript
1. Introduction
2. Simple Timeout
3. Nested Timeout
4. jQuery Example

1. Introduction

OOSMOS for JavaScript (which is written in TypeScript and transpiled to JavaScript) is an open source, easy-to-use hierarchical state machine class that can be used with an environment that has a module loader, e.g. node.js, or a browser.
NOTE: OOSMOS stands for Object Oriented State Machine Operating System. OOSMOS is a small footprint C/C++ state machine based operating system targeting the industrial automation space. (See oosmos.com.) This JavaScript implementation borrows from the Object-Oriented and State Machine elements of OOSMOS but the Operating System elements are supported by the JavaScript runtime.

1.1 Features

  • Very readable encapsulated state machine structure.
  • Simple -- less than 400 lines of code.
  • Same code runs in a browser or Node.js.
  • Supports arbitrarily deep hierarchical state machines.
  • Supports state local variables. (Ideal for caching jQuery elements.)
  • Simple API: Only 5 principal APIs.
  • Can run multiple state machines concurrently.
  • Event functions can pass arguments to the ENTER function via the Transition API.

1.2 Source Code

The source code is available on GitHub.
Link: OOSMOSjs on GitHub.

1.3 Live Demos

There are three live demos on this page:
  1. Simple Timeout - An application that toggles between two states based on timeouts.
  2. Nested Timeout - Is similar to the Simple Timeout demo, but demonstrates the use of more than one timeout active at a time.
  3. jQuery - Demonstrates the use of OOSMOS to manage the display of application elements and user interaction.

2. Simple Timeout

This demo serves as a "hello world" of OOSMOS. It merely toggles between two states based on timeouts. It also shows how to enable state machine debugging.

2.1 Simple Timeout State Chart

Example 1. Simple Timeout

2.2 Simple Timeout Debug Output

2.3 Simple Timeout State Machine

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
import { StateMachine } from 'OOSMOS';
 
class SimpleTimeoutTest extends StateMachine {
constructor() {
super({ DEFAULT: 'A',
A: {
ENTER: function() {
this.Print("In state A");
this.SetTimeoutSeconds(4);
},
TIMEOUT: function() {
this.Transition('B');
},
},
 
B: {
ENTER: function() {
this.Print("In state B");
this.SetTimeoutSeconds(1);
},
TIMEOUT: function() {
this.Transition('A');
},
},
});
}
}
 
const pSimpleTimeoutTest = new SimpleTimeoutTest();
pSimpleTimeoutTest.SetDebug(true, 'debugSimpleTimeout');
pSimpleTimeoutTest.Start();
State Machine

3. Nested Timeout

This demonstrates two active timeouts running in nested states.

3.1 Nested Timeout State Chart

Two timeouts will be active at once. The timeout on state Inner will expire first, transitioning to state InnerTimeout.
Example 2. Nested Timeout

3.2 Nested Timeout Debug Output

3.3 Nested Timeout State Machine

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
import { StateMachine } from 'OOSMOS';
 
class NestedTimeoutTest extends StateMachine {
constructor() {
super({ DEFAULT: 'Outer',
Outer: {
ENTER: function() {
this.Print('In state Outer');
this.SetTimeoutSeconds(4);
},
TIMEOUT: function() {
this.Transition('OuterTimeout');
},
COMPOSITE: {
Inner: {
ENTER: function() {
this.Print('In state Outer.Inner');
this.SetTimeoutSeconds(2);
},
TIMEOUT: function() {
this.Transition('InnerTimeout');
},
},
},
},
 
OuterTimeout: {
ENTER: function() {
this.Print('In state OuterTimeout');
this.Assert(false);
},
},
 
InnerTimeout: {
ENTER: function() {
this.Print('In state InnerTimeout');
this.SetTimeoutSeconds(1);
},
TIMEOUT: function() {
this.Transition('Outer');
},
},
});
}
}
 
const pNestedTimeoutTest = new NestedTimeoutTest();
pNestedTimeoutTest.SetDebug(true, 'debugNestedTimeout');
pNestedTimeoutTest.Start();
State Machine

4. jQuery Example

With the advent of AJAX and Websockets, more and more of the logic of a web application can run in the browser. In fact, I recently developed a feature-rich messaging application that loads only one page of HTML and then, under control of a set of OOSMOS.js state machines, uses a Websockets connection to interact with the server exchanging snippets of JSON.
Here is how it works: Most of the HTML that is loaded is hidden from view using style="display:none" and then the state machine based JavaScript makes the appropriate elements visible based on user interaction. What follows is a simple live example. First, take a look at the state machine. We start out in the Idle state and then transition to the Active state when you press the eStart button which sends the eStart event.
Follow the rest of the logic by inspecting the state chart and the state machine.

4.1 jQuery State Chart

jQuery Example State Machine

4.2 jQuery HTML

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
<div style="float:left">
<div id="Idle" class="State" style="display:none">
IDLE
<button id="eStart">eStart</button>
</div>
 
<div id="Active" class="State" style="display:none">
ACTIVE
<button id="eStop">eStop</button>
<button id="eReset">eReset</button>
 
<div id="A" class="State" style="display:none">
A
<button id="eA2B">eA2B</button>
<button id="eA2BB">eA2BB</button>
<div id="AA" class="State" style="display:none">
AA
<button id="eAA2B">eAA2B</button>
<button id="eAA2BB">eAA2BB</button>
</div>
</div>
 
<div id="B" class="State" style="display:none">
B
<div id="BB" class="State" style="display:none">
BB
</div>
</div>
</div>
</div>
HTML Templates

4.3 jQuery Debug Output

4.4 jQuery State Machine

Things to note in the state machine below:
  • As we ENTER states, we show the corresponding <div> in the HTML. Conversely, we hide them in the EXIT. (See lines 9 and 17.)
  • We can set up jQuery event handlers inside the context where they apply and unbind them when we are done. (See lines 25-26 and 30-31.)
  • State local variables are created on lines 37-41 and 76-80 by replacing the object with a function that declares the state-local variables and then returns the object.
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
import { StateMachine } from 'OOSMOS';
import * as $ from 'jquery';
 
class jQueryDemo extends StateMachine {
constructor() {
super({ DEFAULT: 'Idle',
Idle: {
ENTER: function() {
$('#Idle').show();
 
$('#eStart').click(() => { this.Transition('Active'); });
},
 
EXIT: function() {
$('#eStart').unbind('click');
 
$('#Idle').hide();
},
},
 
Active: {
ENTER: function() {
$('#Active').show();
 
$('#eStop').click(() => { this.Transition('Idle'); });
$('#eReset').click(() => { this.Transition('Active'); });
},
 
EXIT: function() {
$('#eStop').unbind('click');
$('#eReset').unbind('click');
 
$('#Active').hide();
},
 
COMPOSITE: { DEFAULT: 'A',
A: function() {
const $A = $('#A');
const $AA = $('#AA');
 
return {
ENTER: function() {
$A.show();
 
$('#eA2B').click(() => { this.Transition('Active.B'); });
$('#eA2BB').click(() => { this.Transition('Active.B.BB'); });
},
 
EXIT: function() {
$('#eA2B').unbind('click');
$('#eA2BB').unbind('click');
 
$A.hide();
},
 
COMPOSITE: {
AA: {
ENTER: function() {
$AA.show();
 
$('#eAA2B').click(() => { this.Transition('Active.B'); });
$('#eAA2BB').click(() => { this.Transition('Active.B.BB'); });
},
 
EXIT: function() {
$('#eAA2B').unbind('click');
$('#eAA2BB').unbind('click');
 
$AA.hide();
},
},
},
};
},
 
B: function() {
const $B = $('#B');
const $BB = $('#BB');
 
return {
ENTER: function() {
$B.show();
},
 
EXIT: function() {
$B.hide();
},
 
COMPOSITE: {
BB: {
ENTER: function() {
$BB.show();
},
 
EXIT: function() {
$BB.hide();
},
},
},
};
},
},
},
});
}
}
 
const jQueryDemoObject = new jQueryDemo();
jQueryDemoObject.SetDebug(true, 'debug_jQuery');
jQueryDemoObject.Start();
State Machine
Copyright © 2014-2024  OOSMOS, LLC