X-Git-Url: https://git.piment-noir.org/?a=blobdiff_plain;f=src%2Fcharging-station%2FChargingStation.js;h=0411a1a1f12c88bb457b4246f10d7b30131e37a9;hb=a6e68f340a9c5837c767b316dee5d5121188dc47;hp=4ebb52af4b3d21ae2c26b2fb216ba6cfaeaacfc0;hpb=7dde0b73302613be132c41e1f28a42de555dc2b6;p=e-mobility-charging-stations-simulator.git diff --git a/src/charging-station/ChargingStation.js b/src/charging-station/ChargingStation.js index 4ebb52af..0411a1a1 100644 --- a/src/charging-station/ChargingStation.js +++ b/src/charging-station/ChargingStation.js @@ -1,14 +1,7 @@ const Configuration = require('../utils/Configuration'); const logger = require('../utils/Logger'); const WebSocket = require('ws'); -const { - OCPP_JSON_CALL_MESSAGE, - OCPP_JSON_CALL_RESULT_MESSAGE, - OCPP_JSON_CALL_ERROR_MESSAGE, - OCPP_ERROR_INTERNAL_ERROR, - OCPP_SOCKET_TIMEOUT, - OCPP_ERROR_NOT_IMPLEMENTED, -} = require('../utils/Constants'); +const Constants = require('../utils/Constants'); const Utils = require('../utils/Utils'); const OCPPError = require('./OcppError'); const {v4: uuid} = require('uuid'); @@ -79,7 +72,7 @@ class ChargingStation { _getAuthorizeRemoteTxRequests() { const authorizeRemoteTxRequests = this._configuration.configurationKey.find((configElement) => configElement.key === 'AuthorizeRemoteTxRequests'); - return authorizeRemoteTxRequests ? authorizeRemoteTxRequests.value : false; + return authorizeRemoteTxRequests ? Utils.convertToBoolean(authorizeRemoteTxRequests.value) : false; } _buildChargingStation(index, stationTemplate) { @@ -145,9 +138,9 @@ class ChargingStation { }); } } else { - // At first start, send Bootnotification + // At first start, send BootNotification try { - this.sendMessage(uuid(), this._bootNotificationMessage, OCPP_JSON_CALL_MESSAGE, 'BootNotification'); + this.sendMessage(uuid(), this._bootNotificationMessage, Constants.OCPP_JSON_CALL_MESSAGE, 'BootNotification'); } catch (error) { logger.error(this._basicFormatLog() + ' Send boot notification error: ' + error); } @@ -187,20 +180,21 @@ class ChargingStation { } async onMessage(message) { - // Parse the message - const [messageType, messageId, commandName, commandPayload, errorDetails] = JSON.parse(message); - + let [messageType, messageId, commandName, commandPayload, errorDetails] = [0, '', Constants.ENTITY_CHARGING_STATION, '', '']; try { + // Parse the message + [messageType, messageId, commandName, commandPayload, errorDetails] = JSON.parse(message); + // Check the Type of message switch (messageType) { // Incoming Message - case OCPP_JSON_CALL_MESSAGE: + case Constants.OCPP_JSON_CALL_MESSAGE: // Process the call this._statistics.addMessage(commandName); await this.handleRequest(messageId, commandName, commandPayload); break; // Outcome Message - case OCPP_JSON_CALL_RESULT_MESSAGE: + case Constants.OCPP_JSON_CALL_RESULT_MESSAGE: // Respond // eslint-disable-next-line no-case-declarations let responseCallback; let requestPayload; @@ -218,7 +212,7 @@ class ChargingStation { responseCallback(commandName, requestPayload); break; // Error Message - case OCPP_JSON_CALL_ERROR_MESSAGE: + case Constants.OCPP_JSON_CALL_ERROR_MESSAGE: if (!this._requests[messageId]) { // Error throw new Error(`Error for unknown message id ${messageId}`); @@ -270,19 +264,19 @@ class ChargingStation { } } - send(command, messageType = OCPP_JSON_CALL_MESSAGE) { + send(command, messageType = Constants.OCPP_JSON_CALL_MESSAGE) { // Send Message return this.sendMessage(uuid(), command, messageType); } sendError(messageId, err) { // Check exception: only OCPP error are accepted - const error = (err instanceof OCPPError ? err : new OCPPError(OCPP_ERROR_INTERNAL_ERROR, err.message)); + const error = (err instanceof OCPPError ? err : new OCPPError(Constants.OCPP_ERROR_INTERNAL_ERROR, err.message)); // Send error - return this.sendMessage(messageId, error, OCPP_JSON_CALL_ERROR_MESSAGE); + return this.sendMessage(messageId, error, Constants.OCPP_JSON_CALL_ERROR_MESSAGE); } - sendMessage(messageId, command, messageType = OCPP_JSON_CALL_RESULT_MESSAGE, commandName = '') { + sendMessage(messageId, command, messageType = Constants.OCPP_JSON_CALL_RESULT_MESSAGE, commandName = '') { // Send a message through wsConnection const self = this; // Create a promise @@ -291,28 +285,22 @@ class ChargingStation { // Type of message switch (messageType) { // Request - case OCPP_JSON_CALL_MESSAGE: + case Constants.OCPP_JSON_CALL_MESSAGE: this._statistics.addMessage(commandName); // Build request this._requests[messageId] = [responseCallback, rejectCallback, command]; messageToSend = JSON.stringify([messageType, messageId, commandName, command]); break; // Response - case OCPP_JSON_CALL_RESULT_MESSAGE: + case Constants.OCPP_JSON_CALL_RESULT_MESSAGE: // Build response messageToSend = JSON.stringify([messageType, messageId, command]); break; // Error Message - case OCPP_JSON_CALL_ERROR_MESSAGE: + case Constants.OCPP_JSON_CALL_ERROR_MESSAGE: // Build Message - // eslint-disable-next-line no-case-declarations - const { - code, - message, - details, - } = command; - this._statistics.addMessage(`Error ${code}`); - messageToSend = JSON.stringify([messageType, messageId, code, message, details]); + this._statistics.addMessage(`Error ${command.code}`); + messageToSend = JSON.stringify([messageType, messageId, command.code ? command.code : Constants.OCPP_ERROR_GENERIC_ERROR, command.message ? command.message : '', command.details ? command.details : {}]); break; } // Check if wsConnection in ready @@ -324,13 +312,13 @@ class ChargingStation { this._messageQueue.push(messageToSend); } // Request? - if (messageType !== OCPP_JSON_CALL_MESSAGE) { + if (messageType !== Constants.OCPP_JSON_CALL_MESSAGE) { // Yes: send Ok resolve(); } else if (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}`), OCPP_SOCKET_TIMEOUT); + setTimeout(() => rejectCallback(`Timeout for message ${messageId}`), Constants.OCPP_SOCKET_TIMEOUT); } // Function that will receive the request's response @@ -373,7 +361,7 @@ class ChargingStation { // determine number of customized connectors let lastConnector; for (lastConnector in connectorsConfig) { - if (lastConnector === 0 && this._stationInfo.usedConnectorId0) { + if (Utils.convertToInt(lastConnector) === 0 && this._stationInfo.usedConnectorId0) { this._connectors[lastConnector] = connectorsConfig[lastConnector]; } } @@ -413,29 +401,29 @@ class ChargingStation { } handleResponseStartTransaction(payload, requestPayload) { - this._connectors[requestPayload.connectorId] = { - transactionStarted: false, - idTag: requestPayload.idTag, - }; + // Reset connector transaction related attributes + this._connectors[requestPayload.connectorId].transactionStarted = false; + this._connectors[requestPayload.connectorId].idTag = requestPayload.idTag; + if (payload.idTagInfo.status === 'Accepted') { for (const connector in this._connectors) { - if (connector === requestPayload.connectorId) { + if (Utils.convertToInt(connector) === requestPayload.connectorId) { this._connectors[connector].transactionStarted = true; this._connectors[connector].transactionId = payload.transactionId; this._connectors[connector].lastConsumptionValue = 0; this._connectors[connector].lastSoC = 0; - logger.info(this._basicFormatLog() + ' Transaction ' + this._connectors[connector].transactionId + ' STARTED on ' + this._stationInfo.name + '#' + requestPayload.connectorId); + logger.info(this._basicFormatLog() + ' Transaction ' + this._connectors[connector].transactionId + ' STARTED on ' + this._stationInfo.name + '#' + requestPayload.connectorId + ' with idTag ' + requestPayload.idTag); this.sendStatusNotification(requestPayload.connectorId, 'Charging'); - const configuredMeterInterval = this._configuration.configurationKey.find((value) => value.key === 'meterValueInterval'); + const configuredMeterValueSampleInterval = this._configuration.configurationKey.find((value) => value.key === 'MeterValueSampleInterval'); this.startMeterValues(requestPayload.connectorId, - (configuredMeterInterval ? configuredMeterInterval.value * 1000 : 60000), + (configuredMeterValueSampleInterval ? configuredMeterValueSampleInterval.value * 1000 : 60000), this); } } } else { logger.error(this._basicFormatLog() + ' Starting transaction id ' + payload.transactionId + ' REJECTED with status ' + payload.idTagInfo.status + ', idTag ' + requestPayload.idTag); for (const connector in this._connectors) { - if (connector === requestPayload.connectorId) { + if (Utils.convertToInt(connector) === requestPayload.connectorId) { this._resetTransactionOnConnector(connector); } } @@ -450,7 +438,7 @@ class ChargingStation { errorCode, status, }; - await this.sendMessage(uuid(), payload, OCPP_JSON_CALL_MESSAGE, 'StatusNotification'); + await this.sendMessage(uuid(), payload, Constants.OCPP_JSON_CALL_MESSAGE, 'StatusNotification'); } catch (error) { logger.error(this._basicFormatLog() + ' Send status error: ' + error); } @@ -465,7 +453,7 @@ class ChargingStation { const payload = { currentTime: new Date().toISOString(), }; - self.sendMessage(uuid(), payload, OCPP_JSON_CALL_MESSAGE, 'Heartbeat'); + self.sendMessage(uuid(), payload, Constants.OCPP_JSON_CALL_MESSAGE, 'Heartbeat'); } catch (error) { logger.error(self._basicFormatLog() + ' Send heartbeat error: ' + error); } @@ -490,12 +478,12 @@ class ChargingStation { await this.sendError(messageId, error); } } else { - // Throw Exception - await this.sendError(messageId, new OCPPError(OCPP_ERROR_NOT_IMPLEMENTED, 'Not implemented', {})); + // Throw exception + await this.sendError(messageId, new OCPPError(Constants.OCPP_ERROR_NOT_IMPLEMENTED, 'Not implemented', {})); throw new Error(`${commandName} is not implemented ${JSON.stringify(commandPayload, null, ' ')}`); } - // Send Response - await this.sendMessage(messageId, result, OCPP_JSON_CALL_RESULT_MESSAGE); + // Send response + await this.sendMessage(messageId, result, Constants.OCPP_JSON_CALL_RESULT_MESSAGE); } async handleGetConfiguration() { @@ -504,38 +492,32 @@ class ChargingStation { async handleChangeConfiguration(commandPayload) { const keyToChange = this._configuration.configurationKey.find((element) => element.key === commandPayload.key); - if (keyToChange) { - keyToChange.value = commandPayload.value; - return { - status: 'Accepted', - }; + if (keyToChange && !Utils.convertToBoolean(keyToChange.readonly)) { + const keyIndex = this._configuration.configurationKey.indexOf(keyToChange); + this._configuration.configurationKey[keyIndex].value = commandPayload.value; + return Constants.OCPP_RESPONSE_ACCEPTED; } - return { - status: 'Rejected', - }; + return Constants.OCPP_RESPONSE_REJECTED; } async handleRemoteStartTransaction(commandPayload) { const transactionConnectorID = (commandPayload.connectorId ? commandPayload.connectorId : '1'); if (this.isAuthorizationRequested() && this._authorizeRemoteTxRequests) { - // check if authorized + // Check if authorized if (this._authorizedKeys.find((value) => value === commandPayload.idTag)) { // Authorization successful start transaction setTimeout(() => this.sendStartTransaction(transactionConnectorID, commandPayload.idTag), 500); - return { - status: 'Accepted', - }; + logger.info(this._basicFormatLog() + ' Transaction remotely STARTED on ' + this._stationInfo.name + '#' + transactionConnectorID + ' with idTag ' + commandPayload.idTag); + return Constants.OCPP_RESPONSE_ACCEPTED; } // Start authorization checks - return { - status: 'Rejected', - }; + logger.error(this._basicFormatLog() + ' Remote starting transaction REJECTED with status ' + commandPayload.idTagInfo.status + ', idTag ' + commandPayload.idTag); + return Constants.OCPP_RESPONSE_REJECTED; } - // no local authorization check required => start transaction + // No local authorization check required => start transaction setTimeout(() => this.sendStartTransaction(transactionConnectorID, commandPayload.idTag), 500); - return { - status: 'Accepted', - }; + logger.info(this._basicFormatLog() + ' Transaction remotely STARTED on ' + this._stationInfo.name + '#' + transactionConnectorID + ' with idTag ' + commandPayload.idTag); + return Constants.OCPP_RESPONSE_ACCEPTED; } async sendStartTransaction(connectorID, idTag) { @@ -546,7 +528,7 @@ class ChargingStation { meterStart: 0, timestamp: new Date().toISOString(), }; - return await this.sendMessage(uuid(), payload, OCPP_JSON_CALL_MESSAGE, 'StartTransaction'); + return await this.sendMessage(uuid(), payload, Constants.OCPP_JSON_CALL_MESSAGE, 'StartTransaction'); } catch (error) { logger.error(this._basicFormatLog() + ' Send start transaction error: ' + error); this._resetTransactionOnConnector(connectorID); @@ -561,7 +543,7 @@ class ChargingStation { meterStop: 0, timestamp: new Date().toISOString(), }; - await this.sendMessage(uuid(), payload, OCPP_JSON_CALL_MESSAGE, 'StopTransaction'); + await this.sendMessage(uuid(), payload, Constants.OCPP_JSON_CALL_MESSAGE, 'StopTransaction'); logger.info(this._basicFormatLog() + ' Transaction ' + this._connectors[connectorID].transactionId + ' STOPPED on ' + this._stationInfo.name + '#' + connectorID); this.sendStatusNotification(connectorID, 'Available'); } catch (error) { @@ -606,7 +588,7 @@ class ChargingStation { // Persist previous value in connector const connector = self._connectors[connectorID]; let consumption; - consumption = Utils.getRandomInt(self._stationInfo.maxPower / 3600000 * interval, 3); + consumption = Utils.getRandomInt(self._stationInfo.maxPower / 3600000 * interval, 4); if (connector && connector.lastConsumptionValue >= 0) { connector.lastConsumptionValue += consumption; } else { @@ -628,18 +610,18 @@ class ChargingStation { transactionId: self._connectors[connectorID].transactionId, meterValue: [sampledValueLcl], }; - await self.sendMessage(uuid(), payload, OCPP_JSON_CALL_MESSAGE, 'MeterValues'); + await self.sendMessage(uuid(), payload, Constants.OCPP_JSON_CALL_MESSAGE, 'MeterValues'); } catch (error) { logger.error(self._basicFormatLog() + ' Send meter values error: ' + error); } } async startMeterValues(connectorID, interval, self) { - // if (!this._connectors[connectorID].transactionStarted) { - // logger.debug(`${self._basicFormatLog()} Trying to start meter values on connector ID ${connectorID} with no transaction`); - // } else if (this._connectors[connectorID].transactionStarted && !this._connectors[connectorID].transactionId) { - // logger.debug(`${self._basicFormatLog()} Trying to start meter values on connector ID ${connectorID} with no transaction id`); - // } + if (!this._connectors[connectorID].transactionStarted) { + logger.debug(`${self._basicFormatLog()} Trying to start meter values on connector ID ${connectorID} with no transaction`); + } else if (this._connectors[connectorID].transactionStarted && !this._connectors[connectorID].transactionId) { + logger.debug(`${self._basicFormatLog()} Trying to start meter values on connector ID ${connectorID} with no transaction id`); + } this._connectors[connectorID].transactionInterval = setInterval(async () => { const sendMeterValues = performance.timerify(this.sendMeterValues); this._performanceObserver.observe({ @@ -655,9 +637,7 @@ class ChargingStation { this.sendStopTransaction(commandPayload.transactionId, connector); } } - return { - status: 'Accepted', - }; + return Constants.OCPP_RESPONSE_ACCEPTED; } isAuthorizationRequested() {