import fs from 'node:fs';
-import { FileType } from '../types';
+import { type ChargingStation, ChargingStationUtils } from './internal';
+import { FileType, IdTagDistribution } from '../types';
import { FileUtils, Utils, logger } from '../utils';
type TagsCacheValueType = {
export class AuthorizedTagsCache {
private static instance: AuthorizedTagsCache | null = null;
private readonly tagsCaches: Map<string, TagsCacheValueType>;
+ private readonly tagsCachesAddressableIndexes: Map<string, number>;
private constructor() {
this.tagsCaches = new Map<string, TagsCacheValueType>();
+ this.tagsCachesAddressableIndexes = new Map<string, number>();
}
public static getInstance(): AuthorizedTagsCache {
return AuthorizedTagsCache.instance;
}
+ public getIdTag(
+ distribution: IdTagDistribution,
+ chargingStation: ChargingStation,
+ connectorId: number
+ ): string {
+ const hashId = chargingStation.stationInfo.hashId;
+ const authorizationFile = ChargingStationUtils.getAuthorizationFile(
+ chargingStation.stationInfo
+ );
+ switch (distribution) {
+ case IdTagDistribution.RANDOM:
+ return this.getRandomIdTag(hashId, authorizationFile);
+ case IdTagDistribution.ROUND_ROBIN:
+ return this.getRoundRobinIdTag(hashId, authorizationFile);
+ case IdTagDistribution.CONNECTOR_AFFINITY:
+ return this.getConnectorAffinityIdTag(chargingStation, connectorId);
+ default:
+ return this.getRoundRobinIdTag(hashId, authorizationFile);
+ }
+ }
+
public getAuthorizedTags(file: string): string[] | undefined {
if (this.hasTags(file) === false) {
this.setTags(file, this.getAuthorizedTagsFromFile(file));
return this.deleteTags(file);
}
+ private getRandomIdTag(hashId: string, file: string): string {
+ const tags = this.getAuthorizedTags(file);
+ this.tagsCachesAddressableIndexes.set(
+ file + hashId,
+ Math.floor(Utils.secureRandom() * tags.length)
+ );
+ return tags[this.tagsCachesAddressableIndexes.get(file + hashId)];
+ }
+
+ private getRoundRobinIdTag(hashId: string, file: string): string {
+ const tags = this.getAuthorizedTags(file);
+ const idTagIndex = this.tagsCachesAddressableIndexes.get(file + hashId) ?? 0;
+ const idTag = tags[idTagIndex];
+ this.tagsCachesAddressableIndexes.set(
+ file + hashId,
+ idTagIndex === tags.length - 1 ? 0 : idTagIndex + 1
+ );
+ return idTag;
+ }
+
+ private getConnectorAffinityIdTag(chargingStation: ChargingStation, connectorId: number): string {
+ const file = ChargingStationUtils.getAuthorizationFile(chargingStation.stationInfo);
+ const tags = this.getAuthorizedTags(file);
+ const hashId = chargingStation.stationInfo.hashId;
+ this.tagsCachesAddressableIndexes.set(
+ file + hashId,
+ (chargingStation.index - 1 + (connectorId - 1)) % tags.length
+ );
+ return tags[this.tagsCachesAddressableIndexes.get(file + hashId)];
+ }
+
private hasTags(file: string): boolean {
return this.tagsCaches.has(file);
}
`${this.logPrefix(file)} ${FileType.Authorization} file have changed, reload`
);
this.deleteTags(file);
+ this.deleteTagsIndexes(file);
} catch (error) {
FileUtils.handleFileException(
file,
return this.tagsCaches.delete(file);
}
+ private deleteTagsIndexes(file: string): void {
+ for (const [key] of this.tagsCachesAddressableIndexes) {
+ if (key.startsWith(file)) {
+ this.tagsCachesAddressableIndexes.delete(key);
+ }
+ }
+ }
+
private getAuthorizedTagsFromFile(file: string): string[] {
let authorizedTags: string[] = [];
if (file) {
import { AsyncResource } from 'node:async_hooks';
-import { type ChargingStation, ChargingStationUtils } from './internal';
+import { AuthorizedTagsCache, type ChargingStation, ChargingStationUtils } from './internal';
import { BaseError } from '../exception';
// import { PerformanceStatistics } from '../performance';
import { PerformanceStatistics } from '../performance/PerformanceStatistics';
type AuthorizeResponse,
type AutomaticTransactionGeneratorConfiguration,
ConnectorStatusEnum,
- IdTagDistribution,
RequestCommand,
type StartTransactionRequest,
type StartTransactionResponse,
const beginId = PerformanceStatistics.beginMeasure(measureId);
let startResponse: StartTransactionResponse;
if (this.chargingStation.hasAuthorizedTags()) {
- const idTag = this.getIdTag(connectorId);
+ const idTag = AuthorizedTagsCache.getInstance().getIdTag(
+ this.configuration?.idTagDistribution,
+ this.chargingStation,
+ connectorId
+ );
const startTransactionLogMsg = `${this.logPrefix(
connectorId
)} start transaction with an idTag '${idTag}'`;
return this.configuration?.requireAuthorize ?? true;
}
- private getRandomIdTag(authorizationFile: string): string {
- const tags = this.chargingStation.authorizedTagsCache.getAuthorizedTags(authorizationFile);
- this.idTagIndex = Math.floor(Utils.secureRandom() * tags.length);
- return tags[this.idTagIndex];
- }
-
- private getRoundRobinIdTag(authorizationFile: string): string {
- const tags = this.chargingStation.authorizedTagsCache.getAuthorizedTags(authorizationFile);
- const idTag = tags[this.idTagIndex];
- this.idTagIndex = this.idTagIndex === tags.length - 1 ? 0 : this.idTagIndex + 1;
- return idTag;
- }
-
- private getConnectorAffinityIdTag(authorizationFile: string, connectorId: number): string {
- const tags = this.chargingStation.authorizedTagsCache.getAuthorizedTags(authorizationFile);
- this.idTagIndex = (this.chargingStation.index - 1 + (connectorId - 1)) % tags.length;
- return tags[this.idTagIndex];
- }
-
- private getIdTag(connectorId: number): string {
- const authorizationFile = ChargingStationUtils.getAuthorizationFile(
- this.chargingStation.stationInfo
- );
- switch (this.configuration?.idTagDistribution) {
- case IdTagDistribution.RANDOM:
- return this.getRandomIdTag(authorizationFile);
- case IdTagDistribution.ROUND_ROBIN:
- return this.getRoundRobinIdTag(authorizationFile);
- case IdTagDistribution.CONNECTOR_AFFINITY:
- return this.getConnectorAffinityIdTag(authorizationFile, connectorId);
- default:
- return this.getRoundRobinIdTag(authorizationFile);
- }
- }
-
private logPrefix = (connectorId?: number): string => {
return Utils.logPrefix(
` ${this.chargingStation.stationInfo.chargingStationId} | ATG${