import WebSocket, { Data, OPEN, RawData } from 'ws';
import AutomaticTransactionGenerator from './AutomaticTransactionGenerator';
+import { AutomaticTransactionGeneratorConfiguration } from '../types/AutomaticTransactionGenerator';
import BaseError from '../exception/BaseError';
import { ChargePointErrorCode } from '../types/ocpp/ChargePointErrorCode';
import { ChargePointStatus } from '../types/ocpp/ChargePointStatus';
public heartbeatSetInterval!: NodeJS.Timeout;
public ocppRequestService!: OCPPRequestService;
public bootNotificationResponse!: BootNotificationResponse | null;
+ public powerDivider!: number;
private readonly index: number;
private configurationFile!: string;
private bootNotificationRequest!: BootNotificationRequest;
private connectorsConfigurationHash!: string;
private ocppIncomingRequestService!: OCPPIncomingRequestService;
private readonly messageBuffer: Set<string>;
- private wsConfiguredConnectionUrl!: URL;
+ private configuredSupervisionUrl!: URL;
private wsConnectionRestarted: boolean;
- private stopped: boolean;
private autoReconnectRetryCount: number;
+ private stopped: boolean;
private automaticTransactionGenerator!: AutomaticTransactionGenerator;
private webSocketPingSetInterval!: NodeJS.Timeout;
this.requests = new Map<string, CachedRequest>();
this.messageBuffer = new Set<string>();
this.initialize();
- this.authorizedTags = this.getAuthorizedTags();
}
private get wsConnectionUrl(): URL {
- return this.getSupervisionUrlOcppConfiguration()
- ? new URL(
- ChargingStationConfigurationUtils.getConfigurationKey(
+ return new URL(
+ (this.getSupervisionUrlOcppConfiguration()
+ ? ChargingStationConfigurationUtils.getConfigurationKey(
this,
this.getSupervisionUrlOcppKey()
- ).value +
- '/' +
- this.stationInfo.chargingStationId
- )
- : this.wsConfiguredConnectionUrl;
+ ).value
+ : this.configuredSupervisionUrl.href) +
+ '/' +
+ this.stationInfo.chargingStationId
+ );
}
public logPrefix(): string {
return this.stationInfo.mayAuthorizeAtRemoteStart ?? true;
}
- public getNumberOfPhases(): number | undefined {
- switch (this.getCurrentOutType()) {
+ public getNumberOfPhases(stationInfo?: ChargingStationInfo): number | undefined {
+ const localStationInfo: ChargingStationInfo = stationInfo ?? this.stationInfo;
+ switch (this.getCurrentOutType(stationInfo)) {
case CurrentType.AC:
- return !Utils.isUndefined(this.stationInfo.numberOfPhases)
- ? this.stationInfo.numberOfPhases
+ return !Utils.isUndefined(localStationInfo.numberOfPhases)
+ ? localStationInfo.numberOfPhases
: 3;
case CurrentType.DC:
return 0;
return this.connectors.get(id);
}
- public getCurrentOutType(): CurrentType | undefined {
- return this.stationInfo.currentOutType ?? CurrentType.AC;
+ public getCurrentOutType(stationInfo?: ChargingStationInfo): CurrentType {
+ return (stationInfo ?? this.stationInfo).currentOutType ?? CurrentType.AC;
}
public getOcppStrictCompliance(): boolean {
return this.stationInfo?.ocppStrictCompliance ?? false;
}
- public getVoltageOut(): number | undefined {
+ public getVoltageOut(stationInfo?: ChargingStationInfo): number | undefined {
const defaultVoltageOut = ChargingStationUtils.getDefaultVoltageOut(
- this.getCurrentOutType(),
+ this.getCurrentOutType(stationInfo),
this.templateFile,
this.logPrefix()
);
- return !Utils.isUndefined(this.stationInfo.voltageOut)
- ? this.stationInfo.voltageOut
+ const localStationInfo: ChargingStationInfo = stationInfo ?? this.stationInfo;
+ return !Utils.isUndefined(localStationInfo.voltageOut)
+ ? localStationInfo.voltageOut
: defaultVoltageOut;
}
this.getAmperageLimitation() * this.getNumberOfConnectors()
)
: DCElectricUtils.power(this.getVoltageOut(), this.getAmperageLimitation())) /
- this.stationInfo.powerDivider;
+ this.powerDivider;
}
- const connectorMaximumPower = this.getMaximumPower() / this.stationInfo.powerDivider;
+ const connectorMaximumPower = this.getMaximumPower() / this.powerDivider;
const connectorChargingProfilePowerLimit = this.getChargingProfilePowerLimit(connectorId);
return Math.min(
isNaN(connectorMaximumPower) ? Infinity : connectorMaximumPower,
FileUtils.watchJsonFile<string[]>(
this.logPrefix(),
FileType.Authorization,
- this.getAuthorizationFile(),
+ ChargingStationUtils.getAuthorizationFile(this.stationInfo),
this.authorizedTags
);
// Monitor charging station template file
// Initialize
this.initialize();
// Restart the ATG
- if (
- !this.stationInfo.AutomaticTransactionGenerator.enable &&
- this.automaticTransactionGenerator
- ) {
- this.automaticTransactionGenerator.stop();
- }
+ this.stopAutomaticTransactionGenerator();
this.startAutomaticTransactionGenerator();
if (this.getEnableStatistics()) {
this.performanceStatistics.restart();
public async reset(reason?: StopTransactionReason): Promise<void> {
await this.stop(reason);
await Utils.sleep(this.stationInfo.resetTime);
- this.stationInfo = this.getStationInfo();
- this.stationInfo?.Connectors && delete this.stationInfo.Connectors;
+ this.initialize();
this.start();
}
: DCElectricUtils.power(this.getVoltageOut(), limit);
}
- const connectorMaximumPower = this.getMaximumPower() / this.stationInfo.powerDivider;
+ const connectorMaximumPower = this.getMaximumPower() / this.powerDivider;
if (limit > connectorMaximumPower) {
logger.error(
`${this.logPrefix()} Charging profile id ${
}
private getStationInfoFromTemplate(): ChargingStationInfo {
- const stationInfo: ChargingStationInfo = this.getTemplateFromFile();
- if (Utils.isNullOrUndefined(stationInfo)) {
+ const stationTemplate: ChargingStationTemplate = this.getTemplateFromFile();
+ if (Utils.isNullOrUndefined(stationTemplate)) {
const errorMsg = 'Failed to read charging station template file';
logger.error(`${this.logPrefix()} ${errorMsg}`);
throw new BaseError(errorMsg);
}
- if (Utils.isEmptyObject(stationInfo)) {
+ if (Utils.isEmptyObject(stationTemplate)) {
const errorMsg = `Empty charging station information from template file ${this.templateFile}`;
logger.error(`${this.logPrefix()} ${errorMsg}`);
throw new BaseError(errorMsg);
}
- const chargingStationId = ChargingStationUtils.getChargingStationId(this.index, stationInfo);
// Deprecation template keys section
ChargingStationUtils.warnDeprecatedTemplateKey(
- stationInfo,
+ stationTemplate,
'supervisionUrl',
this.templateFile,
this.logPrefix(),
"Use 'supervisionUrls' instead"
);
ChargingStationUtils.convertDeprecatedTemplateKey(
- stationInfo,
+ stationTemplate,
'supervisionUrl',
'supervisionUrls'
);
- if (!Utils.isEmptyArray(stationInfo.power)) {
- stationInfo.power = stationInfo.power as number[];
- const powerArrayRandomIndex = Math.floor(Utils.secureRandom() * stationInfo.power.length);
+ const stationInfo: ChargingStationInfo =
+ ChargingStationUtils.stationTemplateToStationInfo(stationTemplate);
+ stationInfo.chargingStationId = ChargingStationUtils.getChargingStationId(
+ this.index,
+ stationTemplate
+ );
+ ChargingStationUtils.createSerialNumber(stationTemplate, stationInfo);
+ if (!Utils.isEmptyArray(stationTemplate.power)) {
+ stationTemplate.power = stationTemplate.power as number[];
+ const powerArrayRandomIndex = Math.floor(Utils.secureRandom() * stationTemplate.power.length);
stationInfo.maximumPower =
- stationInfo.powerUnit === PowerUnits.KILO_WATT
- ? stationInfo.power[powerArrayRandomIndex] * 1000
- : stationInfo.power[powerArrayRandomIndex];
+ stationTemplate.powerUnit === PowerUnits.KILO_WATT
+ ? stationTemplate.power[powerArrayRandomIndex] * 1000
+ : stationTemplate.power[powerArrayRandomIndex];
} else {
- stationInfo.power = stationInfo.power as number;
+ stationTemplate.power = stationTemplate.power as number;
stationInfo.maximumPower =
- stationInfo.powerUnit === PowerUnits.KILO_WATT
- ? stationInfo.power * 1000
- : stationInfo.power;
- }
- delete stationInfo.power;
- delete stationInfo.powerUnit;
- stationInfo.chargingStationId = chargingStationId;
- stationInfo.resetTime = stationInfo.resetTime
- ? stationInfo.resetTime * 1000
+ stationTemplate.powerUnit === PowerUnits.KILO_WATT
+ ? stationTemplate.power * 1000
+ : stationTemplate.power;
+ }
+ stationInfo.resetTime = stationTemplate.resetTime
+ ? stationTemplate.resetTime * 1000
: Constants.CHARGING_STATION_DEFAULT_RESET_TIME;
+ const configuredMaxConnectors = ChargingStationUtils.getConfiguredNumberOfConnectors(
+ this.index,
+ stationTemplate
+ );
+ ChargingStationUtils.checkConfiguredMaxConnectors(
+ configuredMaxConnectors,
+ this.templateFile,
+ Utils.logPrefix()
+ );
+ const templateMaxConnectors =
+ ChargingStationUtils.getTemplateMaxNumberOfConnectors(stationTemplate);
+ ChargingStationUtils.checkTemplateMaxConnectors(
+ templateMaxConnectors,
+ this.templateFile,
+ Utils.logPrefix()
+ );
+ if (
+ configuredMaxConnectors >
+ (stationTemplate?.Connectors[0] ? templateMaxConnectors - 1 : templateMaxConnectors) &&
+ !stationTemplate?.randomConnectors
+ ) {
+ logger.warn(
+ `${this.logPrefix()} Number of connectors exceeds the number of connector configurations in template ${
+ this.templateFile
+ }, forcing random connector configurations affectation`
+ );
+ stationInfo.randomConnectors = true;
+ }
+ // Build connectors if needed (FIXME: should be factored out)
+ this.initializeConnectors(stationInfo, configuredMaxConnectors, templateMaxConnectors);
+ stationInfo.maximumAmperage = this.getMaximumAmperage(stationInfo);
+ ChargingStationUtils.createStationInfoHash(stationInfo);
return stationInfo;
}
private getStationInfoFromFile(): ChargingStationInfo | null {
let stationInfo: ChargingStationInfo = null;
- if (this.getStationInfoPersistentConfiguration()) {
- stationInfo = this.getConfigurationFromFile()?.stationInfo ?? null;
- }
- if (stationInfo) {
- stationInfo = ChargingStationUtils.createStationInfoHash(stationInfo);
- }
+ this.getStationInfoPersistentConfiguration() &&
+ (stationInfo = this.getConfigurationFromFile()?.stationInfo ?? null);
+ stationInfo && ChargingStationUtils.createStationInfoHash(stationInfo);
return stationInfo;
}
private getStationInfo(): ChargingStationInfo {
const stationInfoFromTemplate: ChargingStationInfo = this.getStationInfoFromTemplate();
- this.hashId = ChargingStationUtils.getHashId(stationInfoFromTemplate);
- this.configurationFile = path.join(
- path.resolve(__dirname, '../'),
- 'assets',
- 'configurations',
- this.hashId + '.json'
- );
const stationInfoFromFile: ChargingStationInfo = this.getStationInfoFromFile();
// Priority: charging station info from template > charging station info from configuration file > charging station info attribute
if (stationInfoFromFile?.templateHash === stationInfoFromTemplate.templateHash) {
}
return stationInfoFromFile;
}
- ChargingStationUtils.createSerialNumber(stationInfoFromTemplate, stationInfoFromFile);
+ ChargingStationUtils.createSerialNumber(this.getTemplateFromFile(), stationInfoFromFile);
return stationInfoFromTemplate;
}
}
private initialize(): void {
- this.stationInfo = this.getStationInfo();
+ this.hashId = ChargingStationUtils.getHashId(this.index, this.getTemplateFromFile());
logger.info(`${this.logPrefix()} Charging station hashId '${this.hashId}'`);
- this.bootNotificationRequest = ChargingStationUtils.createBootNotificationRequest(
- this.stationInfo
- );
- this.ocppConfiguration = this.getOcppConfiguration();
- this.stationInfo?.Configuration && delete this.stationInfo.Configuration;
- this.wsConfiguredConnectionUrl = new URL(
- this.getConfiguredSupervisionUrl().href + '/' + this.stationInfo.chargingStationId
+ this.configurationFile = path.join(
+ path.resolve(__dirname, '../'),
+ 'assets',
+ 'configurations',
+ this.hashId + '.json'
);
- // Build connectors if needed
- const maxConnectors = this.getMaxNumberOfConnectors();
- this.checkMaxConnectors(maxConnectors);
- const templateMaxConnectors = this.getTemplateMaxNumberOfConnectors();
- this.checkTemplateMaxConnectors(templateMaxConnectors);
- if (
- maxConnectors >
- (this.stationInfo?.Connectors[0] ? templateMaxConnectors - 1 : templateMaxConnectors) &&
- !this.stationInfo?.randomConnectors
- ) {
- logger.warn(
- `${this.logPrefix()} Number of connectors exceeds the number of connector configurations in template ${
- this.templateFile
- }, forcing random connector configurations affectation`
- );
- this.stationInfo.randomConnectors = true;
- }
- this.initializeConnectors(this.stationInfo, maxConnectors, templateMaxConnectors);
- this.stationInfo.maximumAmperage = this.getMaximumAmperage();
- if (this.stationInfo) {
- this.stationInfo = ChargingStationUtils.createStationInfoHash(this.stationInfo);
- }
+ this.stationInfo = this.getStationInfo();
this.saveStationInfo();
// Avoid duplication of connectors related information in RAM
this.stationInfo?.Connectors && delete this.stationInfo.Connectors;
- // OCPP configuration
- this.initializeOcppConfiguration();
+ this.configuredSupervisionUrl = this.getConfiguredSupervisionUrl();
if (this.getEnableStatistics()) {
this.performanceStatistics = PerformanceStatistics.getInstance(
this.hashId,
this.stationInfo.chargingStationId,
- this.wsConnectionUrl
+ this.configuredSupervisionUrl
);
}
+ this.bootNotificationRequest = ChargingStationUtils.createBootNotificationRequest(
+ this.stationInfo
+ );
+ this.authorizedTags = ChargingStationUtils.getAuthorizedTags(
+ this.stationInfo,
+ this.templateFile,
+ this.logPrefix()
+ );
+ this.powerDivider = this.getPowerDivider();
+ // OCPP configuration
+ this.ocppConfiguration = this.getOcppConfiguration();
+ this.initializeOcppConfiguration();
switch (this.getOcppVersion()) {
case OCPPVersion.VERSION_16:
this.ocppIncomingRequestService =
status: RegistrationStatus.ACCEPTED,
};
}
- this.stationInfo.powerDivider = this.getPowerDivider();
}
private initializeOcppConfiguration(): void {
ChargingStationConfigurationUtils.addConfigurationKey(
this,
this.getSupervisionUrlOcppKey(),
- this.getConfiguredSupervisionUrl().href,
+ this.configuredSupervisionUrl.href,
{ reboot: true }
);
} else if (
private initializeConnectors(
stationInfo: ChargingStationInfo,
- maxConnectors: number,
+ configuredMaxConnectors: number,
templateMaxConnectors: number
): void {
if (!stationInfo?.Connectors && this.connectors.size === 0) {
if (stationInfo?.Connectors) {
const connectorsConfigHash = crypto
.createHash(Constants.DEFAULT_HASH_ALGORITHM)
- .update(JSON.stringify(stationInfo?.Connectors) + maxConnectors.toString())
+ .update(JSON.stringify(stationInfo?.Connectors) + configuredMaxConnectors.toString())
.digest('hex');
const connectorsConfigChanged =
this.connectors?.size !== 0 && this.connectorsConfigurationHash !== connectorsConfigHash;
const lastConnectorId = Utils.convertToInt(lastConnector);
if (
lastConnectorId === 0 &&
- this.getUseConnectorId0() &&
+ this.getUseConnectorId0(stationInfo) &&
stationInfo?.Connectors[lastConnector]
) {
this.connectors.set(
}
// Generate all connectors
if ((stationInfo?.Connectors[0] ? templateMaxConnectors - 1 : templateMaxConnectors) > 0) {
- for (let index = 1; index <= maxConnectors; index++) {
+ for (let index = 1; index <= configuredMaxConnectors; index++) {
const randConnectorId = stationInfo?.randomConnectors
? Utils.getRandomInteger(Utils.convertToInt(lastConnector), 1)
: index;
}
}
- private checkMaxConnectors(maxConnectors: number): void {
- if (maxConnectors <= 0) {
- logger.warn(
- `${this.logPrefix()} Charging station information from template ${
- this.templateFile
- } with ${maxConnectors} connectors`
- );
- }
- }
-
- private checkTemplateMaxConnectors(templateMaxConnectors: number): void {
- if (templateMaxConnectors === 0) {
- logger.warn(
- `${this.logPrefix()} Charging station information from template ${
- this.templateFile
- } with empty connectors configuration`
- );
- } else if (templateMaxConnectors < 0) {
- logger.error(
- `${this.logPrefix()} Charging station information from template ${
- this.templateFile
- } with no connectors configuration defined`
- );
- }
- }
-
private getConfigurationFromFile(): ChargingStationConfiguration | null {
let configuration: ChargingStationConfiguration = null;
if (this.configurationFile && fs.existsSync(this.configurationFile)) {
logger.error(this.logPrefix() + ' WebSocket error: %j', error);
}
- private getAuthorizationFile(): string | undefined {
- return (
- this.stationInfo.authorizationFile &&
- path.join(
- path.resolve(__dirname, '../'),
- 'assets',
- path.basename(this.stationInfo.authorizationFile)
- )
- );
- }
-
- private getAuthorizedTags(): string[] {
- let authorizedTags: string[] = [];
- const authorizationFile = this.getAuthorizationFile();
- if (authorizationFile) {
- try {
- // Load authorization file
- authorizedTags = JSON.parse(fs.readFileSync(authorizationFile, 'utf8')) as string[];
- } catch (error) {
- FileUtils.handleFileException(
- this.logPrefix(),
- FileType.Authorization,
- authorizationFile,
- error as NodeJS.ErrnoException
- );
- }
- } else {
- logger.info(
- this.logPrefix() + ' No authorization file given in template file ' + this.templateFile
- );
- }
- return authorizedTags;
- }
-
- private getUseConnectorId0(): boolean | undefined {
- return !Utils.isUndefined(this.stationInfo.useConnectorId0)
- ? this.stationInfo.useConnectorId0
+ private getUseConnectorId0(stationInfo?: ChargingStationInfo): boolean | undefined {
+ const localStationInfo = stationInfo ?? this.stationInfo;
+ return !Utils.isUndefined(localStationInfo.useConnectorId0)
+ ? localStationInfo.useConnectorId0
: true;
}
private getPowerDivider(): number {
let powerDivider = this.getNumberOfConnectors();
- if (this.stationInfo.powerSharedByConnectors) {
+ if (this.stationInfo?.powerSharedByConnectors) {
powerDivider = this.getNumberOfRunningTransactions();
}
return powerDivider;
}
- private getTemplateMaxNumberOfConnectors(): number {
- if (!this.stationInfo?.Connectors) {
- return -1;
- }
- return Object.keys(this.stationInfo?.Connectors).length;
- }
-
- private getMaxNumberOfConnectors(): number {
- let maxConnectors: number;
- if (!Utils.isEmptyArray(this.stationInfo.numberOfConnectors)) {
- const numberOfConnectors = this.stationInfo.numberOfConnectors as number[];
- // Distribute evenly the number of connectors
- maxConnectors = numberOfConnectors[(this.index - 1) % numberOfConnectors.length];
- } else if (!Utils.isUndefined(this.stationInfo.numberOfConnectors)) {
- maxConnectors = this.stationInfo.numberOfConnectors as number;
- } else {
- maxConnectors = this.stationInfo?.Connectors[0]
- ? this.getTemplateMaxNumberOfConnectors() - 1
- : this.getTemplateMaxNumberOfConnectors();
- }
- return maxConnectors;
- }
-
- private getMaximumPower(): number {
- return (this.stationInfo['maxPower'] as number) ?? this.stationInfo.maximumPower;
+ private getMaximumPower(stationInfo?: ChargingStationInfo): number {
+ const localStationInfo = stationInfo ?? this.stationInfo;
+ return (localStationInfo['maxPower'] as number) ?? localStationInfo.maximumPower;
}
- private getMaximumAmperage(): number | undefined {
- const maximumPower = this.getMaximumPower();
- switch (this.getCurrentOutType()) {
+ private getMaximumAmperage(stationInfo: ChargingStationInfo): number | undefined {
+ const maximumPower = this.getMaximumPower(stationInfo);
+ switch (this.getCurrentOutType(stationInfo)) {
case CurrentType.AC:
return ACElectricUtils.amperagePerPhaseFromPower(
- this.getNumberOfPhases(),
+ this.getNumberOfPhases(stationInfo),
maximumPower / this.getNumberOfConnectors(),
- this.getVoltageOut()
+ this.getVoltageOut(stationInfo)
);
case CurrentType.DC:
- return DCElectricUtils.amperage(maximumPower, this.getVoltageOut());
+ return DCElectricUtils.amperage(maximumPower, this.getVoltageOut(stationInfo));
}
}
}
private startAutomaticTransactionGenerator() {
- if (this.stationInfo.AutomaticTransactionGenerator.enable) {
+ if (this.getAutomaticTransactionGeneratorConfigurationFromTemplate()?.enable) {
if (!this.automaticTransactionGenerator) {
- this.automaticTransactionGenerator = AutomaticTransactionGenerator.getInstance(this);
+ this.automaticTransactionGenerator = AutomaticTransactionGenerator.getInstance(
+ this.getAutomaticTransactionGeneratorConfigurationFromTemplate(),
+ this
+ );
}
if (!this.automaticTransactionGenerator.started) {
this.automaticTransactionGenerator.start();
}
}
+ private stopAutomaticTransactionGenerator(): void {
+ if (this.automaticTransactionGenerator?.started) {
+ this.automaticTransactionGenerator.stop();
+ this.automaticTransactionGenerator = null;
+ }
+ }
+
private async stopMessageSequence(
reason: StopTransactionReason = StopTransactionReason.NONE
): Promise<void> {
this.stopWebSocketPing();
// Stop heartbeat
this.stopHeartbeat();
- // Stop the ATG
- if (
- this.stationInfo.AutomaticTransactionGenerator.enable &&
- this.automaticTransactionGenerator?.started
- ) {
- this.automaticTransactionGenerator.stop();
+ // Stop ongoing transactions
+ if (this.automaticTransactionGenerator?.configuration?.enable) {
+ this.stopAutomaticTransactionGenerator();
} else {
for (const connectorId of this.connectors.keys()) {
if (connectorId > 0 && this.getConnectorStatus(connectorId)?.transactionStarted) {
// Stop heartbeat
this.stopHeartbeat();
// Stop the ATG if needed
- if (
- this.stationInfo.AutomaticTransactionGenerator.enable &&
- this.stationInfo.AutomaticTransactionGenerator.stopOnConnectionFailure &&
- this.automaticTransactionGenerator?.started
- ) {
- this.automaticTransactionGenerator.stop();
+ if (this.automaticTransactionGenerator?.configuration?.stopOnConnectionFailure) {
+ this.stopAutomaticTransactionGenerator();
}
if (
this.autoReconnectRetryCount < this.getAutoReconnectMaxRetries() ||
}
}
+ private getAutomaticTransactionGeneratorConfigurationFromTemplate(): AutomaticTransactionGeneratorConfiguration | null {
+ return this.getTemplateFromFile()?.AutomaticTransactionGenerator ?? null;
+ }
+
private initializeConnectorStatus(connectorId: number): void {
this.getConnectorStatus(connectorId).idTagLocalAuthorized = false;
this.getConnectorStatus(connectorId).idTagAuthorized = false;
import ChargingStationInfo from '../types/ChargingStationInfo';
import Configuration from '../utils/Configuration';
import Constants from '../utils/Constants';
+import { FileType } from '../types/FileType';
+import FileUtils from '../utils/FileUtils';
import { SampledValueTemplate } from '../types/MeasurandPerPhaseSampledValueTemplates';
import { StandardParametersKey } from '../types/ocpp/Configuration';
import Utils from '../utils/Utils';
import { WebSocketCloseEventStatusString } from '../types/WebSocket';
import { WorkerProcessType } from '../types/Worker';
import crypto from 'crypto';
+import fs from 'fs';
import logger from '../utils/Logger';
import moment from 'moment';
+import path from 'path';
export class ChargingStationUtils {
public static getChargingStationId(
idSuffix;
}
- public static getHashId(stationInfo: ChargingStationInfo): string {
+ public static getHashId(index: number, stationTemplate: ChargingStationTemplate): string {
const hashBootNotificationRequest = {
- chargePointModel: stationInfo.chargePointModel,
- chargePointVendor: stationInfo.chargePointVendor,
- ...(!Utils.isUndefined(stationInfo.chargeBoxSerialNumberPrefix) && {
- chargeBoxSerialNumber: stationInfo.chargeBoxSerialNumberPrefix,
+ chargePointModel: stationTemplate.chargePointModel,
+ chargePointVendor: stationTemplate.chargePointVendor,
+ ...(!Utils.isUndefined(stationTemplate.chargeBoxSerialNumberPrefix) && {
+ chargeBoxSerialNumber: stationTemplate.chargeBoxSerialNumberPrefix,
}),
- ...(!Utils.isUndefined(stationInfo.chargePointSerialNumberPrefix) && {
- chargePointSerialNumber: stationInfo.chargePointSerialNumberPrefix,
+ ...(!Utils.isUndefined(stationTemplate.chargePointSerialNumberPrefix) && {
+ chargePointSerialNumber: stationTemplate.chargePointSerialNumberPrefix,
}),
- ...(!Utils.isUndefined(stationInfo.firmwareVersion) && {
- firmwareVersion: stationInfo.firmwareVersion,
+ ...(!Utils.isUndefined(stationTemplate.firmwareVersion) && {
+ firmwareVersion: stationTemplate.firmwareVersion,
}),
- ...(!Utils.isUndefined(stationInfo.iccid) && { iccid: stationInfo.iccid }),
- ...(!Utils.isUndefined(stationInfo.imsi) && { imsi: stationInfo.imsi }),
- ...(!Utils.isUndefined(stationInfo.meterSerialNumberPrefix) && {
- meterSerialNumber: stationInfo.meterSerialNumberPrefix,
+ ...(!Utils.isUndefined(stationTemplate.iccid) && { iccid: stationTemplate.iccid }),
+ ...(!Utils.isUndefined(stationTemplate.imsi) && { imsi: stationTemplate.imsi }),
+ ...(!Utils.isUndefined(stationTemplate.meterSerialNumberPrefix) && {
+ meterSerialNumber: stationTemplate.meterSerialNumberPrefix,
}),
- ...(!Utils.isUndefined(stationInfo.meterType) && {
- meterType: stationInfo.meterType,
+ ...(!Utils.isUndefined(stationTemplate.meterType) && {
+ meterType: stationTemplate.meterType,
}),
};
return crypto
.createHash(Constants.DEFAULT_HASH_ALGORITHM)
- .update(JSON.stringify(hashBootNotificationRequest) + stationInfo.chargingStationId)
+ .update(
+ JSON.stringify(hashBootNotificationRequest) +
+ ChargingStationUtils.getChargingStationId(index, stationTemplate)
+ )
.digest('hex');
}
+ public static getTemplateMaxNumberOfConnectors(stationTemplate: ChargingStationTemplate): number {
+ const templateConnectors = stationTemplate?.Connectors;
+ if (!templateConnectors) {
+ return -1;
+ }
+ return Object.keys(templateConnectors).length;
+ }
+
+ public static checkTemplateMaxConnectors(
+ templateMaxConnectors: number,
+ templateFile: string,
+ logPrefix: string
+ ): void {
+ if (templateMaxConnectors === 0) {
+ logger.warn(
+ `${logPrefix} Charging station information from template ${templateFile} with empty connectors configuration`
+ );
+ } else if (templateMaxConnectors < 0) {
+ logger.error(
+ `${logPrefix} Charging station information from template ${templateFile} with no connectors configuration defined`
+ );
+ }
+ }
+
+ public static getConfiguredNumberOfConnectors(
+ index: number,
+ stationTemplate: ChargingStationTemplate
+ ): number {
+ let configuredMaxConnectors: number;
+ if (!Utils.isEmptyArray(stationTemplate.numberOfConnectors)) {
+ const numberOfConnectors = stationTemplate.numberOfConnectors as number[];
+ // Distribute evenly the number of connectors
+ configuredMaxConnectors = numberOfConnectors[(index - 1) % numberOfConnectors.length];
+ } else if (!Utils.isUndefined(stationTemplate.numberOfConnectors)) {
+ configuredMaxConnectors = stationTemplate.numberOfConnectors as number;
+ } else {
+ configuredMaxConnectors = stationTemplate?.Connectors[0]
+ ? ChargingStationUtils.getTemplateMaxNumberOfConnectors(stationTemplate) - 1
+ : ChargingStationUtils.getTemplateMaxNumberOfConnectors(stationTemplate);
+ }
+ return configuredMaxConnectors;
+ }
+
+ public static checkConfiguredMaxConnectors(
+ configuredMaxConnectors: number,
+ templateFile: string,
+ logPrefix: string
+ ): void {
+ if (configuredMaxConnectors <= 0) {
+ logger.warn(
+ `${logPrefix} Charging station information from template ${templateFile} with ${configuredMaxConnectors} connectors`
+ );
+ }
+ }
+
public static createBootNotificationRequest(
stationInfo: ChargingStationInfo
): BootNotificationRequest {
}
}
- public static createStationInfoHash(stationInfo: ChargingStationInfo): ChargingStationInfo {
+ public static stationTemplateToStationInfo(
+ stationTemplate: ChargingStationTemplate
+ ): ChargingStationInfo {
+ stationTemplate = Utils.cloneObject(stationTemplate);
+ delete stationTemplate.power;
+ delete stationTemplate.powerUnit;
+ delete stationTemplate.Configuration;
+ delete stationTemplate.AutomaticTransactionGenerator;
+ delete stationTemplate.chargeBoxSerialNumberPrefix;
+ delete stationTemplate.chargePointSerialNumberPrefix;
+ return stationTemplate;
+ }
+
+ public static createStationInfoHash(stationInfo: ChargingStationInfo): void {
const previousInfoHash = stationInfo?.infoHash ?? '';
delete stationInfo.infoHash;
const currentInfoHash = crypto
} else {
stationInfo.infoHash = previousInfoHash;
}
- return stationInfo;
}
public static createSerialNumber(
+ stationTemplate: ChargingStationTemplate,
stationInfo: ChargingStationInfo,
- existingStationInfo?: ChargingStationInfo | null,
- params: { randomSerialNumberUpperCase?: boolean; randomSerialNumber?: boolean } = {
+ params: {
+ randomSerialNumberUpperCase?: boolean;
+ randomSerialNumber?: boolean;
+ } = {
randomSerialNumberUpperCase: true,
randomSerialNumber: true,
}
params = params ?? {};
params.randomSerialNumberUpperCase = params?.randomSerialNumberUpperCase ?? true;
params.randomSerialNumber = params?.randomSerialNumber ?? true;
- if (existingStationInfo) {
- existingStationInfo?.chargePointSerialNumber &&
- (stationInfo.chargePointSerialNumber = existingStationInfo.chargePointSerialNumber);
- existingStationInfo?.chargeBoxSerialNumber &&
- (stationInfo.chargeBoxSerialNumber = existingStationInfo.chargeBoxSerialNumber);
- existingStationInfo?.meterSerialNumber &&
- (stationInfo.meterSerialNumber = existingStationInfo.meterSerialNumber);
- } else {
- const serialNumberSuffix = params?.randomSerialNumber
- ? ChargingStationUtils.getRandomSerialNumberSuffix({
- upperCase: params.randomSerialNumberUpperCase,
- })
- : '';
- stationInfo.chargePointSerialNumber =
- stationInfo?.chargePointSerialNumberPrefix &&
- stationInfo.chargePointSerialNumberPrefix + serialNumberSuffix;
- stationInfo.chargeBoxSerialNumber =
- stationInfo?.chargeBoxSerialNumberPrefix &&
- stationInfo.chargeBoxSerialNumberPrefix + serialNumberSuffix;
- stationInfo.meterSerialNumber =
- stationInfo?.meterSerialNumberPrefix &&
- stationInfo.meterSerialNumberPrefix + serialNumberSuffix;
- }
+ const serialNumberSuffix = params?.randomSerialNumber
+ ? ChargingStationUtils.getRandomSerialNumberSuffix({
+ upperCase: params.randomSerialNumberUpperCase,
+ })
+ : '';
+ stationTemplate?.chargePointSerialNumberPrefix &&
+ stationInfo &&
+ Utils.isNullOrUndefined(stationInfo?.chargePointSerialNumber)
+ ? (stationInfo.chargePointSerialNumber =
+ stationTemplate.chargePointSerialNumberPrefix + serialNumberSuffix)
+ : stationInfo && delete stationInfo.chargePointSerialNumber;
+ stationTemplate?.chargeBoxSerialNumberPrefix &&
+ stationInfo &&
+ Utils.isNullOrUndefined(stationInfo?.chargeBoxSerialNumber)
+ ? (stationInfo.chargeBoxSerialNumber =
+ stationTemplate.chargeBoxSerialNumberPrefix + serialNumberSuffix)
+ : stationInfo && delete stationInfo.chargeBoxSerialNumber;
+ stationTemplate?.meterSerialNumberPrefix &&
+ stationInfo &&
+ Utils.isNullOrUndefined(stationInfo?.meterSerialNumber)
+ ? (stationInfo.meterSerialNumber =
+ stationTemplate.meterSerialNumberPrefix + serialNumberSuffix)
+ : stationInfo && delete stationInfo.meterSerialNumber;
}
public static getAmperageLimitationUnitDivider(stationInfo: ChargingStationInfo): number {
);
}
+ public static getAuthorizedTags(
+ stationInfo: ChargingStationInfo,
+ templateFile: string,
+ logPrefix: string
+ ): string[] {
+ let authorizedTags: string[] = [];
+ const authorizationFile = ChargingStationUtils.getAuthorizationFile(stationInfo);
+ if (authorizationFile) {
+ try {
+ // Load authorization file
+ authorizedTags = JSON.parse(fs.readFileSync(authorizationFile, 'utf8')) as string[];
+ } catch (error) {
+ FileUtils.handleFileException(
+ logPrefix,
+ FileType.Authorization,
+ authorizationFile,
+ error as NodeJS.ErrnoException
+ );
+ }
+ } else {
+ logger.info(logPrefix + ' No authorization file given in template file ' + templateFile);
+ }
+ return authorizedTags;
+ }
+
+ public static getAuthorizationFile(stationInfo: ChargingStationInfo): string | undefined {
+ return (
+ stationInfo.authorizationFile &&
+ path.join(
+ path.resolve(__dirname, '../'),
+ 'assets',
+ path.basename(stationInfo.authorizationFile)
+ )
+ );
+ }
+
private static getRandomSerialNumberSuffix(params?: {
randomBytesLength?: number;
upperCase?: boolean;