Fix Charge Point ID collision with Connector ID in OCPI
MODIFY
src/client/ocpi/CpoOCPIClient.ts
src/client/ocpi/CpoOCPIClient.ts
@@ -175,7 +175,7 @@ export default class CpoOCPIClient extends OCPIClient {
{
location_id: chargingStation.siteID,
// Gireve does not support authorization request on multiple EVSE
- evse_uids: [OCPIUtils.buildEvseUID(chargingStation, connector)]
+ evse_uids: [RoamingUtils.buildEvseUID(chargingStation, connector.connectorId)]
};
// Call IOP
const response = await this.axiosInstance.post(
@@ -462,7 +462,7 @@ export default class CpoOCPIClient extends OCPIClient {
siteID: chargingStation.siteID,
siteAreaID: chargingStation.siteAreaID,
companyID: chargingStation.companyID
- }, chargingStation.siteID, OCPIUtils.buildEvseUID(chargingStation, connector),
+ }, chargingStation.siteID, RoamingUtils.buildEvseUID(chargingStation, connector.connectorId),
status ?? OCPIUtils.convertStatus2OCPIStatus(connector.status));
results.push(result.data);
}
@@ -507,7 +507,7 @@ export default class CpoOCPIClient extends OCPIClient {
siteAreaID: chargingStation.siteAreaID,
companyID: chargingStation.companyID,
}, chargingStation.siteID,
- OCPIUtils.buildEvseUID(chargingStation, connector), OCPIUtils.convertStatus2OCPIStatus(connector.status));
+ RoamingUtils.buildEvseUID(chargingStation, connector.connectorId), OCPIUtils.convertStatus2OCPIStatus(connector.status));
}
public async checkSessions(): Promise<OCPIResult> {
@@ -1097,8 +1097,8 @@ export default class CpoOCPIClient extends OCPIClient {
},
type: OCPILocationType.UNKNOWN,
evses: [{
- uid: OCPIUtils.buildEvseUID(chargingStation, Utils.getConnectorFromID(chargingStation, connectorID)),
- evse_id: RoamingUtils.buildEvseID(countryID, partyID, chargingStation.id, chargePoint && chargePoint.cannotChargeInParallel ? chargePoint.chargePointID : connectorID),
+ uid: RoamingUtils.buildEvseUID(chargingStation, connectorID),
+ evse_id: RoamingUtils.buildEvseID(countryID, partyID, chargingStation, connectorID),
location_id: chargingStation.siteID,
status: OCPIUtils.convertStatus2OCPIStatus(status),
capabilities: [OCPICapability.REMOTE_START_STOP_CAPABLE, OCPICapability.RFID_READER],

MODIFY
src/server/ocpi/OCPIUtils.ts
src/server/ocpi/OCPIUtils.ts
import ChargingStation, { Connector, ConnectorType } from '../../types/ChargingStation';
-import { OCPIConnector, OCPIConnectorType } from '../../types/ocpi/OCPIConnector';
-import { OCPIEvse, OCPIEvseStatus } from '../../types/ocpi/OCPIEvse';
-import { OCPITariff, OCPITariffDimensionType } from '../../types/ocpi/OCPITariff';
-import { OCPIToken, OCPITokenType } from '../../types/ocpi/OCPIToken';
-
import AppError from '../../exception/AppError';
import BackendError from '../../exception/BackendError';
-import { ChargePointStatus } from '../../types/ocpp/OCPPServer';
import ChargingStationStorage from '../../storage/mongodb/ChargingStationStorage';
import Company from '../../types/Company';
import CompanyStorage from '../../storage/mongodb/CompanyStorage';
@@ -14,21 +8,27 @@ import Constants from '../../utils/Constants';
import Logging from '../../utils/Logging';
import LoggingHelper from '../../utils/LoggingHelper';
import OCPIEndpoint from '../../types/ocpi/OCPIEndpoint';
+import RoamingUtils from '../../utils/RoamingUtils';
+import SiteAreaStorage from '../../storage/mongodb/SiteAreaStorage';
+import SiteStorage from '../../storage/mongodb/SiteStorage';
+import { OCPIConnector, OCPIConnectorType } from '../../types/ocpi/OCPIConnector';
+import { OCPIEvse, OCPIEvseStatus } from '../../types/ocpi/OCPIEvse';
import { OCPILocation } from '../../types/ocpi/OCPILocation';
import { OCPIResponse } from '../../types/ocpi/OCPIResponse';
import { OCPIStatusCode } from '../../types/ocpi/OCPIStatusCode';
+import { OCPITariff, OCPITariffDimensionType } from '../../types/ocpi/OCPITariff';
+import { OCPIToken, OCPITokenType } from '../../types/ocpi/OCPIToken';
+import { ChargePointStatus } from '../../types/ocpp/OCPPServer';
import { Request } from 'express';
import { ServerAction } from '../../types/Server';
import { SimplePricingSetting } from '../../types/Setting';
import Site from '../../types/Site';
import SiteArea from '../../types/SiteArea';
-import SiteAreaStorage from '../../storage/mongodb/SiteAreaStorage';
-import SiteStorage from '../../storage/mongodb/SiteStorage';
import Tenant from '../../types/Tenant';
import Utils from '../../utils/Utils';
-import _ from 'lodash';
import moment from 'moment';
+
const MODULE_NAME = 'OCPIUtils';
export default class OCPIUtils {
@@ -99,22 +99,11 @@ export default class OCPIUtils {
return `${countryCode}*${partyId}-${locationId}`;
}
- public static buildEvseUID(chargingStation: ChargingStation, connector: Connector): string {
- // connectors are grouped in the same evse when the connectors cannot charge in parallel
- if (connector.chargePointID) {
- const chargePoint = Utils.getChargePointFromID(chargingStation, connector.chargePointID);
- if (chargePoint && chargePoint.cannotChargeInParallel) {
- return `${chargingStation.id}*${chargePoint.chargePointID}`;
- }
- }
- return `${chargingStation.id}*${connector.connectorId}`;
- }
-
public static buildEvseUIDs(chargingStation: ChargingStation): string[] {
const evseUIDs: string[] = [];
- for (const _connector of chargingStation.connectors) {
- if (_connector) {
- evseUIDs.push(OCPIUtils.buildEvseUID(chargingStation, _connector));
+ for (const connector of chargingStation.connectors) {
+ if (connector) {
+ evseUIDs.push(RoamingUtils.buildEvseUID(chargingStation, connector.connectorId));
}
}
return evseUIDs;

MODIFY
src/server/ocpi/ocpi-services-impl/ocpi-2.1.1/OCPIUtilsService.ts
src/server/ocpi/ocpi-services-impl/ocpi-2.1.1/OCPIUtilsService.ts
@@ -118,7 +118,6 @@ export default class OCPIUtilsService {
public static async convertCPOSite2Location(tenant: Tenant, site: Site,
options: OCPILocationOptions, withChargingStations: boolean, settings: OcpiSetting): Promise<OCPILocation> {
const hasValidSiteGpsCoordinates = Utils.hasValidGpsCoordinates(site.address?.coordinates);
- // Build object
return {
id: site.id,
type: OCPILocationType.UNKNOWN,
@@ -606,7 +605,7 @@ export default class OCPIUtilsService {
break;
}
return {
- id: RoamingUtils.buildEvseID(countryID, partyID, chargingStation.id, connector.connectorId),
+ id: RoamingUtils.buildEvseID(countryID, partyID, chargingStation, connector.connectorId),
standard: type,
format: format,
voltage: voltage,
@@ -660,11 +659,11 @@ export default class OCPIUtilsService {
} else {
connectors = chargingStation.connectors.filter((connector) => connector !== null);
}
- return connectors.map((connector) => {
+ const evses: OCPIEvse[] = [];
+ for (const connector of connectors) {
const evse: OCPIEvse = {
- uid: OCPIUtils.buildEvseUID(chargingStation, connector),
- evse_id: RoamingUtils.buildEvseID(options.countryID, options.partyID,
- chargingStation.id, connector.connectorId),
+ uid: RoamingUtils.buildEvseUID(chargingStation, connector.connectorId),
+ evse_id: RoamingUtils.buildEvseID(options.countryID, options.partyID, chargingStation, connector.connectorId),
location_id: chargingStation.siteID,
status: chargingStation.inactive ? OCPIEvseStatus.INOPERATIVE : OCPIUtils.convertStatus2OCPIStatus(connector.status),
capabilities: [OCPICapability.REMOTE_START_STOP_CAPABLE, OCPICapability.RFID_READER],
@@ -683,8 +682,9 @@ export default class OCPIUtilsService {
evse.siteAreaID = chargingStation.siteAreaID;
evse.companyID = chargingStation.companyID;
}
- return evse;
- });
+ evses.push(evse);
+ }
+ return evses;
}
private static convertChargingStation2UniqueEvse(tenant: Tenant, chargingStation: ChargingStation,
@@ -704,8 +704,8 @@ export default class OCPIUtilsService {
// Build evse
const evse: OCPIEvse = {
// Evse uid must contains the chargePoint.id
- uid: OCPIUtils.buildEvseUID(chargingStation, connectors[0]),
- evse_id: RoamingUtils.buildEvseID(options.countryID, options.partyID, chargingStation.id, chargePoint.chargePointID),
+ uid: RoamingUtils.buildEvseUID(chargingStation, connectors[0].connectorId),
+ evse_id: RoamingUtils.buildEvseID(options.countryID, options.partyID, chargingStation, connectors[0].connectorId),
location_id: chargingStation.siteID,
status: chargingStation.inactive ? OCPIEvseStatus.INOPERATIVE : OCPIUtils.convertStatus2OCPIStatus(connectorOneStatus),
capabilities: [OCPICapability.REMOTE_START_STOP_CAPABLE, OCPICapability.RFID_READER],

MODIFY
src/server/oicp/OICPUtils.ts
src/server/oicp/OICPUtils.ts
@@ -42,7 +42,7 @@ export default class OICPUtils {
public static convertConnector2EvseStatus(chargingStation: ChargingStation, connector: Connector,
options: { countryID: string; partyID: string; addChargeBoxID?: boolean}): OICPEvseStatusRecord {
return {
- EvseID: RoamingUtils.buildEvseID(options.countryID, options.partyID, chargingStation.id, connector.connectorId),
+ EvseID: RoamingUtils.buildEvseID(options.countryID, options.partyID, chargingStation, connector.connectorId),
EvseStatus: chargingStation.inactive ? OICPEvseStatus.OutOfService : OICPUtils.convertStatus2OICPEvseStatus(connector.status),
ChargingStationID: chargingStation.id,
};
@@ -134,7 +134,7 @@ export default class OICPUtils {
let foundConnector: Connector;
if (chargingStation) {
for (const connector of chargingStation.connectors) {
- if (evseID === RoamingUtils.buildEvseID(evseIDComponents.countryCode, evseIDComponents.partyId, chargingStation.id, connector.connectorId)) {
+ if (evseID === RoamingUtils.buildEvseID(evseIDComponents.countryCode, evseIDComponents.partyId, chargingStation, connector.connectorId)) {
foundConnector = connector;
}
}
@@ -415,7 +415,7 @@ export default class OICPUtils {
const evse: OICPEvseDataRecord = {} as OICPEvseDataRecord;
evse.deltaType; // Optional
evse.lastUpdate; // Optional
- evse.EvseID = RoamingUtils.buildEvseID(options.countryID, options.partyID, chargingStation.id, connector.connectorId);
+ evse.EvseID = RoamingUtils.buildEvseID(options.countryID, options.partyID, chargingStation, connector.connectorId);
evse.ChargingPoolID = OICPUtils.buildEChargingPoolID(options.countryID, options.partyID, siteArea.id); // Optional
evse.ChargingStationID = chargingStation.id; // Optional
evse.ChargingStationNames = [

MODIFY
src/utils/RoamingUtils.ts
src/utils/RoamingUtils.ts
+import ChargingStation from '../types/ChargingStation';
import { EvseIdComponents } from '../types/oicp/OICPEvse';
+import Utils from './Utils';
export default class RoamingUtils {
@@ -6,12 +8,32 @@ export default class RoamingUtils {
return `${countryCode}*${partyId}`;
}
- public static buildEvseID(countryCode: string, partyId: string, chargingStationID: string, connectorID: number): string {
+ public static buildEvseID(countryCode: string, partyId: string, chargingStation: ChargingStation, connectorID: number): string {
// Format follows the eMI3 string format for EVSE: https://emi3group.com/wp-content/uploads/sites/5/2018/12/eMI3-standard-v1.0-Part-2.pdf
- const evseID = `${RoamingUtils.buildOperatorName(countryCode, partyId)}*E${chargingStationID}*${connectorID}`;
+ let evseID = `${RoamingUtils.buildOperatorName(countryCode, partyId)}*E${chargingStation.id}*${connectorID}`;
+ // connectors are grouped in the same evse when the connectors cannot charge in parallel
+ const connector = Utils.getConnectorFromID(chargingStation, connectorID);
+ if (connector.chargePointID) {
+ const chargePoint = Utils.getChargePointFromID(chargingStation, connector.chargePointID);
+ if (chargePoint && chargePoint.cannotChargeInParallel) {
+ evseID = `${RoamingUtils.buildOperatorName(countryCode, partyId)}*E${chargingStation.id}*CP${chargePoint.chargePointID}`;
+ }
+ }
return evseID.replace(/[\W_]+/g, '*').toUpperCase();
}
+ public static buildEvseUID(chargingStation: ChargingStation, connectorID: number): string {
+ // connectors are grouped in the same evse when the connectors cannot charge in parallel
+ const connector = Utils.getConnectorFromID(chargingStation, connectorID);
+ if (connector.chargePointID) {
+ const chargePoint = Utils.getChargePointFromID(chargingStation, connector.chargePointID);
+ if (chargePoint && chargePoint.cannotChargeInParallel) {
+ return `${chargingStation.id}*CP${chargePoint.chargePointID}`;
+ }
+ }
+ return `${chargingStation.id}*${connector.connectorId}`;
+ }
+
public static getEvseIdComponents(evseID: string): EvseIdComponents {
// Problem: it is not safe to derive the chargingStationId from evseID because all characters that are not alphanumeric and underscores are replaced with '*'
// also: evseId is set to upper case

Fix Charge Point ID collision with Connector ID in OCPI