const MeasurementBuilder = require('./MeasurementBuilder'); class MeasurementContainer { constructor(options = {}, logger) { this.logger = logger; this.measurements = {}; this.windowSize = options.windowSize || 10; // Default window size // For chaining context this._currentType = null; this._currentVariant = null; this._currentPosition = null; } // Chainable methods type(typeName) { this._currentType = typeName; this._currentVariant = null; this._currentPosition = null; return this; } variant(variantName) { if (!this._currentType) { throw new Error('Type must be specified before variant'); } this._currentVariant = variantName; this._currentPosition = null; return this; } position(positionName) { if (!this._currentVariant) { throw new Error('Variant must be specified before position'); } this._currentPosition = positionName; return this; } // Core methods that complete the chain value(val, timestamp = Date.now()) { if (!this._ensureChainIsValid()) return this; const measurement = this._getOrCreateMeasurement(); measurement.setValue(val, timestamp); return this; } unit(unitName) { if (!this._ensureChainIsValid()) return this; const measurement = this._getOrCreateMeasurement(); measurement.setUnit(unitName); return this; } // Terminal operations - get data out get() { if (!this._ensureChainIsValid()) return null; return this._getOrCreateMeasurement(); } getCurrentValue() { const measurement = this.get(); return measurement ? measurement.getCurrentValue() : null; } getAverage() { const measurement = this.get(); return measurement ? measurement.getAverage() : null; } getMin() { const measurement = this.get(); return measurement ? measurement.getMin() : null; } getMax() { const measurement = this.get(); return measurement ? measurement.getMax() : null; } getAllValues() { const measurement = this.get(); return measurement ? measurement.getAllValues() : null; } // Difference calculations between positions difference() { if (!this._currentType || !this._currentVariant) { throw new Error('Type and variant must be specified for difference calculation'); } // Save position to restore chain state after operation const savedPosition = this._currentPosition; // Get upstream measurement this._currentPosition = 'upstream'; const upstream = this.get(); // Get downstream measurement this._currentPosition = 'downstream'; const downstream = this.get(); // Restore chain state this._currentPosition = savedPosition; if (!upstream || !downstream || upstream.values.length === 0 || downstream.values.length === 0) { return null; } // Ensure units match let downstreamForCalc = downstream; if (upstream.unit && downstream.unit && upstream.unit !== downstream.unit) { try { downstreamForCalc = downstream.convertTo(upstream.unit); } catch (error) { if (this.logger) { this.logger.error(`Unit conversion failed: ${error.message}`); } return null; } } return { value: downstreamForCalc.getCurrentValue() - upstream.getCurrentValue() , avgDiff: downstreamForCalc.getAverage() - upstream.getAverage() , unit: upstream.unit }; } // Helper methods _ensureChainIsValid() { if (!this._currentType || !this._currentVariant || !this._currentPosition) { if (this.logger) { this.logger.error('Incomplete measurement chain, required: type, variant, and position'); } return false; } return true; } _getOrCreateMeasurement() { // Initialize nested structure if needed if (!this.measurements[this._currentType]) { this.measurements[this._currentType] = {}; } if (!this.measurements[this._currentType][this._currentVariant]) { this.measurements[this._currentType][this._currentVariant] = {}; } if (!this.measurements[this._currentType][this._currentVariant][this._currentPosition]) { this.measurements[this._currentType][this._currentVariant][this._currentPosition] = new MeasurementBuilder() .setType(this._currentType) .setVariant(this._currentVariant) .setPosition(this._currentPosition) .setWindowSize(this.windowSize) .build(); } return this.measurements[this._currentType][this._currentVariant][this._currentPosition]; } // Additional utility methods getTypes() { return Object.keys(this.measurements); } getVariants() { if (!this._currentType) { throw new Error('Type must be specified before listing variants'); } return this.measurements[this._currentType] ? Object.keys(this.measurements[this._currentType]) : []; } getPositions() { if (!this._currentType || !this._currentVariant) { throw new Error('Type and variant must be specified before listing positions'); } if (!this.measurements[this._currentType] || !this.measurements[this._currentType][this._currentVariant]) { return []; } return Object.keys(this.measurements[this._currentType][this._currentVariant]); } clear() { this.measurements = {}; this._currentType = null; this._currentVariant = null; this._currentPosition = null; } } module.exports = MeasurementContainer;