dal-engine-core-js-lib-dev 0.0.2 → 0.0.4
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 +142 -73
- package/dist/index.esm.js +142 -73
- package/package.json +1 -1
- package/src/BehavioralControlGraph/BehavioralControlGraph.js +19 -16
- package/src/BehavioralControlGraph/GraphNode.js +67 -15
- package/src/BehavioralControlGraph/Graphs.js +35 -0
- package/src/DALEngine.js +31 -16
- 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 +30 -15
- package/tests/Invariant.test.js +5 -5
- package/tests/SimpleDesign.test.js +4 -1
- package/tests/simple_design_temp.json +0 -1
package/dist/index.esm.js
CHANGED
|
@@ -40,18 +40,6 @@ class UnknownBehaviorError extends DALEngineError {
|
|
|
40
40
|
}
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
let ENGINE_TYPES$1 = {
|
|
44
|
-
BEHAVIOR: 1,
|
|
45
|
-
INVARIANT: 2,
|
|
46
|
-
PARTICIPANT: 3,
|
|
47
|
-
PRIMITIVE: 4,
|
|
48
|
-
BEHAVIORAL_CONTROL_GRAPH: 5,
|
|
49
|
-
GRAPH_NODE: 6,
|
|
50
|
-
};
|
|
51
|
-
ENGINE_TYPES$1 = Object.freeze(ENGINE_TYPES$1);
|
|
52
|
-
|
|
53
|
-
var ENGINE_TYPES = ENGINE_TYPES$1;
|
|
54
|
-
|
|
55
43
|
class MissingAttributes extends DALEngineError {
|
|
56
44
|
constructor (type, attribute) {
|
|
57
45
|
let msg;
|
|
@@ -64,6 +52,18 @@ class MissingAttributes extends DALEngineError {
|
|
|
64
52
|
}
|
|
65
53
|
}
|
|
66
54
|
|
|
55
|
+
let ENGINE_TYPES$1 = {
|
|
56
|
+
BEHAVIOR: 1,
|
|
57
|
+
INVARIANT: 2,
|
|
58
|
+
PARTICIPANT: 3,
|
|
59
|
+
PRIMITIVE: 4,
|
|
60
|
+
BEHAVIORAL_CONTROL_GRAPH: 5,
|
|
61
|
+
GRAPH_NODE: 6,
|
|
62
|
+
};
|
|
63
|
+
ENGINE_TYPES$1 = Object.freeze(ENGINE_TYPES$1);
|
|
64
|
+
|
|
65
|
+
var ENGINE_TYPES = ENGINE_TYPES$1;
|
|
66
|
+
|
|
67
67
|
/**
|
|
68
68
|
* Class representing a Invariant in the design.
|
|
69
69
|
*/
|
|
@@ -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,15 @@ 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 = [];
|
|
390
|
+
this._isAtomic = false;
|
|
389
391
|
if (typeof args === "object" && args !== null) {
|
|
390
392
|
if (Object.hasOwn(args, "uid")) {
|
|
391
|
-
this.
|
|
393
|
+
this._loadNodeFromJSON(args);
|
|
392
394
|
} else {
|
|
393
|
-
this.
|
|
394
|
-
this.
|
|
395
|
+
this._behavior = args.behavior;
|
|
396
|
+
this._goToBehaviorIds = args.goToBehaviorsIds;
|
|
395
397
|
}
|
|
396
398
|
}
|
|
397
399
|
}
|
|
@@ -400,37 +402,87 @@ class GraphNode extends Base {
|
|
|
400
402
|
* Loads the nodes from a JSON object.
|
|
401
403
|
* @param {Object} nodesJSON
|
|
402
404
|
*/
|
|
403
|
-
|
|
405
|
+
_loadNodeFromJSON (nodesJSON) {
|
|
404
406
|
for (const [key, value] of Object.entries(nodesJSON)) {
|
|
405
407
|
if (key === "behavior") {
|
|
406
|
-
this.
|
|
408
|
+
this._behavior = new Behavior(value);
|
|
407
409
|
} else if (key === "goToBehaviorsIds") {
|
|
408
|
-
value.forEach(behaviorId => this.
|
|
410
|
+
value.forEach(behaviorId => this._goToBehaviorIds.push(behaviorId));
|
|
409
411
|
} else {
|
|
410
412
|
this[key] = nodesJSON[key];
|
|
411
413
|
}
|
|
412
414
|
} }
|
|
413
415
|
|
|
414
416
|
/**
|
|
415
|
-
*
|
|
417
|
+
* Returns the behavior of the node.
|
|
418
|
+
* @returns {Behavior|Null}
|
|
419
|
+
*/
|
|
420
|
+
getBehavior () {
|
|
421
|
+
return this._behavior;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
/**
|
|
425
|
+
* Returns the list of behavior names that this node transitions to.
|
|
426
|
+
* @returns {Array}
|
|
427
|
+
*/
|
|
428
|
+
getGoToBehaviors () {
|
|
429
|
+
return this._goToBehaviorIds;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
/**
|
|
433
|
+
* Adds a behavior name to the list of behaviors that this
|
|
416
434
|
* node transitions to.
|
|
417
435
|
* @param {String} behaviorId ID of behavior.
|
|
418
436
|
*/
|
|
419
437
|
addGoToBehavior (behaviorId) {
|
|
420
|
-
this.
|
|
438
|
+
this._goToBehaviorIds.push(behaviorId);
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
/**
|
|
442
|
+
* Adds a behavior name to the list of behaviors that this
|
|
443
|
+
* node transitions to.
|
|
444
|
+
* @param {Array} behaviorIds IDs of behaviors.
|
|
445
|
+
*/
|
|
446
|
+
addGoToBehaviors (behaviorIds) {
|
|
447
|
+
this._goToBehaviorIds.push(...behaviorIds);
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
/**
|
|
451
|
+
* Remove the behavior from the list of transitions.
|
|
452
|
+
* @param {String} behaviorId
|
|
453
|
+
*/
|
|
454
|
+
removeGoToBehavior (behaviorId) {
|
|
455
|
+
const goToIndex = this._goToBehaviorIds.indexOf(behaviorId);
|
|
456
|
+
if (goToIndex > -1) {
|
|
457
|
+
this._goToBehaviorIds.splice(goToIndex, 1);
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
/**
|
|
462
|
+
* Raises a flag to indicate if the behavior is atomic or not.
|
|
463
|
+
* @param {Boolean} isAtomic Flag indicates if the behavior is atomic.
|
|
464
|
+
*/
|
|
465
|
+
setAtomic (isAtomic) {
|
|
466
|
+
this._isAtomic = isAtomic;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
/**
|
|
470
|
+
* Returns whether the behavior is atomic or not.
|
|
471
|
+
* @returns {Boolean}
|
|
472
|
+
*/
|
|
473
|
+
isAtomic () {
|
|
474
|
+
return this._isAtomic;
|
|
421
475
|
}
|
|
422
476
|
|
|
423
477
|
/**
|
|
424
478
|
* Checks if the provided behavior name is a valid
|
|
425
|
-
*
|
|
426
|
-
* result of the this nodes state transformation. i.e.
|
|
427
|
-
* is this behavior in the goToBehavior list.
|
|
479
|
+
* transition from this node.
|
|
428
480
|
* @param {String} behaviorName
|
|
429
481
|
* @returns {Boolean}
|
|
430
482
|
*/
|
|
431
|
-
|
|
432
|
-
for (let i = 0; i < this.
|
|
433
|
-
const behaviorId = this.
|
|
483
|
+
isValidTransition (behaviorName) {
|
|
484
|
+
for (let i = 0; i < this._goToBehaviorIds.length; i++) {
|
|
485
|
+
const behaviorId = this._goToBehaviorIds[i];
|
|
434
486
|
if (behaviorId === behaviorName) {
|
|
435
487
|
return true;
|
|
436
488
|
}
|
|
@@ -454,7 +506,7 @@ class BehavioralControlGraph extends Base {
|
|
|
454
506
|
this.nodes = [];
|
|
455
507
|
if (typeof args === "object" && args !== null) {
|
|
456
508
|
if (Object.hasOwn(args, "uid")) {
|
|
457
|
-
this.
|
|
509
|
+
this._loadGraphFromJSON(args);
|
|
458
510
|
} else {
|
|
459
511
|
this.name = args.name;
|
|
460
512
|
}
|
|
@@ -465,7 +517,7 @@ class BehavioralControlGraph extends Base {
|
|
|
465
517
|
* Loads the graph from a JSON object..
|
|
466
518
|
* @param {Object} graphJson
|
|
467
519
|
*/
|
|
468
|
-
|
|
520
|
+
_loadGraphFromJSON (graphJson) {
|
|
469
521
|
for (const [key, value] of Object.entries(graphJson)) {
|
|
470
522
|
if (key === "nodes") {
|
|
471
523
|
value.forEach(node => this.nodes.push(new GraphNode(node)));
|
|
@@ -476,14 +528,16 @@ class BehavioralControlGraph extends Base {
|
|
|
476
528
|
|
|
477
529
|
/**
|
|
478
530
|
* Adds a node to the graph.
|
|
479
|
-
* @param {Behavior}
|
|
531
|
+
* @param {Behavior} behaviorId
|
|
480
532
|
* @param {Array} goToBehaviorsIds
|
|
533
|
+
* @param {Boolean} isAtomic
|
|
481
534
|
* @returns
|
|
482
535
|
*/
|
|
483
|
-
|
|
536
|
+
_addNode (behaviorId, goToBehaviorsIds, isAtomic) {
|
|
484
537
|
const node = new GraphNode({
|
|
485
|
-
behavior:
|
|
486
|
-
goToBehaviorsIds: goToBehaviorsIds,
|
|
538
|
+
behavior: new Behavior({name: behaviorId}),
|
|
539
|
+
goToBehaviorsIds: goToBehaviorsIds?goToBehaviorsIds:[],
|
|
540
|
+
isAtomic: isAtomic?isAtomic:false,
|
|
487
541
|
});
|
|
488
542
|
this.nodes.push(node);
|
|
489
543
|
return node;
|
|
@@ -497,9 +551,9 @@ class BehavioralControlGraph extends Base {
|
|
|
497
551
|
* does not exist in the graph.
|
|
498
552
|
* @returns
|
|
499
553
|
*/
|
|
500
|
-
|
|
554
|
+
_findNode (behaviorName) {
|
|
501
555
|
for (let i = 0; i < this.nodes.length; i++) {
|
|
502
|
-
const behavior = this.nodes[i].
|
|
556
|
+
const behavior = this.nodes[i].getBehavior();
|
|
503
557
|
if (behavior.name === behaviorName) {
|
|
504
558
|
return this.nodes[i];
|
|
505
559
|
}
|
|
@@ -516,8 +570,8 @@ class BehavioralControlGraph extends Base {
|
|
|
516
570
|
*
|
|
517
571
|
* @param {String} behaviorName
|
|
518
572
|
*/
|
|
519
|
-
|
|
520
|
-
const node = this.
|
|
573
|
+
_setCurrentBehavior (behaviorName) {
|
|
574
|
+
const node = this._findNode(behaviorName);
|
|
521
575
|
/**
|
|
522
576
|
* TODO: Ensure it is atomic because the execution
|
|
523
577
|
* will only set a behavior when its the first one.
|
|
@@ -533,11 +587,11 @@ class BehavioralControlGraph extends Base {
|
|
|
533
587
|
* @throws {InvalidTransitionError} Raised when the provided
|
|
534
588
|
* behavior is not a valid transition.
|
|
535
589
|
*/
|
|
536
|
-
|
|
537
|
-
if (this.currentNode.
|
|
538
|
-
this.currentNode = this.
|
|
590
|
+
_goToBehavior (nextBehaviorName) {
|
|
591
|
+
if (this.currentNode.isValidTransition(nextBehaviorName)) {
|
|
592
|
+
this.currentNode = this._findNode(nextBehaviorName);
|
|
539
593
|
} else {
|
|
540
|
-
throw new InvalidTransitionError(this.currentNode.
|
|
594
|
+
throw new InvalidTransitionError(this.currentNode.getBehavior().name, nextBehaviorName);
|
|
541
595
|
}
|
|
542
596
|
}
|
|
543
597
|
|
|
@@ -548,8 +602,8 @@ class BehavioralControlGraph extends Base {
|
|
|
548
602
|
exportAsMermaid () {
|
|
549
603
|
let mermaid = "flowchart TD\n";
|
|
550
604
|
this.nodes.forEach((node) => {
|
|
551
|
-
node.
|
|
552
|
-
mermaid += ` ${node.
|
|
605
|
+
node.getGoToBehaviors().forEach((behaviorId) => {
|
|
606
|
+
mermaid += ` ${node.getBehavior().name} --> ${behaviorId}\n`;
|
|
553
607
|
});
|
|
554
608
|
});
|
|
555
609
|
return mermaid;
|
|
@@ -572,6 +626,7 @@ class BehavioralControlGraph extends Base {
|
|
|
572
626
|
class DALEngine {
|
|
573
627
|
constructor (args) {
|
|
574
628
|
this.graph = new BehavioralControlGraph();
|
|
629
|
+
this.atomicGraphs = [];
|
|
575
630
|
this.loadArgs(args);
|
|
576
631
|
}
|
|
577
632
|
|
|
@@ -607,8 +662,7 @@ class DALEngine {
|
|
|
607
662
|
* @param {String} jsonText
|
|
608
663
|
*/
|
|
609
664
|
deserialize (jsonText) {
|
|
610
|
-
this.graph = new BehavioralControlGraph();
|
|
611
|
-
this.graph.loadGraphFromJSON(JSON.parse(jsonText));
|
|
665
|
+
this.graph = new BehavioralControlGraph(JSON.parse(jsonText));
|
|
612
666
|
}
|
|
613
667
|
|
|
614
668
|
/**
|
|
@@ -645,34 +699,49 @@ class DALEngine {
|
|
|
645
699
|
* @returns {GraphNode}
|
|
646
700
|
*/
|
|
647
701
|
getNode (behaviorId) {
|
|
648
|
-
return this.graph.
|
|
702
|
+
return this.graph._findNode(behaviorId);
|
|
649
703
|
}
|
|
650
704
|
|
|
651
705
|
/**
|
|
652
706
|
* Adds a node to the graph with the given behaviorId and goToBehaviors.
|
|
653
707
|
* @param {String} behaviorId
|
|
654
708
|
* @param {Array} goToBehaviorsIds
|
|
709
|
+
* @param {Boolean} isAtomic
|
|
655
710
|
* @returns {GraphNode}
|
|
656
711
|
*/
|
|
657
|
-
addNode (behaviorId, goToBehaviorsIds) {
|
|
658
|
-
|
|
659
|
-
return this.graph.addNode(behavior, goToBehaviorsIds);
|
|
712
|
+
addNode (behaviorId, goToBehaviorsIds, isAtomic) {
|
|
713
|
+
return this.graph._addNode(behaviorId, goToBehaviorsIds, isAtomic);
|
|
660
714
|
}
|
|
661
715
|
|
|
662
716
|
/**
|
|
663
|
-
*
|
|
717
|
+
* Deletes a node from the graph with the given behaviorId and
|
|
718
|
+
* removes it from the goToBehavior list of all other nodes.
|
|
664
719
|
* @param {String} behaviorId
|
|
665
|
-
* @param {String|Array} goToBehaviorIds
|
|
666
|
-
* @returns {GraphNode}
|
|
667
720
|
*/
|
|
668
|
-
|
|
669
|
-
const node = this.graph.
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
node.
|
|
721
|
+
removeNode (behaviorId) {
|
|
722
|
+
const node = this.graph._findNode(behaviorId);
|
|
723
|
+
const nodeIndex = this.graph.nodes.indexOf(node);
|
|
724
|
+
this.graph.nodes.splice(nodeIndex, 1);
|
|
725
|
+
for (const node of this.graph.nodes) {
|
|
726
|
+
node.removeGoToBehavior(behaviorId);
|
|
674
727
|
}
|
|
675
|
-
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
/**
|
|
731
|
+
* Sets the current behavior in the graph.
|
|
732
|
+
* @param {String} behaviorId
|
|
733
|
+
*/
|
|
734
|
+
setCurrentBehavior (behaviorId) {
|
|
735
|
+
this.graph._setCurrentBehavior(behaviorId);
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
/**
|
|
739
|
+
* Transitions the graph to the given behavior if it
|
|
740
|
+
* is a valid transition from the current behavior.
|
|
741
|
+
* @param {String} nextBehaviorId ID of the next behavior.
|
|
742
|
+
*/
|
|
743
|
+
goToBehavior (nextBehaviorId) {
|
|
744
|
+
this.graph._goToBehavior(nextBehaviorId);
|
|
676
745
|
}
|
|
677
746
|
}
|
|
678
747
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import Base from "../Base";
|
|
2
2
|
import InvalidTransitionError from "../Errors/InvalidTransitionError";
|
|
3
3
|
import UnknownBehaviorError from "../Errors/UnknownBehaviorError";
|
|
4
|
+
import Behavior from "../Members/Behavior";
|
|
4
5
|
import ENGINE_TYPES from "../TYPES";
|
|
5
6
|
import GraphNode from "./GraphNode";
|
|
6
7
|
|
|
@@ -19,7 +20,7 @@ class BehavioralControlGraph extends Base {
|
|
|
19
20
|
this.nodes = [];
|
|
20
21
|
if (typeof args === "object" && args !== null) {
|
|
21
22
|
if (Object.hasOwn(args, "uid")) {
|
|
22
|
-
this.
|
|
23
|
+
this._loadGraphFromJSON(args);
|
|
23
24
|
} else {
|
|
24
25
|
this.name = args.name;
|
|
25
26
|
}
|
|
@@ -30,7 +31,7 @@ class BehavioralControlGraph extends Base {
|
|
|
30
31
|
* Loads the graph from a JSON object..
|
|
31
32
|
* @param {Object} graphJson
|
|
32
33
|
*/
|
|
33
|
-
|
|
34
|
+
_loadGraphFromJSON (graphJson) {
|
|
34
35
|
for (const [key, value] of Object.entries(graphJson)) {
|
|
35
36
|
if (key === "nodes") {
|
|
36
37
|
value.forEach(node => this.nodes.push(new GraphNode(node)));
|
|
@@ -42,14 +43,16 @@ class BehavioralControlGraph extends Base {
|
|
|
42
43
|
|
|
43
44
|
/**
|
|
44
45
|
* Adds a node to the graph.
|
|
45
|
-
* @param {Behavior}
|
|
46
|
+
* @param {Behavior} behaviorId
|
|
46
47
|
* @param {Array} goToBehaviorsIds
|
|
48
|
+
* @param {Boolean} isAtomic
|
|
47
49
|
* @returns
|
|
48
50
|
*/
|
|
49
|
-
|
|
51
|
+
_addNode (behaviorId, goToBehaviorsIds, isAtomic) {
|
|
50
52
|
const node = new GraphNode({
|
|
51
|
-
behavior:
|
|
52
|
-
goToBehaviorsIds: goToBehaviorsIds,
|
|
53
|
+
behavior: new Behavior({name: behaviorId}),
|
|
54
|
+
goToBehaviorsIds: goToBehaviorsIds?goToBehaviorsIds:[],
|
|
55
|
+
isAtomic: isAtomic?isAtomic:false,
|
|
53
56
|
});
|
|
54
57
|
this.nodes.push(node);
|
|
55
58
|
return node;
|
|
@@ -63,9 +66,9 @@ class BehavioralControlGraph extends Base {
|
|
|
63
66
|
* does not exist in the graph.
|
|
64
67
|
* @returns
|
|
65
68
|
*/
|
|
66
|
-
|
|
69
|
+
_findNode (behaviorName) {
|
|
67
70
|
for (let i = 0; i < this.nodes.length; i++) {
|
|
68
|
-
const behavior = this.nodes[i].
|
|
71
|
+
const behavior = this.nodes[i].getBehavior();
|
|
69
72
|
if (behavior.name === behaviorName) {
|
|
70
73
|
return this.nodes[i];
|
|
71
74
|
}
|
|
@@ -82,8 +85,8 @@ class BehavioralControlGraph extends Base {
|
|
|
82
85
|
*
|
|
83
86
|
* @param {String} behaviorName
|
|
84
87
|
*/
|
|
85
|
-
|
|
86
|
-
const node = this.
|
|
88
|
+
_setCurrentBehavior (behaviorName) {
|
|
89
|
+
const node = this._findNode(behaviorName);
|
|
87
90
|
/**
|
|
88
91
|
* TODO: Ensure it is atomic because the execution
|
|
89
92
|
* will only set a behavior when its the first one.
|
|
@@ -99,11 +102,11 @@ class BehavioralControlGraph extends Base {
|
|
|
99
102
|
* @throws {InvalidTransitionError} Raised when the provided
|
|
100
103
|
* behavior is not a valid transition.
|
|
101
104
|
*/
|
|
102
|
-
|
|
103
|
-
if (this.currentNode.
|
|
104
|
-
this.currentNode = this.
|
|
105
|
+
_goToBehavior (nextBehaviorName) {
|
|
106
|
+
if (this.currentNode.isValidTransition(nextBehaviorName)) {
|
|
107
|
+
this.currentNode = this._findNode(nextBehaviorName);
|
|
105
108
|
} else {
|
|
106
|
-
throw new InvalidTransitionError(this.currentNode.
|
|
109
|
+
throw new InvalidTransitionError(this.currentNode.getBehavior().name, nextBehaviorName);
|
|
107
110
|
}
|
|
108
111
|
}
|
|
109
112
|
|
|
@@ -114,8 +117,8 @@ class BehavioralControlGraph extends Base {
|
|
|
114
117
|
exportAsMermaid () {
|
|
115
118
|
let mermaid = "flowchart TD\n";
|
|
116
119
|
this.nodes.forEach((node) => {
|
|
117
|
-
node.
|
|
118
|
-
mermaid += ` ${node.
|
|
120
|
+
node.getGoToBehaviors().forEach((behaviorId) => {
|
|
121
|
+
mermaid += ` ${node.getBehavior().name} --> ${behaviorId}\n`;
|
|
119
122
|
});
|
|
120
123
|
});
|
|
121
124
|
return mermaid;
|
|
@@ -13,13 +13,15 @@ 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 = [];
|
|
18
|
+
this._isAtomic = false;
|
|
17
19
|
if (typeof args === "object" && args !== null) {
|
|
18
20
|
if (Object.hasOwn(args, "uid")) {
|
|
19
|
-
this.
|
|
21
|
+
this._loadNodeFromJSON(args);
|
|
20
22
|
} else {
|
|
21
|
-
this.
|
|
22
|
-
this.
|
|
23
|
+
this._behavior = args.behavior;
|
|
24
|
+
this._goToBehaviorIds = args.goToBehaviorsIds;
|
|
23
25
|
}
|
|
24
26
|
}
|
|
25
27
|
}
|
|
@@ -28,12 +30,12 @@ class GraphNode extends Base {
|
|
|
28
30
|
* Loads the nodes from a JSON object.
|
|
29
31
|
* @param {Object} nodesJSON
|
|
30
32
|
*/
|
|
31
|
-
|
|
33
|
+
_loadNodeFromJSON (nodesJSON) {
|
|
32
34
|
for (const [key, value] of Object.entries(nodesJSON)) {
|
|
33
35
|
if (key === "behavior") {
|
|
34
|
-
this.
|
|
36
|
+
this._behavior = new Behavior(value);
|
|
35
37
|
} else if (key === "goToBehaviorsIds") {
|
|
36
|
-
value.forEach(behaviorId => this.
|
|
38
|
+
value.forEach(behaviorId => this._goToBehaviorIds.push(behaviorId));
|
|
37
39
|
} else {
|
|
38
40
|
this[key] = nodesJSON[key];
|
|
39
41
|
}
|
|
@@ -41,25 +43,75 @@ class GraphNode extends Base {
|
|
|
41
43
|
}
|
|
42
44
|
|
|
43
45
|
/**
|
|
44
|
-
*
|
|
46
|
+
* Returns the behavior of the node.
|
|
47
|
+
* @returns {Behavior|Null}
|
|
48
|
+
*/
|
|
49
|
+
getBehavior () {
|
|
50
|
+
return this._behavior;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Returns the list of behavior names that this node transitions to.
|
|
55
|
+
* @returns {Array}
|
|
56
|
+
*/
|
|
57
|
+
getGoToBehaviors () {
|
|
58
|
+
return this._goToBehaviorIds;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Adds a behavior name to the list of behaviors that this
|
|
45
63
|
* node transitions to.
|
|
46
64
|
* @param {String} behaviorId ID of behavior.
|
|
47
65
|
*/
|
|
48
66
|
addGoToBehavior (behaviorId) {
|
|
49
|
-
this.
|
|
67
|
+
this._goToBehaviorIds.push(behaviorId);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Adds a behavior name to the list of behaviors that this
|
|
72
|
+
* node transitions to.
|
|
73
|
+
* @param {Array} behaviorIds IDs of behaviors.
|
|
74
|
+
*/
|
|
75
|
+
addGoToBehaviors (behaviorIds) {
|
|
76
|
+
this._goToBehaviorIds.push(...behaviorIds);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Remove the behavior from the list of transitions.
|
|
81
|
+
* @param {String} behaviorId
|
|
82
|
+
*/
|
|
83
|
+
removeGoToBehavior (behaviorId) {
|
|
84
|
+
const goToIndex = this._goToBehaviorIds.indexOf(behaviorId);
|
|
85
|
+
if (goToIndex > -1) {
|
|
86
|
+
this._goToBehaviorIds.splice(goToIndex, 1);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Raises a flag to indicate if the behavior is atomic or not.
|
|
92
|
+
* @param {Boolean} isAtomic Flag indicates if the behavior is atomic.
|
|
93
|
+
*/
|
|
94
|
+
setAtomic (isAtomic) {
|
|
95
|
+
this._isAtomic = isAtomic;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Returns whether the behavior is atomic or not.
|
|
100
|
+
* @returns {Boolean}
|
|
101
|
+
*/
|
|
102
|
+
isAtomic () {
|
|
103
|
+
return this._isAtomic;
|
|
50
104
|
}
|
|
51
105
|
|
|
52
106
|
/**
|
|
53
107
|
* Checks if the provided behavior name is a valid
|
|
54
|
-
*
|
|
55
|
-
* result of the this nodes state transformation. i.e.
|
|
56
|
-
* is this behavior in the goToBehavior list.
|
|
108
|
+
* transition from this node.
|
|
57
109
|
* @param {String} behaviorName
|
|
58
110
|
* @returns {Boolean}
|
|
59
111
|
*/
|
|
60
|
-
|
|
61
|
-
for (let i = 0; i < this.
|
|
62
|
-
const behaviorId = this.
|
|
112
|
+
isValidTransition (behaviorName) {
|
|
113
|
+
for (let i = 0; i < this._goToBehaviorIds.length; i++) {
|
|
114
|
+
const behaviorId = this._goToBehaviorIds[i];
|
|
63
115
|
if (behaviorId === behaviorName) {
|
|
64
116
|
return true;
|
|
65
117
|
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import BehavioralControlGraph from "./BehavioralControlGraph";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Class representing a collection of atomic graphs.
|
|
5
|
+
*
|
|
6
|
+
* These graphs together represent the design. I chose to separate
|
|
7
|
+
* them into their graphs because as the design grows larger, it will
|
|
8
|
+
* be easier to manage the design if it is separated into smaller graphs.
|
|
9
|
+
* It is also easier to visualize in the UI in managable way.
|
|
10
|
+
*/
|
|
11
|
+
export class Graphs {
|
|
12
|
+
|
|
13
|
+
constructor () {
|
|
14
|
+
this._graphs = {};
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Adds a graph to the collection of graphs.
|
|
19
|
+
* @param {String} graphId ID of the graph.
|
|
20
|
+
* @returns {BehavioralControlGraph} The graph that was added.
|
|
21
|
+
*/
|
|
22
|
+
addGraph (graphId) {
|
|
23
|
+
this._graphs[graphId] = new BehavioralControlGraph();
|
|
24
|
+
return this._graphs[graphId];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Returns the graph with the given graphId.
|
|
29
|
+
* @param {String} graphId ID of the graph to return.
|
|
30
|
+
* @returns {BehavioralControlGraph} The graph with the given graphId.
|
|
31
|
+
*/
|
|
32
|
+
getGraph (graphId) {
|
|
33
|
+
return this._graphs[graphId];
|
|
34
|
+
}
|
|
35
|
+
}
|