+++ /dev/null
-import fs from 'node:fs';
-
-import { type ChargingStation, ChargingStationUtils } from './internal';
-import { FileType, IdTagDistribution } from '../types';
-import { FileUtils, Utils, logger } from '../utils';
-
-type TagsCacheValueType = {
- tags: string[];
- tagsFileWatcher: fs.FSWatcher | undefined;
-};
-
-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 {
- if (AuthorizedTagsCache.instance === null) {
- AuthorizedTagsCache.instance = new 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.getTags(file);
- }
-
- public deleteAuthorizedTags(file: string): boolean {
- return this.deleteTags(file);
- }
-
- private getRandomIdTag(hashId: string, file: string): string {
- const tags = this.getAuthorizedTags(file);
- const addressableKey = file + hashId;
- this.tagsCachesAddressableIndexes.set(
- addressableKey,
- Math.floor(Utils.secureRandom() * tags.length)
- );
- return tags[this.tagsCachesAddressableIndexes.get(addressableKey)];
- }
-
- private getRoundRobinIdTag(hashId: string, file: string): string {
- const tags = this.getAuthorizedTags(file);
- const addressableKey = file + hashId;
- const idTagIndex = this.tagsCachesAddressableIndexes.get(addressableKey) ?? 0;
- const idTag = tags[idTagIndex];
- this.tagsCachesAddressableIndexes.set(
- addressableKey,
- 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;
- const addressableKey = file + hashId;
- this.tagsCachesAddressableIndexes.set(
- addressableKey,
- (chargingStation.index - 1 + (connectorId - 1)) % tags.length
- );
- return tags[this.tagsCachesAddressableIndexes.get(addressableKey)];
- }
-
- private hasTags(file: string): boolean {
- return this.tagsCaches.has(file);
- }
-
- private setTags(file: string, tags: string[]) {
- return this.tagsCaches.set(file, {
- tags,
- tagsFileWatcher: FileUtils.watchJsonFile(
- file,
- FileType.Authorization,
- this.logPrefix(file),
- undefined,
- (event, filename) => {
- if (Utils.isNotEmptyString(filename) && event === 'change') {
- try {
- logger.debug(
- `${this.logPrefix(file)} ${FileType.Authorization} file have changed, reload`
- );
- this.deleteTags(file);
- this.deleteTagsIndexes(file);
- } catch (error) {
- FileUtils.handleFileException(
- file,
- FileType.Authorization,
- error as NodeJS.ErrnoException,
- this.logPrefix(file),
- {
- throwError: false,
- }
- );
- }
- }
- }
- ),
- });
- }
-
- private getTags(file: string): string[] | undefined {
- return this.tagsCaches.get(file)?.tags;
- }
-
- private deleteTags(file: string): boolean {
- this.tagsCaches.get(file)?.tagsFileWatcher?.close();
- 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) {
- try {
- // Load authorization file
- authorizedTags = JSON.parse(fs.readFileSync(file, 'utf8')) as string[];
- } catch (error) {
- FileUtils.handleFileException(
- file,
- FileType.Authorization,
- error as NodeJS.ErrnoException,
- this.logPrefix(file)
- );
- }
- } else {
- logger.info(`${this.logPrefix(file)} No authorization file given`);
- }
- return authorizedTags;
- }
-
- private logPrefix = (file: string): string => {
- return Utils.logPrefix(` Authorized tags cache for authorization file '${file}' |`);
- };
-}
import { AsyncResource } from 'node:async_hooks';
-import { AuthorizedTagsCache, type ChargingStation, ChargingStationUtils } from './internal';
+import { type ChargingStation, ChargingStationUtils, IdTagsCache } from './internal';
import { BaseError } from '../exception';
// import { PerformanceStatistics } from '../performance';
import { PerformanceStatistics } from '../performance/PerformanceStatistics';
public readonly configuration: AutomaticTransactionGeneratorConfiguration;
public started: boolean;
private readonly chargingStation: ChargingStation;
- private idTagIndex: number;
private constructor(
automaticTransactionGeneratorConfiguration: AutomaticTransactionGeneratorConfiguration,
this.started = false;
this.configuration = automaticTransactionGeneratorConfiguration;
this.chargingStation = chargingStation;
- this.idTagIndex = 0;
this.connectorsStatus = new Map<number, Status>();
this.initializeConnectorsStatus();
}
const measureId = 'StartTransaction with ATG';
const beginId = PerformanceStatistics.beginMeasure(measureId);
let startResponse: StartTransactionResponse;
- if (this.chargingStation.hasAuthorizedTags()) {
- const idTag = AuthorizedTagsCache.getInstance().getIdTag(
+ if (this.chargingStation.hasIdTags()) {
+ const idTag = IdTagsCache.getInstance().getIdTag(
this.configuration?.idTagDistribution,
this.chargingStation,
connectorId
import WebSocket, { type RawData } from 'ws';
import {
- AuthorizedTagsCache,
AutomaticTransactionGenerator,
ChargingStationConfigurationUtils,
ChargingStationUtils,
ChargingStationWorkerBroadcastChannel,
+ IdTagsCache,
MessageChannelUtils,
SharedLRUCache,
} from './internal';
public stationInfo!: ChargingStationInfo;
public started: boolean;
public starting: boolean;
- public authorizedTagsCache: AuthorizedTagsCache;
+ public idTagsCache: IdTagsCache;
public automaticTransactionGenerator!: AutomaticTransactionGenerator | undefined;
public ocppConfiguration!: ChargingStationOcppConfiguration | undefined;
public wsConnection!: WebSocket | null;
this.requests = new Map<string, CachedRequest>();
this.messageBuffer = new Set<string>();
this.sharedLRUCache = SharedLRUCache.getInstance();
- this.authorizedTagsCache = AuthorizedTagsCache.getInstance();
+ this.idTagsCache = IdTagsCache.getInstance();
this.chargingStationWorkerBroadcastChannel = new ChargingStationWorkerBroadcastChannel(this);
this.initialize();
);
};
- public hasAuthorizedTags(): boolean {
- return Utils.isNotEmptyArray(
- this.authorizedTagsCache.getAuthorizedTags(
- ChargingStationUtils.getAuthorizationFile(this.stationInfo)
- )
- );
+ public hasIdTags(): boolean {
+ const idTagsFile = ChargingStationUtils.getAuthorizationFile(this.stationInfo);
+ return Utils.isNotEmptyArray(this.idTagsCache.getIdTags(idTagsFile));
}
public getEnableStatistics(): boolean {
--- /dev/null
+import fs from 'node:fs';
+
+import { type ChargingStation, ChargingStationUtils } from './internal';
+import { FileType, IdTagDistribution } from '../types';
+import { FileUtils, Utils, logger } from '../utils';
+
+type IdTagsCacheValueType = {
+ idTags: string[];
+ idTagsFileWatcher: fs.FSWatcher | undefined;
+};
+
+export class IdTagsCache {
+ private static instance: IdTagsCache | null = null;
+ private readonly idTagsCaches: Map<string, IdTagsCacheValueType>;
+ private readonly idTagsCachesAddressableIndexes: Map<string, number>;
+
+ private constructor() {
+ this.idTagsCaches = new Map<string, IdTagsCacheValueType>();
+ this.idTagsCachesAddressableIndexes = new Map<string, number>();
+ }
+
+ public static getInstance(): IdTagsCache {
+ if (IdTagsCache.instance === null) {
+ IdTagsCache.instance = new IdTagsCache();
+ }
+ return IdTagsCache.instance;
+ }
+
+ public getIdTag(
+ distribution: IdTagDistribution,
+ chargingStation: ChargingStation,
+ connectorId: number
+ ): string {
+ const hashId = chargingStation.stationInfo.hashId;
+ const idTagsFile = ChargingStationUtils.getAuthorizationFile(chargingStation.stationInfo);
+ switch (distribution) {
+ case IdTagDistribution.RANDOM:
+ return this.getRandomIdTag(hashId, idTagsFile);
+ case IdTagDistribution.ROUND_ROBIN:
+ return this.getRoundRobinIdTag(hashId, idTagsFile);
+ case IdTagDistribution.CONNECTOR_AFFINITY:
+ return this.getConnectorAffinityIdTag(chargingStation, connectorId);
+ default:
+ return this.getRoundRobinIdTag(hashId, idTagsFile);
+ }
+ }
+
+ public getIdTags(file: string): string[] | undefined {
+ if (this.hasIdTagsCache(file) === false) {
+ this.setIdTagsCache(file, this.getIdTagsFromFile(file));
+ }
+ return this.getIdTagsCache(file);
+ }
+
+ public deleteIdTags(file: string): boolean {
+ return this.deleteIdTagsCache(file);
+ }
+
+ private getRandomIdTag(hashId: string, file: string): string {
+ const idTags = this.getIdTags(file);
+ const addressableKey = file + hashId;
+ this.idTagsCachesAddressableIndexes.set(
+ addressableKey,
+ Math.floor(Utils.secureRandom() * idTags.length)
+ );
+ return idTags[this.idTagsCachesAddressableIndexes.get(addressableKey)];
+ }
+
+ private getRoundRobinIdTag(hashId: string, file: string): string {
+ const idTags = this.getIdTags(file);
+ const addressableKey = file + hashId;
+ const idTagIndex = this.idTagsCachesAddressableIndexes.get(addressableKey) ?? 0;
+ const idTag = idTags[idTagIndex];
+ this.idTagsCachesAddressableIndexes.set(
+ addressableKey,
+ idTagIndex === idTags.length - 1 ? 0 : idTagIndex + 1
+ );
+ return idTag;
+ }
+
+ private getConnectorAffinityIdTag(chargingStation: ChargingStation, connectorId: number): string {
+ const file = ChargingStationUtils.getAuthorizationFile(chargingStation.stationInfo);
+ const idTags = this.getIdTags(file);
+ const hashId = chargingStation.stationInfo.hashId;
+ const addressableKey = file + hashId;
+ this.idTagsCachesAddressableIndexes.set(
+ addressableKey,
+ (chargingStation.index - 1 + (connectorId - 1)) % idTags.length
+ );
+ return idTags[this.idTagsCachesAddressableIndexes.get(addressableKey)];
+ }
+
+ private hasIdTagsCache(file: string): boolean {
+ return this.idTagsCaches.has(file);
+ }
+
+ private setIdTagsCache(file: string, idTags: string[]) {
+ return this.idTagsCaches.set(file, {
+ idTags,
+ idTagsFileWatcher: FileUtils.watchJsonFile(
+ file,
+ FileType.Authorization,
+ this.logPrefix(file),
+ undefined,
+ (event, filename) => {
+ if (Utils.isNotEmptyString(filename) && event === 'change') {
+ try {
+ logger.debug(
+ `${this.logPrefix(file)} ${FileType.Authorization} file have changed, reload`
+ );
+ this.deleteIdTagsCache(file);
+ this.deleteIdTagsCacheIndexes(file);
+ } catch (error) {
+ FileUtils.handleFileException(
+ file,
+ FileType.Authorization,
+ error as NodeJS.ErrnoException,
+ this.logPrefix(file),
+ {
+ throwError: false,
+ }
+ );
+ }
+ }
+ }
+ ),
+ });
+ }
+
+ private getIdTagsCache(file: string): string[] | undefined {
+ return this.idTagsCaches.get(file)?.idTags;
+ }
+
+ private deleteIdTagsCache(file: string): boolean {
+ this.idTagsCaches.get(file)?.idTagsFileWatcher?.close();
+ return this.idTagsCaches.delete(file);
+ }
+
+ private deleteIdTagsCacheIndexes(file: string): void {
+ for (const [key] of this.idTagsCachesAddressableIndexes) {
+ if (key.startsWith(file)) {
+ this.idTagsCachesAddressableIndexes.delete(key);
+ }
+ }
+ }
+
+ private getIdTagsFromFile(file: string): string[] {
+ let idTags: string[] = [];
+ if (file) {
+ try {
+ // Load id tags file
+ idTags = JSON.parse(fs.readFileSync(file, 'utf8')) as string[];
+ } catch (error) {
+ FileUtils.handleFileException(
+ file,
+ FileType.Authorization,
+ error as NodeJS.ErrnoException,
+ this.logPrefix(file)
+ );
+ }
+ } else {
+ logger.info(`${this.logPrefix(file)} No id tags file given`);
+ }
+ return idTags;
+ }
+
+ private logPrefix = (file: string): string => {
+ return Utils.logPrefix(` Id tags cache for id tags file '${file}' |`);
+ };
+}
-export * from './AuthorizedTagsCache';
+export * from './IdTagsCache';
export * from './AutomaticTransactionGenerator';
export * from './Bootstrap';
export * from './ChargingStation';
let authorized = false;
if (
chargingStation.getLocalAuthListEnabled() === true &&
- chargingStation.hasAuthorizedTags() === true &&
+ chargingStation.hasIdTags() === true &&
Utils.isNotEmptyString(
- chargingStation.authorizedTagsCache
- .getAuthorizedTags(
- ChargingStationUtils.getAuthorizationFile(chargingStation.stationInfo)
- )
+ chargingStation.idTagsCache
+ .getIdTags(ChargingStationUtils.getAuthorizationFile(chargingStation.stationInfo))
?.find((idTag) => idTag === commandPayload.idTag)
)
) {
chargingStation.getConnectorStatus(connectorId)?.transactionRemoteStarted === true &&
chargingStation.getAuthorizeRemoteTxRequests() === true &&
chargingStation.getLocalAuthListEnabled() === true &&
- chargingStation.hasAuthorizedTags() &&
+ chargingStation.hasIdTags() &&
chargingStation.getConnectorStatus(connectorId)?.idTagLocalAuthorized === false
) {
logger.error(
}
protected handleRequestClearCache(chargingStation: ChargingStation): ClearCacheResponse {
- chargingStation.authorizedTagsCache.deleteAuthorizedTags(
+ chargingStation.idTagsCache.deleteIdTags(
ChargingStationUtils.getAuthorizationFile(chargingStation.stationInfo)
);
return OCPPConstants.OCPP_RESPONSE_ACCEPTED;
ocppPersistentConfiguration?: boolean;
stationInfoPersistentConfiguration?: boolean;
wsOptions?: WsOptions;
+ // FIXME: rename to idTagFile
authorizationFile?: string;
baseName: string;
nameSuffix?: string;