]> Piment Noir Git Repositories - e-mobility-charging-stations-simulator.git/commitdiff
fix: properly resolve evse id
authorJérôme Benoit <jerome.benoit@sap.com>
Sun, 9 Nov 2025 18:22:46 +0000 (19:22 +0100)
committerJérôme Benoit <jerome.benoit@sap.com>
Sun, 9 Nov 2025 18:22:46 +0000 (19:22 +0100)
Signed-off-by: Jérôme Benoit <jerome.benoit@sap.com>
src/charging-station/ChargingStation.ts
src/charging-station/ocpp/2.0/OCPP20ServiceUtils.ts
src/charging-station/ocpp/OCPPServiceUtils.ts
tests/ChargingStationFactory.test.ts
tests/ChargingStationFactory.ts

index cf1a9f81f1b27fcb4ffb5aa632c8da9f2e57729d..125f10de7590685bf15e4b04b8490e6de1d1f46c 100644 (file)
@@ -487,6 +487,18 @@ export class ChargingStation extends EventEmitter {
     )
   }
 
+  public getEvseIdByConnectorId (connectorId: number): number | undefined {
+    if (!this.hasEvses) {
+      return undefined
+    }
+    for (const [evseId, evseStatus] of this.evses) {
+      if (evseStatus.connectors.has(connectorId)) {
+        return evseId
+      }
+    }
+    return undefined
+  }
+
   public getHeartbeatInterval (): number {
     const HeartbeatInterval = getConfigurationKey(this, StandardParametersKey.HeartbeatInterval)
     if (HeartbeatInterval != null) {
index 11c1ab1c0fcfbacd454917dbf6b9c591d664365a..5e48dd0cdf7f404d64111853bc60baedb26cf7ec 100644 (file)
@@ -109,7 +109,7 @@ export class OCPP20ServiceUtils extends OCPPServiceUtils {
     )
   }
 
-  public static async requestStopTransaction(
+  public static async requestStopTransaction (
     chargingStation: ChargingStation,
     connectorId: number
   ): Promise<GenericResponse> {
@@ -133,10 +133,18 @@ export class OCPP20ServiceUtils extends OCPPServiceUtils {
         return OCPP20Constants.OCPP_RESPONSE_REJECTED
       }
 
+      const evseId = chargingStation.getEvseIdByConnectorId(connectorId)
+      if (evseId == null) {
+        logger.error(
+          `${chargingStation.logPrefix()} OCPP20ServiceUtils.requestStopTransaction: Cannot find EVSE ID for connector ${connectorId.toString()}`
+        )
+        return OCPP20Constants.OCPP_RESPONSE_REJECTED
+      }
+
       const transactionEventRequest: OCPP20TransactionEventRequest = {
         eventType: OCPP20TransactionEventEnumType.Ended,
         evse: {
-          id: connectorId,
+          id: evseId,
         },
         seqNo: 0, // This should be managed by the transaction sequence
         timestamp: new Date(),
index df70b9b1e0f2f5f2a23b723a0b1dc772a62516de..c82e9a7ea278f48af94e5b81547df2504afa50fc 100644 (file)
@@ -114,7 +114,7 @@ const buildStatusNotificationRequest = (
         connectorId,
         connectorStatus: status as OCPP20ConnectorStatusEnumType,
         // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
-        evseId: evseId!,
+        evseId: evseId ?? chargingStation.getEvseIdByConnectorId(connectorId)!,
         timestamp: new Date(),
       } satisfies OCPP20StatusNotificationRequest
     default:
index f01cef396b8799efddfca89bfacb67db39c00856..a99753ae0787f71654f8365b8b35a08207b269d9 100644 (file)
@@ -469,6 +469,61 @@ await describe('ChargingStationFactory', async () => {
       })
     })
 
+    await describe('getEvseIdByConnectorId', async () => {
+      await it('Should return undefined for stations without EVSEs', () => {
+        const station = createChargingStation({
+          connectorsCount: 3,
+          stationInfo: { ocppVersion: OCPPVersion.VERSION_16 }, // OCPP 1.6 doesn't use EVSEs
+        })
+
+        expect(station.getEvseIdByConnectorId(1)).toBeUndefined()
+        expect(station.getEvseIdByConnectorId(2)).toBeUndefined()
+      })
+
+      await it('Should return correct EVSE ID for connectors in EVSE mode', () => {
+        const station = createChargingStation({
+          connectorsCount: 6,
+          evseConfiguration: { evsesCount: 2 }, // 2 EVSEs with 3 connectors each
+          stationInfo: { ocppVersion: OCPPVersion.VERSION_201 },
+        })
+
+        // EVSE 1 should have connectors 1, 2, 3
+        expect(station.getEvseIdByConnectorId(1)).toBe(1)
+        expect(station.getEvseIdByConnectorId(2)).toBe(1)
+        expect(station.getEvseIdByConnectorId(3)).toBe(1)
+
+        // EVSE 2 should have connectors 4, 5, 6
+        expect(station.getEvseIdByConnectorId(4)).toBe(2)
+        expect(station.getEvseIdByConnectorId(5)).toBe(2)
+        expect(station.getEvseIdByConnectorId(6)).toBe(2)
+      })
+
+      await it('Should return undefined for non-existent connector IDs', () => {
+        const station = createChargingStation({
+          connectorsCount: 4,
+          evseConfiguration: { evsesCount: 2 },
+          stationInfo: { ocppVersion: OCPPVersion.VERSION_201 },
+        })
+
+        expect(station.getEvseIdByConnectorId(0)).toBeUndefined() // Connector 0 not in EVSEs
+        expect(station.getEvseIdByConnectorId(99)).toBeUndefined() // Non-existent connector
+        expect(station.getEvseIdByConnectorId(-1)).toBeUndefined() // Invalid connector ID
+      })
+
+      await it('Should handle single EVSE with multiple connectors', () => {
+        const station = createChargingStation({
+          connectorsCount: 3,
+          evseConfiguration: { evsesCount: 1 }, // Single EVSE with all connectors
+          stationInfo: { ocppVersion: OCPPVersion.VERSION_201 },
+        })
+
+        // All connectors should belong to EVSE 1
+        expect(station.getEvseIdByConnectorId(1)).toBe(1)
+        expect(station.getEvseIdByConnectorId(2)).toBe(1)
+        expect(station.getEvseIdByConnectorId(3)).toBe(1)
+      })
+    })
+
     await describe('isConnectorAvailable', async () => {
       await it('Should return false for connector ID 0', () => {
         const station = createChargingStation({ connectorsCount: 2 })
index 5bda606acc9919e62ca0008f57b01c40c2b3f4fc..4a7a2b8ebae53f5c8ccbc0bdec9e8c0159b65694 100644 (file)
@@ -111,6 +111,17 @@ export function createChargingStation (options: ChargingStationOptions = {}): Ch
       }
       return chargingStation.connectors.get(connectorId)
     },
+    getEvseIdByConnectorId: (connectorId: number) => {
+      if (!chargingStation.hasEvses) {
+        return undefined
+      }
+      for (const [evseId, evseStatus] of chargingStation.evses.entries()) {
+        if (evseStatus.connectors.has(connectorId)) {
+          return evseId
+        }
+      }
+      return undefined
+    },
     getEvseIdByTransactionId: (transactionId: string) => {
       // Search through EVSEs to find one with matching transaction ID
       if (chargingStation.hasEvses) {