Complete general functions
This commit is contained in:
164
helper/state/stateManager.js
Normal file
164
helper/state/stateManager.js
Normal file
@@ -0,0 +1,164 @@
|
||||
/**
|
||||
* @file stateManager.js
|
||||
*
|
||||
* Permission is hereby granted to any person obtaining a copy of this software
|
||||
* and associated documentation files (the "Software"), to use it for personal
|
||||
* or non-commercial purposes, with the following restrictions:
|
||||
*
|
||||
* 1. **No Copying or Redistribution**: The Software or any of its parts may not
|
||||
* be copied, merged, distributed, sublicensed, or sold without explicit
|
||||
* prior written permission from the author.
|
||||
*
|
||||
* 2. **Commercial Use**: Any use of the Software for commercial purposes requires
|
||||
* a valid license, obtainable only with the explicit consent of the author.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE, AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT, OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF, OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
* Ownership of this code remains solely with the original author. Unauthorized
|
||||
* use of this Software is strictly prohibited.
|
||||
*
|
||||
* @summary Class for managing state transitions and state descriptions.
|
||||
* @description Class for managing state transitions and state descriptions.
|
||||
* @module stateManager
|
||||
* @exports stateManager
|
||||
* @version 0.1.0
|
||||
* @since 0.1.0
|
||||
*
|
||||
* Author:
|
||||
* - Rene De Ren
|
||||
* Email:
|
||||
* - rene@thegoldenbasket.nl
|
||||
*/
|
||||
|
||||
class stateManager {
|
||||
constructor(config, logger) {
|
||||
this.currentState = config.state.current;
|
||||
this.availableStates = config.state.available;
|
||||
this.descriptions = config.state.descriptions;
|
||||
this.logger = logger;
|
||||
this.transitionTimeleft = 0;
|
||||
this.transitionTimes = config.time;
|
||||
|
||||
// Define valid transitions (can be extended dynamically if needed)
|
||||
this.validTransitions = config.state.allowedTransitions;
|
||||
|
||||
// NEW: Initialize runtime tracking
|
||||
this.runTimeHours = 0; // cumulative runtime in hours
|
||||
this.runTimeStart = null; // timestamp when active state began
|
||||
|
||||
// Define active states (runtime counts only in these states)
|
||||
this.activeStates = config.state.activeStates;
|
||||
}
|
||||
|
||||
getCurrentState() {
|
||||
return this.currentState;
|
||||
}
|
||||
|
||||
transitionTo(newState,signal) {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (signal && signal.aborted) {
|
||||
this.logger.debug("Transition aborted.");
|
||||
return reject("Transition aborted.");
|
||||
}
|
||||
|
||||
if (!this.isValidTransition(newState)) {
|
||||
return reject(
|
||||
`Invalid transition from ${this.currentState} to ${newState}. Transition not executed.`
|
||||
); //go back early and reject promise
|
||||
}
|
||||
|
||||
// NEW: Handle runtime tracking based on active states
|
||||
this.handleRuntimeTracking(newState);
|
||||
|
||||
const transitionDuration = this.transitionTimes[this.currentState] || 0; // Default to 0 if no transition time
|
||||
this.logger.debug(
|
||||
`Transition from ${this.currentState} to ${newState} will take ${transitionDuration}s.`
|
||||
);
|
||||
|
||||
if (transitionDuration > 0) {
|
||||
const timeoutId = setTimeout(() => {
|
||||
this.currentState = newState;
|
||||
resolve(`Transition from ${this.currentState} to ${newState} completed in ${transitionDuration}s.`);
|
||||
}, transitionDuration * 1000);
|
||||
if (signal) {
|
||||
signal.addEventListener('abort', () => {
|
||||
clearTimeout(timeoutId);
|
||||
reject(new Error('Transition aborted'));
|
||||
});
|
||||
}
|
||||
} else {
|
||||
this.currentState = newState;
|
||||
resolve(`Immediate transition to ${this.currentState} completed.`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
handleRuntimeTracking(newState) {
|
||||
// NEW: Handle runtime tracking based on active states
|
||||
const wasActive = this.activeStates.has(this.currentState);
|
||||
const willBeActive = this.activeStates.has(newState);
|
||||
if (wasActive && !willBeActive && this.runTimeStart) {
|
||||
// stop runtime timer and accumulate elapsed time
|
||||
const elapsed = (Date.now() - this.runTimeStart) / 3600000; // hours
|
||||
this.runTimeHours += elapsed;
|
||||
this.runTimeStart = null;
|
||||
this.logger.debug(
|
||||
`Runtime timer stopped; elapsed=${elapsed.toFixed(
|
||||
3
|
||||
)}h, total=${this.runTimeHours.toFixed(3)}h.`
|
||||
);
|
||||
} else if (!wasActive && willBeActive && !this.runTimeStart) {
|
||||
// starting new runtime
|
||||
this.runTimeStart = Date.now();
|
||||
this.logger.debug("Runtime timer started.");
|
||||
}
|
||||
}
|
||||
|
||||
isValidTransition(newState) {
|
||||
this.logger.debug(
|
||||
`Check 1 Transition valid ? From ${
|
||||
this.currentState
|
||||
} To ${newState} => ${this.validTransitions[this.currentState]?.has(
|
||||
newState
|
||||
)} `
|
||||
);
|
||||
this.logger.debug(
|
||||
`Check 2 Transition valid ? ${
|
||||
this.currentState
|
||||
} is not equal to ${newState} => ${this.currentState !== newState}`
|
||||
);
|
||||
// check if transition is valid and not the same as before
|
||||
const valid =
|
||||
this.validTransitions[this.currentState]?.has(newState) &&
|
||||
this.currentState !== newState;
|
||||
|
||||
//if not valid
|
||||
if (!valid) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
getStateDescription(state = this.currentState) {
|
||||
return this.descriptions[state] || "No description available.";
|
||||
}
|
||||
|
||||
// NEW: Getter to retrieve current cumulative runtime (active time) in hours.
|
||||
getRunTimeHours() {
|
||||
// If currently active add the ongoing duration.
|
||||
let currentElapsed = 0;
|
||||
if (this.runTimeStart) {
|
||||
currentElapsed = (Date.now() - this.runTimeStart) / 3600000;
|
||||
}
|
||||
return this.runTimeHours + currentElapsed;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = stateManager;
|
||||
Reference in New Issue
Block a user