bhive
Version:
Behavior Tree for Javascript
221 lines (193 loc) • 5.79 kB
JavaScript
const uuid = require('uuid/v4');
const {RUNNING, ERROR} = require('../constants');
/**
* @typedef {object} NodeData
* @property {string} id - uuid of class
* @property {string} type - type of node (e.g. class name)
* @property {string} name - name of node
* @property {NodeCategory} category - categories of nodes
* @property {string} description
*/
/**
* Class BaseNode
*/
class BaseNode {
/**
* @constructor
* @param {object} params
* @param {NodeCategory} params.category
* @param {string} params.type - The type of the node
* (usually the name of the class is used)
* @param {string} params.name - The name for human
* @param {string} params.description
* @param {object} params.properties
**/
constructor({category, type, name, description, properties} = {}) {
/**
* unique identifier of node. It uses the UUID format.
* @type {NodeId}
* @see RFC4122: https://tools.ietf.org/html/rfc4122
**/
this.id = uuid();
/**
* Node category. Must be `COMPOSITE`, `DECORATOR`, `TASK` or
* `CONDITION`. This is defined automatically be inheriting the
* correspondent class.
* @type {NodeCategory}
**/
this.category = category || 'TASK';
/**
* type of node. Must be a unique. e.g. class name
* @type {string}
**/
this.type = type || this.constructor.name;
/**
* Node name.
* @type {string}
**/
this.name = name || this.type;
/**
* Node description.
* @type {string}
*/
this.description = description || '';
/**
* A dictionary (key, value) describing the node properties. Useful for
* defining custom variables inside the visual editor.
*
* @type {Object}
* @readonly
**/
this.properties = properties || {};
}
/**
* @param {Context} context A run instance.
* @returns {NodeStatus} The run state.
*/
tick(context) {
return this._tick(context);
}
/**
* @param {Context} context A run instance.
* @returns {NodeStatus} The run state.
**/
_tick(context) {
// ENTER
this._enter(context);
// OPEN
if (!context.treeBoard.node(this, {safe: true}).isOpen()) {
this._open(context);
}
// RUN
const status = this._run(context);
context.treeBoard.node(this).set('lastStatus', status);
// CLOSE
if (status !== RUNNING) {
this._close(context);
}
// EXIT
this._exit(context);
return status;
}
/**
* Wrapper for enter method.
* @param {Context} context - A run instance.
* @protected
**/
_enter(context) {
context.enterNode(this);
this.enter(context);
}
/**
* Wrapper for open method.
* @param {Context} context - A run instance.
* @protected
**/
_open(context) {
context.openNode(this);
context.treeBoard.node(this).openNode();
this.open(context);
}
/**
* Wrapper for run method.
* @param {Context} context A run instance.
* @return {NodeStatus} A state constant.
* @protected
**/
_run(context) {
try {
context.runNode(this);
const result = this.run(context);
context.logger.DEBUG_debug(`run() result:\t${this.name}: ` + result);
return result;
} catch (e) {
context.logger.DEBUG_err(
`failed to execute run() on node [${this.type}] ${this.name}(${this.id})\n`
+ `${e.message}\n${e.stack}`);
return ERROR;
}
}
/**
* Wrapper for close method.
* @param {Context} context A run instance.
* @returns {void}
**/
_close(context) {
context.closeNode(this);
context.treeBoard.node(this.id).set('isOpen', false);
this.close(context);
}
/**
* Wrapper for exit method.
* @param {Context} context A run instance.
* @protected
**/
_exit(context) {
context.exitNode(this);
this.exit(context);
}
/**
* Enter method, override this to use. It is called every time a node is
* asked to run, before the run itself.
*
* @param {Context} context A run instance.
**/
enter(context) {
}
/**
* Open method, override this to use. It is called only before the run
* callback and only if the not isn't closed.
*
* Note: a node will be closed if it returned `RUNNING` in the run.
*
* @param {Context} context A run instance.
**/
open(context) {
}
/**
* Context method, override this to use. This method must contain the real
* execution of node (perform a task, call children, etc.). It is called
* every time a node is asked to run.
*
* @param {Context} context A run instance.
**/
run(context) {
}
/**
* Close method, override this to use. This method is called after the run
* callback, and only if the run return a state different from
* `RUNNING`.
*
* @param {Context} context A run instance.
**/
close(context) {
}
/**
* Exit method, override this to use. Called every time in the end of the
* execution.
* @param {Context} context A run instance.
**/
exit(context) {
}
}
module.exports = BaseNode;