X-Git-Url: https://git.piment-noir.org/?a=blobdiff_plain;f=src%2Fcharging-station%2FChargingStationUtils.ts;h=9f93e5ffe0c8be18c33d26604f3d96af65be7682;hb=52952bf8f73e5ad1a85c0c9205412a9198bfc720;hp=0138d7850a5a15b4936167fdcb56a1e27b0068d1;hpb=ae5020a33e77e4fb452a5d2f54c59bf9837e5190;p=e-mobility-charging-stations-simulator.git diff --git a/src/charging-station/ChargingStationUtils.ts b/src/charging-station/ChargingStationUtils.ts index 0138d785..9f93e5ff 100644 --- a/src/charging-station/ChargingStationUtils.ts +++ b/src/charging-station/ChargingStationUtils.ts @@ -9,6 +9,7 @@ import type { ChargingStation } from './internal'; import { BaseError } from '../exception'; import { AmpereUnits, + AvailabilityType, type BootNotificationRequest, BootReasonEnumType, type ChargingProfile, @@ -17,7 +18,9 @@ import { type ChargingSchedulePeriod, type ChargingStationInfo, type ChargingStationTemplate, + type ConnectorStatus, CurrentType, + type EvseTemplate, type OCPP16BootNotificationRequest, type OCPP20BootNotificationRequest, OCPPVersion, @@ -66,12 +69,6 @@ export class ChargingStationUtils { ...(!Utils.isUndefined(stationTemplate.chargePointSerialNumberPrefix) && { chargePointSerialNumber: stationTemplate.chargePointSerialNumberPrefix, }), - // FIXME?: Should a firmware version change always reference a new configuration file? - ...(!Utils.isUndefined(stationTemplate.firmwareVersion) && { - firmwareVersion: stationTemplate.firmwareVersion, - }), - ...(!Utils.isUndefined(stationTemplate.iccid) && { iccid: stationTemplate.iccid }), - ...(!Utils.isUndefined(stationTemplate.imsi) && { imsi: stationTemplate.imsi }), ...(!Utils.isUndefined(stationTemplate.meterSerialNumberPrefix) && { meterSerialNumber: stationTemplate.meterSerialNumberPrefix, }), @@ -98,12 +95,18 @@ export class ChargingStationUtils { return true; } - public static getTemplateMaxNumberOfConnectors(stationTemplate: ChargingStationTemplate): number { - const templateConnectors = stationTemplate?.Connectors; - if (!templateConnectors) { + public static getMaxNumberOfEvses(evses: Record): number { + if (!evses) { + return -1; + } + return Object.keys(evses).length; + } + + public static getMaxNumberOfConnectors(connectors: Record): number { + if (!connectors) { return -1; } - return Object.keys(templateConnectors).length; + return Object.keys(connectors).length; } public static checkTemplateMaxConnectors( @@ -122,18 +125,28 @@ export class ChargingStationUtils { } } - public static getConfiguredNumberOfConnectors(stationTemplate: ChargingStationTemplate): number { + public static getConfiguredNumberOfConnectors(stationInfo: ChargingStationInfo): number { let configuredMaxConnectors: number; - if (Utils.isNotEmptyArray(stationTemplate.numberOfConnectors) === true) { - const numberOfConnectors = stationTemplate.numberOfConnectors as number[]; + if (Utils.isNotEmptyArray(stationInfo.numberOfConnectors) === true) { + const numberOfConnectors = stationInfo.numberOfConnectors as number[]; configuredMaxConnectors = numberOfConnectors[Math.floor(Utils.secureRandom() * numberOfConnectors.length)]; - } else if (Utils.isUndefined(stationTemplate.numberOfConnectors) === false) { - configuredMaxConnectors = stationTemplate.numberOfConnectors as number; - } else { - configuredMaxConnectors = stationTemplate?.Connectors[0] - ? ChargingStationUtils.getTemplateMaxNumberOfConnectors(stationTemplate) - 1 - : ChargingStationUtils.getTemplateMaxNumberOfConnectors(stationTemplate); + } else if (Utils.isUndefined(stationInfo.numberOfConnectors) === false) { + configuredMaxConnectors = stationInfo.numberOfConnectors as number; + } else if (stationInfo.Connectors && !stationInfo.Evses) { + configuredMaxConnectors = stationInfo?.Connectors[0] + ? ChargingStationUtils.getMaxNumberOfConnectors(stationInfo.Connectors) - 1 + : ChargingStationUtils.getMaxNumberOfConnectors(stationInfo.Connectors); + } else if (stationInfo.Evses && !stationInfo.Connectors) { + configuredMaxConnectors = 0; + for (const evse in stationInfo.Evses) { + if (evse === '0') { + continue; + } + configuredMaxConnectors += ChargingStationUtils.getMaxNumberOfConnectors( + stationInfo.Evses[evse].Connectors + ); + } } return configuredMaxConnectors; } @@ -150,6 +163,88 @@ export class ChargingStationUtils { } } + public static checkStationInfoConnectorStatus( + connectorId: number, + connectorStatus: ConnectorStatus, + logPrefix: string, + templateFile: string + ): void { + if (!Utils.isNullOrUndefined(connectorStatus?.status)) { + logger.warn( + `${logPrefix} Charging station information from template ${templateFile} with connector ${connectorId} status configuration defined, undefine it` + ); + delete connectorStatus.status; + } + } + + public static buildConnectorsMap( + connectors: Record, + logPrefix: string, + templateFile: string + ): Map { + const connectorsMap = new Map(); + if (ChargingStationUtils.getMaxNumberOfConnectors(connectors) > 0) { + for (const connector in connectors) { + const connectorStatus = connectors[connector]; + const connectorId = Utils.convertToInt(connector); + ChargingStationUtils.checkStationInfoConnectorStatus( + connectorId, + connectorStatus, + logPrefix, + templateFile + ); + connectorsMap.set(connectorId, Utils.cloneObject(connectorStatus)); + } + } else { + logger.warn( + `${logPrefix} Charging station information from template ${templateFile} with no connectors, cannot build connectors map` + ); + } + return connectorsMap; + } + + public static initializeConnectorsMapStatus( + connectors: Map, + logPrefix: string + ): void { + for (const connectorId of connectors.keys()) { + if (connectorId > 0 && connectors.get(connectorId)?.transactionStarted === true) { + logger.warn( + `${logPrefix} Connector ${connectorId} at initialization has a transaction started: ${ + connectors.get(connectorId)?.transactionId + }` + ); + } + if ( + connectorId === 0 && + Utils.isNullOrUndefined(connectors.get(connectorId)?.transactionStarted) + ) { + connectors.get(connectorId).availability = AvailabilityType.Operative; + if (Utils.isUndefined(connectors.get(connectorId)?.chargingProfiles)) { + connectors.get(connectorId).chargingProfiles = []; + } + } else if ( + connectorId > 0 && + Utils.isNullOrUndefined(connectors.get(connectorId)?.transactionStarted) + ) { + ChargingStationUtils.initializeConnectorStatus(connectors.get(connectorId)); + } + } + } + + public static resetConnectorStatus(connectorStatus: ConnectorStatus): void { + connectorStatus.idTagLocalAuthorized = false; + connectorStatus.idTagAuthorized = false; + connectorStatus.transactionRemoteStarted = false; + connectorStatus.transactionStarted = false; + delete connectorStatus?.localAuthorizeIdTag; + delete connectorStatus?.authorizeIdTag; + delete connectorStatus?.transactionId; + delete connectorStatus?.transactionIdTag; + connectorStatus.transactionEnergyActiveImportRegisterValue = 0; + delete connectorStatus?.transactionBeginMeterValue; + } + public static createBootNotificationRequest( stationInfo: ChargingStationInfo, bootReason: BootReasonEnumType = BootReasonEnumType.PowerUp @@ -335,17 +430,17 @@ export class ChargingStationUtils { 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); + const chargingProfiles = + Utils.cloneObject(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) + ...Utils.cloneObject(chargingStation.getConnectorStatus(0).chargingProfiles).sort( + (a, b) => b.stackLevel - a.stackLevel + ) ); } if (Utils.isNotEmptyArray(chargingProfiles)) { @@ -423,6 +518,19 @@ export class ChargingStationUtils { ); } + private static initializeConnectorStatus(connectorStatus: ConnectorStatus): void { + connectorStatus.availability = AvailabilityType.Operative; + connectorStatus.idTagLocalAuthorized = false; + connectorStatus.idTagAuthorized = false; + connectorStatus.transactionRemoteStarted = false; + connectorStatus.transactionStarted = false; + connectorStatus.energyActiveImportRegisterValue = 0; + connectorStatus.transactionEnergyActiveImportRegisterValue = 0; + if (Utils.isUndefined(connectorStatus.chargingProfiles)) { + connectorStatus.chargingProfiles = []; + } + } + private static warnDeprecatedTemplateKey( template: ChargingStationTemplate, key: string, @@ -465,10 +573,16 @@ export class ChargingStationUtils { matchingChargingProfile: ChargingProfile; } | null { const debugLogMsg = `${logPrefix} ${moduleName}.getLimitFromChargingProfiles: Matching charging profile found for power limitation: %j`; + const currentMoment = moment(); + const currentDate = new Date(); for (const chargingProfile of chargingProfiles) { // Set helpers - const currentMoment = moment(); const chargingSchedule = chargingProfile.chargingSchedule; + if (!chargingSchedule?.startSchedule) { + logger.warn( + `${logPrefix} ${moduleName}.getLimitFromChargingProfiles: startSchedule is not defined in charging profile id ${chargingProfile.chargingProfileId}` + ); + } // Check type (recurring) and if it is already active // Adjust the daily recurring schedule to today if ( @@ -476,8 +590,12 @@ export class ChargingStationUtils { chargingProfile.recurrencyKind === RecurrencyKindType.DAILY && currentMoment.isAfter(chargingSchedule.startSchedule) ) { - const currentDate = new Date(); - chargingSchedule.startSchedule = new Date(chargingSchedule.startSchedule); + if (!(chargingSchedule?.startSchedule instanceof Date)) { + logger.warn( + `${logPrefix} ${moduleName}.getLimitFromChargingProfiles: startSchedule is not a Date object in charging profile id ${chargingProfile.chargingProfileId}. Trying to convert it to a Date object` + ); + chargingSchedule.startSchedule = new Date(chargingSchedule.startSchedule); + } chargingSchedule.startSchedule.setFullYear( currentDate.getFullYear(), currentDate.getMonth(),