diff --git a/01_Data/src/layers/sequelize/model/DeviceModel/Component.ts b/01_Data/src/layers/sequelize/model/DeviceModel/Component.ts index 1cda963ae..d7a7e4285 100644 --- a/01_Data/src/layers/sequelize/model/DeviceModel/Component.ts +++ b/01_Data/src/layers/sequelize/model/DeviceModel/Component.ts @@ -4,9 +4,10 @@ // SPDX-License-Identifier: Apache 2.0 import { ComponentType, CustomDataType, EVSEType, Namespace, VariableType } from "@citrineos/base"; -import { BelongsTo, Column, DataType, ForeignKey, HasMany, Model, Table } from "sequelize-typescript"; +import { BelongsTo, BelongsToMany, Column, DataType, ForeignKey, Model, Table } from "sequelize-typescript"; import { Evse } from "./Evse"; import { Variable } from "./Variable"; +import { ComponentVariable } from "./ComponentVariable"; @Table export class Component extends Model implements ComponentType { @@ -21,30 +22,31 @@ export class Component extends Model implements ComponentType { @Column({ type: DataType.STRING, - unique: 'evse_name_instance' + unique: 'name_instance' }) declare name: string; - + @Column({ type: DataType.STRING, - unique: 'evse_name_instance' + unique: 'name_instance' }) declare instance?: string; /** * Relations */ - + @BelongsTo(() => Evse) declare evse?: EVSEType; @ForeignKey(() => Evse) - @Column({ - type: DataType.INTEGER, - unique: 'evse_name_instance' - }) + @Column(DataType.INTEGER) declare evseDatabaseId?: number; - @HasMany(() => Variable) + @BelongsToMany(() => Variable, () => ComponentVariable) declare variables?: VariableType[]; + + // Declare the association methods, to be automatically generated by Sequelize at runtime + public addVariable!: (variable: Variable) => Promise; + public getVariables!: () => Promise; } \ No newline at end of file diff --git a/01_Data/src/layers/sequelize/model/DeviceModel/ComponentVariable.ts b/01_Data/src/layers/sequelize/model/DeviceModel/ComponentVariable.ts new file mode 100644 index 000000000..bcfbb849b --- /dev/null +++ b/01_Data/src/layers/sequelize/model/DeviceModel/ComponentVariable.ts @@ -0,0 +1,19 @@ +// Copyright (c) 2023 S44, LLC +// Copyright Contributors to the CitrineOS Project +// +// SPDX-License-Identifier: Apache 2.0 + +import { Table, Model, ForeignKey, Column, DataType } from "sequelize-typescript"; +import { Component } from "./Component"; +import { Variable } from "./Variable"; + +@Table +export class ComponentVariable extends Model { + @ForeignKey(() => Component) + @Column(DataType.INTEGER) + declare componentId: number; + + @ForeignKey(() => Variable) + @Column(DataType.INTEGER) + declare variableId: number; +} \ No newline at end of file diff --git a/01_Data/src/layers/sequelize/model/DeviceModel/Variable.ts b/01_Data/src/layers/sequelize/model/DeviceModel/Variable.ts index 6b93e4de2..d96a66b1b 100644 --- a/01_Data/src/layers/sequelize/model/DeviceModel/Variable.ts +++ b/01_Data/src/layers/sequelize/model/DeviceModel/Variable.ts @@ -4,10 +4,11 @@ // SPDX-License-Identifier: Apache 2.0 import { ComponentType, CustomDataType, Namespace, VariableCharacteristicsType, VariableType } from "@citrineos/base"; -import { BelongsTo, Column, DataType, ForeignKey, HasMany, HasOne, Model, Table } from "sequelize-typescript"; +import { BelongsToMany, Column, DataType, HasMany, HasOne, Model, Table } from "sequelize-typescript"; import { Component } from "./Component"; import { VariableAttribute } from "./VariableAttribute"; import { VariableCharacteristics } from "./VariableCharacteristics"; +import { ComponentVariable } from "./ComponentVariable"; @Table export class Variable extends Model implements VariableType { @@ -16,16 +17,16 @@ export class Variable extends Model implements VariableType { declare customData?: CustomDataType; - /** - * Fields - */ + /** + * Fields + */ @Column({ type: DataType.STRING, unique: 'name_instance' }) declare name: string; - + @Column({ type: DataType.STRING, unique: 'name_instance' @@ -35,17 +36,17 @@ export class Variable extends Model implements VariableType { /** * Relations */ - - @BelongsTo(() => Component) - declare component: ComponentType; - @ForeignKey(() => Component) - @Column(DataType.INTEGER) - declare componentId?: number; + @BelongsToMany(() => Component, () => ComponentVariable) + declare components?: ComponentType[]; @HasMany(() => VariableAttribute) declare variableAttributes?: VariableAttribute[]; @HasOne(() => VariableCharacteristics) - declare variableCharacteristics: VariableCharacteristicsType; + declare variableCharacteristics?: VariableCharacteristicsType; + + // Declare the association methods, to be automatically generated by Sequelize at runtime + public addComponent!: (variable: Component) => Promise; + public getComponents!: () => Promise; } \ No newline at end of file diff --git a/01_Data/src/layers/sequelize/model/DeviceModel/VariableAttribute.ts b/01_Data/src/layers/sequelize/model/DeviceModel/VariableAttribute.ts index d384e52f8..08e20ed50 100644 --- a/01_Data/src/layers/sequelize/model/DeviceModel/VariableAttribute.ts +++ b/01_Data/src/layers/sequelize/model/DeviceModel/VariableAttribute.ts @@ -3,15 +3,14 @@ // // SPDX-License-Identifier: Apache 2.0 -import { AttributeEnumType, ComponentType, CustomDataType, DataEnumType, EVSEType, MutabilityEnumType, Namespace, StatusInfoType, VariableAttributeType, VariableType } from "@citrineos/base"; -import { BeforeCreate, BeforeUpdate, BelongsTo, Column, DataType, ForeignKey, HasMany, HasOne, Index, Model, Table } from "sequelize-typescript"; +import { AttributeEnumType, ComponentType, CustomDataType, DataEnumType, EVSEType, MutabilityEnumType, Namespace, VariableAttributeType, VariableType } from "@citrineos/base"; +import { BelongsTo, Column, DataType, ForeignKey, HasMany, Index, Model, Table } from "sequelize-typescript"; import * as bcrypt from "bcrypt"; import { Variable } from "./Variable"; import { Component } from "./Component"; import { Evse } from "./Evse"; import { Boot } from "../Boot"; import { VariableStatus } from "./VariableStatus"; -import { VariableCharacteristics } from "./VariableCharacteristics"; @Table export class VariableAttribute extends Model implements VariableAttributeType { @@ -26,14 +25,14 @@ export class VariableAttribute extends Model implements VariableAttributeType { @Index @Column({ - unique: 'stationId_type_variableId_componentId_evseDatabaseId' + unique: 'stationId_type_variableId_componentId' }) declare stationId: string; @Column({ type: DataType.STRING, defaultValue: AttributeEnumType.Actual, - unique: 'stationId_type_variableId_componentId_evseDatabaseId' + unique: 'stationId_type_variableId_componentId' }) declare type?: AttributeEnumType; // From VariableCharacteristics, which belongs to Variable associated with this VariableAttribute @@ -44,7 +43,7 @@ export class VariableAttribute extends Model implements VariableAttributeType { declare dataType: DataEnumType; @Column({ - // TODO: Make this configurable? + // TODO: Make this configurable? also used in VariableStatus model type: DataType.STRING(4000), set(valueString) { if (valueString) { @@ -91,7 +90,7 @@ export class VariableAttribute extends Model implements VariableAttributeType { @ForeignKey(() => Variable) @Column({ type: DataType.INTEGER, - unique: 'stationId_type_variableId_componentId_evseDatabaseId' + unique: 'stationId_type_variableId_componentId' }) declare variableId?: number; @@ -101,7 +100,7 @@ export class VariableAttribute extends Model implements VariableAttributeType { @ForeignKey(() => Component) @Column({ type: DataType.INTEGER, - unique: 'stationId_type_variableId_componentId_evseDatabaseId' + unique: 'stationId_type_variableId_componentId' }) declare componentId?: number; @@ -109,10 +108,7 @@ export class VariableAttribute extends Model implements VariableAttributeType { declare evse?: EVSEType; @ForeignKey(() => Evse) - @Column({ - type: DataType.INTEGER, - unique: 'stationId_type_variableId_componentId_evseDatabaseId' - }) + @Column(DataType.INTEGER) declare evseDatabaseId?: number; // History of variable status. Can be directly from GetVariablesResponse or SetVariablesResponse, or from NotifyReport handling, or from 'setOnCharger' option for data api diff --git a/01_Data/src/layers/sequelize/model/DeviceModel/VariableStatus.ts b/01_Data/src/layers/sequelize/model/DeviceModel/VariableStatus.ts index 11e0bd43d..da5591857 100644 --- a/01_Data/src/layers/sequelize/model/DeviceModel/VariableStatus.ts +++ b/01_Data/src/layers/sequelize/model/DeviceModel/VariableStatus.ts @@ -14,7 +14,7 @@ export class VariableStatus extends Model { declare customData?: CustomDataType; - @Column(DataType.STRING) + @Column(DataType.STRING(4000)) declare value: string; @Column(DataType.STRING) diff --git a/01_Data/src/layers/sequelize/repository/DeviceModel.ts b/01_Data/src/layers/sequelize/repository/DeviceModel.ts index 91c2391d8..22921f045 100644 --- a/01_Data/src/layers/sequelize/repository/DeviceModel.ts +++ b/01_Data/src/layers/sequelize/repository/DeviceModel.ts @@ -3,95 +3,117 @@ // // SPDX-License-Identifier: Apache 2.0 -import { AttributeEnumType, ComponentType, GetVariableResultType, ReportDataType, SetVariableDataType, SetVariableResultType, SetVariableStatusEnumType, StatusInfoType, VariableType } from "@citrineos/base"; +import { AttributeEnumType, ComponentType, DataEnumType, GetVariableResultType, MutabilityEnumType, ReportDataType, SetVariableDataType, SetVariableResultType, VariableType } from "@citrineos/base"; import { VariableAttributeQuerystring } from "../../../interfaces/queries/VariableAttribute"; import { SequelizeRepository } from "./Base"; import { IDeviceModelRepository } from "../../../interfaces"; import { Op } from "sequelize"; import { VariableAttribute, Component, Evse, Variable, VariableCharacteristics } from "../model/DeviceModel"; import { VariableStatus } from "../model/DeviceModel/VariableStatus"; +import { ComponentVariable } from "../model/DeviceModel/ComponentVariable"; // TODO: Document this export class DeviceModelRepository extends SequelizeRepository implements IDeviceModelRepository { async createOrUpdateDeviceModelByStationId(value: ReportDataType, stationId: string): Promise { - const component: ComponentType = value.component; - const variable: VariableType = value.variable; - let savedComponent = await this.s.models[Component.MODEL_NAME].findOne({ - where: { name: component.name, instance: component.instance ? component.instance : null }, - include: component.evse ? [{ model: Evse, where: { id: component.evse.id, connectorId: component.evse.connectorId ? component.evse.connectorId : null } }] : [Evse] - }); - if (!savedComponent) { - // Create component if not exists - savedComponent = await Component.build({ - ...component - }, { include: [Evse] }).save(); - } - let savedVariable = await this.s.models[Variable.MODEL_NAME].findOne({ - where: { name: variable.name, instance: variable.instance ? variable.instance : null }, - include: [{ model: Component, where: { id: savedComponent.get('id') } }] - }); - if (!savedVariable) { - // Create variable if not exists - savedVariable = await Variable.build({ - componentId: savedComponent.get('id'), - ...variable - }).save(); + // Doing this here so that no records are created if the data is invalid + const variablAttributeTypes = value.variableAttribute.map(attr => attr.type ? attr.type : AttributeEnumType.Actual) + if (variablAttributeTypes.length != (new Set(variablAttributeTypes)).size) { + throw new Error("All variable attributes in ReportData must have different types."); } - let savedVariableCharacteristics: VariableCharacteristics | undefined = await this.s.models[VariableCharacteristics.MODEL_NAME].findOne({ - where: { variableId: savedVariable.get('id') } - }).then(row => (row as VariableCharacteristics)); + + const [component, variable] = await this.findOrCreateEvseAndComponentAndVariable(value.component, value.variable, stationId); + + let dataType: DataEnumType | null = null; if (value.variableCharacteristics) { - const variableCharacteristicsModel = VariableCharacteristics.build({ - variableId: savedVariable.get('id'), - ...value.variableCharacteristics + const [variableCharacteristics, variableCharacteristicsCreated] = await VariableCharacteristics.upsert({ + ...value.variableCharacteristics, + variable: variable, + variableId: variable.id }); - // TODO: Although VariableCharacteristics is optional, VariableCharacteristics.dataType is a vital field for understanding VariableAttribute.value and should be set to some default and incorporated in handling VariableAttribute.value - // Create or update variable characteristics - if (savedVariableCharacteristics) { - for (const k in variableCharacteristicsModel.dataValues) { - savedVariableCharacteristics.setDataValue(k, variableCharacteristicsModel.getDataValue(k)); - } - savedVariableCharacteristics = await savedVariableCharacteristics.save(); - } else { - savedVariableCharacteristics = await variableCharacteristicsModel.save(); - } + dataType = variableCharacteristics.dataType; } - const savedVariableAttributes: VariableAttribute[] = []; - const evseDatabaseId = savedComponent.get('evseDatabaseId'); - for (const variableAttribute of value.variableAttribute) { - const variableAttributeModel = VariableAttribute.build({ + + return await Promise.all(value.variableAttribute.map(async variableAttribute => { + // Even though defaults are set on the VariableAttribute model, those only apply when creating an object + // So we need to set them here to ensure they are set correctly when updating + const [savedVariableAttribute, variableAttributeCreated] = await VariableAttribute.upsert({ stationId: stationId, - variableId: savedVariable.get('id'), - componentId: savedComponent.get('id'), - evseDatabaseId: evseDatabaseId, - dataType: savedVariableCharacteristics ? savedVariableCharacteristics.dataType : undefined, - ...variableAttribute - }, { - include: [{ model: Variable, where: { id: savedVariable.get('id') }, include: [VariableCharacteristics] }, - { model: Component, where: { id: savedComponent.get('id') }, include: evseDatabaseId ? [{ model: Evse, where: { databaseId: evseDatabaseId } }] : [] }] + variableId: variable.id, + componentId: component.id, + evseDatabaseId: component.evseDatabaseId, + type: variableAttribute.type ? variableAttribute.type : AttributeEnumType.Actual, + dataType: dataType, + value: variableAttribute.value, + mutability: variableAttribute.mutability ? variableAttribute.mutability : MutabilityEnumType.ReadWrite, + persistent: variableAttribute.persistent ? variableAttribute.persistent : false, + constant: variableAttribute.constant ? variableAttribute.constant : false }); - let savedVariableAttribute = await super.readByQuery({ - where: { stationId: stationId, type: variableAttribute.type ? variableAttribute.type : AttributeEnumType.Actual }, - include: [{ model: Variable, where: { id: savedVariable.get('id') }, include: [VariableCharacteristics] }, - { model: Component, where: { id: savedComponent.get('id') }, include: evseDatabaseId ? [{ model: Evse, where: { databaseId: evseDatabaseId } }] : [] }] - }, VariableAttribute.MODEL_NAME) - // Create or update variable attribute - if (savedVariableAttribute) { - for (const k in variableAttributeModel.dataValues) { - if (k !== 'id') { // id is not a field that can be updated - const updatedValue = variableAttributeModel.getDataValue(k); - savedVariableAttribute.setDataValue(k, updatedValue); - } - } - savedVariableAttribute = await savedVariableAttribute.save(); - } else { // Reload in order to eager load (otherwise component & variable will be undefined) - savedVariableAttribute = await (await variableAttributeModel.save()).reload(); + return savedVariableAttribute; + })); + } + async findOrCreateEvseAndComponentAndVariable(componentType: ComponentType, variableType: VariableType, stationId: string): Promise<[Component, Variable]> { + const component = await this.findOrCreateEvseAndComponent(componentType, stationId); + + const [variable, variableCreated] = await Variable.findOrCreate({ + where: { name: variableType.name, instance: variableType.instance ? variableType.instance : null }, + defaults: { + ...variableType } - savedVariableAttributes.push(savedVariableAttribute); + }); + + // This can happen asynchronously + ComponentVariable.findOrCreate({ + where: { componentId: component.id, variableId: variable.id } + }) + + return [component, variable] + } + + async findOrCreateEvseAndComponent(componentType: ComponentType, stationId: string): Promise { + const evse = componentType.evse ? (await Evse.findOrCreate({ where: { id: componentType.evse.id, connectorId: componentType.evse.connectorId ? componentType.evse.connectorId : null } }))[0] : undefined; + + const [component, componentCreated] = await Component.findOrCreate({ + where: { name: componentType.name, instance: componentType.instance ? componentType.instance : null }, + defaults: { // Explicit assignment because evse field is a relation and is not able to accept a default value + name: componentType.name, + instance: componentType.instance + } + }); + // Note: this permits changing the evse related to the component + if (component.evseDatabaseId !== evse?.databaseId && evse) { + await component.update({ evseDatabaseId: evse.databaseId }); } - return savedVariableAttributes; + + if (componentCreated) { + // Excerpt from OCPP 2.0.1 Part 1 Architecture & Topology - 4.2 : + // "When a Charging Station does not report: Present, Available and/or Enabled + // the Central System SHALL assume them to be readonly and set to true." + // These default variables and their attributes are created here if the component is new, + // and they will be overwritten if they are included in the update + const defaultComponentVariableNames = ['Present', 'Available', 'Enabled']; + for (const defaultComponentVariableName of defaultComponentVariableNames) { + const [defaultComponentVariable, defaultComponentVariableCreated] = await Variable.findOrCreate({ where: { name: defaultComponentVariableName, instance: null } }); + + // This can happen asynchronously + ComponentVariable.findOrCreate({ + where: { componentId: component.id, variableId: defaultComponentVariable.id } + }) + + await VariableAttribute.create({ + stationId: stationId, + variableId: defaultComponentVariable.id, + componentId: component.id, + evseDatabaseId: evse?.databaseId, + dataType: DataEnumType.boolean, + value: 'true', + mutability: MutabilityEnumType.ReadOnly + }); + } + } + + return component; } async createOrUpdateByGetVariablesResultAndStationId(getVariablesResult: GetVariableResultType[], stationId: string): Promise { @@ -99,18 +121,10 @@ export class DeviceModelRepository extends SequelizeRepository { let savedVariableAttributes: VariableAttribute[] = []; for (const data of setVariablesData) { - savedVariableAttributes = await savedVariableAttributes.concat(await this.createOrUpdateDeviceModelByStationId({ + const savedVariableAttribute = (await this.createOrUpdateDeviceModelByStationId({ component: { - name: data.component.name, - instance: data.component.instance, - ...(data.component.evse ? { - evse: { - id: data.component.evse.id, - connectorId: data.component.evse.connectorId - } - } : {}) + ...data.component }, variable: { - name: data.variable.name, - instance: data.variable.instance + ...data.variable }, variableAttribute: [ { @@ -154,7 +160,8 @@ export class DeviceModelRepository extends SequelizeRepository { const savedVariableAttribute = await super.readByQuery({ where: { stationId: stationId, type: result.attributeType ? result.attributeType : AttributeEnumType.Actual }, - include: [VariableStatus, - { - model: Component, where: { name: result.component.name, instance: result.component.instance ? result.component.instance : null }, - include: result.component.evse ? [{ model: Evse, where: { id: result.component.evse.id, connectorId: result.component.evse.connectorId ? result.component.evse.connectorId : null } }] : [] - }, - { model: Variable, where: { name: result.variable.name, instance: result.variable.instance ? result.variable.instance : null } }] + include: [{ model: Component, where: { name: result.component.name, instance: result.component.instance ? result.component.instance : null } }, + { model: Variable, where: { name: result.variable.name, instance: result.variable.instance ? result.variable.instance : null } }] }, VariableAttribute.MODEL_NAME); if (savedVariableAttribute) { - const savedVariableStatusArray = [await VariableStatus.build({ + await VariableStatus.create({ value: savedVariableAttribute.value, status: result.attributeStatus, statusInfo: result.attributeStatusInfo, variableAttributeId: savedVariableAttribute.get('id') - }, { include: [VariableAttribute] }).save()]; - savedVariableAttribute.statuses = savedVariableAttribute.statuses ? savedVariableAttribute.statuses.concat(savedVariableStatusArray) : savedVariableStatusArray; - return savedVariableAttribute; + }); + // Reload in order to include the statuses + return savedVariableAttribute.reload({ + include: [VariableStatus] + }); } else { - throw new Error("Unable to update variable attribute..."); + throw new Error("Unable to update variable attribute status..."); } } - readAllSetVariableByStationId(stationId: string): Promise { - return super.readAllByQuery({ + async readAllSetVariableByStationId(stationId: string): Promise { + const variableAttributeArray = await super.readAllByQuery({ where: { stationId: stationId, bootConfigSetId: { [Op.ne]: null } }, include: [{ model: Component, include: [Evse] }, Variable] - }, VariableAttribute.MODEL_NAME) - .then(variableAttributeArray => { - const setVariableDataTypeArray: SetVariableDataType[] = []; - for (const variableAttribute of variableAttributeArray) { - setVariableDataTypeArray.push(this.createSetVariableDataType(variableAttribute)); - } - return setVariableDataTypeArray; - }); + }, VariableAttribute.MODEL_NAME); + + return variableAttributeArray.map(variableAttribute => this.createSetVariableDataType(variableAttribute)); } readAllByQuery(query: VariableAttributeQuerystring): Promise { @@ -226,16 +226,10 @@ export class DeviceModelRepository extends SequelizeRepository { // TODO: Look into fixing that // sequelizeLogger.debug(timing, sql); diff --git a/03_Modules/Reporting/src/module/module.ts b/03_Modules/Reporting/src/module/module.ts index 7050ba941..b59c62c5a 100644 --- a/03_Modules/Reporting/src/module/module.ts +++ b/03_Modules/Reporting/src/module/module.ts @@ -3,8 +3,9 @@ // // SPDX-License-Identifier: Apache 2.0 -import { AbstractModule, CallAction, SystemConfig, ICache, IMessageSender, IMessageHandler, EventGroup, AsHandler, IMessage, NotifyReportRequest, HandlerProperties, SetVariableStatusEnumType, NotifyReportResponse, NotifyMonitoringReportRequest, NotifyMonitoringReportResponse, LogStatusNotificationRequest, LogStatusNotificationResponse, NotifyCustomerInformationRequest, NotifyCustomerInformationResponse, GetBaseReportResponse, StatusNotificationRequest, StatusNotificationResponse, SecurityEventNotificationRequest, SecurityEventNotificationResponse } from "@citrineos/base"; +import { AbstractModule, CallAction, SystemConfig, ICache, IMessageSender, IMessageHandler, EventGroup, AsHandler, IMessage, NotifyReportRequest, HandlerProperties, SetVariableStatusEnumType, NotifyReportResponse, NotifyMonitoringReportRequest, NotifyMonitoringReportResponse, LogStatusNotificationRequest, LogStatusNotificationResponse, NotifyCustomerInformationRequest, NotifyCustomerInformationResponse, GetBaseReportResponse, SecurityEventNotificationRequest, SecurityEventNotificationResponse } from "@citrineos/base"; import { IDeviceModelRepository, ISecurityEventRepository, sequelize } from "@citrineos/data"; +import { Component, Variable } from "@citrineos/data/lib/layers/sequelize"; import { RabbitMqReceiver, RabbitMqSender, Timer } from "@citrineos/util"; import deasyncPromise from "deasync-promise"; import { ILogObj, Logger } from 'tslog'; @@ -152,18 +153,13 @@ export class ReportingModule extends AbstractModule { ): Promise { this._logger.info("NotifyReport received:", message, props); - 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.websocket.maxCachingSeconds); - this._logger.info("Ongoing", success, message.payload.requestId); - } - for (const reportDataType of (message.payload.reportData ? message.payload.reportData : [])) { const variableAttributes = await this._deviceModelRepository.createOrUpdateDeviceModelByStationId(reportDataType, message.context.stationId); for (const variableAttribute of variableAttributes) { + // Reload is necessary because the upsert method used in createOrUpdateDeviceModelByStationId does not allow eager loading + await variableAttribute.reload({ + include: [Component, Variable] + }); this._deviceModelRepository.updateResultByStationId({ attributeType: variableAttribute.type, attributeStatus: SetVariableStatusEnumType.Accepted, attributeStatusInfo: { reasonCode: message.action }, @@ -172,6 +168,15 @@ export class ReportingModule extends AbstractModule { } } + 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.websocket.maxCachingSeconds); + this._logger.info("Ongoing", success, message.payload.requestId); + } + // Create response const response: NotifyReportResponse = {}; diff --git a/Server/docker-compose.yml b/Server/docker-compose.yml index a9a636923..5147b61ae 100644 --- a/Server/docker-compose.yml +++ b/Server/docker-compose.yml @@ -25,6 +25,12 @@ services: POSTGRES_DB: citrine POSTGRES_USER: citrine POSTGRES_PASSWORD: "citrine" + healthcheck: + test: ["CMD-SHELL", "pg_isready", "-d", "db_prod"] + interval: 30s + timeout: 60s + retries: 5 + start_period: 80s redis: image: redis:latest ports: @@ -56,6 +62,9 @@ services: volumes: - ./data/directus/uploads:/directus/uploads - ./data/directus/extensions:/directus/extensions + depends_on: + ocpp-db: + condition: service_healthy environment: KEY: '1234567890' SECRET: '0987654321' diff --git a/Server/src/config/envs/docker.ts b/Server/src/config/envs/docker.ts index 1d422e8f4..423dc3765 100644 --- a/Server/src/config/envs/docker.ts +++ b/Server/src/config/envs/docker.ts @@ -17,7 +17,7 @@ export function createDockerConfig() { unknownChargerStatus: RegistrationStatusEnumType.Accepted, getBaseReportOnPending: true, bootWithRejectedVariables: true, - autoAccept: false, + autoAccept: true, endpointPrefix: "/configuration" }, evdriver: {