+
+ // eslint-disable-next-line consistent-this
+ private async sendMeterValues(connectorId: number, interval: number, self: ChargingStation, debug = false): Promise<void> {
+ try {
+ const meterValue: MeterValue = {
+ timestamp: new Date().toISOString(),
+ sampledValue: [],
+ };
+ const meterValuesTemplate: SampledValue[] = 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 === MeterValueMeasurand.STATE_OF_CHARGE && self._getConfigurationKey(StandardParametersKey.MeterValuesSampledData).value.includes(MeterValueMeasurand.STATE_OF_CHARGE)) {
+ meterValue.sampledValue.push({
+ ...!Utils.isUndefined(meterValuesTemplate[index].unit) ? { unit: meterValuesTemplate[index].unit } : { unit: MeterValueUnit.PERCENT },
+ ...!Utils.isUndefined(meterValuesTemplate[index].context) && { context: meterValuesTemplate[index].context },
+ measurand: meterValuesTemplate[index].measurand,
+ ...!Utils.isUndefined(meterValuesTemplate[index].location) ? { location: meterValuesTemplate[index].location } : { location: MeterValueLocation.EV },
+ ...!Utils.isUndefined(meterValuesTemplate[index].value) ? { value: meterValuesTemplate[index].value } : { value: Utils.getRandomInt(100).toString() },
+ });
+ const sampledValuesIndex = meterValue.sampledValue.length - 1;
+ if (Utils.convertToInt(meterValue.sampledValue[sampledValuesIndex].value) > 100 || debug) {
+ logger.error(`${self._logPrefix()} MeterValues measurand ${meterValue.sampledValue[sampledValuesIndex].measurand ? meterValue.sampledValue[sampledValuesIndex].measurand : MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER}: connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${meterValue.sampledValue[sampledValuesIndex].value}/100`);
+ }
+ // Voltage measurand
+ } else if (meterValuesTemplate[index].measurand && meterValuesTemplate[index].measurand === MeterValueMeasurand.VOLTAGE && self._getConfigurationKey(StandardParametersKey.MeterValuesSampledData).value.includes(MeterValueMeasurand.VOLTAGE)) {
+ const voltageMeasurandValue = Utils.getRandomFloatRounded(self._getVoltageOut() + self._getVoltageOut() * 0.1, self._getVoltageOut() - self._getVoltageOut() * 0.1);
+ meterValue.sampledValue.push({
+ ...!Utils.isUndefined(meterValuesTemplate[index].unit) ? { unit: meterValuesTemplate[index].unit } : { unit: MeterValueUnit.VOLT },
+ ...!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: voltageMeasurandValue.toString() },
+ });
+ for (let phase = 1; self._getNumberOfPhases() === 3 && phase <= self._getNumberOfPhases(); phase++) {
+ let phaseValue: string;
+ if (self._getVoltageOut() >= 0 && self._getVoltageOut() <= 250) {
+ phaseValue = `L${phase}-N`;
+ } else if (self._getVoltageOut() > 250) {
+ phaseValue = `L${phase}-L${(phase + 1) % self._getNumberOfPhases() !== 0 ? (phase + 1) % self._getNumberOfPhases() : self._getNumberOfPhases()}`;
+ }
+ meterValue.sampledValue.push({
+ ...!Utils.isUndefined(meterValuesTemplate[index].unit) ? { unit: meterValuesTemplate[index].unit } : { unit: MeterValueUnit.VOLT },
+ ...!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: voltageMeasurandValue.toString() },
+ phase: phaseValue as MeterValuePhase,
+ });
+ }
+ // Power.Active.Import measurand
+ } else if (meterValuesTemplate[index].measurand && meterValuesTemplate[index].measurand === MeterValueMeasurand.POWER_ACTIVE_IMPORT && self._getConfigurationKey(StandardParametersKey.MeterValuesSampledData).value.includes(MeterValueMeasurand.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 : MeterValueMeasurand.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 : MeterValueMeasurand.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 : MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER}: Unknown ${self._getPowerOutType()} powerOutType in template file ${self._stationTemplateFile}, cannot calculate ${meterValuesTemplate[index].measurand ? meterValuesTemplate[index].measurand : MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER} measurand value`;
+ const powerMeasurandValues = {} as MeasurandValues;
+ 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 PowerOutType.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.allPhases = Utils.roundTo(powerMeasurandValues.L1 + powerMeasurandValues.L2 + powerMeasurandValues.L3, 2);
+ }
+ break;
+ case PowerOutType.DC:
+ if (Utils.isUndefined(meterValuesTemplate[index].value)) {
+ powerMeasurandValues.allPhases = Utils.getRandomFloatRounded(maxPower);
+ }
+ break;
+ default:
+ logger.error(errMsg);
+ throw Error(errMsg);
+ }
+ meterValue.sampledValue.push({
+ ...!Utils.isUndefined(meterValuesTemplate[index].unit) ? { unit: meterValuesTemplate[index].unit } : { unit: MeterValueUnit.WATT },
+ ...!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.allPhases.toString() },
+ });
+ const sampledValuesIndex = meterValue.sampledValue.length - 1;
+ if (Utils.convertToFloat(meterValue.sampledValue[sampledValuesIndex].value) > maxPower || debug) {
+ logger.error(`${self._logPrefix()} MeterValues measurand ${meterValue.sampledValue[sampledValuesIndex].measurand ? meterValue.sampledValue[sampledValuesIndex].measurand : MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER}: connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${meterValue.sampledValue[sampledValuesIndex].value}/${maxPower}`);
+ }
+ for (let phase = 1; self._getNumberOfPhases() === 3 && phase <= self._getNumberOfPhases(); phase++) {
+ const phaseValue = `L${phase}-N`;
+ meterValue.sampledValue.push({
+ ...!Utils.isUndefined(meterValuesTemplate[index].unit) ? { unit: meterValuesTemplate[index].unit } : { unit: MeterValueUnit.WATT },
+ ...!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}`] as string },
+ phase: phaseValue as MeterValuePhase,
+ });
+ }
+ // Current.Import measurand
+ } else if (meterValuesTemplate[index].measurand && meterValuesTemplate[index].measurand === MeterValueMeasurand.CURRENT_IMPORT && self._getConfigurationKey(StandardParametersKey.MeterValuesSampledData).value.includes(MeterValueMeasurand.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 : MeterValueMeasurand.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 : MeterValueMeasurand.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 : MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER}: Unknown ${self._getPowerOutType()} powerOutType in template file ${self._stationTemplateFile}, cannot calculate ${meterValuesTemplate[index].measurand ? meterValuesTemplate[index].measurand : MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER} measurand value`;
+ const currentMeasurandValues: MeasurandValues = {} as MeasurandValues;
+ let maxAmperage: number;
+ switch (self._getPowerOutType()) {
+ case PowerOutType.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.allPhases = Utils.roundTo((currentMeasurandValues.L1 + currentMeasurandValues.L2 + currentMeasurandValues.L3) / self._getNumberOfPhases(), 2);
+ }
+ break;
+ case PowerOutType.DC:
+ maxAmperage = ElectricUtils.ampTotalFromPower(self._stationInfo.maxPower / self._stationInfo.powerDivider, self._getVoltageOut());
+ if (Utils.isUndefined(meterValuesTemplate[index].value)) {
+ currentMeasurandValues.allPhases = Utils.getRandomFloatRounded(maxAmperage);
+ }
+ break;
+ default:
+ logger.error(errMsg);
+ throw Error(errMsg);
+ }
+ meterValue.sampledValue.push({
+ ...!Utils.isUndefined(meterValuesTemplate[index].unit) ? { unit: meterValuesTemplate[index].unit } : { unit: MeterValueUnit.AMP },
+ ...!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.allPhases.toString() },
+ });
+ const sampledValuesIndex = meterValue.sampledValue.length - 1;
+ if (Utils.convertToFloat(meterValue.sampledValue[sampledValuesIndex].value) > maxAmperage || debug) {
+ logger.error(`${self._logPrefix()} MeterValues measurand ${meterValue.sampledValue[sampledValuesIndex].measurand ? meterValue.sampledValue[sampledValuesIndex].measurand : MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER}: connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${meterValue.sampledValue[sampledValuesIndex].value}/${maxAmperage}`);
+ }
+ for (let phase = 1; self._getNumberOfPhases() === 3 && phase <= self._getNumberOfPhases(); phase++) {
+ const phaseValue = `L${phase}`;
+ meterValue.sampledValue.push({
+ ...!Utils.isUndefined(meterValuesTemplate[index].unit) ? { unit: meterValuesTemplate[index].unit } : { unit: MeterValueUnit.AMP },
+ ...!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] as string },
+ phase: phaseValue as MeterValuePhase,
+ });
+ }
+ // Energy.Active.Import.Register measurand (default)
+ } else if (!meterValuesTemplate[index].measurand || meterValuesTemplate[index].measurand === MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER) {
+ // FIXME: factor out powerDivider checks
+ if (Utils.isUndefined(self._stationInfo.powerDivider)) {
+ const errMsg = `${self._logPrefix()} MeterValues measurand ${meterValuesTemplate[index].measurand ? meterValuesTemplate[index].measurand : MeterValueMeasurand.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 : MeterValueMeasurand.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;
+ }
+ }
+ meterValue.sampledValue.push({
+ ...!Utils.isUndefined(meterValuesTemplate[index].unit) ? { unit: meterValuesTemplate[index].unit } : { unit: MeterValueUnit.WATT_HOUR },
+ ...!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.toString() },
+ });
+ const sampledValuesIndex = meterValue.sampledValue.length - 1;
+ const maxConsumption = Math.round(self._stationInfo.maxPower * 3600 / (self._stationInfo.powerDivider * interval));
+ if (Utils.convertToFloat(meterValue.sampledValue[sampledValuesIndex].value) > maxConsumption || debug) {
+ logger.error(`${self._logPrefix()} MeterValues measurand ${meterValue.sampledValue[sampledValuesIndex].measurand ? meterValue.sampledValue[sampledValuesIndex].measurand : MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER}: connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${meterValue.sampledValue[sampledValuesIndex].value}/${maxConsumption}`);
+ }
+ // Unsupported measurand
+ } else {
+ logger.info(`${self._logPrefix()} Unsupported MeterValues measurand ${meterValuesTemplate[index].measurand ? meterValuesTemplate[index].measurand : MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER} on connectorId ${connectorId}`);
+ }
+ }
+ const payload: MeterValuesRequest = {
+ connectorId,
+ transactionId: self.getConnector(connectorId).transactionId,
+ meterValue: meterValue,
+ };
+ await self.sendMessage(Utils.generateUUID(), payload, MessageType.CALL_MESSAGE, RequestCommand.METERVALUES);
+ } catch (error) {
+ this.handleRequestError(RequestCommand.METERVALUES, error);
+ }
+ }
+
+ private handleRequestError(commandName: RequestCommand, error: Error) {
+ logger.error(this._logPrefix() + ' Send ' + commandName + ' error: %j', error);
+ throw error;
+ }