X-Git-Url: https://git.piment-noir.org/?a=blobdiff_plain;f=src%2Fcharging-station%2FChargingStation.js;h=ebc3509ce97c193dde369d4f9269d5e6a65b705f;hb=027b409aee809cc066ab83006284d03e1678023b;hp=7315f73f98e6c7fcef257ceeaa1854d0d3f93495;hpb=894a1780a9aeabd574529e2c80be3b7aa884b93c;p=e-mobility-charging-stations-simulator.git diff --git a/src/charging-station/ChargingStation.js b/src/charging-station/ChargingStation.js index 7315f73f..ebc3509c 100644 --- a/src/charging-station/ChargingStation.js +++ b/src/charging-station/ChargingStation.js @@ -4,58 +4,106 @@ const WebSocket = require('ws'); const Constants = require('../utils/Constants'); const Utils = require('../utils/Utils'); const OCPPError = require('./OcppError'); -const {v4: uuid} = require('uuid'); const AutomaticTransactionGenerator = require('./AutomaticTransactionGenerator'); const Statistics = require('../utils/Statistics'); const fs = require('fs'); const {performance, PerformanceObserver} = require('perf_hooks'); class ChargingStation { - constructor(index, stationTemplate) { - this._requests = {}; + constructor(index, stationTemplateFile) { + this._index = index; + this._stationTemplateFile = stationTemplateFile; + this._initialize(); + this._autoReconnectRetryCount = 0; this._autoReconnectMaxRetries = Configuration.getAutoReconnectMaxRetries(); // -1 for unlimited this._autoReconnectTimeout = Configuration.getAutoReconnectTimeout() * 1000; // ms, zero for disabling + + this._requests = {}; + this._messageQueue = []; + this._isSocketRestart = false; - this._stationInfo = this._buildChargingStation(index, stationTemplate); + } + + _initialize() { + this._stationInfo = this._buildStationInfo(); + this._bootNotificationMessage = { + chargePointModel: this._stationInfo.chargePointModel, + chargePointVendor: this._stationInfo.chargePointVendor, + }; + this._configuration = this._getConfiguration(); + this._authorizedTags = this._getAuthorizedTags(); + this._supervisionUrl = this._getSupervisionURL(); this._statistics = new Statistics(this._stationInfo.name); this._performanceObserver = new PerformanceObserver((list) => { const entry = list.getEntries()[0]; this._statistics.logPerformance(entry, 'ChargingStation'); this._performanceObserver.disconnect(); }); - this._index = index; - this._messageQueue = []; - this._bootNotificationMessage = { - chargePointModel: this._stationInfo.chargePointModel, - chargePointVendor: this._stationInfo.chargePointVendor, - }; - this._configuration = this._getConfiguration(stationTemplate); - this._authorizationFile = this._getAuthorizationFile(stationTemplate); - this._supervisionUrl = this._getSupervisionURL(index, stationTemplate); } _basicFormatLog() { return Utils.basicFormatLog(` ${this._stationInfo.name}:`); } - // eslint-disable-next-line class-methods-use-this - _getConfiguration(stationTemplate) { - return stationTemplate.Configuration ? stationTemplate.Configuration : {}; + _getConfiguration() { + return this._stationInfo.Configuration ? this._stationInfo.Configuration : {}; } - // eslint-disable-next-line class-methods-use-this - _getAuthorizationFile(stationTemplate) { - return stationTemplate.authorizationFile ? stationTemplate.authorizationFile : ''; + _getAuthorizationFile() { + return this._stationInfo.authorizationFile ? this._stationInfo.authorizationFile : ''; } - // eslint-disable-next-line class-methods-use-this - _getSupervisionURL(index, stationTemplate) { - const supervisionUrls = JSON.parse(JSON.stringify(stationTemplate.supervisionURL ? stationTemplate.supervisionURL : Configuration.getSupervisionURLs())); + _getAuthorizedTags() { + let authorizedTags = []; + const authorizationFile = this._getAuthorizationFile(); + if (authorizationFile) { + try { + // Load authorization file + const fileDescriptor = fs.openSync(authorizationFile, 'r'); + authorizedTags = JSON.parse(fs.readFileSync(fileDescriptor, 'utf8')); + fs.closeSync(fileDescriptor); + } catch (error) { + logger.error(this._basicFormatLog() + ' Authorization file loading error: ' + error); + } + } else { + logger.info(this._basicFormatLog() + ' No authorization file given in template file ' + this._stationTemplateFile); + } + return authorizedTags; + } + + _startAuthorizationFileMonitoring() { + // eslint-disable-next-line no-unused-vars + fs.watchFile(this._getAuthorizationFile(), (current, previous) => { + try { + logger.debug(this._basicFormatLog() + ' Authorization file ' + this._getAuthorizationFile() + ' have changed, reload'); + // Initialize _authorizedTags + this._authorizedTags = this._getAuthorizedTags(); + } catch (error) { + logger.error(this._basicFormatLog() + ' Authorization file monitoring error: ' + error); + } + }); + } + + _startStationTemplateFileMonitoring() { + // eslint-disable-next-line no-unused-vars + fs.watchFile(this._stationTemplateFile, (current, previous) => { + try { + logger.debug(this._basicFormatLog() + ' Template file ' + this._stationTemplateFile + ' have changed, reload'); + // Initialize + this._initialize(); + } catch (error) { + logger.error(this._basicFormatLog() + ' Charging station template file monitoring error: ' + error); + } + }); + } + + _getSupervisionURL() { + const supervisionUrls = Utils.cloneJSonDocument(this._stationInfo.supervisionURL ? this._stationInfo.supervisionURL : Configuration.getSupervisionURLs()); let indexUrl = 0; if (Array.isArray(supervisionUrls)) { - if (Configuration.getEquallySupervisionDistribution()) { - indexUrl = index % supervisionUrls.length; + if (Configuration.getDistributeStationToTenantEqually()) { + indexUrl = this._index % supervisionUrls.length; } else { // Get a random url indexUrl = Math.floor(Math.random() * supervisionUrls.length); @@ -65,55 +113,48 @@ class ChargingStation { return supervisionUrls; } - // eslint-disable-next-line class-methods-use-this - _getStationName(index, stationTemplate) { - return stationTemplate.fixedName ? stationTemplate.baseName : stationTemplate.baseName + '-' + ('000000000' + index).substr(('000000000' + index).length - 4); + _getStationName(stationTemplate) { + return stationTemplate.fixedName ? stationTemplate.baseName : stationTemplate.baseName + '-' + ('000000000' + this._index).substr(('000000000' + this._index).length - 4); } _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) { - if (Array.isArray(stationTemplate.power)) { - stationTemplate.maxPower = stationTemplate.power[Math.floor(Math.random() * stationTemplate.power.length)]; + _getLocalAuthListEnabled() { + const localAuthListEnabled = this._configuration.configurationKey.find((configElement) => configElement.key === 'LocalAuthListEnabled'); + return localAuthListEnabled ? Utils.convertToBoolean(localAuthListEnabled.value) : false; + } + + _buildStationInfo() { + let stationTemplateFromFile; + try { + // Load template file + const fileDescriptor = fs.openSync(this._stationTemplateFile, 'r'); + stationTemplateFromFile = JSON.parse(fs.readFileSync(fileDescriptor, 'utf8')); + fs.closeSync(fileDescriptor); + } catch (error) { + logger.error(this._basicFormatLog() + ' Template file loading error: ' + error); + } + const stationTemplate = stationTemplateFromFile || {}; + if (Array.isArray(stationTemplateFromFile.power)) { + stationTemplate.maxPower = stationTemplateFromFile.power[Math.floor(Math.random() * stationTemplateFromFile.power.length)]; } else { - stationTemplate.maxPower = stationTemplate.power; + stationTemplate.maxPower = stationTemplateFromFile.power; } - stationTemplate.name = this._getStationName(index, stationTemplate); + stationTemplate.name = this._getStationName(stationTemplateFromFile); return stationTemplate; } async start() { logger.info(this._basicFormatLog() + ' Will communicate with ' + this._supervisionUrl); + // Monitor authorization file + this._startAuthorizationFileMonitoring(); + // Monitor station template file + this._startStationTemplateFileMonitoring(); this._url = this._supervisionUrl + '/' + this._stationInfo.name; this._wsConnection = new WebSocket(this._url, 'ocpp1.6'); - if (this._authorizationFile !== '') { - try { - // load file - const fileDescriptor = fs.openSync(this._authorizationFile, 'r'); - this._authorizedKeys = JSON.parse(fs.readFileSync(fileDescriptor, 'utf8')); - fs.closeSync(fileDescriptor); - // get remote authorization logic - // FIXME: move to the constructor - this._authorizeRemoteTxRequests = this._getAuthorizeRemoteTxRequests(); - // monitor authorization file - // eslint-disable-next-line no-unused-vars - fs.watchFile(this._authorizationFile, (current, previous) => { - try { - // reload file - const fileDescriptor = fs.openSync(this._authorizationFile, 'r'); - this._authorizedKeys = JSON.parse(fs.readFileSync(fileDescriptor, 'utf8')); - fs.closeSync(fileDescriptor); - } catch (error) { - logger.error(this._basicFormatLog() + ' Authorization file error: ' + error); - } - }); - } catch (error) { - logger.error(this._basicFormatLog() + ' Authorization file error: ' + error); - } - } // Handle Socket incoming messages this._wsConnection.on('message', this.onMessage.bind(this)); // Handle Socket error @@ -129,7 +170,7 @@ class ChargingStation { onOpen() { logger.info(`${this._basicFormatLog()} Is connected to server through ${this._url}`); if (this._isSocketRestart) { - this.basicStartMessageSequence(); + this._basicStartMessageSequence(); if (this._messageQueue.length > 0) { this._messageQueue.forEach((message) => { if (this._wsConnection.readyState === WebSocket.OPEN) { @@ -138,9 +179,9 @@ class ChargingStation { }); } } else { - // At first start, send Bootnotification + // At first start, send BootNotification try { - this.sendMessage(uuid(), this._bootNotificationMessage, Constants.OCPP_JSON_CALL_MESSAGE, 'BootNotification'); + this.sendMessage(Utils.generateUUID(), this._bootNotificationMessage, Constants.OCPP_JSON_CALL_MESSAGE, 'BootNotification'); } catch (error) { logger.error(this._basicFormatLog() + ' Send boot notification error: ' + error); } @@ -176,7 +217,7 @@ class ChargingStation { } onPing() { - logger.info(this._basicFormatLog() + ' Has received a WS ping (rfc6455) from the server'); + logger.debug(this._basicFormatLog() + ' Has received a WS ping (rfc6455) from the server'); } async onMessage(message) { @@ -239,6 +280,25 @@ class ChargingStation { } } + // eslint-disable-next-line class-methods-use-this + async _startHeartbeat(self) { + if (self._heartbeatInterval && !self._heartbeatSetInterval) { + logger.info(self._basicFormatLog() + ' Heartbeat started every ' + self._heartbeatInterval + 'ms'); + self._heartbeatSetInterval = setInterval(() => { + try { + const payload = { + currentTime: new Date().toISOString(), + }; + self.sendMessage(Utils.generateUUID(), payload, Constants.OCPP_JSON_CALL_MESSAGE, 'Heartbeat'); + } catch (error) { + logger.error(self._basicFormatLog() + ' Send heartbeat error: ' + error); + } + }, self._heartbeatInterval); + } else { + logger.error(self._basicFormatLog() + ' Heartbeat interval undefined, not starting the heartbeat'); + } + } + _reconnect(error) { logger.error(this._basicFormatLog() + ' Socket: abnormally closed', error); // Stop heartbeat interval @@ -266,7 +326,7 @@ class ChargingStation { send(command, messageType = Constants.OCPP_JSON_CALL_MESSAGE) { // Send Message - return this.sendMessage(uuid(), command, messageType); + return this.sendMessage(Utils.generateUUID(), command, messageType); } sendError(messageId, err) { @@ -276,6 +336,23 @@ class ChargingStation { return this.sendMessage(messageId, error, Constants.OCPP_JSON_CALL_ERROR_MESSAGE); } + async sendStatusNotification(connectorId, status, errorCode = 'NoError') { + try { + const payload = { + connectorId, + errorCode, + status, + }; + await this.sendMessage(Utils.generateUUID(), payload, Constants.OCPP_JSON_CALL_MESSAGE, 'StatusNotification'); + } catch (error) { + logger.error(this._basicFormatLog() + ' Send status error: ' + error); + } + } + + async sendStatusNotificationWithTimeout(connectorId, status, errorCode = 'NoError', timeout = Constants.STATUS_NOTIFICATION_TIMEOUT) { + setTimeout(() => this.sendStatusNotification(connectorId, status, errorCode), timeout); + } + sendMessage(messageId, command, messageType = Constants.OCPP_JSON_CALL_RESULT_MESSAGE, commandName = '') { // Send a message through wsConnection const self = this; @@ -303,7 +380,7 @@ class ChargingStation { 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 + // Check if wsConnection is ready if (this._wsConnection.readyState === WebSocket.OPEN) { // Yes: Send Message this._wsConnection.send(messageToSend); @@ -328,7 +405,7 @@ class ChargingStation { if (typeof self[responseCallbackFn] === 'function') { self[responseCallbackFn](payload, requestPayload, self); } else { - // logger.error(this._basicFormatLog() + ' Trying to call an undefined callback function: ' + responseCallbackFn) + logger.debug(self._basicFormatLog() + ' Trying to call an undefined callback function: ' + responseCallbackFn); } // Send the response resolve(payload); @@ -346,22 +423,17 @@ class ChargingStation { }); } - handleResponseBootNotification(payload) { - if (payload.status === 'Accepted') { - this._heartbeatInterval = payload.interval * 1000; - this.basicStartMessageSequence(); - } - } - - async basicStartMessageSequence() { + async _basicStartMessageSequence() { this._startHeartbeat(this); - if (!this._connectors) { // build connectors + // build connectors + if (!this._connectors) { this._connectors = {}; - const connectorsConfig = JSON.parse(JSON.stringify(this._stationInfo.Connectors)); + const connectorsConfig = Utils.cloneJSonDocument(this._stationInfo.Connectors); // determine number of customized connectors let lastConnector; for (lastConnector in connectorsConfig) { - if (lastConnector === 0 && this._stationInfo.usedConnectorId0) { + // add connector 0, OCPP specification violation that for example KEBA have + if (Utils.convertToInt(lastConnector) === 0 && this._stationInfo.useConnectorId0) { this._connectors[lastConnector] = connectorsConfig[lastConnector]; } } @@ -374,7 +446,7 @@ class ChargingStation { } // generate all connectors for (let index = 1; index <= maxConnectors; index++) { - const randConnectorID = (this._stationInfo.randomConnectors ? Utils.getRandomInt(lastConnector, 1) : index); + const randConnectorID = this._stationInfo.randomConnectors ? Utils.getRandomInt(maxConnectors, 1) : index; this._connectors[index] = connectorsConfig[randConnectorID]; } } @@ -382,12 +454,12 @@ class ChargingStation { for (const connector in this._connectors) { if (!this._connectors[connector].transactionStarted) { if (this._connectors[connector].bootStatus) { - setTimeout(() => this.sendStatusNotification(connector, this._connectors[connector].bootStatus), 500); + this.sendStatusNotificationWithTimeout(connector, this._connectors[connector].bootStatus); } else { - setTimeout(() => this.sendStatusNotification(connector, 'Available'), 500); + this.sendStatusNotificationWithTimeout(connector, 'Available'); } } else { - setTimeout(() => this.sendStatusNotification(connector, 'Charging'), 500); + this.sendStatusNotificationWithTimeout(connector, 'Charging'); } } @@ -400,30 +472,49 @@ class ChargingStation { this._statistics.start(); } + _resetTransactionOnConnector(connectorID) { + this._connectors[connectorID].transactionStarted = false; + this._connectors[connectorID].transactionId = null; + this._connectors[connectorID].lastConsumptionValue = -1; + this._connectors[connectorID].lastSoC = -1; + if (this._connectors[connectorID].transactionInterval) { + clearInterval(this._connectors[connectorID].transactionInterval); + } + } + + handleResponseBootNotification(payload) { + if (payload.status === 'Accepted') { + this._heartbeatInterval = payload.interval * 1000; + this._basicStartMessageSequence(); + } else { + logger.info(this._basicFormatLog() + ' Boot Notification rejected'); + } + } + 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) === Utils.convertToInt(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 + ' for 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) === Utils.convertToInt(requestPayload.connectorId)) { this._resetTransactionOnConnector(connector); } } @@ -431,36 +522,16 @@ class ChargingStation { } } - async sendStatusNotification(connectorId, status, errorCode = 'NoError') { - try { - const payload = { - connectorId, - errorCode, - status, - }; - await this.sendMessage(uuid(), payload, Constants.OCPP_JSON_CALL_MESSAGE, 'StatusNotification'); - } catch (error) { - logger.error(this._basicFormatLog() + ' Send status error: ' + error); - } + handleResponseStatusNotification(payload) { + logger.debug(this._basicFormatLog() + ' Status notification response received: %j', payload); } - // eslint-disable-next-line class-methods-use-this - async _startHeartbeat(self) { - if (self._heartbeatInterval && !self._heartbeatSetInterval) { - logger.info(self._basicFormatLog() + ' Heartbeat started every ' + self._heartbeatInterval + 'ms'); - self._heartbeatSetInterval = setInterval(() => { - try { - const payload = { - currentTime: new Date().toISOString(), - }; - self.sendMessage(uuid(), payload, Constants.OCPP_JSON_CALL_MESSAGE, 'Heartbeat'); - } catch (error) { - logger.error(self._basicFormatLog() + ' Send heartbeat error: ' + error); - } - }, self._heartbeatInterval); - } else { - logger.error(self._basicFormatLog() + ' Heartbeat interval undefined, not starting the heartbeat'); - } + handleResponseMeterValues(payload) { + logger.debug(this._basicFormatLog() + ' MeterValues response received: %j', payload); + } + + handleResponseHeartbeat(payload) { + logger.debug(this._basicFormatLog() + ' Heartbeat response received: %j', payload); } async handleRequest(messageId, commandName, commandPayload) { @@ -478,11 +549,11 @@ class ChargingStation { await this.sendError(messageId, error); } } else { - // Throw Exception + // 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 + // Send response await this.sendMessage(messageId, result, Constants.OCPP_JSON_CALL_RESULT_MESSAGE); } @@ -492,8 +563,9 @@ class ChargingStation { async handleChangeConfiguration(commandPayload) { const keyToChange = this._configuration.configurationKey.find((element) => element.key === commandPayload.key); - if (keyToChange) { - keyToChange.value = commandPayload.value; + 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 Constants.OCPP_RESPONSE_REJECTED; @@ -501,18 +573,29 @@ class ChargingStation { async handleRemoteStartTransaction(commandPayload) { const transactionConnectorID = (commandPayload.connectorId ? commandPayload.connectorId : '1'); - if (this.isAuthorizationRequested() && this._authorizeRemoteTxRequests) { + if (this.hasAuthorizedTags() && this._getLocalAuthListEnabled() && this._getAuthorizeRemoteTxRequests()) { // Check if authorized - if (this._authorizedKeys.find((value) => value === commandPayload.idTag)) { + if (this._authorizedTags.find((value) => value === commandPayload.idTag)) { // Authorization successful start transaction - setTimeout(() => this.sendStartTransaction(transactionConnectorID, commandPayload.idTag), 500); + setTimeout(() => this.sendStartTransaction(transactionConnectorID, commandPayload.idTag), Constants.START_TRANSACTION_TIMEOUT); + logger.debug(this._basicFormatLog() + ' Transaction remotely STARTED on ' + this._stationInfo.name + '#' + transactionConnectorID + ' for idTag ' + commandPayload.idTag); return Constants.OCPP_RESPONSE_ACCEPTED; } - // Start authorization checks + 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 - setTimeout(() => this.sendStartTransaction(transactionConnectorID, commandPayload.idTag), 500); + setTimeout(() => this.sendStartTransaction(transactionConnectorID, commandPayload.idTag), Constants.START_TRANSACTION_TIMEOUT); + logger.debug(this._basicFormatLog() + ' Transaction remotely STARTED on ' + this._stationInfo.name + '#' + transactionConnectorID + ' for idTag ' + commandPayload.idTag); + return Constants.OCPP_RESPONSE_ACCEPTED; + } + + async handleRemoteStopTransaction(commandPayload) { + for (const connector in this._connectors) { + if (this._connectors[connector].transactionId === commandPayload.transactionId) { + this.sendStopTransaction(commandPayload.transactionId, connector); + } + } return Constants.OCPP_RESPONSE_ACCEPTED; } @@ -524,7 +607,7 @@ class ChargingStation { meterStart: 0, timestamp: new Date().toISOString(), }; - return await this.sendMessage(uuid(), payload, Constants.OCPP_JSON_CALL_MESSAGE, 'StartTransaction'); + return await this.sendMessage(Utils.generateUUID(), payload, Constants.OCPP_JSON_CALL_MESSAGE, 'StartTransaction'); } catch (error) { logger.error(this._basicFormatLog() + ' Send start transaction error: ' + error); this._resetTransactionOnConnector(connectorID); @@ -539,7 +622,7 @@ class ChargingStation { meterStop: 0, timestamp: new Date().toISOString(), }; - await this.sendMessage(uuid(), payload, Constants.OCPP_JSON_CALL_MESSAGE, 'StopTransaction'); + await this.sendMessage(Utils.generateUUID(), 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) { @@ -550,23 +633,13 @@ class ChargingStation { } } - _resetTransactionOnConnector(connectorID) { - this._connectors[connectorID].transactionStarted = false; - this._connectors[connectorID].transactionId = null; - this._connectors[connectorID].lastConsumptionValue = -1; - this._connectors[connectorID].lastSoC = -1; - if (this._connectors[connectorID].transactionInterval) { - clearInterval(this._connectors[connectorID].transactionInterval); - } - } - // eslint-disable-next-line class-methods-use-this async sendMeterValues(connectorID, interval, self) { try { const sampledValueLcl = { timestamp: new Date().toISOString(), }; - const meterValuesClone = JSON.parse(JSON.stringify(self._getConnector(connectorID).MeterValues)); + const meterValuesClone = Utils.cloneJSonDocument(self._getConnector(connectorID).MeterValues); if (Array.isArray(meterValuesClone)) { sampledValueLcl.sampledValue = meterValuesClone; } else { @@ -578,13 +651,13 @@ class ChargingStation { if (sampledValueLcl.sampledValue[index].value > 100) { logger.info(self._basicFormatLog() + ' Meter type: ' + (sampledValueLcl.sampledValue[index].measurand ? sampledValueLcl.sampledValue[index].measurand : 'default') + - ' value: ' + sampledValueLcl.sampledValue[index].value); + ', value: ' + sampledValueLcl.sampledValue[index].value); } } else { // 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 { @@ -596,7 +669,7 @@ class ChargingStation { if (sampledValueLcl.sampledValue[index].value > (self._stationInfo.maxPower * 3600 / interval) || sampledValueLcl.sampledValue[index].value < 500) { logger.info(self._basicFormatLog() + ' Meter type: ' + (sampledValueLcl.sampledValue[index].measurand ? sampledValueLcl.sampledValue[index].measurand : 'default') + - ' value: ' + sampledValueLcl.sampledValue[index].value + '/' + (self._stationInfo.maxPower * 3600 / interval)); + ', value: ' + sampledValueLcl.sampledValue[index].value + '/' + (self._stationInfo.maxPower * 3600 / interval)); } } } @@ -606,18 +679,18 @@ class ChargingStation { transactionId: self._connectors[connectorID].transactionId, meterValue: [sampledValueLcl], }; - await self.sendMessage(uuid(), payload, Constants.OCPP_JSON_CALL_MESSAGE, 'MeterValues'); + await self.sendMessage(Utils.generateUUID(), 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 started`); + } 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({ @@ -627,22 +700,13 @@ class ChargingStation { }, interval); } - async handleRemoteStopTransaction(commandPayload) { - for (const connector in this._connectors) { - if (this._connectors[connector].transactionId === commandPayload.transactionId) { - this.sendStopTransaction(commandPayload.transactionId, connector); - } - } - return Constants.OCPP_RESPONSE_ACCEPTED; - } - - isAuthorizationRequested() { - return this._authorizedKeys && this._authorizedKeys.length > 0; + hasAuthorizedTags() { + return Array.isArray(this._authorizedTags) && this._authorizedTags.length > 0; } getRandomTagId() { - const index = Math.round(Math.floor(Math.random() * this._authorizedKeys.length - 1)); - return this._authorizedKeys[index]; + const index = Math.round(Math.floor(Math.random() * this._authorizedTags.length - 1)); + return this._authorizedTags[index]; } _getConnector(number) {