UNPKG

@citrineos/data

Version:

The OCPP data module which includes all persistence layer implementation.

472 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.SequelizeTransactionEventRepository = void 0; const base_1 = require("@citrineos/base"); const TransactionEvent_1 = require("../model/TransactionEvent"); const Base_1 = require("./Base"); const Authorization_1 = require("../model/Authorization"); const DeviceModel_1 = require("../model/DeviceModel"); const sequelize_1 = require("sequelize"); const _2_0_1_1 = require("../mapper/2.0.1"); const Location_1 = require("../model/Location"); const ChargingStationSequence_1 = require("./ChargingStationSequence"); class SequelizeTransactionEventRepository extends Base_1.SequelizeRepository { constructor(config, logger, namespace = TransactionEvent_1.TransactionEvent.MODEL_NAME, sequelizeInstance, transaction, evse, idToken, meterValue, startTransaction, stopTransaction, connector, chargingStationSequence) { super(config, namespace, logger, sequelizeInstance); this.transaction = transaction ? transaction : new Base_1.SequelizeRepository(config, TransactionEvent_1.Transaction.MODEL_NAME, logger, sequelizeInstance); this.evse = evse ? evse : new Base_1.SequelizeRepository(config, DeviceModel_1.Evse.MODEL_NAME, logger, sequelizeInstance); this.idToken = idToken ? idToken : new Base_1.SequelizeRepository(config, Authorization_1.IdToken.MODEL_NAME, logger, sequelizeInstance); this.meterValue = meterValue ? meterValue : new Base_1.SequelizeRepository(config, TransactionEvent_1.MeterValue.MODEL_NAME, logger, sequelizeInstance); this.startTransaction = startTransaction ? startTransaction : new Base_1.SequelizeRepository(config, TransactionEvent_1.StartTransaction.MODEL_NAME, logger, sequelizeInstance); this.stopTransaction = stopTransaction ? stopTransaction : new Base_1.SequelizeRepository(config, TransactionEvent_1.StopTransaction.MODEL_NAME, logger, sequelizeInstance); this.connector = connector ? connector : new Base_1.SequelizeRepository(config, Location_1.Connector.MODEL_NAME, logger, sequelizeInstance); this.chargingStationSequence = chargingStationSequence || new ChargingStationSequence_1.SequelizeChargingStationSequenceRepository(config, logger); } /** * @param value TransactionEventRequest received from charging station. Will be used to create TransactionEvent, * MeterValues, and either create or update Transaction. IdTokens (and associated AdditionalInfo) and EVSEs are * assumed to already exist and will not be created as part of this call. * * @param stationId StationId of charging station which sent TransactionEventRequest. * * @returns Saved TransactionEvent */ createOrUpdateTransactionByTransactionEventAndStationId(tenantId, value, stationId) { return __awaiter(this, void 0, void 0, function* () { let evse; if (value.evse) { [evse] = yield this.evse.readOrCreateByQuery(tenantId, { where: { tenantId, id: value.evse.id, connectorId: value.evse.connectorId ? value.evse.connectorId : null, }, }); } return yield this.s.transaction((sequelizeTransaction) => __awaiter(this, void 0, void 0, function* () { let finalTransaction; let created = false; const existingTransaction = yield this.transaction.readOnlyOneByQuery(tenantId, { where: { stationId, transactionId: value.transactionInfo.transactionId, }, transaction: sequelizeTransaction, }); if (existingTransaction) { finalTransaction = yield existingTransaction.update(Object.assign({ isActive: value.eventType !== base_1.OCPP2_0_1.TransactionEventEnumType.Ended }, value.transactionInfo), { transaction: sequelizeTransaction, }); } else { const newTransaction = TransactionEvent_1.Transaction.build(Object.assign(Object.assign({ tenantId, stationId, isActive: value.eventType !== base_1.OCPP2_0_1.TransactionEventEnumType.Ended }, (evse ? { evseDatabaseId: evse.databaseId } : {})), value.transactionInfo)); finalTransaction = yield newTransaction.save({ transaction: sequelizeTransaction }); created = true; } const transactionDatabaseId = finalTransaction.id; let event = TransactionEvent_1.TransactionEvent.build(Object.assign({ tenantId, stationId, transactionDatabaseId }, value)); if (value.idToken && value.idToken.type !== base_1.OCPP2_0_1.IdTokenEnumType.NoAuthorization) { const idToken = yield this.idToken.readOnlyOneByQuery(tenantId, { where: { idToken: value.idToken.idToken, type: value.idToken.type, }, sequelizeTransaction, }); if (!idToken) { // TODO: Log Warning... } else { event.idTokenId = idToken.id; } } event = yield event.save({ transaction: sequelizeTransaction }); if (value.meterValue && value.meterValue.length > 0) { yield Promise.all(value.meterValue.map((meterValue) => __awaiter(this, void 0, void 0, function* () { const savedMeterValue = yield TransactionEvent_1.MeterValue.create(Object.assign({ tenantId, transactionEventId: event.id, transactionDatabaseId: transactionDatabaseId }, meterValue), { transaction: sequelizeTransaction }); this.meterValue.emit('created', [savedMeterValue]); }))); } yield event.reload({ include: [TransactionEvent_1.MeterValue], transaction: sequelizeTransaction }); this.emit('created', [event]); const allMeterValues = yield this.meterValue.readAllByQuery(tenantId, { where: { transactionDatabaseId, }, transaction: sequelizeTransaction, }); const meterValueTypes = allMeterValues.map((meterValue) => _2_0_1_1.MeterValueMapper.toMeterValueType(meterValue)); yield finalTransaction.update({ totalKwh: base_1.MeterValueUtils.getTotalKwh(meterValueTypes) }, { transaction: sequelizeTransaction }); yield finalTransaction.reload({ include: [ { model: TransactionEvent_1.TransactionEvent, as: TransactionEvent_1.Transaction.TRANSACTION_EVENTS_ALIAS, include: [Authorization_1.IdToken] }, TransactionEvent_1.MeterValue, DeviceModel_1.Evse, ], transaction: sequelizeTransaction, }); this.transaction.emit(created ? 'created' : 'updated', [finalTransaction]); return finalTransaction; })); }); } readAllByStationIdAndTransactionId(tenantId, stationId, transactionId) { const _super = Object.create(null, { readAllByQuery: { get: () => super.readAllByQuery } }); return __awaiter(this, void 0, void 0, function* () { return yield _super.readAllByQuery.call(this, tenantId, { where: { stationId }, include: [{ model: TransactionEvent_1.Transaction, where: { transactionId } }, TransactionEvent_1.MeterValue, DeviceModel_1.Evse, Authorization_1.IdToken], }) .then((transactionEvents) => { transactionEvents === null || transactionEvents === void 0 ? void 0 : transactionEvents.forEach((transactionEvent) => (transactionEvent.transaction = undefined)); return transactionEvents; }); }); } readTransactionByStationIdAndTransactionId(tenantId, stationId, transactionId) { return __awaiter(this, void 0, void 0, function* () { return yield this.transaction.readOnlyOneByQuery(tenantId, { where: { stationId, transactionId }, include: [TransactionEvent_1.MeterValue, DeviceModel_1.Evse], }); }); } readAllTransactionsByStationIdAndEvseAndChargingStates(tenantId, stationId, evse, chargingStates) { return __awaiter(this, void 0, void 0, function* () { const includeObj = evse ? [ { model: DeviceModel_1.Evse, where: { id: evse.id, connectorId: evse.connectorId ? evse.connectorId : null }, }, ] : []; return yield this.transaction .readAllByQuery(tenantId, { where: Object.assign({ stationId }, (chargingStates ? { chargingState: { [sequelize_1.Op.in]: chargingStates } } : {})), include: includeObj, }) .then((row) => row); }); } readAllActiveTransactionsIncludeTransactionEventByIdToken(tenantId, idToken) { return __awaiter(this, void 0, void 0, function* () { return yield this.transaction.readAllByQuery(tenantId, { where: { isActive: true }, include: [ { model: TransactionEvent_1.TransactionEvent, as: TransactionEvent_1.Transaction.TRANSACTION_EVENTS_ALIAS, required: true, include: [ { model: Authorization_1.IdToken, required: true, where: { idToken: idToken.idToken, type: idToken.type, }, }, ], }, ], }); }); } readAllActiveTransactionsIncludeStartTransactionByIdToken(tenantId, idToken) { return __awaiter(this, void 0, void 0, function* () { return yield this.transaction.readAllByQuery(tenantId, { where: { isActive: true }, include: [ { model: TransactionEvent_1.StartTransaction, required: true, include: [ { model: Authorization_1.IdToken, required: true, where: { idToken: idToken, }, }, ], }, ], }); }); } readAllMeterValuesByTransactionDataBaseId(tenantId, transactionDataBaseId) { return __awaiter(this, void 0, void 0, function* () { return this.meterValue .readAllByQuery(tenantId, { where: { transactionDatabaseId: transactionDataBaseId }, }) .then((row) => row); }); } findByTransactionId(tenantId, transactionId) { return __awaiter(this, void 0, void 0, function* () { return this.transaction.readOnlyOneByQuery(tenantId, { where: { transactionId }, include: [ { model: TransactionEvent_1.TransactionEvent, as: TransactionEvent_1.Transaction.TRANSACTION_EVENTS_ALIAS, include: [Authorization_1.IdToken] }, TransactionEvent_1.MeterValue, DeviceModel_1.Evse, ], }); }); } getTransactions(tenantId, dateFrom, dateTo, offset, limit) { return __awaiter(this, void 0, void 0, function* () { const queryOptions = { where: {}, include: [ { model: TransactionEvent_1.TransactionEvent, as: TransactionEvent_1.Transaction.TRANSACTION_EVENTS_ALIAS, include: [Authorization_1.IdToken] }, TransactionEvent_1.MeterValue, DeviceModel_1.Evse, ], }; if (dateFrom) { queryOptions.where.updatedAt = queryOptions.where.updatedAt || {}; queryOptions.where.updatedAt[sequelize_1.Op.gte] = dateFrom; } if (dateTo) { queryOptions.where.updatedAt = queryOptions.where.updatedAt || {}; queryOptions.where.updatedAt[sequelize_1.Op.lt] = dateTo; } if (offset) { queryOptions.offset = offset; } if (limit) { queryOptions.limit = limit; } return this.transaction.readAllByQuery(tenantId, queryOptions); }); } getTransactionsCount(tenantId, dateFrom, dateTo) { return __awaiter(this, void 0, void 0, function* () { const queryOptions = { where: {}, }; if (dateFrom) { queryOptions.where.updatedAt = queryOptions.where.updatedAt || {}; queryOptions.where.updatedAt[sequelize_1.Op.gte] = dateFrom; } if (dateTo) { queryOptions.where.updatedAt = queryOptions.where.updatedAt || {}; queryOptions.where.updatedAt[sequelize_1.Op.lt] = dateTo; } return TransactionEvent_1.Transaction.count(queryOptions); }); } readAllTransactionsByQuery(tenantId, query) { return __awaiter(this, void 0, void 0, function* () { return yield this.transaction.readAllByQuery(tenantId, query); }); } getEvseIdsWithActiveTransactionByStationId(tenantId, stationId) { return __awaiter(this, void 0, void 0, function* () { const activeTransactions = yield this.transaction.readAllByQuery(tenantId, { where: { stationId: stationId, isActive: true, }, include: [DeviceModel_1.Evse], }); const evseIds = []; activeTransactions.forEach((transaction) => { var _a; const evseId = (_a = transaction.evse) === null || _a === void 0 ? void 0 : _a.id; if (evseId) { evseIds.push(evseId); } }); return evseIds; }); } getActiveTransactionByStationIdAndEvseId(tenantId, stationId, evseId) { return __awaiter(this, void 0, void 0, function* () { return yield this.transaction .readAllByQuery(tenantId, { where: { stationId, isActive: true, }, include: [ { model: TransactionEvent_1.TransactionEvent, as: TransactionEvent_1.Transaction.TRANSACTION_EVENTS_ALIAS, include: [Authorization_1.IdToken] }, TransactionEvent_1.MeterValue, { model: DeviceModel_1.Evse, where: { id: evseId }, required: true }, ], }) .then((transactions) => { if (transactions.length > 1) { transactions.sort((t1, t2) => t2.updatedAt.getTime() - t1.updatedAt.getTime()); } return transactions[0]; }); }); } createMeterValue(tenantId, meterValue, transactionDatabaseId) { return __awaiter(this, void 0, void 0, function* () { yield this.s.transaction((sequelizeTransaction) => __awaiter(this, void 0, void 0, function* () { const savedMeterValue = yield TransactionEvent_1.MeterValue.create(Object.assign({ tenantId, transactionDatabaseId: transactionDatabaseId }, meterValue), { transaction: sequelizeTransaction }); this.meterValue.emit('created', [savedMeterValue]); })); }); } updateTransactionTotalCostById(tenantId, totalCost, id) { return __awaiter(this, void 0, void 0, function* () { yield this.transaction.updateByKey(tenantId, { totalCost: totalCost }, id.toString()); }); } updateTransactionByMeterValues(tenantId, meterValues, stationId, transactionId) { return __awaiter(this, void 0, void 0, function* () { // Find existing transaction const transaction = yield this.readTransactionByStationIdAndTransactionId(tenantId, stationId, transactionId.toString()); if (!transaction) { this.logger.error(`Transaction ${transactionId} on station ${stationId} does not exist.`); return; } // Store meter values yield Promise.all(meterValues.map((meterValue) => __awaiter(this, void 0, void 0, function* () { meterValue.transactionDatabaseId = transaction.id; yield meterValue.save(); this.meterValue.emit('created', [meterValue]); }))); // Update transaction total kWh const allMeterValues = yield this.meterValue.readAllByQuery(tenantId, { where: { transactionDatabaseId: transaction.id, }, }); const meterValueTypes = allMeterValues.map((meterValue) => _2_0_1_1.MeterValueMapper.toMeterValueType(meterValue)); yield transaction.update({ totalKwh: base_1.MeterValueUtils.getTotalKwh(meterValueTypes) }); }); } createTransactionByStartTransaction(tenantId, request, stationId) { return __awaiter(this, void 0, void 0, function* () { return yield this.s.transaction((sequelizeTransaction) => __awaiter(this, void 0, void 0, function* () { // Build StartTransaction event let event = TransactionEvent_1.StartTransaction.build(Object.assign({ tenantId, stationId }, request)); // Associate IdToken with StartTransaction const idToken = yield this.idToken.readOnlyOneByQuery(tenantId, { where: { idToken: request.idTag, }, sequelizeTransaction, }); if (!idToken) { this.logger.error(`Unable to find idTag ${request.idTag}.`); throw new Error(`Unable to find idTag ${request.idTag}.`); } event.idTokenDatabaseId = idToken.id; // Associate Connector with StartTransaction const connector = yield this.connector.readOnlyOneByQuery(tenantId, { where: { connectorId: request.connectorId, stationId, }, sequelizeTransaction, }); if (!connector) { this.logger.error(`Unable to find connector ${request.connectorId}.`); throw new Error(`Unable to find connector ${request.connectorId}.`); } event.connectorDatabaseId = connector.id; // Generate transactionId const transactionId = yield this.chargingStationSequence.getNextSequenceValue(tenantId, stationId, base_1.ChargingStationSequenceType.transactionId); // Store transaction in db let newTransaction = TransactionEvent_1.Transaction.build({ tenantId, stationId, isActive: true, transactionId: transactionId.toString(), }); newTransaction = yield newTransaction.save({ transaction: sequelizeTransaction }); // Store StartTransaction in db event.transactionDatabaseId = newTransaction.id; event = yield event.save({ transaction: sequelizeTransaction }); this.startTransaction.emit('created', [event]); // Return the new transaction with StartTransaction and IdToken yield newTransaction.reload({ include: [{ model: TransactionEvent_1.StartTransaction, include: [Authorization_1.IdToken] }], transaction: sequelizeTransaction, }); this.transaction.emit('created', [newTransaction]); return newTransaction; })); }); } createStopTransaction(tenantId, transactionDatabaseId, stationId, meterStop, timestamp, meterValues, reason, idTokenDatabaseId) { return __awaiter(this, void 0, void 0, function* () { const stopTransaction = yield TransactionEvent_1.StopTransaction.create({ tenantId, stationId, transactionDatabaseId, meterStop, timestamp: timestamp.toISOString(), reason, idTokenDatabaseId, meterValues, }); this.stopTransaction.emit('created', [stopTransaction]); if (meterValues.length > 0) { yield Promise.all(meterValues.map((meterValue) => __awaiter(this, void 0, void 0, function* () { meterValue.transactionDatabaseId = transactionDatabaseId; meterValue.stopTransactionDatabaseId = stopTransaction.id; yield meterValue.save(); this.meterValue.emit('created', [meterValue]); }))); } return stopTransaction; }); } updateTransactionByStationIdAndTransactionId(tenantId, transaction, transactionId, stationId) { return __awaiter(this, void 0, void 0, function* () { const transactions = yield this.transaction.updateAllByQuery(tenantId, transaction, { where: { // unique constraint transactionId, stationId, }, }); return transactions.length > 0 ? transactions[0] : undefined; }); } } exports.SequelizeTransactionEventRepository = SequelizeTransactionEventRepository; //# sourceMappingURL=TransactionEvent.js.map