/** * Simulator — random-walk driver for the measurement input. * * Lifted verbatim from Measurement.simulateInput. The orchestrator decides * what to do with the returned value (originally written to `inputValue`), * so this module owns nothing but the walk and its bounds. */ class Simulator { constructor({ config, logger } = {}) { if (!config || !config.scaling) { throw new Error('Simulator requires { config.scaling }'); } this.config = config; this.logger = logger || { warn() {}, info() {}, debug() {}, error() {} }; const s = config.scaling; this.inputRange = Math.abs(s.inputMax - s.inputMin); this.processRange = Math.abs(s.absMax - s.absMin); this.simValue = 0; } step() { const s = this.config.scaling; const sign = Math.random() < 0.5 ? -1 : 1; let maxStep; if (s.enabled) { // Step size scales with the live input window; fall back to 1 so a // collapsed range still wanders instead of freezing at zero. maxStep = this.inputRange > 0 ? this.inputRange * 0.05 : 1; if (this.simValue < s.inputMin || this.simValue > s.inputMax) { this.logger.warn(`Simulated value ${this.simValue} is outside of input range constraining between min=${s.inputMin} and max=${s.inputMax}`); this.simValue = _constrain(this.simValue, s.inputMin, s.inputMax); } } else { maxStep = this.processRange > 0 ? this.processRange * 0.05 : 1; if (this.simValue < s.absMin || this.simValue > s.absMax) { this.logger.warn(`Simulated value ${this.simValue} is outside of abs range constraining between min=${s.absMin} and max=${s.absMax}`); this.simValue = _constrain(this.simValue, s.absMin, s.absMax); } } this.simValue += sign * Math.random() * maxStep; return this.simValue; } reset() { this.simValue = 0; } get current() { return this.simValue; } } function _constrain(v, lo, hi) { return Math.min(Math.max(v, lo), hi); } module.exports = Simulator;