UNPKG

@citrineos/data

Version:

The OCPP data module which includes all persistence layer implementation.

441 lines 24 kB
"use strict"; // Copyright (c) 2023 S44, LLC // Copyright Contributors to the CitrineOS Project // // SPDX-License-Identifier: Apache 2.0 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.SequelizeDeviceModelRepository = void 0; const base_1 = require("@citrineos/base"); const Base_1 = require("./Base"); const sequelize_1 = require("sequelize"); const DeviceModel_1 = require("../model/DeviceModel"); const ComponentVariable_1 = require("../model/DeviceModel/ComponentVariable"); // TODO: Document this class SequelizeDeviceModelRepository extends Base_1.SequelizeRepository { constructor(config, logger, sequelizeInstance, variable, component, evse, componentVariable, variableCharacteristics, variableStatus) { super(config, DeviceModel_1.VariableAttribute.MODEL_NAME, logger, sequelizeInstance); this.variable = variable ? variable : new Base_1.SequelizeRepository(config, DeviceModel_1.Variable.MODEL_NAME, logger, sequelizeInstance); this.component = component ? component : new Base_1.SequelizeRepository(config, DeviceModel_1.Component.MODEL_NAME, logger, sequelizeInstance); this.evse = evse ? evse : new Base_1.SequelizeRepository(config, DeviceModel_1.Evse.MODEL_NAME, logger, sequelizeInstance); this.componentVariable = componentVariable ? componentVariable : new Base_1.SequelizeRepository(config, ComponentVariable_1.ComponentVariable.MODEL_NAME, logger, sequelizeInstance); this.variableCharacteristics = variableCharacteristics ? variableCharacteristics : new Base_1.SequelizeRepository(config, DeviceModel_1.VariableCharacteristics.MODEL_NAME, logger, sequelizeInstance); this.variableStatus = variableStatus ? variableStatus : new Base_1.SequelizeRepository(config, DeviceModel_1.VariableStatus.MODEL_NAME, logger, sequelizeInstance); } createOrUpdateDeviceModelByStationId(value, stationId, isoTimestamp) { return __awaiter(this, void 0, void 0, function* () { var _a, _b, _c, _d; // Doing this here so that no records are created if the data is invalid const variableAttributeTypes = value.variableAttribute.map((attr) => { var _a; return (_a = attr.type) !== null && _a !== void 0 ? _a : base_1.OCPP2_0_1.AttributeEnumType.Actual; }); if (variableAttributeTypes.length !== new Set(variableAttributeTypes).size) { throw new Error('All variable attributes in ReportData must have different types.'); } const [component, variable] = yield this.findOrCreateEvseAndComponentAndVariable(value.component, value.variable, stationId); let dataType = null; if (value.variableCharacteristics) { const variableCharacteristicsType = value.variableCharacteristics; dataType = variableCharacteristicsType.dataType; const vc = { unit: (_a = variableCharacteristicsType.unit) !== null && _a !== void 0 ? _a : null, dataType, minLimit: (_b = variableCharacteristicsType.minLimit) !== null && _b !== void 0 ? _b : null, maxLimit: (_c = variableCharacteristicsType.maxLimit) !== null && _c !== void 0 ? _c : null, valuesList: (_d = variableCharacteristicsType.valuesList) !== null && _d !== void 0 ? _d : null, supportsMonitoring: variableCharacteristicsType.supportsMonitoring, variableId: variable.id, }; yield this.s.transaction((transaction) => __awaiter(this, void 0, void 0, function* () { const savedVariableCharacteristics = yield this.s.models[DeviceModel_1.VariableCharacteristics.MODEL_NAME].findOne({ where: { variableId: variable.id, }, transaction, }); if (!savedVariableCharacteristics) { const createdVariableCharacteristics = yield DeviceModel_1.VariableCharacteristics.create(vc, { transaction, }); this.variableCharacteristics.emit('created', [createdVariableCharacteristics]); return createdVariableCharacteristics; } else { return yield this.variableCharacteristics.updateAllByQuery(vc, { where: { variableId: variable.id, }, transaction, }); } })); } return yield Promise.all(value.variableAttribute.map((variableAttribute) => __awaiter(this, void 0, void 0, function* () { var _a, _b, _c, _d, _e, _f, _g; const [savedVariableAttribute, variableAttributeCreated] = yield this.readOrCreateByQuery({ where: { // the composite unique index of VariableAttribute stationId: stationId, variableId: variable.id, componentId: component.id, type: (_a = variableAttribute.type) !== null && _a !== void 0 ? _a : base_1.OCPP2_0_1.AttributeEnumType.Actual, }, defaults: { // used to define what must be created in case nothing was found. If the defaults do not // contain values for every column, Sequelize will take the values given to where (if present). evseDatabaseId: component.evseDatabaseId, dataType, value: variableAttribute.value, generatedAt: isoTimestamp, mutability: (_b = variableAttribute.mutability) !== null && _b !== void 0 ? _b : base_1.OCPP2_0_1.MutabilityEnumType.ReadWrite, persistent: variableAttribute.persistent ? variableAttribute.persistent : false, constant: variableAttribute.constant ? variableAttribute.constant : false, }, }); if (!variableAttributeCreated) { return (yield this.updateByKey({ evseDatabaseId: component.evseDatabaseId, dataType: dataType !== null && dataType !== void 0 ? dataType : savedVariableAttribute.dataType, type: (_c = variableAttribute.type) !== null && _c !== void 0 ? _c : savedVariableAttribute.type, value: (_d = variableAttribute.value) !== null && _d !== void 0 ? _d : null, mutability: (_e = variableAttribute.mutability) !== null && _e !== void 0 ? _e : savedVariableAttribute.mutability, persistent: (_f = variableAttribute.persistent) !== null && _f !== void 0 ? _f : false, constant: (_g = variableAttribute.constant) !== null && _g !== void 0 ? _g : false, generatedAt: isoTimestamp, }, savedVariableAttribute.id)); } return savedVariableAttribute; }))); }); } findOrCreateEvseAndComponentAndVariable(componentType, variableType, stationId) { return __awaiter(this, void 0, void 0, function* () { const component = yield this.findOrCreateEvseAndComponent(componentType, stationId); const [variable] = yield this.variable.readOrCreateByQuery({ where: { name: variableType.name, instance: variableType.instance ? variableType.instance : null, }, defaults: Object.assign({}, variableType), }); // TODO discuss & verify appropriate way to remove associations between components and variables (not currently possible) // This can happen asynchronously yield this.componentVariable.readOrCreateByQuery({ where: { componentId: component.id, variableId: variable.id }, }); return [component, variable]; }); } findOrCreateEvseAndComponent(componentType, stationId) { return __awaiter(this, void 0, void 0, function* () { const evse = componentType.evse ? (yield this.evse.readOrCreateByQuery({ where: { id: componentType.evse.id, connectorId: componentType.evse.connectorId ? componentType.evse.connectorId : null, }, }))[0] : undefined; const [component, componentCreated] = yield this.component.readOrCreateByQuery({ 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 === null || evse === void 0 ? void 0 : evse.databaseId) && evse) { yield this.component.updateByKey({ evseDatabaseId: evse.databaseId }, component.get('id')); } if (componentCreated && stationId) { // Only execute if this method is called in the context of a specific station // 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] = yield this.variable.readOrCreateByQuery({ where: { name: defaultComponentVariableName, instance: null, }, }); // This can happen asynchronously yield this.componentVariable.readOrCreateByQuery({ where: { componentId: component.id, variableId: defaultComponentVariable.id }, }); yield this.create(DeviceModel_1.VariableAttribute.build({ stationId, variableId: defaultComponentVariable.id, componentId: component.id, evseDatabaseId: evse === null || evse === void 0 ? void 0 : evse.databaseId, dataType: base_1.OCPP2_0_1.DataEnumType.boolean, value: 'true', mutability: base_1.OCPP2_0_1.MutabilityEnumType.ReadOnly, })); } } return component; }); } createOrUpdateByGetVariablesResultAndStationId(getVariablesResult, stationId, isoTimestamp) { return __awaiter(this, void 0, void 0, function* () { const savedVariableAttributes = []; for (const result of getVariablesResult) { const savedVariableAttribute = (yield this.createOrUpdateDeviceModelByStationId({ component: Object.assign({}, result.component), variable: Object.assign({}, result.variable), variableAttribute: [ { type: result.attributeType, value: result.attributeValue, }, ], }, stationId, isoTimestamp))[0]; yield this.variableStatus.create(DeviceModel_1.VariableStatus.build({ value: result.attributeValue, status: result.attributeStatus, statusInfo: result.attributeStatusInfo, variableAttributeId: savedVariableAttribute.get('id'), }, { include: [DeviceModel_1.VariableAttribute] })); savedVariableAttributes.push(savedVariableAttribute); } return savedVariableAttributes; }); } createOrUpdateBySetVariablesDataAndStationId(setVariablesData, stationId, isoTimestamp) { return __awaiter(this, void 0, void 0, function* () { const savedVariableAttributes = []; for (const data of setVariablesData) { const savedVariableAttribute = (yield this.createOrUpdateDeviceModelByStationId({ component: Object.assign({}, data.component), variable: Object.assign({}, data.variable), variableAttribute: [ { type: data.attributeType, value: data.attributeValue, }, ], }, stationId, isoTimestamp))[0]; savedVariableAttributes.push(savedVariableAttribute); } return savedVariableAttributes; }); } updateResultByStationId(result, stationId, isoTimestamp) { const _super = Object.create(null, { readOnlyOneByQuery: { get: () => super.readOnlyOneByQuery } }); return __awaiter(this, void 0, void 0, function* () { var _a; const savedVariableAttribute = yield _super.readOnlyOneByQuery.call(this, { where: { stationId, type: (_a = result.attributeType) !== null && _a !== void 0 ? _a : base_1.OCPP2_0_1.AttributeEnumType.Actual }, include: [ { model: DeviceModel_1.Component, where: { name: result.component.name, instance: result.component.instance ? result.component.instance : null, }, }, { model: DeviceModel_1.Variable, where: { name: result.variable.name, instance: result.variable.instance ? result.variable.instance : null, }, }, ], }); if (savedVariableAttribute) { yield this.variableStatus.create(DeviceModel_1.VariableStatus.build({ value: savedVariableAttribute.value, status: result.attributeStatus, statusInfo: result.attributeStatusInfo, variableAttributeId: savedVariableAttribute.get('id'), })); if (result.attributeStatus !== base_1.OCPP2_0_1.SetVariableStatusEnumType.Accepted) { const mostRecentAcceptedStatus = (yield this.variableStatus.readAllByQuery({ where: { variableAttributeId: savedVariableAttribute.get('id'), status: base_1.OCPP2_0_1.SetVariableStatusEnumType.Accepted, }, limit: 1, order: [['createdAt', 'DESC']], }))[0]; savedVariableAttribute.setDataValue('value', mostRecentAcceptedStatus === null || mostRecentAcceptedStatus === void 0 ? void 0 : mostRecentAcceptedStatus.value); } savedVariableAttribute.set('generatedAt', isoTimestamp); yield savedVariableAttribute.save(); // Reload in order to include the statuses return yield savedVariableAttribute.reload({ include: [DeviceModel_1.VariableStatus], }); } else { throw new Error('Unable to update variable attribute status...'); } }); } readAllSetVariableByStationId(stationId) { const _super = Object.create(null, { readAllByQuery: { get: () => super.readAllByQuery } }); return __awaiter(this, void 0, void 0, function* () { const variableAttributeArray = yield _super.readAllByQuery.call(this, { where: { stationId, bootConfigSetId: { [sequelize_1.Op.ne]: null }, }, include: [{ model: DeviceModel_1.Component, include: [DeviceModel_1.Evse] }, DeviceModel_1.Variable], }); return variableAttributeArray.map((variableAttribute) => this.createSetVariableDataType(variableAttribute)); }); } readAllByQuerystring(query) { const _super = Object.create(null, { readAllByQuery: { get: () => super.readAllByQuery } }); return __awaiter(this, void 0, void 0, function* () { const readQuery = this.constructQuery(query); readQuery.include.push(DeviceModel_1.VariableStatus); return yield _super.readAllByQuery.call(this, readQuery); }); } existByQuerystring(query) { const _super = Object.create(null, { existByQuery: { get: () => super.existByQuery } }); return __awaiter(this, void 0, void 0, function* () { return yield _super.existByQuery.call(this, this.constructQuery(query)); }); } deleteAllByQuerystring(query) { const _super = Object.create(null, { deleteAllByQuery: { get: () => super.deleteAllByQuery } }); return __awaiter(this, void 0, void 0, function* () { return yield _super.deleteAllByQuery.call(this, this.constructQuery(query)); }); } findComponentAndVariable(componentType, variableType) { return __awaiter(this, void 0, void 0, function* () { const component = yield this.component.readOnlyOneByQuery({ where: { name: componentType.name, instance: componentType.instance ? componentType.instance : null, }, }); const variable = yield this.variable.readOnlyOneByQuery({ where: { name: variableType.name, instance: variableType.instance ? variableType.instance : null, }, }); if (variable) { const variableCharacteristics = yield this.variableCharacteristics.readOnlyOneByQuery({ where: { variableId: variable.get('id') }, }); variable.variableCharacteristics = variableCharacteristics; } return [component, variable]; }); } findEvseByIdAndConnectorId(id, connectorId) { return __awaiter(this, void 0, void 0, function* () { const storedEvses = yield this.evse.readAllByQuery({ where: { // unique constraints id: id, connectorId: connectorId, }, }); return storedEvses.length > 0 ? storedEvses[0] : undefined; }); } findVariableCharacteristicsByVariableNameAndVariableInstance(variableName, variableInstance) { return __awaiter(this, void 0, void 0, function* () { const variableCharacteristics = yield this.variableCharacteristics.readAllByQuery({ include: [ { model: DeviceModel_1.Variable, where: { name: variableName, instance: variableInstance, }, }, ], }); return variableCharacteristics.length > 0 ? variableCharacteristics[0] : undefined; }); } /** * Private Methods */ createSetVariableDataType(input) { if (!input.value) { throw new Error('Value must be present to generate SetVariableDataType from VariableAttribute'); } else { return { attributeType: input.type, attributeValue: input.value, component: Object.assign({}, input.component), variable: Object.assign({}, input.variable), }; } } constructQuery(queryParams) { var _a; const evseInclude = ((_a = queryParams.component_evse_id) !== null && _a !== void 0 ? _a : queryParams.component_evse_connectorId) ? { model: DeviceModel_1.Evse, where: Object.assign(Object.assign({}, (queryParams.component_evse_id ? { id: queryParams.component_evse_id } : {})), (queryParams.component_evse_connectorId ? { connectorId: queryParams.component_evse_connectorId } : {})), } : DeviceModel_1.Evse; const attributeType = queryParams.type && queryParams.type.toUpperCase() === 'NULL' ? null : queryParams.type; return { where: Object.assign(Object.assign(Object.assign(Object.assign({}, (queryParams.stationId ? { stationId: queryParams.stationId } : {})), (queryParams.type === undefined ? {} : { type: attributeType })), (queryParams.value ? { value: queryParams.value } : {})), (queryParams.status === undefined ? {} : { status: queryParams.status })), include: [ { model: DeviceModel_1.Component, where: Object.assign(Object.assign({}, (queryParams.component_name ? { name: queryParams.component_name } : {})), (queryParams.component_instance ? { instance: queryParams.component_instance } : {})), include: [evseInclude], }, { model: DeviceModel_1.Variable, where: Object.assign(Object.assign({}, (queryParams.variable_name ? { name: queryParams.variable_name } : {})), (queryParams.variable_instance ? { instance: queryParams.variable_instance } : {})), include: [DeviceModel_1.VariableCharacteristics], }, ], }; } } exports.SequelizeDeviceModelRepository = SequelizeDeviceModelRepository; //# sourceMappingURL=DeviceModel.js.map