const Graph = require("graph-js");
/** Class FSM, finite state machine.
*/
class FSM{
#graph;
#idFirstState;
#idLastState;
constructor(){
this.#graph = new Graph();
this.#idFirstState = null;
this.#idLastState = null;
}
/*#equal(str1, str2){
str1 = str1.toString();
str2 = str2.toString();
return str1.localeCompare(str2) === 0;
}*/
/** This method returns the finite state machine's graph.
* @returns {Graph} the finite state machine's graph.
*/
getGraph(){
return this.#graph;
}
/** This method registers the first state of FSM.
* @param {Behaviour} behaviour a desired behaviour (OneShotBehaviour, CyclicBehaviour, FSMBehaviour...).
* @param {any} id state's identifier (only number or string).
*/
registerFirstState(behaviour, id){
this.#idFirstState = id;
this.#graph.addNode(behaviour, id);
}
/** This method registers the last state of FSM.
* @param {Behaviour} behaviour a desired behaviour (OneShotBehaviour, CyclicBehaviour, FSMBehaviour...).
* @param {any} id state's identifier (only number or string).
*/
registerLastState(behaviour, id){
this.#idLastState = id;
this.#graph.addNode(behaviour, id);
}
/** This method registers a state of FSM.
* @param {Behaviour} behaviour a desired behaviour (OneShotBehaviour, CyclicBehaviour, FSMBehaviour...).
* @param {any} id state's identifier (only number or string).
*/
registerState(behaviour, id){
this.#graph.addNode(behaviour, id);
}
/** This method registers the default transition.
* @param {any} idStateStart the start state's identifier.
* @param {any} idStateEnd the end state's identifier.
*/
registerDefaultTransition(idStateStart, idStateEnd){
this.#graph.addEdge(idStateStart, idStateEnd, "default");
}
/** This method registers the default transition.
* @param {any} idStateStart the start state's identifier (only number or string).
* @param {any} idStateEnd the end state's identifier (only number or string).
* @param {any} id the transition's identifier (only number or string).
*/
registerTransition(idStateStart, idStateEnd, id){
this.#graph.addEdge(idStateStart, idStateEnd, id);
}
#execute(id){
const nodes = this.#graph.getNodes();
const edges = this.#graph.getEdges();
let behaviour = null;
let transition = null;
nodes.forEach((node) => {
if(Graph.equal(node.getId(), id)){
behaviour = node.getContent();
behaviour.action();
while(!behaviour.done()) {
behaviour.action();
}
transition = behaviour.onEnd();
if(!Graph.equal(id, this.#idLastState)){
edges.forEach((edge) => {
if(Graph.equal(edge.getId(), transition)){
this.#execute(edge.getNodeEnd().getId());
}
});
}
}
});
}
/** This method runs the FSM.
*/
run(){
this.#execute(this.#idFirstState);
}
}
module.exports = FSM;