1 import fs from
'node:fs';
3 import { type ChargingStation
, ChargingStationUtils
} from
'./internal';
4 import { FileType
, IdTagDistribution
} from
'../types';
5 import { FileUtils
, Utils
, logger
} from
'../utils';
7 type IdTagsCacheValueType
= {
9 idTagsFileWatcher
: fs
.FSWatcher
| undefined;
12 export class IdTagsCache
{
13 private static instance
: IdTagsCache
| null = null;
14 private readonly idTagsCaches
: Map
<string, IdTagsCacheValueType
>;
15 private readonly idTagsCachesAddressableIndexes
: Map
<string, number>;
17 private constructor() {
18 this.idTagsCaches
= new Map
<string, IdTagsCacheValueType
>();
19 this.idTagsCachesAddressableIndexes
= new Map
<string, number>();
22 public static getInstance(): IdTagsCache
{
23 if (IdTagsCache
.instance
=== null) {
24 IdTagsCache
.instance
= new IdTagsCache();
26 return IdTagsCache
.instance
;
30 distribution
: IdTagDistribution
,
31 chargingStation
: ChargingStation
,
34 const hashId
= chargingStation
.stationInfo
.hashId
;
35 const idTagsFile
= ChargingStationUtils
.getIdTagsFile(chargingStation
.stationInfo
);
36 switch (distribution
) {
37 case IdTagDistribution
.RANDOM
:
38 return this.getRandomIdTag(hashId
, idTagsFile
);
39 case IdTagDistribution
.ROUND_ROBIN
:
40 return this.getRoundRobinIdTag(hashId
, idTagsFile
);
41 case IdTagDistribution
.CONNECTOR_AFFINITY
:
42 return this.getConnectorAffinityIdTag(chargingStation
, connectorId
);
44 return this.getRoundRobinIdTag(hashId
, idTagsFile
);
48 public getIdTags(file
: string): string[] | undefined {
49 if (this.hasIdTagsCache(file
) === false) {
50 this.setIdTagsCache(file
, this.getIdTagsFromFile(file
));
52 return this.getIdTagsCache(file
);
55 public deleteIdTags(file
: string): boolean {
56 return this.deleteIdTagsCache(file
);
59 private getRandomIdTag(hashId
: string, file
: string): string {
60 const idTags
= this.getIdTags(file
);
61 const addressableKey
= this.getIdTagsCacheIndexesAddressableKey(file
, hashId
);
62 this.idTagsCachesAddressableIndexes
.set(
64 Math.floor(Utils
.secureRandom() * idTags
.length
)
66 return idTags
[this.idTagsCachesAddressableIndexes
.get(addressableKey
)];
69 private getRoundRobinIdTag(hashId
: string, file
: string): string {
70 const idTags
= this.getIdTags(file
);
71 const addressableKey
= this.getIdTagsCacheIndexesAddressableKey(file
, hashId
);
72 const idTagIndex
= this.idTagsCachesAddressableIndexes
.get(addressableKey
) ?? 0;
73 const idTag
= idTags
[idTagIndex
];
74 this.idTagsCachesAddressableIndexes
.set(
76 idTagIndex
=== idTags
.length
- 1 ? 0 : idTagIndex
+ 1
81 private getConnectorAffinityIdTag(chargingStation
: ChargingStation
, connectorId
: number): string {
82 const file
= ChargingStationUtils
.getIdTagsFile(chargingStation
.stationInfo
);
83 const idTags
= this.getIdTags(file
);
84 const addressableKey
= this.getIdTagsCacheIndexesAddressableKey(
86 chargingStation
.stationInfo
.hashId
88 this.idTagsCachesAddressableIndexes
.set(
90 (chargingStation
.index
- 1 + (connectorId
- 1)) % idTags
.length
92 return idTags
[this.idTagsCachesAddressableIndexes
.get(addressableKey
)];
95 private hasIdTagsCache(file
: string): boolean {
96 return this.idTagsCaches
.has(file
);
99 private setIdTagsCache(file
: string, idTags
: string[]) {
100 return this.idTagsCaches
.set(file
, {
102 idTagsFileWatcher
: FileUtils
.watchJsonFile(
104 FileType
.Authorization
,
105 this.logPrefix(file
),
107 (event
, filename
) => {
108 if (Utils
.isNotEmptyString(filename
) && event
=== 'change') {
111 `${this.logPrefix(file)} ${FileType.Authorization} file have changed, reload`
113 this.deleteIdTagsCache(file
);
114 this.deleteIdTagsCacheIndexes(file
);
116 FileUtils
.handleFileException(
118 FileType
.Authorization
,
119 error
as NodeJS
.ErrnoException
,
120 this.logPrefix(file
),
132 private getIdTagsCache(file
: string): string[] | undefined {
133 return this.idTagsCaches
.get(file
)?.idTags
;
136 private deleteIdTagsCache(file
: string): boolean {
137 this.idTagsCaches
.get(file
)?.idTagsFileWatcher
?.close();
138 return this.idTagsCaches
.delete(file
);
141 private deleteIdTagsCacheIndexes(file
: string): void {
142 for (const [key
] of this.idTagsCachesAddressableIndexes
) {
143 if (key
.startsWith(file
)) {
144 this.idTagsCachesAddressableIndexes
.delete(key
);
149 private getIdTagsCacheIndexesAddressableKey(prefix
: string, uid
: string): string {
150 return `${prefix}${uid}`;
153 private getIdTagsFromFile(file
: string): string[] {
154 let idTags
: string[] = [];
158 idTags
= JSON
.parse(fs
.readFileSync(file
, 'utf8')) as string[];
160 FileUtils
.handleFileException(
162 FileType
.Authorization
,
163 error
as NodeJS
.ErrnoException
,
168 logger
.info(`${this.logPrefix(file)} No id tags file given`);
173 private logPrefix
= (file
: string): string => {
174 return Utils
.logPrefix(` Id tags cache for id tags file '${file}' |`);