Toggle navigation
Toggle navigation
This project
Loading...
Sign in
arcs
/
arcs.js
Go to a project
Toggle navigation
Toggle navigation pinning
Projects
Groups
Snippets
Help
Project
Activity
Repository
Pipelines
Graphs
Issues
3
Merge Requests
0
Wiki
Network
Create a new issue
Builds
Commits
Authored by
Jean-Yves Didier
2022-01-20 23:20:07 +0100
Browse Files
Options
Browse Files
Download
Email Patches
Plain Diff
Commit
b7e52a8525f3cad32fa7ea4a7c63f0a48c7d73be
b7e52a85
1 parent
9d248e7a
fixes on statemachine behaviour
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
182 additions
and
35 deletions
build/arcs.js
src/statemachine.js
src/transitionnetwork.js
test/unit.mjs
build/arcs.js
View file @
b7e52a8
This diff is collapsed. Click to expand it.
src/statemachine.js
View file @
b7e52a8
...
...
@@ -16,11 +16,17 @@ let StateMachine = Component.create(function (obj) {
// dynamic construction: properties are initial state that have properties
// that are tokens and value that are the final state
var
initial
=
""
,
final
=
""
,
transitions
=
{},
currentState
=
""
,
self
=
this
;
// this part is not good with astTokens
var
astTokens
=
{};
// it keeps AST from event logic expressions
var
tokenEvents
=
{};
// it keeps promises for tokens.
var
network
=
{};
let
resetTokens
=
function
()
{
Object
.
keys
(
tokenEvents
).
forEach
(
(
t
)
=>
tokenEvents
[
t
]
=
false
);
};
var
addToken
=
function
(
t
)
{
if
(
self
.
slots
.
indexOf
(
t
)
<
0
)
{
self
.
slot
(
t
,
function
(
s
)
{
...
...
@@ -35,12 +41,14 @@ let StateMachine = Component.create(function (obj) {
var
setSheet
=
function
(
s
)
{
// we build promise trees using ast
var
t
;
resetTokens
();
if
(
transitions
.
hasOwnProperty
(
s
))
{
tokenEvents
=
{};
for
(
t
in
transitions
[
s
])
{
if
(
transitions
[
s
].
hasOwnProperty
(
t
))
{
network
=
TransitionNetwork
.
build
(
astTokens
[
t
],
tokenEvents
).
promise
;
network
[
t
]
=
TransitionNetwork
.
build
(
astTokens
[
s
][
t
],
tokenEvents
);
/*network = TransitionNetwork.build(astTokens[s][t],tokenEvents).promise;
network.then(
function() {
var token;
...
...
@@ -51,7 +59,7 @@ let StateMachine = Component.create(function (obj) {
// then activate next sheet
setSheet(transitions[s][t]);
}
);
);
*/
}
}
}
...
...
@@ -97,7 +105,9 @@ let StateMachine = Component.create(function (obj) {
}
}
astTokens
[
token
]
=
tsd
;
// first fix on transitions
astTokens
[
start
]
=
astTokens
[
start
]
||
{};
astTokens
[
start
][
token
]
=
tsd
;
if
(
transitions
[
start
]
===
undefined
)
{
transitions
[
start
]
=
{};
...
...
@@ -112,8 +122,17 @@ let StateMachine = Component.create(function (obj) {
*/
this
.
setToken
=
function
(
token
)
{
if
(
tokenEvents
.
hasOwnProperty
(
token
))
{
tokenEvents
[
token
].
accept
();
//tokenEvents[token].accept();
tokenEvents
[
token
]
=
true
;
}
for
(
const
[
key
,
value
]
of
Object
.
entries
(
network
))
{
if
(
value
.
eval
())
{
network
=
{};
setSheet
(
transitions
[
currentState
][
key
]);
}
}
};
/**
* Sets transitions from a list of transitions
...
...
src/transitionnetwork.js
View file @
b7e52a8
/* the aim of the transition network is to build a network of promises */
import
TokenEvent
from
'./tokenevent.js'
;
const
Leaf
=
function
(
tokenEvents
,
nodeName
)
{
this
.
eval
=
function
()
{
return
tokenEvents
[
nodeName
];
};
};
const
OrNode
=
function
(
tokenEvents
,
left
,
right
)
{
this
.
eval
=
function
()
{
return
left
.
eval
(
tokenEvents
)
||
right
.
eval
(
tokenEvents
);
};
};
const
AndNode
=
function
(
tokenEvents
,
left
,
right
)
{
this
.
eval
=
function
(
tokenEvents
)
{
return
left
.
eval
(
tokenEvents
)
&&
left
.
eval
(
tokenEvents
);
};
};
let
TransitionNetwork
=
function
()
{
let
TransitionNetwork
=
{};
//let TransitionNetwork = function() {
// object storing token events (that is to say references to promises)
this
.
promise
=
{};
/*this.promise = {};
this.and = function(tn) {
this.promise = Promise.all([this.promise, tn.promise]);
...
...
@@ -14,16 +34,41 @@ let TransitionNetwork = function() {
this.or = function(tn) {
this.promise = Promise.race([this.promise, tn.promise]);
return this;
};
};
*/
};
//
};
TransitionNetwork
.
build
=
function
(
tree
,
tokenEvents
)
{
var
res
;
var
tmpTN
;
var
rightTN
;
if
(
typeof
tree
===
"string"
)
{
tokenEvents
[
tree
]
=
tokenEvents
[
tree
]
??
false
;
return
new
Leaf
(
tokenEvents
,
tree
);
}
res
=
TransitionNetwork
.
build
(
tree
[
0
],
tokenEvents
);
var
i
;
for
(
i
=
1
;
i
<
tree
.
length
;
i
++
)
{
if
(
tree
[
i
].
hasOwnProperty
(
'and'
))
{
rightTN
=
TransitionNetwork
.
build
(
tree
[
i
][
'and'
],
tokenEvents
);
tmpTN
=
new
AndNode
(
tokenEvents
,
res
,
rightTN
);
}
else
{
if
(
tree
[
i
].
hasOwnProperty
(
'or'
))
{
rightTN
=
TransitionNetwork
.
build
(
tree
[
i
][
'or'
],
tokenEvents
);
tmpTN
=
new
OrNode
(
tokenEvents
,
res
,
rightTN
);
}
else
{
console
.
warn
(
'[ARCS] Illegal tree'
);
}
}
res
=
tmpTN
;
}
/*if (typeof tree === "string") {
// here we have a terminal string i.e. a token event
var tokenEvent;
if (tokenEvents.hasOwnProperty(tree)) {
...
...
@@ -51,7 +96,7 @@ TransitionNetwork.build = function(tree, tokenEvents) {
}
}
res = tmpTN;
}
}
*/
return
res
;
};
...
...
test/unit.mjs
View file @
b7e52a8
...
...
@@ -7,7 +7,6 @@ let signalHandler = function(chai, utils) {
const MODULE = 'signalHandler';
const FLAG_EXPECTED_SIGNALS = MODULE + "#expectedSignals";
let isComponent = function(obj) {
return Array.isArray(obj.slots) &&
typeof obj.signals === 'object' &&
...
...
@@ -22,14 +21,16 @@ let signalHandler = function(chai, utils) {
);
});
chai.Assertion.addMethod('emit', function(signal) {
chai.Assertion.addMethod('emit', function(signal, options = {}) {
const registeredSignals = utils.flag(this, FLAG_EXPECTED_SIGNALS)
?? utils.flag(this, FLAG_EXPECTED_SIGNALS, [])
?? utils.flag(this, FLAG_EXPECTED_SIGNALS);
options.withArgs = options.withArgs ?? [];
options.count = options.count ?? 0;
registeredSignals.push(
signal
);
});
registeredSignals.push(
{ name: signal,options: options }
);
});
chai.Assertion.addMethod('when',function(slot) {
...
...
@@ -45,24 +46,54 @@ let signalHandler = function(chai, utils) {
let Handler = new ARCS.Component.create(
function() {
this.triggered = false;
this.data = [];
this.callCount = 0;
this.trigger = function() {
this.triggered = true;
this.callCount++;
this.data = arguments;
};
}, ['trigger']
);
let handler = new Handler();
ARCS.Component.connect(obj, signal, handler, "trigger");
obj[slot].call(obj);
ARCS.Component.connect(obj, signal.name, handler, "trigger");
if (Array.isArray(slot)) {
slot.forEach( s => obj[s].call(obj) );
} else {
obj[slot].call(obj);
}
self.assert(
handler.triggered,
"expected #{this} to emit signal "+signal,
"expected #{this} not to emit signal "+signal
"expected #{this} to emit signal "+signal
.name
,
"expected #{this} not to emit signal "+signal
.name
);
signal.options = signal.options ?? {};
if (signal.options.hasOwnProperty("count") && signal.options.count > 0) {
let count = signal.options.count;
self.assert(
handler.callCount === count,
"expected #{this}." + signal.name +" to be emitted " + count + " times",
"expected #{this}." + signal.name +" not to emit signal " +count + " times"
);
}
if (signal.options.hasOwnProperty("withArgs") && signal.options.withArgs.length > 0) {
let args = signal.options.withArgs;
self.assert(
args.length === handler.data.length,
"expected #{this} to have " + args.length + " arguments",
"expected #{this} not to have " + args.length + " arguments"
);
for(let i=0; i < args.length; i++) {
new chai.Assertion(handler.data[i]).to.deep.equal(
args[i],
"expected #{this} to equal \"" + args[i] +"\""
);
}
}
});
});
};
chai.use(signalHandler);
...
...
@@ -144,19 +175,71 @@ describe('statemachine', function() {
expect(sm.slots).to.deep.equal(['setToken', 'next']);
});
// it ('should trigger a single transition', function() {
// let sm = new ARCS.StateMachine();
// sm.setTransitions({
// start: {next:"end"}
// });
// sm.start();
// expect(sm).to.emit('requestSheet').when('next');
//
// // here we should be able to capture the right signal emission and
// // its params.
//
// });
it ('should trigger a single transition', function() {
let sm = new ARCS.StateMachine({
initial: "start",
transitions: {
start: {next:"end"}
}
});
sm.start();
expect(sm).to.emit('requestSheet', { withArgs: ["end"]}).when('next');
});
it ('should trigger the right transition when faning out with the first token', function() {
let sm = new ARCS.StateMachine({
initial: "start",
transitions: {
start: { token1: "state1", token2: "state2" }
}
});
sm.start();
expect(sm).to.emit('requestSheet', { withArgs: ["state1"]}).when("token1");
});
it ('should trigger the right transition when faning out with the second token', function() {
let sm = new ARCS.StateMachine({
initial: "start",
transitions: {
start: { token1: "state1", token2: "state2" }
}
});
sm.start();
expect(sm).to.emit('requestSheet', { withArgs: ["state2"]}).when("token2");
});
it ('should trigger a transition when at least one event of two is fired (version a)', function() {
let sm = new ARCS.StateMachine({
initial: "start",
transitions: {
start: { "a|b" :"end"}
}
});
sm.start();
expect(sm).to.emit('requestSheet', { withArgs: ["end"]}).when('a');
});
it ('should trigger a transition when at least one event of two is fired (version b)', function() {
let sm = new ARCS.StateMachine({
initial: "start",
transitions: {
start: { "a|b" :"end"}
}
});
sm.start();
expect(sm).to.emit('requestSheet', { withArgs: ["end"]}).when('b');
});
it ('should trigger a transition when two events are fired', function() {
let sm = new ARCS.StateMachine({
initial: "start",
transitions: {
start: { "a&b" :"end"}
}
});
sm.start();
expect(sm).to.emit('requestSheet', { withArgs: ["end"]}).when(['a','b']);
});
});
...
...
Please
register
or
login
to post a comment