From 2249b13016a22022a2e0b0838d7ba01fa53ecf7c Mon Sep 17 00:00:00 2001 From: Rishabh Vaish Date: Sun, 5 Apr 2026 08:08:24 -0700 Subject: [PATCH] fix(ocpp): check connector availability in RemoteStartTransaction auto-selection (#1774) MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit * fix(ocpp): check connector availability in RemoteStartTransaction auto-selection The OCPP 1.6 RemoteStartTransaction handler's connector auto-selection (when connectorId is omitted) only checked transactionStarted and reservation status but not administrative availability. This meant an Inoperative connector could be selected, causing the request to be rejected even when other Operative connectors were free. Added isConnectorAvailable() check to the auto-selection loop, matching the behavior already present in the OCPP 2.0 selectAvailableEvse(). * [autofix.ci] apply automated fixes * Update tests/charging-station/ocpp/1.6/OCPP16IncomingRequestService-RemoteStartTransaction.test.ts * Apply suggestion from @jerome-benoit --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: Jérôme Benoit --- .../ocpp/1.6/OCPP16IncomingRequestService.ts | 1 + ...uestService-RemoteStartTransaction.test.ts | 51 +++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/src/charging-station/ocpp/1.6/OCPP16IncomingRequestService.ts b/src/charging-station/ocpp/1.6/OCPP16IncomingRequestService.ts index e3f16aa7..3718e774 100644 --- a/src/charging-station/ocpp/1.6/OCPP16IncomingRequestService.ts +++ b/src/charging-station/ocpp/1.6/OCPP16IncomingRequestService.ts @@ -1228,6 +1228,7 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { connectorId++ ) { if ( + chargingStation.isConnectorAvailable(connectorId) && chargingStation.getConnectorStatus(connectorId)?.transactionStarted === false && !OCPP16ServiceUtils.hasReservation(chargingStation, connectorId, commandPayload.idTag) ) { diff --git a/tests/charging-station/ocpp/1.6/OCPP16IncomingRequestService-RemoteStartTransaction.test.ts b/tests/charging-station/ocpp/1.6/OCPP16IncomingRequestService-RemoteStartTransaction.test.ts index 40dd71d6..bcf40e75 100644 --- a/tests/charging-station/ocpp/1.6/OCPP16IncomingRequestService-RemoteStartTransaction.test.ts +++ b/tests/charging-station/ocpp/1.6/OCPP16IncomingRequestService-RemoteStartTransaction.test.ts @@ -155,6 +155,57 @@ await describe('OCPP16IncomingRequestService — RemoteStartTransaction', async assert.strictEqual(response.status, GenericStatus.Rejected) }) + // @spec §5.11 — Auto-select skips Inoperative connectors + await it('should skip inoperative connectors during auto-selection without connectorId', async () => { + // Arrange + const { station, testableService } = testContext + + // Set connector 1 to Inoperative + const connector1Status = station.getConnectorStatus(1) + if (connector1Status != null) { + connector1Status.availability = AvailabilityType.Inoperative + } + + const request: RemoteStartTransactionRequest = { + idTag: TEST_ID_TAG, + } + + // Act + const response = await testableService.handleRequestRemoteStartTransaction(station, request) + + // Assert — should select connector 2 (which is Operative) and accept + assert.strictEqual(response.status, GenericStatus.Accepted) + assert.strictEqual( + request.connectorId, + 2, + 'should auto-select connector 2 since connector 1 is Inoperative' + ) + }) + + // @spec §5.11 — All connectors Inoperative, no connectorId specified + await it('should reject remote start transaction when all connectors are inoperative and no connectorId specified', async () => { + // Arrange + const { station, testableService } = testContext + + // Set all connectors to Inoperative + for (let connectorId = 1; connectorId <= station.getNumberOfConnectors(); connectorId++) { + const connectorStatus = station.getConnectorStatus(connectorId) + if (connectorStatus != null) { + connectorStatus.availability = AvailabilityType.Inoperative + } + } + + const request: RemoteStartTransactionRequest = { + idTag: TEST_ID_TAG, + } + + // Act + const response = await testableService.handleRequestRemoteStartTransaction(station, request) + + // Assert + assert.strictEqual(response.status, GenericStatus.Rejected) + }) + // @spec §5.11 — Non-existing connector await it('should reject remote start transaction with non-existing connectorId', async () => { // Arrange -- 2.43.0