]> Piment Noir Git Repositories - e-mobility-charging-stations-simulator.git/commitdiff
refactor(ocpp): extract connector status operations into dedicated module
authorJérôme Benoit <jerome.benoit@sap.com>
Wed, 1 Apr 2026 15:13:47 +0000 (17:13 +0200)
committerJérôme Benoit <jerome.benoit@sap.com>
Wed, 1 Apr 2026 15:13:47 +0000 (17:13 +0200)
Move sendAndSetConnectorStatus, restoreConnectorStatus, and
checkConnectorStatusTransition into OCPPConnectorStatusOperations.ts
to enforce the operations/utils semantic separation without creating
circular dependencies. The new module depends only on Constants
(leaf modules), not on version-specific ServiceUtils.

src/charging-station/ocpp/1.6/OCPP16IncomingRequestService.ts
src/charging-station/ocpp/1.6/OCPP16RequestService.ts
src/charging-station/ocpp/1.6/OCPP16ResponseService.ts
src/charging-station/ocpp/1.6/OCPP16ServiceUtils.ts
src/charging-station/ocpp/2.0/OCPP20IncomingRequestService.ts
src/charging-station/ocpp/2.0/OCPP20ResponseService.ts
src/charging-station/ocpp/2.0/OCPP20ServiceUtils.ts
src/charging-station/ocpp/OCPPConnectorStatusOperations.ts [new file with mode: 0644]
src/charging-station/ocpp/OCPPServiceUtils.ts
src/charging-station/ocpp/index.ts
tests/charging-station/ocpp/OCPPConnectorStatusOperations.test.ts [moved from tests/charging-station/ocpp/OCPPServiceUtils-connectorStatus.test.ts with 97% similarity]

index c387da4b07ccaeef74a7e9c08dd31f83505a29a2..2c5eb3560bdd38bcb4f595af801f97cf62c108d8 100644 (file)
@@ -116,6 +116,7 @@ import {
   truncateId,
 } from '../../../utils/index.js'
 import { AuthContext } from '../auth/index.js'
+import { sendAndSetConnectorStatus } from '../OCPPConnectorStatusOperations.js'
 import { OCPPConstants } from '../OCPPConstants.js'
 import { OCPPIncomingRequestService } from '../OCPPIncomingRequestService.js'
 import { isIdTagAuthorized } from '../OCPPServiceOperations.js'
@@ -125,7 +126,6 @@ import {
   isConnectorIdValid,
   isIncomingRequestCommandSupported,
   isMessageTriggerSupported,
-  sendAndSetConnectorStatus,
 } from '../OCPPServiceUtils.js'
 import { OCPP16Constants } from './OCPP16Constants.js'
 import { OCPP16ServiceUtils } from './OCPP16ServiceUtils.js'
index 5b850139253ce95db212d099860929e4ec7f007e..eeb4d938376775f4bcc16b8d3707475b33a1426f 100644 (file)
@@ -17,12 +17,9 @@ import {
   type RequestParams,
 } from '../../../types/index.js'
 import { generateUUID, logger } from '../../../utils/index.js'
+import { sendAndSetConnectorStatus } from '../OCPPConnectorStatusOperations.js'
 import { OCPPRequestService } from '../OCPPRequestService.js'
-import {
-  createPayloadValidatorMap,
-  isRequestCommandSupported,
-  sendAndSetConnectorStatus,
-} from '../OCPPServiceUtils.js'
+import { createPayloadValidatorMap, isRequestCommandSupported } from '../OCPPServiceUtils.js'
 import { OCPP16Constants } from './OCPP16Constants.js'
 import { OCPP16ServiceUtils } from './OCPP16ServiceUtils.js'
 
index 9ecc9d299b64819c4d6dadd7faa7908bc97f6c36..b60ce5e92fcfc9fdf085055f84abc96ca5f99b1d 100644 (file)
@@ -35,13 +35,12 @@ import {
   type ResponseHandler,
 } from '../../../types/index.js'
 import { Constants, convertToInt, logger, truncateId } from '../../../utils/index.js'
-import { OCPPResponseService } from '../OCPPResponseService.js'
 import {
-  createPayloadValidatorMap,
-  isRequestCommandSupported,
   restoreConnectorStatus,
   sendAndSetConnectorStatus,
-} from '../OCPPServiceUtils.js'
+} from '../OCPPConnectorStatusOperations.js'
+import { OCPPResponseService } from '../OCPPResponseService.js'
+import { createPayloadValidatorMap, isRequestCommandSupported } from '../OCPPServiceUtils.js'
 import { OCPP16ServiceUtils } from './OCPP16ServiceUtils.js'
 
 const moduleName = 'OCPP16ResponseService'
