You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
55 lines
1.7 KiB
55 lines
1.7 KiB
import assert from "./assert.js";
|
|
import { log } from "./logging.js";
|
|
|
|
export class FSM {
|
|
|
|
constructor(data, events) {
|
|
// data is really only for logging/debugging
|
|
this.data = data;
|
|
this.events = events;
|
|
this.state = "START";
|
|
this.on_state = undefined;
|
|
}
|
|
|
|
onTransition(cb) {
|
|
this.transition_cb = cb;
|
|
}
|
|
|
|
transition(next_state) {
|
|
this.state = next_state;
|
|
|
|
if(this.transition_cb) {
|
|
this.transition_cb(this);
|
|
}
|
|
}
|
|
|
|
event_names() {
|
|
return Object.getOwnPropertyNames(Object.getPrototypeOf(this.events)).filter(k => k !== "constructor");
|
|
}
|
|
|
|
async do(event, ...args) {
|
|
const evhandler = this.events[event];
|
|
assert(evhandler !== undefined, `Invalid event ${event}. Available ones are '${this.event_names()}'.`);
|
|
|
|
// NOTE: you have to use .call to pass in the this.events object or else this === undefined in the call
|
|
const next_state = await evhandler.call(this.events, this.state, ...args);
|
|
assert(next_state, `The event "${event}" returned "${next_state}" but must be a truthy true state.`);
|
|
|
|
if(Array.isArray(next_state)) {
|
|
assert(next_state.length == 2, `Returning an array only allows 2 elements (state, func) but you returned ${next_state}`);
|
|
let [state, func] = next_state;
|
|
|
|
log.debug(`FSM ${this.events.constructor.name}: (${event}) = ${this.state} -> ${state} (${args})`, "DATA:", this.data, func ? `THEN ${func.name}()` : undefined);
|
|
|
|
this.transition(state);
|
|
await func();
|
|
} else {
|
|
log.debug(`FSM ${this.events.constructor.name}: (${event}) = ${this.state} -> ${next_state} (${args})`, "DATA:", this.data);
|
|
this.transition(next_state);
|
|
}
|
|
|
|
return this.state;
|
|
}
|
|
}
|
|
|
|
export default FSM;
|
|
|