X-Git-Url: https://git.piment-noir.org/?a=blobdiff_plain;f=src%2Fcharging-station%2FChargingStation.ts;h=1e10118bc86c5a0dec8d3e1c71a5becd4f118c4f;hb=28e6dd3a9fd0db84953aa8d149d0a19fadc63554;hp=ad4dac388a293b56243982184dd1f40848b7fbf3;hpb=057e2042576fb5f4dd4c201f1bd0f64c35ccf961;p=e-mobility-charging-stations-simulator.git diff --git a/src/charging-station/ChargingStation.ts b/src/charging-station/ChargingStation.ts index ad4dac38..1e10118b 100644 --- a/src/charging-station/ChargingStation.ts +++ b/src/charging-station/ChargingStation.ts @@ -20,6 +20,7 @@ import { ConnectorStatus } from '../types/ConnectorStatus'; import Constants from '../utils/Constants'; import { ErrorType } from '../types/ocpp/ErrorType'; import FileUtils from '../utils/FileUtils'; +import { JsonType } from '../types/JsonType'; import { MessageType } from '../types/ocpp/MessageType'; import OCPP16IncomingRequestService from './ocpp/1.6/OCPP16IncomingRequestService'; import OCPP16RequestService from './ocpp/1.6/OCPP16RequestService'; @@ -31,11 +32,12 @@ import { OCPPVersion } from '../types/ocpp/OCPPVersion'; import PerformanceStatistics from '../performance/PerformanceStatistics'; import { SampledValueTemplate } from '../types/MeasurandPerPhaseSampledValueTemplates'; import { StopTransactionReason } from '../types/ocpp/Transaction'; +import { SupervisionUrlDistribution } from '../types/ConfigurationData'; import { URL } from 'url'; import Utils from '../utils/Utils'; import crypto from 'crypto'; import fs from 'fs'; -import logger from '../utils/Logger'; +import getLogger from '../utils/Logger'; import { parentPort } from 'worker_threads'; import path from 'path'; @@ -50,6 +52,7 @@ 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; @@ -64,6 +67,7 @@ export default class ChargingStation { private webSocketPingSetInterval!: NodeJS.Timeout; constructor(index: number, stationTemplateFile: string) { + this.id = Utils.generateUUID(); this.index = index; this.stationTemplateFile = stationTemplateFile; this.connectors = new Map(); @@ -121,10 +125,30 @@ export default class ChargingStation { return this?.wsConnection?.readyState === OPEN; } - public isRegistered(): boolean { + public getRegistrationStatus(): RegistrationStatus { + return this?.bootNotificationResponse?.status; + } + + public isInUnknownState(): boolean { + return Utils.isNullOrUndefined(this?.bootNotificationResponse?.status); + } + + public isInPendingState(): boolean { + return this?.bootNotificationResponse?.status === RegistrationStatus.PENDING; + } + + public isInAcceptedState(): boolean { return this?.bootNotificationResponse?.status === RegistrationStatus.ACCEPTED; } + public isInRejectedState(): boolean { + return this?.bootNotificationResponse?.status === RegistrationStatus.REJECTED; + } + + public isRegistered(): boolean { + return !this.isInUnknownState() && (this.isInAcceptedState() || this.isInPendingState()); + } + public isChargingStationAvailable(): boolean { return this.getConnectorStatus(0).availability === AvailabilityType.OPERATIVE; } @@ -145,6 +169,10 @@ export default class ChargingStation { return this.stationInfo.currentOutType ?? CurrentType.AC; } + public getOcppStrictCompliance(): boolean { + return this.stationInfo.ocppStrictCompliance ?? false; + } + public getVoltageOut(): number | undefined { const errMsg = `${this.logPrefix()} Unknown ${this.getCurrentOutType()} currentOutType in template file ${this.stationTemplateFile}, cannot define default voltage out`; let defaultVoltageOut: number; @@ -156,7 +184,7 @@ export default class ChargingStation { defaultVoltageOut = Voltage.VOLTAGE_400; break; default: - logger.error(errMsg); + getLogger().error(errMsg); throw new Error(errMsg); } return !Utils.isUndefined(this.stationInfo.voltageOut) ? this.stationInfo.voltageOut : defaultVoltageOut; @@ -236,17 +264,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)) { - logger.warn(`${this.logPrefix()} Trying to get unsupported MeterValues measurand '${measurand}' ${phase ? `on phase ${phase} ` : ''}in template on connectorId ${connectorId}`); + getLogger().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)) { - 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`); + 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`); 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)) { - logger.warn(`${this.logPrefix()} Unsupported MeterValues measurand '${measurand}' ${phase ? `on phase ${phase} ` : ''}in template on connectorId ${connectorId}`); + getLogger().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]; @@ -260,10 +288,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}`; - logger.error(errorMsg); + getLogger().error(errorMsg); throw new Error(errorMsg); } - logger.debug(`${this.logPrefix()} No MeterValues for measurand '${measurand}' ${phase ? `on phase ${phase} ` : ''}in template on connectorId ${connectorId}`); + getLogger().debug(`${this.logPrefix()} No MeterValues for measurand '${measurand}' ${phase ? `on phase ${phase} ` : ''}in template on connectorId ${connectorId}`); } public getAutomaticTransactionGeneratorRequireAuthorize(): boolean { @@ -276,11 +304,11 @@ export default class ChargingStation { this.heartbeatSetInterval = setInterval(async (): Promise => { await this.ocppRequestService.sendHeartbeat(); }, this.getHeartbeatInterval()); - logger.info(this.logPrefix() + ' Heartbeat started every ' + Utils.formatDurationMilliSeconds(this.getHeartbeatInterval())); + getLogger().info(this.logPrefix() + ' Heartbeat started every ' + Utils.formatDurationMilliSeconds(this.getHeartbeatInterval())); } else if (this.heartbeatSetInterval) { - logger.info(this.logPrefix() + ' Heartbeat already started every ' + Utils.formatDurationMilliSeconds(this.getHeartbeatInterval())); + getLogger().info(this.logPrefix() + ' Heartbeat already started every ' + Utils.formatDurationMilliSeconds(this.getHeartbeatInterval())); } else { - logger.error(`${this.logPrefix()} Heartbeat interval set to ${this.getHeartbeatInterval() ? Utils.formatDurationMilliSeconds(this.getHeartbeatInterval()) : this.getHeartbeatInterval()}, not starting the heartbeat`); + getLogger().error(`${this.logPrefix()} Heartbeat interval set to ${this.getHeartbeatInterval() ? Utils.formatDurationMilliSeconds(this.getHeartbeatInterval()) : this.getHeartbeatInterval()}, not starting the heartbeat`); } } @@ -293,18 +321,18 @@ export default class ChargingStation { public startMeterValues(connectorId: number, interval: number): void { if (connectorId === 0) { - logger.error(`${this.logPrefix()} Trying to start MeterValues on connector Id ${connectorId.toString()}`); + getLogger().error(`${this.logPrefix()} Trying to start MeterValues on connector Id ${connectorId.toString()}`); return; } if (!this.getConnectorStatus(connectorId)) { - logger.error(`${this.logPrefix()} Trying to start MeterValues on non existing connector Id ${connectorId.toString()}`); + getLogger().error(`${this.logPrefix()} Trying to start MeterValues on non existing connector Id ${connectorId.toString()}`); return; } if (!this.getConnectorStatus(connectorId)?.transactionStarted) { - logger.error(`${this.logPrefix()} Trying to start MeterValues on connector Id ${connectorId} with no transaction started`); + getLogger().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) { - logger.error(`${this.logPrefix()} Trying to start MeterValues on connector Id ${connectorId} with no transaction id`); + getLogger().error(`${this.logPrefix()} Trying to start MeterValues on connector Id ${connectorId} with no transaction id`); return; } if (interval > 0) { @@ -313,7 +341,7 @@ export default class ChargingStation { await this.ocppRequestService.sendMeterValues(connectorId, this.getConnectorStatus(connectorId).transactionId, interval); }, interval); } else { - logger.error(`${this.logPrefix()} Charging station ${StandardParametersKey.MeterValueSampleInterval} configuration set to ${interval ? Utils.formatDurationMilliSeconds(interval) : interval}, not sending MeterValues`); + getLogger().error(`${this.logPrefix()} Charging station ${StandardParametersKey.MeterValueSampleInterval} configuration set to ${interval ? Utils.formatDurationMilliSeconds(interval) : interval}, not sending MeterValues`); } } @@ -384,7 +412,7 @@ export default class ChargingStation { reboot, }); } else { - logger.error(`${this.logPrefix()} Trying to add an already existing configuration key: %j`, keyFound); + getLogger().error(`${this.logPrefix()} Trying to add an already existing configuration key: %j`, keyFound); } } @@ -394,7 +422,7 @@ export default class ChargingStation { const keyIndex = this.configuration.configurationKey.indexOf(keyFound); this.configuration.configurationKey[keyIndex].value = value; } else { - logger.error(`${this.logPrefix()} Trying to set a value on a non existing configuration key: %j`, { key, value }); + getLogger().error(`${this.logPrefix()} Trying to set a value on a non existing configuration key: %j`, { key, value }); } } @@ -461,6 +489,10 @@ export default class ChargingStation { } catch (error) { FileUtils.handleFileException(this.logPrefix(), 'Template', this.stationTemplateFile, error as NodeJS.ErrnoException); } + const chargingStationId = this.getChargingStationId(stationTemplateFromFile); + // Deprecation template keys section + this.warnDeprecatedTemplateKey(stationTemplateFromFile, 'supervisionUrl', chargingStationId, 'Use \'supervisionUrls\' instead'); + this.convertDeprecatedTemplateKey(stationTemplateFromFile, 'supervisionUrl', 'supervisionUrls'); const stationInfo: ChargingStationInfo = stationTemplateFromFile ?? {} as ChargingStationInfo; stationInfo.wsOptions = stationTemplateFromFile?.wsOptions ?? {}; if (!Utils.isEmptyArray(stationTemplateFromFile.power)) { @@ -477,7 +509,7 @@ export default class ChargingStation { } delete stationInfo.power; delete stationInfo.powerUnit; - stationInfo.chargingStationId = this.getChargingStationId(stationTemplateFromFile); + stationInfo.chargingStationId = chargingStationId; stationInfo.resetTime = stationTemplateFromFile.resetTime ? stationTemplateFromFile.resetTime * 1000 : Constants.CHARGING_STATION_DEFAULT_RESET_TIME; return stationInfo; } @@ -488,7 +520,7 @@ export default class ChargingStation { private handleUnsupportedVersion(version: OCPPVersion) { const errMsg = `${this.logPrefix()} Unsupported protocol version '${version}' configured in template file ${this.stationTemplateFile}`; - logger.error(errMsg); + getLogger().error(errMsg); throw new Error(errMsg); } @@ -505,18 +537,18 @@ export default class ChargingStation { // Build connectors if needed const maxConnectors = this.getMaxNumberOfConnectors(); if (maxConnectors <= 0) { - logger.warn(`${this.logPrefix()} Charging station template ${this.stationTemplateFile} with ${maxConnectors} connectors`); + getLogger().warn(`${this.logPrefix()} Charging station template ${this.stationTemplateFile} with ${maxConnectors} connectors`); } const templateMaxConnectors = this.getTemplateMaxNumberOfConnectors(); if (templateMaxConnectors <= 0) { - logger.warn(`${this.logPrefix()} Charging station template ${this.stationTemplateFile} with no connector configuration`); + getLogger().warn(`${this.logPrefix()} Charging station template ${this.stationTemplateFile} with no connector configuration`); } if (!this.stationInfo.Connectors[0]) { - logger.warn(`${this.logPrefix()} Charging station template ${this.stationTemplateFile} with no connector Id 0 configuration`); + getLogger().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) { - logger.warn(`${this.logPrefix()} Number of connectors exceeds the number of connector configurations in template ${this.stationTemplateFile}, forcing random connector configurations affectation`); + getLogger().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'); @@ -622,31 +654,27 @@ export default class ChargingStation { } private async onOpen(): Promise { - logger.info(`${this.logPrefix()} Connected to OCPP server through ${this.wsConnectionUrl.toString()}`); - if (!this.isRegistered()) { + getLogger().info(`${this.logPrefix()} Connected to OCPP server through ${this.wsConnectionUrl.toString()}`); + if (!this.isInAcceptedState()) { // Send BootNotification let registrationRetryCount = 0; do { this.bootNotificationResponse = await this.ocppRequestService.sendBootNotification(this.bootNotificationRequest.chargePointModel, this.bootNotificationRequest.chargePointVendor, this.bootNotificationRequest.chargeBoxSerialNumber, this.bootNotificationRequest.firmwareVersion); - if (!this.isRegistered()) { - registrationRetryCount++; + if (!this.isInAcceptedState()) { + this.getRegistrationMaxRetries() !== -1 && registrationRetryCount++; await Utils.sleep(this.bootNotificationResponse?.interval ? this.bootNotificationResponse.interval * 1000 : Constants.OCPP_DEFAULT_BOOT_NOTIFICATION_INTERVAL); } - } while (!this.isRegistered() && (registrationRetryCount <= this.getRegistrationMaxRetries() || this.getRegistrationMaxRetries() === -1)); + } while (!this.isInAcceptedState() && (registrationRetryCount <= this.getRegistrationMaxRetries() || this.getRegistrationMaxRetries() === -1)); } - if (this.isRegistered() && this.stationInfo.autoRegister) { - await this.ocppRequestService.sendBootNotification(this.bootNotificationRequest.chargePointModel, - this.bootNotificationRequest.chargePointVendor, this.bootNotificationRequest.chargeBoxSerialNumber, this.bootNotificationRequest.firmwareVersion); - } - if (this.isRegistered()) { + if (this.isInAcceptedState()) { await this.startMessageSequence(); this.stopped && (this.stopped = false); if (this.wsConnectionRestarted && this.isWebSocketConnectionOpened()) { this.flushMessageBuffer(); } } else { - logger.error(`${this.logPrefix()} Registration failure: max retries reached (${this.getRegistrationMaxRetries()}) or retry disabled (${this.getRegistrationMaxRetries()})`); + getLogger().error(`${this.logPrefix()} Registration failure: max retries reached (${this.getRegistrationMaxRetries()}) or retry disabled (${this.getRegistrationMaxRetries()})`); } this.autoReconnectRetryCount = 0; this.wsConnectionRestarted = false; @@ -657,12 +685,12 @@ export default class ChargingStation { // Normal close case WebSocketCloseEventStatusCode.CLOSE_NORMAL: case WebSocketCloseEventStatusCode.CLOSE_NO_STATUS: - logger.info(`${this.logPrefix()} WebSocket normally closed with status '${Utils.getWebSocketCloseEventStatusString(code)}' and reason '${reason}'`); + getLogger().info(`${this.logPrefix()} WebSocket normally closed with status '${Utils.getWebSocketCloseEventStatusString(code)}' and reason '${reason}'`); this.autoReconnectRetryCount = 0; break; // Abnormal close default: - logger.error(`${this.logPrefix()} WebSocket abnormally closed with status '${Utils.getWebSocketCloseEventStatusString(code)}' and reason '${reason}'`); + getLogger().error(`${this.logPrefix()} WebSocket abnormally closed with status '${Utils.getWebSocketCloseEventStatusString(code)}' and reason '${reason}'`); await this.reconnect(code); break; } @@ -670,10 +698,10 @@ export default class ChargingStation { private async onMessage(data: Data): Promise { let [messageType, messageId, commandName, commandPayload, errorDetails]: IncomingRequest = [0, '', '' as IncomingRequestCommand, {}, {}]; - let responseCallback: (payload: Record | string, requestPayload: Record) => void; + let responseCallback: (payload: JsonType | string, requestPayload: JsonType | OCPPError) => void; let rejectCallback: (error: OCPPError, requestStatistic?: boolean) => void; let requestCommandName: RequestCommand | IncomingRequestCommand; - let requestPayload: Record; + let requestPayload: JsonType | OCPPError; let cachedRequest: CachedRequest; let errMsg: string; try { @@ -726,27 +754,27 @@ export default class ChargingStation { // Error default: errMsg = `${this.logPrefix()} Wrong message type ${messageType}`; - logger.error(errMsg); + getLogger().error(errMsg); throw new OCPPError(ErrorType.PROTOCOL_ERROR, errMsg); } } catch (error) { // Log - logger.error('%s Incoming OCPP message %j matching cached request %j processing error %j', this.logPrefix(), data.toString(), this.requests.get(messageId), error); + getLogger().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 { - logger.debug(this.logPrefix() + ' Received a WS ping (rfc6455) from the server'); + getLogger().debug(this.logPrefix() + ' Received a WS ping (rfc6455) from the server'); } private onPong(): void { - logger.debug(this.logPrefix() + ' Received a WS pong (rfc6455) from the server'); + getLogger().debug(this.logPrefix() + ' Received a WS pong (rfc6455) from the server'); } private async onError(error: WSError): Promise { - logger.error(this.logPrefix() + ' WebSocket error: %j', error); + getLogger().error(this.logPrefix() + ' WebSocket error: %j', error); // switch (error.code) { // case 'ECONNREFUSED': // await this.reconnect(error); @@ -775,7 +803,7 @@ export default class ChargingStation { FileUtils.handleFileException(this.logPrefix(), 'Authorization', authorizationFile, error as NodeJS.ErrnoException); } } else { - logger.info(this.logPrefix() + ' No authorization file given in template file ' + this.stationTemplateFile); + getLogger().info(this.logPrefix() + ' No authorization file given in template file ' + this.stationTemplateFile); } return authorizedTags; } @@ -848,6 +876,10 @@ export default class ChargingStation { } private async startMessageSequence(): Promise { + if (this.stationInfo.autoRegister) { + await this.ocppRequestService.sendBootNotification(this.bootNotificationRequest.chargePointModel, + this.bootNotificationRequest.chargePointVendor, this.bootNotificationRequest.chargeBoxSerialNumber, this.bootNotificationRequest.firmwareVersion); + } // Start WebSocket ping this.startWebSocketPing(); // Start heartbeat @@ -919,11 +951,11 @@ export default class ChargingStation { this.wsConnection.ping((): void => { /* This is intentional */ }); } }, webSocketPingInterval * 1000); - logger.info(this.logPrefix() + ' WebSocket ping started every ' + Utils.formatDurationSeconds(webSocketPingInterval)); + getLogger().info(this.logPrefix() + ' WebSocket ping started every ' + Utils.formatDurationSeconds(webSocketPingInterval)); } else if (this.webSocketPingSetInterval) { - logger.info(this.logPrefix() + ' WebSocket ping every ' + Utils.formatDurationSeconds(webSocketPingInterval) + ' already started'); + getLogger().info(this.logPrefix() + ' WebSocket ping every ' + Utils.formatDurationSeconds(webSocketPingInterval) + ' already started'); } else { - logger.error(`${this.logPrefix()} WebSocket ping interval set to ${webSocketPingInterval ? Utils.formatDurationSeconds(webSocketPingInterval) : webSocketPingInterval}, not starting the WebSocket ping`); + getLogger().error(`${this.logPrefix()} WebSocket ping interval set to ${webSocketPingInterval ? Utils.formatDurationSeconds(webSocketPingInterval) : webSocketPingInterval}, not starting the WebSocket ping`); } } @@ -933,17 +965,45 @@ 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}`); + } + } + + private convertDeprecatedTemplateKey(template: ChargingStationTemplate, deprecatedKey: string, key: string): void { + if (!Utils.isUndefined(template[deprecatedKey])) { + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + template[key] = template[deprecatedKey]; + delete template[deprecatedKey]; + } + } + private getConfiguredSupervisionUrl(): URL { - const supervisionUrls = Utils.cloneObject(this.stationInfo.supervisionUrl ?? Configuration.getSupervisionUrls()); - let indexUrl = 0; + const supervisionUrls = Utils.cloneObject(this.stationInfo.supervisionUrls ?? Configuration.getSupervisionUrls()); if (!Utils.isEmptyArray(supervisionUrls)) { - if (Configuration.getDistributeStationsToTenantsEqually()) { - indexUrl = this.index % supervisionUrls.length; - } else { - // Get a random url - indexUrl = Math.floor(Utils.secureRandom() * supervisionUrls.length); + let urlIndex = 0; + switch (Configuration.getSupervisionUrlDistribution()) { + case SupervisionUrlDistribution.ROUND_ROBIN: + urlIndex = (this.index - 1) % supervisionUrls.length; + break; + case SupervisionUrlDistribution.RANDOM: + // Get a random url + urlIndex = Math.floor(Utils.secureRandom() * supervisionUrls.length); + break; + case SupervisionUrlDistribution.SEQUENTIAL: + if (this.index <= supervisionUrls.length) { + urlIndex = this.index - 1; + } else { + getLogger().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}`); + urlIndex = (this.index - 1) % supervisionUrls.length; + break; } - return new URL(supervisionUrls[indexUrl]); + return new URL(supervisionUrls[urlIndex]); } return new URL(supervisionUrls as string); } @@ -957,7 +1017,7 @@ export default class ChargingStation { if (HeartBeatInterval) { return Utils.convertToInt(HeartBeatInterval.value) * 1000; } - !this.stationInfo.autoRegister && logger.warn(`${this.logPrefix()} Heartbeat interval configuration key not set, using default value: ${Constants.DEFAULT_HEARTBEAT_INTERVAL}`); + !this.stationInfo.autoRegister && getLogger().warn(`${this.logPrefix()} Heartbeat interval configuration key not set, using default value: ${Constants.DEFAULT_HEARTBEAT_INTERVAL}`); return Constants.DEFAULT_HEARTBEAT_INTERVAL; } @@ -985,7 +1045,7 @@ export default class ChargingStation { break; } this.wsConnection = new WebSocket(this.wsConnectionUrl, protocol, options); - logger.info(this.logPrefix() + ' Open OCPP connection to URL ' + this.wsConnectionUrl.toString()); + getLogger().info(this.logPrefix() + ' Open OCPP connection to URL ' + this.wsConnectionUrl.toString()); } private stopMeterValues(connectorId: number) { @@ -1001,11 +1061,11 @@ export default class ChargingStation { fs.watch(authorizationFile, (event, filename) => { if (filename && event === 'change') { try { - logger.debug(this.logPrefix() + ' Authorization file ' + authorizationFile + ' have changed, reload'); + getLogger().debug(this.logPrefix() + ' Authorization file ' + authorizationFile + ' have changed, reload'); // Initialize authorizedTags this.authorizedTags = this.getAuthorizedTags(); } catch (error) { - logger.error(this.logPrefix() + ' Authorization file monitoring error: %j', error); + getLogger().error(this.logPrefix() + ' Authorization file monitoring error: %j', error); } } }); @@ -1013,7 +1073,7 @@ export default class ChargingStation { FileUtils.handleFileException(this.logPrefix(), 'Authorization', authorizationFile, error as NodeJS.ErrnoException); } } else { - logger.info(this.logPrefix() + ' No authorization file given in template file ' + this.stationTemplateFile + '. Not monitoring changes'); + getLogger().info(this.logPrefix() + ' No authorization file given in template file ' + this.stationTemplateFile + '. Not monitoring changes'); } } @@ -1022,7 +1082,7 @@ export default class ChargingStation { fs.watch(this.stationTemplateFile, (event, filename): void => { if (filename && event === 'change') { try { - logger.debug(this.logPrefix() + ' Template file ' + this.stationTemplateFile + ' have changed, reload'); + getLogger().debug(this.logPrefix() + ' Template file ' + this.stationTemplateFile + ' have changed, reload'); // Initialize this.initialize(); // Restart the ATG @@ -1038,7 +1098,7 @@ export default class ChargingStation { } // FIXME?: restart heartbeat and WebSocket ping when their interval values have changed } catch (error) { - logger.error(this.logPrefix() + ' Charging station template file monitoring error: %j', error); + getLogger().error(this.logPrefix() + ' Charging station template file monitoring error: %j', error); } } }); @@ -1067,13 +1127,13 @@ export default class ChargingStation { this.autoReconnectRetryCount++; const reconnectDelay = (this.getReconnectExponentialDelay() ? Utils.exponentialDelay(this.autoReconnectRetryCount) : this.getConnectionTimeout() * 1000); const reconnectTimeout = (reconnectDelay - 100) > 0 && reconnectDelay; - logger.error(`${this.logPrefix()} WebSocket: connection retry in ${Utils.roundTo(reconnectDelay, 2)}ms, timeout ${reconnectTimeout}ms`); + getLogger().error(`${this.logPrefix()} WebSocket: connection retry in ${Utils.roundTo(reconnectDelay, 2)}ms, timeout ${reconnectTimeout}ms`); await Utils.sleep(reconnectDelay); - logger.error(this.logPrefix() + ' WebSocket: reconnecting try #' + this.autoReconnectRetryCount.toString()); + getLogger().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) { - logger.error(`${this.logPrefix()} WebSocket reconnect failure: max retries reached (${this.autoReconnectRetryCount}) or retry disabled (${this.getAutoReconnectMaxRetries()})`); + getLogger().error(`${this.logPrefix()} WebSocket reconnect failure: max retries reached (${this.autoReconnectRetryCount}) or retry disabled (${this.getAutoReconnectMaxRetries()})`); } }