+ // eslint-disable-next-line class-methods-use-this
+ async sendMeterValues(connectorId, interval, self, debug = false) {
+ try {
+ const sampledValues = {
+ timestamp: new Date().toISOString(),
+ sampledValue: [],
+ };
+ const meterValuesTemplate = self.getConnector(connectorId).MeterValues;
+ for (let index = 0; index < meterValuesTemplate.length; index++) {
+ const connector = self.getConnector(connectorId);
+ // SoC measurand
+ if (meterValuesTemplate[index].measurand && meterValuesTemplate[index].measurand === 'SoC' && self._getConfigurationKey('MeterValuesSampledData').value.includes('SoC')) {
+ sampledValues.sampledValue.push({
+ ...!Utils.isUndefined(meterValuesTemplate[index].unit) ? {unit: meterValuesTemplate[index].unit} : {unit: 'Percent'},
+ ...!Utils.isUndefined(meterValuesTemplate[index].context) && {context: meterValuesTemplate[index].context},
+ measurand: meterValuesTemplate[index].measurand,
+ ...!Utils.isUndefined(meterValuesTemplate[index].location) ? {location: meterValuesTemplate[index].location} : {location: 'EV'},
+ ...!Utils.isUndefined(meterValuesTemplate[index].value) ? {value: meterValuesTemplate[index].value} : {value: Utils.getRandomInt(100)},
+ });
+ const sampledValuesIndex = sampledValues.sampledValue.length - 1;
+ if (sampledValues.sampledValue[sampledValuesIndex].value > 100 || debug) {
+ logger.error(`${self._logPrefix()} MeterValues measurand ${sampledValues.sampledValue[sampledValuesIndex].measurand ? sampledValues.sampledValue[sampledValuesIndex].measurand : 'Energy.Active.Import.Register'}: connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${sampledValues.sampledValue[sampledValuesIndex].value}/100`);
+ }
+ // Voltage measurand
+ } else if (meterValuesTemplate[index].measurand && meterValuesTemplate[index].measurand === 'Voltage' && self._getConfigurationKey('MeterValuesSampledData').value.includes('Voltage')) {
+ sampledValues.sampledValue.push({
+ ...!Utils.isUndefined(meterValuesTemplate[index].unit) ? {unit: meterValuesTemplate[index].unit} : {unit: 'V'},
+ ...!Utils.isUndefined(meterValuesTemplate[index].context) && {context: meterValuesTemplate[index].context},
+ measurand: meterValuesTemplate[index].measurand,
+ ...!Utils.isUndefined(meterValuesTemplate[index].location) && {location: meterValuesTemplate[index].location},
+ ...!Utils.isUndefined(meterValuesTemplate[index].value) ? {value: meterValuesTemplate[index].value} : {value: self._getVoltageOut()},
+ });
+ for (let phase = 1; self._getPowerOutType() === 'AC' && self._getNumberOfPhases() === 3 && phase <= self._getNumberOfPhases(); phase++) {
+ const voltageValue = sampledValues.sampledValue[sampledValues.sampledValue.length - 1].value;
+ let phaseValue;
+ if (voltageValue >= 0 && voltageValue <= 250) {
+ phaseValue = `L${phase}-N`;
+ } else if (voltageValue > 250) {
+ phaseValue = `L${phase}-L${(phase + 1) % self._getNumberOfPhases() !== 0 ? (phase + 1) % self._getNumberOfPhases() : self._getNumberOfPhases()}`;
+ }
+ sampledValues.sampledValue.push({
+ ...!Utils.isUndefined(meterValuesTemplate[index].unit) ? {unit: meterValuesTemplate[index].unit} : {unit: 'V'},
+ ...!Utils.isUndefined(meterValuesTemplate[index].context) && {context: meterValuesTemplate[index].context},
+ measurand: meterValuesTemplate[index].measurand,
+ ...!Utils.isUndefined(meterValuesTemplate[index].location) && {location: meterValuesTemplate[index].location},
+ ...!Utils.isUndefined(meterValuesTemplate[index].value) ? {value: meterValuesTemplate[index].value} : {value: self._getVoltageOut()},
+ phase: phaseValue,
+ });
+ }
+ // Power.Active.Import measurand
+ } else if (meterValuesTemplate[index].measurand && meterValuesTemplate[index].measurand === 'Power.Active.Import' && self._getConfigurationKey('MeterValuesSampledData').value.includes('Power.Active.Import')) {
+ // FIXME: factor out powerDivider checks
+ if (Utils.isUndefined(self._stationInfo.powerDivider)) {
+ const errMsg = `${self._logPrefix()} MeterValues measurand ${meterValuesTemplate[index].measurand ? meterValuesTemplate[index].measurand : 'Energy.Active.Import.Register'}: powerDivider is undefined`;
+ logger.error(errMsg);
+ throw Error(errMsg);
+ } else if (self._stationInfo.powerDivider && self._stationInfo.powerDivider <= 0) {
+ const errMsg = `${self._logPrefix()} MeterValues measurand ${meterValuesTemplate[index].measurand ? meterValuesTemplate[index].measurand : 'Energy.Active.Import.Register'}: powerDivider have zero or below value ${self._stationInfo.powerDivider}`;
+ logger.error(errMsg);
+ throw Error(errMsg);
+ }
+ const errMsg = `${self._logPrefix()} MeterValues measurand ${meterValuesTemplate[index].measurand ? meterValuesTemplate[index].measurand : 'Energy.Active.Import.Register'}: Unknown ${self._getPowerOutType()} powerOutType in template file ${self._stationTemplateFile}, cannot calculate ${meterValuesTemplate[index].measurand ? meterValuesTemplate[index].measurand : 'Energy.Active.Import.Register'} measurand value`;
+ const powerMeasurandValues = {};
+ const maxPower = Math.round(self._stationInfo.maxPower / self._stationInfo.powerDivider);
+ const maxPowerPerPhase = Math.round((self._stationInfo.maxPower / self._stationInfo.powerDivider) / self._getNumberOfPhases());
+ switch (self._getPowerOutType()) {
+ case 'AC':
+ if (Utils.isUndefined(meterValuesTemplate[index].value)) {
+ powerMeasurandValues.L1 = Utils.getRandomFloatRounded(maxPowerPerPhase);
+ powerMeasurandValues.L2 = 0;
+ powerMeasurandValues.L3 = 0;
+ if (self._getNumberOfPhases() === 3) {
+ powerMeasurandValues.L2 = Utils.getRandomFloatRounded(maxPowerPerPhase);
+ powerMeasurandValues.L3 = Utils.getRandomFloatRounded(maxPowerPerPhase);
+ }
+ powerMeasurandValues.all = Utils.roundTo(powerMeasurandValues.L1 + powerMeasurandValues.L2 + powerMeasurandValues.L3, 2);
+ }
+ break;
+ case 'DC':
+ if (Utils.isUndefined(meterValuesTemplate[index].value)) {
+ powerMeasurandValues.all = Utils.getRandomFloatRounded(maxPower);
+ }
+ break;
+ default:
+ logger.error(errMsg);
+ throw Error(errMsg);
+ }
+ sampledValues.sampledValue.push({
+ ...!Utils.isUndefined(meterValuesTemplate[index].unit) ? {unit: meterValuesTemplate[index].unit} : {unit: 'W'},
+ ...!Utils.isUndefined(meterValuesTemplate[index].context) && {context: meterValuesTemplate[index].context},
+ measurand: meterValuesTemplate[index].measurand,
+ ...!Utils.isUndefined(meterValuesTemplate[index].location) && {location: meterValuesTemplate[index].location},
+ ...!Utils.isUndefined(meterValuesTemplate[index].value) ? {value: meterValuesTemplate[index].value} : {value: powerMeasurandValues.all},
+ });
+ const sampledValuesIndex = sampledValues.sampledValue.length - 1;
+ if (sampledValues.sampledValue[sampledValuesIndex].value > maxPower || debug) {
+ logger.error(`${self._logPrefix()} MeterValues measurand ${sampledValues.sampledValue[sampledValuesIndex].measurand ? sampledValues.sampledValue[sampledValuesIndex].measurand : 'Energy.Active.Import.Register'}: connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${sampledValues.sampledValue[sampledValuesIndex].value}/${maxPower}`);
+ }
+ for (let phase = 1; self._getPowerOutType() === 'AC' && self._getNumberOfPhases() === 3 && phase <= self._getNumberOfPhases(); phase++) {
+ const phaseValue = `L${phase}-N`;
+ sampledValues.sampledValue.push({
+ ...!Utils.isUndefined(meterValuesTemplate[index].unit) ? {unit: meterValuesTemplate[index].unit} : {unit: 'W'},
+ ...!Utils.isUndefined(meterValuesTemplate[index].context) && {context: meterValuesTemplate[index].context},
+ ...!Utils.isUndefined(meterValuesTemplate[index].measurand) && {measurand: meterValuesTemplate[index].measurand},
+ ...!Utils.isUndefined(meterValuesTemplate[index].location) && {location: meterValuesTemplate[index].location},
+ ...!Utils.isUndefined(meterValuesTemplate[index].value) ? {value: meterValuesTemplate[index].value} : {value: powerMeasurandValues[`L${phase}`]},
+ phase: phaseValue,
+ });
+ }
+ // Current.Import measurand
+ } else if (meterValuesTemplate[index].measurand && meterValuesTemplate[index].measurand === 'Current.Import' && self._getConfigurationKey('MeterValuesSampledData').value.includes('Current.Import')) {
+ // FIXME: factor out powerDivider checks
+ if (Utils.isUndefined(self._stationInfo.powerDivider)) {
+ const errMsg = `${self._logPrefix()} MeterValues measurand ${meterValuesTemplate[index].measurand ? meterValuesTemplate[index].measurand : 'Energy.Active.Import.Register'}: powerDivider is undefined`;
+ logger.error(errMsg);
+ throw Error(errMsg);
+ } else if (self._stationInfo.powerDivider && self._stationInfo.powerDivider <= 0) {
+ const errMsg = `${self._logPrefix()} MeterValues measurand ${meterValuesTemplate[index].measurand ? meterValuesTemplate[index].measurand : 'Energy.Active.Import.Register'}: powerDivider have zero or below value ${self._stationInfo.powerDivider}`;
+ logger.error(errMsg);
+ throw Error(errMsg);
+ }
+ const errMsg = `${self._logPrefix()} MeterValues measurand ${meterValuesTemplate[index].measurand ? meterValuesTemplate[index].measurand : 'Energy.Active.Import.Register'}: Unknown ${self._getPowerOutType()} powerOutType in template file ${self._stationTemplateFile}, cannot calculate ${meterValuesTemplate[index].measurand ? meterValuesTemplate[index].measurand : 'Energy.Active.Import.Register'} measurand value`;
+ const currentMeasurandValues = {};
+ let maxAmperage;
+ switch (self._getPowerOutType()) {
+ case 'AC':
+ maxAmperage = ElectricUtils.ampPerPhaseFromPower(self._getNumberOfPhases(), self._stationInfo.maxPower / self._stationInfo.powerDivider, self._getVoltageOut());
+ if (Utils.isUndefined(meterValuesTemplate[index].value)) {
+ currentMeasurandValues.L1 = Utils.getRandomFloatRounded(maxAmperage);
+ currentMeasurandValues.L2 = 0;
+ currentMeasurandValues.L3 = 0;
+ if (self._getNumberOfPhases() === 3) {
+ currentMeasurandValues.L2 = Utils.getRandomFloatRounded(maxAmperage);
+ currentMeasurandValues.L3 = Utils.getRandomFloatRounded(maxAmperage);
+ }
+ currentMeasurandValues.all = Utils.roundTo((currentMeasurandValues.L1 + currentMeasurandValues.L2 + currentMeasurandValues.L3) / self._getNumberOfPhases(), 2);
+ }
+ break;
+ case 'DC':
+ maxAmperage = ElectricUtils.ampTotalFromPower(self._stationInfo.maxPower / self._stationInfo.powerDivider, self._getVoltageOut());
+ if (Utils.isUndefined(meterValuesTemplate[index].value)) {
+ currentMeasurandValues.all = Utils.getRandomFloatRounded(maxAmperage);
+ }
+ break;
+ default:
+ logger.error(errMsg);
+ throw Error(errMsg);
+ }
+ sampledValues.sampledValue.push({
+ ...!Utils.isUndefined(meterValuesTemplate[index].unit) ? {unit: meterValuesTemplate[index].unit} : {unit: 'A'},
+ ...!Utils.isUndefined(meterValuesTemplate[index].context) && {context: meterValuesTemplate[index].context},
+ measurand: meterValuesTemplate[index].measurand,
+ ...!Utils.isUndefined(meterValuesTemplate[index].location) && {location: meterValuesTemplate[index].location},
+ ...!Utils.isUndefined(meterValuesTemplate[index].value) ? {value: meterValuesTemplate[index].value} : {value: currentMeasurandValues.all},
+ });
+ const sampledValuesIndex = sampledValues.sampledValue.length - 1;
+ if (sampledValues.sampledValue[sampledValuesIndex].value > maxAmperage || debug) {
+ logger.error(`${self._logPrefix()} MeterValues measurand ${sampledValues.sampledValue[sampledValuesIndex].measurand ? sampledValues.sampledValue[sampledValuesIndex].measurand : 'Energy.Active.Import.Register'}: connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${sampledValues.sampledValue[sampledValuesIndex].value}/${maxAmperage}`);
+ }
+ for (let phase = 1; self._getPowerOutType() === 'AC' && self._getNumberOfPhases() === 3 && phase <= self._getNumberOfPhases(); phase++) {
+ const phaseValue = `L${phase}`;
+ sampledValues.sampledValue.push({
+ ...!Utils.isUndefined(meterValuesTemplate[index].unit) ? {unit: meterValuesTemplate[index].unit} : {unit: 'A'},
+ ...!Utils.isUndefined(meterValuesTemplate[index].context) && {context: meterValuesTemplate[index].context},
+ ...!Utils.isUndefined(meterValuesTemplate[index].measurand) && {measurand: meterValuesTemplate[index].measurand},
+ ...!Utils.isUndefined(meterValuesTemplate[index].location) && {location: meterValuesTemplate[index].location},
+ ...!Utils.isUndefined(meterValuesTemplate[index].value) ? {value: meterValuesTemplate[index].value} : {value: currentMeasurandValues[phaseValue]},
+ phase: phaseValue,
+ });
+ }
+ // Energy.Active.Import.Register measurand (default)
+ } else if (!meterValuesTemplate[index].measurand || meterValuesTemplate[index].measurand === 'Energy.Active.Import.Register') {
+ if (Utils.isUndefined(self._stationInfo.powerDivider)) {
+ const errMsg = `${self._logPrefix()} MeterValues measurand ${meterValuesTemplate[index].measurand ? meterValuesTemplate[index].measurand : 'Energy.Active.Import.Register'}: powerDivider is undefined`;
+ logger.error(errMsg);
+ throw Error(errMsg);
+ } else if (self._stationInfo.powerDivider && self._stationInfo.powerDivider <= 0) {
+ const errMsg = `${self._logPrefix()} MeterValues measurand ${meterValuesTemplate[index].measurand ? meterValuesTemplate[index].measurand : 'Energy.Active.Import.Register'}: powerDivider have zero or below value ${self._stationInfo.powerDivider}`;
+ logger.error(errMsg);
+ throw Error(errMsg);
+ }
+ if (Utils.isUndefined(meterValuesTemplate[index].value)) {
+ const measurandValue = Utils.getRandomInt(self._stationInfo.maxPower / (self._stationInfo.powerDivider * 3600000) * interval);
+ // Persist previous value in connector
+ if (connector && !Utils.isNullOrUndefined(connector.lastEnergyActiveImportRegisterValue) && connector.lastEnergyActiveImportRegisterValue >= 0) {
+ connector.lastEnergyActiveImportRegisterValue += measurandValue;
+ } else {
+ connector.lastEnergyActiveImportRegisterValue = 0;
+ }
+ }
+ sampledValues.sampledValue.push({
+ ...!Utils.isUndefined(meterValuesTemplate[index].unit) ? {unit: meterValuesTemplate[index].unit} : {unit: 'Wh'},
+ ...!Utils.isUndefined(meterValuesTemplate[index].context) && {context: meterValuesTemplate[index].context},
+ ...!Utils.isUndefined(meterValuesTemplate[index].measurand) && {measurand: meterValuesTemplate[index].measurand},
+ ...!Utils.isUndefined(meterValuesTemplate[index].location) && {location: meterValuesTemplate[index].location},
+ ...!Utils.isUndefined(meterValuesTemplate[index].value) ? {value: meterValuesTemplate[index].value} : {value: connector.lastEnergyActiveImportRegisterValue},
+ });
+ const sampledValuesIndex = sampledValues.sampledValue.length - 1;
+ const maxConsumption = self._stationInfo.maxPower * 3600 / (self._stationInfo.powerDivider * interval);
+ if (sampledValues.sampledValue[sampledValuesIndex].value > maxConsumption || debug) {
+ logger.error(`${self._logPrefix()} MeterValues measurand ${sampledValues.sampledValue[sampledValuesIndex].measurand ? sampledValues.sampledValue[sampledValuesIndex].measurand : 'Energy.Active.Import.Register'}: connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${sampledValues.sampledValue[sampledValuesIndex].value}/${maxConsumption}`);
+ }
+ // Unsupported measurand
+ } else {
+ logger.info(`${self._logPrefix()} Unsupported MeterValues measurand ${meterValuesTemplate[index].measurand ? meterValuesTemplate[index].measurand : 'Energy.Active.Import.Register'} on connectorId ${connectorId}`);
+ }
+ }
+
+ const payload = {
+ connectorId,
+ transactionId: self.getConnector(connectorId).transactionId,
+ meterValue: sampledValues,
+ };
+ await self.sendMessage(Utils.generateUUID(), payload, Constants.OCPP_JSON_CALL_MESSAGE, 'MeterValues');
+ } catch (error) {
+ logger.error(self._logPrefix() + ' Send MeterValues error: ' + error);
+ throw error;
+ }