X-Git-Url: https://git.piment-noir.org/?a=blobdiff_plain;f=src%2Fcharging-station%2FChargingStation.ts;h=b555e528f5e40ad75ee0b39aa4b98638ecec2c70;hb=1fe16c9c1f11b81be83aa2059ecefe63e524929d;hp=6900cdc3c67e2ec9a1d83da627fa99262d39c525;hpb=734d790d9983a43c20f9ffa728f18825625e8119;p=e-mobility-charging-stations-simulator.git diff --git a/src/charging-station/ChargingStation.ts b/src/charging-station/ChargingStation.ts index 6900cdc3..b555e528 100644 --- a/src/charging-station/ChargingStation.ts +++ b/src/charging-station/ChargingStation.ts @@ -37,22 +37,22 @@ import logger from '../utils/Logger'; import path from 'path'; export default class ChargingStation { - public stationTemplateFile: string; + public readonly stationTemplateFile: string; public authorizedTags: string[]; public stationInfo!: ChargingStationInfo; - public connectors: Map; + public readonly connectors: Map; public configuration!: ChargingStationConfiguration; public wsConnection!: WebSocket; - public requests: Map; + public readonly requests: Map; public performanceStatistics!: PerformanceStatistics; public heartbeatSetInterval!: NodeJS.Timeout; public ocppRequestService!: OCPPRequestService; - private index: number; + private readonly index: number; private bootNotificationRequest!: BootNotificationRequest; private bootNotificationResponse!: BootNotificationResponse | null; private connectorsConfigurationHash!: string; private ocppIncomingRequestService!: OCPPIncomingRequestService; - private messageQueue: string[]; + private readonly messageBuffer: Set; private wsConnectionUrl!: URL; private wsConnectionRestarted: boolean; private stopped: boolean; @@ -71,7 +71,7 @@ export default class ChargingStation { this.autoReconnectRetryCount = 0; this.requests = new Map(); - this.messageQueue = new Array(); + this.messageBuffer = new Set(); this.authorizedTags = this.getAuthorizedTags(); } @@ -126,6 +126,10 @@ export default class ChargingStation { return this.getConnectorStatus(id).availability === AvailabilityType.OPERATIVE; } + public getNumberOfConnectors(): number { + return this.connectors.get(0) ? this.connectors.size - 1 : this.connectors.size; + } + public getConnectorStatus(id: number): ConnectorStatus { return this.connectors.get(id); } @@ -396,9 +400,12 @@ export default class ChargingStation { !cpReplaced && this.getConnectorStatus(connectorId).chargingProfiles?.push(cp); } - public resetTransactionOnConnector(connectorId: number): void { - this.getConnectorStatus(connectorId).authorized = false; + public resetConnectorStatus(connectorId: number): void { + this.getConnectorStatus(connectorId).idTagLocalAuthorized = false; + this.getConnectorStatus(connectorId).idTagAuthorized = false; + this.getConnectorStatus(connectorId).transactionRemoteStarted = false; this.getConnectorStatus(connectorId).transactionStarted = false; + delete this.getConnectorStatus(connectorId).localAuthorizeIdTag; delete this.getConnectorStatus(connectorId).authorizeIdTag; delete this.getConnectorStatus(connectorId).transactionId; delete this.getConnectorStatus(connectorId).transactionIdTag; @@ -407,28 +414,16 @@ export default class ChargingStation { this.stopMeterValues(connectorId); } - public addToMessageQueue(message: string): void { - let dups = false; - // Handle dups in message queue - for (const bufferedMessage of this.messageQueue) { - // Message already in the queue - if (message === bufferedMessage) { - dups = true; - break; - } - } - if (!dups) { - // Queue message - this.messageQueue.push(message); - } + public bufferMessage(message: string): void { + this.messageBuffer.add(message); } - private flushMessageQueue() { - if (!Utils.isEmptyArray(this.messageQueue)) { - this.messageQueue.forEach((message, index) => { - this.messageQueue.splice(index, 1); + private flushMessageBuffer() { + if (this.messageBuffer.size > 0) { + this.messageBuffer.forEach((message) => { // TODO: evaluate the need to track performance this.wsConnection.send(message); + this.messageBuffer.delete(message); }); } } @@ -451,6 +446,9 @@ export default class ChargingStation { FileUtils.handleFileException(this.logPrefix(), 'Template', this.stationTemplateFile, error); } const stationInfo: ChargingStationInfo = stationTemplateFromFile ?? {} as ChargingStationInfo; + stationInfo.wsOptions = stationTemplateFromFile?.wsOptions ?? {}; + stationInfo.wsOptions.origin = stationTemplateFromFile?.wsOptions?.origin ?? 'http://localhost'; + stationInfo.wsOptions.handshakeTimeout = stationTemplateFromFile?.wsOptions?.handshakeTimeout ?? this.getConnectionTimeout() * 1000; if (!Utils.isEmptyArray(stationTemplateFromFile.power)) { stationTemplateFromFile.power = stationTemplateFromFile.power as number[]; const powerArrayRandomIndex = Math.floor(Utils.secureRandom() * stationTemplateFromFile.power.length); @@ -508,8 +506,8 @@ export default class ChargingStation { this.stationInfo.randomConnectors = true; } const connectorsConfigHash = crypto.createHash('sha256').update(JSON.stringify(this.stationInfo.Connectors) + maxConnectors.toString()).digest('hex'); - const connectorsConfigChanged = this.connectors.size !== 0 && this.connectorsConfigurationHash !== connectorsConfigHash; - if (!this.connectors || this.connectors.size === 0 || connectorsConfigChanged) { + const connectorsConfigChanged = this.connectors?.size !== 0 && this.connectorsConfigurationHash !== connectorsConfigHash; + if (this.connectors?.size === 0 || connectorsConfigChanged) { connectorsConfigChanged && (this.connectors.clear()); this.connectorsConfigurationHash = connectorsConfigHash; // Add connector Id 0 @@ -541,7 +539,7 @@ export default class ChargingStation { // Initialize transaction attributes on connectors for (const connectorId of this.connectors.keys()) { if (connectorId > 0 && !this.getConnectorStatus(connectorId)?.transactionStarted) { - this.initTransactionAttributesOnConnector(connectorId); + this.initializeConnectorStatus(connectorId); } } switch (this.getOCPPVersion()) { @@ -623,7 +621,7 @@ export default class ChargingStation { await this.startMessageSequence(); this.stopped && (this.stopped = false); if (this.wsConnectionRestarted && this.isWebSocketConnectionOpened()) { - this.flushMessageQueue(); + this.flushMessageBuffer(); } } else { logger.error(`${this.logPrefix()} Registration failure: max retries reached (${this.getRegistrationMaxRetries()}) or retry disabled (${this.getRegistrationMaxRetries()})`); @@ -651,7 +649,7 @@ 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 rejectCallback: (error: OCPPError) => void; + let rejectCallback: (error: OCPPError, requestStatistic?: boolean) => void; let requestCommandName: RequestCommand | IncomingRequestCommand; let requestPayload: Record; let cachedRequest: CachedRequest; @@ -711,7 +709,7 @@ export default class ChargingStation { } } catch (error) { // Log - logger.error('%s Incoming OCPP message %j matching cached request %j processing error %j', this.logPrefix(), data, 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, commandName); } @@ -827,10 +825,6 @@ export default class ChargingStation { return maxConnectors; } - private getNumberOfConnectors(): number { - return this.connectors.get(0) ? this.connectors.size - 1 : this.connectors.size; - } - private async startMessageSequence(): Promise { // Start WebSocket ping this.startWebSocketPing(); @@ -951,9 +945,7 @@ export default class ChargingStation { } } - private openWSConnection(options?: ClientOptions & ClientRequestArgs, forceCloseOpened = false): void { - options = options ?? {}; - options.handshakeTimeout = options?.handshakeTimeout ?? this.getConnectionTimeout() * 1000; + private openWSConnection(options: ClientOptions & ClientRequestArgs = this.stationInfo.wsOptions, forceCloseOpened = false): void { if (!Utils.isNullOrUndefined(this.stationInfo.supervisionUser) && !Utils.isNullOrUndefined(this.stationInfo.supervisionPassword)) { options.auth = `${this.stationInfo.supervisionUser}:${this.stationInfo.supervisionPassword}`; } @@ -1051,19 +1043,21 @@ export default class ChargingStation { 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; + const reconnectTimeout = (reconnectDelay - 100) > 0 ? reconnectDelay : 0; logger.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()); - this.openWSConnection({ handshakeTimeout: reconnectTimeout }, true); + 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()})`); } } - private initTransactionAttributesOnConnector(connectorId: number): void { - this.getConnectorStatus(connectorId).authorized = false; + private initializeConnectorStatus(connectorId: number): void { + this.getConnectorStatus(connectorId).idTagLocalAuthorized = false; + this.getConnectorStatus(connectorId).idTagAuthorized = false; + this.getConnectorStatus(connectorId).transactionRemoteStarted = false; this.getConnectorStatus(connectorId).transactionStarted = false; this.getConnectorStatus(connectorId).energyActiveImportRegisterValue = 0; this.getConnectorStatus(connectorId).transactionEnergyActiveImportRegisterValue = 0;