X-Git-Url: https://git.piment-noir.org/?a=blobdiff_plain;f=src%2Fcharging-station%2FChargingStation.ts;h=e509c0288118e0b59c7380a4e78f8680e0af6fc6;hb=edfb206c5a6a309cbe5a8e6bf3c3f52ab57d96fc;hp=843093e27fd226b17bbba8f7062b271a8808e5a6;hpb=9ac86a7ea04eb1155c2a720b36f6bf33ba336767;p=e-mobility-charging-stations-simulator.git diff --git a/src/charging-station/ChargingStation.ts b/src/charging-station/ChargingStation.ts index 843093e2..e509c028 100644 --- a/src/charging-station/ChargingStation.ts +++ b/src/charging-station/ChargingStation.ts @@ -1,20 +1,21 @@ -import { AuthorizationStatus, StartTransactionResponse, StopTransactionReason, StopTransactionResponse } from '../types/Transaction'; +import { AuthorizationStatus, StartTransactionResponse, StopTransactionReason, StopTransactionResponse } from '../types/ocpp/1.6/Transaction'; import ChargingStationConfiguration, { ConfigurationKey } from '../types/ChargingStationConfiguration'; import ChargingStationTemplate, { PowerOutType } from '../types/ChargingStationTemplate'; -import { ConfigurationResponse, DefaultRequestResponse, UnlockResponse } from '../types/RequestResponses'; +import { ConfigurationResponse, DefaultRequestResponse, UnlockResponse } from '../types/ocpp/1.6/RequestResponses'; import Connectors, { Connector } from '../types/Connectors'; -import MeterValue, { MeterValueLocation, MeterValueMeasurand, MeterValuePhase, MeterValueUnit } from '../types/MeterValue'; +import MeterValue, { MeterValueLocation, MeterValueMeasurand, MeterValuePhase, MeterValueUnit } from '../types/ocpp/1.6/MeterValue'; import { PerformanceObserver, performance } from 'perf_hooks'; import AutomaticTransactionGenerator from './AutomaticTransactionGenerator'; -import { ChargePointErrorCode } from '../types/ChargePointErrorCode'; -import { ChargePointStatus } from '../types/ChargePointStatus'; +import { ChargePointErrorCode } from '../types/ocpp/1.6/ChargePointErrorCode'; +import { ChargePointStatus } from '../types/ocpp/1.6/ChargePointStatus'; import ChargingStationInfo from '../types/ChargingStationInfo'; import Configuration from '../utils/Configuration'; -import Constants from '../utils/Constants.js'; +import Constants from '../utils/Constants'; import ElectricUtils from '../utils/ElectricUtils'; import MeasurandValues from '../types/MeasurandValues'; -import OCPPError from './OcppError.js'; +import OCPPError from './OcppError'; +import Requests from '../types/ocpp/1.6/Requests'; import Statistics from '../utils/Statistics'; import Utils from '../utils/Utils'; import WebSocket from 'ws'; @@ -44,8 +45,8 @@ export default class ChargingStation { private _autoReconnectRetryCount: number; private _autoReconnectMaxRetries: number; private _autoReconnectTimeout: number; - private _requests: { [id: string]: [(payload?, requestPayload?) => void, (error?: OCPPError) => void, object] }; - private _messageQueue: any[]; + private _requests: Requests; + private _messageQueue: string[]; private _automaticTransactionGeneration: AutomaticTransactionGenerator; private _authorizedTags: string[]; private _heartbeatInterval: number; @@ -56,7 +57,7 @@ export default class ChargingStation { constructor(index: number, stationTemplateFile: string) { this._index = index; this._stationTemplateFile = stationTemplateFile; - this._connectors = {}; + this._connectors = {} as Connectors; this._initialize(); this._hasStopped = false; @@ -65,8 +66,8 @@ export default class ChargingStation { this._autoReconnectMaxRetries = Configuration.getAutoReconnectMaxRetries(); // -1 for unlimited this._autoReconnectTimeout = Configuration.getAutoReconnectTimeout() * 1000; // Ms, zero for disabling - this._requests = {}; - this._messageQueue = []; + this._requests = {} as Requests; + this._messageQueue = [] as string[]; this._authorizedTags = this._loadAndGetAuthorizedTags(); } @@ -83,7 +84,7 @@ export default class ChargingStation { stationTemplateFromFile = JSON.parse(fs.readFileSync(fileDescriptor, 'utf8')) as ChargingStationTemplate; fs.closeSync(fileDescriptor); } catch (error) { - logger.error('Template file ' + this._stationTemplateFile + ' loading error: ' + error); + logger.error('Template file ' + this._stationTemplateFile + ' loading error: %j', error); throw error; } const stationInfo: ChargingStationInfo = stationTemplateFromFile || {} as ChargingStationInfo; @@ -201,7 +202,7 @@ export default class ChargingStation { authorizedTags = JSON.parse(fs.readFileSync(fileDescriptor, 'utf8')) as string[]; fs.closeSync(fileDescriptor); } catch (error) { - logger.error(this._logPrefix() + ' Authorization file ' + authorizationFile + ' loading error: ' + error); + logger.error(this._logPrefix() + ' Authorization file ' + authorizationFile + ' loading error: %j', error); throw error; } } else { @@ -263,7 +264,7 @@ export default class ChargingStation { if (!Utils.isEmptyArray(this._stationInfo.numberOfConnectors)) { const numberOfConnectors = this._stationInfo.numberOfConnectors as number[]; // Distribute evenly the number of connectors - maxConnectors = numberOfConnectors[(this._index - 1) % numberOfConnectors.length] ; + maxConnectors = numberOfConnectors[(this._index - 1) % numberOfConnectors.length]; } else if (!Utils.isUndefined(this._stationInfo.numberOfConnectors)) { maxConnectors = this._stationInfo.numberOfConnectors as number; } else { @@ -306,16 +307,16 @@ export default class ChargingStation { } _getSupervisionURL(): string { - const supervisionUrls = Utils.cloneObject(this._stationInfo.supervisionURL ? this._stationInfo.supervisionURL : Configuration.getSupervisionURLs()) as string|string[]; + const supervisionUrls = Utils.cloneObject(this._stationInfo.supervisionURL ? this._stationInfo.supervisionURL : Configuration.getSupervisionURLs()) as string | string[]; let indexUrl = 0; if (!Utils.isEmptyArray(supervisionUrls)) { - if (Configuration.getDistributeStationToTenantEqually()) { + if (Configuration.getDistributeStationsToTenantsEqually()) { indexUrl = this._index % supervisionUrls.length; } else { // Get a random url indexUrl = Math.floor(Math.random() * supervisionUrls.length); } - return supervisionUrls[indexUrl] ; + return supervisionUrls[indexUrl]; } return supervisionUrls as string; } @@ -404,7 +405,7 @@ export default class ChargingStation { // Initialize _authorizedTags this._authorizedTags = this._loadAndGetAuthorizedTags(); } catch (error) { - logger.error(this._logPrefix() + ' Authorization file monitoring error: ' + error); + logger.error(this._logPrefix() + ' Authorization file monitoring error: %j', error); } }); } @@ -421,7 +422,7 @@ export default class ChargingStation { this._automaticTransactionGeneration.stop().catch(() => { }); } } catch (error) { - logger.error(this._logPrefix() + ' Charging station template file monitoring error: ' + error); + logger.error(this._logPrefix() + ' Charging station template file monitoring error: %j', error); } }); } @@ -487,13 +488,13 @@ export default class ChargingStation { } _reconnect(error): void { - logger.error(this._logPrefix() + ' Socket: abnormally closed', error); + logger.error(this._logPrefix() + ' Socket: abnormally closed: %j', error); // Stop the ATG if needed if (this._stationInfo.AutomaticTransactionGenerator.enable && this._stationInfo.AutomaticTransactionGenerator.stopOnConnectionFailure && this._automaticTransactionGeneration && !this._automaticTransactionGeneration.timeToStop) { - this._automaticTransactionGeneration.stop().catch(() => {}); + this._automaticTransactionGeneration.stop().catch(() => { }); } // Stop heartbeat this._stopHeartbeat(); @@ -519,8 +520,9 @@ export default class ChargingStation { if (this._hasSocketRestarted) { this._startMessageSequence(); if (!Utils.isEmptyArray(this._messageQueue)) { - this._messageQueue.forEach((message) => { + this._messageQueue.forEach((message, index) => { if (this._wsConnection && this._wsConnection.readyState === WebSocket.OPEN) { + this._messageQueue.splice(index, 1); this._wsConnection.send(message); } }); @@ -530,28 +532,28 @@ export default class ChargingStation { this._hasSocketRestarted = false; } - onError(error): void { - switch (error) { + onError(errorEvent): void { + switch (errorEvent) { case 'ECONNREFUSED': this._hasSocketRestarted = true; - this._reconnect(error); + this._reconnect(errorEvent); break; default: - logger.error(this._logPrefix() + ' Socket error: ' + error); + logger.error(this._logPrefix() + ' Socket error: %j', errorEvent); break; } } - onClose(error): void { - switch (error) { + onClose(closeEvent): void { + switch (closeEvent) { case 1000: // Normal close case 1005: - logger.info(this._logPrefix() + ' Socket normally closed ' + error); + logger.info(this._logPrefix() + ' Socket normally closed: %j', closeEvent); this._autoReconnectRetryCount = 0; break; default: // Abnormal close this._hasSocketRestarted = true; - this._reconnect(error); + this._reconnect(closeEvent); break; } } @@ -560,11 +562,11 @@ export default class ChargingStation { logger.debug(this._logPrefix() + ' Has received a WS ping (rfc6455) from the server'); } - async onMessage(message): Promise { + async onMessage(messageEvent): Promise { let [messageType, messageId, commandName, commandPayload, errorDetails] = [0, '', Constants.ENTITY_CHARGING_STATION, '', '']; try { // Parse the message - [messageType, messageId, commandName, commandPayload, errorDetails] = JSON.parse(message); + [messageType, messageId, commandName, commandPayload, errorDetails] = JSON.parse(messageEvent); // Check the Type of message switch (messageType) { @@ -618,7 +620,7 @@ export default class ChargingStation { } } catch (error) { // Log - logger.error('%s Incoming message %j processing error %s on request content type %s', this._logPrefix(), message, error, this._requests[messageId]); + logger.error('%s Incoming message %j processing error %s on request content type %s', this._logPrefix(), messageEvent, error, this._requests[messageId]); // Send error await this.sendError(messageId, error, commandName); } @@ -631,7 +633,7 @@ export default class ChargingStation { }; await this.sendMessage(Utils.generateUUID(), payload, Constants.OCPP_JSON_CALL_MESSAGE, 'Heartbeat'); } catch (error) { - logger.error(this._logPrefix() + ' Send Heartbeat error: ' + error); + logger.error(this._logPrefix() + ' Send Heartbeat error: %j', error); throw error; } } @@ -640,7 +642,7 @@ export default class ChargingStation { try { await this.sendMessage(Utils.generateUUID(), this._bootNotificationMessage, Constants.OCPP_JSON_CALL_MESSAGE, 'BootNotification'); } catch (error) { - logger.error(this._logPrefix() + ' Send BootNotification error: ' + error); + logger.error(this._logPrefix() + ' Send BootNotification error: %j', error); throw error; } } @@ -655,7 +657,7 @@ export default class ChargingStation { }; await this.sendMessage(Utils.generateUUID(), payload, Constants.OCPP_JSON_CALL_MESSAGE, 'StatusNotification'); } catch (error) { - logger.error(this._logPrefix() + ' Send StatusNotification error: ' + error); + logger.error(this._logPrefix() + ' Send StatusNotification error: %j', error); throw error; } } @@ -670,7 +672,7 @@ export default class ChargingStation { }; return await this.sendMessage(Utils.generateUUID(), payload, Constants.OCPP_JSON_CALL_MESSAGE, 'StartTransaction') as StartTransactionResponse; } catch (error) { - logger.error(this._logPrefix() + ' Send StartTransaction error: ' + error); + logger.error(this._logPrefix() + ' Send StartTransaction error: %j', error); throw error; } } @@ -687,7 +689,7 @@ export default class ChargingStation { }; return await this.sendMessage(Utils.generateUUID(), payload, Constants.OCPP_JSON_CALL_MESSAGE, 'StopTransaction') as StartTransactionResponse; } catch (error) { - logger.error(this._logPrefix() + ' Send StopTransaction error: ' + error); + logger.error(this._logPrefix() + ' Send StopTransaction error: %j', error); throw error; } } @@ -801,7 +803,7 @@ export default class ChargingStation { ...!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}`] }, + ...!Utils.isUndefined(meterValuesTemplate[index].value) ? { value: meterValuesTemplate[index].value } : { value: powerMeasurandValues[`L${phase}`] as string }, phase: phaseValue as MeterValuePhase, }); } @@ -862,7 +864,7 @@ export default class ChargingStation { ...!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] }, + ...!Utils.isUndefined(meterValuesTemplate[index].value) ? { value: meterValuesTemplate[index].value } : { value: currentMeasurandValues[phaseValue] as string }, phase: phaseValue as MeterValuePhase, }); } @@ -892,7 +894,8 @@ export default class ChargingStation { ...!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() }, + ...!Utils.isUndefined(meterValuesTemplate[index].value) ? { value: meterValuesTemplate[index].value } : + { value: connector.lastEnergyActiveImportRegisterValue.toString() }, }); const sampledValuesIndex = sampledValues.sampledValue.length - 1; const maxConsumption = Math.round(self._stationInfo.maxPower * 3600 / (self._stationInfo.powerDivider * interval)); @@ -912,7 +915,7 @@ export default class ChargingStation { }; await self.sendMessage(Utils.generateUUID(), payload, Constants.OCPP_JSON_CALL_MESSAGE, 'MeterValues'); } catch (error) { - logger.error(self._logPrefix() + ' Send MeterValues error: ' + error); + logger.error(self._logPrefix() + ' Send MeterValues error: %j', error); throw error; } } @@ -997,7 +1000,7 @@ export default class ChargingStation { if (self.getEnableStatistics()) { self._statistics.addMessage(commandName, messageType); } - logger.debug(`${self._logPrefix()} Error %j occurred when calling command %s with parameters %j`, error, commandName, commandParams); + logger.debug(`${self._logPrefix()} Error: %j occurred when calling command %s with parameters: %j`, error, commandName, commandParams); // Build Exception // eslint-disable-next-line no-empty-function self._requests[messageId] = [() => { }, () => { }, {}]; // Properly format the request @@ -1045,56 +1048,57 @@ export default class ChargingStation { } handleResponseStartTransaction(payload: StartTransactionResponse, requestPayload): void { - if (this.getConnector(requestPayload.connectorId).transactionStarted) { - logger.debug(this._logPrefix() + ' Try to start a transaction on an already used connector ' + requestPayload.connectorId + ': %s', this.getConnector(requestPayload.connectorId)); + const connectorId = Utils.convertToInt(requestPayload.connectorId); + if (this.getConnector(connectorId).transactionStarted) { + logger.debug(this._logPrefix() + ' Trying to start a transaction on an already used connector ' + connectorId.toString() + ': %j', this.getConnector(connectorId)); return; } let transactionConnectorId: number; for (const connector in this._connectors) { - if (Utils.convertToInt(connector) === Utils.convertToInt(requestPayload.connectorId)) { + if (Utils.convertToInt(connector) === connectorId) { transactionConnectorId = Utils.convertToInt(connector); break; } } if (!transactionConnectorId) { - logger.error(this._logPrefix() + ' Try to start a transaction on a non existing connector Id ' + requestPayload.connectorId); + logger.error(this._logPrefix() + ' Trying to start a transaction on a non existing connector Id ' + connectorId.toString()); return; } if (payload.idTagInfo?.status === AuthorizationStatus.ACCEPTED) { - this.getConnector(requestPayload.connectorId).transactionStarted = true; - this.getConnector(requestPayload.connectorId).transactionId = payload.transactionId; - this.getConnector(requestPayload.connectorId).idTag = requestPayload.idTag; - this.getConnector(requestPayload.connectorId).lastEnergyActiveImportRegisterValue = 0; - this.sendStatusNotification(requestPayload.connectorId, ChargePointStatus.CHARGING); - logger.info(this._logPrefix() + ' Transaction ' + payload.transactionId + ' STARTED on ' + this._stationInfo.name + '#' + requestPayload.connectorId + ' for idTag ' + requestPayload.idTag); + this.getConnector(connectorId).transactionStarted = true; + this.getConnector(connectorId).transactionId = payload.transactionId; + this.getConnector(connectorId).idTag = requestPayload.idTag; + this.getConnector(connectorId).lastEnergyActiveImportRegisterValue = 0; + this.sendStatusNotification(connectorId, ChargePointStatus.CHARGING).catch(() => { }); + logger.info(this._logPrefix() + ' Transaction ' + payload.transactionId.toString() + ' STARTED on ' + this._stationInfo.name + '#' + connectorId.toString() + ' for idTag ' + requestPayload.idTag); if (this._stationInfo.powerSharedByConnectors) { this._stationInfo.powerDivider++; } const configuredMeterValueSampleInterval = this._getConfigurationKey('MeterValueSampleInterval'); - this._startMeterValues(requestPayload.connectorId, + this._startMeterValues(connectorId, configuredMeterValueSampleInterval ? Utils.convertToInt(configuredMeterValueSampleInterval.value) * 1000 : 60000); } else { - logger.error(this._logPrefix() + ' Starting transaction id ' + payload.transactionId + ' REJECTED with status ' + payload.idTagInfo?.status + ', idTag ' + requestPayload.idTag); - this._resetTransactionOnConnector(requestPayload.connectorId); - this.sendStatusNotification(requestPayload.connectorId, ChargePointStatus.AVAILABLE); + logger.error(this._logPrefix() + ' Starting transaction id ' + payload.transactionId.toString() + ' REJECTED with status ' + payload.idTagInfo?.status + ', idTag ' + requestPayload.idTag); + this._resetTransactionOnConnector(connectorId); + this.sendStatusNotification(connectorId, ChargePointStatus.AVAILABLE).catch(() => { }); } } handleResponseStopTransaction(payload: StopTransactionResponse, requestPayload): void { let transactionConnectorId: number; for (const connector in this._connectors) { - if (this.getConnector(Utils.convertToInt(connector)).transactionId === requestPayload.transactionId) { + if (this.getConnector(Utils.convertToInt(connector)).transactionId === Utils.convertToInt(requestPayload.transactionId)) { transactionConnectorId = Utils.convertToInt(connector); break; } } if (!transactionConnectorId) { - logger.error(this._logPrefix() + ' Try to stop a non existing transaction ' + requestPayload.transactionId); + logger.error(this._logPrefix() + ' Trying to stop a non existing transaction ' + requestPayload.transactionId); return; } if (payload.idTagInfo?.status === AuthorizationStatus.ACCEPTED) { - this.sendStatusNotification(transactionConnectorId, ChargePointStatus.AVAILABLE); + this.sendStatusNotification(transactionConnectorId, ChargePointStatus.AVAILABLE).catch(() => { }); if (this._stationInfo.powerSharedByConnectors) { this._stationInfo.powerDivider--; } @@ -1126,7 +1130,7 @@ export default class ChargingStation { response = await this['handleRequest' + commandName](commandPayload); } catch (error) { // Log - logger.error(this._logPrefix() + ' Handle request error: ' + error); + logger.error(this._logPrefix() + ' Handle request error: %j', error); // Send back response to inform backend await this.sendError(messageId, error, commandName); throw error; @@ -1154,7 +1158,7 @@ export default class ChargingStation { async handleRequestUnlockConnector(commandPayload): Promise { const connectorId = Utils.convertToInt(commandPayload.connectorId); if (connectorId === 0) { - logger.error(this._logPrefix() + ' Try to unlock connector ' + connectorId.toString()); + logger.error(this._logPrefix() + ' Trying to unlock connector ' + connectorId.toString()); return Constants.OCPP_RESPONSE_UNLOCK_NOT_SUPPORTED; } if (this.getConnector(connectorId).transactionStarted) { @@ -1295,7 +1299,7 @@ export default class ChargingStation { return Constants.OCPP_RESPONSE_ACCEPTED; } } - logger.info(this._logPrefix() + ' Try to stop remotely a non existing transaction ' + commandPayload.transactionId); + logger.info(this._logPrefix() + ' Trying to remote stop a non existing transaction ' + transactionId.toString()); return Constants.OCPP_RESPONSE_REJECTED; } }