const {Component} = require("./Component");
const {Synchronize} = require("./Synchronize");
const {EventBase, EventBusBase, EventListener} = require("./EventBase");
const {checkType, createHiddenProp} = require("./Utility");
const {EventFilter} = require("./EventFilter");
/**
* An event for comunicating between significance components
*
* @extends EventBase
*/
class SignificanceEvent extends EventBase {
/**
* Creates a new event to be sent over the significance bus
*
* @param {string} sourceName - The name of the source of the event.
* @param {string} sourceType - The type of the source.
*/
constructor(sourceName, sourceType) {
super();
checkType("SignificanceEvent.constructor", "sourceName", sourceName, "string");
checkType("SignificanceEvent.constructor", "sourceType", sourceType, "string");
createHiddenProp(this, "_sourceName", sourceName, true);
createHiddenProp(this, "_sourceType", sourceType, true);
}
// eslint-disable-next-line jsdoc/require-jsdoc
get sourceName() {
return this._sourceName || "initializing";
}
// eslint-disable-next-line jsdoc/require-jsdoc
get sourceType() {
return this._sourceType || "initializing";
}
// eslint-disable-next-line jsdoc/require-jsdoc
get allowedEventTypes() {
return new Set(["register", "init", "change", "significance"]);
}
// eslint-disable-next-line jsdoc/require-jsdoc
get eventBus() {
return Significance.eventBus;
}
}
const significanceEventBus = new EventBusBase(SignificanceEvent);
const weightingMap = new Map();
/**
* A component for handling signficiance
*
* @extends Component
*/
class Significance extends Component {
/**
* Creates a new Significance object
*/
constructor() {
super("significance", "significance", SignificanceEvent);
createHiddenProp(this, "_changeList", new Set());
let filter = new EventFilter("allow", {eventType: "change", all: true});
new EventListener(Significance.eventBus, filter, this.getChange.bind(this));
}
/**
* Collects change events
*
* @param {EventBusBase} evt The intrinsic event
*/
getChange(evt) {
this._changeList.add(evt.data);
}
/**
* Every tick, collect Intrinsic changes, roll them up, and emit them as a significance event
*/
async onTick() {
// collect changes
let changes = [... this._changeList.values()].map((v) => {
return {type: v.intrinsic.name, val: v.newNormVal};
});
// calculate weights
changes.forEach((c) => c.weightedVal = Significance.getWeight(c.type) * c.val);
// calculate significance
let significance = changes.reduce((sig, c) => sig + c.weightedVal, 0);
// remove old changes
this._changeList.clear();
// emit event
return this.sendEvent("significance", {
significance,
changes,
});
}
/** the significance event bus, for communicating between perception {@link Component|Components} */
static get eventBus() {
return significanceEventBus;
}
/**
* Set the weight of a specific intrinsic value
*
* @param {string} intrinsicName The name of the intrinsic
* @param {string} weightingFactor The weight factor
*/
static setWeight(intrinsicName, weightingFactor) {
checkType("setWeighting", "intrinsicName", intrinsicName, "string");
checkType("setWeighting", "weightingFactor", weightingFactor, "number");
weightingMap.set(intrinsicName, weightingFactor);
}
/**
* Returns the weight for an intrinsic
*
* @param {string} intrinsicName The intrinsic weight value to get
*/
static getWeight(intrinsicName) {
checkType("setWeighting", "intrinsicName", intrinsicName, "string");
let weight = weightingMap.get(intrinsicName);
return weight || 1.0;
}
/**
* Clears the previously set weights. Mostly used for testing.
*/
static clearWeights() {
weightingMap.clear();
}
/**
* Initializes the Significance singleton
*/
static async init() {
// TODO: need to clean this up during shutdown
let s = new Significance();
await Synchronize.register(s.onTick.bind(s));
return s;
}
/**
* Shuts down the Significance singleton
*/
static async shutdown() {
}
}
module.exports = {
Significance,
SignificanceEvent,
};