1 import fs from
'node:fs';
3 import type { ChargingStation
} from
'./ChargingStation';
4 import { ChargingStationUtils
} from
'./ChargingStationUtils';
5 import { FileType
, IdTagDistribution
} from
'../types';
6 import { FileUtils
, Utils
, logger
} from
'../utils';
8 type IdTagsCacheValueType
= {
10 idTagsFileWatcher
: fs
.FSWatcher
| undefined;
13 export class IdTagsCache
{
14 private static instance
: IdTagsCache
| null = null;
15 private readonly idTagsCaches
: Map
<string, IdTagsCacheValueType
>;
16 private readonly idTagsCachesAddressableIndexes
: Map
<string, number>;
18 private constructor() {
19 this.idTagsCaches
= new Map
<string, IdTagsCacheValueType
>();
20 this.idTagsCachesAddressableIndexes
= new Map
<string, number>();
23 public static getInstance(): IdTagsCache
{
24 if (IdTagsCache
.instance
=== null) {
25 IdTagsCache
.instance
= new IdTagsCache();
27 return IdTagsCache
.instance
;
31 distribution
: IdTagDistribution
,
32 chargingStation
: ChargingStation
,
35 const hashId
= chargingStation
.stationInfo
.hashId
;
36 const idTagsFile
= ChargingStationUtils
.getIdTagsFile(chargingStation
.stationInfo
);
37 switch (distribution
) {
38 case IdTagDistribution
.RANDOM
:
39 return this.getRandomIdTag(hashId
, idTagsFile
);
40 case IdTagDistribution
.ROUND_ROBIN
:
41 return this.getRoundRobinIdTag(hashId
, idTagsFile
);
42 case IdTagDistribution
.CONNECTOR_AFFINITY
:
43 return this.getConnectorAffinityIdTag(chargingStation
, connectorId
);
45 return this.getRoundRobinIdTag(hashId
, idTagsFile
);
49 public getIdTags(file
: string): string[] | undefined {
50 if (this.hasIdTagsCache(file
) === false) {
51 this.setIdTagsCache(file
, this.getIdTagsFromFile(file
));
53 return this.getIdTagsCache(file
);
56 public deleteIdTags(file
: string): boolean {
57 return this.deleteIdTagsCache(file
);
60 private getRandomIdTag(hashId
: string, file
: string): string {
61 const idTags
= this.getIdTags(file
);
62 const addressableKey
= this.getIdTagsCacheIndexesAddressableKey(file
, hashId
);
63 this.idTagsCachesAddressableIndexes
.set(
65 Math.floor(Utils
.secureRandom() * idTags
.length
)
67 return idTags
[this.idTagsCachesAddressableIndexes
.get(addressableKey
)];
70 private getRoundRobinIdTag(hashId
: string, file
: string): string {
71 const idTags
= this.getIdTags(file
);
72 const addressableKey
= this.getIdTagsCacheIndexesAddressableKey(file
, hashId
);
73 const idTagIndex
= this.idTagsCachesAddressableIndexes
.get(addressableKey
) ?? 0;
74 const idTag
= idTags
[idTagIndex
];
75 this.idTagsCachesAddressableIndexes
.set(
77 idTagIndex
=== idTags
.length
- 1 ? 0 : idTagIndex
+ 1
82 private getConnectorAffinityIdTag(chargingStation
: ChargingStation
, connectorId
: number): string {
83 const file
= ChargingStationUtils
.getIdTagsFile(chargingStation
.stationInfo
);
84 const idTags
= this.getIdTags(file
);
85 const addressableKey
= this.getIdTagsCacheIndexesAddressableKey(
87 chargingStation
.stationInfo
.hashId
89 this.idTagsCachesAddressableIndexes
.set(
91 (chargingStation
.index
- 1 + (connectorId
- 1)) % idTags
.length
93 return idTags
[this.idTagsCachesAddressableIndexes
.get(addressableKey
)];
96 private hasIdTagsCache(file
: string): boolean {
97 return this.idTagsCaches
.has(file
);
100 private setIdTagsCache(file
: string, idTags
: string[]) {
101 return this.idTagsCaches
.set(file
, {
103 idTagsFileWatcher
: FileUtils
.watchJsonFile(
105 FileType
.Authorization
,
106 this.logPrefix(file
),
108 (event
, filename
) => {
109 if (Utils
.isNotEmptyString(filename
) && event
=== 'change') {
112 `${this.logPrefix(file)} ${FileType.Authorization} file have changed, reload`
114 this.deleteIdTagsCache(file
);
115 this.deleteIdTagsCacheIndexes(file
);
117 FileUtils
.handleFileException(
119 FileType
.Authorization
,
120 error
as NodeJS
.ErrnoException
,
121 this.logPrefix(file
),
133 private getIdTagsCache(file
: string): string[] | undefined {
134 return this.idTagsCaches
.get(file
)?.idTags
;
137 private deleteIdTagsCache(file
: string): boolean {
138 this.idTagsCaches
.get(file
)?.idTagsFileWatcher
?.close();
139 return this.idTagsCaches
.delete(file
);
142 private deleteIdTagsCacheIndexes(file
: string): void {
143 for (const [key
] of this.idTagsCachesAddressableIndexes
) {
144 if (key
.startsWith(file
)) {
145 this.idTagsCachesAddressableIndexes
.delete(key
);
150 private getIdTagsCacheIndexesAddressableKey(prefix
: string, uid
: string): string {
151 return `${prefix}${uid}`;
154 private getIdTagsFromFile(file
: string): string[] {
155 let idTags
: string[] = [];
159 idTags
= JSON
.parse(fs
.readFileSync(file
, 'utf8')) as string[];
161 FileUtils
.handleFileException(
163 FileType
.Authorization
,
164 error
as NodeJS
.ErrnoException
,
169 logger
.info(`${this.logPrefix(file)} No id tags file given`);
174 private logPrefix
= (file
: string): string => {
175 return Utils
.logPrefix(` Id tags cache for id tags file '${file}' |`);