@citrineos/data
Version:
The OCPP data module which includes all persistence layer implementation.
255 lines • 11.1 kB
JavaScript
// SPDX-FileCopyrightText: 2025 Contributors to the CitrineOS Project
//
// SPDX-License-Identifier: Apache-2.0
import { CrudRepository, OCPPVersion } from '@citrineos/base';
import { Op } from 'sequelize';
import { Sequelize } from 'sequelize-typescript';
import { Logger } from 'tslog';
import {} from '../../../index.js';
import { ChargingStation, Connector, Location, SequelizeRepository, StatusNotification, } from '../index.js';
import { Evse, LatestStatusNotification } from '../model/index.js';
export class SequelizeLocationRepository extends SequelizeRepository {
chargingStation;
statusNotification;
latestStatusNotification;
connector;
constructor(config, logger, sequelizeInstance, chargingStation, statusNotification, latestStatusNotification, connector) {
super(config, Location.MODEL_NAME, logger, sequelizeInstance);
this.chargingStation = chargingStation
? chargingStation
: new SequelizeRepository(config, ChargingStation.MODEL_NAME, logger, sequelizeInstance);
this.statusNotification = statusNotification
? statusNotification
: new SequelizeRepository(config, StatusNotification.MODEL_NAME, logger, sequelizeInstance);
this.latestStatusNotification = latestStatusNotification
? latestStatusNotification
: new SequelizeRepository(config, LatestStatusNotification.MODEL_NAME, logger, sequelizeInstance);
this.connector = connector
? connector
: new SequelizeRepository(config, Connector.MODEL_NAME, logger, sequelizeInstance);
}
async readLocationById(tenantId, id) {
return await this.readOnlyOneByQuery(tenantId, {
where: { id },
include: [ChargingStation],
});
}
async readChargingStationByStationId(tenantId, stationId) {
return ((await ChargingStation.findOne({
where: {
id: stationId,
tenantId,
},
include: [{ model: Evse, include: [Connector] }],
})) ?? undefined);
}
async setChargingStationIsOnlineAndOCPPVersion(tenantId, stationId, isOnline, ocppVersion) {
return await this.chargingStation.updateByKey(tenantId, { isOnline: isOnline, protocol: ocppVersion }, stationId);
}
async doesChargingStationExistByStationId(tenantId, stationId) {
return await this.chargingStation.existsByKey(tenantId, stationId);
}
async addStatusNotificationToChargingStation(tenantId, stationId, statusNotification) {
const savedStatusNotification = await this.statusNotification.create(tenantId, statusNotification);
try {
await this.updateLatestStatusNotification(tenantId, stationId, savedStatusNotification);
}
catch (e) {
this.logger.error(`Failed to update latest status notification with error: ${e.message}`, e);
}
}
async updateLatestStatusNotification(tenantId, stationId, statusNotification) {
const evseId = statusNotification.evseId;
const connectorId = statusNotification.connectorId;
const statusNotificationId = statusNotification.id;
// delete operation doesn't support "include" in query
// so we need to find them at first and then delete
const existingLatestStatusNotifications = await this.latestStatusNotification.readAllByQuery(tenantId, {
where: {
stationId,
},
include: [
{
model: StatusNotification,
where: {
evseId,
connectorId,
},
require: true,
},
],
});
const idsToDelete = existingLatestStatusNotifications.map((l) => l.id);
await this.latestStatusNotification.deleteAllByQuery(tenantId, {
where: {
stationId,
id: {
[Op.in]: idsToDelete,
},
},
});
await this.latestStatusNotification.create(tenantId, LatestStatusNotification.build({
tenantId,
stationId,
statusNotificationId,
}));
}
async getChargingStationsByIds(tenantId, stationIds) {
const query = {
where: {
id: {
[Op.in]: stationIds,
},
},
};
return this.chargingStation.readAllByQuery(tenantId, query);
}
async createOrUpdateLocationWithChargingStations(tenantId, location) {
location.tenantId = tenantId;
let savedLocation;
if (location.id) {
const result = await this.readOrCreateByQuery(tenantId, {
where: {
tenantId,
id: location.id,
},
defaults: {
name: location.name,
address: location.address,
city: location.city,
postalCode: location.postalCode,
state: location.state,
country: location.country,
coordinates: location.coordinates,
},
});
savedLocation = result[0];
const locationCreated = result[1];
if (!locationCreated) {
const values = {};
values.name = location.name ?? undefined;
values.address = location.address ?? undefined;
values.city = location.city ?? undefined;
values.postalCode = location.postalCode ?? undefined;
values.state = location.state ?? undefined;
values.country = location.country ?? undefined;
values.coordinates = location.coordinates ?? undefined;
await this.updateByKey(tenantId, { ...values }, savedLocation.id);
}
}
else {
savedLocation = await this.create(tenantId, Location.build({ ...location }));
}
if (location.chargingPool && location.chargingPool.length > 0) {
for (const chargingStation of location.chargingPool) {
chargingStation.locationId = savedLocation.id;
await this.createOrUpdateChargingStation(tenantId, chargingStation);
}
}
return savedLocation.reload({ include: ChargingStation });
}
async createOrUpdateChargingStation(tenantId, chargingStation) {
chargingStation.tenantId = tenantId;
if (chargingStation.id) {
const [savedChargingStation, chargingStationCreated] = await this.chargingStation.readOrCreateByQuery(tenantId, {
where: {
tenantId,
id: chargingStation.id,
},
defaults: {
locationId: chargingStation.locationId,
chargePointVendor: chargingStation.chargePointVendor,
chargePointModel: chargingStation.chargePointModel,
chargePointSerialNumber: chargingStation.chargePointSerialNumber,
chargeBoxSerialNumber: chargingStation.chargeBoxSerialNumber,
firmwareVersion: chargingStation.firmwareVersion,
iccid: chargingStation.iccid,
imsi: chargingStation.imsi,
meterType: chargingStation.meterType,
meterSerialNumber: chargingStation.meterSerialNumber,
},
});
if (!chargingStationCreated) {
await this.chargingStation.updateByKey(tenantId, {
locationId: chargingStation.locationId,
chargePointVendor: chargingStation.chargePointVendor,
chargePointModel: chargingStation.chargePointModel,
chargePointSerialNumber: chargingStation.chargePointSerialNumber,
chargeBoxSerialNumber: chargingStation.chargeBoxSerialNumber,
firmwareVersion: chargingStation.firmwareVersion,
iccid: chargingStation.iccid,
imsi: chargingStation.imsi,
meterType: chargingStation.meterType,
meterSerialNumber: chargingStation.meterSerialNumber,
}, savedChargingStation.id);
}
return savedChargingStation;
}
else {
return await this.chargingStation.create(tenantId, ChargingStation.build({ ...chargingStation }));
}
}
async createOrUpdateConnector(tenantId, connector) {
let result;
await this.s.transaction(async (sequelizeTransaction) => {
const [savedConnector, connectorCreated] = await this.connector.readOrCreateByQuery(tenantId, {
where: {
tenantId,
stationId: connector.stationId,
connectorId: connector.connectorId,
},
defaults: {
...connector,
},
transaction: sequelizeTransaction,
});
if (!connectorCreated) {
const updatedConnectors = await this.connector.updateAllByQuery(tenantId, connector, {
where: {
id: savedConnector.id,
},
transaction: sequelizeTransaction,
});
result = updatedConnectors.length > 0 ? updatedConnectors[0] : undefined;
}
else {
result = savedConnector;
}
});
return result;
}
async updateAllConnectorsByQuery(tenantId, value, query) {
return await this.connector.updateAllByQuery(tenantId, value, query);
}
async readConnectorByStationIdAndOcpp16ConnectorId(tenantId, stationId, ocpp16ConnectorId) {
return ((await Connector.findOne({
where: {
tenantId,
stationId,
connectorId: ocpp16ConnectorId,
},
include: [Evse],
})) ?? undefined);
}
async readEvseByStationIdAndOcpp201EvseId(tenantId, stationId, ocpp201EvseId) {
return ((await Evse.findOne({
where: {
stationId,
evseTypeId: ocpp201EvseId,
tenantId,
},
include: [Connector],
})) ?? undefined);
}
async readConnectorByStationIdAndOcpp201EvseType(tenantId, stationId, ocpp201EvseType) {
return ((await Connector.findOne({
where: {
tenantId,
stationId,
evseTypeConnectorId: ocpp201EvseType.connectorId,
},
include: [{ model: Evse, where: { evseTypeId: ocpp201EvseType.id }, required: true }],
})) ?? undefined);
}
}
//# sourceMappingURL=Location.js.map