@citrineos/data
Version:
The OCPP data module which includes all persistence layer implementation.
472 lines • 24 kB
JavaScript
"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