dal-engine-core-js-lib-dev 0.0.3 → 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +607 -201
- package/dist/index.esm.js +607 -201
- package/package.json +1 -1
- package/src/Base.js +10 -5
- package/src/BehavioralControlGraph/BehavioralControlGraph.js +79 -37
- package/src/BehavioralControlGraph/GraphNode.js +75 -24
- package/src/BehavioralControlGraph/Graphs.js +137 -0
- package/src/DALEngine.js +153 -54
- package/src/Errors/BehaviorAlreadyExistsError.js +10 -0
- package/src/Errors/GraphWithNameExistsError.js +10 -0
- package/src/Errors/ParticipantAlreadyExistsError.js +10 -0
- package/src/Errors/UnknownGraph.js +10 -0
- package/src/Errors/UnknownParticipantError.js +9 -0
- package/src/Members/Behavior.js +44 -22
- package/src/Members/Invariant.js +37 -22
- package/src/Members/Participant.js +28 -20
- package/src/TYPES.js +2 -3
- package/src/helpers/isLoadedFromFile.js +15 -0
- package/tests/DALEngine.test.js +8 -0
- package/tests/Graphs.test.js +80 -0
- package/tests/Invariant.test.js +1 -0
package/dist/index.cjs
CHANGED
|
@@ -1,25 +1,51 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
class DALEngineError extends Error {
|
|
4
|
+
constructor (message) {
|
|
5
|
+
super(message);
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
class GraphWithNameExistsError extends DALEngineError {
|
|
10
|
+
constructor (name) {
|
|
11
|
+
let msg = `Graph with name "${name}" already exists.`;
|
|
12
|
+
super(msg);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
class UnknownGraph extends DALEngineError {
|
|
17
|
+
constructor (name) {
|
|
18
|
+
let msg = `Graph with name "${name}" does not exist.`;
|
|
19
|
+
super(msg);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
6
23
|
class Base {
|
|
7
24
|
/**
|
|
8
|
-
* Initialize the base class.
|
|
25
|
+
* Initialize the base class of all members of the design.
|
|
26
|
+
* The dal_engine_uid is a unique identifier for each instance.
|
|
27
|
+
* When the design is serialized, the dal_engine_uid is used
|
|
28
|
+
* to identify when the design is being loaded from file.
|
|
9
29
|
* @param {String} name
|
|
10
30
|
*/
|
|
11
31
|
constructor () {
|
|
12
|
-
this.
|
|
32
|
+
this.dal_engine_uid = crypto.randomUUID();
|
|
13
33
|
}
|
|
14
34
|
|
|
35
|
+
/**
|
|
36
|
+
* Returns the type of object as specifed in TYPES.js.
|
|
37
|
+
* (e.g. participant, behavior, invariant, graph node, etc.)
|
|
38
|
+
* @returns {Number} The type of node.
|
|
39
|
+
*/
|
|
15
40
|
getType () {
|
|
16
41
|
return this.type;
|
|
17
42
|
}
|
|
18
43
|
}
|
|
19
44
|
|
|
20
|
-
class
|
|
21
|
-
constructor (
|
|
22
|
-
|
|
45
|
+
class BehaviorAlreadyExistsError extends DALEngineError {
|
|
46
|
+
constructor (name) {
|
|
47
|
+
let msg = `Node with Behavior name "${name}" already exists in the graph.`;
|
|
48
|
+
super(msg);
|
|
23
49
|
}
|
|
24
50
|
}
|
|
25
51
|
|
|
@@ -33,6 +59,18 @@ class InvalidTransitionError extends DALEngineError {
|
|
|
33
59
|
}
|
|
34
60
|
}
|
|
35
61
|
|
|
62
|
+
class MissingAttributes extends DALEngineError {
|
|
63
|
+
constructor (type, attribute) {
|
|
64
|
+
let msg;
|
|
65
|
+
if (Array.isArray(attribute) && attribute.length > 1) {
|
|
66
|
+
msg = `"${type}" must be initialized with the attributes "${attribute}".`;
|
|
67
|
+
} else {
|
|
68
|
+
msg = `"${type}" must be initialized with the attribute "${attribute}".`;
|
|
69
|
+
}
|
|
70
|
+
super(msg);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
36
74
|
class UnknownBehaviorError extends DALEngineError {
|
|
37
75
|
constructor (behaviorName) {
|
|
38
76
|
super(`The behavior named "${behaviorName}" was not found in graph.`);
|
|
@@ -42,38 +80,69 @@ class UnknownBehaviorError extends DALEngineError {
|
|
|
42
80
|
}
|
|
43
81
|
}
|
|
44
82
|
|
|
83
|
+
/**
|
|
84
|
+
* Checks if the provided object was loaded from a file. This is determind by
|
|
85
|
+
* checking if the argument is an object and has a "dal_engine_uid" attribute,
|
|
86
|
+
* which is added to all objects when they are created and is written to file
|
|
87
|
+
* when the object is serialized.
|
|
88
|
+
*
|
|
89
|
+
* @param {*} obj The object to check.
|
|
90
|
+
* @returns {Boolean} True if the object was loaded from file, false otherwise.
|
|
91
|
+
*/
|
|
92
|
+
const isLoadedFromFile = (obj) => {
|
|
93
|
+
return typeof obj === "object" && obj !== null
|
|
94
|
+
&& !Array.isArray(obj) && Object.hasOwn(obj, "dal_engine_uid");
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
class ParticipantAlreadyExistsError extends DALEngineError {
|
|
98
|
+
constructor (name) {
|
|
99
|
+
let msg = `Participant with name "${name}" already exists in the behavior.`;
|
|
100
|
+
super(msg);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
class UnknownParticipantError extends DALEngineError {
|
|
105
|
+
constructor (participantName) {
|
|
106
|
+
super(`The participant named "${participantName}" was not found in the behavior.`);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
45
110
|
let ENGINE_TYPES = {
|
|
46
111
|
BEHAVIOR: 1,
|
|
47
112
|
INVARIANT: 2,
|
|
48
113
|
PARTICIPANT: 3,
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
GRAPH_NODE: 6,
|
|
114
|
+
BEHAVIORAL_CONTROL_GRAPH: 4,
|
|
115
|
+
GRAPH_NODE: 5,
|
|
52
116
|
};
|
|
53
117
|
ENGINE_TYPES = Object.freeze(ENGINE_TYPES);
|
|
54
118
|
|
|
55
119
|
var ENGINE_TYPES$1 = ENGINE_TYPES;
|
|
56
120
|
|
|
57
|
-
class MissingAttributes extends DALEngineError {
|
|
58
|
-
constructor (type, attribute) {
|
|
59
|
-
let msg;
|
|
60
|
-
if (Array.isArray(attribute) && attribute.length > 1) {
|
|
61
|
-
msg = `"${type}" must be initialized with the attributes "${attribute}".`;
|
|
62
|
-
} else {
|
|
63
|
-
msg = `"${type}" must be initialized with the attribute "${attribute}".`;
|
|
64
|
-
}
|
|
65
|
-
super(msg);
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Class representing a Invariant in the design.
|
|
71
|
-
*/
|
|
72
121
|
class Invariant extends Base {
|
|
73
122
|
/**
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
*
|
|
123
|
+
* Class representing a Invariant in the design. Invariants are rules that
|
|
124
|
+
* define a valid world state for participants in behaviors. The invariants
|
|
125
|
+
* are used to check if the design has entered a semantically invalid state
|
|
126
|
+
* during execution.
|
|
127
|
+
*
|
|
128
|
+
* The expected attributes in args are:
|
|
129
|
+
* - name: Name of the invariant.
|
|
130
|
+
* - rule: The rule that defines the how to enforce the invariant. This
|
|
131
|
+
* will be formally defined in a collection of invariant rules that can
|
|
132
|
+
* be chosen from when creating an invariant.
|
|
133
|
+
*
|
|
134
|
+
* Currently, the only supported invariant rule is the string min length
|
|
135
|
+
* rule, which can be specified as follows:
|
|
136
|
+
* {
|
|
137
|
+
* "name": "MinLengthConstraint",
|
|
138
|
+
* "rule": {
|
|
139
|
+
* "type": "minLength",
|
|
140
|
+
* "keys": ["value", "name"],
|
|
141
|
+
* "value": 1,
|
|
142
|
+
* },
|
|
143
|
+
* }
|
|
144
|
+
*
|
|
145
|
+
* @param {Object} args The arguments to initialize the invariant with.
|
|
77
146
|
*/
|
|
78
147
|
constructor (args) {
|
|
79
148
|
super();
|
|
@@ -81,17 +150,13 @@ class Invariant extends Base {
|
|
|
81
150
|
this.invariantViolated = false;
|
|
82
151
|
this.invariantType = null;
|
|
83
152
|
this.traceId = null;
|
|
84
|
-
|
|
85
|
-
this._loadInvariantFromJSON(args);
|
|
86
|
-
} else {
|
|
87
|
-
this._loadArgs(args);
|
|
88
|
-
}
|
|
153
|
+
(isLoadedFromFile(args) ? this._loadFromFile(args) : this._loadArgs(args));
|
|
89
154
|
}
|
|
90
155
|
|
|
91
156
|
/**
|
|
92
|
-
* Loads the provided arguments.
|
|
157
|
+
* Loads the invariant from the provided arguments.
|
|
93
158
|
* @throws {MissingAttributes} Thrown when required attr is not present.
|
|
94
|
-
* @param {Object} args
|
|
159
|
+
* @param {Object} args The arguments to initialize the invariant with.
|
|
95
160
|
*/
|
|
96
161
|
_loadArgs (args) {
|
|
97
162
|
const expectedAttributes = ["name", "rule"];
|
|
@@ -108,10 +173,10 @@ class Invariant extends Base {
|
|
|
108
173
|
}
|
|
109
174
|
|
|
110
175
|
/**
|
|
111
|
-
* Loads the
|
|
112
|
-
* @param {Object} invariantJSON
|
|
176
|
+
* Loads the invariant from file.
|
|
177
|
+
* @param {Object} invariantJSON The JSON object to load the invariant from.
|
|
113
178
|
*/
|
|
114
|
-
|
|
179
|
+
_loadFromFile (invariantJSON) {
|
|
115
180
|
for (const [key, value] of Object.entries(invariantJSON)) {
|
|
116
181
|
this[key] = value;
|
|
117
182
|
} // Reset these because they are set by the execution
|
|
@@ -120,9 +185,10 @@ class Invariant extends Base {
|
|
|
120
185
|
}
|
|
121
186
|
|
|
122
187
|
/**
|
|
123
|
-
* Evaluate the invariant
|
|
124
|
-
*
|
|
125
|
-
* @
|
|
188
|
+
* Evaluate the invariant by applying the invariant rule to the provided
|
|
189
|
+
* value. Returns a flag indicating whether the invariant was violated.
|
|
190
|
+
* @param {*} value The value to evaluate the invariant on.
|
|
191
|
+
* @returns {Boolean} Returns flag indicating if invariant was violated.
|
|
126
192
|
*/
|
|
127
193
|
evaluate (value) {
|
|
128
194
|
this.invariantViolated = false;
|
|
@@ -133,8 +199,8 @@ class Invariant extends Base {
|
|
|
133
199
|
}
|
|
134
200
|
|
|
135
201
|
/**
|
|
136
|
-
* Enforce the string min length invariant
|
|
137
|
-
* @param value
|
|
202
|
+
* Enforce the string min length invariant.
|
|
203
|
+
* @param {*} value The value to enforce the invariant on.
|
|
138
204
|
*/
|
|
139
205
|
enforceMinLength (value) {
|
|
140
206
|
if ("keys" in this.rule) {
|
|
@@ -170,7 +236,7 @@ class Invariant extends Base {
|
|
|
170
236
|
* automated testing.
|
|
171
237
|
*
|
|
172
238
|
* Substrate invariants correspond to a trace that represents
|
|
173
|
-
* an environment
|
|
239
|
+
* an environment which reveals a limitation of the substrate,
|
|
174
240
|
* thus motivating the invariant. It is also the environment
|
|
175
241
|
* in which an implementation can prove that it respects this
|
|
176
242
|
* invariant.
|
|
@@ -187,14 +253,17 @@ class Invariant extends Base {
|
|
|
187
253
|
}
|
|
188
254
|
}
|
|
189
255
|
|
|
190
|
-
/**
|
|
191
|
-
* Class representing a participant in the design.
|
|
192
|
-
*/
|
|
193
256
|
class Participant extends Base {
|
|
194
257
|
/**
|
|
195
|
-
*
|
|
196
|
-
*
|
|
197
|
-
*
|
|
258
|
+
* Class representing a participant in the design. The participants are
|
|
259
|
+
* entites in deisgn that participate in the behavior. They are mapped onto
|
|
260
|
+
* the implementation and their value is loaded from the execution. The
|
|
261
|
+
* participants define a valid world state for the behavior to happen in
|
|
262
|
+
* through invariants. When the value of the participant violates an
|
|
263
|
+
* invariant, the design has entered a semantically invalid state and is
|
|
264
|
+
* the root cause of downstream failure(s).
|
|
265
|
+
*
|
|
266
|
+
* @param {Object} args The arguments to initialize the participant.
|
|
198
267
|
*/
|
|
199
268
|
constructor (args) {
|
|
200
269
|
super();
|
|
@@ -202,11 +271,7 @@ class Participant extends Base {
|
|
|
202
271
|
this.invariants = [];
|
|
203
272
|
this.abstractionId = null;
|
|
204
273
|
this.invariantViolated = false;
|
|
205
|
-
|
|
206
|
-
this._loadParticipantFromJSON(args);
|
|
207
|
-
} else {
|
|
208
|
-
this._loadArgs(args);
|
|
209
|
-
}
|
|
274
|
+
(isLoadedFromFile(args) ? this._loadFromFile(args) : this._loadArgs(args));
|
|
210
275
|
}
|
|
211
276
|
|
|
212
277
|
/**
|
|
@@ -230,9 +295,9 @@ class Participant extends Base {
|
|
|
230
295
|
|
|
231
296
|
/**
|
|
232
297
|
* Loads the participant from a JSON object.
|
|
233
|
-
* @param {Object} participantJSON
|
|
298
|
+
* @param {Object} participantJSON The JSON object read from file.
|
|
234
299
|
*/
|
|
235
|
-
|
|
300
|
+
_loadFromFile (participantJSON) {
|
|
236
301
|
for (const [key, value] of Object.entries(participantJSON)) {
|
|
237
302
|
if (key === "invariants") {
|
|
238
303
|
value.forEach(node => this.invariants.push(new Invariant(node)));
|
|
@@ -243,8 +308,8 @@ class Participant extends Base {
|
|
|
243
308
|
|
|
244
309
|
/**
|
|
245
310
|
* Adds an invariant to the participant.
|
|
246
|
-
* @param {Invariant} invariant
|
|
247
|
-
* @returns
|
|
311
|
+
* @param {Invariant} invariant The invariant to add.
|
|
312
|
+
* @returns {Invariant} The invariant that was added.
|
|
248
313
|
*/
|
|
249
314
|
addInvariant (invariant) {
|
|
250
315
|
this.invariants.push(invariant);
|
|
@@ -252,16 +317,18 @@ class Participant extends Base {
|
|
|
252
317
|
}
|
|
253
318
|
|
|
254
319
|
/**
|
|
255
|
-
* Sets the value of
|
|
256
|
-
* @param {*} value
|
|
320
|
+
* Sets the value of this participant.
|
|
321
|
+
* @param {*} value The value to set for the participant.
|
|
257
322
|
*/
|
|
258
323
|
setValue (value) {
|
|
259
324
|
this.value = value;
|
|
260
325
|
}
|
|
261
326
|
|
|
262
327
|
/**
|
|
263
|
-
* Enforces the
|
|
264
|
-
*
|
|
328
|
+
* Enforces the participant's invariants. Raises a flag indicating if an
|
|
329
|
+
* invariant was violated and counts the number of invariant violations.
|
|
330
|
+
*
|
|
331
|
+
* @returns {Boolean} Returns flag indicating if any invariant was violated.
|
|
265
332
|
*/
|
|
266
333
|
enforceInvariants () {
|
|
267
334
|
this.invariantViolated = false;
|
|
@@ -281,19 +348,23 @@ class Participant extends Base {
|
|
|
281
348
|
* This abstraction id will be used to assign a value to the
|
|
282
349
|
* participant from the execution using the logged abstraction id.
|
|
283
350
|
*
|
|
284
|
-
* @param {String} abstractionId
|
|
351
|
+
* @param {String} abstractionId The abstraction ID to map to the
|
|
352
|
+
* participant.
|
|
285
353
|
*/
|
|
286
354
|
mapAbstraction (abstractionId) {
|
|
355
|
+
/**
|
|
356
|
+
* TODO: Much like the behavior, I am settling on a clean way to map
|
|
357
|
+
* the participant onto the implementation without introducing new
|
|
358
|
+
* unncessary layers.
|
|
359
|
+
*/
|
|
287
360
|
this.abstractionId = abstractionId;
|
|
288
361
|
}
|
|
289
362
|
}
|
|
290
363
|
|
|
291
|
-
/**
|
|
292
|
-
* Class representing a Behavior in the design.
|
|
293
|
-
*/
|
|
294
364
|
class Behavior extends Base {
|
|
295
365
|
/**
|
|
296
366
|
* Initialize the Behavior.
|
|
367
|
+
*
|
|
297
368
|
* @param {String} name
|
|
298
369
|
* @param args
|
|
299
370
|
*/
|
|
@@ -303,15 +374,12 @@ class Behavior extends Base {
|
|
|
303
374
|
this.participants = [];
|
|
304
375
|
this.abstractionIds = [];
|
|
305
376
|
this.invalidWorldState = false;
|
|
306
|
-
|
|
307
|
-
this._loadBehaviorFromJSON(args);
|
|
308
|
-
} else {
|
|
309
|
-
this._loadArgs(args);
|
|
310
|
-
}
|
|
377
|
+
(isLoadedFromFile(args) ? this._loadFromFile(args) : this._loadArgs(args));
|
|
311
378
|
}
|
|
312
379
|
|
|
313
380
|
/**
|
|
314
381
|
* Loads the provided arguments.
|
|
382
|
+
*
|
|
315
383
|
* @throws {MissingAttributes} Thrown when required attr is not present.
|
|
316
384
|
* @param {Object} args
|
|
317
385
|
*/
|
|
@@ -330,10 +398,12 @@ class Behavior extends Base {
|
|
|
330
398
|
}
|
|
331
399
|
|
|
332
400
|
/**
|
|
333
|
-
* Loads the behavior from a JSON object.
|
|
334
|
-
*
|
|
401
|
+
* Loads the behavior from a JSON object that was read from file.
|
|
402
|
+
*
|
|
403
|
+
* @param {Object} behaviorJSON The JSON object representing the behavior
|
|
404
|
+
* read from file.
|
|
335
405
|
*/
|
|
336
|
-
|
|
406
|
+
_loadFromFile (behaviorJSON) {
|
|
337
407
|
for (const [key, value] of Object.entries(behaviorJSON)) {
|
|
338
408
|
if (key === "participants") {
|
|
339
409
|
value.forEach(node => this.participants.push(new Participant(node)));
|
|
@@ -344,73 +414,107 @@ class Behavior extends Base {
|
|
|
344
414
|
|
|
345
415
|
/**
|
|
346
416
|
* Adds a participant to the behavior.
|
|
347
|
-
*
|
|
348
|
-
* @
|
|
417
|
+
*
|
|
418
|
+
* @param {Participant} participant The participant to add.
|
|
419
|
+
* @returns {Participant} The added participant.
|
|
420
|
+
* @throws {ParticipantAlreadyExistsError} Thrown when a participant with
|
|
421
|
+
* the same name already exists in the behavior.
|
|
349
422
|
*/
|
|
350
423
|
addParticipant (participant) {
|
|
424
|
+
if (this.participants.some(p => p.name === participant.name)) {
|
|
425
|
+
throw new ParticipantAlreadyExistsError(participant.name);
|
|
426
|
+
}
|
|
351
427
|
this.participants.push(participant);
|
|
352
428
|
return participant;
|
|
353
429
|
}
|
|
354
430
|
|
|
355
431
|
/**
|
|
356
|
-
*
|
|
357
|
-
*
|
|
358
|
-
*
|
|
432
|
+
* Sets the value of a participant and checks for invariant violations.
|
|
433
|
+
* If any invariant is violated, the world state for this behavior is
|
|
434
|
+
* marked as invalid.
|
|
435
|
+
*
|
|
436
|
+
* @param {String} name Name of the participant whose value is being set.
|
|
437
|
+
* @param {*} value Value to set for the participant.
|
|
438
|
+
* @throws {UnknownParticipantError} Thrown when a participant with the
|
|
439
|
+
* provided name does not exist in the behavior.
|
|
359
440
|
*/
|
|
360
|
-
setParticipantValue (
|
|
361
|
-
const participant = this.participants.find(obj => obj.name ===
|
|
441
|
+
setParticipantValue (name, value) {
|
|
442
|
+
const participant = this.participants.find(obj => obj.name === name);
|
|
443
|
+
if (!participant) {
|
|
444
|
+
throw new UnknownParticipantError(name);
|
|
445
|
+
}
|
|
362
446
|
participant.value = value;
|
|
363
|
-
|
|
364
|
-
if (violation) {
|
|
447
|
+
if (participant.enforceInvariants()) {
|
|
365
448
|
this.invalidWorldState = true;
|
|
366
449
|
}
|
|
367
450
|
}
|
|
368
451
|
|
|
369
452
|
/**
|
|
370
|
-
* Maps the abstraction id from
|
|
371
|
-
*
|
|
453
|
+
* Maps the abstraction id from implementation to the behavior.
|
|
454
|
+
*
|
|
455
|
+
* @param {String} abstractionId ID of mapped abstraction.
|
|
372
456
|
*/
|
|
373
457
|
addMapping (abstractionId) {
|
|
458
|
+
/**
|
|
459
|
+
* TODO: After some more thinking, it seems to me that the
|
|
460
|
+
* uniqe identifier should be a file name and line number.
|
|
461
|
+
* It doesn't make sense to create a new abstraction id,
|
|
462
|
+
* however, I will resolve this soon and remove this TODO.
|
|
463
|
+
*/
|
|
374
464
|
this.abstractionIds.push(abstractionId);
|
|
375
465
|
}
|
|
376
466
|
}
|
|
377
467
|
|
|
378
|
-
/**
|
|
379
|
-
* Class representing a behavioral control graph node.
|
|
380
|
-
*/
|
|
381
468
|
class GraphNode extends Base {
|
|
382
469
|
/**
|
|
383
|
-
*
|
|
384
|
-
*
|
|
385
|
-
*
|
|
470
|
+
* Class representing a behavioral control graph node. Each node has a
|
|
471
|
+
* behavior and a list of behaviors it can transition to. The node also
|
|
472
|
+
* has flags to indicate if the behavior is atomic or if the node is a
|
|
473
|
+
* design fork.
|
|
474
|
+
*
|
|
475
|
+
* @param {Object} args The args to initialize the graph node.
|
|
386
476
|
*/
|
|
387
477
|
constructor (args) {
|
|
388
478
|
super();
|
|
389
479
|
this.type = ENGINE_TYPES$1.GRAPH_NODE;
|
|
390
480
|
this._behavior = null;
|
|
391
481
|
this._goToBehaviorIds = [];
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
482
|
+
this._isAtomic = false;
|
|
483
|
+
this._isDesignFork = false;
|
|
484
|
+
(isLoadedFromFile(args) ? this._loadFromFile(args) : this._loadArgs(args));
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
/**
|
|
488
|
+
* Loads the provided arguments.
|
|
489
|
+
* @param {Object} args Arguments to initialize the graph node with.
|
|
490
|
+
* @throws {MissingAttributes} Thrown when required attr is not present.
|
|
491
|
+
*/
|
|
492
|
+
_loadArgs (args) {
|
|
493
|
+
const expectedAttributes = ["behavior", "goToBehaviorIds", "isAtomic", "isDesignFork"];
|
|
494
|
+
if (typeof args !== "object" || args === null || Array.isArray(args)) {
|
|
495
|
+
// Not an object, so all attributes are missing.
|
|
496
|
+
throw new MissingAttributes("GraphNode", expectedAttributes);
|
|
399
497
|
}
|
|
498
|
+
expectedAttributes.forEach((attr) => {
|
|
499
|
+
if (!(attr in args)) {
|
|
500
|
+
throw new MissingAttributes("GraphNode", attr);
|
|
501
|
+
}
|
|
502
|
+
this["_" + attr] = args[attr];
|
|
503
|
+
});
|
|
400
504
|
}
|
|
401
505
|
|
|
402
506
|
/**
|
|
403
|
-
* Loads the
|
|
404
|
-
* @param {Object}
|
|
507
|
+
* Loads the node from file.
|
|
508
|
+
* @param {Object} nodeJSON The JSON object read from file.
|
|
405
509
|
*/
|
|
406
|
-
|
|
407
|
-
for (const [key, value] of Object.entries(
|
|
510
|
+
_loadFromFile (nodeJSON) {
|
|
511
|
+
for (const [key, value] of Object.entries(nodeJSON)) {
|
|
408
512
|
if (key === "behavior") {
|
|
409
513
|
this._behavior = new Behavior(value);
|
|
410
|
-
} else if (key === "
|
|
514
|
+
} else if (key === "goToBehaviorIds") {
|
|
411
515
|
value.forEach(behaviorId => this._goToBehaviorIds.push(behaviorId));
|
|
412
516
|
} else {
|
|
413
|
-
this[key] =
|
|
517
|
+
this[key] = nodeJSON[key];
|
|
414
518
|
}
|
|
415
519
|
} }
|
|
416
520
|
|
|
@@ -424,15 +528,14 @@ class GraphNode extends Base {
|
|
|
424
528
|
|
|
425
529
|
/**
|
|
426
530
|
* Returns the list of behavior names that this node transitions to.
|
|
427
|
-
* @returns {Array}
|
|
531
|
+
* @returns {Array} List of behavior names that this node transitions to.
|
|
428
532
|
*/
|
|
429
533
|
getGoToBehaviors () {
|
|
430
534
|
return this._goToBehaviorIds;
|
|
431
535
|
}
|
|
432
536
|
|
|
433
537
|
/**
|
|
434
|
-
* Adds a behavior
|
|
435
|
-
* node transitions to.
|
|
538
|
+
* Adds a behavior to the transitions from this node.
|
|
436
539
|
* @param {String} behaviorId ID of behavior.
|
|
437
540
|
*/
|
|
438
541
|
addGoToBehavior (behaviorId) {
|
|
@@ -440,14 +543,17 @@ class GraphNode extends Base {
|
|
|
440
543
|
}
|
|
441
544
|
|
|
442
545
|
/**
|
|
443
|
-
* Adds
|
|
444
|
-
* node transitions to.
|
|
546
|
+
* Adds list of behaviors to the transitions from this node.
|
|
445
547
|
* @param {Array} behaviorIds IDs of behaviors.
|
|
446
548
|
*/
|
|
447
549
|
addGoToBehaviors (behaviorIds) {
|
|
448
550
|
this._goToBehaviorIds.push(...behaviorIds);
|
|
449
551
|
}
|
|
450
552
|
|
|
553
|
+
/**
|
|
554
|
+
* Remove the behavior from the list of transitions.
|
|
555
|
+
* @param {String} behaviorId
|
|
556
|
+
*/
|
|
451
557
|
removeGoToBehavior (behaviorId) {
|
|
452
558
|
const goToIndex = this._goToBehaviorIds.indexOf(behaviorId);
|
|
453
559
|
if (goToIndex > -1) {
|
|
@@ -455,6 +561,38 @@ class GraphNode extends Base {
|
|
|
455
561
|
}
|
|
456
562
|
}
|
|
457
563
|
|
|
564
|
+
/**
|
|
565
|
+
* Raises a flag to indicate if the behavior is atomic or not.
|
|
566
|
+
* @param {Boolean} isAtomic Flag indicates if the behavior is atomic.
|
|
567
|
+
*/
|
|
568
|
+
setIsAtomic (isAtomic) {
|
|
569
|
+
this._isAtomic = isAtomic;
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
/**
|
|
573
|
+
* Returns whether the behavior is atomic or not.
|
|
574
|
+
* @returns {Boolean}
|
|
575
|
+
*/
|
|
576
|
+
isAtomic () {
|
|
577
|
+
return this._isAtomic;
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
/**
|
|
581
|
+
* Raises a flag to indicate if this node is a fork in the design.
|
|
582
|
+
* @param {Boolean} forks Flag indicates if the node is a fork.
|
|
583
|
+
*/
|
|
584
|
+
setIsDesignFork (forks) {
|
|
585
|
+
this._isDesignFork = forks;
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
/**
|
|
589
|
+
* Returns whether the node is a fork in the design or not.
|
|
590
|
+
* @returns {Boolean}
|
|
591
|
+
*/
|
|
592
|
+
isDesignFork () {
|
|
593
|
+
return this._isDesignFork;
|
|
594
|
+
}
|
|
595
|
+
|
|
458
596
|
/**
|
|
459
597
|
* Checks if the provided behavior name is a valid
|
|
460
598
|
* transition from this node.
|
|
@@ -472,33 +610,60 @@ class GraphNode extends Base {
|
|
|
472
610
|
}
|
|
473
611
|
}
|
|
474
612
|
|
|
475
|
-
/**
|
|
476
|
-
* Class representing the behavioral control graph.
|
|
477
|
-
*/
|
|
478
613
|
class BehavioralControlGraph extends Base {
|
|
479
614
|
/**
|
|
480
|
-
*
|
|
481
|
-
*
|
|
482
|
-
*
|
|
615
|
+
* Class representing the behavioral control graph. The behavioral control
|
|
616
|
+
* graph is a directed graph where nodes represent behaviors and edges
|
|
617
|
+
* represent valid transitions between behaviors.
|
|
618
|
+
*
|
|
619
|
+
* The graph can be used to execute the design by starting at the atomic
|
|
620
|
+
* node and transitioning to observed behaviors. As the design is executed,
|
|
621
|
+
* the values of the participants are set from the observed values and the
|
|
622
|
+
* invariants are checked at each transition to recognize if the design has
|
|
623
|
+
* entered a semantically invalid state.
|
|
624
|
+
*
|
|
625
|
+
* Currently, it only has to be initialized with a name and the remaining
|
|
626
|
+
* attributes can be added using the provided methods. If the graph is being
|
|
627
|
+
* loaded from a file, then the presence of a UID in the args will select
|
|
628
|
+
* the relevant method to load the graph from a JSON object.
|
|
629
|
+
*
|
|
630
|
+
* @param {Object} args The args to initialize the behavioral control graph.
|
|
483
631
|
*/
|
|
484
632
|
constructor (args) {
|
|
485
633
|
super();
|
|
486
634
|
this.type = ENGINE_TYPES$1.BEHAVIORAL_CONTROL_GRAPH;
|
|
487
635
|
this.nodes = [];
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
636
|
+
(isLoadedFromFile(args) ? this._loadFromFile(args) : this._loadArgs(args));
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
/**
|
|
640
|
+
* Loads the provided arguments.
|
|
641
|
+
* @throws {MissingAttributes} Thrown when required attr is not present.
|
|
642
|
+
* @param {Object} args
|
|
643
|
+
*/
|
|
644
|
+
_loadArgs (args) {
|
|
645
|
+
/**
|
|
646
|
+
* TODO: Move the attributes to private and use getters and setters
|
|
647
|
+
* for them. Repeat for all the other classes.
|
|
648
|
+
*/
|
|
649
|
+
const expectedAttributes = ["name"];
|
|
650
|
+
if (typeof args !== "object" || args === null || Array.isArray(args)) {
|
|
651
|
+
// Not an object, so all attributes are missing.
|
|
652
|
+
throw new MissingAttributes("BehavioralControlGraph", expectedAttributes);
|
|
494
653
|
}
|
|
654
|
+
expectedAttributes.forEach((attr) => {
|
|
655
|
+
if (!(attr in args)) {
|
|
656
|
+
throw new MissingAttributes("BehavioralControlGraph", attr);
|
|
657
|
+
}
|
|
658
|
+
this[attr] = args[attr];
|
|
659
|
+
});
|
|
495
660
|
}
|
|
496
661
|
|
|
497
662
|
/**
|
|
498
663
|
* Loads the graph from a JSON object..
|
|
499
664
|
* @param {Object} graphJson
|
|
500
665
|
*/
|
|
501
|
-
|
|
666
|
+
_loadFromFile (graphJson) {
|
|
502
667
|
for (const [key, value] of Object.entries(graphJson)) {
|
|
503
668
|
if (key === "nodes") {
|
|
504
669
|
value.forEach(node => this.nodes.push(new GraphNode(node)));
|
|
@@ -508,15 +673,27 @@ class BehavioralControlGraph extends Base {
|
|
|
508
673
|
} }
|
|
509
674
|
|
|
510
675
|
/**
|
|
511
|
-
* Adds a node to the graph.
|
|
512
|
-
*
|
|
513
|
-
* @param {
|
|
514
|
-
* @
|
|
676
|
+
* Adds a node to the graph with the provided arguments.
|
|
677
|
+
*
|
|
678
|
+
* @param {Behavior} behaviorId ID of the behavior represented by the node.
|
|
679
|
+
* @param {Array} goToBehaviorIds IDs of the behaviors that are valid
|
|
680
|
+
* transitions from this node.
|
|
681
|
+
* @param {Boolean} isAtomic Flag indicating if the behavior represented by
|
|
682
|
+
* the node is atomic.
|
|
683
|
+
* @param {Boolean} isDesignFork Flag indicating if the node is a
|
|
684
|
+
* design fork.
|
|
685
|
+
* @throws {BehaviorAlreadyExistsError} Raised when a node with the provided
|
|
686
|
+
* @returns {GraphNode} The created graph node.
|
|
515
687
|
*/
|
|
516
|
-
|
|
688
|
+
addNode (behaviorId, goToBehaviorIds, isAtomic, isDesignFork) {
|
|
689
|
+
if (this.nodes.some((node) => node.getBehavior().name === behaviorId)) {
|
|
690
|
+
throw new BehaviorAlreadyExistsError(behaviorId);
|
|
691
|
+
}
|
|
517
692
|
const node = new GraphNode({
|
|
518
|
-
behavior:
|
|
519
|
-
|
|
693
|
+
behavior: new Behavior({name: behaviorId}),
|
|
694
|
+
goToBehaviorIds: goToBehaviorIds?goToBehaviorIds:[],
|
|
695
|
+
isAtomic: isAtomic?isAtomic:false,
|
|
696
|
+
isDesignFork: isDesignFork?isDesignFork:false,
|
|
520
697
|
});
|
|
521
698
|
this.nodes.push(node);
|
|
522
699
|
return node;
|
|
@@ -528,29 +705,25 @@ class BehavioralControlGraph extends Base {
|
|
|
528
705
|
* @param {String} behaviorName
|
|
529
706
|
* @throws {UnknownBehaviorError} Raised when the provided behavior
|
|
530
707
|
* does not exist in the graph.
|
|
531
|
-
* @returns
|
|
708
|
+
* @returns {GraphNode} The found graph node.
|
|
532
709
|
*/
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
710
|
+
findNode (behaviorName) {
|
|
711
|
+
const node = this.nodes.find(
|
|
712
|
+
(node) => node.getBehavior().name === behaviorName
|
|
713
|
+
);
|
|
714
|
+
if (!node) {
|
|
715
|
+
throw new UnknownBehaviorError(behaviorName);
|
|
539
716
|
}
|
|
540
|
-
|
|
717
|
+
return node;
|
|
541
718
|
}
|
|
542
719
|
|
|
543
720
|
/**
|
|
544
721
|
* Sets the active node given the behavior name.
|
|
545
|
-
* The execution provides the next observed
|
|
546
|
-
* behavior and the active node indicates if
|
|
547
|
-
* if it is a valid transition.
|
|
548
|
-
*
|
|
549
722
|
*
|
|
550
723
|
* @param {String} behaviorName
|
|
551
724
|
*/
|
|
552
|
-
|
|
553
|
-
const node = this.
|
|
725
|
+
setCurrentBehavior (behaviorName) {
|
|
726
|
+
const node = this.findNode(behaviorName);
|
|
554
727
|
/**
|
|
555
728
|
* TODO: Ensure it is atomic because the execution
|
|
556
729
|
* will only set a behavior when its the first one.
|
|
@@ -562,13 +735,15 @@ class BehavioralControlGraph extends Base {
|
|
|
562
735
|
/**
|
|
563
736
|
* Check if the observed behavior is a valid transition
|
|
564
737
|
* given the current node.
|
|
565
|
-
*
|
|
738
|
+
*
|
|
739
|
+
* @param {String} nextBehaviorName Name of the next behavior to
|
|
740
|
+
* transition to.
|
|
566
741
|
* @throws {InvalidTransitionError} Raised when the provided
|
|
567
742
|
* behavior is not a valid transition.
|
|
568
743
|
*/
|
|
569
|
-
|
|
744
|
+
goToBehavior (nextBehaviorName) {
|
|
570
745
|
if (this.currentNode.isValidTransition(nextBehaviorName)) {
|
|
571
|
-
this.currentNode = this.
|
|
746
|
+
this.currentNode = this.findNode(nextBehaviorName);
|
|
572
747
|
} else {
|
|
573
748
|
throw new InvalidTransitionError(this.currentNode.getBehavior().name, nextBehaviorName);
|
|
574
749
|
}
|
|
@@ -590,30 +765,174 @@ class BehavioralControlGraph extends Base {
|
|
|
590
765
|
}
|
|
591
766
|
|
|
592
767
|
/**
|
|
593
|
-
*
|
|
594
|
-
*
|
|
595
|
-
*
|
|
768
|
+
* Class representing a collection of atomic graphs.
|
|
769
|
+
*
|
|
770
|
+
* These graphs together represent the design. Within each graph, every node
|
|
771
|
+
* must be part of the same tree. There cannot be multiple disconnected trees,
|
|
772
|
+
* if there are, then it is a separate graph in this collection. Each graph
|
|
773
|
+
* is identified by a unique name and is selected as the active graph when the
|
|
774
|
+
* atomic behavior is observed. The atomic behavior is not transitioned to from
|
|
775
|
+
* any other behavior, it is the root of the tree.
|
|
776
|
+
*/
|
|
777
|
+
class Graphs {
|
|
778
|
+
constructor () {
|
|
779
|
+
this._graphs = {};
|
|
780
|
+
this.addGraph("default graph");
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
/**
|
|
784
|
+
* Load the graphs from file.
|
|
785
|
+
*
|
|
786
|
+
* @param {String} jsonText JSON text representing the collection of graphs.
|
|
787
|
+
*/
|
|
788
|
+
loadFromJson (jsonText) {
|
|
789
|
+
const parsed = JSON.parse(jsonText);
|
|
790
|
+
Object.keys(parsed._graphs).forEach(graphId => {
|
|
791
|
+
this._graphs[graphId] = new BehavioralControlGraph(parsed._graphs[graphId]);
|
|
792
|
+
});
|
|
793
|
+
this._activeGraph = this._graphs[Object.keys(this._graphs)[0]];
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
/**
|
|
797
|
+
* Given a graph ID, creates the graph and adds it to the collection.
|
|
798
|
+
*
|
|
799
|
+
* @param {String} graphId ID of the graph.
|
|
800
|
+
* @returns {BehavioralControlGraph} The graph that was added.
|
|
801
|
+
* @throws {GraphWithNameExistsError} Raised when a graph with the provided
|
|
802
|
+
* name already exists in the collection of graphs.
|
|
803
|
+
*/
|
|
804
|
+
addGraph (graphId) {
|
|
805
|
+
if (graphId in this._graphs) {
|
|
806
|
+
throw new GraphWithNameExistsError(graphId);
|
|
807
|
+
}
|
|
808
|
+
this._graphs[graphId] = new BehavioralControlGraph({name: graphId});
|
|
809
|
+
this._activeGraph = this._graphs[graphId];
|
|
810
|
+
return this._activeGraph;
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
/**
|
|
814
|
+
* Returns the graph with the given graphID from the collection.
|
|
815
|
+
*
|
|
816
|
+
* @param {String} graphId ID of the graph to return.
|
|
817
|
+
* @returns {BehavioralControlGraph} The graph with the given graphId.
|
|
818
|
+
* @throws {UnknownGraph} Raised when a graph with the provided graphId does
|
|
819
|
+
* not exist in the collection of graphs.
|
|
820
|
+
*/
|
|
821
|
+
getGraph (graphId) {
|
|
822
|
+
if (graphId in this._graphs) {
|
|
823
|
+
return this._graphs[graphId];
|
|
824
|
+
} else {
|
|
825
|
+
throw new UnknownGraph(graphId);
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
/**
|
|
830
|
+
* Removes a graph with the given id from the collection of graphs. After
|
|
831
|
+
* the graph is removed, it selects the first graph in the collection of
|
|
832
|
+
* graphs. If there are no graphs left in the collection, it creates a new
|
|
833
|
+
* graph with the name "default graph" and selects it as the active graph.
|
|
834
|
+
*
|
|
835
|
+
* @param {String} graphId ID of the graph to remove.
|
|
836
|
+
* @throws {UnknownGraph} Raised when the provided graphId does not exist
|
|
837
|
+
* in the collection of graphs.
|
|
838
|
+
*/
|
|
839
|
+
removeGraph (graphId) {
|
|
840
|
+
if (graphId in this._graphs) {
|
|
841
|
+
delete this._graphs[graphId];
|
|
842
|
+
} else {
|
|
843
|
+
throw new UnknownGraph(graphId);
|
|
844
|
+
}
|
|
845
|
+
// TODO: Is there a better policy for which graph to select
|
|
846
|
+
// after the current graph is removed?
|
|
847
|
+
if (Object.keys(this._graphs).length === 0) {
|
|
848
|
+
this.addGraph("default graph");
|
|
849
|
+
} else {
|
|
850
|
+
this._activeGraph = this._graphs[Object.keys(this._graphs)[0]];
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
/**
|
|
855
|
+
* Finds the graph with the given graphId and sets it as the active graph.
|
|
856
|
+
*
|
|
857
|
+
* @param {String} graphId Id of the graph to set as active.
|
|
858
|
+
* @returns {BehavioralControlGraph} The currently active graph.
|
|
859
|
+
* @throws {UnknownGraph} Raised when the provided graphId does not exist
|
|
860
|
+
* in the collection of graphs.
|
|
861
|
+
*/
|
|
862
|
+
setActiveGraph (graphId) {
|
|
863
|
+
if (graphId in this._graphs) {
|
|
864
|
+
this._activeGraph = this._graphs[graphId];
|
|
865
|
+
} else {
|
|
866
|
+
throw new UnknownGraph(graphId);
|
|
867
|
+
}
|
|
868
|
+
return this._activeGraph;
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
/**
|
|
872
|
+
* Returns the active graph.
|
|
873
|
+
*
|
|
874
|
+
* @returns {BehavioralControlGraph} The currently active graph.
|
|
875
|
+
*/
|
|
876
|
+
getActiveGraph () {
|
|
877
|
+
return this._activeGraph;
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
/**
|
|
881
|
+
* Returns a collection of all the graphs in the design.
|
|
882
|
+
*
|
|
883
|
+
* @returns {Object} The collection of all graphs in the design.
|
|
884
|
+
*/
|
|
885
|
+
getGraphs () {
|
|
886
|
+
return this._graphs;
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
/**
|
|
890
|
+
* Returns a list of all the graph names in the design.
|
|
891
|
+
*
|
|
892
|
+
* @returns {Array} A list of all graph names in the design.
|
|
893
|
+
*/
|
|
894
|
+
getGraphNames () {
|
|
895
|
+
return Object.keys(this._graphs);
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
/**
|
|
900
|
+
* This engine can be used to define and execute designs defined in a
|
|
901
|
+
* Design Abstraction Language (DAL).
|
|
596
902
|
*
|
|
597
|
-
*
|
|
598
|
-
* the
|
|
599
|
-
*
|
|
600
|
-
*
|
|
601
|
-
*
|
|
602
|
-
*
|
|
603
|
-
*
|
|
903
|
+
* This class is the main interface for users to interact with the
|
|
904
|
+
* engine. It exposes functions to configure the engine and to execute
|
|
905
|
+
* the design. It also exposes functions to serialize and deserialize
|
|
906
|
+
* the engine to and from JSON text.
|
|
907
|
+
*
|
|
908
|
+
* A design can consist of multiple atomic behavioral control graphs and
|
|
909
|
+
* this class allows users to create, select, and delete graphs. It also
|
|
910
|
+
* allows users to add nodes to the graph and to transition between
|
|
911
|
+
* behaviors in the graph. It also allows users to create participants,
|
|
912
|
+
* behaviors, and invariants and assign them to nodes in the graph.
|
|
913
|
+
*
|
|
914
|
+
* The selected graph is determined by the atomic behavior that is observed.
|
|
915
|
+
* The design is executed by transitioning between behaviors in the graph.
|
|
916
|
+
* The values of the participants are set from the observed values and the
|
|
917
|
+
* invariants are checked at each transition to recognize if the design has
|
|
918
|
+
* entered a semantically invalid state.
|
|
604
919
|
*/
|
|
605
920
|
class DALEngine {
|
|
606
921
|
constructor (args) {
|
|
607
|
-
this.
|
|
608
|
-
this.
|
|
922
|
+
this.graphs = new Graphs();
|
|
923
|
+
this.graph = this.graphs.getActiveGraph();
|
|
924
|
+
this._loadArgs(args);
|
|
925
|
+
console.log("Initialized DAL Engine with graphs: ", this.graphs);
|
|
609
926
|
}
|
|
610
927
|
|
|
611
928
|
/**
|
|
612
|
-
*
|
|
613
|
-
*
|
|
614
|
-
* @
|
|
929
|
+
* Sets the provided arguments to the engine.
|
|
930
|
+
*
|
|
931
|
+
* @throws {MissingAttributes} Thrown when required attributes are
|
|
932
|
+
* not present.
|
|
933
|
+
* @param {Object} args Arguments to load.
|
|
615
934
|
*/
|
|
616
|
-
|
|
935
|
+
_loadArgs (args) {
|
|
617
936
|
const expectedAttributes = ["name"];
|
|
618
937
|
if (typeof args !== "object" || args === null || Array.isArray(args)) {
|
|
619
938
|
// Not an object, so all attributes are missing.
|
|
@@ -628,100 +947,187 @@ class DALEngine {
|
|
|
628
947
|
}
|
|
629
948
|
|
|
630
949
|
/**
|
|
631
|
-
*
|
|
632
|
-
*
|
|
950
|
+
* Serializes the behavioral control graphs and returns the JSON text.
|
|
951
|
+
*
|
|
952
|
+
* @returns {String} Returns JSON string representing the control graphs.
|
|
633
953
|
*/
|
|
634
954
|
serialize () {
|
|
635
|
-
return JSON.stringify(this.
|
|
955
|
+
return JSON.stringify(this.graphs);
|
|
636
956
|
}
|
|
637
957
|
|
|
638
958
|
/**
|
|
639
|
-
*
|
|
640
|
-
*
|
|
959
|
+
* Loads the behavioral control graphs from JSON text and sets
|
|
960
|
+
* the active graph to the first graph in the collection of graphs.
|
|
961
|
+
*
|
|
962
|
+
* @param {String} serializedText JSON text representing the control
|
|
963
|
+
* graphs.
|
|
964
|
+
* @throws {SyntaxError|TypeError} Thrown when the JSON text is invalid.
|
|
641
965
|
*/
|
|
642
|
-
deserialize (
|
|
643
|
-
|
|
966
|
+
deserialize (serializedText) {
|
|
967
|
+
// TODO: Improve validation to throw specific error.
|
|
968
|
+
this.graphs = new Graphs();
|
|
969
|
+
this.graphs.loadFromJson(serializedText);
|
|
970
|
+
this.graph = this.graphs.getActiveGraph();
|
|
644
971
|
}
|
|
645
972
|
|
|
646
973
|
/**
|
|
647
|
-
* Creates a
|
|
648
|
-
*
|
|
649
|
-
* @
|
|
974
|
+
* Creates a graph with the given name and sets it as the active graph.
|
|
975
|
+
*
|
|
976
|
+
* @param {String} name Name of the graph to create.
|
|
977
|
+
* @throws {GraphWithNameExistsError} Thrown when a graph with the
|
|
978
|
+
* provided name already exists.
|
|
979
|
+
*/
|
|
980
|
+
createGraph (name) {
|
|
981
|
+
this.graph = this.graphs.addGraph(name);
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
/**
|
|
985
|
+
* Sets the active graph to the graph with the given graphId.
|
|
986
|
+
*
|
|
987
|
+
* @param {String} graphId ID of the graph to set as active
|
|
988
|
+
* @throws {UnknownGraph} Thrown when the provided graphId does
|
|
989
|
+
* not exist in the collection of graphs.
|
|
990
|
+
*/
|
|
991
|
+
selectGraph (graphId) {
|
|
992
|
+
this.graph = this.graphs.setActiveGraph(graphId);
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
/**
|
|
996
|
+
* Removes the graph with the given graphId
|
|
997
|
+
*
|
|
998
|
+
* @param {String} graphId Id of graph to remove.
|
|
999
|
+
* @throws {UnknownGraph} Thrown when the provided graphId does not
|
|
1000
|
+
* exist in the collection of graphs.
|
|
1001
|
+
*/
|
|
1002
|
+
removeGraph (graphId) {
|
|
1003
|
+
this.graphs.removeGraph(graphId);
|
|
1004
|
+
this.graph = this.graphs.getActiveGraph();
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
/**
|
|
1008
|
+
* Returns the names of all the graphs in the design.
|
|
1009
|
+
*
|
|
1010
|
+
* @returns {Array} Returns an array of graph names.
|
|
1011
|
+
*/
|
|
1012
|
+
getSelectableGraphs () {
|
|
1013
|
+
return this.graphs.getGraphNames();
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
/**
|
|
1017
|
+
* Creates a participant with the provided args and returns it.
|
|
1018
|
+
*
|
|
1019
|
+
* @param {Object} args Arguments to create the participant with.
|
|
1020
|
+
* @returns {Participant} Returns the created participant.
|
|
1021
|
+
* @throws {MissingAttributes} Thrown when required attributes are not
|
|
1022
|
+
* present in the args. See Participant class for required attributes.
|
|
650
1023
|
*/
|
|
651
1024
|
createParticipant (args) {
|
|
652
1025
|
return new Participant(args);
|
|
653
1026
|
}
|
|
654
1027
|
|
|
655
1028
|
/**
|
|
656
|
-
* Creates a behavior.
|
|
657
|
-
*
|
|
658
|
-
* @
|
|
1029
|
+
* Creates a behavior with the provided args and returns it.
|
|
1030
|
+
*
|
|
1031
|
+
* @param {Object} args Arguments to create the behavior with.
|
|
1032
|
+
* @returns {Behavior} Returns the created behavior.
|
|
1033
|
+
* @throws {MissingAttributes} Thrown when required attributes are not
|
|
1034
|
+
* present in the args. See Behavior class for required attributes.
|
|
659
1035
|
*/
|
|
660
1036
|
createBehavior (args) {
|
|
661
1037
|
return new Behavior(args);
|
|
662
1038
|
}
|
|
663
1039
|
|
|
664
1040
|
/**
|
|
665
|
-
* Creates an invariant.
|
|
666
|
-
*
|
|
667
|
-
* @
|
|
1041
|
+
* Creates an invariant with the provided args and returns it.
|
|
1042
|
+
*
|
|
1043
|
+
* @param {Object} args Arguments to create the invariant with.
|
|
1044
|
+
* @returns {Invariant} Returns the created invariant.
|
|
1045
|
+
* @throws {MissingAttributes} Raised when required attributes are not
|
|
1046
|
+
* present in the args. See Invariant class for required attributes.
|
|
668
1047
|
*/
|
|
669
1048
|
createInvariant (args) {
|
|
670
1049
|
return new Invariant(args);
|
|
671
1050
|
}
|
|
672
1051
|
|
|
673
|
-
|
|
674
1052
|
/**
|
|
675
1053
|
* Returns the node in the graph with the given behavior name.
|
|
676
|
-
*
|
|
677
|
-
* @
|
|
1054
|
+
*
|
|
1055
|
+
* @param {String} behaviorId ID of the behavior.
|
|
1056
|
+
* @returns {GraphNode} Returns the node in the graph with the
|
|
1057
|
+
* given behavior name.
|
|
1058
|
+
* @throws {UnknownBehaviorError} Thrown when the provided behaviorId is not
|
|
1059
|
+
* a valid behavior in the graph.
|
|
678
1060
|
*/
|
|
679
1061
|
getNode (behaviorId) {
|
|
680
|
-
return this.graph.
|
|
1062
|
+
return this.graph.findNode(behaviorId);
|
|
681
1063
|
}
|
|
682
1064
|
|
|
683
1065
|
/**
|
|
684
1066
|
* Adds a node to the graph with the given behaviorId and goToBehaviors.
|
|
685
|
-
*
|
|
686
|
-
* @param {
|
|
687
|
-
* @
|
|
1067
|
+
*
|
|
1068
|
+
* @param {String} behaviorId ID of the behavior for the node.
|
|
1069
|
+
* @param {Array} goToBehaviorIds IDs of the behaviors that this node
|
|
1070
|
+
* transitions to.
|
|
1071
|
+
* @param {Boolean} isAtomic Flag to indicate if this node contains an
|
|
1072
|
+
* atomic behavior.
|
|
1073
|
+
* @param {Boolean} isDesignFork Flag to indicate if this node
|
|
1074
|
+
* is a fork in the design.
|
|
1075
|
+
* @returns {GraphNode} Returns the created graph node.
|
|
1076
|
+
* @throws {BehaviorAlreadyExistsError} Raised when a node with the provided
|
|
1077
|
+
* behaviorId already exists in the graph.
|
|
688
1078
|
*/
|
|
689
|
-
addNode (behaviorId,
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
1079
|
+
addNode (behaviorId, goToBehaviorIds, isAtomic, isDesignFork) {
|
|
1080
|
+
return this.graph.addNode(
|
|
1081
|
+
behaviorId,
|
|
1082
|
+
goToBehaviorIds,
|
|
1083
|
+
isAtomic,
|
|
1084
|
+
isDesignFork
|
|
1085
|
+
);
|
|
693
1086
|
}
|
|
694
1087
|
|
|
695
1088
|
/**
|
|
696
1089
|
* Deletes a node from the graph with the given behaviorId and
|
|
697
1090
|
* removes it from the goToBehavior list of all other nodes.
|
|
698
|
-
*
|
|
1091
|
+
*
|
|
1092
|
+
* @param {String} behaviorId Behavior ID of the node to delete.
|
|
1093
|
+
* @returns {GraphNode} Returns the deleted graph node.
|
|
1094
|
+
* @throws {UnknownBehaviorError} Thrown when the provided behaviorId is not
|
|
1095
|
+
* a valid behavior in the graph.
|
|
699
1096
|
*/
|
|
700
1097
|
removeNode (behaviorId) {
|
|
701
|
-
const node = this.graph.
|
|
1098
|
+
const node = this.graph.findNode(behaviorId);
|
|
702
1099
|
const nodeIndex = this.graph.nodes.indexOf(node);
|
|
703
|
-
this.graph.nodes.splice(nodeIndex, 1);
|
|
1100
|
+
const removedNode = this.graph.nodes.splice(nodeIndex, 1);
|
|
704
1101
|
for (const node of this.graph.nodes) {
|
|
705
1102
|
node.removeGoToBehavior(behaviorId);
|
|
706
1103
|
}
|
|
1104
|
+
return removedNode[0];
|
|
707
1105
|
}
|
|
708
1106
|
|
|
709
1107
|
/**
|
|
710
|
-
* Sets the current behavior in the graph.
|
|
711
|
-
*
|
|
1108
|
+
* Sets the current behavior in the graph. Since the behavior is not
|
|
1109
|
+
* being transitioned from another, it must be an atomic behavior.
|
|
1110
|
+
*
|
|
1111
|
+
* @param {String} behaviorId ID of the behavior to set as current.
|
|
1112
|
+
* @throws {UnknownBehaviorError} Thrown when the provided behavior is not
|
|
1113
|
+
* a valid behavior in the graph.
|
|
712
1114
|
*/
|
|
713
1115
|
setCurrentBehavior (behaviorId) {
|
|
714
|
-
this.graph.
|
|
1116
|
+
this.graph.setCurrentBehavior(behaviorId);
|
|
715
1117
|
}
|
|
716
1118
|
|
|
717
|
-
|
|
718
1119
|
/**
|
|
719
|
-
*
|
|
720
|
-
*
|
|
1120
|
+
* For the current node in the graph, transitions to the node with the given
|
|
1121
|
+
* behaviorId if it is a valid transition.
|
|
1122
|
+
*
|
|
721
1123
|
* @param {String} nextBehaviorId ID of the next behavior.
|
|
1124
|
+
* @throws {UnknownBehaviorError} Thrown when the provided behavior is not
|
|
1125
|
+
* a valid behavior in the graph.
|
|
1126
|
+
* @throws {InvalidTransitionError} Thrown when the provided behavior is not
|
|
1127
|
+
* a valid transition from the current node.
|
|
722
1128
|
*/
|
|
723
1129
|
goToBehavior (nextBehaviorId) {
|
|
724
|
-
this.graph.
|
|
1130
|
+
this.graph.goToBehavior(nextBehaviorId);
|
|
725
1131
|
}
|
|
726
1132
|
}
|
|
727
1133
|
|