-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 {
} 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';
// 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 {
return crypto
.createHash(Constants.DEFAULT_HASH_ALGORITHM)
.update(
- JSON.stringify(chargingStationInfo) +
- ChargingStationUtils.getChargingStationId(index, stationTemplate)
+ `${JSON.stringify(chargingStationInfo)}${ChargingStationUtils.getChargingStationId(
+ index,
+ stationTemplate
+ )}`
)
.digest('hex');
}
}
public static createBootNotificationRequest(
- stationInfo: ChargingStationInfo
+ stationInfo: ChargingStationInfo,
+ bootReason: BootReasonEnumType = BootReasonEnumType.PowerUp
): BootNotificationRequest {
const ocppVersion = stationInfo.ocppVersion ?? OCPPVersion.VERSION_16;
switch (ocppVersion) {
case OCPPVersion.VERSION_20:
case OCPPVersion.VERSION_201:
return {
- reason: BootReasonEnumType.PowerUp,
+ reason: bootReason,
chargingStation: {
model: stationInfo.chargePointModel,
vendorName: stationInfo.chargePointVendor,
if (!Utils.isUndefined(template[key])) {
logger.warn(
`${logPrefix} Deprecated template key '${key}' usage in file '${templateFile}'${
- logMsgToAppend && '. ' + logMsgToAppend
+ !Utils.isEmptyString(logMsgToAppend) && `. ${logMsgToAppend}`
}`
);
}
})
: '';
stationInfo.chargePointSerialNumber =
- stationTemplate?.chargePointSerialNumberPrefix &&
- stationTemplate.chargePointSerialNumberPrefix + serialNumberSuffix;
+ !Utils.isEmptyString(stationTemplate?.chargePointSerialNumberPrefix) &&
+ `${stationTemplate.chargePointSerialNumberPrefix}${serialNumberSuffix}`;
stationInfo.chargeBoxSerialNumber =
- stationTemplate?.chargeBoxSerialNumberPrefix &&
- stationTemplate.chargeBoxSerialNumberPrefix + serialNumberSuffix;
+ !Utils.isEmptyString(stationTemplate?.chargeBoxSerialNumberPrefix) &&
+ `${stationTemplate.chargeBoxSerialNumberPrefix}${serialNumberSuffix}`;
stationInfo.meterSerialNumber =
- stationTemplate?.meterSerialNumberPrefix &&
- stationTemplate.meterSerialNumberPrefix + serialNumberSuffix;
+ !Utils.isEmptyString(stationTemplate?.meterSerialNumberPrefix) &&
+ `${stationTemplate.meterSerialNumberPrefix}${serialNumberSuffix}`;
}
public static propagateSerialNumber(
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)
*
* @param logPrefix -
* @returns
*/
- public static getLimitFromChargingProfiles(
+ private static getLimitFromChargingProfiles(
chargingProfiles: ChargingProfile[],
logPrefix: string
): {
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;