index b7840fdb46f361257a5048f4caa9a968e7dfb524..9894752b037dc4bd354ab0a0633708c869c4ea4f 100644 (file)
@@ -59,6 +59,7 @@ import {
   truncateId,
 } from '../../../utils/index.js'
 import { mapOCPP16Status, OCPPAuthServiceFactory } from '../auth/index.js'
+import { sendAndSetConnectorStatus } from '../OCPPConnectorStatusOperations.js'
 import {
   buildEmptyMeterValue,
   buildMeterValue,
@@ -66,7 +67,6 @@ import {
   createPayloadConfigs,
   getSampledValueTemplate,
   PayloadValidatorOptions,
-  sendAndSetConnectorStatus,
 } from '../OCPPServiceUtils.js'
 import { OCPP16Constants } from './OCPP16Constants.js'
 
index 90df5d0c164d4025a4af7a40343869061527d3e3..14cc212bf5271da25c4692409a3cdb001a64758f 100644 (file)
@@ -147,13 +147,15 @@ import {
   mapOCPP20TokenType,
   OCPPAuthServiceFactory,
 } from '../auth/index.js'
+import {
+  restoreConnectorStatus,
+  sendAndSetConnectorStatus,
+} from '../OCPPConnectorStatusOperations.js'
 import { OCPPIncomingRequestService } from '../OCPPIncomingRequestService.js'
 import {
   buildMeterValue,
   createPayloadValidatorMap,
   isIncomingRequestCommandSupported,
-  restoreConnectorStatus,
-  sendAndSetConnectorStatus,
 } from '../OCPPServiceUtils.js'
 import {
   type GetInstalledCertificatesResult,
index 4e3c92df5558095af2d1c73962e8468eb2849990..cbe58fa23578edfe5e26b400f9fde46594905f4e 100644 (file)
@@ -41,12 +41,9 @@ import {
   type ResponseHandler,
 } from '../../../types/index.js'
 import { convertToDate, logger } from '../../../utils/index.js'
+import { sendAndSetConnectorStatus } from '../OCPPConnectorStatusOperations.js'
 import { OCPPResponseService } from '../OCPPResponseService.js'
-import {
-  createPayloadValidatorMap,
-  isRequestCommandSupported,
-  sendAndSetConnectorStatus,
-} from '../OCPPServiceUtils.js'
+import { createPayloadValidatorMap, isRequestCommandSupported } from '../OCPPServiceUtils.js'
 import { OCPP20IncomingRequestService } from './OCPP20IncomingRequestService.js'
 import { OCPP20ServiceUtils } from './OCPP20ServiceUtils.js'
 const moduleName = 'OCPP20ResponseService'
index fd1e0b2feb4b5cc3b33f69f3b42a4a5bcab8b7c2..940d1c73ecf3ac4cf58c6eba917ebe6465a1571c 100644 (file)
@@ -52,11 +52,11 @@ import {
   mapOCPP20TokenType,
   OCPPAuthServiceFactory,
 } from '../auth/index.js'
+import { sendAndSetConnectorStatus } from '../OCPPConnectorStatusOperations.js'
 import {
   buildMeterValue,
   createPayloadConfigs,
   PayloadValidatorOptions,
-  sendAndSetConnectorStatus,
 } from '../OCPPServiceUtils.js'
 import { OCPP20VariableManager } from './OCPP20VariableManager.js'
 
diff --git a/src/charging-station/ocpp/OCPPConnectorStatusOperations.ts b/src/charging-station/ocpp/OCPPConnectorStatusOperations.ts
new file mode 100644 (file)
index 0000000..d07e280
--- /dev/null
@@ -0,0 +1,119 @@
+import { type ChargingStation } from '../../charging-station/index.js'
+import { OCPPError } from '../../exception/index.js'
+import {
+  ChargingStationEvents,
+  type ConnectorStatus,
+  ConnectorStatusEnum,
+  ErrorType,
+  OCPPVersion,
+  RequestCommand,
+  type StatusNotificationRequest,
+  type StatusNotificationResponse,
+} from '../../types/index.js'
+import { logger } from '../../utils/index.js'
+import { OCPP16Constants } from './1.6/OCPP16Constants.js'
+import { OCPP20Constants } from './2.0/OCPP20Constants.js'
+
+/**
+ * Sends a StatusNotification request and updates the connector status locally.
+ * @param chargingStation - Target charging station
+ * @param commandParams - Status notification parameters including connector ID and status
+ * @param options - Optional settings to control whether the request is actually sent
+ * @param options.send - Whether to actually send the status notification
+ */
+export const sendAndSetConnectorStatus = async (
+  chargingStation: ChargingStation,
+  commandParams: StatusNotificationRequest,
+  options?: { send: boolean }
+): Promise<void> => {
+  options = { send: true, ...options }
+  const params = commandParams as Record<string, unknown>
+  const connectorId = params.connectorId as number
+  const status = (params.connectorStatus ?? params.status) as ConnectorStatusEnum
+  const connectorStatus = chargingStation.getConnectorStatus(connectorId)
+  if (connectorStatus == null) {
+    return
+  }
+  if (options.send) {
+    checkConnectorStatusTransition(chargingStation, connectorId, status)
+    await chargingStation.ocppRequestService.requestHandler<
+      StatusNotificationRequest,
+      StatusNotificationResponse
+    >(chargingStation, RequestCommand.STATUS_NOTIFICATION, commandParams)
+  }
+  connectorStatus.status = status
+  chargingStation.emitChargingStationEvent(ChargingStationEvents.connectorStatusChanged, {
+    connectorId,
+    ...connectorStatus,
+  })
+}
+
+/**
+ * Restores a connector status to Reserved or Available based on its current state.
+ * @param chargingStation - Target charging station
+ * @param connectorId - Connector ID to restore
+ * @param connectorStatus - Current connector status to evaluate
+ */
+export const restoreConnectorStatus = async (
+  chargingStation: ChargingStation,
+  connectorId: number,
+  connectorStatus: ConnectorStatus | undefined
+): Promise<void> => {
+  if (
+    connectorStatus?.reservation != null &&
+    connectorStatus.status !== ConnectorStatusEnum.Reserved
+  ) {
+    await sendAndSetConnectorStatus(chargingStation, {
+      connectorId,
+      status: ConnectorStatusEnum.Reserved,
+    } as unknown as StatusNotificationRequest)
+  } else if (connectorStatus?.status !== ConnectorStatusEnum.Available) {
+    await sendAndSetConnectorStatus(chargingStation, {
+      connectorId,
+      status: ConnectorStatusEnum.Available,
+    } as unknown as StatusNotificationRequest)
+  }
+}
+
+const checkConnectorStatusTransition = (
+  chargingStation: ChargingStation,
+  connectorId: number,
+  status: ConnectorStatusEnum
+): boolean => {
+  const fromStatus = chargingStation.getConnectorStatus(connectorId)?.status
+  let chargingStationTransitions: readonly { from?: ConnectorStatusEnum; to: ConnectorStatusEnum }[]
+  let connectorTransitions: readonly { from?: ConnectorStatusEnum; to: ConnectorStatusEnum }[]
+  switch (chargingStation.stationInfo?.ocppVersion) {
+    case OCPPVersion.VERSION_16:
+      chargingStationTransitions = OCPP16Constants.ChargePointStatusChargingStationTransitions
+      connectorTransitions = OCPP16Constants.ChargePointStatusConnectorTransitions
+      break
+    case OCPPVersion.VERSION_20:
+    case OCPPVersion.VERSION_201:
+      chargingStationTransitions = OCPP20Constants.ChargingStationStatusTransitions
+      connectorTransitions = OCPP20Constants.ConnectorStatusTransitions
+      break
+    default:
+      throw new OCPPError(
+        ErrorType.INTERNAL_ERROR,
+        // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
+        `Cannot check connector status transition: OCPP version ${chargingStation.stationInfo?.ocppVersion} not supported`,
+        RequestCommand.STATUS_NOTIFICATION
+      )
+  }
+  const transitions = connectorId === 0 ? chargingStationTransitions : connectorTransitions
+  const transitionAllowed = transitions.some(
+    transition => transition.from === fromStatus && transition.to === status
+  )
+  if (!transitionAllowed) {
+    logger.warn(
+      `${chargingStation.logPrefix()} OCPP ${
+        chargingStation.stationInfo.ocppVersion
+      } connector id ${connectorId.toString()} status transition from '${
+        // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
+        chargingStation.getConnectorStatus(connectorId)?.status
+      }' to '${status}' is not allowed`
+    )
+  }
+  return transitionAllowed
+}
index b9996f4a21aa3d375fb4622235b2a0b8dc650bef..495e8445614b88f328836ca5b7488cdb99d223d8 100644 (file)
@@ -11,10 +11,8 @@ import type { StopTransactionReason } from '../../types/index.js'
 import { type ChargingStation, getConfigurationKey } from '../../charging-station/index.js'
 import { BaseError, OCPPError } from '../../exception/index.js'
 import {
-  ChargingStationEvents,
   type ConfigurationKeyType,
   type ConnectorStatus,
-  ConnectorStatusEnum,
   CurrentType,
   ErrorType,
   FileType,
@@ -41,8 +39,6 @@ import {
   type SampledValue,
   type SampledValueTemplate,
   StandardParametersKey,
-  type StatusNotificationRequest,
-  type StatusNotificationResponse,
 } from '../../types/index.js'
 import {
   ACElectricUtils,
@@ -61,8 +57,6 @@ import {
   min,
   roundTo,
 } from '../../utils/index.js'
-import { OCPP16Constants } from './1.6/OCPP16Constants.js'
-import { OCPP20Constants } from './2.0/OCPP20Constants.js'
 import { OCPPConstants } from './OCPPConstants.js'
 
 const moduleName = 'OCPPServiceUtils'
@@ -92,67 +86,6 @@ interface SingleValueMeasurandData {
   value: number
 }
 
-/**
- * Sends a StatusNotification request and updates the connector status locally.
- * @param chargingStation - Target charging station
- * @param commandParams - Status notification parameters including connector ID and status
- * @param options - Optional settings to control whether the request is actually sent
- * @param options.send - Whether to actually send the status notification
- */
-export const sendAndSetConnectorStatus = async (
-  chargingStation: ChargingStation,
-  commandParams: StatusNotificationRequest,
-  options?: { send: boolean }
-): Promise<void> => {
-  options = { send: true, ...options }
-  const params = commandParams as Record<string, unknown>
-  const connectorId = params.connectorId as number
-  const status = (params.connectorStatus ?? params.status) as ConnectorStatusEnum
-  const connectorStatus = chargingStation.getConnectorStatus(connectorId)
-  if (connectorStatus == null) {
-    return
-  }
-  if (options.send) {
-    checkConnectorStatusTransition(chargingStation, connectorId, status)
-    await chargingStation.ocppRequestService.requestHandler<
-      StatusNotificationRequest,
-      StatusNotificationResponse
-    >(chargingStation, RequestCommand.STATUS_NOTIFICATION, commandParams)
-  }
-  connectorStatus.status = status
-  chargingStation.emitChargingStationEvent(ChargingStationEvents.connectorStatusChanged, {
-    connectorId,
-    ...connectorStatus,
-  })
-}
-
-/**
- * Restores a connector status to Reserved or Available based on its current state.
- * @param chargingStation - Target charging station
- * @param connectorId - Connector ID to restore
- * @param connectorStatus - Current connector status to evaluate
- */
-export const restoreConnectorStatus = async (
-  chargingStation: ChargingStation,
-  connectorId: number,
-  connectorStatus: ConnectorStatus | undefined
-): Promise<void> => {
-  if (
-    connectorStatus?.reservation != null &&
-    connectorStatus.status !== ConnectorStatusEnum.Reserved
-  ) {
-    await sendAndSetConnectorStatus(chargingStation, {
-      connectorId,
-      status: ConnectorStatusEnum.Reserved,
-    } as unknown as StatusNotificationRequest)
-  } else if (connectorStatus?.status !== ConnectorStatusEnum.Available) {
-    await sendAndSetConnectorStatus(chargingStation, {
-      connectorId,
-      status: ConnectorStatusEnum.Available,
-    } as unknown as StatusNotificationRequest)
-  }
-}
-
 /**
  * Maps an OCPP 1.6 or generic stop transaction reason to OCPP 2.0 stopped and trigger reasons.
  * @param reason - Stop transaction reason to map
@@ -226,49 +159,6 @@ export const mapStopReasonToOCPP20 = (
   }
 }
 
-const checkConnectorStatusTransition = (
-  chargingStation: ChargingStation,
-  connectorId: number,
-  status: ConnectorStatusEnum
-): boolean => {
-  const fromStatus = chargingStation.getConnectorStatus(connectorId)?.status
-  let chargingStationTransitions: readonly { from?: ConnectorStatusEnum; to: ConnectorStatusEnum }[]
-  let connectorTransitions: readonly { from?: ConnectorStatusEnum; to: ConnectorStatusEnum }[]
-  switch (chargingStation.stationInfo?.ocppVersion) {
-    case OCPPVersion.VERSION_16:
-      chargingStationTransitions = OCPP16Constants.ChargePointStatusChargingStationTransitions
-      connectorTransitions = OCPP16Constants.ChargePointStatusConnectorTransitions
-      break
-    case OCPPVersion.VERSION_20:
-    case OCPPVersion.VERSION_201:
-      chargingStationTransitions = OCPP20Constants.ChargingStationStatusTransitions
-      connectorTransitions = OCPP20Constants.ConnectorStatusTransitions
-      break
-    default:
-      throw new OCPPError(
-        ErrorType.INTERNAL_ERROR,
-        // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
-        `Cannot check connector status transition: OCPP version ${chargingStation.stationInfo?.ocppVersion} not supported`,
-        RequestCommand.STATUS_NOTIFICATION
-      )
-  }
-  const transitions = connectorId === 0 ? chargingStationTransitions : connectorTransitions
-  const transitionAllowed = transitions.some(
-    transition => transition.from === fromStatus && transition.to === status
-  )
-  if (!transitionAllowed) {
-    logger.warn(
-      `${chargingStation.logPrefix()} OCPP ${
-        chargingStation.stationInfo.ocppVersion
-      } connector id ${connectorId.toString()} status transition from '${
-        // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
-        chargingStation.getConnectorStatus(connectorId)?.status
-      }' to '${status}' is not allowed`
-    )
-  }
-  return transitionAllowed
-}
-
 /**
  * Converts Ajv validation errors to the corresponding OCPP error type.
  * @param errors - Array of Ajv validation error objects
index 4d084d960179aafc1a5d6d8cccf21c706dad7aa3..d570f06efd81fd01963c7e19fb561556517f8de5 100644 (file)
@@ -8,6 +8,10 @@ export { OCPP20ResponseService } from './2.0/OCPP20ResponseService.js'
 export { buildTransactionEvent, OCPP20ServiceUtils } from './2.0/OCPP20ServiceUtils.js'
 export { OCPP20VariableManager } from './2.0/OCPP20VariableManager.js'
 export { OCPPAuthServiceFactory } from './auth/index.js'
+export {
+  restoreConnectorStatus,
+  sendAndSetConnectorStatus,
+} from './OCPPConnectorStatusOperations.js'
 export { OCPPConstants } from './OCPPConstants.js'
 export { OCPPIncomingRequestService } from './OCPPIncomingRequestService.js'
 export { OCPPRequestService } from './OCPPRequestService.js'
@@ -20,4 +24,4 @@ export {
   stopRunningTransactions,
   stopTransactionOnConnector,
 } from './OCPPServiceOperations.js'
-export { buildMeterValue, sendAndSetConnectorStatus } from './OCPPServiceUtils.js'
+export { buildMeterValue } from './OCPPServiceUtils.js'
similarity index 97%
rename from tests/charging-station/ocpp/OCPPServiceUtils-connectorStatus.test.ts
rename to tests/charging-station/ocpp/OCPPConnectorStatusOperations.test.ts
index 7a6f0802129b3b181c10cb4b6662a56e7df6dc93..3aad34228af5b2ee5584744ed57c17b2b9bc3414 100644 (file)
@@ -1,5 +1,5 @@
 /**
- * @file Tests for OCPPServiceUtils connector status management
+ * @file Tests for OCPPConnectorStatusOperations
  * @description Verifies sendAndSetConnectorStatus and restoreConnectorStatus functions
  *
  * Covers:
@@ -17,7 +17,7 @@ import type { MockChargingStationOptions } from '../helpers/StationHelpers.js'
 import {
   restoreConnectorStatus,
   sendAndSetConnectorStatus,
-} from '../../../src/charging-station/ocpp/OCPPServiceUtils.js'
+} from '../../../src/charging-station/ocpp/OCPPConnectorStatusOperations.js'
 import {
   ConnectorStatusEnum,
   type OCPP16StatusNotificationRequest,
@@ -44,7 +44,7 @@ function createStationWithRequestHandler (opts?: Partial<MockChargingStationOpti
   return { requestHandler, station }
 }
 
-await describe('OCPPServiceUtils — connector status management', async () => {
+await describe('OCPPConnectorStatusOperations', async () => {
   afterEach(() => {
     standardCleanup()
   })