dal-engine-core-js-lib-dev 0.0.1 → 0.0.3
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 +158 -48
- package/dist/index.esm.js +158 -48
- package/package.json +1 -1
- package/src/BehavioralControlGraph/BehavioralControlGraph.js +15 -15
- package/src/BehavioralControlGraph/GraphNode.js +55 -15
- package/src/DALEngine.js +75 -4
- package/src/Members/Behavior.js +5 -5
- package/src/Members/Invariant.js +4 -4
- package/src/Members/Participant.js +5 -5
- package/tests/DALEngine.test.js +51 -39
- package/tests/Invariant.test.js +6 -7
- package/tests/SimpleDesign.test.js +18 -25
package/dist/index.esm.js
CHANGED
|
@@ -80,9 +80,9 @@ class Invariant extends Base {
|
|
|
80
80
|
this.invariantType = null;
|
|
81
81
|
this.traceId = null;
|
|
82
82
|
if (typeof args === "object" && Object.hasOwn(args, "uid")) {
|
|
83
|
-
this.
|
|
83
|
+
this._loadInvariantFromJSON(args);
|
|
84
84
|
} else {
|
|
85
|
-
this.
|
|
85
|
+
this._loadArgs(args);
|
|
86
86
|
}
|
|
87
87
|
}
|
|
88
88
|
|
|
@@ -91,7 +91,7 @@ class Invariant extends Base {
|
|
|
91
91
|
* @throws {MissingAttributes} Thrown when required attr is not present.
|
|
92
92
|
* @param {Object} args
|
|
93
93
|
*/
|
|
94
|
-
|
|
94
|
+
_loadArgs (args) {
|
|
95
95
|
const expectedAttributes = ["name", "rule"];
|
|
96
96
|
if (typeof args !== "object" || args === null || Array.isArray(args)) {
|
|
97
97
|
// Not an object, so all attributes are missing.
|
|
@@ -109,7 +109,7 @@ class Invariant extends Base {
|
|
|
109
109
|
* Loads the participant from a JSON object.
|
|
110
110
|
* @param {Object} invariantJSON
|
|
111
111
|
*/
|
|
112
|
-
|
|
112
|
+
_loadInvariantFromJSON (invariantJSON) {
|
|
113
113
|
for (const [key, value] of Object.entries(invariantJSON)) {
|
|
114
114
|
this[key] = value;
|
|
115
115
|
} // Reset these because they are set by the execution
|
|
@@ -196,14 +196,14 @@ class Participant extends Base {
|
|
|
196
196
|
*/
|
|
197
197
|
constructor (args) {
|
|
198
198
|
super();
|
|
199
|
-
this.type = ENGINE_TYPES.
|
|
199
|
+
this.type = ENGINE_TYPES.PARTICIPANT;
|
|
200
200
|
this.invariants = [];
|
|
201
201
|
this.abstractionId = null;
|
|
202
202
|
this.invariantViolated = false;
|
|
203
203
|
if (typeof args === "object" && Object.hasOwn(args, "uid")) {
|
|
204
|
-
this.
|
|
204
|
+
this._loadParticipantFromJSON(args);
|
|
205
205
|
} else {
|
|
206
|
-
this.
|
|
206
|
+
this._loadArgs(args);
|
|
207
207
|
}
|
|
208
208
|
}
|
|
209
209
|
|
|
@@ -212,7 +212,7 @@ class Participant extends Base {
|
|
|
212
212
|
* @throws {MissingAttributes} Thrown when required attr is not present.
|
|
213
213
|
* @param {Object} args
|
|
214
214
|
*/
|
|
215
|
-
|
|
215
|
+
_loadArgs (args) {
|
|
216
216
|
const expectedAttributes = ["name"];
|
|
217
217
|
if (typeof args !== "object" || args === null || Array.isArray(args)) {
|
|
218
218
|
// Not an object, so all attributes are missing.
|
|
@@ -230,7 +230,7 @@ class Participant extends Base {
|
|
|
230
230
|
* Loads the participant from a JSON object.
|
|
231
231
|
* @param {Object} participantJSON
|
|
232
232
|
*/
|
|
233
|
-
|
|
233
|
+
_loadParticipantFromJSON (participantJSON) {
|
|
234
234
|
for (const [key, value] of Object.entries(participantJSON)) {
|
|
235
235
|
if (key === "invariants") {
|
|
236
236
|
value.forEach(node => this.invariants.push(new Invariant(node)));
|
|
@@ -302,9 +302,9 @@ class Behavior extends Base {
|
|
|
302
302
|
this.abstractionIds = [];
|
|
303
303
|
this.invalidWorldState = false;
|
|
304
304
|
if (typeof args === "object" && Object.hasOwn(args, "uid")) {
|
|
305
|
-
this.
|
|
305
|
+
this._loadBehaviorFromJSON(args);
|
|
306
306
|
} else {
|
|
307
|
-
this.
|
|
307
|
+
this._loadArgs(args);
|
|
308
308
|
}
|
|
309
309
|
}
|
|
310
310
|
|
|
@@ -313,7 +313,7 @@ class Behavior extends Base {
|
|
|
313
313
|
* @throws {MissingAttributes} Thrown when required attr is not present.
|
|
314
314
|
* @param {Object} args
|
|
315
315
|
*/
|
|
316
|
-
|
|
316
|
+
_loadArgs (args) {
|
|
317
317
|
const expectedAttributes = ["name"];
|
|
318
318
|
if (typeof args !== "object" || args === null || Array.isArray(args)) {
|
|
319
319
|
// Not an object, so all attributes are missing.
|
|
@@ -331,7 +331,7 @@ class Behavior extends Base {
|
|
|
331
331
|
* Loads the behavior from a JSON object.
|
|
332
332
|
* @param {Object} behaviorJSON
|
|
333
333
|
*/
|
|
334
|
-
|
|
334
|
+
_loadBehaviorFromJSON (behaviorJSON) {
|
|
335
335
|
for (const [key, value] of Object.entries(behaviorJSON)) {
|
|
336
336
|
if (key === "participants") {
|
|
337
337
|
value.forEach(node => this.participants.push(new Participant(node)));
|
|
@@ -345,7 +345,7 @@ class Behavior extends Base {
|
|
|
345
345
|
* @param {Participant} participant
|
|
346
346
|
* @returns
|
|
347
347
|
*/
|
|
348
|
-
|
|
348
|
+
addParticipant (participant) {
|
|
349
349
|
this.participants.push(participant);
|
|
350
350
|
return participant;
|
|
351
351
|
}
|
|
@@ -385,13 +385,14 @@ class GraphNode extends Base {
|
|
|
385
385
|
constructor (args) {
|
|
386
386
|
super();
|
|
387
387
|
this.type = ENGINE_TYPES.GRAPH_NODE;
|
|
388
|
-
this.
|
|
388
|
+
this._behavior = null;
|
|
389
|
+
this._goToBehaviorIds = [];
|
|
389
390
|
if (typeof args === "object" && args !== null) {
|
|
390
391
|
if (Object.hasOwn(args, "uid")) {
|
|
391
|
-
this.
|
|
392
|
+
this._loadNodeFromJSON(args);
|
|
392
393
|
} else {
|
|
393
|
-
this.
|
|
394
|
-
this.
|
|
394
|
+
this._behavior = args.behavior;
|
|
395
|
+
this._goToBehaviorIds = args.goToBehaviorsIds;
|
|
395
396
|
}
|
|
396
397
|
}
|
|
397
398
|
}
|
|
@@ -400,29 +401,68 @@ class GraphNode extends Base {
|
|
|
400
401
|
* Loads the nodes from a JSON object.
|
|
401
402
|
* @param {Object} nodesJSON
|
|
402
403
|
*/
|
|
403
|
-
|
|
404
|
+
_loadNodeFromJSON (nodesJSON) {
|
|
404
405
|
for (const [key, value] of Object.entries(nodesJSON)) {
|
|
405
406
|
if (key === "behavior") {
|
|
406
|
-
this.
|
|
407
|
-
} else if (key === "
|
|
408
|
-
value.forEach(
|
|
407
|
+
this._behavior = new Behavior(value);
|
|
408
|
+
} else if (key === "goToBehaviorsIds") {
|
|
409
|
+
value.forEach(behaviorId => this._goToBehaviorIds.push(behaviorId));
|
|
409
410
|
} else {
|
|
410
411
|
this[key] = nodesJSON[key];
|
|
411
412
|
}
|
|
412
413
|
} }
|
|
413
414
|
|
|
415
|
+
/**
|
|
416
|
+
* Returns the behavior of the node.
|
|
417
|
+
* @returns {Behavior|Null}
|
|
418
|
+
*/
|
|
419
|
+
getBehavior () {
|
|
420
|
+
return this._behavior;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
/**
|
|
424
|
+
* Returns the list of behavior names that this node transitions to.
|
|
425
|
+
* @returns {Array}
|
|
426
|
+
*/
|
|
427
|
+
getGoToBehaviors () {
|
|
428
|
+
return this._goToBehaviorIds;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
/**
|
|
432
|
+
* Adds a behavior name to the list of behaviors that this
|
|
433
|
+
* node transitions to.
|
|
434
|
+
* @param {String} behaviorId ID of behavior.
|
|
435
|
+
*/
|
|
436
|
+
addGoToBehavior (behaviorId) {
|
|
437
|
+
this._goToBehaviorIds.push(behaviorId);
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
/**
|
|
441
|
+
* Adds a behavior name to the list of behaviors that this
|
|
442
|
+
* node transitions to.
|
|
443
|
+
* @param {Array} behaviorIds IDs of behaviors.
|
|
444
|
+
*/
|
|
445
|
+
addGoToBehaviors (behaviorIds) {
|
|
446
|
+
this._goToBehaviorIds.push(...behaviorIds);
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
removeGoToBehavior (behaviorId) {
|
|
450
|
+
const goToIndex = this._goToBehaviorIds.indexOf(behaviorId);
|
|
451
|
+
if (goToIndex > -1) {
|
|
452
|
+
this._goToBehaviorIds.splice(goToIndex, 1);
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
|
|
414
456
|
/**
|
|
415
457
|
* Checks if the provided behavior name is a valid
|
|
416
|
-
*
|
|
417
|
-
* result of the this nodes state transformation. i.e.
|
|
418
|
-
* is this behavior in the goToBehavior list.
|
|
458
|
+
* transition from this node.
|
|
419
459
|
* @param {String} behaviorName
|
|
420
460
|
* @returns {Boolean}
|
|
421
461
|
*/
|
|
422
|
-
|
|
423
|
-
for (let i = 0; i < this.
|
|
424
|
-
const
|
|
425
|
-
if (
|
|
462
|
+
isValidTransition (behaviorName) {
|
|
463
|
+
for (let i = 0; i < this._goToBehaviorIds.length; i++) {
|
|
464
|
+
const behaviorId = this._goToBehaviorIds[i];
|
|
465
|
+
if (behaviorId === behaviorName) {
|
|
426
466
|
return true;
|
|
427
467
|
}
|
|
428
468
|
}
|
|
@@ -445,7 +485,7 @@ class BehavioralControlGraph extends Base {
|
|
|
445
485
|
this.nodes = [];
|
|
446
486
|
if (typeof args === "object" && args !== null) {
|
|
447
487
|
if (Object.hasOwn(args, "uid")) {
|
|
448
|
-
this.
|
|
488
|
+
this._loadGraphFromJSON(args);
|
|
449
489
|
} else {
|
|
450
490
|
this.name = args.name;
|
|
451
491
|
}
|
|
@@ -456,7 +496,7 @@ class BehavioralControlGraph extends Base {
|
|
|
456
496
|
* Loads the graph from a JSON object..
|
|
457
497
|
* @param {Object} graphJson
|
|
458
498
|
*/
|
|
459
|
-
|
|
499
|
+
_loadGraphFromJSON (graphJson) {
|
|
460
500
|
for (const [key, value] of Object.entries(graphJson)) {
|
|
461
501
|
if (key === "nodes") {
|
|
462
502
|
value.forEach(node => this.nodes.push(new GraphNode(node)));
|
|
@@ -468,13 +508,13 @@ class BehavioralControlGraph extends Base {
|
|
|
468
508
|
/**
|
|
469
509
|
* Adds a node to the graph.
|
|
470
510
|
* @param {Behavior} behavior
|
|
471
|
-
* @param {Array}
|
|
511
|
+
* @param {Array} goToBehaviorsIds
|
|
472
512
|
* @returns
|
|
473
513
|
*/
|
|
474
|
-
|
|
514
|
+
_addNode (behavior, goToBehaviorsIds) {
|
|
475
515
|
const node = new GraphNode({
|
|
476
516
|
behavior: behavior,
|
|
477
|
-
|
|
517
|
+
goToBehaviorsIds: goToBehaviorsIds,
|
|
478
518
|
});
|
|
479
519
|
this.nodes.push(node);
|
|
480
520
|
return node;
|
|
@@ -488,9 +528,9 @@ class BehavioralControlGraph extends Base {
|
|
|
488
528
|
* does not exist in the graph.
|
|
489
529
|
* @returns
|
|
490
530
|
*/
|
|
491
|
-
|
|
531
|
+
_findNode (behaviorName) {
|
|
492
532
|
for (let i = 0; i < this.nodes.length; i++) {
|
|
493
|
-
const behavior = this.nodes[i].
|
|
533
|
+
const behavior = this.nodes[i].getBehavior();
|
|
494
534
|
if (behavior.name === behaviorName) {
|
|
495
535
|
return this.nodes[i];
|
|
496
536
|
}
|
|
@@ -507,8 +547,8 @@ class BehavioralControlGraph extends Base {
|
|
|
507
547
|
*
|
|
508
548
|
* @param {String} behaviorName
|
|
509
549
|
*/
|
|
510
|
-
|
|
511
|
-
const node = this.
|
|
550
|
+
_setCurrentBehavior (behaviorName) {
|
|
551
|
+
const node = this._findNode(behaviorName);
|
|
512
552
|
/**
|
|
513
553
|
* TODO: Ensure it is atomic because the execution
|
|
514
554
|
* will only set a behavior when its the first one.
|
|
@@ -524,11 +564,11 @@ class BehavioralControlGraph extends Base {
|
|
|
524
564
|
* @throws {InvalidTransitionError} Raised when the provided
|
|
525
565
|
* behavior is not a valid transition.
|
|
526
566
|
*/
|
|
527
|
-
|
|
528
|
-
if (this.currentNode.
|
|
529
|
-
this.currentNode = this.
|
|
567
|
+
_goToBehavior (nextBehaviorName) {
|
|
568
|
+
if (this.currentNode.isValidTransition(nextBehaviorName)) {
|
|
569
|
+
this.currentNode = this._findNode(nextBehaviorName);
|
|
530
570
|
} else {
|
|
531
|
-
throw new InvalidTransitionError(this.currentNode.
|
|
571
|
+
throw new InvalidTransitionError(this.currentNode.getBehavior().name, nextBehaviorName);
|
|
532
572
|
}
|
|
533
573
|
}
|
|
534
574
|
|
|
@@ -539,8 +579,8 @@ class BehavioralControlGraph extends Base {
|
|
|
539
579
|
exportAsMermaid () {
|
|
540
580
|
let mermaid = "flowchart TD\n";
|
|
541
581
|
this.nodes.forEach((node) => {
|
|
542
|
-
node.
|
|
543
|
-
mermaid += ` ${node.
|
|
582
|
+
node.getGoToBehaviors().forEach((behaviorId) => {
|
|
583
|
+
mermaid += ` ${node.getBehavior().name} --> ${behaviorId}\n`;
|
|
544
584
|
});
|
|
545
585
|
});
|
|
546
586
|
return mermaid;
|
|
@@ -563,9 +603,26 @@ class BehavioralControlGraph extends Base {
|
|
|
563
603
|
class DALEngine {
|
|
564
604
|
constructor (args) {
|
|
565
605
|
this.graph = new BehavioralControlGraph();
|
|
566
|
-
|
|
567
|
-
|
|
606
|
+
this.loadArgs(args);
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
/**
|
|
610
|
+
* Loads the provided arguments.
|
|
611
|
+
* @throws {MissingAttributes} Thrown when required attr is not present.
|
|
612
|
+
* @param {Object} args
|
|
613
|
+
*/
|
|
614
|
+
loadArgs (args) {
|
|
615
|
+
const expectedAttributes = ["name"];
|
|
616
|
+
if (typeof args !== "object" || args === null || Array.isArray(args)) {
|
|
617
|
+
// Not an object, so all attributes are missing.
|
|
618
|
+
throw new MissingAttributes("Engine", expectedAttributes);
|
|
568
619
|
}
|
|
620
|
+
expectedAttributes.forEach((attr) => {
|
|
621
|
+
if (!(attr in args)) {
|
|
622
|
+
throw new MissingAttributes("Engine", attr);
|
|
623
|
+
}
|
|
624
|
+
this[attr] = args[attr];
|
|
625
|
+
});
|
|
569
626
|
}
|
|
570
627
|
|
|
571
628
|
/**
|
|
@@ -581,8 +638,7 @@ class DALEngine {
|
|
|
581
638
|
* @param {String} jsonText
|
|
582
639
|
*/
|
|
583
640
|
deserialize (jsonText) {
|
|
584
|
-
this.graph = new BehavioralControlGraph();
|
|
585
|
-
this.graph.loadGraphFromJSON(JSON.parse(jsonText));
|
|
641
|
+
this.graph = new BehavioralControlGraph(JSON.parse(jsonText));
|
|
586
642
|
}
|
|
587
643
|
|
|
588
644
|
/**
|
|
@@ -611,6 +667,60 @@ class DALEngine {
|
|
|
611
667
|
createInvariant (args) {
|
|
612
668
|
return new Invariant(args);
|
|
613
669
|
}
|
|
670
|
+
|
|
671
|
+
|
|
672
|
+
/**
|
|
673
|
+
* Returns the node in the graph with the given behavior name.
|
|
674
|
+
* @param {String} behaviorId
|
|
675
|
+
* @returns {GraphNode}
|
|
676
|
+
*/
|
|
677
|
+
getNode (behaviorId) {
|
|
678
|
+
return this.graph._findNode(behaviorId);
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
/**
|
|
682
|
+
* Adds a node to the graph with the given behaviorId and goToBehaviors.
|
|
683
|
+
* @param {String} behaviorId
|
|
684
|
+
* @param {Array} goToBehaviorsIds
|
|
685
|
+
* @returns {GraphNode}
|
|
686
|
+
*/
|
|
687
|
+
addNode (behaviorId, goToBehaviorsIds) {
|
|
688
|
+
const behavior = this.createBehavior({name: behaviorId});
|
|
689
|
+
const goToIds = goToBehaviorsIds?goToBehaviorsIds:[];
|
|
690
|
+
return this.graph._addNode(behavior, goToIds);
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
/**
|
|
694
|
+
* Deletes a node from the graph with the given behaviorId and
|
|
695
|
+
* removes it from the goToBehavior list of all other nodes.
|
|
696
|
+
* @param {String} behaviorId
|
|
697
|
+
*/
|
|
698
|
+
removeNode (behaviorId) {
|
|
699
|
+
const node = this.graph._findNode(behaviorId);
|
|
700
|
+
const nodeIndex = this.graph.nodes.indexOf(node);
|
|
701
|
+
this.graph.nodes.splice(nodeIndex, 1);
|
|
702
|
+
for (const node of this.graph.nodes) {
|
|
703
|
+
node.removeGoToBehavior(behaviorId);
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
/**
|
|
708
|
+
* Sets the current behavior in the graph.
|
|
709
|
+
* @param {String} behaviorId
|
|
710
|
+
*/
|
|
711
|
+
setCurrentBehavior (behaviorId) {
|
|
712
|
+
this.graph._setCurrentBehavior(behaviorId);
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
|
|
716
|
+
/**
|
|
717
|
+
* Transitions the graph to the given behavior if it
|
|
718
|
+
* is a valid transition from the current behavior.
|
|
719
|
+
* @param {String} nextBehaviorId ID of the next behavior.
|
|
720
|
+
*/
|
|
721
|
+
goToBehavior (nextBehaviorId) {
|
|
722
|
+
this.graph._goToBehavior(nextBehaviorId);
|
|
723
|
+
}
|
|
614
724
|
}
|
|
615
725
|
|
|
616
726
|
export { DALEngine };
|
package/package.json
CHANGED
|
@@ -19,7 +19,7 @@ class BehavioralControlGraph extends Base {
|
|
|
19
19
|
this.nodes = [];
|
|
20
20
|
if (typeof args === "object" && args !== null) {
|
|
21
21
|
if (Object.hasOwn(args, "uid")) {
|
|
22
|
-
this.
|
|
22
|
+
this._loadGraphFromJSON(args);
|
|
23
23
|
} else {
|
|
24
24
|
this.name = args.name;
|
|
25
25
|
}
|
|
@@ -30,7 +30,7 @@ class BehavioralControlGraph extends Base {
|
|
|
30
30
|
* Loads the graph from a JSON object..
|
|
31
31
|
* @param {Object} graphJson
|
|
32
32
|
*/
|
|
33
|
-
|
|
33
|
+
_loadGraphFromJSON (graphJson) {
|
|
34
34
|
for (const [key, value] of Object.entries(graphJson)) {
|
|
35
35
|
if (key === "nodes") {
|
|
36
36
|
value.forEach(node => this.nodes.push(new GraphNode(node)));
|
|
@@ -43,13 +43,13 @@ class BehavioralControlGraph extends Base {
|
|
|
43
43
|
/**
|
|
44
44
|
* Adds a node to the graph.
|
|
45
45
|
* @param {Behavior} behavior
|
|
46
|
-
* @param {Array}
|
|
46
|
+
* @param {Array} goToBehaviorsIds
|
|
47
47
|
* @returns
|
|
48
48
|
*/
|
|
49
|
-
|
|
49
|
+
_addNode (behavior, goToBehaviorsIds) {
|
|
50
50
|
const node = new GraphNode({
|
|
51
51
|
behavior: behavior,
|
|
52
|
-
|
|
52
|
+
goToBehaviorsIds: goToBehaviorsIds,
|
|
53
53
|
});
|
|
54
54
|
this.nodes.push(node);
|
|
55
55
|
return node;
|
|
@@ -63,9 +63,9 @@ class BehavioralControlGraph extends Base {
|
|
|
63
63
|
* does not exist in the graph.
|
|
64
64
|
* @returns
|
|
65
65
|
*/
|
|
66
|
-
|
|
66
|
+
_findNode (behaviorName) {
|
|
67
67
|
for (let i = 0; i < this.nodes.length; i++) {
|
|
68
|
-
const behavior = this.nodes[i].
|
|
68
|
+
const behavior = this.nodes[i].getBehavior();
|
|
69
69
|
if (behavior.name === behaviorName) {
|
|
70
70
|
return this.nodes[i];
|
|
71
71
|
}
|
|
@@ -82,8 +82,8 @@ class BehavioralControlGraph extends Base {
|
|
|
82
82
|
*
|
|
83
83
|
* @param {String} behaviorName
|
|
84
84
|
*/
|
|
85
|
-
|
|
86
|
-
const node = this.
|
|
85
|
+
_setCurrentBehavior (behaviorName) {
|
|
86
|
+
const node = this._findNode(behaviorName);
|
|
87
87
|
/**
|
|
88
88
|
* TODO: Ensure it is atomic because the execution
|
|
89
89
|
* will only set a behavior when its the first one.
|
|
@@ -99,11 +99,11 @@ class BehavioralControlGraph extends Base {
|
|
|
99
99
|
* @throws {InvalidTransitionError} Raised when the provided
|
|
100
100
|
* behavior is not a valid transition.
|
|
101
101
|
*/
|
|
102
|
-
|
|
103
|
-
if (this.currentNode.
|
|
104
|
-
this.currentNode = this.
|
|
102
|
+
_goToBehavior (nextBehaviorName) {
|
|
103
|
+
if (this.currentNode.isValidTransition(nextBehaviorName)) {
|
|
104
|
+
this.currentNode = this._findNode(nextBehaviorName);
|
|
105
105
|
} else {
|
|
106
|
-
throw new InvalidTransitionError(this.currentNode.
|
|
106
|
+
throw new InvalidTransitionError(this.currentNode.getBehavior().name, nextBehaviorName);
|
|
107
107
|
}
|
|
108
108
|
}
|
|
109
109
|
|
|
@@ -114,8 +114,8 @@ class BehavioralControlGraph extends Base {
|
|
|
114
114
|
exportAsMermaid () {
|
|
115
115
|
let mermaid = "flowchart TD\n";
|
|
116
116
|
this.nodes.forEach((node) => {
|
|
117
|
-
node.
|
|
118
|
-
mermaid += ` ${node.
|
|
117
|
+
node.getGoToBehaviors().forEach((behaviorId) => {
|
|
118
|
+
mermaid += ` ${node.getBehavior().name} --> ${behaviorId}\n`;
|
|
119
119
|
});
|
|
120
120
|
});
|
|
121
121
|
return mermaid;
|
|
@@ -13,13 +13,14 @@ class GraphNode extends Base {
|
|
|
13
13
|
constructor (args) {
|
|
14
14
|
super();
|
|
15
15
|
this.type = ENGINE_TYPES.GRAPH_NODE;
|
|
16
|
-
this.
|
|
16
|
+
this._behavior = null;
|
|
17
|
+
this._goToBehaviorIds = [];
|
|
17
18
|
if (typeof args === "object" && args !== null) {
|
|
18
19
|
if (Object.hasOwn(args, "uid")) {
|
|
19
|
-
this.
|
|
20
|
+
this._loadNodeFromJSON(args);
|
|
20
21
|
} else {
|
|
21
|
-
this.
|
|
22
|
-
this.
|
|
22
|
+
this._behavior = args.behavior;
|
|
23
|
+
this._goToBehaviorIds = args.goToBehaviorsIds;
|
|
23
24
|
}
|
|
24
25
|
}
|
|
25
26
|
}
|
|
@@ -28,30 +29,69 @@ class GraphNode extends Base {
|
|
|
28
29
|
* Loads the nodes from a JSON object.
|
|
29
30
|
* @param {Object} nodesJSON
|
|
30
31
|
*/
|
|
31
|
-
|
|
32
|
+
_loadNodeFromJSON (nodesJSON) {
|
|
32
33
|
for (const [key, value] of Object.entries(nodesJSON)) {
|
|
33
34
|
if (key === "behavior") {
|
|
34
|
-
this.
|
|
35
|
-
} else if (key === "
|
|
36
|
-
value.forEach(
|
|
35
|
+
this._behavior = new Behavior(value);
|
|
36
|
+
} else if (key === "goToBehaviorsIds") {
|
|
37
|
+
value.forEach(behaviorId => this._goToBehaviorIds.push(behaviorId));
|
|
37
38
|
} else {
|
|
38
39
|
this[key] = nodesJSON[key];
|
|
39
40
|
}
|
|
40
41
|
};
|
|
41
42
|
}
|
|
42
43
|
|
|
44
|
+
/**
|
|
45
|
+
* Returns the behavior of the node.
|
|
46
|
+
* @returns {Behavior|Null}
|
|
47
|
+
*/
|
|
48
|
+
getBehavior () {
|
|
49
|
+
return this._behavior;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Returns the list of behavior names that this node transitions to.
|
|
54
|
+
* @returns {Array}
|
|
55
|
+
*/
|
|
56
|
+
getGoToBehaviors () {
|
|
57
|
+
return this._goToBehaviorIds;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Adds a behavior name to the list of behaviors that this
|
|
62
|
+
* node transitions to.
|
|
63
|
+
* @param {String} behaviorId ID of behavior.
|
|
64
|
+
*/
|
|
65
|
+
addGoToBehavior (behaviorId) {
|
|
66
|
+
this._goToBehaviorIds.push(behaviorId);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Adds a behavior name to the list of behaviors that this
|
|
71
|
+
* node transitions to.
|
|
72
|
+
* @param {Array} behaviorIds IDs of behaviors.
|
|
73
|
+
*/
|
|
74
|
+
addGoToBehaviors (behaviorIds) {
|
|
75
|
+
this._goToBehaviorIds.push(...behaviorIds);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
removeGoToBehavior (behaviorId) {
|
|
79
|
+
const goToIndex = this._goToBehaviorIds.indexOf(behaviorId);
|
|
80
|
+
if (goToIndex > -1) {
|
|
81
|
+
this._goToBehaviorIds.splice(goToIndex, 1);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
43
85
|
/**
|
|
44
86
|
* Checks if the provided behavior name is a valid
|
|
45
|
-
*
|
|
46
|
-
* result of the this nodes state transformation. i.e.
|
|
47
|
-
* is this behavior in the goToBehavior list.
|
|
87
|
+
* transition from this node.
|
|
48
88
|
* @param {String} behaviorName
|
|
49
89
|
* @returns {Boolean}
|
|
50
90
|
*/
|
|
51
|
-
|
|
52
|
-
for (let i = 0; i < this.
|
|
53
|
-
const
|
|
54
|
-
if (
|
|
91
|
+
isValidTransition (behaviorName) {
|
|
92
|
+
for (let i = 0; i < this._goToBehaviorIds.length; i++) {
|
|
93
|
+
const behaviorId = this._goToBehaviorIds[i];
|
|
94
|
+
if (behaviorId === behaviorName) {
|
|
55
95
|
return true;
|
|
56
96
|
}
|
|
57
97
|
}
|
package/src/DALEngine.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import BehavioralControlGraph from "./BehavioralControlGraph/BehavioralControlGraph";
|
|
2
|
+
import MissingAttributes from "./Errors/MissingAttributes";
|
|
2
3
|
import Behavior from "./Members/Behavior";
|
|
3
4
|
import Invariant from "./Members/Invariant";
|
|
4
5
|
import Participant from "./Members/Participant";
|
|
@@ -19,9 +20,26 @@ import Participant from "./Members/Participant";
|
|
|
19
20
|
export class DALEngine {
|
|
20
21
|
constructor (args) {
|
|
21
22
|
this.graph = new BehavioralControlGraph();
|
|
22
|
-
|
|
23
|
-
|
|
23
|
+
this.loadArgs(args);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Loads the provided arguments.
|
|
28
|
+
* @throws {MissingAttributes} Thrown when required attr is not present.
|
|
29
|
+
* @param {Object} args
|
|
30
|
+
*/
|
|
31
|
+
loadArgs (args) {
|
|
32
|
+
const expectedAttributes = ["name"];
|
|
33
|
+
if (typeof args !== "object" || args === null || Array.isArray(args)) {
|
|
34
|
+
// Not an object, so all attributes are missing.
|
|
35
|
+
throw new MissingAttributes("Engine", expectedAttributes);
|
|
24
36
|
}
|
|
37
|
+
expectedAttributes.forEach((attr) => {
|
|
38
|
+
if (!(attr in args)) {
|
|
39
|
+
throw new MissingAttributes("Engine", attr);
|
|
40
|
+
}
|
|
41
|
+
this[attr] = args[attr];
|
|
42
|
+
});
|
|
25
43
|
}
|
|
26
44
|
|
|
27
45
|
/**
|
|
@@ -37,8 +55,7 @@ export class DALEngine {
|
|
|
37
55
|
* @param {String} jsonText
|
|
38
56
|
*/
|
|
39
57
|
deserialize (jsonText) {
|
|
40
|
-
this.graph = new BehavioralControlGraph();
|
|
41
|
-
this.graph.loadGraphFromJSON(JSON.parse(jsonText));
|
|
58
|
+
this.graph = new BehavioralControlGraph(JSON.parse(jsonText));
|
|
42
59
|
}
|
|
43
60
|
|
|
44
61
|
/**
|
|
@@ -67,4 +84,58 @@ export class DALEngine {
|
|
|
67
84
|
createInvariant (args) {
|
|
68
85
|
return new Invariant(args);
|
|
69
86
|
}
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Returns the node in the graph with the given behavior name.
|
|
91
|
+
* @param {String} behaviorId
|
|
92
|
+
* @returns {GraphNode}
|
|
93
|
+
*/
|
|
94
|
+
getNode (behaviorId) {
|
|
95
|
+
return this.graph._findNode(behaviorId);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Adds a node to the graph with the given behaviorId and goToBehaviors.
|
|
100
|
+
* @param {String} behaviorId
|
|
101
|
+
* @param {Array} goToBehaviorsIds
|
|
102
|
+
* @returns {GraphNode}
|
|
103
|
+
*/
|
|
104
|
+
addNode (behaviorId, goToBehaviorsIds) {
|
|
105
|
+
const behavior = this.createBehavior({name: behaviorId});
|
|
106
|
+
const goToIds = goToBehaviorsIds?goToBehaviorsIds:[];
|
|
107
|
+
return this.graph._addNode(behavior, goToIds);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Deletes a node from the graph with the given behaviorId and
|
|
112
|
+
* removes it from the goToBehavior list of all other nodes.
|
|
113
|
+
* @param {String} behaviorId
|
|
114
|
+
*/
|
|
115
|
+
removeNode (behaviorId) {
|
|
116
|
+
const node = this.graph._findNode(behaviorId);
|
|
117
|
+
const nodeIndex = this.graph.nodes.indexOf(node);
|
|
118
|
+
this.graph.nodes.splice(nodeIndex, 1);
|
|
119
|
+
for (const node of this.graph.nodes) {
|
|
120
|
+
node.removeGoToBehavior(behaviorId);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Sets the current behavior in the graph.
|
|
126
|
+
* @param {String} behaviorId
|
|
127
|
+
*/
|
|
128
|
+
setCurrentBehavior (behaviorId) {
|
|
129
|
+
this.graph._setCurrentBehavior(behaviorId);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Transitions the graph to the given behavior if it
|
|
135
|
+
* is a valid transition from the current behavior.
|
|
136
|
+
* @param {String} nextBehaviorId ID of the next behavior.
|
|
137
|
+
*/
|
|
138
|
+
goToBehavior (nextBehaviorId) {
|
|
139
|
+
this.graph._goToBehavior(nextBehaviorId);
|
|
140
|
+
}
|
|
70
141
|
}
|