X-Git-Url: https://git.piment-noir.org/?a=blobdiff_plain;f=src%2Fcharging-station%2FChargingStation.js;h=1ffcfab72f9923001770364de10166e9eb632df9;hb=5933cbc8c16288268032886a14e03d47b8ce84f6;hp=da9cfb8778cab064ab297dc8cc4c218f951385a0;hpb=cb07a745a9df55af217c81f256bd84f8c3dd61ac;p=e-mobility-charging-stations-simulator.git diff --git a/src/charging-station/ChargingStation.js b/src/charging-station/ChargingStation.js index da9cfb87..1ffcfab7 100644 --- a/src/charging-station/ChargingStation.js +++ b/src/charging-station/ChargingStation.js @@ -1,16 +1,18 @@ -const Configuration = require('../utils/Configuration'); -const logger = require('../utils/Logger'); -const WebSocket = require('ws'); -const Constants = require('../utils/Constants'); -const Utils = require('../utils/Utils'); -const OCPPError = require('./OcppError'); -const AutomaticTransactionGenerator = require('./AutomaticTransactionGenerator'); -const Statistics = require('../utils/Statistics'); -const fs = require('fs'); -const crypto = require('crypto'); -const {performance, PerformanceObserver} = require('perf_hooks'); - -class ChargingStation { +import {PerformanceObserver, performance} from 'perf_hooks'; + +import AutomaticTransactionGenerator from './AutomaticTransactionGenerator.js'; +import Configuration from '../utils/Configuration.js'; +import Constants from '../utils/Constants.js'; +import ElectricUtils from '../utils/ElectricUtils.js'; +import OCPPError from './OcppError.js'; +import Statistics from '../utils/Statistics.js'; +import Utils from '../utils/Utils.js'; +import WebSocket from 'ws'; +import crypto from 'crypto'; +import fs from 'fs'; +import logger from '../utils/Logger.js'; + +export default class ChargingStation { constructor(index, stationTemplateFile) { this._index = index; this._stationTemplateFile = stationTemplateFile; @@ -59,8 +61,8 @@ class ChargingStation { this._bootNotificationMessage = { chargePointModel: this._stationInfo.chargePointModel, chargePointVendor: this._stationInfo.chargePointVendor, - chargePointSerialNumber: this._stationInfo.chargePointSerialNumberPrefix ? this._stationInfo.chargePointSerialNumberPrefix : '', - firmwareVersion: this._stationInfo.firmwareVersion ? this._stationInfo.firmwareVersion : '', + ...!Utils.isUndefined(this._stationInfo.chargePointSerialNumberPrefix) && {chargePointSerialNumber: this._stationInfo.chargePointSerialNumberPrefix}, + ...!Utils.isUndefined(this._stationInfo.firmwareVersion) && {firmwareVersion: this._stationInfo.firmwareVersion}, }; this._configuration = this._getConfiguration(); this._supervisionUrl = this._getSupervisionURL(); @@ -68,13 +70,11 @@ class ChargingStation { // Build connectors if needed const maxConnectors = this._getMaxNumberOfConnectors(); if (maxConnectors <= 0) { - const errMsg = `${this._logPrefix()} Charging station template ${this._stationTemplateFile} with ${maxConnectors} connectors`; - logger.warn(errMsg); + logger.warn(`${this._logPrefix()} Charging station template ${this._stationTemplateFile} with ${maxConnectors} connectors`); } const templateMaxConnectors = this._getTemplateMaxNumberOfConnectors(); if (templateMaxConnectors <= 0) { - const errMsg = `${this._logPrefix()} Charging station template ${this._stationTemplateFile} with no connector configurations`; - logger.warn(errMsg); + logger.warn(`${this._logPrefix()} Charging station template ${this._stationTemplateFile} with no connector configurations`); } // Sanity check if (maxConnectors > (this._stationInfo.Connectors[0] ? templateMaxConnectors - 1 : templateMaxConnectors) && !Utils.convertToBoolean(this._stationInfo.randomConnectors)) { @@ -216,6 +216,27 @@ class ChargingStation { return this._connectors[0] ? Object.keys(this._connectors).length - 1 : Object.keys(this._connectors).length; } + _getVoltageOut() { + const errMsg = `${this._logPrefix()} Unknown ${this._getPowerOutType()} powerOutType in template file ${this._stationTemplateFile}, cannot define default voltage out`; + let defaultVoltageOut; + switch (this._getPowerOutType()) { + case 'AC': + defaultVoltageOut = 230; + break; + case 'DC': + defaultVoltageOut = 400; + break; + default: + logger.error(errMsg); + throw Error(errMsg); + } + return !Utils.isUndefined(this._stationInfo.voltageOut) ? Utils.convertToInt(this._stationInfo.voltageOut) : defaultVoltageOut; + } + + _getPowerOutType() { + return !Utils.isUndefined(this._stationInfo.powerOutType) ? this._stationInfo.powerOutType : 'AC'; + } + _getSupervisionURL() { const supervisionUrls = Utils.cloneObject(this._stationInfo.supervisionURL ? this._stationInfo.supervisionURL : Configuration.getSupervisionURLs()); let indexUrl = 0; @@ -483,7 +504,7 @@ class ChargingStation { if (Utils.isIterable(this._requests[messageId])) { [responseCallback, , requestPayload] = this._requests[messageId]; } else { - throw new Error(`Response request for unknown message id ${messageId} is not iterable`); + throw new Error(`Response request for message id ${messageId} is not iterable`); } if (!responseCallback) { // Error @@ -503,7 +524,7 @@ class ChargingStation { if (Utils.isIterable(this._requests[messageId])) { [, rejectCallback] = this._requests[messageId]; } else { - throw new Error(`Error request for unknown message id ${messageId} is not iterable`); + throw new Error(`Error request for message id ${messageId} is not iterable`); } delete this._requests[messageId]; rejectCallback(new OCPPError(commandName, commandPayload, errorDetails)); @@ -574,27 +595,18 @@ class ChargingStation { } } - sendStartTransactionWithTimeout(connectorId, idTag, timeout) { + sendStartTransactionWithTimeout(connectorId, idTag, timeout = Constants.START_TRANSACTION_TIMEOUT) { setTimeout(() => this.sendStartTransaction(connectorId, idTag), timeout); } async sendStopTransaction(transactionId, reason = '') { try { - let payload; - if (reason) { - payload = { - transactionId, - meterStop: 0, - timestamp: new Date().toISOString(), - reason, - }; - } else { - payload = { - transactionId, - meterStop: 0, - timestamp: new Date().toISOString(), - }; - } + const payload = { + transactionId, + meterStop: 0, + timestamp: new Date().toISOString(), + ...reason && {reason}, + }; await this.sendMessage(Utils.generateUUID(), payload, Constants.OCPP_JSON_CALL_MESSAGE, 'StopTransaction'); } catch (error) { logger.error(this._logPrefix() + ' Send StopTransaction error: ' + error); @@ -623,7 +635,7 @@ class ChargingStation { }); 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}`); + 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')) { @@ -632,27 +644,149 @@ class ChargingStation { ...!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: 230}, + ...!Utils.isUndefined(meterValuesTemplate[index].value) ? {value: meterValuesTemplate[index].value} : {value: self._getVoltageOut()}, }); - for (let phase = 1; self._getNumberOfPhases() === 3 && phase <= self._getNumberOfPhases(); phase++) { + 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 <= 240) { + if (voltageValue >= 0 && voltageValue <= 250) { phaseValue = `L${phase}-N`; - } else if (voltageValue > 240) { - phaseValue = `L${phase}-L${(phase + 1) % self._getNumberOfPhases() !== 0 ? (phase + 1) % self._getNumberOfPhases() : self._getNumberOfPhases() }`; + } 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: 230}, + ...!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') { + // 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); @@ -665,7 +799,7 @@ class ChargingStation { 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 && connector.lastEnergyActiveImportRegisterValue >= 0) { + if (connector && !Utils.isNullOrUndefined(connector.lastEnergyActiveImportRegisterValue) && connector.lastEnergyActiveImportRegisterValue >= 0) { connector.lastEnergyActiveImportRegisterValue += measurandValue; } else { connector.lastEnergyActiveImportRegisterValue = 0; @@ -679,20 +813,7 @@ class ChargingStation { ...!Utils.isUndefined(meterValuesTemplate[index].value) ? {value: meterValuesTemplate[index].value} : {value: connector.lastEnergyActiveImportRegisterValue}, }); const sampledValuesIndex = sampledValues.sampledValue.length - 1; - // const measurandValuePerPhase = Utils.roundTo(sampledValues.sampledValue[sampledValuesIndex].value / self._getNumberOfPhases(), 2); - // for (let phase = 1; 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: '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}, - // value: measurandValuePerPhase, - // phase: phaseValue, - // }); - // } - logger.info(`${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}`); - const maxConsumption = self._stationInfo.maxPower * 3600 / (self._stationInfo.powerDivider * interval); + const maxConsumption = Math.round(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}`); } @@ -770,39 +891,49 @@ class ChargingStation { } else if (this._wsConnection && this._wsConnection.readyState === WebSocket.OPEN) { // Send timeout in case connection is open otherwise wait for ever // FIXME: Handle message on timeout - setTimeout(() => rejectCallback(`Timeout for message ${messageId}`), Constants.OCPP_SOCKET_TIMEOUT); + setTimeout(() => rejectCallback(new OCPPError(command.code ? command.code : Constants.OCPP_ERROR_GENERIC_ERROR, command.message ? command.message : '', command.details ? command.details : {})), Constants.OCPP_SOCKET_TIMEOUT); } // Function that will receive the request's response function responseCallback(payload, requestPayload) { - if (self.getEnableStatistics()) { - self._statistics.addMessage(commandName, true); - } - const responseCallbackFn = 'handleResponse' + commandName; - if (typeof self[responseCallbackFn] === 'function') { - self[responseCallbackFn](payload, requestPayload, self); - } else { - logger.debug(self._logPrefix() + ' Trying to call an undefined response callback function: ' + responseCallbackFn); - } + self.handleResponse(commandName, payload, requestPayload, self); // Send the response resolve(payload); } // Function that will receive the request's rejection - function rejectCallback(reason) { + function rejectCallback(error) { + if (!(error instanceof OCPPError)) { + const errMsg = `${self._logPrefix()} Argument error is not an instance of OCPPError in rejectCallback call`; + logger.error(errMsg); + throw Error(errMsg); + } + logger.debug(`${self._logPrefix()} Error %j on commandName %s command %j`, error, commandName, command); if (self.getEnableStatistics()) { - self._statistics.addMessage(`Error ${command.code ? command.code : Constants.OCPP_ERROR_GENERIC_ERROR} on ${commandName}`, true); + self._statistics.addMessage(`Error on commandName ${commandName}`, true); } // Build Exception // eslint-disable-next-line no-empty-function self._requests[messageId] = [() => { }, () => { }, '']; // Properly format the request - const error = reason instanceof OCPPError ? reason : new Error(reason); // Send error reject(error); } }); } + // eslint-disable-next-line class-methods-use-this + handleResponse(commandName, payload, requestPayload, self) { + if (self.getEnableStatistics()) { + self._statistics.addMessage(commandName, true); + } + const responseCallbackFn = 'handleResponse' + commandName; + if (typeof self[responseCallbackFn] === 'function') { + self[responseCallbackFn](payload, requestPayload, self); + } else { + logger.error(self._logPrefix() + ' Trying to call an undefined response callback function: ' + responseCallbackFn); + } + } + handleResponseBootNotification(payload) { if (payload.status === 'Accepted') { this._heartbeatInterval = payload.interval * 1000; @@ -907,12 +1038,12 @@ class ChargingStation { if (this.getEnableStatistics()) { this._statistics.addMessage(commandName, true); } - let result; + let response; // Call if (typeof this['handle' + commandName] === 'function') { try { - // Call the method - result = await this['handle' + commandName](commandPayload); + // Call the method to build the response + response = await this['handle' + commandName](commandPayload); } catch (error) { // Log logger.error(this._logPrefix() + ' Handle request error: ' + error); @@ -925,7 +1056,7 @@ class ChargingStation { throw new Error(`${commandName} is not implemented ${JSON.stringify(commandPayload, null, ' ')}`); } // Send response - await this.sendMessage(messageId, result, Constants.OCPP_JSON_CALL_RESULT_MESSAGE); + await this.sendMessage(messageId, response, Constants.OCPP_JSON_CALL_RESULT_MESSAGE); } async handleReset(commandPayload) { @@ -1049,7 +1180,7 @@ class ChargingStation { // Check if authorized if (this._authorizedTags.find((value) => value === commandPayload.idTag)) { // Authorization successful start transaction - this.sendStartTransactionWithTimeout(transactionConnectorID, commandPayload.idTag, Constants.START_TRANSACTION_TIMEOUT); + this.sendStartTransactionWithTimeout(transactionConnectorID, commandPayload.idTag); logger.debug(this._logPrefix() + ' Transaction remotely STARTED on ' + this._stationInfo.name + '#' + transactionConnectorID + ' for idTag ' + commandPayload.idTag); return Constants.OCPP_RESPONSE_ACCEPTED; } @@ -1057,7 +1188,7 @@ class ChargingStation { return Constants.OCPP_RESPONSE_REJECTED; } // No local authorization check required => start transaction - this.sendStartTransactionWithTimeout(transactionConnectorID, commandPayload.idTag, Constants.START_TRANSACTION_TIMEOUT); + this.sendStartTransactionWithTimeout(transactionConnectorID, commandPayload.idTag); logger.debug(this._logPrefix() + ' Transaction remotely STARTED on ' + this._stationInfo.name + '#' + transactionConnectorID + ' for idTag ' + commandPayload.idTag); return Constants.OCPP_RESPONSE_ACCEPTED; } @@ -1074,4 +1205,3 @@ class ChargingStation { } } -module.exports = ChargingStation;