From 9f2e313013116428f5bce2be59e2f5c07502c026 Mon Sep 17 00:00:00 2001 From: =?utf8?q?J=C3=A9r=C3=B4me=20Benoit?= Date: Mon, 21 Feb 2022 20:52:29 +0100 Subject: [PATCH] Ensure 1:1 mapping between charging station instance and its OCPP services MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Signed-off-by: Jérôme Benoit --- .../AutomaticTransactionGenerator.ts | 38 +++--- src/charging-station/ChargingStation.ts | 119 +++++++++--------- src/charging-station/UIWebSocketServer.ts | 6 +- .../ocpp/1.6/OCPP16IncomingRequestService.ts | 49 ++++---- .../ocpp/1.6/OCPP16RequestService.ts | 27 ++-- .../ocpp/1.6/OCPP16ResponseService.ts | 49 ++++---- .../ocpp/1.6/OCPP16ServiceUtils.ts | 6 +- .../ocpp/OCPPIncomingRequestService.ts | 16 ++- .../ocpp/OCPPRequestService.ts | 26 ++-- .../ocpp/OCPPResponseService.ts | 14 ++- .../AbstractUIService.ts | 4 +- .../ui-websocket-services/UIServiceUtils.ts | 4 +- src/performance/PerformanceStatistics.ts | 30 +++-- src/performance/storage/Storage.ts | 4 +- src/types/Statistics.ts | 1 + src/utils/FileUtils.ts | 10 +- src/utils/Logger.ts | 20 ++- 17 files changed, 231 insertions(+), 192 deletions(-) diff --git a/src/charging-station/AutomaticTransactionGenerator.ts b/src/charging-station/AutomaticTransactionGenerator.ts index 1d753a01..dddfc045 100644 --- a/src/charging-station/AutomaticTransactionGenerator.ts +++ b/src/charging-station/AutomaticTransactionGenerator.ts @@ -7,7 +7,7 @@ import Constants from '../utils/Constants'; import PerformanceStatistics from '../performance/PerformanceStatistics'; import { Status } from '../types/AutomaticTransactionGenerator'; import Utils from '../utils/Utils'; -import getLogger from '../utils/Logger'; +import logger from '../utils/Logger'; export default class AutomaticTransactionGenerator { public started: boolean; @@ -23,7 +23,7 @@ export default class AutomaticTransactionGenerator { public start(): void { if (this.started) { - getLogger().error(`${this.logPrefix()} trying to start while already started`); + logger.error(`${this.logPrefix()} trying to start while already started`); return; } this.startConnectors(); @@ -32,7 +32,7 @@ export default class AutomaticTransactionGenerator { public stop(): void { if (!this.started) { - getLogger().error(`${this.logPrefix()} trying to stop while not started`); + logger.error(`${this.logPrefix()} trying to stop while not started`); return; } this.stopConnectors(); @@ -60,36 +60,36 @@ export default class AutomaticTransactionGenerator { private async internalStartConnector(connectorId: number): Promise { this.initStartConnectorStatus(connectorId); - getLogger().info(this.logPrefix(connectorId) + ' started on connector and will run for ' + Utils.formatDurationMilliSeconds(this.connectorsStatus.get(connectorId).stopDate.getTime() - this.connectorsStatus.get(connectorId).startDate.getTime())); + logger.info(this.logPrefix(connectorId) + ' started on connector and will run for ' + Utils.formatDurationMilliSeconds(this.connectorsStatus.get(connectorId).stopDate.getTime() - this.connectorsStatus.get(connectorId).startDate.getTime())); while (this.connectorsStatus.get(connectorId).start) { if ((new Date()) > this.connectorsStatus.get(connectorId).stopDate) { this.stopConnector(connectorId); break; } if (!this.chargingStation.isInAcceptedState()) { - getLogger().error(this.logPrefix(connectorId) + ' entered in transaction loop while the charging station is not in accepted state'); + logger.error(this.logPrefix(connectorId) + ' entered in transaction loop while the charging station is not in accepted state'); this.stopConnector(connectorId); break; } if (!this.chargingStation.isChargingStationAvailable()) { - getLogger().info(this.logPrefix(connectorId) + ' entered in transaction loop while the charging station is unavailable'); + logger.info(this.logPrefix(connectorId) + ' entered in transaction loop while the charging station is unavailable'); this.stopConnector(connectorId); break; } if (!this.chargingStation.isConnectorAvailable(connectorId)) { - getLogger().info(`${this.logPrefix(connectorId)} entered in transaction loop while the connector ${connectorId} is unavailable`); + logger.info(`${this.logPrefix(connectorId)} entered in transaction loop while the connector ${connectorId} is unavailable`); this.stopConnector(connectorId); break; } if (!this.chargingStation?.ocppRequestService) { - getLogger().info(`${this.logPrefix(connectorId)} transaction loop waiting for charging station service to be initialized`); + logger.info(`${this.logPrefix(connectorId)} transaction loop waiting for charging station service to be initialized`); do { await Utils.sleep(Constants.CHARGING_STATION_ATG_INITIALIZATION_TIME); } while (!this.chargingStation?.ocppRequestService); } const wait = Utils.getRandomInteger(this.chargingStation.stationInfo.AutomaticTransactionGenerator.maxDelayBetweenTwoTransactions, this.chargingStation.stationInfo.AutomaticTransactionGenerator.minDelayBetweenTwoTransactions) * 1000; - getLogger().info(this.logPrefix(connectorId) + ' waiting for ' + Utils.formatDurationMilliSeconds(wait)); + logger.info(this.logPrefix(connectorId) + ' waiting for ' + Utils.formatDurationMilliSeconds(wait)); await Utils.sleep(wait); const start = Utils.secureRandom(); if (start < this.chargingStation.stationInfo.AutomaticTransactionGenerator.probabilityOfStart) { @@ -98,30 +98,30 @@ export default class AutomaticTransactionGenerator { const startResponse = await this.startTransaction(connectorId); this.connectorsStatus.get(connectorId).startTransactionRequests++; if (startResponse?.idTagInfo?.status !== AuthorizationStatus.ACCEPTED) { - getLogger().warn(this.logPrefix(connectorId) + ' start transaction rejected'); + logger.warn(this.logPrefix(connectorId) + ' start transaction rejected'); this.connectorsStatus.get(connectorId).rejectedStartTransactionRequests++; } else { // Wait until end of transaction const waitTrxEnd = Utils.getRandomInteger(this.chargingStation.stationInfo.AutomaticTransactionGenerator.maxDuration, this.chargingStation.stationInfo.AutomaticTransactionGenerator.minDuration) * 1000; - getLogger().info(this.logPrefix(connectorId) + ' transaction ' + this.chargingStation.getConnectorStatus(connectorId).transactionId.toString() + ' started and will stop in ' + Utils.formatDurationMilliSeconds(waitTrxEnd)); + logger.info(this.logPrefix(connectorId) + ' transaction ' + this.chargingStation.getConnectorStatus(connectorId).transactionId.toString() + ' started and will stop in ' + Utils.formatDurationMilliSeconds(waitTrxEnd)); this.connectorsStatus.get(connectorId).acceptedStartTransactionRequests++; await Utils.sleep(waitTrxEnd); // Stop transaction - getLogger().info(this.logPrefix(connectorId) + ' stop transaction ' + this.chargingStation.getConnectorStatus(connectorId).transactionId.toString()); + logger.info(this.logPrefix(connectorId) + ' stop transaction ' + this.chargingStation.getConnectorStatus(connectorId).transactionId.toString()); await this.stopTransaction(connectorId); } } else { this.connectorsStatus.get(connectorId).skippedConsecutiveTransactions++; this.connectorsStatus.get(connectorId).skippedTransactions++; - getLogger().info(this.logPrefix(connectorId) + ' skipped consecutively ' + this.connectorsStatus.get(connectorId).skippedConsecutiveTransactions.toString() + '/' + this.connectorsStatus.get(connectorId).skippedTransactions.toString() + ' transaction(s)'); + logger.info(this.logPrefix(connectorId) + ' skipped consecutively ' + this.connectorsStatus.get(connectorId).skippedConsecutiveTransactions.toString() + '/' + this.connectorsStatus.get(connectorId).skippedTransactions.toString() + ' transaction(s)'); } this.connectorsStatus.get(connectorId).lastRunDate = new Date(); } await this.stopTransaction(connectorId); this.connectorsStatus.get(connectorId).stoppedDate = new Date(); - getLogger().info(this.logPrefix(connectorId) + ' stopped on connector and lasted for ' + Utils.formatDurationMilliSeconds(this.connectorsStatus.get(connectorId).stoppedDate.getTime() - this.connectorsStatus.get(connectorId).startDate.getTime())); - getLogger().debug(`${this.logPrefix(connectorId)} connector status %j`, this.connectorsStatus.get(connectorId)); + logger.info(this.logPrefix(connectorId) + ' stopped on connector and lasted for ' + Utils.formatDurationMilliSeconds(this.connectorsStatus.get(connectorId).stoppedDate.getTime() - this.connectorsStatus.get(connectorId).startDate.getTime())); + logger.debug(`${this.logPrefix(connectorId)} connector status %j`, this.connectorsStatus.get(connectorId)); } private startConnector(connectorId: number): void { @@ -167,7 +167,7 @@ export default class AutomaticTransactionGenerator { this.connectorsStatus.get(connectorId).authorizeRequests++; if (authorizeResponse?.idTagInfo?.status === AuthorizationStatus.ACCEPTED) { this.connectorsStatus.get(connectorId).acceptedAuthorizeRequests++; - getLogger().info(this.logPrefix(connectorId) + ' start transaction for idTag ' + idTag); + logger.info(this.logPrefix(connectorId) + ' start transaction for idTag ' + idTag); // Start transaction startResponse = await this.chargingStation.ocppRequestService.sendStartTransaction(connectorId, idTag); PerformanceStatistics.endMeasure(measureId, beginId); @@ -177,13 +177,13 @@ export default class AutomaticTransactionGenerator { PerformanceStatistics.endMeasure(measureId, beginId); return authorizeResponse; } - getLogger().info(this.logPrefix(connectorId) + ' start transaction for idTag ' + idTag); + logger.info(this.logPrefix(connectorId) + ' start transaction for idTag ' + idTag); // Start transaction startResponse = await this.chargingStation.ocppRequestService.sendStartTransaction(connectorId, idTag); PerformanceStatistics.endMeasure(measureId, beginId); return startResponse; } - getLogger().info(this.logPrefix(connectorId) + ' start transaction without an idTag'); + logger.info(this.logPrefix(connectorId) + ' start transaction without an idTag'); startResponse = await this.chargingStation.ocppRequestService.sendStartTransaction(connectorId); PerformanceStatistics.endMeasure(measureId, beginId); return startResponse; @@ -202,7 +202,7 @@ export default class AutomaticTransactionGenerator { reason); this.connectorsStatus.get(connectorId).stopTransactionRequests++; } else { - getLogger().warn(`${this.logPrefix(connectorId)} trying to stop a not started transaction${transactionId ? ' ' + transactionId.toString() : ''}`); + logger.warn(`${this.logPrefix(connectorId)} trying to stop a not started transaction${transactionId ? ' ' + transactionId.toString() : ''}`); } PerformanceStatistics.endMeasure(measureId, beginId); return stopResponse; diff --git a/src/charging-station/ChargingStation.ts b/src/charging-station/ChargingStation.ts index 1e10118b..028de591 100644 --- a/src/charging-station/ChargingStation.ts +++ b/src/charging-station/ChargingStation.ts @@ -37,11 +37,12 @@ import { URL } from 'url'; import Utils from '../utils/Utils'; import crypto from 'crypto'; import fs from 'fs'; -import getLogger from '../utils/Logger'; +import logger from '../utils/Logger'; import { parentPort } from 'worker_threads'; import path from 'path'; export default class ChargingStation { + public readonly id: string; public readonly stationTemplateFile: string; public authorizedTags: string[]; public stationInfo!: ChargingStationInfo; @@ -52,7 +53,6 @@ export default class ChargingStation { public performanceStatistics!: PerformanceStatistics; public heartbeatSetInterval!: NodeJS.Timeout; public ocppRequestService!: OCPPRequestService; - private readonly id: string; private readonly index: number; private bootNotificationRequest!: BootNotificationRequest; private bootNotificationResponse!: BootNotificationResponse | null; @@ -70,16 +70,13 @@ export default class ChargingStation { this.id = Utils.generateUUID(); this.index = index; this.stationTemplateFile = stationTemplateFile; - this.connectors = new Map(); - this.initialize(); - this.stopped = false; this.wsConnectionRestarted = false; this.autoReconnectRetryCount = 0; - + this.connectors = new Map(); this.requests = new Map(); this.messageBuffer = new Set(); - + this.initialize(); this.authorizedTags = this.getAuthorizedTags(); } @@ -154,7 +151,7 @@ export default class ChargingStation { } public isConnectorAvailable(id: number): boolean { - return this.getConnectorStatus(id).availability === AvailabilityType.OPERATIVE; + return id > 0 && this.getConnectorStatus(id).availability === AvailabilityType.OPERATIVE; } public getNumberOfConnectors(): number { @@ -184,7 +181,7 @@ export default class ChargingStation { defaultVoltageOut = Voltage.VOLTAGE_400; break; default: - getLogger().error(errMsg); + logger.error(errMsg); throw new Error(errMsg); } return !Utils.isUndefined(this.stationInfo.voltageOut) ? this.stationInfo.voltageOut : defaultVoltageOut; @@ -264,17 +261,17 @@ export default class ChargingStation { public getSampledValueTemplate(connectorId: number, measurand: MeterValueMeasurand = MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER, phase?: MeterValuePhase): SampledValueTemplate | undefined { if (!Constants.SUPPORTED_MEASURANDS.includes(measurand)) { - getLogger().warn(`${this.logPrefix()} Trying to get unsupported MeterValues measurand '${measurand}' ${phase ? `on phase ${phase} ` : ''}in template on connectorId ${connectorId}`); + logger.warn(`${this.logPrefix()} Trying to get unsupported MeterValues measurand '${measurand}' ${phase ? `on phase ${phase} ` : ''}in template on connectorId ${connectorId}`); return; } if (measurand !== MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER && !this.getConfigurationKey(StandardParametersKey.MeterValuesSampledData).value.includes(measurand)) { - getLogger().debug(`${this.logPrefix()} Trying to get MeterValues measurand '${measurand}' ${phase ? `on phase ${phase} ` : ''}in template on connectorId ${connectorId} not found in '${StandardParametersKey.MeterValuesSampledData}' OCPP parameter`); + logger.debug(`${this.logPrefix()} Trying to get MeterValues measurand '${measurand}' ${phase ? `on phase ${phase} ` : ''}in template on connectorId ${connectorId} not found in '${StandardParametersKey.MeterValuesSampledData}' OCPP parameter`); return; } const sampledValueTemplates: SampledValueTemplate[] = this.getConnectorStatus(connectorId).MeterValues; for (let index = 0; !Utils.isEmptyArray(sampledValueTemplates) && index < sampledValueTemplates.length; index++) { if (!Constants.SUPPORTED_MEASURANDS.includes(sampledValueTemplates[index]?.measurand ?? MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER)) { - getLogger().warn(`${this.logPrefix()} Unsupported MeterValues measurand '${measurand}' ${phase ? `on phase ${phase} ` : ''}in template on connectorId ${connectorId}`); + logger.warn(`${this.logPrefix()} Unsupported MeterValues measurand '${measurand}' ${phase ? `on phase ${phase} ` : ''}in template on connectorId ${connectorId}`); } else if (phase && sampledValueTemplates[index]?.phase === phase && sampledValueTemplates[index]?.measurand === measurand && this.getConfigurationKey(StandardParametersKey.MeterValuesSampledData).value.includes(measurand)) { return sampledValueTemplates[index]; @@ -288,10 +285,10 @@ export default class ChargingStation { } if (measurand === MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER) { const errorMsg = `${this.logPrefix()} Missing MeterValues for default measurand '${measurand}' in template on connectorId ${connectorId}`; - getLogger().error(errorMsg); + logger.error(errorMsg); throw new Error(errorMsg); } - getLogger().debug(`${this.logPrefix()} No MeterValues for measurand '${measurand}' ${phase ? `on phase ${phase} ` : ''}in template on connectorId ${connectorId}`); + logger.debug(`${this.logPrefix()} No MeterValues for measurand '${measurand}' ${phase ? `on phase ${phase} ` : ''}in template on connectorId ${connectorId}`); } public getAutomaticTransactionGeneratorRequireAuthorize(): boolean { @@ -304,11 +301,11 @@ export default class ChargingStation { this.heartbeatSetInterval = setInterval(async (): Promise => { await this.ocppRequestService.sendHeartbeat(); }, this.getHeartbeatInterval()); - getLogger().info(this.logPrefix() + ' Heartbeat started every ' + Utils.formatDurationMilliSeconds(this.getHeartbeatInterval())); + logger.info(this.logPrefix() + ' Heartbeat started every ' + Utils.formatDurationMilliSeconds(this.getHeartbeatInterval())); } else if (this.heartbeatSetInterval) { - getLogger().info(this.logPrefix() + ' Heartbeat already started every ' + Utils.formatDurationMilliSeconds(this.getHeartbeatInterval())); + logger.info(this.logPrefix() + ' Heartbeat already started every ' + Utils.formatDurationMilliSeconds(this.getHeartbeatInterval())); } else { - getLogger().error(`${this.logPrefix()} Heartbeat interval set to ${this.getHeartbeatInterval() ? Utils.formatDurationMilliSeconds(this.getHeartbeatInterval()) : this.getHeartbeatInterval()}, not starting the heartbeat`); + logger.error(`${this.logPrefix()} Heartbeat interval set to ${this.getHeartbeatInterval() ? Utils.formatDurationMilliSeconds(this.getHeartbeatInterval()) : this.getHeartbeatInterval()}, not starting the heartbeat`); } } @@ -321,18 +318,18 @@ export default class ChargingStation { public startMeterValues(connectorId: number, interval: number): void { if (connectorId === 0) { - getLogger().error(`${this.logPrefix()} Trying to start MeterValues on connector Id ${connectorId.toString()}`); + logger.error(`${this.logPrefix()} Trying to start MeterValues on connector Id ${connectorId.toString()}`); return; } if (!this.getConnectorStatus(connectorId)) { - getLogger().error(`${this.logPrefix()} Trying to start MeterValues on non existing connector Id ${connectorId.toString()}`); + logger.error(`${this.logPrefix()} Trying to start MeterValues on non existing connector Id ${connectorId.toString()}`); return; } if (!this.getConnectorStatus(connectorId)?.transactionStarted) { - getLogger().error(`${this.logPrefix()} Trying to start MeterValues on connector Id ${connectorId} with no transaction started`); + logger.error(`${this.logPrefix()} Trying to start MeterValues on connector Id ${connectorId} with no transaction started`); return; } else if (this.getConnectorStatus(connectorId)?.transactionStarted && !this.getConnectorStatus(connectorId)?.transactionId) { - getLogger().error(`${this.logPrefix()} Trying to start MeterValues on connector Id ${connectorId} with no transaction id`); + logger.error(`${this.logPrefix()} Trying to start MeterValues on connector Id ${connectorId} with no transaction id`); return; } if (interval > 0) { @@ -341,7 +338,7 @@ export default class ChargingStation { await this.ocppRequestService.sendMeterValues(connectorId, this.getConnectorStatus(connectorId).transactionId, interval); }, interval); } else { - getLogger().error(`${this.logPrefix()} Charging station ${StandardParametersKey.MeterValueSampleInterval} configuration set to ${interval ? Utils.formatDurationMilliSeconds(interval) : interval}, not sending MeterValues`); + logger.error(`${this.logPrefix()} Charging station ${StandardParametersKey.MeterValueSampleInterval} configuration set to ${interval ? Utils.formatDurationMilliSeconds(interval) : interval}, not sending MeterValues`); } } @@ -412,7 +409,7 @@ export default class ChargingStation { reboot, }); } else { - getLogger().error(`${this.logPrefix()} Trying to add an already existing configuration key: %j`, keyFound); + logger.error(`${this.logPrefix()} Trying to add an already existing configuration key: %j`, keyFound); } } @@ -422,7 +419,7 @@ export default class ChargingStation { const keyIndex = this.configuration.configurationKey.indexOf(keyFound); this.configuration.configurationKey[keyIndex].value = value; } else { - getLogger().error(`${this.logPrefix()} Trying to set a value on a non existing configuration key: %j`, { key, value }); + logger.error(`${this.logPrefix()} Trying to set a value on a non existing configuration key: %j`, { key, value }); } } @@ -520,7 +517,7 @@ export default class ChargingStation { private handleUnsupportedVersion(version: OCPPVersion) { const errMsg = `${this.logPrefix()} Unsupported protocol version '${version}' configured in template file ${this.stationTemplateFile}`; - getLogger().error(errMsg); + logger.error(errMsg); throw new Error(errMsg); } @@ -537,18 +534,18 @@ export default class ChargingStation { // Build connectors if needed const maxConnectors = this.getMaxNumberOfConnectors(); if (maxConnectors <= 0) { - getLogger().warn(`${this.logPrefix()} Charging station template ${this.stationTemplateFile} with ${maxConnectors} connectors`); + logger.warn(`${this.logPrefix()} Charging station template ${this.stationTemplateFile} with ${maxConnectors} connectors`); } const templateMaxConnectors = this.getTemplateMaxNumberOfConnectors(); if (templateMaxConnectors <= 0) { - getLogger().warn(`${this.logPrefix()} Charging station template ${this.stationTemplateFile} with no connector configuration`); + logger.warn(`${this.logPrefix()} Charging station template ${this.stationTemplateFile} with no connector configuration`); } if (!this.stationInfo.Connectors[0]) { - getLogger().warn(`${this.logPrefix()} Charging station template ${this.stationTemplateFile} with no connector Id 0 configuration`); + logger.warn(`${this.logPrefix()} Charging station template ${this.stationTemplateFile} with no connector Id 0 configuration`); } // Sanity check if (maxConnectors > (this.stationInfo.Connectors[0] ? templateMaxConnectors - 1 : templateMaxConnectors) && !this.stationInfo.randomConnectors) { - getLogger().warn(`${this.logPrefix()} Number of connectors exceeds the number of connector configurations in template ${this.stationTemplateFile}, forcing random connector configurations affectation`); + logger.warn(`${this.logPrefix()} Number of connectors exceeds the number of connector configurations in template ${this.stationTemplateFile}, forcing random connector configurations affectation`); this.stationInfo.randomConnectors = true; } const connectorsConfigHash = crypto.createHash('sha256').update(JSON.stringify(this.stationInfo.Connectors) + maxConnectors.toString()).digest('hex'); @@ -591,8 +588,8 @@ export default class ChargingStation { this.wsConfiguredConnectionUrl = new URL(this.getConfiguredSupervisionUrl().href + '/' + this.stationInfo.chargingStationId); switch (this.getOcppVersion()) { case OCPPVersion.VERSION_16: - this.ocppIncomingRequestService = new OCPP16IncomingRequestService(this); - this.ocppRequestService = new OCPP16RequestService(this, new OCPP16ResponseService(this)); + this.ocppIncomingRequestService = OCPP16IncomingRequestService.getInstance(this); + this.ocppRequestService = OCPP16RequestService.getInstance(this, OCPP16ResponseService.getInstance(this)); break; default: this.handleUnsupportedVersion(this.getOcppVersion()); @@ -609,7 +606,7 @@ export default class ChargingStation { } this.stationInfo.powerDivider = this.getPowerDivider(); if (this.getEnableStatistics()) { - this.performanceStatistics = new PerformanceStatistics(this.stationInfo.chargingStationId, this.wsConnectionUrl); + this.performanceStatistics = PerformanceStatistics.getInstance(this.id, this.stationInfo.chargingStationId, this.wsConnectionUrl); } } @@ -654,7 +651,7 @@ export default class ChargingStation { } private async onOpen(): Promise { - getLogger().info(`${this.logPrefix()} Connected to OCPP server through ${this.wsConnectionUrl.toString()}`); + logger.info(`${this.logPrefix()} Connected to OCPP server through ${this.wsConnectionUrl.toString()}`); if (!this.isInAcceptedState()) { // Send BootNotification let registrationRetryCount = 0; @@ -674,7 +671,7 @@ export default class ChargingStation { this.flushMessageBuffer(); } } else { - getLogger().error(`${this.logPrefix()} Registration failure: max retries reached (${this.getRegistrationMaxRetries()}) or retry disabled (${this.getRegistrationMaxRetries()})`); + logger.error(`${this.logPrefix()} Registration failure: max retries reached (${this.getRegistrationMaxRetries()}) or retry disabled (${this.getRegistrationMaxRetries()})`); } this.autoReconnectRetryCount = 0; this.wsConnectionRestarted = false; @@ -685,12 +682,12 @@ export default class ChargingStation { // Normal close case WebSocketCloseEventStatusCode.CLOSE_NORMAL: case WebSocketCloseEventStatusCode.CLOSE_NO_STATUS: - getLogger().info(`${this.logPrefix()} WebSocket normally closed with status '${Utils.getWebSocketCloseEventStatusString(code)}' and reason '${reason}'`); + logger.info(`${this.logPrefix()} WebSocket normally closed with status '${Utils.getWebSocketCloseEventStatusString(code)}' and reason '${reason}'`); this.autoReconnectRetryCount = 0; break; // Abnormal close default: - getLogger().error(`${this.logPrefix()} WebSocket abnormally closed with status '${Utils.getWebSocketCloseEventStatusString(code)}' and reason '${reason}'`); + logger.error(`${this.logPrefix()} WebSocket abnormally closed with status '${Utils.getWebSocketCloseEventStatusString(code)}' and reason '${reason}'`); await this.reconnect(code); break; } @@ -754,27 +751,27 @@ export default class ChargingStation { // Error default: errMsg = `${this.logPrefix()} Wrong message type ${messageType}`; - getLogger().error(errMsg); + logger.error(errMsg); throw new OCPPError(ErrorType.PROTOCOL_ERROR, errMsg); } } catch (error) { // Log - getLogger().error('%s Incoming OCPP message %j matching cached request %j processing error %j', this.logPrefix(), data.toString(), this.requests.get(messageId), error); + logger.error('%s Incoming OCPP message %j matching cached request %j processing error %j', this.logPrefix(), data.toString(), this.requests.get(messageId), error); // Send error messageType === MessageType.CALL_MESSAGE && await this.ocppRequestService.sendError(messageId, error as OCPPError, commandName); } } private onPing(): void { - getLogger().debug(this.logPrefix() + ' Received a WS ping (rfc6455) from the server'); + logger.debug(this.logPrefix() + ' Received a WS ping (rfc6455) from the server'); } private onPong(): void { - getLogger().debug(this.logPrefix() + ' Received a WS pong (rfc6455) from the server'); + logger.debug(this.logPrefix() + ' Received a WS pong (rfc6455) from the server'); } private async onError(error: WSError): Promise { - getLogger().error(this.logPrefix() + ' WebSocket error: %j', error); + logger.error(this.logPrefix() + ' WebSocket error: %j', error); // switch (error.code) { // case 'ECONNREFUSED': // await this.reconnect(error); @@ -803,7 +800,7 @@ export default class ChargingStation { FileUtils.handleFileException(this.logPrefix(), 'Authorization', authorizationFile, error as NodeJS.ErrnoException); } } else { - getLogger().info(this.logPrefix() + ' No authorization file given in template file ' + this.stationTemplateFile); + logger.info(this.logPrefix() + ' No authorization file given in template file ' + this.stationTemplateFile); } return authorizedTags; } @@ -927,8 +924,7 @@ export default class ChargingStation { this.stopHeartbeat(); // Stop the ATG if (this.stationInfo.AutomaticTransactionGenerator.enable && - this.automaticTransactionGenerator && - this.automaticTransactionGenerator.started) { + this.automaticTransactionGenerator?.started) { this.automaticTransactionGenerator.stop(); } else { for (const connectorId of this.connectors.keys()) { @@ -951,11 +947,11 @@ export default class ChargingStation { this.wsConnection.ping((): void => { /* This is intentional */ }); } }, webSocketPingInterval * 1000); - getLogger().info(this.logPrefix() + ' WebSocket ping started every ' + Utils.formatDurationSeconds(webSocketPingInterval)); + logger.info(this.logPrefix() + ' WebSocket ping started every ' + Utils.formatDurationSeconds(webSocketPingInterval)); } else if (this.webSocketPingSetInterval) { - getLogger().info(this.logPrefix() + ' WebSocket ping every ' + Utils.formatDurationSeconds(webSocketPingInterval) + ' already started'); + logger.info(this.logPrefix() + ' WebSocket ping every ' + Utils.formatDurationSeconds(webSocketPingInterval) + ' already started'); } else { - getLogger().error(`${this.logPrefix()} WebSocket ping interval set to ${webSocketPingInterval ? Utils.formatDurationSeconds(webSocketPingInterval) : webSocketPingInterval}, not starting the WebSocket ping`); + logger.error(`${this.logPrefix()} WebSocket ping interval set to ${webSocketPingInterval ? Utils.formatDurationSeconds(webSocketPingInterval) : webSocketPingInterval}, not starting the WebSocket ping`); } } @@ -967,7 +963,7 @@ export default class ChargingStation { private warnDeprecatedTemplateKey(template: ChargingStationTemplate, key: string, chargingStationId: string, logMsgToAppend = ''): void { if (!Utils.isUndefined(template[key])) { - getLogger().warn(`${Utils.logPrefix(` ${chargingStationId} |`)} Deprecated template key '${key}' usage in file '${this.stationTemplateFile}'${logMsgToAppend && '. ' + logMsgToAppend}`); + logger.warn(`${Utils.logPrefix(` ${chargingStationId} |`)} Deprecated template key '${key}' usage in file '${this.stationTemplateFile}'${logMsgToAppend && '. ' + logMsgToAppend}`); } } @@ -995,11 +991,11 @@ export default class ChargingStation { if (this.index <= supervisionUrls.length) { urlIndex = this.index - 1; } else { - getLogger().warn(`${this.logPrefix()} No more configured supervision urls available, using the first one`); + logger.warn(`${this.logPrefix()} No more configured supervision urls available, using the first one`); } break; default: - getLogger().error(`${this.logPrefix()} Unknown supervision url distribution '${Configuration.getSupervisionUrlDistribution()}' from values '${SupervisionUrlDistribution.toString()}', defaulting to ${SupervisionUrlDistribution.ROUND_ROBIN}`); + logger.error(`${this.logPrefix()} Unknown supervision url distribution '${Configuration.getSupervisionUrlDistribution()}' from values '${SupervisionUrlDistribution.toString()}', defaulting to ${SupervisionUrlDistribution.ROUND_ROBIN}`); urlIndex = (this.index - 1) % supervisionUrls.length; break; } @@ -1017,7 +1013,7 @@ export default class ChargingStation { if (HeartBeatInterval) { return Utils.convertToInt(HeartBeatInterval.value) * 1000; } - !this.stationInfo.autoRegister && getLogger().warn(`${this.logPrefix()} Heartbeat interval configuration key not set, using default value: ${Constants.DEFAULT_HEARTBEAT_INTERVAL}`); + !this.stationInfo.autoRegister && logger.warn(`${this.logPrefix()} Heartbeat interval configuration key not set, using default value: ${Constants.DEFAULT_HEARTBEAT_INTERVAL}`); return Constants.DEFAULT_HEARTBEAT_INTERVAL; } @@ -1045,7 +1041,7 @@ export default class ChargingStation { break; } this.wsConnection = new WebSocket(this.wsConnectionUrl, protocol, options); - getLogger().info(this.logPrefix() + ' Open OCPP connection to URL ' + this.wsConnectionUrl.toString()); + logger.info(this.logPrefix() + ' Open OCPP connection to URL ' + this.wsConnectionUrl.toString()); } private stopMeterValues(connectorId: number) { @@ -1061,11 +1057,11 @@ export default class ChargingStation { fs.watch(authorizationFile, (event, filename) => { if (filename && event === 'change') { try { - getLogger().debug(this.logPrefix() + ' Authorization file ' + authorizationFile + ' have changed, reload'); + logger.debug(this.logPrefix() + ' Authorization file ' + authorizationFile + ' have changed, reload'); // Initialize authorizedTags this.authorizedTags = this.getAuthorizedTags(); } catch (error) { - getLogger().error(this.logPrefix() + ' Authorization file monitoring error: %j', error); + logger.error(this.logPrefix() + ' Authorization file monitoring error: %j', error); } } }); @@ -1073,7 +1069,7 @@ export default class ChargingStation { FileUtils.handleFileException(this.logPrefix(), 'Authorization', authorizationFile, error as NodeJS.ErrnoException); } } else { - getLogger().info(this.logPrefix() + ' No authorization file given in template file ' + this.stationTemplateFile + '. Not monitoring changes'); + logger.info(this.logPrefix() + ' No authorization file given in template file ' + this.stationTemplateFile + '. Not monitoring changes'); } } @@ -1082,7 +1078,7 @@ export default class ChargingStation { fs.watch(this.stationTemplateFile, (event, filename): void => { if (filename && event === 'change') { try { - getLogger().debug(this.logPrefix() + ' Template file ' + this.stationTemplateFile + ' have changed, reload'); + logger.debug(this.logPrefix() + ' Template file ' + this.stationTemplateFile + ' have changed, reload'); // Initialize this.initialize(); // Restart the ATG @@ -1098,7 +1094,7 @@ export default class ChargingStation { } // FIXME?: restart heartbeat and WebSocket ping when their interval values have changed } catch (error) { - getLogger().error(this.logPrefix() + ' Charging station template file monitoring error: %j', error); + logger.error(this.logPrefix() + ' Charging station template file monitoring error: %j', error); } } }); @@ -1119,21 +1115,20 @@ export default class ChargingStation { // Stop the ATG if needed if (this.stationInfo.AutomaticTransactionGenerator.enable && this.stationInfo.AutomaticTransactionGenerator.stopOnConnectionFailure && - this.automaticTransactionGenerator && - this.automaticTransactionGenerator.started) { + this.automaticTransactionGenerator?.started) { this.automaticTransactionGenerator.stop(); } if (this.autoReconnectRetryCount < this.getAutoReconnectMaxRetries() || this.getAutoReconnectMaxRetries() === -1) { this.autoReconnectRetryCount++; const reconnectDelay = (this.getReconnectExponentialDelay() ? Utils.exponentialDelay(this.autoReconnectRetryCount) : this.getConnectionTimeout() * 1000); const reconnectTimeout = (reconnectDelay - 100) > 0 && reconnectDelay; - getLogger().error(`${this.logPrefix()} WebSocket: connection retry in ${Utils.roundTo(reconnectDelay, 2)}ms, timeout ${reconnectTimeout}ms`); + logger.error(`${this.logPrefix()} WebSocket: connection retry in ${Utils.roundTo(reconnectDelay, 2)}ms, timeout ${reconnectTimeout}ms`); await Utils.sleep(reconnectDelay); - getLogger().error(this.logPrefix() + ' WebSocket: reconnecting try #' + this.autoReconnectRetryCount.toString()); + logger.error(this.logPrefix() + ' WebSocket: reconnecting try #' + this.autoReconnectRetryCount.toString()); this.openWSConnection({ ...this.stationInfo.wsOptions, handshakeTimeout: reconnectTimeout }, true); this.wsConnectionRestarted = true; } else if (this.getAutoReconnectMaxRetries() !== -1) { - getLogger().error(`${this.logPrefix()} WebSocket reconnect failure: max retries reached (${this.autoReconnectRetryCount}) or retry disabled (${this.getAutoReconnectMaxRetries()})`); + logger.error(`${this.logPrefix()} WebSocket reconnect failure: max retries reached (${this.autoReconnectRetryCount}) or retry disabled (${this.getAutoReconnectMaxRetries()})`); } } diff --git a/src/charging-station/UIWebSocketServer.ts b/src/charging-station/UIWebSocketServer.ts index a8990763..96e9a968 100644 --- a/src/charging-station/UIWebSocketServer.ts +++ b/src/charging-station/UIWebSocketServer.ts @@ -7,7 +7,7 @@ import Configuration from '../utils/Configuration'; import { IncomingMessage } from 'http'; import UIServiceFactory from './ui-websocket-services/UIServiceFactory'; import Utils from '../utils/Utils'; -import getLogger from '../utils/Logger'; +import logger from '../utils/Logger'; export default class UIWebSocketServer extends Server { public readonly chargingStations: Set; @@ -48,11 +48,11 @@ export default class UIWebSocketServer extends Server { throw new BaseError('UI protocol request is not iterable'); } this.uiServices.get(version).handleMessage(command, payload).catch(() => { - getLogger().error(`${this.logPrefix()} Error while handling command %s message: %j`, command, payload); + logger.error(`${this.logPrefix()} Error while handling command %s message: %j`, command, payload); }); }); socket.on('error', (error) => { - getLogger().error(`${this.logPrefix()} Error on WebSocket: %j`, error); + logger.error(`${this.logPrefix()} Error on WebSocket: %j`, error); }); }); } diff --git a/src/charging-station/ocpp/1.6/OCPP16IncomingRequestService.ts b/src/charging-station/ocpp/1.6/OCPP16IncomingRequestService.ts index aa0eff46..d0d7bf09 100644 --- a/src/charging-station/ocpp/1.6/OCPP16IncomingRequestService.ts +++ b/src/charging-station/ocpp/1.6/OCPP16IncomingRequestService.ts @@ -21,14 +21,17 @@ import OCPPIncomingRequestService from '../OCPPIncomingRequestService'; import { URL } from 'url'; import Utils from '../../../utils/Utils'; import fs from 'fs'; -import getLogger from '../../../utils/Logger'; +import logger from '../../../utils/Logger'; import path from 'path'; import tar from 'tar'; export default class OCPP16IncomingRequestService extends OCPPIncomingRequestService { private incomingRequestHandlers: Map; - constructor(chargingStation: ChargingStation) { + public constructor(chargingStation: ChargingStation) { + if (new.target?.name === 'OCPP16IncomingRequestService') { + throw new TypeError('Cannot construct OCPP16IncomingRequestService instances directly'); + } super(chargingStation); this.incomingRequestHandlers = new Map([ [OCPP16IncomingRequestCommand.RESET, this.handleRequestReset.bind(this)], @@ -59,7 +62,7 @@ export default class OCPP16IncomingRequestService extends OCPPIncomingRequestSer result = await this.incomingRequestHandlers.get(commandName)(commandPayload); } catch (error) { // Log - getLogger().error(this.chargingStation.logPrefix() + ' Handle request error: %j', error); + logger.error(this.chargingStation.logPrefix() + ' Handle request error: %j', error); throw error; } } else { @@ -81,7 +84,7 @@ export default class OCPP16IncomingRequestService extends OCPPIncomingRequestSer await Utils.sleep(this.chargingStation.stationInfo.resetTime); this.chargingStation.start(); }); - getLogger().info(`${this.chargingStation.logPrefix()} ${commandPayload.type} reset command received, simulating it. The station will be back online in ${Utils.formatDurationMilliSeconds(this.chargingStation.stationInfo.resetTime)}`); + logger.info(`${this.chargingStation.logPrefix()} ${commandPayload.type} reset command received, simulating it. The station will be back online in ${Utils.formatDurationMilliSeconds(this.chargingStation.stationInfo.resetTime)}`); return Constants.OCPP_RESPONSE_ACCEPTED; } @@ -92,7 +95,7 @@ export default class OCPP16IncomingRequestService extends OCPPIncomingRequestSer private async handleRequestUnlockConnector(commandPayload: UnlockConnectorRequest): Promise { const connectorId = commandPayload.connectorId; if (connectorId === 0) { - getLogger().error(this.chargingStation.logPrefix() + ' Trying to unlock connector ' + connectorId.toString()); + logger.error(this.chargingStation.logPrefix() + ' Trying to unlock connector ' + connectorId.toString()); return Constants.OCPP_RESPONSE_UNLOCK_NOT_SUPPORTED; } if (this.chargingStation.getConnectorStatus(connectorId)?.transactionStarted) { @@ -157,10 +160,10 @@ export default class OCPP16IncomingRequestService extends OCPPIncomingRequestSer private handleRequestChangeConfiguration(commandPayload: ChangeConfigurationRequest): ChangeConfigurationResponse { // JSON request fields type sanity check if (!Utils.isString(commandPayload.key)) { - getLogger().error(`${this.chargingStation.logPrefix()} ${OCPP16RequestCommand.CHANGE_CONFIGURATION} request key field is not a string:`, commandPayload); + logger.error(`${this.chargingStation.logPrefix()} ${OCPP16RequestCommand.CHANGE_CONFIGURATION} request key field is not a string:`, commandPayload); } if (!Utils.isString(commandPayload.value)) { - getLogger().error(`${this.chargingStation.logPrefix()} ${OCPP16RequestCommand.CHANGE_CONFIGURATION} request value field is not a string:`, commandPayload); + logger.error(`${this.chargingStation.logPrefix()} ${OCPP16RequestCommand.CHANGE_CONFIGURATION} request value field is not a string:`, commandPayload); } const keyToChange = this.chargingStation.getConfigurationKey(commandPayload.key, true); if (!keyToChange) { @@ -198,7 +201,7 @@ export default class OCPP16IncomingRequestService extends OCPPIncomingRequestSer private handleRequestSetChargingProfile(commandPayload: SetChargingProfileRequest): SetChargingProfileResponse { if (!this.chargingStation.getConnectorStatus(commandPayload.connectorId)) { - getLogger().error(`${this.chargingStation.logPrefix()} Trying to set charging profile(s) to a non existing connector Id ${commandPayload.connectorId}`); + logger.error(`${this.chargingStation.logPrefix()} Trying to set charging profile(s) to a non existing connector Id ${commandPayload.connectorId}`); return Constants.OCPP_SET_CHARGING_PROFILE_RESPONSE_REJECTED; } if (commandPayload.csChargingProfiles.chargingProfilePurpose === ChargingProfilePurposeType.CHARGE_POINT_MAX_PROFILE && commandPayload.connectorId !== 0) { @@ -208,18 +211,18 @@ export default class OCPP16IncomingRequestService extends OCPPIncomingRequestSer return Constants.OCPP_SET_CHARGING_PROFILE_RESPONSE_REJECTED; } this.chargingStation.setChargingProfile(commandPayload.connectorId, commandPayload.csChargingProfiles); - getLogger().debug(`${this.chargingStation.logPrefix()} Charging profile(s) set, dump their stack: %j`, this.chargingStation.getConnectorStatus(commandPayload.connectorId).chargingProfiles); + logger.debug(`${this.chargingStation.logPrefix()} Charging profile(s) set, dump their stack: %j`, this.chargingStation.getConnectorStatus(commandPayload.connectorId).chargingProfiles); return Constants.OCPP_SET_CHARGING_PROFILE_RESPONSE_ACCEPTED; } private handleRequestClearChargingProfile(commandPayload: ClearChargingProfileRequest): ClearChargingProfileResponse { if (!this.chargingStation.getConnectorStatus(commandPayload.connectorId)) { - getLogger().error(`${this.chargingStation.logPrefix()} Trying to clear a charging profile(s) to a non existing connector Id ${commandPayload.connectorId}`); + logger.error(`${this.chargingStation.logPrefix()} Trying to clear a charging profile(s) to a non existing connector Id ${commandPayload.connectorId}`); return Constants.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_UNKNOWN; } if (commandPayload.connectorId && !Utils.isEmptyArray(this.chargingStation.getConnectorStatus(commandPayload.connectorId).chargingProfiles)) { this.chargingStation.getConnectorStatus(commandPayload.connectorId).chargingProfiles = []; - getLogger().debug(`${this.chargingStation.logPrefix()} Charging profile(s) cleared, dump their stack: %j`, this.chargingStation.getConnectorStatus(commandPayload.connectorId).chargingProfiles); + logger.debug(`${this.chargingStation.logPrefix()} Charging profile(s) cleared, dump their stack: %j`, this.chargingStation.getConnectorStatus(commandPayload.connectorId).chargingProfiles); return Constants.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_ACCEPTED; } if (!commandPayload.connectorId) { @@ -242,7 +245,7 @@ export default class OCPP16IncomingRequestService extends OCPPIncomingRequestSer } if (clearCurrentCP) { this.chargingStation.getConnectorStatus(commandPayload.connectorId).chargingProfiles[index] = {} as OCPP16ChargingProfile; - getLogger().debug(`${this.chargingStation.logPrefix()} Charging profile(s) cleared, dump their stack: %j`, this.chargingStation.getConnectorStatus(commandPayload.connectorId).chargingProfiles); + logger.debug(`${this.chargingStation.logPrefix()} Charging profile(s) cleared, dump their stack: %j`, this.chargingStation.getConnectorStatus(commandPayload.connectorId).chargingProfiles); clearedCP = true; } }); @@ -258,7 +261,7 @@ export default class OCPP16IncomingRequestService extends OCPPIncomingRequestSer private async handleRequestChangeAvailability(commandPayload: ChangeAvailabilityRequest): Promise { const connectorId: number = commandPayload.connectorId; if (!this.chargingStation.getConnectorStatus(connectorId)) { - getLogger().error(`${this.chargingStation.logPrefix()} Trying to change the availability of a non existing connector Id ${connectorId.toString()}`); + logger.error(`${this.chargingStation.logPrefix()} Trying to change the availability of a non existing connector Id ${connectorId.toString()}`); return Constants.OCPP_AVAILABILITY_RESPONSE_REJECTED; } const chargePointStatus: OCPP16ChargePointStatus = commandPayload.type === OCPP16AvailabilityType.OPERATIVE @@ -310,14 +313,14 @@ export default class OCPP16IncomingRequestService extends OCPPIncomingRequestSer authorized = true; } } else { - getLogger().warn(`${this.chargingStation.logPrefix()} The charging station configuration expects authorize at remote start transaction but local authorization or authorize isn't enabled`); + logger.warn(`${this.chargingStation.logPrefix()} The charging station configuration expects authorize at remote start transaction but local authorization or authorize isn't enabled`); } if (authorized) { // Authorization successful, start transaction if (this.setRemoteStartTransactionChargingProfile(transactionConnectorId, commandPayload.chargingProfile)) { this.chargingStation.getConnectorStatus(transactionConnectorId).transactionRemoteStarted = true; if ((await this.chargingStation.ocppRequestService.sendStartTransaction(transactionConnectorId, commandPayload.idTag)).idTagInfo.status === OCPP16AuthorizationStatus.ACCEPTED) { - getLogger().debug(this.chargingStation.logPrefix() + ' Transaction remotely STARTED on ' + this.chargingStation.stationInfo.chargingStationId + '#' + transactionConnectorId.toString() + ' for idTag ' + commandPayload.idTag); + logger.debug(this.chargingStation.logPrefix() + ' Transaction remotely STARTED on ' + this.chargingStation.stationInfo.chargingStationId + '#' + transactionConnectorId.toString() + ' for idTag ' + commandPayload.idTag); return Constants.OCPP_RESPONSE_ACCEPTED; } return this.notifyRemoteStartTransactionRejected(transactionConnectorId, commandPayload.idTag); @@ -330,7 +333,7 @@ export default class OCPP16IncomingRequestService extends OCPPIncomingRequestSer if (this.setRemoteStartTransactionChargingProfile(transactionConnectorId, commandPayload.chargingProfile)) { this.chargingStation.getConnectorStatus(transactionConnectorId).transactionRemoteStarted = true; if ((await this.chargingStation.ocppRequestService.sendStartTransaction(transactionConnectorId, commandPayload.idTag)).idTagInfo.status === OCPP16AuthorizationStatus.ACCEPTED) { - getLogger().debug(this.chargingStation.logPrefix() + ' Transaction remotely STARTED on ' + this.chargingStation.stationInfo.chargingStationId + '#' + transactionConnectorId.toString() + ' for idTag ' + commandPayload.idTag); + logger.debug(this.chargingStation.logPrefix() + ' Transaction remotely STARTED on ' + this.chargingStation.stationInfo.chargingStationId + '#' + transactionConnectorId.toString() + ' for idTag ' + commandPayload.idTag); return Constants.OCPP_RESPONSE_ACCEPTED; } return this.notifyRemoteStartTransactionRejected(transactionConnectorId, commandPayload.idTag); @@ -347,17 +350,17 @@ export default class OCPP16IncomingRequestService extends OCPPIncomingRequestSer await this.chargingStation.ocppRequestService.sendStatusNotification(connectorId, OCPP16ChargePointStatus.AVAILABLE); this.chargingStation.getConnectorStatus(connectorId).status = OCPP16ChargePointStatus.AVAILABLE; } - getLogger().warn(this.chargingStation.logPrefix() + ' Remote starting transaction REJECTED on connector Id ' + connectorId.toString() + ', idTag ' + idTag + ', availability ' + this.chargingStation.getConnectorStatus(connectorId).availability + ', status ' + this.chargingStation.getConnectorStatus(connectorId).status); + logger.warn(this.chargingStation.logPrefix() + ' Remote starting transaction REJECTED on connector Id ' + connectorId.toString() + ', idTag ' + idTag + ', availability ' + this.chargingStation.getConnectorStatus(connectorId).availability + ', status ' + this.chargingStation.getConnectorStatus(connectorId).status); return Constants.OCPP_RESPONSE_REJECTED; } private setRemoteStartTransactionChargingProfile(connectorId: number, cp: OCPP16ChargingProfile): boolean { if (cp && cp.chargingProfilePurpose === ChargingProfilePurposeType.TX_PROFILE) { this.chargingStation.setChargingProfile(connectorId, cp); - getLogger().debug(`${this.chargingStation.logPrefix()} Charging profile(s) set at remote start transaction, dump their stack: %j`, this.chargingStation.getConnectorStatus(connectorId).chargingProfiles); + logger.debug(`${this.chargingStation.logPrefix()} Charging profile(s) set at remote start transaction, dump their stack: %j`, this.chargingStation.getConnectorStatus(connectorId).chargingProfiles); return true; } else if (cp && cp.chargingProfilePurpose !== ChargingProfilePurposeType.TX_PROFILE) { - getLogger().warn(`${this.chargingStation.logPrefix()} Not allowed to set ${cp.chargingProfilePurpose} charging profile(s) at remote start transaction`); + logger.warn(`${this.chargingStation.logPrefix()} Not allowed to set ${cp.chargingProfilePurpose} charging profile(s) at remote start transaction`); return false; } else if (!cp) { return true; @@ -375,12 +378,12 @@ export default class OCPP16IncomingRequestService extends OCPPIncomingRequestSer return Constants.OCPP_RESPONSE_ACCEPTED; } } - getLogger().info(this.chargingStation.logPrefix() + ' Trying to remote stop a non existing transaction ' + transactionId.toString()); + logger.info(this.chargingStation.logPrefix() + ' Trying to remote stop a non existing transaction ' + transactionId.toString()); return Constants.OCPP_RESPONSE_REJECTED; } private async handleRequestGetDiagnostics(commandPayload: GetDiagnosticsRequest): Promise { - getLogger().debug(this.chargingStation.logPrefix() + ' ' + OCPP16IncomingRequestCommand.GET_DIAGNOSTICS + ' request received: %j', commandPayload); + logger.debug(this.chargingStation.logPrefix() + ' ' + OCPP16IncomingRequestCommand.GET_DIAGNOSTICS + ' request received: %j', commandPayload); const uri = new URL(commandPayload.location); if (uri.protocol.startsWith('ftp:')) { let ftpClient: Client; @@ -399,7 +402,7 @@ export default class OCPP16IncomingRequestService extends OCPPIncomingRequestSer if (accessResponse.code === 220) { // eslint-disable-next-line @typescript-eslint/no-misused-promises ftpClient.trackProgress(async (info) => { - getLogger().info(`${this.chargingStation.logPrefix()} ${info.bytes / 1024} bytes transferred from diagnostics archive ${info.name}`); + logger.info(`${this.chargingStation.logPrefix()} ${info.bytes / 1024} bytes transferred from diagnostics archive ${info.name}`); await this.chargingStation.ocppRequestService.sendDiagnosticsStatusNotification(OCPP16DiagnosticsStatus.Uploading); }); uploadResponse = await ftpClient.uploadFrom(path.join(path.resolve(__dirname, '../../../../'), diagnosticsArchive), uri.pathname + diagnosticsArchive); @@ -421,7 +424,7 @@ export default class OCPP16IncomingRequestService extends OCPPIncomingRequestSer return this.handleIncomingRequestError(OCPP16IncomingRequestCommand.GET_DIAGNOSTICS, error as Error, Constants.OCPP_RESPONSE_EMPTY); } } else { - getLogger().error(`${this.chargingStation.logPrefix()} Unsupported protocol ${uri.protocol} to transfer the diagnostic logs archive`); + logger.error(`${this.chargingStation.logPrefix()} Unsupported protocol ${uri.protocol} to transfer the diagnostic logs archive`); await this.chargingStation.ocppRequestService.sendDiagnosticsStatusNotification(OCPP16DiagnosticsStatus.UploadFailed); return Constants.OCPP_RESPONSE_EMPTY; } diff --git a/src/charging-station/ocpp/1.6/OCPP16RequestService.ts b/src/charging-station/ocpp/1.6/OCPP16RequestService.ts index b4a17cd4..0385f45a 100644 --- a/src/charging-station/ocpp/1.6/OCPP16RequestService.ts +++ b/src/charging-station/ocpp/1.6/OCPP16RequestService.ts @@ -6,6 +6,7 @@ import { CurrentType, Voltage } from '../../../types/ChargingStationTemplate'; import { DiagnosticsStatusNotificationRequest, HeartbeatRequest, OCPP16BootNotificationRequest, OCPP16IncomingRequestCommand, OCPP16RequestCommand, StatusNotificationRequest } from '../../../types/ocpp/1.6/Requests'; import { MeterValueUnit, MeterValuesRequest, OCPP16MeterValue, OCPP16MeterValueMeasurand, OCPP16MeterValuePhase } from '../../../types/ocpp/1.6/MeterValues'; +import ChargingStation from '../../ChargingStation'; import Constants from '../../../utils/Constants'; import { ErrorType } from '../../../types/ocpp/ErrorType'; import { JsonType } from '../../../types/JsonType'; @@ -19,11 +20,19 @@ import { OCPP16DiagnosticsStatus } from '../../../types/ocpp/1.6/DiagnosticsStat import { OCPP16ServiceUtils } from './OCPP16ServiceUtils'; import OCPPError from '../../../exception/OCPPError'; import OCPPRequestService from '../OCPPRequestService'; +import OCPPResponseService from '../OCPPResponseService'; import { SendParams } from '../../../types/ocpp/Requests'; import Utils from '../../../utils/Utils'; -import getLogger from '../../../utils/Logger'; +import logger from '../../../utils/Logger'; export default class OCPP16RequestService extends OCPPRequestService { + public constructor(chargingStation: ChargingStation, ocppResponseService: OCPPResponseService) { + if (new.target?.name === 'OCPP16RequestService') { + throw new TypeError('Cannot construct OCPP16RequestService instances directly'); + } + super(chargingStation, ocppResponseService); + } + public async sendHeartbeat(params?: SendParams): Promise { try { const payload: HeartbeatRequest = {}; @@ -139,7 +148,7 @@ export default class OCPP16RequestService extends OCPPRequestService { meterValue.sampledValue.push(OCPP16ServiceUtils.buildSampledValue(socSampledValueTemplate, socSampledValueTemplateValue)); const sampledValuesIndex = meterValue.sampledValue.length - 1; if (Utils.convertToInt(meterValue.sampledValue[sampledValuesIndex].value) > 100 || debug) { - getLogger().error(`${this.chargingStation.logPrefix()} MeterValues measurand ${meterValue.sampledValue[sampledValuesIndex].measurand ?? OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER}: connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${meterValue.sampledValue[sampledValuesIndex].value}/100`); + logger.error(`${this.chargingStation.logPrefix()} MeterValues measurand ${meterValue.sampledValue[sampledValuesIndex].measurand ?? OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER}: connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${meterValue.sampledValue[sampledValuesIndex].value}/100`); } } // Voltage measurand @@ -224,14 +233,14 @@ export default class OCPP16RequestService extends OCPPRequestService { : Utils.getRandomFloatRounded(maxPower / unitDivider); break; default: - getLogger().error(errMsg); + logger.error(errMsg); throw new OCPPError(ErrorType.INTERNAL_ERROR, errMsg, OCPP16RequestCommand.METER_VALUES); } meterValue.sampledValue.push(OCPP16ServiceUtils.buildSampledValue(powerSampledValueTemplate, powerMeasurandValues.allPhases)); const sampledValuesIndex = meterValue.sampledValue.length - 1; const maxPowerRounded = Utils.roundTo(maxPower / unitDivider, 2); if (Utils.convertToFloat(meterValue.sampledValue[sampledValuesIndex].value) > maxPowerRounded || debug) { - getLogger().error(`${this.chargingStation.logPrefix()} MeterValues measurand ${meterValue.sampledValue[sampledValuesIndex].measurand ?? OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER}: connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${meterValue.sampledValue[sampledValuesIndex].value}/${maxPowerRounded}`); + logger.error(`${this.chargingStation.logPrefix()} MeterValues measurand ${meterValue.sampledValue[sampledValuesIndex].measurand ?? OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER}: connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${meterValue.sampledValue[sampledValuesIndex].value}/${maxPowerRounded}`); } for (let phase = 1; this.chargingStation.getNumberOfPhases() === 3 && phase <= this.chargingStation.getNumberOfPhases(); phase++) { const phaseValue = `L${phase}-N`; @@ -240,7 +249,7 @@ export default class OCPP16RequestService extends OCPPRequestService { const sampledValuesPerPhaseIndex = meterValue.sampledValue.length - 1; const maxPowerPerPhaseRounded = Utils.roundTo(maxPowerPerPhase / unitDivider, 2); if (Utils.convertToFloat(meterValue.sampledValue[sampledValuesPerPhaseIndex].value) > maxPowerPerPhaseRounded || debug) { - getLogger().error(`${this.chargingStation.logPrefix()} MeterValues measurand ${meterValue.sampledValue[sampledValuesPerPhaseIndex].measurand ?? OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER}: phase ${meterValue.sampledValue[sampledValuesPerPhaseIndex].phase}, connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${meterValue.sampledValue[sampledValuesPerPhaseIndex].value}/${maxPowerPerPhaseRounded}`); + logger.error(`${this.chargingStation.logPrefix()} MeterValues measurand ${meterValue.sampledValue[sampledValuesPerPhaseIndex].measurand ?? OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER}: phase ${meterValue.sampledValue[sampledValuesPerPhaseIndex].phase}, connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${meterValue.sampledValue[sampledValuesPerPhaseIndex].value}/${maxPowerPerPhaseRounded}`); } } } @@ -290,13 +299,13 @@ export default class OCPP16RequestService extends OCPPRequestService { : Utils.getRandomFloatRounded(maxAmperage); break; default: - getLogger().error(errMsg); + logger.error(errMsg); throw new OCPPError(ErrorType.INTERNAL_ERROR, errMsg, OCPP16RequestCommand.METER_VALUES); } meterValue.sampledValue.push(OCPP16ServiceUtils.buildSampledValue(currentSampledValueTemplate, currentMeasurandValues.allPhases)); const sampledValuesIndex = meterValue.sampledValue.length - 1; if (Utils.convertToFloat(meterValue.sampledValue[sampledValuesIndex].value) > maxAmperage || debug) { - getLogger().error(`${this.chargingStation.logPrefix()} MeterValues measurand ${meterValue.sampledValue[sampledValuesIndex].measurand ?? OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER}: connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${meterValue.sampledValue[sampledValuesIndex].value}/${maxAmperage}`); + logger.error(`${this.chargingStation.logPrefix()} MeterValues measurand ${meterValue.sampledValue[sampledValuesIndex].measurand ?? OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER}: connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${meterValue.sampledValue[sampledValuesIndex].value}/${maxAmperage}`); } for (let phase = 1; this.chargingStation.getNumberOfPhases() === 3 && phase <= this.chargingStation.getNumberOfPhases(); phase++) { const phaseValue = `L${phase}`; @@ -304,7 +313,7 @@ export default class OCPP16RequestService extends OCPPRequestService { currentMeasurandValues[phaseValue], null, phaseValue as OCPP16MeterValuePhase)); const sampledValuesPerPhaseIndex = meterValue.sampledValue.length - 1; if (Utils.convertToFloat(meterValue.sampledValue[sampledValuesPerPhaseIndex].value) > maxAmperage || debug) { - getLogger().error(`${this.chargingStation.logPrefix()} MeterValues measurand ${meterValue.sampledValue[sampledValuesPerPhaseIndex].measurand ?? OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER}: phase ${meterValue.sampledValue[sampledValuesPerPhaseIndex].phase}, connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${meterValue.sampledValue[sampledValuesPerPhaseIndex].value}/${maxAmperage}`); + logger.error(`${this.chargingStation.logPrefix()} MeterValues measurand ${meterValue.sampledValue[sampledValuesPerPhaseIndex].measurand ?? OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER}: phase ${meterValue.sampledValue[sampledValuesPerPhaseIndex].phase}, connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${meterValue.sampledValue[sampledValuesPerPhaseIndex].value}/${maxAmperage}`); } } } @@ -331,7 +340,7 @@ export default class OCPP16RequestService extends OCPPRequestService { Utils.roundTo(this.chargingStation.getEnergyActiveImportRegisterByTransactionId(transactionId) / unitDivider, 2))); const sampledValuesIndex = meterValue.sampledValue.length - 1; if (energyValueRounded > maxEnergyRounded || debug) { - getLogger().error(`${this.chargingStation.logPrefix()} MeterValues measurand ${meterValue.sampledValue[sampledValuesIndex].measurand ?? OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER}: connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${energyValueRounded}/${maxEnergyRounded}, duration: ${Utils.roundTo(interval / (3600 * 1000), 4)}h`); + logger.error(`${this.chargingStation.logPrefix()} MeterValues measurand ${meterValue.sampledValue[sampledValuesIndex].measurand ?? OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER}: connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${energyValueRounded}/${maxEnergyRounded}, duration: ${Utils.roundTo(interval / (3600 * 1000), 4)}h`); } } const payload: MeterValuesRequest = { diff --git a/src/charging-station/ocpp/1.6/OCPP16ResponseService.ts b/src/charging-station/ocpp/1.6/OCPP16ResponseService.ts index 3e6611f2..876cc80b 100644 --- a/src/charging-station/ocpp/1.6/OCPP16ResponseService.ts +++ b/src/charging-station/ocpp/1.6/OCPP16ResponseService.ts @@ -15,12 +15,15 @@ import OCPPError from '../../../exception/OCPPError'; import OCPPResponseService from '../OCPPResponseService'; import { ResponseHandler } from '../../../types/ocpp/Responses'; import Utils from '../../../utils/Utils'; -import getLogger from '../../../utils/Logger'; +import logger from '../../../utils/Logger'; export default class OCPP16ResponseService extends OCPPResponseService { private responseHandlers: Map; - constructor(chargingStation: ChargingStation) { + public constructor(chargingStation: ChargingStation) { + if (new.target?.name === 'OCPP16ResponseService') { + throw new TypeError('Cannot construct OCPP16ResponseService instances directly'); + } super(chargingStation); this.responseHandlers = new Map([ [OCPP16RequestCommand.BOOT_NOTIFICATION, this.handleResponseBootNotification.bind(this)], @@ -39,7 +42,7 @@ export default class OCPP16ResponseService extends OCPPResponseService { try { await this.responseHandlers.get(commandName)(payload, requestPayload); } catch (error) { - getLogger().error(this.chargingStation.logPrefix() + ' Handle request response error: %j', error); + logger.error(this.chargingStation.logPrefix() + ' Handle request response error: %j', error); throw error; } } else { @@ -59,14 +62,14 @@ export default class OCPP16ResponseService extends OCPPResponseService { } if (Object.values(OCPP16RegistrationStatus).includes(payload.status)) { const logMsg = `${this.chargingStation.logPrefix()} Charging station in '${payload.status}' state on the central server`; - payload.status === OCPP16RegistrationStatus.REJECTED ? getLogger().warn(logMsg) : getLogger().info(logMsg); + payload.status === OCPP16RegistrationStatus.REJECTED ? logger.warn(logMsg) : logger.info(logMsg); } else { - getLogger().error(this.chargingStation.logPrefix() + ' Charging station boot notification response received: %j with undefined registration status', payload); + logger.error(this.chargingStation.logPrefix() + ' Charging station boot notification response received: %j with undefined registration status', payload); } } private handleResponseHeartbeat(payload: HeartbeatResponse, requestPayload: HeartbeatRequest): void { - getLogger().debug(this.chargingStation.logPrefix() + ' Heartbeat response received: %j to Heartbeat request: %j', payload, requestPayload); + logger.debug(this.chargingStation.logPrefix() + ' Heartbeat response received: %j to Heartbeat request: %j', payload, requestPayload); } private handleResponseAuthorize(payload: OCPP16AuthorizeResponse, requestPayload: AuthorizeRequest): void { @@ -79,11 +82,11 @@ export default class OCPP16ResponseService extends OCPPResponseService { } if (payload.idTagInfo.status === OCPP16AuthorizationStatus.ACCEPTED) { this.chargingStation.getConnectorStatus(authorizeConnectorId).idTagAuthorized = true; - getLogger().debug(`${this.chargingStation.logPrefix()} IdTag ${requestPayload.idTag} authorized on connector ${authorizeConnectorId}`); + logger.debug(`${this.chargingStation.logPrefix()} IdTag ${requestPayload.idTag} authorized on connector ${authorizeConnectorId}`); } else { this.chargingStation.getConnectorStatus(authorizeConnectorId).idTagAuthorized = false; delete this.chargingStation.getConnectorStatus(authorizeConnectorId).authorizeIdTag; - getLogger().debug(`${this.chargingStation.logPrefix()} IdTag ${requestPayload.idTag} refused with status ${payload.idTagInfo.status} on connector ${authorizeConnectorId}`); + logger.debug(`${this.chargingStation.logPrefix()} IdTag ${requestPayload.idTag} refused with status ${payload.idTagInfo.status} on connector ${authorizeConnectorId}`); } } @@ -98,44 +101,44 @@ export default class OCPP16ResponseService extends OCPPResponseService { } } if (!transactionConnectorId) { - getLogger().error(this.chargingStation.logPrefix() + ' Trying to start a transaction on a non existing connector Id ' + connectorId.toString()); + logger.error(this.chargingStation.logPrefix() + ' Trying to start a transaction on a non existing connector Id ' + connectorId.toString()); return; } if (this.chargingStation.getConnectorStatus(connectorId).transactionRemoteStarted && this.chargingStation.getAuthorizeRemoteTxRequests() && this.chargingStation.getLocalAuthListEnabled() && this.chargingStation.hasAuthorizedTags() && !this.chargingStation.getConnectorStatus(connectorId).idTagLocalAuthorized) { - getLogger().error(this.chargingStation.logPrefix() + ' Trying to start a transaction with a not local authorized idTag ' + this.chargingStation.getConnectorStatus(connectorId).localAuthorizeIdTag + ' on connector Id ' + connectorId.toString()); + logger.error(this.chargingStation.logPrefix() + ' Trying to start a transaction with a not local authorized idTag ' + this.chargingStation.getConnectorStatus(connectorId).localAuthorizeIdTag + ' on connector Id ' + connectorId.toString()); await this.resetConnectorOnStartTransactionError(connectorId); return; } if (this.chargingStation.getConnectorStatus(connectorId).transactionRemoteStarted && this.chargingStation.getAuthorizeRemoteTxRequests() && this.chargingStation.getMayAuthorizeAtRemoteStart() && !this.chargingStation.getConnectorStatus(connectorId).idTagLocalAuthorized && !this.chargingStation.getConnectorStatus(connectorId).idTagAuthorized) { - getLogger().error(this.chargingStation.logPrefix() + ' Trying to start a transaction with a not authorized idTag ' + this.chargingStation.getConnectorStatus(connectorId).authorizeIdTag + ' on connector Id ' + connectorId.toString()); + logger.error(this.chargingStation.logPrefix() + ' Trying to start a transaction with a not authorized idTag ' + this.chargingStation.getConnectorStatus(connectorId).authorizeIdTag + ' on connector Id ' + connectorId.toString()); await this.resetConnectorOnStartTransactionError(connectorId); return; } if (this.chargingStation.getConnectorStatus(connectorId).idTagAuthorized && this.chargingStation.getConnectorStatus(connectorId).authorizeIdTag !== requestPayload.idTag) { - getLogger().error(this.chargingStation.logPrefix() + ' Trying to start a transaction with an idTag ' + requestPayload.idTag + ' different from the authorize request one ' + this.chargingStation.getConnectorStatus(connectorId).authorizeIdTag + ' on connector Id ' + connectorId.toString()); + logger.error(this.chargingStation.logPrefix() + ' Trying to start a transaction with an idTag ' + requestPayload.idTag + ' different from the authorize request one ' + this.chargingStation.getConnectorStatus(connectorId).authorizeIdTag + ' on connector Id ' + connectorId.toString()); await this.resetConnectorOnStartTransactionError(connectorId); return; } if (this.chargingStation.getConnectorStatus(connectorId).idTagLocalAuthorized && this.chargingStation.getConnectorStatus(connectorId).localAuthorizeIdTag !== requestPayload.idTag) { - getLogger().error(this.chargingStation.logPrefix() + ' Trying to start a transaction with an idTag ' + requestPayload.idTag + ' different from the local authorized one ' + this.chargingStation.getConnectorStatus(connectorId).localAuthorizeIdTag + ' on connector Id ' + connectorId.toString()); + logger.error(this.chargingStation.logPrefix() + ' Trying to start a transaction with an idTag ' + requestPayload.idTag + ' different from the local authorized one ' + this.chargingStation.getConnectorStatus(connectorId).localAuthorizeIdTag + ' on connector Id ' + connectorId.toString()); await this.resetConnectorOnStartTransactionError(connectorId); return; } if (this.chargingStation.getConnectorStatus(connectorId)?.transactionStarted) { - getLogger().debug(this.chargingStation.logPrefix() + ' Trying to start a transaction on an already used connector ' + connectorId.toString() + ': %j', this.chargingStation.getConnectorStatus(connectorId)); + logger.debug(this.chargingStation.logPrefix() + ' Trying to start a transaction on an already used connector ' + connectorId.toString() + ': %j', this.chargingStation.getConnectorStatus(connectorId)); return; } if (this.chargingStation.getConnectorStatus(connectorId)?.status !== OCPP16ChargePointStatus.AVAILABLE && this.chargingStation.getConnectorStatus(connectorId)?.status !== OCPP16ChargePointStatus.PREPARING) { - getLogger().error(`${this.chargingStation.logPrefix()} Trying to start a transaction on connector ${connectorId.toString()} with status ${this.chargingStation.getConnectorStatus(connectorId)?.status}`); + logger.error(`${this.chargingStation.logPrefix()} Trying to start a transaction on connector ${connectorId.toString()} with status ${this.chargingStation.getConnectorStatus(connectorId)?.status}`); return; } if (!Number.isInteger(payload.transactionId)) { - getLogger().warn(`${this.chargingStation.logPrefix()} Trying to start a transaction on connector ${connectorId.toString()} with a non integer transaction Id ${payload.transactionId}, converting to integer`); + logger.warn(`${this.chargingStation.logPrefix()} Trying to start a transaction on connector ${connectorId.toString()} with a non integer transaction Id ${payload.transactionId}, converting to integer`); payload.transactionId = Utils.convertToInt(payload.transactionId); } @@ -150,14 +153,14 @@ export default class OCPP16ResponseService extends OCPPResponseService { this.chargingStation.getConnectorStatus(connectorId).transactionBeginMeterValue); await this.chargingStation.ocppRequestService.sendStatusNotification(connectorId, OCPP16ChargePointStatus.CHARGING); this.chargingStation.getConnectorStatus(connectorId).status = OCPP16ChargePointStatus.CHARGING; - getLogger().info(this.chargingStation.logPrefix() + ' Transaction ' + payload.transactionId.toString() + ' STARTED on ' + this.chargingStation.stationInfo.chargingStationId + '#' + connectorId.toString() + ' for idTag ' + requestPayload.idTag); + logger.info(this.chargingStation.logPrefix() + ' Transaction ' + payload.transactionId.toString() + ' STARTED on ' + this.chargingStation.stationInfo.chargingStationId + '#' + connectorId.toString() + ' for idTag ' + requestPayload.idTag); if (this.chargingStation.stationInfo.powerSharedByConnectors) { this.chargingStation.stationInfo.powerDivider++; } const configuredMeterValueSampleInterval = this.chargingStation.getConfigurationKey(OCPP16StandardParametersKey.MeterValueSampleInterval); this.chargingStation.startMeterValues(connectorId, configuredMeterValueSampleInterval ? Utils.convertToInt(configuredMeterValueSampleInterval.value) * 1000 : 60000); } else { - getLogger().warn(this.chargingStation.logPrefix() + ' Starting transaction id ' + payload.transactionId.toString() + ' REJECTED with status ' + payload?.idTagInfo?.status + ', idTag ' + requestPayload.idTag); + logger.warn(this.chargingStation.logPrefix() + ' Starting transaction id ' + payload.transactionId.toString() + ' REJECTED with status ' + payload?.idTagInfo?.status + ', idTag ' + requestPayload.idTag); await this.resetConnectorOnStartTransactionError(connectorId); } } @@ -179,7 +182,7 @@ export default class OCPP16ResponseService extends OCPPResponseService { } } if (!transactionConnectorId) { - getLogger().error(this.chargingStation.logPrefix() + ' Trying to stop a non existing transaction ' + requestPayload.transactionId.toString()); + logger.error(this.chargingStation.logPrefix() + ' Trying to stop a non existing transaction ' + requestPayload.transactionId.toString()); return; } if (payload.idTagInfo?.status === OCPP16AuthorizationStatus.ACCEPTED) { @@ -196,18 +199,18 @@ export default class OCPP16ResponseService extends OCPPResponseService { if (this.chargingStation.stationInfo.powerSharedByConnectors) { this.chargingStation.stationInfo.powerDivider--; } - getLogger().info(this.chargingStation.logPrefix() + ' Transaction ' + requestPayload.transactionId.toString() + ' STOPPED on ' + this.chargingStation.stationInfo.chargingStationId + '#' + transactionConnectorId.toString()); + logger.info(this.chargingStation.logPrefix() + ' Transaction ' + requestPayload.transactionId.toString() + ' STOPPED on ' + this.chargingStation.stationInfo.chargingStationId + '#' + transactionConnectorId.toString()); this.chargingStation.resetConnectorStatus(transactionConnectorId); } else { - getLogger().warn(this.chargingStation.logPrefix() + ' Stopping transaction id ' + requestPayload.transactionId.toString() + ' REJECTED with status ' + payload.idTagInfo?.status); + logger.warn(this.chargingStation.logPrefix() + ' Stopping transaction id ' + requestPayload.transactionId.toString() + ' REJECTED with status ' + payload.idTagInfo?.status); } } private handleResponseStatusNotification(payload: StatusNotificationRequest, requestPayload: StatusNotificationResponse): void { - getLogger().debug(this.chargingStation.logPrefix() + ' Status notification response received: %j to StatusNotification request: %j', payload, requestPayload); + logger.debug(this.chargingStation.logPrefix() + ' Status notification response received: %j to StatusNotification request: %j', payload, requestPayload); } private handleResponseMeterValues(payload: MeterValuesRequest, requestPayload: MeterValuesResponse): void { - getLogger().debug(this.chargingStation.logPrefix() + ' MeterValues response received: %j to MeterValues request: %j', payload, requestPayload); + logger.debug(this.chargingStation.logPrefix() + ' MeterValues response received: %j to MeterValues request: %j', payload, requestPayload); } } diff --git a/src/charging-station/ocpp/1.6/OCPP16ServiceUtils.ts b/src/charging-station/ocpp/1.6/OCPP16ServiceUtils.ts index 276d2016..f55c7429 100644 --- a/src/charging-station/ocpp/1.6/OCPP16ServiceUtils.ts +++ b/src/charging-station/ocpp/1.6/OCPP16ServiceUtils.ts @@ -8,17 +8,17 @@ import OCPPError from '../../../exception/OCPPError'; import { RequestCommand } from '../../../types/ocpp/Requests'; import { SampledValueTemplate } from '../../../types/MeasurandPerPhaseSampledValueTemplates'; import Utils from '../../../utils/Utils'; -import getLogger from '../../../utils/Logger'; +import logger from '../../../utils/Logger'; export class OCPP16ServiceUtils { public static checkMeasurandPowerDivider(chargingStation: ChargingStation, measurandType: OCPP16MeterValueMeasurand): void { if (Utils.isUndefined(chargingStation.stationInfo.powerDivider)) { const errMsg = `${chargingStation.logPrefix()} MeterValues measurand ${measurandType ?? OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER}: powerDivider is undefined`; - getLogger().error(errMsg); + logger.error(errMsg); throw new OCPPError(ErrorType.INTERNAL_ERROR, errMsg, RequestCommand.METER_VALUES); } else if (chargingStation.stationInfo?.powerDivider <= 0) { const errMsg = `${chargingStation.logPrefix()} MeterValues measurand ${measurandType ?? OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER}: powerDivider have zero or below value ${chargingStation.stationInfo.powerDivider}`; - getLogger().error(errMsg); + logger.error(errMsg); throw new OCPPError(ErrorType.INTERNAL_ERROR, errMsg, RequestCommand.METER_VALUES); } } diff --git a/src/charging-station/ocpp/OCPPIncomingRequestService.ts b/src/charging-station/ocpp/OCPPIncomingRequestService.ts index 3f8dda43..3fc960a9 100644 --- a/src/charging-station/ocpp/OCPPIncomingRequestService.ts +++ b/src/charging-station/ocpp/OCPPIncomingRequestService.ts @@ -1,17 +1,25 @@ -import ChargingStation from '../ChargingStation'; +import type ChargingStation from '../ChargingStation'; import { IncomingRequestCommand } from '../../types/ocpp/Requests'; import { JsonType } from '../../types/JsonType'; -import getLogger from '../../utils/Logger'; +import logger from '../../utils/Logger'; export default abstract class OCPPIncomingRequestService { + private static readonly instances: Map = new Map(); protected chargingStation: ChargingStation; - constructor(chargingStation: ChargingStation) { + protected constructor(chargingStation: ChargingStation) { this.chargingStation = chargingStation; } + public static getInstance(this: new (chargingStation: ChargingStation) => T, chargingStation: ChargingStation): T { + if (!OCPPIncomingRequestService.instances.has(chargingStation.id)) { + OCPPIncomingRequestService.instances.set(chargingStation.id, new this(chargingStation)); + } + return OCPPIncomingRequestService.instances.get(chargingStation.id) as T; + } + protected handleIncomingRequestError(commandName: IncomingRequestCommand, error: Error, errorOcppResponse?: T): T { - getLogger().error(this.chargingStation.logPrefix() + ' Incoming request command %s error: %j', commandName, error); + logger.error(this.chargingStation.logPrefix() + ' Incoming request command %s error: %j', commandName, error); if (errorOcppResponse) { return errorOcppResponse; } diff --git a/src/charging-station/ocpp/OCPPRequestService.ts b/src/charging-station/ocpp/OCPPRequestService.ts index 9f7fcfb8..8eaeb833 100644 --- a/src/charging-station/ocpp/OCPPRequestService.ts +++ b/src/charging-station/ocpp/OCPPRequestService.ts @@ -4,28 +4,36 @@ import { DiagnosticsStatus, IncomingRequestCommand, RequestCommand, SendParams } import { BootNotificationResponse } from '../../types/ocpp/Responses'; import { ChargePointErrorCode } from '../../types/ocpp/ChargePointErrorCode'; import { ChargePointStatus } from '../../types/ocpp/ChargePointStatus'; -import ChargingStation from '../ChargingStation'; +import type ChargingStation from '../ChargingStation'; import Constants from '../../utils/Constants'; import { ErrorType } from '../../types/ocpp/ErrorType'; import { JsonType } from '../../types/JsonType'; import { MessageType } from '../../types/ocpp/MessageType'; import { MeterValue } from '../../types/ocpp/MeterValues'; import OCPPError from '../../exception/OCPPError'; -import OCPPResponseService from './OCPPResponseService'; +import type OCPPResponseService from './OCPPResponseService'; import PerformanceStatistics from '../../performance/PerformanceStatistics'; import Utils from '../../utils/Utils'; -import getLogger from '../../utils/Logger'; +import logger from '../../utils/Logger'; export default abstract class OCPPRequestService { - public chargingStation: ChargingStation; - protected ocppResponseService: OCPPResponseService; + private static readonly instances: Map = new Map(); + protected readonly chargingStation: ChargingStation; + private readonly ocppResponseService: OCPPResponseService; - constructor(chargingStation: ChargingStation, ocppResponseService: OCPPResponseService) { + protected constructor(chargingStation: ChargingStation, ocppResponseService: OCPPResponseService) { this.chargingStation = chargingStation; this.ocppResponseService = ocppResponseService; } - public async sendMessage(messageId: string, messageData: JsonType | OCPPError, messageType: MessageType, commandName: RequestCommand | IncomingRequestCommand, + public static getInstance(this: new (chargingStation: ChargingStation, ocppResponseService: OCPPResponseService) => T, chargingStation: ChargingStation, ocppResponseService: OCPPResponseService): T { + if (!OCPPRequestService.instances.has(chargingStation.id)) { + OCPPRequestService.instances.set(chargingStation.id, new this(chargingStation, ocppResponseService)); + } + return OCPPRequestService.instances.get(chargingStation.id) as T; + } + + protected async sendMessage(messageId: string, messageData: JsonType | OCPPError, messageType: MessageType, commandName: RequestCommand | IncomingRequestCommand, params: SendParams = { skipBufferingOnError: false, triggerMessage: false @@ -99,7 +107,7 @@ export default abstract class OCPPRequestService { if (requestStatistic && self.chargingStation.getEnableStatistics()) { self.chargingStation.performanceStatistics.addRequestStatistic(commandName, MessageType.CALL_ERROR_MESSAGE); } - getLogger().error(`${self.chargingStation.logPrefix()} Error %j occurred when calling command %s with message data %j`, error, commandName, messageData); + logger.error(`${self.chargingStation.logPrefix()} Error %j occurred when calling command %s with message data %j`, error, commandName, messageData); self.chargingStation.requests.delete(messageId); reject(error); } @@ -111,7 +119,7 @@ export default abstract class OCPPRequestService { } protected handleRequestError(commandName: RequestCommand, error: Error): void { - getLogger().error(this.chargingStation.logPrefix() + ' Request command %s error: %j', commandName, error); + logger.error(this.chargingStation.logPrefix() + ' Request command %s error: %j', commandName, error); throw error; } diff --git a/src/charging-station/ocpp/OCPPResponseService.ts b/src/charging-station/ocpp/OCPPResponseService.ts index 17bf8065..c0dd3f76 100644 --- a/src/charging-station/ocpp/OCPPResponseService.ts +++ b/src/charging-station/ocpp/OCPPResponseService.ts @@ -1,13 +1,21 @@ -import ChargingStation from '../ChargingStation'; +import type ChargingStation from '../ChargingStation'; import { JsonType } from '../../types/JsonType'; import { RequestCommand } from '../../types/ocpp/Requests'; export default abstract class OCPPResponseService { - protected chargingStation: ChargingStation; + private static readonly instances: Map = new Map(); + protected readonly chargingStation: ChargingStation; - constructor(chargingStation: ChargingStation) { + protected constructor(chargingStation: ChargingStation) { this.chargingStation = chargingStation; } + public static getInstance(this: new (chargingStation: ChargingStation) => T, chargingStation: ChargingStation): T { + if (!OCPPResponseService.instances.has(chargingStation.id)) { + OCPPResponseService.instances.set(chargingStation.id, new this(chargingStation)); + } + return OCPPResponseService.instances.get(chargingStation.id) as T; + } + public abstract handleResponse(commandName: RequestCommand, payload: JsonType | string, requestPayload: JsonType): Promise; } diff --git a/src/charging-station/ui-websocket-services/AbstractUIService.ts b/src/charging-station/ui-websocket-services/AbstractUIService.ts index b4905da4..2dd6a200 100644 --- a/src/charging-station/ui-websocket-services/AbstractUIService.ts +++ b/src/charging-station/ui-websocket-services/AbstractUIService.ts @@ -3,7 +3,7 @@ import { ProtocolCommand, ProtocolRequestHandler } from '../../types/UIProtocol' import BaseError from '../../exception/BaseError'; import { JsonType } from '../../types/JsonType'; import UIWebSocketServer from '../UIWebSocketServer'; -import getLogger from '../../utils/Logger'; +import logger from '../../utils/Logger'; export default abstract class AbstractUIService { protected readonly uiWebSocketServer: UIWebSocketServer; @@ -24,7 +24,7 @@ export default abstract class AbstractUIService { messageResponse = await this.messageHandlers.get(command)(payload) as JsonType; } catch (error) { // Log - getLogger().error(this.uiWebSocketServer.logPrefix() + ' Handle message error: %j', error); + logger.error(this.uiWebSocketServer.logPrefix() + ' Handle message error: %j', error); throw error; } } else { diff --git a/src/charging-station/ui-websocket-services/UIServiceUtils.ts b/src/charging-station/ui-websocket-services/UIServiceUtils.ts index e71b5f33..2c37e80a 100644 --- a/src/charging-station/ui-websocket-services/UIServiceUtils.ts +++ b/src/charging-station/ui-websocket-services/UIServiceUtils.ts @@ -2,7 +2,7 @@ import { Protocol, ProtocolVersion } from '../../types/UIProtocol'; import { IncomingMessage } from 'http'; import Utils from '../../utils/Utils'; -import getLogger from '../../utils/Logger'; +import logger from '../../utils/Logger'; export class UIServiceUtils { public static handleProtocols = (protocols: Set, request: IncomingMessage): string | false => { @@ -17,7 +17,7 @@ export class UIServiceUtils { return fullProtocol; } } - getLogger().error(`${Utils.logPrefix(' UI WebSocket Server:')} Unsupported protocol: ${protocol} or protocol version: ${version}`); + logger.error(`${Utils.logPrefix(' UI WebSocket Server:')} Unsupported protocol: ${protocol} or protocol version: ${version}`); return false; }; } diff --git a/src/performance/PerformanceStatistics.ts b/src/performance/PerformanceStatistics.ts index 96006346..b9fa1490 100644 --- a/src/performance/PerformanceStatistics.ts +++ b/src/performance/PerformanceStatistics.ts @@ -10,19 +10,29 @@ import Configuration from '../utils/Configuration'; import { MessageType } from '../types/ocpp/MessageType'; import { URL } from 'url'; import Utils from '../utils/Utils'; -import getLogger from '../utils/Logger'; +import logger from '../utils/Logger'; import { parentPort } from 'worker_threads'; export default class PerformanceStatistics { + private static readonly instances: Map = new Map(); private readonly objId: string; + private readonly objName: string; private performanceObserver: PerformanceObserver; private readonly statistics: Statistics; private displayInterval: NodeJS.Timeout; - public constructor(objId: string, uri: URL) { + private constructor(objId: string, objName: string, uri: URL) { this.objId = objId; + this.objName = objName; this.initializePerformanceObserver(); - this.statistics = { id: this.objId ?? 'Object id not specified', uri: uri.toString(), createdAt: new Date(), statisticsData: new Map>() }; + this.statistics = { id: this.objId ?? 'Object id not specified', name: this.objName ?? 'Object name not specified', uri: uri.toString(), createdAt: new Date(), statisticsData: new Map>() }; + } + + public static getInstance(objId: string, objName: string, uri: URL): PerformanceStatistics { + if (!PerformanceStatistics.instances.has(objId)) { + PerformanceStatistics.instances.set(objId, new PerformanceStatistics(objId, objName, uri)); + } + return PerformanceStatistics.instances.get(objId); } public static beginMeasure(id: string): string { @@ -60,7 +70,7 @@ export default class PerformanceStatistics { } break; default: - getLogger().error(`${this.logPrefix()} wrong message type ${messageType}`); + logger.error(`${this.logPrefix()} wrong message type ${messageType}`); break; } } @@ -68,7 +78,7 @@ export default class PerformanceStatistics { public start(): void { this.startLogStatisticsInterval(); if (Configuration.getPerformanceStorage().enabled) { - getLogger().info(`${this.logPrefix()} storage enabled: type ${Configuration.getPerformanceStorage().type}, uri: ${Configuration.getPerformanceStorage().uri}`); + logger.info(`${this.logPrefix()} storage enabled: type ${Configuration.getPerformanceStorage().type}, uri: ${Configuration.getPerformanceStorage().uri}`); } } @@ -89,13 +99,13 @@ export default class PerformanceStatistics { this.performanceObserver = new PerformanceObserver((list) => { const lastPerformanceEntry = list.getEntries()[0]; this.addPerformanceEntryToStatistics(lastPerformanceEntry); - getLogger().debug(`${this.logPrefix()} '${lastPerformanceEntry.name}' performance entry: %j`, lastPerformanceEntry); + logger.debug(`${this.logPrefix()} '${lastPerformanceEntry.name}' performance entry: %j`, lastPerformanceEntry); }); this.performanceObserver.observe({ entryTypes: ['measure'] }); } private logStatistics(): void { - getLogger().info(this.logPrefix() + ' %j', this.statistics); + logger.info(this.logPrefix() + ' %j', this.statistics); } private startLogStatisticsInterval(): void { @@ -103,9 +113,9 @@ export default class PerformanceStatistics { this.displayInterval = setInterval(() => { this.logStatistics(); }, Configuration.getLogStatisticsInterval() * 1000); - getLogger().info(this.logPrefix() + ' logged every ' + Utils.formatDurationSeconds(Configuration.getLogStatisticsInterval())); + logger.info(this.logPrefix() + ' logged every ' + Utils.formatDurationSeconds(Configuration.getLogStatisticsInterval())); } else { - getLogger().info(this.logPrefix() + ' log interval is set to ' + Configuration.getLogStatisticsInterval().toString() + '. Not logging statistics'); + logger.info(this.logPrefix() + ' log interval is set to ' + Configuration.getLogStatisticsInterval().toString() + '. Not logging statistics'); } } @@ -200,6 +210,6 @@ export default class PerformanceStatistics { } private logPrefix(): string { - return Utils.logPrefix(` ${this.objId} | Performance statistics`); + return Utils.logPrefix(` ${this.objName} | Performance statistics`); } } diff --git a/src/performance/storage/Storage.ts b/src/performance/storage/Storage.ts index 80906699..db33a694 100644 --- a/src/performance/storage/Storage.ts +++ b/src/performance/storage/Storage.ts @@ -5,7 +5,7 @@ import { DBName, StorageType } from '../../types/Storage'; import Statistics from '../../types/Statistics'; import { URL } from 'url'; import Utils from '../../utils/Utils'; -import getLogger from '../../utils/Logger'; +import logger from '../../utils/Logger'; export abstract class Storage { protected readonly storageUri: URL; @@ -18,7 +18,7 @@ export abstract class Storage { } protected handleDBError(type: StorageType, error: Error, table?: string): void { - getLogger().error(`${this.logPrefix} ${this.getDBNameFromStorageType(type)} error '${error.message}'${(!Utils.isNullOrUndefined(table) || !table) && ` in table or collection '${table}'`}: %j`, error); + logger.error(`${this.logPrefix} ${this.getDBNameFromStorageType(type)} error '${error.message}'${(!Utils.isNullOrUndefined(table) || !table) && ` in table or collection '${table}'`}: %j`, error); } protected getDBNameFromStorageType(type: StorageType): DBName { diff --git a/src/types/Statistics.ts b/src/types/Statistics.ts index cf1e01b6..bc6466c9 100644 --- a/src/types/Statistics.ts +++ b/src/types/Statistics.ts @@ -23,6 +23,7 @@ export interface StatisticsData { export default interface Statistics { id: string; + name: string; uri: string; createdAt: Date; updatedAt?: Date; diff --git a/src/utils/FileUtils.ts b/src/utils/FileUtils.ts index c6850c71..e68500e3 100644 --- a/src/utils/FileUtils.ts +++ b/src/utils/FileUtils.ts @@ -1,5 +1,5 @@ import chalk from 'chalk'; -import getLogger from './Logger'; +import logger from './Logger'; export default class FileUtils { static handleFileException(logPrefix: string, fileType: string, filePath: string, error: NodeJS.ErrnoException, consoleOut = false): void { @@ -8,25 +8,25 @@ export default class FileUtils { if (consoleOut) { console.warn(chalk.green(prefix) + chalk.yellow(fileType + ' file ' + filePath + ' not found: '), error); } else { - getLogger().warn(prefix + fileType + ' file ' + filePath + ' not found: %j', error); + logger.warn(prefix + fileType + ' file ' + filePath + ' not found: %j', error); } } else if (error.code === 'EEXIST') { if (consoleOut) { console.warn(chalk.green(prefix) + chalk.yellow(fileType + ' file ' + filePath + ' already exists: '), error); } else { - getLogger().warn(prefix + fileType + ' file ' + filePath + ' already exists: %j', error); + logger.warn(prefix + fileType + ' file ' + filePath + ' already exists: %j', error); } } else if (error.code === 'EACCES') { if (consoleOut) { console.warn(chalk.green(prefix) + chalk.yellow(fileType + ' file ' + filePath + ' access denied: '), error); } else { - getLogger().warn(prefix + fileType + ' file ' + filePath + ' access denied: %j', error); + logger.warn(prefix + fileType + ' file ' + filePath + ' access denied: %j', error); } } else { if (consoleOut) { console.warn(chalk.green(prefix) + chalk.yellow(fileType + ' file ' + filePath + ' error: '), error); } else { - getLogger().warn(prefix + fileType + ' file ' + filePath + ' error: %j', error); + logger.warn(prefix + fileType + ' file ' + filePath + ' error: %j', error); } throw error; } diff --git a/src/utils/Logger.ts b/src/utils/Logger.ts index 61a3ec0d..ccbc63ca 100644 --- a/src/utils/Logger.ts +++ b/src/utils/Logger.ts @@ -19,26 +19,20 @@ if (Configuration.getLogRotate()) { ]; } -let loggerInstance: Logger | null = null; -const getLogger = (): Logger => { - if (!loggerInstance) { - loggerInstance = createLogger({ - level: Configuration.getLogLevel(), - format: format.combine(format.splat(), format[Configuration.getLogFormat()]()), - transports: transports, - }); - return loggerInstance; - } -}; +const logger: Logger = createLogger({ + level: Configuration.getLogLevel(), + format: format.combine(format.splat(), format[Configuration.getLogFormat()]()), + transports: transports, +}); // // If enabled, log to the `console` with the format: // `${info.level}: ${info.message} JSON.stringify({ ...rest }) ` // if (Configuration.getLogConsole()) { - getLogger().add(new Console({ + logger.add(new Console({ format: format.combine(format.splat(), format[Configuration.getLogFormat()]()), })); } -export default getLogger; +export default logger; -- 2.34.1