@citrineos/reporting
Version:
The reporting module for OCPP v2.0.1. This module is not intended to be used directly, but rather as a dependency for other modules.
307 lines • 17.8 kB
JavaScript
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
import { AbstractModule, AsHandler, ErrorCode, EventGroup, Namespace, OCPP1_6, OCPP1_6_CallAction, OCPP2_0_1, OCPP2_0_1_CallAction, OcppError, OCPPValidator, OCPPVersion, } from '@citrineos/base';
import { Component, sequelize, Variable } from '@citrineos/data';
import { Logger } from 'tslog';
import { DeviceModelService } from './services.js';
/**
* Component that handles provisioning related messages.
*/
export class ReportingModule extends AbstractModule {
/**
* Get Base Report variables. While NotifyReport requests correlated with a GetBaseReport's requestId
* are still being sent, cache value is 'ongoing'. Once a NotifyReport with tbc === false (or undefined)
* is received, cache value is 'complete'.
*/
static GET_BASE_REPORT_REQUEST_ID_MAX = 10000000; // 10,000,000
static GET_BASE_REPORT_ONGOING_CACHE_VALUE = 'ongoing';
static GET_BASE_REPORT_COMPLETE_CACHE_VALUE = 'complete';
_deviceModelService;
/**
* Fields
*/
_requests = [];
_responses = [];
_securityEventRepository;
_variableMonitoringRepository;
_ocppMessageRepository;
/**
* This is the constructor function that initializes the {@link ReportingModule}.
*
* @param {BootstrapConfig & SystemConfig} config - The `config` contains configuration settings for the module.
*
* @param {ICache} [cache] - The cache instance which is shared among the modules & Central System to pass information such as blacklisted actions or boot status.
*
* @param {IMessageSender} [sender] - The `sender` parameter is an optional parameter that represents an instance of the {@link IMessageSender} interface.
* It is used to send messages from the central system to external systems or devices. If no `sender` is provided, a default {@link RabbitMqSender} instance is created and used.
*
* @param {IMessageHandler} [handler] - The `handler` parameter is an optional parameter that represents an instance of the {@link IMessageHandler} interface.
* It is used to handle incoming messages and dispatch them to the appropriate methods or functions. If no `handler` is provided, a default {@link RabbitMqReceiver} instance is created and used.
*
* @param {Logger<ILogObj>} [logger] - The `logger` parameter is an optional parameter that represents an instance of {@link Logger<ILogObj>}.
* It is used to propagate system wide logger settings and will serve as the parent logger for any sub-component logging. If no `logger` is provided, a default {@link Logger<ILogObj>} instance is created and used.
*
* @param {IDeviceModelRepository} [deviceModelRepository] - An optional parameter of type {@link IDeviceModelRepository} which represents a repository for accessing and manipulating variable data.
* If no `deviceModelRepository` is provided, a default {@link sequelize:deviceModelRepository} instance is created and used.
*
* @param {ISecurityEventRepository} [securityEventRepository] - An optional parameter of type {@link ISecurityEventRepository} which represents a repository for accessing security event notification data.
*
* @param {IVariableMonitoringRepository} [variableMonitoringRepository] - An optional parameter of type {@link IVariableMonitoringRepository} which represents a repository for accessing and manipulating monitoring data.
*/
constructor(config, cache, sender, handler, logger, ocppValidator, deviceModelRepository, securityEventRepository, variableMonitoringRepository, ocppMessageRepository) {
super(config, cache, handler, sender, EventGroup.Reporting, logger, ocppValidator);
this._requests = config.modules.reporting.requests;
this._responses = config.modules.reporting.responses;
this._deviceModelRepository =
deviceModelRepository || new sequelize.SequelizeDeviceModelRepository(config, this._logger);
this._securityEventRepository =
securityEventRepository ||
new sequelize.SequelizeSecurityEventRepository(config, this._logger);
this._variableMonitoringRepository =
variableMonitoringRepository ||
new sequelize.SequelizeVariableMonitoringRepository(config, this._logger);
this._ocppMessageRepository =
ocppMessageRepository || new sequelize.SequelizeOCPPMessageRepository(config, this._logger);
this._deviceModelService = new DeviceModelService(this._deviceModelRepository);
}
/**
* Constructor
*/
_deviceModelRepository;
get deviceModelRepository() {
return this._deviceModelRepository;
}
/**
* Handle Requests
*/
async _handleLogStatusNotification(message, props) {
this._logger.debug('LogStatusNotification received:', message, props);
// TODO: LogStatusNotification is usually triggered. Ideally, it should be sent to the callbackUrl from the message api that sent the trigger message
// Validate requestId requirement
// requestId is mandatory unless message was triggered by TriggerMessageRequest AND no log upload ongoing
if (!message.payload.requestId) {
await this.sendCallErrorWithMessage(message, new OcppError(message.context.correlationId, ErrorCode.OccurrenceConstraintViolation, 'RequestId is required.'));
return;
}
// Create response
const response = {};
const messageConfirmation = await this.sendCallResultWithMessage(message, response);
this._logger.debug('LogStatusNotification response sent: ', messageConfirmation);
}
async _handleNotifyCustomerInformation(message, props) {
this._logger.debug('NotifyCustomerInformation request received:', message, props);
// Validate requestId was provided in a previous CustomerInformationRequest
const requestId = message.payload.requestId;
const previousRequest = await this._ocppMessageRepository.readAllByQuery(message.context.tenantId, {
where: {
tenantId: message.context.tenantId,
stationId: message.context.stationId,
action: OCPP2_0_1_CallAction.CustomerInformation,
message: {
requestId: requestId,
},
},
limit: 1,
}, Namespace.OCPPMessage);
if (!previousRequest || previousRequest.length === 0) {
await this.sendCallErrorWithMessage(message, new OcppError(message.context.correlationId, ErrorCode.PropertyConstraintViolation, 'RequestId was not provided in a CustomerInformationRequest.'));
return;
}
// Create response
const response = {};
const messageConfirmation = await this.sendCallResultWithMessage(message, response);
this._logger.debug('NotifyCustomerInformation response sent: ', messageConfirmation);
}
async _handleNotifyMonitoringReport(message, props) {
this._logger.debug('NotifyMonitoringReport request received:', message, props);
for (const monitorType of message.payload.monitor ? message.payload.monitor : []) {
const stationId = message.context.stationId;
const [component, variable] = await this._deviceModelRepository.findOrCreateEvseAndComponentAndVariable(message.context.tenantId, monitorType.component, monitorType.variable);
await this._variableMonitoringRepository.createOrUpdateByMonitoringDataTypeAndStationId(message.context.tenantId, monitorType, component ? component.id : null, variable ? variable.id : null, stationId);
}
// Create response
const response = {};
const messageConfirmation = await this.sendCallResultWithMessage(message, response);
this._logger.debug('NotifyMonitoringReport response sent: ', messageConfirmation);
}
async _handleNotifyReport(message, props) {
this._logger.info('NotifyReport received:', message, props);
const timestamp = message.payload.generatedAt;
try {
for (const reportDataType of message.payload.reportData ? message.payload.reportData : []) {
// To keep consistency with VariableAttributeType defined in OCPP 2.0.1:
// mutability: Default is ReadWrite when omitted.
// if it is not present, we set it to ReadWrite
for (const variableAttr of reportDataType.variableAttribute) {
if (!variableAttr.mutability) {
variableAttr.mutability = OCPP2_0_1.MutabilityEnumType.ReadWrite;
}
}
const variableAttributes = await this._deviceModelRepository.createOrUpdateDeviceModelByStationId(message.context.tenantId, reportDataType, message.context.stationId, timestamp);
for (const variableAttribute of variableAttributes) {
// Reload is necessary because in createOrUpdateDeviceModelByStationId does not do eager loading
await variableAttribute.reload({
include: [Component, Variable],
});
await this._deviceModelRepository.updateResultByStationId(message.context.tenantId, {
attributeType: variableAttribute.type,
attributeStatus: OCPP2_0_1.SetVariableStatusEnumType.Accepted,
attributeStatusInfo: { reasonCode: message.action },
component: variableAttribute.component,
variable: variableAttribute.variable,
}, message.context.stationId, timestamp);
}
}
}
catch (error) {
if (error.name === 'SequelizeForeignKeyConstraintError') {
await this.sendCallErrorWithMessage(message, new OcppError(message.context.correlationId, ErrorCode.PropertyConstraintViolation, 'Referenced entity does not exist.'));
return;
}
throw error;
}
if (!message.payload.tbc) {
// Default if omitted is false
const success = await this._cache.set(message.payload.requestId.toString(), ReportingModule.GET_BASE_REPORT_COMPLETE_CACHE_VALUE, message.context.stationId);
this._logger.info('Completed', success, message.payload.requestId);
}
else {
// tbc (to be continued) is true
// Continue to set get base report ongoing. Will extend the timeout.
const success = await this._cache.set(message.payload.requestId.toString(), ReportingModule.GET_BASE_REPORT_ONGOING_CACHE_VALUE, message.context.stationId, this.config.maxCachingSeconds);
this._logger.info('Ongoing', success, message.payload.requestId);
}
// Create response
const response = {};
await this.sendCallResultWithMessage(message, response);
this._logger.debug('NotifyReport response sent:', message, props);
}
async _handleSecurityEventNotification(message, props) {
this._logger.debug('SecurityEventNotification request received:', message, props);
await this._securityEventRepository.createByStationId(message.context.tenantId, message.payload, message.context.stationId);
await this.sendCallResultWithMessage(message, {});
}
/**
* Handle responses
*/
_handleGetBaseReport(message, props) {
this._logger.debug('GetBaseReport response received:', message, props);
}
_handleGetReport(message, props) {
this._logger.debug('GetReport response received:', message, props);
const status = message.payload.status;
const statusInfo = message.payload.statusInfo;
if (status === OCPP2_0_1.GenericDeviceModelStatusEnumType.Rejected ||
status === OCPP2_0_1.GenericDeviceModelStatusEnumType.NotSupported) {
this._logger.error('Failed to get report.', status, statusInfo?.reasonCode, statusInfo?.additionalInfo);
}
}
async _handleGetMonitoringReport(message, props) {
this._logger.debug('GetMonitoringReport response received:', message, props);
const status = message.payload.status;
const statusInfo = message.payload.statusInfo;
if (status === OCPP2_0_1.GenericDeviceModelStatusEnumType.Rejected ||
status === OCPP2_0_1.GenericDeviceModelStatusEnumType.NotSupported) {
this._logger.error('Failed to get monitoring report.', status, statusInfo?.reasonCode, statusInfo?.additionalInfo);
}
}
_handleGetLog(message, props) {
this._logger.debug('GetLog response received:', message, props);
}
_handleCustomerInformation(message, props) {
this._logger.debug('CustomerInformation response received:', message, props);
}
/**
* OCPP 1.6 Handlers
*/
async _handleDiagnosticsStatusNotification(message, props) {
this._logger.debug('DiagnosticsStatusNotification received:', message, props);
// Create response
const response = {};
const messageConfirmation = await this.sendCallResultWithMessage(message, response);
this._logger.debug('DiagnosticsStatusNotification response sent: ', messageConfirmation);
}
_handleGetDiagnostics(message, props) {
this._logger.debug('GetDiagnostics response received:', message, props);
}
}
__decorate([
AsHandler(OCPPVersion.OCPP2_0_1, OCPP2_0_1_CallAction.LogStatusNotification),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, Object]),
__metadata("design:returntype", Promise)
], ReportingModule.prototype, "_handleLogStatusNotification", null);
__decorate([
AsHandler(OCPPVersion.OCPP2_0_1, OCPP2_0_1_CallAction.NotifyCustomerInformation),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, Object]),
__metadata("design:returntype", Promise)
], ReportingModule.prototype, "_handleNotifyCustomerInformation", null);
__decorate([
AsHandler(OCPPVersion.OCPP2_0_1, OCPP2_0_1_CallAction.NotifyMonitoringReport),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, Object]),
__metadata("design:returntype", Promise)
], ReportingModule.prototype, "_handleNotifyMonitoringReport", null);
__decorate([
AsHandler(OCPPVersion.OCPP2_0_1, OCPP2_0_1_CallAction.NotifyReport),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, Object]),
__metadata("design:returntype", Promise)
], ReportingModule.prototype, "_handleNotifyReport", null);
__decorate([
AsHandler(OCPPVersion.OCPP2_0_1, OCPP2_0_1_CallAction.SecurityEventNotification),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, Object]),
__metadata("design:returntype", Promise)
], ReportingModule.prototype, "_handleSecurityEventNotification", null);
__decorate([
AsHandler(OCPPVersion.OCPP2_0_1, OCPP2_0_1_CallAction.GetBaseReport),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, Object]),
__metadata("design:returntype", void 0)
], ReportingModule.prototype, "_handleGetBaseReport", null);
__decorate([
AsHandler(OCPPVersion.OCPP2_0_1, OCPP2_0_1_CallAction.GetReport),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, Object]),
__metadata("design:returntype", void 0)
], ReportingModule.prototype, "_handleGetReport", null);
__decorate([
AsHandler(OCPPVersion.OCPP2_0_1, OCPP2_0_1_CallAction.GetMonitoringReport),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, Object]),
__metadata("design:returntype", Promise)
], ReportingModule.prototype, "_handleGetMonitoringReport", null);
__decorate([
AsHandler(OCPPVersion.OCPP2_0_1, OCPP2_0_1_CallAction.GetLog),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, Object]),
__metadata("design:returntype", void 0)
], ReportingModule.prototype, "_handleGetLog", null);
__decorate([
AsHandler(OCPPVersion.OCPP2_0_1, OCPP2_0_1_CallAction.CustomerInformation),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, Object]),
__metadata("design:returntype", void 0)
], ReportingModule.prototype, "_handleCustomerInformation", null);
__decorate([
AsHandler(OCPPVersion.OCPP1_6, OCPP1_6_CallAction.DiagnosticsStatusNotification),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, Object]),
__metadata("design:returntype", Promise)
], ReportingModule.prototype, "_handleDiagnosticsStatusNotification", null);
__decorate([
AsHandler(OCPPVersion.OCPP1_6, OCPP1_6_CallAction.GetDiagnostics),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, Object]),
__metadata("design:returntype", void 0)
], ReportingModule.prototype, "_handleGetDiagnostics", null);
//# sourceMappingURL=module.js.map