@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
JavaScript
"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