UNPKG

@citrineos/base

Version:

The base module for OCPP v2.0.1 including all interfaces. This module is not intended to be used directly, but rather as a dependency for other modules.

148 lines 6.84 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.MeterValueUtils = void 0; const model_1 = require("../ocpp/model"); class MeterValueUtils { /** * Calculate the total Kwh * * @param {array} meterValues - meterValues of a transaction. * @return {number} total Kwh based on the best available energy measurement. */ static getTotalKwh(meterValues) { const filteredValues = this.filterValidMeterValues(meterValues); const timestampToKwhMap = this.getTimestampToKwhMap(filteredValues); const sortedValues = this.getSortedKwhByTimestampAscending(timestampToKwhMap); return this.calculateTotalKwh(sortedValues); } static filterValidMeterValues(meterValues) { return meterValues.filter((mv) => // When missing, context is by default Sample_Periodic by spec !mv.sampledValue[0].context || this.validContexts.has(mv.sampledValue[0].context)); } static getTimestampToKwhMap(meterValues) { const valuesMap = new Map(); for (const meterValue of meterValues) { const timestamp = Date.parse(meterValue.timestamp); let energyValue = null; // Try strategies in order of preference // 1. Overall Energy.Active.Import.Register energyValue = this.findMeasurandValue(meterValue.sampledValue, model_1.OCPP2_0_1.MeasurandEnumType.Energy_Active_Import_Register, false); // 2. Energy.Active.Import.Interval if (energyValue === null) { energyValue = this.findMeasurandValue(meterValue.sampledValue, model_1.OCPP2_0_1.MeasurandEnumType.Energy_Active_Import_Interval, false); } // 3. Energy.Active.Net if (energyValue === null) { energyValue = this.findMeasurandValue(meterValue.sampledValue, model_1.OCPP2_0_1.MeasurandEnumType.Energy_Active_Net, false); } // 4. Sum of phased Energy.Active.Import.Register values if (energyValue === null) { energyValue = this.sumPhasedValues(meterValue.sampledValue, model_1.OCPP2_0_1.MeasurandEnumType.Energy_Active_Import_Register); } // 5. Sum of phased Energy.Active.Import.Interval values if (energyValue === null) { energyValue = this.sumPhasedValues(meterValue.sampledValue, model_1.OCPP2_0_1.MeasurandEnumType.Energy_Active_Import_Interval); } // Store the value if we found one if (energyValue !== null) { valuesMap.set(timestamp, energyValue); } } return valuesMap; } /** * Find a specific measurand value from sampledValues * @param sampledValues Array of sampled values * @param measurand The measurand type to look for * @param phased Whether to look for phased values (true) or non-phased values (false) * @returns The normalized value in kWh, or null if not found */ static findMeasurandValue(sampledValues, measurand, phased) { const value = sampledValues.find((sv) => sv.measurand === measurand && !phased === !sv.phase); if (value) { return this.normalizeToKwh(value); } return null; } /** * Sum phased values for a specific measurand * @param sampledValues Array of sampled values * @param measurand The measurand type to sum * @returns The sum of phase values in kWh, or null if no valid phase values found */ static sumPhasedValues(sampledValues, measurand) { // Find all values for this measurand that have individual phases L1, L2, or L3 const phaseValues = sampledValues.filter((sv) => sv.measurand === measurand && sv.phase && // Must have a phase specified (sv.phase === model_1.OCPP2_0_1.PhaseEnumType.L1 || sv.phase === model_1.OCPP2_0_1.PhaseEnumType.L2 || sv.phase === model_1.OCPP2_0_1.PhaseEnumType.L3)); // If no phase values found, try phase-to-neutral as a last resort if (phaseValues.length === 0) { const phaseNeutralValues = sampledValues.filter((sv) => sv.measurand === measurand && sv.phase && // Must have a phase specified (sv.phase === model_1.OCPP2_0_1.PhaseEnumType.L1_N || sv.phase === model_1.OCPP2_0_1.PhaseEnumType.L2_N || sv.phase === model_1.OCPP2_0_1.PhaseEnumType.L3_N)); if (phaseNeutralValues.length === 0) { return null; } let sum = 0; for (const value of phaseNeutralValues) { const normalizedValue = this.normalizeToKwh(value); if (normalizedValue !== null) { sum += normalizedValue; } } return sum; } // Sum all the normalized phase values let sum = 0; for (const value of phaseValues) { const normalizedValue = this.normalizeToKwh(value); if (normalizedValue !== null) { sum += normalizedValue; } } return sum; } static normalizeToKwh(value) { var _a, _b, _c, _d; let powerOfTen = (_b = (_a = value.unitOfMeasure) === null || _a === void 0 ? void 0 : _a.multiplier) !== null && _b !== void 0 ? _b : 0; const unit = (_d = (_c = value.unitOfMeasure) === null || _c === void 0 ? void 0 : _c.unit) === null || _d === void 0 ? void 0 : _d.toUpperCase(); switch (unit) { case 'KWH': case 'KVARH': // For reactive energy case 'KVAH': // For apparent energy break; case 'WH': case 'VARH': // For reactive energy case 'VAH': // For apparent energy case undefined: powerOfTen -= 3; break; default: throw new Error(`Unknown unit for energy measurement: ${unit}`); } return value.value * Math.pow(10, powerOfTen); } static getSortedKwhByTimestampAscending(valuesMap) { return Array.from(valuesMap.entries()) .sort((a, b) => a[0] - b[0]) .map((entry) => entry[1]); } static calculateTotalKwh(sortedValues) { if (sortedValues.length < 2) { return 0; } return sortedValues[sortedValues.length - 1] - sortedValues[0]; } } exports.MeterValueUtils = MeterValueUtils; MeterValueUtils.validContexts = new Set([ model_1.OCPP2_0_1.ReadingContextEnumType.Transaction_Begin, model_1.OCPP2_0_1.ReadingContextEnumType.Sample_Periodic, model_1.OCPP2_0_1.ReadingContextEnumType.Transaction_End, ]); //# sourceMappingURL=MeterValueUtils.js.map