X-Git-Url: https://git.piment-noir.org/?a=blobdiff_plain;f=src%2Fcharging-station%2FChargingStationUtils.ts;h=c10a3a15b229579523ad92fbf5efb679a8cf5961;hb=44eb6026079c8dc2c77b10a96a42d0c0b2da7c8f;hp=540ec4ffa36bfef0dccbdb45afc2a8ca1e6a0769;hpb=d270cc878c61c42098557a0e03cc1620f74112de;p=e-mobility-charging-stations-simulator.git diff --git a/src/charging-station/ChargingStationUtils.ts b/src/charging-station/ChargingStationUtils.ts index 540ec4ff..c10a3a15 100644 --- a/src/charging-station/ChargingStationUtils.ts +++ b/src/charging-station/ChargingStationUtils.ts @@ -1,9 +1,10 @@ -import crypto from 'crypto'; +import crypto from 'node:crypto'; import path from 'path'; import { fileURLToPath } from 'url'; import moment from 'moment'; +import type ChargingStation from './ChargingStation'; import BaseError from '../exception/BaseError'; import type { ChargingStationInfo } from '../types/ChargingStationInfo'; import { @@ -14,13 +15,18 @@ import { } from '../types/ChargingStationTemplate'; import { ChargingProfileKindType, RecurrencyKindType } from '../types/ocpp/1.6/ChargingProfile'; import type { OCPP16BootNotificationRequest } from '../types/ocpp/1.6/Requests'; -import { BootReasonEnumType, OCPP20BootNotificationRequest } from '../types/ocpp/2.0/Requests'; -import type { ChargingProfile, ChargingSchedulePeriod } from '../types/ocpp/ChargingProfile'; +import { BootReasonEnumType, type OCPP20BootNotificationRequest } from '../types/ocpp/2.0/Requests'; +import { + type ChargingProfile, + ChargingRateUnitType, + type ChargingSchedulePeriod, +} from '../types/ocpp/ChargingProfile'; import { OCPPVersion } from '../types/ocpp/OCPPVersion'; import type { BootNotificationRequest } from '../types/ocpp/Requests'; import { WorkerProcessType } from '../types/Worker'; import Configuration from '../utils/Configuration'; import Constants from '../utils/Constants'; +import { ACElectricUtils, DCElectricUtils } from '../utils/ElectricUtils'; import logger from '../utils/Logger'; import Utils from '../utils/Utils'; @@ -38,18 +44,16 @@ export class ChargingStationUtils { // In case of multiple instances: add instance index to charging station id const instanceIndex = process.env.CF_INSTANCE_INDEX ?? 0; const idSuffix = stationTemplate.nameSuffix ?? ''; - const idStr = '000000000' + index.toString(); + const idStr = `000000000${index.toString()}`; return stationTemplate?.fixedName ? stationTemplate.baseName - : stationTemplate.baseName + - '-' + - instanceIndex.toString() + - idStr.substring(idStr.length - 4) + - idSuffix; + : `${stationTemplate.baseName}-${instanceIndex.toString()}${idStr.substring( + idStr.length - 4 + )}${idSuffix}`; } public static getHashId(index: number, stationTemplate: ChargingStationTemplate): string { - const hashBootNotificationRequest = { + const chargingStationInfo = { chargePointModel: stationTemplate.chargePointModel, chargePointVendor: stationTemplate.chargePointVendor, ...(!Utils.isUndefined(stationTemplate.chargeBoxSerialNumberPrefix) && { @@ -74,7 +78,7 @@ export class ChargingStationUtils { return crypto .createHash(Constants.DEFAULT_HASH_ALGORITHM) .update( - JSON.stringify(hashBootNotificationRequest) + + JSON.stringify(chargingStationInfo) + ChargingStationUtils.getChargingStationId(index, stationTemplate) ) .digest('hex'); @@ -133,7 +137,8 @@ export class ChargingStationUtils { } public static createBootNotificationRequest( - stationInfo: ChargingStationInfo + stationInfo: ChargingStationInfo, + bootReason: BootReasonEnumType = BootReasonEnumType.PowerUp ): BootNotificationRequest { const ocppVersion = stationInfo.ocppVersion ?? OCPPVersion.VERSION_16; switch (ocppVersion) { @@ -162,7 +167,7 @@ export class ChargingStationUtils { case OCPPVersion.VERSION_20: case OCPPVersion.VERSION_201: return { - reason: BootReasonEnumType.PowerUp, + reason: bootReason, chargingStation: { model: stationInfo.chargePointModel, vendorName: stationInfo.chargePointVendor, @@ -172,10 +177,12 @@ export class ChargingStationUtils { ...(!Utils.isUndefined(stationInfo.chargeBoxSerialNumber) && { serialNumber: stationInfo.chargeBoxSerialNumber, }), - modem: { - ...(!Utils.isUndefined(stationInfo.iccid) && { iccid: stationInfo.iccid }), - ...(!Utils.isUndefined(stationInfo.imsi) && { imsi: stationInfo.imsi }), - }, + ...((!Utils.isUndefined(stationInfo.iccid) || !Utils.isUndefined(stationInfo.imsi)) && { + modem: { + ...(!Utils.isUndefined(stationInfo.iccid) && { iccid: stationInfo.iccid }), + ...(!Utils.isUndefined(stationInfo.imsi) && { imsi: stationInfo.imsi }), + }, + }), }, } as OCPP20BootNotificationRequest; } @@ -201,7 +208,7 @@ export class ChargingStationUtils { if (!Utils.isUndefined(template[key])) { logger.warn( `${logPrefix} Deprecated template key '${key}' usage in file '${templateFile}'${ - logMsgToAppend && '. ' + logMsgToAppend + logMsgToAppend && `. ${logMsgToAppend}` }` ); } @@ -242,7 +249,7 @@ export class ChargingStationUtils { public static createSerialNumber( stationTemplate: ChargingStationTemplate, - stationInfo: ChargingStationInfo = {} as ChargingStationInfo, + stationInfo: ChargingStationInfo, params: { randomSerialNumberUpperCase?: boolean; randomSerialNumber?: boolean; @@ -273,7 +280,7 @@ export class ChargingStationUtils { public static propagateSerialNumber( stationTemplate: ChargingStationTemplate, stationInfoSrc: ChargingStationInfo, - stationInfoDst: ChargingStationInfo = {} as ChargingStationInfo + stationInfoDst: ChargingStationInfo ) { if (!stationInfoSrc || !stationTemplate) { throw new BaseError( @@ -307,6 +314,99 @@ export class ChargingStationUtils { return unitDivider; } + public static getChargingStationConnectorChargingProfilesPowerLimit( + chargingStation: ChargingStation, + connectorId: number + ): number | undefined { + let limit: number, matchingChargingProfile: ChargingProfile; + let chargingProfiles: ChargingProfile[] = []; + // Get charging profiles for connector and sort by stack level + chargingProfiles = chargingStation + .getConnectorStatus(connectorId) + .chargingProfiles.sort((a, b) => b.stackLevel - a.stackLevel); + // Get profiles on connector 0 + if (chargingStation.getConnectorStatus(0).chargingProfiles) { + chargingProfiles.push( + ...chargingStation + .getConnectorStatus(0) + .chargingProfiles.sort((a, b) => b.stackLevel - a.stackLevel) + ); + } + if (!Utils.isEmptyArray(chargingProfiles)) { + const result = ChargingStationUtils.getLimitFromChargingProfiles( + chargingProfiles, + chargingStation.logPrefix() + ); + if (!Utils.isNullOrUndefined(result)) { + limit = result.limit; + matchingChargingProfile = result.matchingChargingProfile; + switch (chargingStation.getCurrentOutType()) { + case CurrentType.AC: + limit = + matchingChargingProfile.chargingSchedule.chargingRateUnit === + ChargingRateUnitType.WATT + ? limit + : ACElectricUtils.powerTotal( + chargingStation.getNumberOfPhases(), + chargingStation.getVoltageOut(), + limit + ); + break; + case CurrentType.DC: + limit = + matchingChargingProfile.chargingSchedule.chargingRateUnit === + ChargingRateUnitType.WATT + ? limit + : DCElectricUtils.power(chargingStation.getVoltageOut(), limit); + } + const connectorMaximumPower = + chargingStation.getMaximumPower() / chargingStation.powerDivider; + if (limit > connectorMaximumPower) { + logger.error( + `${chargingStation.logPrefix()} Charging profile id ${ + matchingChargingProfile.chargingProfileId + } limit ${limit} is greater than connector id ${connectorId} maximum ${connectorMaximumPower}: %j`, + result + ); + limit = connectorMaximumPower; + } + } + } + return limit; + } + + public static getDefaultVoltageOut( + currentType: CurrentType, + templateFile: string, + logPrefix: string + ): Voltage { + const errMsg = `Unknown ${currentType} currentOutType in template file ${templateFile}, cannot define default voltage out`; + let defaultVoltageOut: number; + switch (currentType) { + case CurrentType.AC: + defaultVoltageOut = Voltage.VOLTAGE_230; + break; + case CurrentType.DC: + defaultVoltageOut = Voltage.VOLTAGE_400; + break; + default: + logger.error(`${logPrefix} ${errMsg}`); + throw new BaseError(errMsg); + } + return defaultVoltageOut; + } + + public static getAuthorizationFile(stationInfo: ChargingStationInfo): string | undefined { + return ( + stationInfo.authorizationFile && + path.join( + path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../'), + 'assets', + path.basename(stationInfo.authorizationFile) + ) + ); + } + /** * Charging profiles should already be sorted by connectorId and stack level (highest stack level has priority) * @@ -314,7 +414,7 @@ export class ChargingStationUtils { * @param logPrefix - * @returns */ - public static getLimitFromChargingProfiles( + private static getLimitFromChargingProfiles( chargingProfiles: ChargingProfile[], logPrefix: string ): { @@ -404,38 +504,6 @@ export class ChargingStationUtils { return null; } - public static getDefaultVoltageOut( - currentType: CurrentType, - templateFile: string, - logPrefix: string - ): Voltage { - const errMsg = `Unknown ${currentType} currentOutType in template file ${templateFile}, cannot define default voltage out`; - let defaultVoltageOut: number; - switch (currentType) { - case CurrentType.AC: - defaultVoltageOut = Voltage.VOLTAGE_230; - break; - case CurrentType.DC: - defaultVoltageOut = Voltage.VOLTAGE_400; - break; - default: - logger.error(`${logPrefix} ${errMsg}`); - throw new BaseError(errMsg); - } - return defaultVoltageOut; - } - - public static getAuthorizationFile(stationInfo: ChargingStationInfo): string | undefined { - return ( - stationInfo.authorizationFile && - path.join( - path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../'), - 'assets', - path.basename(stationInfo.authorizationFile) - ) - ); - } - private static getRandomSerialNumberSuffix(params?: { randomBytesLength?: number; upperCase?: boolean;