-import fs from 'node:fs';
-
-import type { ChargingStation } from './ChargingStation';
-import { ChargingStationUtils } from './ChargingStationUtils';
-import { FileType, IdTagDistribution } from '../types';
-import { FileUtils, Utils, logger } from '../utils';
-
-type IdTagsCacheValueType = {
- idTags: string[];
- idTagsFileWatcher: fs.FSWatcher | undefined;
-};
+import { type FSWatcher, readFileSync } from 'node:fs'
+
+import { FileType, IdTagDistribution } from '../types/index.js'
+import {
+ handleFileException,
+ isNotEmptyString,
+ logger,
+ logPrefix,
+ secureRandom,
+ watchJsonFile
+} from '../utils/index.js'
+import type { ChargingStation } from './ChargingStation.js'
+import { getIdTagsFile } from './Helpers.js'
+
+interface IdTagsCacheValueType {
+ idTags: string[]
+ idTagsFileWatcher: FSWatcher | undefined
+}
export class IdTagsCache {
- private static instance: IdTagsCache | null = null;
- private readonly idTagsCaches: Map<string, IdTagsCacheValueType>;
- private readonly idTagsCachesAddressableIndexes: Map<string, number>;
+ 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>();
+ private constructor () {
+ this.idTagsCaches = new Map<string, IdTagsCacheValueType>()
+ this.idTagsCachesAddressableIndexes = new Map<string, number>()
}
- public static getInstance(): IdTagsCache {
+ public static getInstance (): IdTagsCache {
if (IdTagsCache.instance === null) {
- IdTagsCache.instance = new IdTagsCache();
+ IdTagsCache.instance = new IdTagsCache()
}
- return IdTagsCache.instance;
+ return IdTagsCache.instance
}
- public getIdTag(
+ /**
+ * Gets one idtag from the cache given the distribution
+ * Must be called after checking the cache is not an empty array
+ *
+ * @param distribution -
+ * @param chargingStation -
+ * @param connectorId -
+ * @returns
+ */
+ public getIdTag (
distribution: IdTagDistribution,
chargingStation: ChargingStation,
connectorId: number
): string {
- const hashId = chargingStation.stationInfo.hashId;
- const idTagsFile = ChargingStationUtils.getIdTagsFile(chargingStation.stationInfo);
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+ const hashId = chargingStation.stationInfo!.hashId
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+ const idTagsFile = getIdTagsFile(chargingStation.stationInfo!)!
switch (distribution) {
case IdTagDistribution.RANDOM:
- return this.getRandomIdTag(hashId, idTagsFile);
+ return this.getRandomIdTag(hashId, idTagsFile)
case IdTagDistribution.ROUND_ROBIN:
- return this.getRoundRobinIdTag(hashId, idTagsFile);
+ return this.getRoundRobinIdTag(hashId, idTagsFile)
case IdTagDistribution.CONNECTOR_AFFINITY:
- return this.getConnectorAffinityIdTag(chargingStation, connectorId);
+ return this.getConnectorAffinityIdTag(chargingStation, connectorId)
default:
- return this.getRoundRobinIdTag(hashId, idTagsFile);
+ return this.getRoundRobinIdTag(hashId, idTagsFile)
}
}
- public getIdTags(file: string): string[] | undefined {
- if (this.hasIdTagsCache(file) === false) {
- this.setIdTagsCache(file, this.getIdTagsFromFile(file));
+ /**
+ * Gets all idtags from the cache
+ * Must be called after checking the cache is not an empty array
+ *
+ * @param file -
+ * @returns
+ */
+ public getIdTags (file: string): string[] | undefined {
+ if (!this.hasIdTagsCache(file)) {
+ this.setIdTagsCache(file, this.getIdTagsFromFile(file))
}
- return this.getIdTagsCache(file);
+ return this.getIdTagsCache(file)
}
- public deleteIdTags(file: string): boolean {
- return this.deleteIdTagsCache(file);
+ public deleteIdTags (file: string): boolean {
+ return this.deleteIdTagsCache(file) && this.deleteIdTagsCacheIndexes(file)
}
- private getRandomIdTag(hashId: string, file: string): string {
- const idTags = this.getIdTags(file);
- const addressableKey = this.getIdTagsCacheIndexesAddressableKey(file, hashId);
+ private getRandomIdTag (hashId: string, file: string): string {
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+ const idTags = this.getIdTags(file)!
+ const addressableKey = this.getIdTagsCacheIndexesAddressableKey(file, hashId)
this.idTagsCachesAddressableIndexes.set(
addressableKey,
- Math.floor(Utils.secureRandom() * idTags.length)
- );
- return idTags[this.idTagsCachesAddressableIndexes.get(addressableKey)];
+ Math.floor(secureRandom() * idTags.length)
+ )
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+ return idTags[this.idTagsCachesAddressableIndexes.get(addressableKey)!]
}
- private getRoundRobinIdTag(hashId: string, file: string): string {
- const idTags = this.getIdTags(file);
- const addressableKey = this.getIdTagsCacheIndexesAddressableKey(file, hashId);
- const idTagIndex = this.idTagsCachesAddressableIndexes.get(addressableKey) ?? 0;
- const idTag = idTags[idTagIndex];
+ private getRoundRobinIdTag (hashId: string, file: string): string {
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+ const idTags = this.getIdTags(file)!
+ const addressableKey = this.getIdTagsCacheIndexesAddressableKey(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;
+ )
+ return idTag
}
- private getConnectorAffinityIdTag(chargingStation: ChargingStation, connectorId: number): string {
- const file = ChargingStationUtils.getIdTagsFile(chargingStation.stationInfo);
- const idTags = this.getIdTags(file);
+ private getConnectorAffinityIdTag (chargingStation: ChargingStation, connectorId: number): string {
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+ const file = getIdTagsFile(chargingStation.stationInfo!)!
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+ const idTags = this.getIdTags(file)!
const addressableKey = this.getIdTagsCacheIndexesAddressableKey(
file,
- chargingStation.stationInfo.hashId
- );
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+ chargingStation.stationInfo!.hashId
+ )
this.idTagsCachesAddressableIndexes.set(
addressableKey,
(chargingStation.index - 1 + (connectorId - 1)) % idTags.length
- );
- return idTags[this.idTagsCachesAddressableIndexes.get(addressableKey)];
+ )
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+ return idTags[this.idTagsCachesAddressableIndexes.get(addressableKey)!]
}
- private hasIdTagsCache(file: string): boolean {
- return this.idTagsCaches.has(file);
+ private hasIdTagsCache (file: string): boolean {
+ return this.idTagsCaches.has(file)
}
- private setIdTagsCache(file: string, idTags: string[]) {
+ private setIdTagsCache (file: string, idTags: string[]): Map<string, IdTagsCacheValueType> {
return this.idTagsCaches.set(file, {
idTags,
- idTagsFileWatcher: FileUtils.watchJsonFile(
+ idTagsFileWatcher: watchJsonFile(
file,
FileType.Authorization,
this.logPrefix(file),
undefined,
(event, filename) => {
- if (Utils.isNotEmptyString(filename) && event === 'change') {
+ if (isNotEmptyString(filename) && event === 'change') {
try {
logger.debug(
`${this.logPrefix(file)} ${FileType.Authorization} file have changed, reload`
- );
- this.deleteIdTagsCache(file);
- this.deleteIdTagsCacheIndexes(file);
+ )
+ this.deleteIdTags(file)
} catch (error) {
- FileUtils.handleFileException(
+ handleFileException(
file,
FileType.Authorization,
error as NodeJS.ErrnoException,
this.logPrefix(file),
{
- throwError: false,
+ throwError: false
}
- );
+ )
}
}
}
- ),
- });
+ )
+ })
}
- private getIdTagsCache(file: string): string[] | undefined {
- return this.idTagsCaches.get(file)?.idTags;
+ 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 deleteIdTagsCache (file: string): boolean {
+ this.idTagsCaches.get(file)?.idTagsFileWatcher?.close()
+ return this.idTagsCaches.delete(file)
}
- private deleteIdTagsCacheIndexes(file: string): void {
+ private deleteIdTagsCacheIndexes (file: string): boolean {
+ const deleted: boolean[] = []
for (const [key] of this.idTagsCachesAddressableIndexes) {
if (key.startsWith(file)) {
- this.idTagsCachesAddressableIndexes.delete(key);
+ deleted.push(this.idTagsCachesAddressableIndexes.delete(key))
}
}
+ return !deleted.some(value => !value)
}
- private getIdTagsCacheIndexesAddressableKey(prefix: string, uid: string): string {
- return `${prefix}${uid}`;
+ private getIdTagsCacheIndexesAddressableKey (prefix: string, uid: string): string {
+ return `${prefix}${uid}`
}
- private getIdTagsFromFile(file: string): string[] {
- let idTags: string[] = [];
- if (file) {
+ private getIdTagsFromFile (file: string): string[] {
+ if (isNotEmptyString(file)) {
try {
- // Load id tags file
- idTags = JSON.parse(fs.readFileSync(file, 'utf8')) as string[];
+ return JSON.parse(readFileSync(file, 'utf8')) as string[]
} catch (error) {
- FileUtils.handleFileException(
+ handleFileException(
file,
FileType.Authorization,
error as NodeJS.ErrnoException,
this.logPrefix(file)
- );
+ )
}
- } else {
- logger.info(`${this.logPrefix(file)} No id tags file given`);
}
- return idTags;
+ return []
}
- private logPrefix = (file: string): string => {
- return Utils.logPrefix(` Id tags cache for id tags file '${file}' |`);
- };
+ private readonly logPrefix = (file: string): string => {
+ return logPrefix(` Id tags cache for id tags file '${file}' |`)
+ }
}