X-Git-Url: https://git.piment-noir.org/?a=blobdiff_plain;f=src%2Fcharging-station%2FChargingStation.ts;h=7fdc9a952e140372c44836e7b5f12107441e2805;hb=44eb6026079c8dc2c77b10a96a42d0c0b2da7c8f;hp=a52ebacb88eeb91a62e1d3ddf91d70b3a519f40b;hpb=ad67a158ed1e333a255b49e0c8b4aaa9c7b85867;p=e-mobility-charging-stations-simulator.git diff --git a/src/charging-station/ChargingStation.ts b/src/charging-station/ChargingStation.ts index a52ebacb..7fdc9a95 100644 --- a/src/charging-station/ChargingStation.ts +++ b/src/charging-station/ChargingStation.ts @@ -1,7 +1,7 @@ // Partial Copyright Jerome Benoit. 2021-2023. All Rights Reserved. -import crypto from 'crypto'; import fs from 'fs'; +import crypto from 'node:crypto'; import path from 'path'; import { URL } from 'url'; import { parentPort } from 'worker_threads'; @@ -43,7 +43,6 @@ import { SupervisionUrlDistribution } from '../types/ConfigurationData'; import type { ConnectorStatus } from '../types/ConnectorStatus'; import { FileType } from '../types/FileType'; import type { JsonType } from '../types/JsonType'; -import { ChargingProfile, ChargingRateUnitType } from '../types/ocpp/ChargingProfile'; import { ConnectorPhaseRotation, StandardParametersKey, @@ -66,6 +65,7 @@ import { type IncomingRequest, IncomingRequestCommand, type MeterValuesRequest, + type OutgoingRequest, RequestCommand, type ResponseCallback, type StatusNotificationRequest, @@ -146,14 +146,14 @@ export default class ChargingStation { private get wsConnectionUrl(): URL { return new URL( - (this.getSupervisionUrlOcppConfiguration() - ? ChargingStationConfigurationUtils.getConfigurationKey( - this, - this.getSupervisionUrlOcppKey() - ).value - : this.configuredSupervisionUrl.href) + - '/' + - this.stationInfo.chargingStationId + `${ + this.getSupervisionUrlOcppConfiguration() + ? ChargingStationConfigurationUtils.getConfigurationKey( + this, + this.getSupervisionUrlOcppKey() + ).value + : this.configuredSupervisionUrl.href + }/${this.stationInfo.chargingStationId}` ); } @@ -265,6 +265,11 @@ export default class ChargingStation { : defaultVoltageOut; } + public getMaximumPower(stationInfo?: ChargingStationInfo): number { + const localStationInfo = stationInfo ?? this.stationInfo; + return (localStationInfo['maxPower'] as number) ?? localStationInfo.maximumPower; + } + public getConnectorMaximumAvailablePower(connectorId: number): number { let connectorAmperageLimitationPowerLimit: number; if ( @@ -282,13 +287,14 @@ export default class ChargingStation { this.powerDivider; } const connectorMaximumPower = this.getMaximumPower() / this.powerDivider; - const connectorChargingProfilePowerLimit = this.getChargingProfilePowerLimit(connectorId); + const connectorChargingProfilesPowerLimit = + ChargingStationUtils.getChargingStationConnectorChargingProfilesPowerLimit(this, connectorId); return Math.min( isNaN(connectorMaximumPower) ? Infinity : connectorMaximumPower, isNaN(connectorAmperageLimitationPowerLimit) ? Infinity : connectorAmperageLimitationPowerLimit, - isNaN(connectorChargingProfilePowerLimit) ? Infinity : connectorChargingProfilePowerLimit + isNaN(connectorChargingProfilesPowerLimit) ? Infinity : connectorChargingProfilesPowerLimit ); } @@ -341,16 +347,16 @@ export default class ChargingStation { public getEnergyActiveImportRegisterByTransactionId( transactionId: number, - meterStop = false + rounded = false ): number { return this.getEnergyActiveImportRegister( this.getConnectorStatus(this.getConnectorIdByTransactionId(transactionId)), - meterStop + rounded ); } - public getEnergyActiveImportRegisterByConnectorId(connectorId: number): number { - return this.getEnergyActiveImportRegister(this.getConnectorStatus(connectorId)); + public getEnergyActiveImportRegisterByConnectorId(connectorId: number, rounded = false): number { + return this.getEnergyActiveImportRegister(this.getConnectorStatus(connectorId), rounded); } public getAuthorizeRemoteTxRequests(): boolean { @@ -388,15 +394,15 @@ export default class ChargingStation { }); }, this.getHeartbeatInterval()); logger.info( - this.logPrefix() + - ' Heartbeat started every ' + - Utils.formatDurationMilliSeconds(this.getHeartbeatInterval()) + `${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()) + `${this.logPrefix()} Heartbeat already started every ${Utils.formatDurationMilliSeconds( + this.getHeartbeatInterval() + )}` ); } else { logger.error( @@ -642,7 +648,7 @@ export default class ChargingStation { case OCPPVersion.VERSION_16: case OCPPVersion.VERSION_20: case OCPPVersion.VERSION_201: - protocol = 'ocpp' + ocppVersion; + protocol = `ocpp${ocppVersion}`; break; default: this.handleUnsupportedVersion(ocppVersion); @@ -762,8 +768,21 @@ export default class ChargingStation { private flushMessageBuffer(): void { if (this.messageBuffer.size > 0) { this.messageBuffer.forEach((message) => { - // TODO: evaluate the need to track performance + let beginId: string; + let commandName: RequestCommand; + const [messageType] = JSON.parse(message) as OutgoingRequest | Response | ErrorResponse; + const isRequest = messageType === MessageType.CALL_MESSAGE; + if (isRequest) { + [, , commandName] = JSON.parse(message) as OutgoingRequest; + beginId = PerformanceStatistics.beginMeasure(commandName); + } this.wsConnection.send(message); + isRequest && PerformanceStatistics.endMeasure(commandName, beginId); + logger.debug( + `${this.logPrefix()} >> Buffered ${OCPPServiceUtils.getMessageTypeString( + messageType + )} payload sent: ${message}` + ); this.messageBuffer.delete(message); }); } @@ -958,7 +977,7 @@ export default class ChargingStation { private initialize(): void { this.configurationFile = path.join( path.dirname(this.templateFile.replace('station-templates', 'configurations')), - ChargingStationUtils.getHashId(this.index, this.getTemplateFromFile()) + '.json' + `${ChargingStationUtils.getHashId(this.index, this.getTemplateFromFile())}.json` ); this.stationInfo = this.getStationInfo(); this.saveStationInfo(); @@ -1563,7 +1582,7 @@ export default class ChargingStation { logger.debug( `${this.logPrefix()} << Command '${ requestCommandName ?? Constants.UNKNOWN_COMMAND - }' received error payload: ${JSON.stringify(request)}` + }' received error response payload: ${JSON.stringify(request)}` ); errorCallback(new OCPPError(errorType, errorMessage, requestCommandName, errorDetails)); break; @@ -1625,31 +1644,28 @@ export default class ChargingStation { } private onPing(): void { - logger.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 { - logger.debug(this.logPrefix() + ' Received a WS pong (rfc6455) from the server'); + logger.debug(`${this.logPrefix()} Received a WS pong (rfc6455) from the server`); } private onError(error: WSError): void { this.closeWSConnection(); - logger.error(this.logPrefix() + ' WebSocket error:', error); + logger.error(`${this.logPrefix()} WebSocket error:`, error); } - private getEnergyActiveImportRegister( - connectorStatus: ConnectorStatus, - meterStop = false - ): number { + private getEnergyActiveImportRegister(connectorStatus: ConnectorStatus, rounded = false): number { if (this.getMeteringPerTransaction() === true) { return ( - (meterStop === true + (rounded === true ? Math.round(connectorStatus?.transactionEnergyActiveImportRegisterValue) : connectorStatus?.transactionEnergyActiveImportRegisterValue) ?? 0 ); } return ( - (meterStop === true + (rounded === true ? Math.round(connectorStatus?.energyActiveImportRegisterValue) : connectorStatus?.energyActiveImportRegisterValue) ?? 0 ); @@ -1727,11 +1743,6 @@ export default class ChargingStation { return powerDivider; } - private getMaximumPower(stationInfo?: ChargingStationInfo): number { - const localStationInfo = stationInfo ?? this.stationInfo; - return (localStationInfo['maxPower'] as number) ?? localStationInfo.maximumPower; - } - private getMaximumAmperage(stationInfo: ChargingStationInfo): number | undefined { const maximumPower = this.getMaximumPower(stationInfo); switch (this.getCurrentOutType(stationInfo)) { @@ -1765,60 +1776,6 @@ export default class ChargingStation { } } - private getChargingProfilePowerLimit(connectorId: number): number | undefined { - let limit: number, matchingChargingProfile: ChargingProfile; - let chargingProfiles: ChargingProfile[] = []; - // Get charging profiles for connector and sort by stack level - chargingProfiles = this.getConnectorStatus(connectorId).chargingProfiles.sort( - (a, b) => b.stackLevel - a.stackLevel - ); - // Get profiles on connector 0 - if (this.getConnectorStatus(0).chargingProfiles) { - chargingProfiles.push( - ...this.getConnectorStatus(0).chargingProfiles.sort((a, b) => b.stackLevel - a.stackLevel) - ); - } - if (!Utils.isEmptyArray(chargingProfiles)) { - const result = ChargingStationUtils.getLimitFromChargingProfiles( - chargingProfiles, - this.logPrefix() - ); - if (!Utils.isNullOrUndefined(result)) { - limit = result.limit; - matchingChargingProfile = result.matchingChargingProfile; - switch (this.getCurrentOutType()) { - case CurrentType.AC: - limit = - matchingChargingProfile.chargingSchedule.chargingRateUnit === - ChargingRateUnitType.WATT - ? limit - : ACElectricUtils.powerTotal(this.getNumberOfPhases(), this.getVoltageOut(), limit); - break; - case CurrentType.DC: - limit = - matchingChargingProfile.chargingSchedule.chargingRateUnit === - ChargingRateUnitType.WATT - ? limit - : DCElectricUtils.power(this.getVoltageOut(), limit); - } - const connectorMaximumPower = this.getMaximumPower() / this.powerDivider; - if (limit > connectorMaximumPower) { - logger.error( - `${this.logPrefix()} Charging profile id ${ - matchingChargingProfile.chargingProfileId - } limit ${limit} is greater than connector id ${connectorId} maximum ${connectorMaximumPower}: %j`, - this.getConnectorStatus(connectorId).chargingProfiles.find( - (chargingProfile) => - chargingProfile.chargingProfileId === matchingChargingProfile.chargingProfileId - ) - ); - limit = connectorMaximumPower; - } - } - } - return limit; - } - private async startMessageSequence(): Promise { if (this.stationInfo?.autoRegister === true) { await this.ocppRequestService.requestHandler< @@ -1934,15 +1891,15 @@ export default class ChargingStation { } }, webSocketPingInterval * 1000); logger.info( - this.logPrefix() + - ' WebSocket ping started every ' + - Utils.formatDurationSeconds(webSocketPingInterval) + `${this.logPrefix()} WebSocket ping started every ${Utils.formatDurationSeconds( + webSocketPingInterval + )}` ); } else if (this.webSocketPingSetInterval) { logger.info( - this.logPrefix() + - ' WebSocket ping already started every ' + - Utils.formatDurationSeconds(webSocketPingInterval) + `${this.logPrefix()} WebSocket ping already started every ${Utils.formatDurationSeconds( + webSocketPingInterval + )}` ); } else { logger.error( @@ -2070,7 +2027,7 @@ export default class ChargingStation { ); await Utils.sleep(reconnectDelay); logger.error( - this.logPrefix() + ' WebSocket connection retry #' + this.autoReconnectRetryCount.toString() + `${this.logPrefix()} WebSocket connection retry #${this.autoReconnectRetryCount.toString()}` ); this.openWSConnection( { ...(this.stationInfo?.wsOptions ?? {}), handshakeTimeout: reconnectTimeout },