+ private getConfigurationFromFile(): ChargingStationConfiguration | undefined {
+ let configuration: ChargingStationConfiguration | undefined;
+ if (this.configurationFile && fs.existsSync(this.configurationFile)) {
+ try {
+ if (this.sharedLRUCache.hasChargingStationConfiguration(this.configurationFileHash)) {
+ configuration = this.sharedLRUCache.getChargingStationConfiguration(
+ this.configurationFileHash
+ );
+ } else {
+ const measureId = `${FileType.ChargingStationConfiguration} read`;
+ const beginId = PerformanceStatistics.beginMeasure(measureId);
+ configuration = JSON.parse(
+ fs.readFileSync(this.configurationFile, 'utf8')
+ ) as ChargingStationConfiguration;
+ PerformanceStatistics.endMeasure(measureId, beginId);
+ this.configurationFileHash = configuration.configurationHash;
+ this.sharedLRUCache.setChargingStationConfiguration(configuration);
+ }
+ } catch (error) {
+ FileUtils.handleFileException(
+ this.configurationFile,
+ FileType.ChargingStationConfiguration,
+ error as NodeJS.ErrnoException,
+ this.logPrefix()
+ );
+ }
+ }
+ return configuration;
+ }
+
+ private saveConfiguration(): void {
+ if (this.configurationFile) {
+ try {
+ if (!fs.existsSync(path.dirname(this.configurationFile))) {
+ fs.mkdirSync(path.dirname(this.configurationFile), { recursive: true });
+ }
+ const configurationData: ChargingStationConfiguration =
+ Utils.cloneObject(this.getConfigurationFromFile()) ?? {};
+ this.ocppConfiguration?.configurationKey &&
+ (configurationData.configurationKey = this.ocppConfiguration.configurationKey);
+ this.stationInfo && (configurationData.stationInfo = this.stationInfo);
+ delete configurationData.configurationHash;
+ const configurationHash = crypto
+ .createHash(Constants.DEFAULT_HASH_ALGORITHM)
+ .update(JSON.stringify(configurationData))
+ .digest('hex');
+ if (this.configurationFileHash !== configurationHash) {
+ configurationData.configurationHash = configurationHash;
+ const measureId = `${FileType.ChargingStationConfiguration} write`;
+ const beginId = PerformanceStatistics.beginMeasure(measureId);
+ const fileDescriptor = fs.openSync(this.configurationFile, 'w');
+ fs.writeFileSync(fileDescriptor, JSON.stringify(configurationData, null, 2), 'utf8');
+ fs.closeSync(fileDescriptor);
+ PerformanceStatistics.endMeasure(measureId, beginId);
+ this.sharedLRUCache.deleteChargingStationConfiguration(this.configurationFileHash);
+ this.configurationFileHash = configurationHash;
+ this.sharedLRUCache.setChargingStationConfiguration(configurationData);
+ } else {
+ logger.debug(
+ `${this.logPrefix()} Not saving unchanged charging station configuration file ${
+ this.configurationFile
+ }`
+ );
+ }
+ } catch (error) {
+ FileUtils.handleFileException(
+ this.configurationFile,
+ FileType.ChargingStationConfiguration,
+ error as NodeJS.ErrnoException,
+ this.logPrefix()
+ );
+ }
+ } else {
+ logger.error(
+ `${this.logPrefix()} Trying to save charging station configuration to undefined configuration file`
+ );
+ }
+ }
+
+ private getOcppConfigurationFromTemplate(): ChargingStationOcppConfiguration | undefined {
+ return this.getTemplateFromFile()?.Configuration;
+ }
+
+ private getOcppConfigurationFromFile(): ChargingStationOcppConfiguration | undefined {
+ let configuration: ChargingStationConfiguration | undefined;
+ if (this.getOcppPersistentConfiguration() === true) {
+ const configurationFromFile = this.getConfigurationFromFile();
+ configuration = configurationFromFile?.configurationKey && configurationFromFile;
+ }
+ if (!Utils.isNullOrUndefined(configuration)) {
+ delete configuration.stationInfo;
+ delete configuration.configurationHash;
+ }
+ return configuration;
+ }
+
+ private getOcppConfiguration(): ChargingStationOcppConfiguration | undefined {
+ let ocppConfiguration: ChargingStationOcppConfiguration | undefined =
+ this.getOcppConfigurationFromFile();
+ if (!ocppConfiguration) {
+ ocppConfiguration = this.getOcppConfigurationFromTemplate();
+ }
+ return ocppConfiguration;
+ }
+
+ private async onOpen(): Promise<void> {
+ if (this.isWebSocketConnectionOpened() === true) {
+ logger.info(
+ `${this.logPrefix()} Connection to OCPP server through ${this.wsConnectionUrl.toString()} succeeded`
+ );
+ if (this.isRegistered() === false) {
+ // Send BootNotification
+ let registrationRetryCount = 0;
+ do {
+ this.bootNotificationResponse = await this.ocppRequestService.requestHandler<
+ BootNotificationRequest,
+ BootNotificationResponse
+ >(this, RequestCommand.BOOT_NOTIFICATION, this.bootNotificationRequest, {
+ skipBufferingOnError: true,
+ });
+ if (this.isRegistered() === false) {
+ this.getRegistrationMaxRetries() !== -1 && registrationRetryCount++;
+ await Utils.sleep(
+ this?.bootNotificationResponse?.interval
+ ? this.bootNotificationResponse.interval * 1000
+ : Constants.OCPP_DEFAULT_BOOT_NOTIFICATION_INTERVAL
+ );
+ }
+ } while (
+ this.isRegistered() === false &&
+ (registrationRetryCount <= this.getRegistrationMaxRetries() ||
+ this.getRegistrationMaxRetries() === -1)
+ );
+ }
+ if (this.isRegistered() === true) {
+ if (this.isInAcceptedState() === true) {
+ await this.startMessageSequence();
+ }
+ } else {
+ logger.error(
+ `${this.logPrefix()} Registration failure: max retries reached (${this.getRegistrationMaxRetries()}) or retry disabled (${this.getRegistrationMaxRetries()})`
+ );
+ }
+ this.wsConnectionRestarted = false;
+ this.autoReconnectRetryCount = 0;
+ parentPort?.postMessage(MessageChannelUtils.buildUpdatedMessage(this));
+ } else {
+ logger.warn(
+ `${this.logPrefix()} Connection to OCPP server through ${this.wsConnectionUrl.toString()} failed`
+ );
+ }
+ }
+
+ private async onClose(code: number, reason: Buffer): Promise<void> {
+ switch (code) {
+ // Normal close
+ case WebSocketCloseEventStatusCode.CLOSE_NORMAL: