Call delete of TxProfile after comsumption calculation in Stop Transaction + Add user ID in connector
MODIFY
src/server/ocpp/services/OCPPService.ts
src/server/ocpp/services/OCPPService.ts
@@ -629,6 +629,7 @@ export default class OCPPService {
foundConnector.inactivityStatus = InactivityStatus.INFO;
foundConnector.currentStateOfCharge = 0;
foundConnector.activeTransactionID = transaction.id;
+ foundConnector.userID = transaction.userID;
foundConnector.activeTransactionDate = transaction.timestamp;
foundConnector.activeTagID = transaction.tagID;
} else {
@@ -730,36 +731,6 @@ export default class OCPPService {
const transaction = await TransactionStorage.getTransaction(headers.tenantID, stopTransaction.transactionId);
UtilsService.assertObjectExists(ServerAction.STOP_TRANSACTION, transaction, `Transaction with ID '${stopTransaction.transactionId}' doesn't exist`,
'OCPPService', 'handleStopTransaction', null);
- // Delete TxProfile if any
- const chargingProfiles = await ChargingStationStorage.getChargingProfiles(headers.tenantID, {
- chargingStationID: chargingStation.id,
- connectorID: transaction.connectorId,
- profilePurposeType: ChargingProfilePurposeType.TX_PROFILE,
- transactionId: stopTransaction.transactionId
- }, Constants.DB_PARAMS_MAX_LIMIT);
- // Delete all TxProfiles
- for (const chargingProfile of chargingProfiles.result) {
- try {
- await OCPPUtils.clearAndDeleteChargingProfile(headers.tenantID, chargingProfile);
- Logging.logDebug({
- tenantID: headers.tenantID,
- source: chargingStation.id,
- action: ServerAction.CHARGING_PROFILE_DELETE,
- message: `Connector '${transaction.connectorId}' > Transaction ID '${transaction.id}' > TX Charging Profile with ID '${chargingProfile.id}'`,
- module: MODULE_NAME, method: 'handleStopTransaction',
- detailedMessages: { chargingProfile }
- });
- } catch (error) {
- Logging.logError({
- tenantID: headers.tenantID,
- source: chargingStation.id,
- action: ServerAction.CHARGING_PROFILE_DELETE,
- message: `Connector '${transaction.connectorId}' > Transaction ID '${transaction.id}' > Cannot delete TX Charging Profile with ID '${chargingProfile.id}'`,
- module: MODULE_NAME, method: 'handleStopTransaction',
- detailedMessages: { error: error.message, stack: error.stack, chargingProfile }
- });
- }
- }
// Get the TagID that stopped the transaction
const tagId = this.getStopTransactionTagId(stopTransaction, transaction);
let user: User, alternateUser: User;
@@ -831,6 +802,8 @@ export default class OCPPService {
const tenant = await TenantStorage.getTenant(headers.tenantID);
// Recompute the Smart Charging Plan
if (Utils.isTenantComponentActive(tenant, TenantComponents.SMART_CHARGING)) {
+ // Delete TxProfile if any
+ await this.deleteAllTransactionTxProfile(headers.tenantID, transaction);
// Call async because the Transaction ID on the connector should be cleared
// eslint-disable-next-line @typescript-eslint/no-misused-promises
setTimeout(async () => {
@@ -873,6 +846,38 @@ export default class OCPPService {
}
}
+ private async deleteAllTransactionTxProfile(tenantID: string, transaction: Transaction) {
+ const chargingProfiles = await ChargingStationStorage.getChargingProfiles(tenantID, {
+ chargingStationID: transaction.chargeBoxID,
+ connectorID: transaction.connectorId,
+ profilePurposeType: ChargingProfilePurposeType.TX_PROFILE,
+ transactionId: transaction.id
+ }, Constants.DB_PARAMS_MAX_LIMIT);
+ // Delete all TxProfiles
+ for (const chargingProfile of chargingProfiles.result) {
+ try {
+ await OCPPUtils.clearAndDeleteChargingProfile(tenantID, chargingProfile);
+ Logging.logDebug({
+ tenantID: tenantID,
+ source: transaction.chargeBoxID,
+ action: ServerAction.CHARGING_PROFILE_DELETE,
+ message: `Connector '${transaction.connectorId}' > Transaction ID '${transaction.id}' > TX Charging Profile with ID '${chargingProfile.id}'`,
+ module: MODULE_NAME, method: 'handleStopTransaction',
+ detailedMessages: { chargingProfile }
+ });
+ } catch (error) {
+ Logging.logError({
+ tenantID: tenantID,
+ source: transaction.chargeBoxID,
+ action: ServerAction.CHARGING_PROFILE_DELETE,
+ message: `Connector '${transaction.connectorId}' > Transaction ID '${transaction.id}' > Cannot delete TX Charging Profile with ID '${chargingProfile.id}'`,
+ module: MODULE_NAME, method: 'handleStopTransaction',
+ detailedMessages: { error: error.message, stack: error.stack, chargingProfile }
+ });
+ }
+ }
+ }
+
private async updateConnectorStatus(tenantID: string, chargingStation: ChargingStation, statusNotification: OCPPStatusNotificationRequestExtended, bothConnectorsUpdated) {
// Get it
let foundConnector: Connector = Utils.getConnectorFromID(chargingStation, statusNotification.connectorId);
@@ -883,6 +888,7 @@ export default class OCPPService {
activeTransactionID: 0,
activeTransactionDate: null,
activeTagID: null,
+ userID: null,
connectorId: statusNotification.connectorId,
currentConsumption: 0,
status: ChargePointStatus.UNAVAILABLE,
@@ -1492,6 +1498,7 @@ export default class OCPPService {
foundConnector.totalInactivitySecs = transaction.currentTotalInactivitySecs;
// Set Transaction ID
foundConnector.activeTransactionID = transaction.id;
+ foundConnector.userID = transaction.userID;
foundConnector.activeTagID = transaction.tagID;
// Update Heartbeat
chargingStation.lastHeartBeat = new Date();
@@ -1508,14 +1515,7 @@ export default class OCPPService {
});
// Cleanup connector transaction data
} else if (foundConnector) {
- foundConnector.currentConsumption = 0;
- foundConnector.totalConsumption = 0;
- foundConnector.totalInactivitySecs = 0;
- foundConnector.inactivityStatus = InactivityStatus.INFO;
- foundConnector.currentStateOfCharge = 0;
- foundConnector.activeTransactionID = 0;
- foundConnector.activeTransactionDate = null;
- foundConnector.activeTagID = null;
+ OCPPUtils.checkAndFreeChargingStationConnector(chargingStation, foundConnector.connectorId);
}
}

MODIFY
src/server/ocpp/utils/OCPPUtils.ts
src/server/ocpp/utils/OCPPUtils.ts
@@ -697,6 +697,7 @@ export default class OCPPUtils {
foundConnector.activeTransactionID = 0;
foundConnector.activeTransactionDate = null;
foundConnector.activeTagID = null;
+ foundConnector.userID = null;
}
}
}

MODIFY
src/storage/mongodb/ChargingStationStorage.ts
src/storage/mongodb/ChargingStationStorage.ts
import { ChargingProfile, ChargingProfilePurposeType, ChargingRateUnitType } from '../../types/ChargingProfile';
-import ChargingStation, { ChargingStationOcppParameters, ChargingStationTemplate, Connector, ConnectorType, OcppParameter } from '../../types/ChargingStation';
+import ChargingStation, { ChargePoint, ChargingStationOcppParameters, ChargingStationTemplate, Connector, ConnectorType, OcppParameter } from '../../types/ChargingStation';
import { ChargingStationInError, ChargingStationInErrorType } from '../../types/InError';
import { GridFSBucket, GridFSBucketReadStream } from 'mongodb';
@@ -254,6 +254,11 @@ export default class ChargingStationStorage {
}
// Remove the limit
aggregation.pop();
+ // Users on connectors
+ DatabaseUtils.pushArrayLookupInAggregation('connectors', DatabaseUtils.pushUserLookupInAggregation.bind(this), {
+ tenantID, aggregation: aggregation, localField: 'connectors.userID', foreignField: '_id',
+ asField: 'connectors.user', oneToOneCardinality: true, objectIDFields: ['createdBy', 'lastChangedBy']
+ });
// Add Created By / Last Changed By
DatabaseUtils.pushCreatedLastChangedInAggregation(tenantID, aggregation);
// Convert Object ID to string
@@ -424,40 +429,6 @@ export default class ChargingStationStorage {
const chargingStationFilter = {
_id: chargingStationToSave.id
};
- // Convert
- if (chargingStationToSave.connectors && Array.isArray(chargingStationToSave.connectors)) {
- for (const connector of chargingStationToSave.connectors) {
- if (connector) {
- connector.connectorId = Utils.convertToInt(connector.connectorId);
- connector.power = connector.power ? Utils.convertToInt(connector.power) : null;
- connector.amperage = connector.amperage ? Utils.convertToInt(connector.amperage) : null;
- connector.voltage = connector.voltage ? Utils.convertToInt(connector.voltage) : null;
- connector.chargePointID = connector.chargePointID ? Utils.convertToInt(connector.chargePointID) : null;
- connector.currentType = connector.currentType ? connector.currentType : null;
- connector.numberOfConnectedPhase = connector.numberOfConnectedPhase ? Utils.convertToInt(connector.numberOfConnectedPhase) : null;
- connector.currentConsumption = Utils.convertToFloat(connector.currentConsumption);
- connector.totalInactivitySecs = Utils.convertToInt(connector.totalInactivitySecs);
- connector.totalConsumption = Utils.convertToFloat(connector.totalConsumption);
- connector.activeTransactionID = Utils.convertToInt(connector.activeTransactionID);
- connector.activeTransactionDate = Utils.convertToDate(connector.activeTransactionDate);
- }
- }
- }
- if (chargingStationToSave.chargePoints && Array.isArray(chargingStationToSave.chargePoints)) {
- for (const chargePoint of chargingStationToSave.chargePoints) {
- if (chargePoint) {
- chargePoint.voltage = chargePoint.voltage ? Utils.convertToInt(chargePoint.voltage) : null;
- chargePoint.amperage = chargePoint.amperage ? Utils.convertToInt(chargePoint.amperage) : null;
- chargePoint.numberOfConnectedPhase = chargePoint.numberOfConnectedPhase ? Utils.convertToInt(chargePoint.numberOfConnectedPhase) : null;
- chargePoint.cannotChargeInParallel = Utils.convertToBoolean(chargePoint.cannotChargeInParallel);
- chargePoint.sharePowerToAllConnectors = Utils.convertToBoolean(chargePoint.sharePowerToAllConnectors);
- chargePoint.excludeFromPowerLimitation = Utils.convertToBoolean(chargePoint.excludeFromPowerLimitation);
- chargePoint.power = chargePoint.power ? Utils.convertToInt(chargePoint.power) : null;
- chargePoint.efficiency = chargePoint.efficiency ? Utils.convertToInt(chargePoint.efficiency) : null;
- chargePoint.connectorIDs = chargePoint.connectorIDs.map((connectorID) => Utils.convertToInt(connectorID));
- }
- }
- }
// Properties to save
const chargingStationMDB = {
_id: chargingStationToSave.id,
@@ -490,8 +461,10 @@ export default class ChargingStationStorage {
excludeFromSmartCharging: Utils.convertToBoolean(chargingStationToSave.excludeFromSmartCharging),
powerLimitUnit: chargingStationToSave.powerLimitUnit,
voltage: Utils.convertToInt(chargingStationToSave.voltage),
- chargePoints: chargingStationToSave.chargePoints ? chargingStationToSave.chargePoints : null,
- connectors: chargingStationToSave.connectors,
+ connectors: chargingStationToSave.connectors ? chargingStationToSave.connectors.map(
+ (connector) => ChargingStationStorage.connector2connectorMDB(connector)) : [],
+ chargePoints: chargingStationToSave.chargePoints ? chargingStationToSave.chargePoints.map(
+ (chargePoint) => ChargingStationStorage.chargePoint2ChargePointMDB(chargePoint)) : [],
coordinates: chargingStationToSave.coordinates,
remoteAuthorizations: chargingStationToSave.remoteAuthorizations,
currentIPAddress: chargingStationToSave.currentIPAddress,
@@ -523,23 +496,11 @@ export default class ChargingStationStorage {
// Debug
const uniqueTimerID = Logging.traceStart(MODULE_NAME, 'saveChargingStationConnector');
// Ensure good typing
- if (connector) {
- connector.connectorId = Utils.convertToInt(connector.connectorId);
- connector.currentConsumption = Utils.convertToFloat(connector.currentConsumption);
- connector.totalInactivitySecs = Utils.convertToInt(connector.totalInactivitySecs);
- connector.totalConsumption = Utils.convertToFloat(connector.totalConsumption);
- connector.power = Utils.convertToInt(connector.power);
- connector.voltage = Utils.convertToInt(connector.voltage);
- connector.amperage = Utils.convertToInt(connector.amperage);
- connector.activeTransactionID = Utils.convertToInt(connector.activeTransactionID);
- connector.activeTransactionDate = Utils.convertToDate(connector.activeTransactionDate);
- }
+ const connectorMDB = ChargingStationStorage.connector2connectorMDB(connector);
// Check Tenant
await Utils.checkTenant(tenantID);
const updatedFields: any = {};
- updatedFields['connectors.' + (connector.connectorId - 1)] = connector;
- // Update model
- chargingStation.connectors[connector.connectorId - 1] = connector;
+ updatedFields['connectors.' + (connector.connectorId - 1)] = connectorMDB;
// Modify and return the modified document
const result = await global.database.getCollection<any>(tenantID, 'chargingstations').findOneAndUpdate(
{ '_id': chargingStation.id },
@@ -947,4 +908,55 @@ export default class ChargingStationStorage {
return [];
}
}
+
+ private static connector2connectorMDB(connector: Connector): object {
+ if (!connector) {
+ return null;
+ }
+ return {
+ connectorId: Utils.convertToInt(connector.connectorId),
+ currentConsumption: Utils.convertToFloat(connector.currentConsumption),
+ currentStateOfCharge: connector.currentStateOfCharge,
+ totalInactivitySecs: Utils.convertToInt(connector.totalInactivitySecs),
+ totalConsumption: Utils.convertToFloat(connector.totalConsumption),
+ status: connector.status,
+ errorCode: connector.errorCode,
+ info: connector.info,
+ vendorErrorCode: connector.vendorErrorCode,
+ power: Utils.convertToInt(connector.power),
+ type: connector.type,
+ voltage: Utils.convertToInt(connector.voltage),
+ amperage: Utils.convertToInt(connector.amperage),
+ amperageLimit: connector.amperageLimit,
+ activeTransactionID: Utils.convertToInt(connector.activeTransactionID),
+ userID: Utils.convertToObjectID(connector.userID),
+ activeTransactionDate: Utils.convertToDate(connector.activeTransactionDate),
+ activeTagID: connector.activeTagID,
+ statusLastChangedOn: connector.statusLastChangedOn,
+ inactivityStatus: connector.inactivityStatus,
+ numberOfConnectedPhase: connector.numberOfConnectedPhase,
+ currentType: connector.currentType,
+ chargePointID: connector.chargePointID,
+ };
+ }
+
+ private static chargePoint2ChargePointMDB(chargePoint: ChargePoint): object {
+ if (!chargePoint) {
+ return null;
+ }
+ return {
+ chargePointID: Utils.convertToInt(chargePoint.chargePointID),
+ currentType: chargePoint.currentType,
+ voltage: chargePoint.voltage ? Utils.convertToInt(chargePoint.voltage) : null,
+ amperage: chargePoint.amperage ? Utils.convertToInt(chargePoint.amperage) : null,
+ numberOfConnectedPhase: chargePoint.numberOfConnectedPhase ? Utils.convertToInt(chargePoint.numberOfConnectedPhase) : null,
+ cannotChargeInParallel: Utils.convertToBoolean(chargePoint.cannotChargeInParallel),
+ sharePowerToAllConnectors: Utils.convertToBoolean(chargePoint.sharePowerToAllConnectors),
+ excludeFromPowerLimitation: Utils.convertToBoolean(chargePoint.excludeFromPowerLimitation),
+ ocppParamForPowerLimitation: chargePoint.ocppParamForPowerLimitation,
+ power: chargePoint.power ? Utils.convertToInt(chargePoint.power) : null,
+ efficiency: chargePoint.efficiency ? Utils.convertToInt(chargePoint.efficiency) : null,
+ connectorIDs: chargePoint.connectorIDs.map((connectorID) => Utils.convertToInt(connectorID)),
+ };
+ }
}

MODIFY
src/storage/mongodb/DatabaseUtils.ts
src/storage/mongodb/DatabaseUtils.ts
@@ -115,6 +115,29 @@ export default class DatabaseUtils {
});
}
+ public static pushArrayLookupInAggregation(arrayName: string, lookupMethod: (lookupParams: DbLookup) => void, lookupParams: DbLookup) {
+ // Unwind the source
+ lookupParams.aggregation.push({ '$unwind': `$${arrayName}` });
+ // Call the lookup
+ lookupMethod(lookupParams);
+ // Unwind the result array
+ // lookupParams.aggregation.push({ '$unwind': { path: `$${lookupParams.asField}`, preserveNullAndEmptyArrays: true } });
+ // Group back to arrays
+ lookupParams.aggregation.push(
+ JSON.parse(`{
+ "$group": {
+ "_id": "$_id",
+ "root": { "$first": "$$ROOT" },
+ "${arrayName}": { "$push": "$${arrayName}" }
+ }
+ }`)
+ );
+ // Replace array
+ lookupParams.aggregation.push(JSON.parse(`{ "$addFields": { "root.${arrayName}": "$${arrayName}" } }`));
+ // Replace root
+ lookupParams.aggregation.push({ $replaceRoot: { newRoot: '$root' } });
+ }
+
public static pushCollectionLookupInAggregation(collection: string, lookupParams: DbLookup, externalPipeline?: Record<string, any>[]) {
// Build Lookup's pipeline
if (!lookupParams.pipelineMatch) {

MODIFY
src/types/ChargingStation.ts
src/types/ChargingStation.ts
@@ -103,6 +103,7 @@ export interface Connector {
amperage?: number;
amperageLimit?: number;
activeTransactionID?: number;
+ userID?: string;
activeTransactionDate?: Date;
activeTagID?: string;
statusLastChangedOn?: Date;

Call delete of TxProfile after comsumption calculation in Stop Transaction + Add user ID in connector