UNPKG

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