--- /dev/null
+import { FileType } from '../types/FileType';
+import FileUtils from '../utils/FileUtils';
+import Utils from '../utils/Utils';
+import fs from 'fs';
+import logger from '../utils/Logger';
+
+export default class AuthorizedTagsCache {
+ private static instance: AuthorizedTagsCache | null = null;
+ private readonly tagsCaches: Map<string, string[]>;
+ private readonly FSWatchers: Map<string, fs.FSWatcher>;
+
+ private constructor() {
+ this.tagsCaches = new Map<string, string[]>();
+ this.FSWatchers = new Map<string, fs.FSWatcher>();
+ }
+
+ public static getInstance(): AuthorizedTagsCache {
+ if (!AuthorizedTagsCache.instance) {
+ AuthorizedTagsCache.instance = new AuthorizedTagsCache();
+ }
+ return AuthorizedTagsCache.instance;
+ }
+
+ public getAuthorizedTags(file: string): string[] {
+ if (!this.hasTags(file)) {
+ this.setTags(file, this.getAuthorizedTagsFromFile(file));
+ // Monitor authorization file
+ !this.FSWatchers.has(file) &&
+ this.FSWatchers.set(
+ file,
+ FileUtils.watchJsonFile(
+ this.logPrefix(file),
+ FileType.Authorization,
+ file,
+ null,
+ (event, filename) => {
+ if (filename && event === 'change') {
+ try {
+ logger.debug(
+ this.logPrefix(file) +
+ ' ' +
+ FileType.Authorization +
+ ' file have changed, reload'
+ );
+ this.deleteTags(file);
+ this.deleteFSWatcher(file);
+ } catch (error) {
+ FileUtils.handleFileException(
+ this.logPrefix(file),
+ FileType.Authorization,
+ file,
+ error as NodeJS.ErrnoException,
+ {
+ throwError: false,
+ }
+ );
+ }
+ }
+ }
+ )
+ );
+ }
+ return this.getTags(file);
+ }
+
+ private hasTags(file: string): boolean {
+ return this.tagsCaches.has(file);
+ }
+
+ private setTags(file: string, tags: string[]) {
+ return this.tagsCaches.set(file, tags);
+ }
+
+ private getTags(file: string): string[] {
+ return this.tagsCaches.get(file);
+ }
+
+ private deleteTags(file: string): boolean {
+ return this.tagsCaches.delete(file);
+ }
+
+ private deleteFSWatcher(file: string): boolean {
+ this.FSWatchers.get(file).close();
+ return this.FSWatchers.delete(file);
+ }
+
+ 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(
+ this.logPrefix(file),
+ FileType.Authorization,
+ file,
+ error as NodeJS.ErrnoException
+ );
+ }
+ } 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 { WSError, WebSocketCloseEventStatusCode } from '../types/WebSocket';
import WebSocket, { Data, RawData } from 'ws';
+import AuthorizedTagsCache from './AuthorizedTagsCache';
import AutomaticTransactionGenerator from './AutomaticTransactionGenerator';
import { AutomaticTransactionGeneratorConfiguration } from '../types/AutomaticTransactionGenerator';
import BaseError from '../exception/BaseError';
export default class ChargingStation {
public hashId!: string;
public readonly templateFile: string;
- public authorizedTags!: string[];
+ public authorizedTagsCache: AuthorizedTagsCache;
public stationInfo!: ChargingStationInfo;
public readonly connectors: Map<number, ConnectorStatus>;
public ocppConfiguration!: ChargingStationOcppConfiguration;
private wsConnectionRestarted: boolean;
private autoReconnectRetryCount: number;
private stopped: boolean;
+ private templateFileWatcher!: fs.FSWatcher;
private readonly cache: ChargingStationCache;
private automaticTransactionGenerator!: AutomaticTransactionGenerator;
private webSocketPingSetInterval!: NodeJS.Timeout;
this.wsConnectionRestarted = false;
this.autoReconnectRetryCount = 0;
this.cache = ChargingStationCache.getInstance();
+ this.authorizedTagsCache = AuthorizedTagsCache.getInstance();
this.connectors = new Map<number, ConnectorStatus>();
this.requests = new Map<string, CachedRequest>();
this.messageBuffer = new Set<string>();
}
public getRandomIdTag(): string {
- const index = Math.floor(Utils.secureRandom() * this.authorizedTags.length);
- return this.authorizedTags[index];
+ const authorizationFile = ChargingStationUtils.getAuthorizationFile(this.stationInfo);
+ const index = Math.floor(
+ Utils.secureRandom() * this.authorizedTagsCache.getAuthorizedTags(authorizationFile).length
+ );
+ return this.authorizedTagsCache.getAuthorizedTags(authorizationFile)[index];
}
public hasAuthorizedTags(): boolean {
- return !Utils.isEmptyArray(this.authorizedTags);
+ return !Utils.isEmptyArray(
+ this.authorizedTagsCache.getAuthorizedTags(
+ ChargingStationUtils.getAuthorizationFile(this.stationInfo)
+ )
+ );
}
public getEnableStatistics(): boolean | undefined {
this.wsConnection.on('ping', this.onPing.bind(this) as (this: WebSocket, data: Buffer) => void);
// Handle WebSocket pong
this.wsConnection.on('pong', this.onPong.bind(this) as (this: WebSocket, data: Buffer) => void);
- // Monitor authorization file
- FileUtils.watchJsonFile<string[]>(
- this.logPrefix(),
- FileType.Authorization,
- ChargingStationUtils.getAuthorizationFile(this.stationInfo),
- this.authorizedTags
- );
// Monitor charging station template file
- FileUtils.watchJsonFile(
+ this.templateFileWatcher = FileUtils.watchJsonFile(
this.logPrefix(),
FileType.ChargingStationTemplate,
this.templateFile,
this.performanceStatistics.stop();
}
this.cache.deleteChargingStationConfiguration(this.configurationFileHash);
+ this.templateFileWatcher.close();
this.cache.deleteChargingStationTemplate(this.stationInfo?.templateHash);
this.bootNotificationResponse = null;
parentPort.postMessage({
this.bootNotificationRequest = ChargingStationUtils.createBootNotificationRequest(
this.stationInfo
);
- this.authorizedTags = ChargingStationUtils.getAuthorizedTags(
- this.stationInfo,
- this.templateFile,
- this.logPrefix()
- );
this.powerDivider = this.getPowerDivider();
// OCPP configuration
this.ocppConfiguration = this.getOcppConfiguration();
import ChargingStationInfo from '../types/ChargingStationInfo';
import Configuration from '../utils/Configuration';
import Constants from '../utils/Constants';
-import { FileType } from '../types/FileType';
-import FileUtils from '../utils/FileUtils';
import { SampledValueTemplate } from '../types/MeasurandPerPhaseSampledValueTemplates';
import { StandardParametersKey } from '../types/ocpp/Configuration';
import Utils from '../utils/Utils';
import { WorkerProcessType } from '../types/Worker';
import crypto from 'crypto';
import { fileURLToPath } from 'url';
-import fs from 'fs';
import logger from '../utils/Logger';
import moment from 'moment';
import path from 'path';
);
}
- public static getAuthorizedTags(
- stationInfo: ChargingStationInfo,
- templateFile: string,
- logPrefix: string
- ): string[] {
- let authorizedTags: string[] = [];
- const authorizationFile = ChargingStationUtils.getAuthorizationFile(stationInfo);
- if (authorizationFile) {
- try {
- // Load authorization file
- authorizedTags = JSON.parse(fs.readFileSync(authorizationFile, 'utf8')) as string[];
- } catch (error) {
- FileUtils.handleFileException(
- logPrefix,
- FileType.Authorization,
- authorizationFile,
- error as NodeJS.ErrnoException
- );
- }
- } else {
- logger.info(logPrefix + ' No authorization file given in template file ' + templateFile);
- }
- return authorizedTags;
- }
-
public static getAuthorizationFile(stationInfo: ChargingStationInfo): string | undefined {
return (
stationInfo.authorizationFile &&
import type ChargingStation from '../../ChargingStation';
import { ChargingStationConfigurationUtils } from '../../ChargingStationConfigurationUtils';
+import { ChargingStationUtils } from '../../ChargingStationUtils';
import Constants from '../../../utils/Constants';
import { DefaultResponse } from '../../../types/ocpp/Responses';
import { ErrorType } from '../../../types/ocpp/ErrorType';
if (
chargingStation.getLocalAuthListEnabled() &&
chargingStation.hasAuthorizedTags() &&
- chargingStation.authorizedTags.find((value) => value === commandPayload.idTag)
+ chargingStation.authorizedTagsCache
+ .getAuthorizedTags(
+ ChargingStationUtils.getAuthorizationFile(chargingStation.stationInfo)
+ )
+ .find((value) => value === commandPayload.idTag)
) {
connectorStatus.localAuthorizeIdTag = commandPayload.idTag;
connectorStatus.idTagLocalAuthorized = true;
import logger from './Logger';
export default class FileUtils {
- static watchJsonFile<T extends JsonType>(
+ public static watchJsonFile<T extends JsonType>(
logPrefix: string,
fileType: FileType,
file: string,
- attribute?: T,
+ refreshedVariable?: T,
listener: fs.WatchListener<string> = (event, filename) => {
if (filename && event === 'change') {
try {
logger.debug(logPrefix + ' ' + fileType + ' file ' + file + ' have changed, reload');
- attribute && (attribute = JSON.parse(fs.readFileSync(file, 'utf8')) as T);
+ refreshedVariable && (refreshedVariable = JSON.parse(fs.readFileSync(file, 'utf8')) as T);
} catch (error) {
FileUtils.handleFileException(logPrefix, fileType, file, error as NodeJS.ErrnoException, {
throwError: false,
}
}
}
- ) {
+ ): fs.FSWatcher {
if (file) {
try {
- fs.watch(file, listener);
+ return fs.watch(file, listener);
} catch (error) {
FileUtils.handleFileException(logPrefix, fileType, file, error as NodeJS.ErrnoException, {
throwError: false,
}
}
- static handleFileException(
+ public static handleFileException(
logPrefix: string,
fileType: FileType,
file: string,