// Calibration helpers for the pumping-station predicted volume / level // streams. Pure functions over a context bag holding the live // MeasurementContainer + basin geometry. After every calibration the // integrator state is reset so the next tick starts from the new anchor. function _resetFlowState(ctx, timestamp) { if (ctx.flowAggregator?.resetState) { ctx.flowAggregator.resetState(timestamp); return; } ctx._predictedFlowState = { inflow: 0, outflow: 0, lastTimestamp: timestamp }; } function _clearSeries(measurements, type) { const series = measurements.type(type).variant('predicted').position('atequipment'); if (series.exists()) { const m = series.get(); if (m) { m.values = []; m.timestamps = []; } } } function _levelFromVolume(basin, volume) { const area = basin.surfaceArea; return area > 0 ? Math.max(volume, 0) / area : 0; } function _volumeFromLevel(basin, level) { const area = basin.surfaceArea; return area > 0 ? Math.max(level, 0) * area : 0; } function calibratePredictedVolume(ctx, calibratedVol, timestamp = Date.now()) { if (!ctx?.measurements || !ctx.basin) { throw new Error('calibratePredictedVolume: ctx.measurements and ctx.basin required'); } const { measurements, basin } = ctx; _clearSeries(measurements, 'volume'); _clearSeries(measurements, 'level'); measurements.type('volume').variant('predicted').position('atequipment') .value(calibratedVol, timestamp, 'm3').unit('m3'); measurements.type('level').variant('predicted').position('atequipment') .value(_levelFromVolume(basin, calibratedVol), timestamp, 'm'); _resetFlowState(ctx, timestamp); } function calibratePredictedLevel(ctx, level, timestamp = Date.now(), unit = 'm') { if (!ctx?.measurements || !ctx.basin) { throw new Error('calibratePredictedLevel: ctx.measurements and ctx.basin required'); } const { measurements, basin } = ctx; _clearSeries(measurements, 'volume'); _clearSeries(measurements, 'level'); measurements.type('level').variant('predicted').position('atequipment') .value(level, timestamp, unit); measurements.type('volume').variant('predicted').position('atequipment') .value(_volumeFromLevel(basin, level), timestamp, 'm3'); _resetFlowState(ctx, timestamp); } function setManualInflow(ctx, value, timestamp = Date.now(), unit = 'm3/s') { if (!ctx?.measurements) throw new Error('setManualInflow: ctx.measurements required'); const num = Number(value); ctx.measurements.type('flow').variant('predicted').position('in').child('manual-qin') .value(num, timestamp, unit); } module.exports = { calibratePredictedVolume, calibratePredictedLevel, setManualInflow, };