UNPKG

@citrineos/configuration

Version:

The configuration module for OCPP v2.0.1. This module is not intended to be used directly, but rather as a dependency for other modules.

273 lines 15.2 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.BootNotificationService = void 0; const data_1 = require("@citrineos/data"); const base_1 = require("@citrineos/base"); const tslog_1 = require("tslog"); class BootNotificationService { constructor(bootRepository, cache, config, logger) { this._bootRepository = bootRepository; this._cache = cache; this._config = config; this._logger = logger ? logger.getSubLogger({ name: this.constructor.name }) : new tslog_1.Logger({ name: this.constructor.name }); } determineBootStatus(bootConfig) { let bootStatus = bootConfig ? data_1.OCPP2_0_1_Mapper.BootMapper.toRegistrationStatusEnumType(bootConfig.status) : this._config.ocpp2_0_1.unknownChargerStatus; if (bootStatus === base_1.OCPP2_0_1.RegistrationStatusEnumType.Pending) { let needToGetBaseReport = this._config.ocpp2_0_1.getBaseReportOnPending; let needToSetVariables = false; if (bootConfig) { if (bootConfig.getBaseReportOnPending !== undefined && bootConfig.getBaseReportOnPending !== null) { needToGetBaseReport = bootConfig.getBaseReportOnPending; } if (bootConfig.pendingBootSetVariables && bootConfig.pendingBootSetVariables.length > 0) { needToSetVariables = true; } } if (!needToGetBaseReport && !needToSetVariables && this._config.ocpp2_0_1.autoAccept) { bootStatus = base_1.OCPP2_0_1.RegistrationStatusEnumType.Accepted; } } return bootStatus; } createBootNotificationResponse(stationId) { return __awaiter(this, void 0, void 0, function* () { // Unknown chargers, chargers without a BootConfig, will use SystemConfig.unknownChargerStatus for status. const bootConfig = yield this._bootRepository.readByKey(stationId); const bootStatus = this.determineBootStatus(bootConfig); // When any BootConfig field is not set, the corresponding field on the SystemConfig will be used. return { currentTime: new Date().toISOString(), status: bootStatus, statusInfo: data_1.OCPP2_0_1_Mapper.BootMapper.toStatusInfo(bootConfig === null || bootConfig === void 0 ? void 0 : bootConfig.statusInfo), interval: bootStatus === base_1.OCPP2_0_1.RegistrationStatusEnumType.Accepted ? (bootConfig === null || bootConfig === void 0 ? void 0 : bootConfig.heartbeatInterval) || this._config.heartbeatInterval : (bootConfig === null || bootConfig === void 0 ? void 0 : bootConfig.bootRetryInterval) || this._config.bootRetryInterval, }; }); } updateBootConfig(bootNotificationResponse, stationId) { return __awaiter(this, void 0, void 0, function* () { let bootConfigDbEntity = yield this._bootRepository.readByKey(stationId); if (!bootConfigDbEntity) { const unknownChargerBootConfig = { status: bootNotificationResponse.status, statusInfo: bootNotificationResponse.statusInfo, }; bootConfigDbEntity = yield this._bootRepository.createOrUpdateByKey(unknownChargerBootConfig, stationId); } if (!bootConfigDbEntity) { throw new Error('Unable to create/update BootConfig...'); } else { bootConfigDbEntity.lastBootTime = bootNotificationResponse.currentTime; yield bootConfigDbEntity.save(); } return bootConfigDbEntity; }); } /** * Determines whether to blacklist or whitelist charger actions based on its boot status. * * If the new boot is accepted and the charger actions were previously blacklisted, then whitelist the charger actions. * If the new boot is not accepted and charger actions were previously whitelisted, then blacklist the charger actions. * * @param stationId * @param cachedBootStatus * @param bootNotificationResponseStatus */ cacheChargerActionsPermissions(stationId, cachedBootStatus, bootNotificationResponseStatus) { return __awaiter(this, void 0, void 0, function* () { // New boot status is Accepted and cachedBootStatus exists (meaning there was a previous Rejected or Pending boot) if (bootNotificationResponseStatus === base_1.OCPP2_0_1.RegistrationStatusEnumType.Accepted) { if (cachedBootStatus) { // Undo blacklisting of charger-originated actions const promises = Array.from(base_1.OCPP2_0_1_CALL_SCHEMA_MAP).map((_a) => __awaiter(this, [_a], void 0, function* ([action]) { if (action !== base_1.OCPP2_0_1_CallAction.BootNotification) { return this._cache.remove(action, stationId); } })); yield Promise.all(promises); // Remove cached boot status yield this._cache.remove(base_1.BOOT_STATUS, stationId); this._logger.debug('Cached boot status removed: ', cachedBootStatus); } } else if (!cachedBootStatus) { // Status is not Accepted; i.e. Status is Rejected or Pending. // Cached boot status for charger did not exist; i.e. this is the first BootNotificationResponse to be Rejected or Pending. // Blacklist all charger-originated actions except BootNotification // GetReport messages will need to un-blacklist NotifyReport // TriggerMessage will need to un-blacklist the message it triggers const promises = Array.from(base_1.OCPP2_0_1_CALL_SCHEMA_MAP).map((_a) => __awaiter(this, [_a], void 0, function* ([action]) { if (action !== base_1.OCPP2_0_1_CallAction.BootNotification) { return this._cache.set(action, 'blacklisted', stationId); } })); yield Promise.all(promises); } }); } createGetBaseReportRequest(stationId, maxCachingSeconds) { return __awaiter(this, void 0, void 0, function* () { // OCTT tool does not meet B07.FR.04; instead always sends requestId === 0 // Commenting out this line, using requestId === 0 until fixed (10/26/2023) // const requestId = Math.floor(Math.random() * ConfigurationModule.GET_BASE_REPORT_REQUEST_ID_MAX); const requestId = 0; yield this._cache.set(requestId.toString(), 'ongoing', stationId, maxCachingSeconds); return { requestId: requestId, reportBase: base_1.OCPP2_0_1.ReportBaseEnumType.FullInventory, }; }); } /** * Based on the GetBaseReportMessageConfirmation, checks the cache to ensure GetBaseReport truly succeeded. * If GetBaseReport did not succeed, this method will throw. Otherwise, it will finish without throwing. * * @param stationId * @param requestId * @param getBaseReportMessageConfirmation * @param maxCachingSeconds */ confirmGetBaseReportSuccess(stationId, requestId, getBaseReportMessageConfirmation, maxCachingSeconds) { return __awaiter(this, void 0, void 0, function* () { if (getBaseReportMessageConfirmation.success) { this._logger.debug(`GetBaseReport successfully sent to charger: ${getBaseReportMessageConfirmation}`); // Wait for GetBaseReport to complete let getBaseReportCacheValue = yield this._cache.onChange(requestId, maxCachingSeconds, stationId); while (getBaseReportCacheValue === 'ongoing') { getBaseReportCacheValue = yield this._cache.onChange(requestId, maxCachingSeconds, stationId); } if (getBaseReportCacheValue === 'complete') { this._logger.debug('GetBaseReport process successful.'); // All NotifyReports have been processed } else { throw new Error('GetBaseReport process failed--message timed out without a response.'); } } else { throw new Error(`GetBaseReport failed: ${JSON.stringify(getBaseReportMessageConfirmation)}`); } }); } /** * Methods for OCPP 1.6 */ determineOcpp16BootStatus(bootConfig) { let bootStatus = bootConfig ? data_1.OCPP1_6_Mapper.BootMapper.toRegistrationStatusEnumType(bootConfig.status) : this._config.ocpp1_6.unknownChargerStatus; if (bootStatus === base_1.OCPP1_6.BootNotificationResponseStatus.Pending) { let needToGetConfigurations = true; let needToChangeConfigurations = true; if (bootConfig) { if (bootConfig.getConfigurationsOnPending !== undefined && bootConfig.getConfigurationsOnPending !== null) { needToGetConfigurations = bootConfig.getConfigurationsOnPending; } if (bootConfig.changeConfigurationsOnPending !== undefined && bootConfig.changeConfigurationsOnPending !== null) { needToChangeConfigurations = bootConfig.changeConfigurationsOnPending; } } if (!needToGetConfigurations && !needToChangeConfigurations) { bootStatus = base_1.OCPP1_6.BootNotificationResponseStatus.Accepted; } } return bootStatus; } createOcpp16BootNotificationResponse(stationId) { return __awaiter(this, void 0, void 0, function* () { const boot = yield this._bootRepository.readByKey(stationId); const status = this.determineOcpp16BootStatus(boot); return { currentTime: new Date().toISOString(), status, interval: status === base_1.OCPP1_6.BootNotificationResponseStatus.Accepted ? (boot === null || boot === void 0 ? void 0 : boot.heartbeatInterval) || this._config.heartbeatInterval : (boot === null || boot === void 0 ? void 0 : boot.bootRetryInterval) || this._config.bootRetryInterval, }; }); } /** * Determines whether to blacklist or whitelist charger actions based on its boot status. * * If the new boot is accepted and the charger actions were previously blacklisted, then whitelist the charger actions. * If the new boot is not accepted and charger actions were previously whitelisted, then blacklist the charger actions. * * @param stationId * @param cachedBootStatus * @param bootNotificationResponseStatus */ cacheOcpp16ChargerActionsPermissions(stationId, cachedBootStatus, bootNotificationResponseStatus) { return __awaiter(this, void 0, void 0, function* () { // New boot status is Accepted and cachedBootStatus exists (meaning there was a previous Rejected or Pending boot) if (bootNotificationResponseStatus === base_1.OCPP1_6.BootNotificationResponseStatus.Accepted) { if (cachedBootStatus) { // Undo blacklisting of charger-originated actions const promises = Array.from(base_1.OCPP1_6_CALL_SCHEMA_MAP).map((_a) => __awaiter(this, [_a], void 0, function* ([action]) { if (action !== base_1.OCPP1_6_CallAction.BootNotification) { return this._cache.remove(action, stationId); } })); yield Promise.all(promises); // Remove cached boot status yield this._cache.remove(base_1.BOOT_STATUS, stationId); this._logger.debug(`Cached boot status ${cachedBootStatus} removed for station ${stationId}.`); } } else if (!cachedBootStatus) { // Status is not Accepted; i.e. Status is Rejected or Pending. // Cached boot status for charger did not exist; i.e. this is the first BootNotificationResponse to be Rejected or Pending. // Blacklist all charger-originated actions except BootNotification // ChangeConfiguration, GetConfiguration and TriggerMessage will need to un-blacklist the message it triggers const promises = Array.from(base_1.OCPP1_6_CALL_SCHEMA_MAP).map((_a) => __awaiter(this, [_a], void 0, function* ([action]) { if (action !== base_1.OCPP1_6_CallAction.BootNotification) { return this._cache.set(action, 'blacklisted', stationId); } })); yield Promise.all(promises); } }); } updateOcpp16BootConfig(response, stationId) { return __awaiter(this, void 0, void 0, function* () { const heartbeatInterval = response.status === base_1.OCPP1_6.BootNotificationResponseStatus.Accepted ? response.interval : undefined; const bootRetryInterval = response.status !== base_1.OCPP1_6.BootNotificationResponseStatus.Accepted ? response.interval : undefined; const unknownChargerBootConfig = { status: response.status, heartbeatInterval, bootRetryInterval, }; let bootConfigDbEntity = yield this._bootRepository.createOrUpdateByKey(unknownChargerBootConfig, stationId); if (bootConfigDbEntity) { bootConfigDbEntity = yield this._bootRepository.updateLastBootTimeByKey(response.currentTime, stationId); } if (!bootConfigDbEntity) { throw new Error('Unable to create/update BootConfig...'); } return bootConfigDbEntity; }); } } exports.BootNotificationService = BootNotificationService; //# sourceMappingURL=BootNotificationService.js.map