From 2569d5de16ec0eff6f60d2f05d52985888deb553 Mon Sep 17 00:00:00 2001 From: =?utf8?q?J=C3=A9r=C3=B4me=20Benoit?= Date: Mon, 10 Nov 2025 15:30:23 +0100 Subject: [PATCH] refactor: cleanup payload validation code MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Signed-off-by: Jérôme Benoit --- src/charging-station/ChargingStation.ts | 31 +- src/charging-station/Helpers.ts | 6 +- .../ocpp/1.6/OCPP16IncomingRequestService.ts | 185 +--------- .../ocpp/1.6/OCPP16RequestService.ts | 118 +------ .../ocpp/1.6/OCPP16ResponseService.ts | 309 +---------------- .../ocpp/1.6/OCPP16ServiceUtils.ts | 315 +++++++++++++++++- .../ocpp/2.0/OCPP20IncomingRequestService.ts | 75 +---- .../ocpp/2.0/OCPP20RequestService.ts | 53 +-- .../ocpp/2.0/OCPP20ResponseService.ts | 106 +----- .../ocpp/2.0/OCPP20ServiceUtils.ts | 178 +++++++++- .../ocpp/OCPPIncomingRequestService.ts | 4 +- .../ocpp/OCPPRequestService.ts | 6 +- .../ocpp/OCPPResponseService.ts | 4 +- src/charging-station/ocpp/OCPPServiceUtils.ts | 112 +++++-- 14 files changed, 641 insertions(+), 861 deletions(-) diff --git a/src/charging-station/ChargingStation.ts b/src/charging-station/ChargingStation.ts index 9569f9b1..1ee68a9a 100644 --- a/src/charging-station/ChargingStation.ts +++ b/src/charging-station/ChargingStation.ts @@ -421,24 +421,19 @@ export class ChargingStation extends EventEmitter { public getConnectorMaximumAvailablePower (connectorId: number): number { let connectorAmperageLimitationLimit: number | undefined const amperageLimitation = this.getAmperageLimitation() - if ( - amperageLimitation != null && - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - amperageLimitation < this.stationInfo!.maximumAmperage! - ) { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + if (amperageLimitation != null && amperageLimitation < this.stationInfo!.maximumAmperage!) { + const voltageOut = this.getVoltageOut() connectorAmperageLimitationLimit = (this.stationInfo?.currentOutType === CurrentType.AC ? ACElectricUtils.powerTotal( this.getNumberOfPhases(), - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - this.stationInfo.voltageOut!, + voltageOut, amperageLimitation * (this.hasEvses ? this.getNumberOfEvses() : this.getNumberOfConnectors()) ) : // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - DCElectricUtils.power(this.stationInfo!.voltageOut!, amperageLimitation)) / - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - this.powerDivider! + DCElectricUtils.power(voltageOut, amperageLimitation)) / this.powerDivider! } // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const connectorMaximumPower = this.stationInfo!.maximumPower! / this.powerDivider! @@ -635,6 +630,14 @@ export class ChargingStation extends EventEmitter { } } + public getVoltageOut (stationInfo?: ChargingStationInfo): Voltage { + return ( + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + (stationInfo ?? this.stationInfo!).voltageOut ?? + getDefaultVoltageOut(this.getCurrentOutType(stationInfo), this.logPrefix(), this.templateFile) + ) + } + public getWebSocketPingInterval (): number { return getConfigurationKey(this, StandardParametersKey.WebSocketPingInterval) != null ? convertToInt(getConfigurationKey(this, StandardParametersKey.WebSocketPingInterval)?.value) @@ -1471,14 +1474,6 @@ export class ChargingStation extends EventEmitter { return stationTemplate?.useConnectorId0 ?? Constants.DEFAULT_STATION_INFO.useConnectorId0! } - private getVoltageOut (stationInfo?: ChargingStationInfo): Voltage { - return ( - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - (stationInfo ?? this.stationInfo!).voltageOut ?? - getDefaultVoltageOut(this.getCurrentOutType(stationInfo), this.logPrefix(), this.templateFile) - ) - } - private handleErrorMessage (errorResponse: ErrorResponse): void { const [messageType, messageId, errorType, errorMessage, errorDetails] = errorResponse if (!this.requests.has(messageId)) { diff --git a/src/charging-station/Helpers.ts b/src/charging-station/Helpers.ts index c1edc530..bd354879 100644 --- a/src/charging-station/Helpers.ts +++ b/src/charging-station/Helpers.ts @@ -810,15 +810,13 @@ const buildChargingProfilesLimit = ( ? limit : ACElectricUtils.powerTotal( chargingStation.getNumberOfPhases(), - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - chargingStation.stationInfo.voltageOut!, + chargingStation.getVoltageOut(), limit ) case CurrentType.DC: return chargingProfile.chargingSchedule.chargingRateUnit === ChargingRateUnitType.WATT ? limit - : // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - DCElectricUtils.power(chargingStation.stationInfo.voltageOut!, limit) + : DCElectricUtils.power(chargingStation.getVoltageOut(), limit) default: logger.error( `${chargingStation.logPrefix()} ${moduleName}.buildChargingProfilesLimit: ${errorMsg}` diff --git a/src/charging-station/ocpp/1.6/OCPP16IncomingRequestService.ts b/src/charging-station/ocpp/1.6/OCPP16IncomingRequestService.ts index b14ffbf5..2ace4b87 100644 --- a/src/charging-station/ocpp/1.6/OCPP16IncomingRequestService.ts +++ b/src/charging-station/ocpp/1.6/OCPP16IncomingRequestService.ts @@ -55,7 +55,6 @@ import { type OCPP16ChargingProfile, OCPP16ChargingProfilePurposeType, type OCPP16ChargingSchedule, - type OCPP16ClearCacheRequest, type OCPP16ClearChargingProfileRequest, type OCPP16ClearChargingProfileResponse, type OCPP16DataTransferRequest, @@ -119,7 +118,7 @@ import { OCPP16ServiceUtils } from './OCPP16ServiceUtils.js' const moduleName = 'OCPP16IncomingRequestService' export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { - protected payloadValidateFunctions: Map> + protected payloadValidatorFunctions: Map> private readonly incomingRequestHandlers: Map< OCPP16IncomingRequestCommand, @@ -201,181 +200,11 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { this.handleRequestUpdateFirmware.bind(this) as unknown as IncomingRequestHandler, ], ]) - this.payloadValidateFunctions = new Map< - OCPP16IncomingRequestCommand, - ValidateFunction - >([ - [ - OCPP16IncomingRequestCommand.CANCEL_RESERVATION, - this.ajv.compile( - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/CancelReservation.json', - moduleName, - 'constructor' - ) - ), - ], - [ - OCPP16IncomingRequestCommand.CHANGE_AVAILABILITY, - this.ajv.compile( - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/ChangeAvailability.json', - moduleName, - 'constructor' - ) - ), - ], - [ - OCPP16IncomingRequestCommand.CHANGE_CONFIGURATION, - this.ajv.compile( - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/ChangeConfiguration.json', - moduleName, - 'constructor' - ) - ), - ], - [ - OCPP16IncomingRequestCommand.CLEAR_CACHE, - this.ajv.compile( - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/ClearCache.json', - moduleName, - 'constructor' - ) - ), - ], - [ - OCPP16IncomingRequestCommand.CLEAR_CHARGING_PROFILE, - this.ajv.compile( - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/ClearChargingProfile.json', - moduleName, - 'constructor' - ) - ), - ], - [ - OCPP16IncomingRequestCommand.DATA_TRANSFER, - this.ajv.compile( - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/DataTransfer.json', - moduleName, - 'constructor' - ) - ), - ], - [ - OCPP16IncomingRequestCommand.GET_COMPOSITE_SCHEDULE, - this.ajv.compile( - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/GetCompositeSchedule.json', - moduleName, - 'constructor' - ) - ), - ], - [ - OCPP16IncomingRequestCommand.GET_CONFIGURATION, - this.ajv.compile( - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/GetConfiguration.json', - moduleName, - 'constructor' - ) - ), - ], - [ - OCPP16IncomingRequestCommand.GET_DIAGNOSTICS, - this.ajv.compile( - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/GetDiagnostics.json', - moduleName, - 'constructor' - ) - ), - ], - [ - OCPP16IncomingRequestCommand.REMOTE_START_TRANSACTION, - this.ajv.compile( - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/RemoteStartTransaction.json', - moduleName, - 'constructor' - ) - ), - ], - [ - OCPP16IncomingRequestCommand.REMOTE_STOP_TRANSACTION, - this.ajv.compile( - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/RemoteStopTransaction.json', - moduleName, - 'constructor' - ) - ), - ], - [ - OCPP16IncomingRequestCommand.RESERVE_NOW, - this.ajv.compile( - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/ReserveNow.json', - moduleName, - 'constructor' - ) - ), - ], - [ - OCPP16IncomingRequestCommand.RESET, - this.ajv.compile( - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/Reset.json', - moduleName, - 'constructor' - ) - ), - ], - [ - OCPP16IncomingRequestCommand.SET_CHARGING_PROFILE, - this.ajv.compile( - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/SetChargingProfile.json', - moduleName, - 'constructor' - ) - ), - ], - [ - OCPP16IncomingRequestCommand.TRIGGER_MESSAGE, - this.ajv.compile( - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/TriggerMessage.json', - moduleName, - 'constructor' - ) - ), - ], - [ - OCPP16IncomingRequestCommand.UNLOCK_CONNECTOR, - this.ajv.compile( - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/UnlockConnector.json', - moduleName, - 'constructor' - ) - ), - ], - [ - OCPP16IncomingRequestCommand.UPDATE_FIRMWARE, - this.ajv.compile( - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/UpdateFirmware.json', - moduleName, - 'constructor' - ) - ), - ], - ]) + this.payloadValidatorFunctions = OCPP16ServiceUtils.createPayloadValidatorMap( + OCPP16ServiceUtils.createIncomingRequestPayloadConfigs(), + OCPP16ServiceUtils.createIncomingRequestFactoryOptions(moduleName, 'constructor'), + this.ajv + ) // Handle incoming request events this.on( OCPP16IncomingRequestCommand.REMOTE_START_TRANSACTION, @@ -1781,7 +1610,7 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { commandName: OCPP16IncomingRequestCommand, commandPayload: JsonType ): boolean { - if (this.payloadValidateFunctions.has(commandName)) { + if (this.payloadValidatorFunctions.has(commandName)) { return this.validateIncomingRequestPayload(chargingStation, commandName, commandPayload) } logger.warn( diff --git a/src/charging-station/ocpp/1.6/OCPP16RequestService.ts b/src/charging-station/ocpp/1.6/OCPP16RequestService.ts index 10505ff0..cc63df8e 100644 --- a/src/charging-station/ocpp/1.6/OCPP16RequestService.ts +++ b/src/charging-station/ocpp/1.6/OCPP16RequestService.ts @@ -10,19 +10,10 @@ import { ErrorType, type JsonObject, type JsonType, - type OCPP16AuthorizeRequest, - type OCPP16BootNotificationRequest, OCPP16ChargePointStatus, - type OCPP16DataTransferRequest, - type OCPP16DiagnosticsStatusNotificationRequest, - type OCPP16FirmwareStatusNotificationRequest, - type OCPP16HeartbeatRequest, type OCPP16MeterValue, - type OCPP16MeterValuesRequest, OCPP16RequestCommand, type OCPP16StartTransactionRequest, - type OCPP16StatusNotificationRequest, - type OCPP16StopTransactionRequest, OCPPVersion, type RequestParams, } from '../../../types/index.js' @@ -34,115 +25,18 @@ import { OCPP16ServiceUtils } from './OCPP16ServiceUtils.js' const moduleName = 'OCPP16RequestService' export class OCPP16RequestService extends OCPPRequestService { - protected payloadValidateFunctions: Map> + protected payloadValidatorFunctions: Map> public constructor (ocppResponseService: OCPPResponseService) { // if (new.target.name === moduleName) { // throw new TypeError(`Cannot construct ${new.target.name} instances directly`) // } super(OCPPVersion.VERSION_16, ocppResponseService) - this.payloadValidateFunctions = new Map>([ - [ - OCPP16RequestCommand.AUTHORIZE, - this.ajv.compile( - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/Authorize.json', - moduleName, - 'constructor' - ) - ), - ], - [ - OCPP16RequestCommand.BOOT_NOTIFICATION, - this.ajv.compile( - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/BootNotification.json', - moduleName, - 'constructor' - ) - ), - ], - [ - OCPP16RequestCommand.DATA_TRANSFER, - this.ajv.compile( - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/DataTransfer.json', - moduleName, - 'constructor' - ) - ), - ], - [ - OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION, - this.ajv.compile( - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/DiagnosticsStatusNotification.json', - moduleName, - 'constructor' - ) - ), - ], - [ - OCPP16RequestCommand.FIRMWARE_STATUS_NOTIFICATION, - this.ajv.compile( - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/FirmwareStatusNotification.json', - moduleName, - 'constructor' - ) - ), - ], - [ - OCPP16RequestCommand.HEARTBEAT, - this.ajv.compile( - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/Heartbeat.json', - moduleName, - 'constructor' - ) - ), - ], - [ - OCPP16RequestCommand.METER_VALUES, - this.ajv.compile( - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/MeterValues.json', - moduleName, - 'constructor' - ) - ), - ], - [ - OCPP16RequestCommand.START_TRANSACTION, - this.ajv.compile( - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/StartTransaction.json', - moduleName, - 'constructor' - ) - ), - ], - [ - OCPP16RequestCommand.STATUS_NOTIFICATION, - this.ajv.compile( - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/StatusNotification.json', - moduleName, - 'constructor' - ) - ), - ], - [ - OCPP16RequestCommand.STOP_TRANSACTION, - this.ajv.compile( - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/StopTransaction.json', - moduleName, - 'constructor' - ) - ), - ], - ]) + this.payloadValidatorFunctions = OCPP16ServiceUtils.createPayloadValidatorMap( + OCPP16ServiceUtils.createRequestPayloadConfigs(), + OCPP16ServiceUtils.createRequestFactoryOptions(moduleName, 'constructor'), + this.ajv + ) this.buildRequestPayload = this.buildRequestPayload.bind(this) } diff --git a/src/charging-station/ocpp/1.6/OCPP16ResponseService.ts b/src/charging-station/ocpp/1.6/OCPP16ResponseService.ts index 3b7a2211..06b7be4a 100644 --- a/src/charging-station/ocpp/1.6/OCPP16ResponseService.ts +++ b/src/charging-station/ocpp/1.6/OCPP16ResponseService.ts @@ -13,45 +13,28 @@ import { } from '../../../charging-station/index.js' import { OCPPError } from '../../../exception/index.js' import { - type ChangeConfigurationResponse, ChargingStationEvents, ErrorType, - type GenericResponse, - type GetConfigurationResponse, - type GetDiagnosticsResponse, type JsonType, OCPP16AuthorizationStatus, type OCPP16AuthorizeRequest, type OCPP16AuthorizeResponse, type OCPP16BootNotificationResponse, - type OCPP16ChangeAvailabilityResponse, OCPP16ChargePointStatus, - type OCPP16ClearChargingProfileResponse, - type OCPP16DataTransferResponse, - type OCPP16DiagnosticsStatusNotificationResponse, - type OCPP16FirmwareStatusNotificationResponse, - type OCPP16GetCompositeScheduleResponse, - type OCPP16HeartbeatResponse, OCPP16IncomingRequestCommand, type OCPP16MeterValue, type OCPP16MeterValuesRequest, type OCPP16MeterValuesResponse, OCPP16RequestCommand, - type OCPP16ReserveNowResponse, OCPP16StandardParametersKey, type OCPP16StartTransactionRequest, type OCPP16StartTransactionResponse, - type OCPP16StatusNotificationResponse, type OCPP16StopTransactionRequest, type OCPP16StopTransactionResponse, - type OCPP16TriggerMessageResponse, - type OCPP16UpdateFirmwareResponse, OCPPVersion, RegistrationStatusEnumType, ReservationTerminationReason, type ResponseHandler, - type SetChargingProfileResponse, - type UnlockConnectorResponse, } from '../../../types/index.js' import { Constants, convertToInt, isAsyncFunction, logger } from '../../../utils/index.js' import { OCPPResponseService } from '../OCPPResponseService.js' @@ -65,7 +48,7 @@ export class OCPP16ResponseService extends OCPPResponseService { ValidateFunction > - protected payloadValidateFunctions: Map> + protected payloadValidatorFunctions: Map> private readonly responseHandlers: Map public constructor () { @@ -100,283 +83,17 @@ export class OCPP16ResponseService extends OCPPResponseService { this.handleResponseStopTransaction.bind(this) as ResponseHandler, ], ]) - this.payloadValidateFunctions = new Map>([ - [ - OCPP16RequestCommand.AUTHORIZE, - this.ajv.compile( - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/AuthorizeResponse.json', - moduleName, - 'constructor' - ) - ), - ], - [ - OCPP16RequestCommand.BOOT_NOTIFICATION, - this.ajv.compile( - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/BootNotificationResponse.json', - moduleName, - 'constructor' - ) - ), - ], - [ - OCPP16RequestCommand.DATA_TRANSFER, - this.ajv.compile( - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/DataTransferResponse.json', - moduleName, - 'constructor' - ) - ), - ], - [ - OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION, - this.ajv.compile( - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/DiagnosticsStatusNotificationResponse.json', - moduleName, - 'constructor' - ) - ), - ], - [ - OCPP16RequestCommand.FIRMWARE_STATUS_NOTIFICATION, - this.ajv.compile( - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/FirmwareStatusNotificationResponse.json', - moduleName, - 'constructor' - ) - ), - ], - [ - OCPP16RequestCommand.HEARTBEAT, - this.ajv.compile( - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/HeartbeatResponse.json', - moduleName, - 'constructor' - ) - ), - ], - [ - OCPP16RequestCommand.METER_VALUES, - this.ajv.compile( - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/MeterValuesResponse.json', - moduleName, - 'constructor' - ) - ), - ], - [ - OCPP16RequestCommand.START_TRANSACTION, - this.ajv.compile( - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/StartTransactionResponse.json', - moduleName, - 'constructor' - ) - ), - ], - [ - OCPP16RequestCommand.STATUS_NOTIFICATION, - this.ajv.compile( - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/StatusNotificationResponse.json', - moduleName, - 'constructor' - ) - ), - ], - [ - OCPP16RequestCommand.STOP_TRANSACTION, - this.ajv.compile( - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/StopTransactionResponse.json', - moduleName, - 'constructor' - ) - ), - ], - ]) - this.incomingRequestResponsePayloadValidateFunctions = new Map< - OCPP16IncomingRequestCommand, - ValidateFunction - >([ - [ - OCPP16IncomingRequestCommand.CANCEL_RESERVATION, - this.ajvIncomingRequest.compile( - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/CancelReservationResponse.json', - moduleName, - 'constructor' - ) - ), - ], - [ - OCPP16IncomingRequestCommand.CHANGE_AVAILABILITY, - this.ajvIncomingRequest.compile( - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/ChangeAvailabilityResponse.json', - moduleName, - 'constructor' - ) - ), - ], - [ - OCPP16IncomingRequestCommand.CHANGE_CONFIGURATION, - this.ajvIncomingRequest.compile( - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/ChangeConfigurationResponse.json', - moduleName, - 'constructor' - ) - ), - ], - [ - OCPP16IncomingRequestCommand.CLEAR_CACHE, - this.ajvIncomingRequest.compile( - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/ClearCacheResponse.json', - moduleName, - 'constructor' - ) - ), - ], - [ - OCPP16IncomingRequestCommand.CLEAR_CHARGING_PROFILE, - this.ajvIncomingRequest.compile( - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/ClearChargingProfileResponse.json', - moduleName, - 'constructor' - ) - ), - ], - [ - OCPP16IncomingRequestCommand.DATA_TRANSFER, - this.ajvIncomingRequest.compile( - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/DataTransferResponse.json', - moduleName, - 'constructor' - ) - ), - ], - [ - OCPP16IncomingRequestCommand.GET_COMPOSITE_SCHEDULE, - this.ajvIncomingRequest.compile( - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/GetCompositeScheduleResponse.json', - moduleName, - 'constructor' - ) - ), - ], - [ - OCPP16IncomingRequestCommand.GET_CONFIGURATION, - this.ajvIncomingRequest.compile( - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/GetConfigurationResponse.json', - moduleName, - 'constructor' - ) - ), - ], - [ - OCPP16IncomingRequestCommand.GET_DIAGNOSTICS, - this.ajvIncomingRequest.compile( - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/GetDiagnosticsResponse.json', - moduleName, - 'constructor' - ) - ), - ], - [ - OCPP16IncomingRequestCommand.REMOTE_START_TRANSACTION, - this.ajvIncomingRequest.compile( - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/RemoteStartTransactionResponse.json', - moduleName, - 'constructor' - ) - ), - ], - [ - OCPP16IncomingRequestCommand.REMOTE_STOP_TRANSACTION, - this.ajvIncomingRequest.compile( - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/RemoteStopTransactionResponse.json', - moduleName, - 'constructor' - ) - ), - ], - [ - OCPP16IncomingRequestCommand.RESERVE_NOW, - this.ajvIncomingRequest.compile( - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/ReserveNowResponse.json', - moduleName, - 'constructor' - ) - ), - ], - [ - OCPP16IncomingRequestCommand.RESET, - this.ajvIncomingRequest.compile( - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/ResetResponse.json', - moduleName, - 'constructor' - ) - ), - ], - [ - OCPP16IncomingRequestCommand.SET_CHARGING_PROFILE, - this.ajvIncomingRequest.compile( - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/SetChargingProfileResponse.json', - moduleName, - 'constructor' - ) - ), - ], - [ - OCPP16IncomingRequestCommand.TRIGGER_MESSAGE, - this.ajvIncomingRequest.compile( - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/TriggerMessageResponse.json', - moduleName, - 'constructor' - ) - ), - ], - [ - OCPP16IncomingRequestCommand.UNLOCK_CONNECTOR, - this.ajvIncomingRequest.compile( - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/UnlockConnectorResponse.json', - moduleName, - 'constructor' - ) - ), - ], - [ - OCPP16IncomingRequestCommand.UPDATE_FIRMWARE, - this.ajvIncomingRequest.compile( - OCPP16ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/1.6/UpdateFirmwareResponse.json', - moduleName, - 'constructor' - ) - ), - ], - ]) + this.payloadValidatorFunctions = OCPP16ServiceUtils.createPayloadValidatorMap( + OCPP16ServiceUtils.createResponsePayloadConfigs(), + OCPP16ServiceUtils.createResponseFactoryOptions(moduleName, 'constructor'), + this.ajv + ) + this.incomingRequestResponsePayloadValidateFunctions = + OCPP16ServiceUtils.createPayloadValidatorMap( + OCPP16ServiceUtils.createIncomingRequestResponsePayloadConfigs(), + OCPP16ServiceUtils.createIncomingRequestResponseFactoryOptions(moduleName, 'constructor'), + this.ajvIncomingRequest + ) this.validatePayload = this.validatePayload.bind(this) } @@ -862,7 +579,7 @@ export class OCPP16ResponseService extends OCPPResponseService { commandName: OCPP16RequestCommand, payload: JsonType ): boolean { - if (this.payloadValidateFunctions.has(commandName)) { + if (this.payloadValidatorFunctions.has(commandName)) { logger.debug( `${chargingStation.logPrefix()} ${moduleName}.validatePayload: Validating '${commandName}' response payload` ) diff --git a/src/charging-station/ocpp/1.6/OCPP16ServiceUtils.ts b/src/charging-station/ocpp/1.6/OCPP16ServiceUtils.ts index 24876bbd..0d3c9937 100644 --- a/src/charging-station/ocpp/1.6/OCPP16ServiceUtils.ts +++ b/src/charging-station/ocpp/1.6/OCPP16ServiceUtils.ts @@ -28,11 +28,11 @@ import { type OCPP16ChargingProfile, type OCPP16ChargingSchedule, type OCPP16ClearChargingProfileRequest, - type OCPP16IncomingRequestCommand, + OCPP16IncomingRequestCommand, type OCPP16MeterValue, OCPP16MeterValueContext, OCPP16MeterValueUnit, - type OCPP16RequestCommand, + OCPP16RequestCommand, OCPP16StandardParametersKey, OCPP16StopTransactionReason, type OCPP16SupportedFeatureProfiles, @@ -64,8 +64,7 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils { meterValue.sampledValue.push( OCPP16ServiceUtils.buildSampledValue( chargingStation.stationInfo?.ocppVersion, - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - sampledValueTemplate!, + sampledValueTemplate, roundTo((meterStart ?? 0) / unitDivider, 4), OCPP16MeterValueContext.TRANSACTION_BEGIN ) @@ -379,6 +378,312 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils { } } + /** + * Factory options for OCPP 1.6 Incoming Request Service + * @param moduleName - Name of the OCPP module + * @param methodName - Name of the method/command + * @returns Factory options object for OCPP 1.6 incoming request validators + */ + public static createIncomingRequestFactoryOptions = (moduleName: string, methodName: string) => + OCPP16ServiceUtils.PayloadValidatorOptions( + OCPPVersion.VERSION_16, + 'assets/json-schemas/ocpp/1.6', + moduleName, + methodName + ) + + /** + * OCPP 1.6 Incoming Request Service validator configurations + * @returns Array of validator configuration tuples + */ + public static createIncomingRequestPayloadConfigs = (): [ + OCPP16IncomingRequestCommand, + { schemaPath: string } + ][] => [ + [ + OCPP16IncomingRequestCommand.CANCEL_RESERVATION, + OCPP16ServiceUtils.PayloadValidatorConfig('CancelReservation.json'), + ], + [ + OCPP16IncomingRequestCommand.CHANGE_AVAILABILITY, + OCPP16ServiceUtils.PayloadValidatorConfig('ChangeAvailability.json'), + ], + [ + OCPP16IncomingRequestCommand.CHANGE_CONFIGURATION, + OCPP16ServiceUtils.PayloadValidatorConfig('ChangeConfiguration.json'), + ], + [ + OCPP16IncomingRequestCommand.CLEAR_CACHE, + OCPP16ServiceUtils.PayloadValidatorConfig('ClearCache.json'), + ], + [ + OCPP16IncomingRequestCommand.CLEAR_CHARGING_PROFILE, + OCPP16ServiceUtils.PayloadValidatorConfig('ClearChargingProfile.json'), + ], + [ + OCPP16IncomingRequestCommand.DATA_TRANSFER, + OCPP16ServiceUtils.PayloadValidatorConfig('DataTransfer.json'), + ], + [ + OCPP16IncomingRequestCommand.GET_COMPOSITE_SCHEDULE, + OCPP16ServiceUtils.PayloadValidatorConfig('GetCompositeSchedule.json'), + ], + [ + OCPP16IncomingRequestCommand.GET_CONFIGURATION, + OCPP16ServiceUtils.PayloadValidatorConfig('GetConfiguration.json'), + ], + [ + OCPP16IncomingRequestCommand.GET_DIAGNOSTICS, + OCPP16ServiceUtils.PayloadValidatorConfig('GetDiagnostics.json'), + ], + [ + OCPP16IncomingRequestCommand.REMOTE_START_TRANSACTION, + OCPP16ServiceUtils.PayloadValidatorConfig('RemoteStartTransaction.json'), + ], + [ + OCPP16IncomingRequestCommand.REMOTE_STOP_TRANSACTION, + OCPP16ServiceUtils.PayloadValidatorConfig('RemoteStopTransaction.json'), + ], + [ + OCPP16IncomingRequestCommand.RESERVE_NOW, + OCPP16ServiceUtils.PayloadValidatorConfig('ReserveNow.json'), + ], + [OCPP16IncomingRequestCommand.RESET, OCPP16ServiceUtils.PayloadValidatorConfig('Reset.json')], + [ + OCPP16IncomingRequestCommand.SET_CHARGING_PROFILE, + OCPP16ServiceUtils.PayloadValidatorConfig('SetChargingProfile.json'), + ], + [ + OCPP16IncomingRequestCommand.TRIGGER_MESSAGE, + OCPP16ServiceUtils.PayloadValidatorConfig('TriggerMessage.json'), + ], + [ + OCPP16IncomingRequestCommand.UNLOCK_CONNECTOR, + OCPP16ServiceUtils.PayloadValidatorConfig('UnlockConnector.json'), + ], + [ + OCPP16IncomingRequestCommand.UPDATE_FIRMWARE, + OCPP16ServiceUtils.PayloadValidatorConfig('UpdateFirmware.json'), + ], + ] + + /** + * Factory options for OCPP 1.6 Incoming Request Response Service + * @param moduleName - Name of the OCPP module + * @param methodName - Name of the method/command + * @returns Factory options object for OCPP 1.6 incoming request response validators + */ + public static createIncomingRequestResponseFactoryOptions = ( + moduleName: string, + methodName: string + ) => + OCPP16ServiceUtils.PayloadValidatorOptions( + OCPPVersion.VERSION_16, + 'assets/json-schemas/ocpp/1.6', + moduleName, + methodName + ) + + /** + * OCPP 1.6 Incoming Request Response Service validator configurations + * @returns Array of validator configuration tuples + */ + public static createIncomingRequestResponsePayloadConfigs = (): [ + OCPP16IncomingRequestCommand, + { schemaPath: string } + ][] => [ + [ + OCPP16IncomingRequestCommand.CANCEL_RESERVATION, + OCPP16ServiceUtils.PayloadValidatorConfig('CancelReservationResponse.json'), + ], + [ + OCPP16IncomingRequestCommand.CHANGE_AVAILABILITY, + OCPP16ServiceUtils.PayloadValidatorConfig('ChangeAvailabilityResponse.json'), + ], + [ + OCPP16IncomingRequestCommand.CHANGE_CONFIGURATION, + OCPP16ServiceUtils.PayloadValidatorConfig('ChangeConfigurationResponse.json'), + ], + [ + OCPP16IncomingRequestCommand.CLEAR_CACHE, + OCPP16ServiceUtils.PayloadValidatorConfig('ClearCacheResponse.json'), + ], + [ + OCPP16IncomingRequestCommand.CLEAR_CHARGING_PROFILE, + OCPP16ServiceUtils.PayloadValidatorConfig('ClearChargingProfileResponse.json'), + ], + [ + OCPP16IncomingRequestCommand.DATA_TRANSFER, + OCPP16ServiceUtils.PayloadValidatorConfig('DataTransferResponse.json'), + ], + [ + OCPP16IncomingRequestCommand.GET_COMPOSITE_SCHEDULE, + OCPP16ServiceUtils.PayloadValidatorConfig('GetCompositeScheduleResponse.json'), + ], + [ + OCPP16IncomingRequestCommand.GET_CONFIGURATION, + OCPP16ServiceUtils.PayloadValidatorConfig('GetConfigurationResponse.json'), + ], + [ + OCPP16IncomingRequestCommand.GET_DIAGNOSTICS, + OCPP16ServiceUtils.PayloadValidatorConfig('GetDiagnosticsResponse.json'), + ], + [ + OCPP16IncomingRequestCommand.REMOTE_START_TRANSACTION, + OCPP16ServiceUtils.PayloadValidatorConfig('RemoteStartTransactionResponse.json'), + ], + [ + OCPP16IncomingRequestCommand.REMOTE_STOP_TRANSACTION, + OCPP16ServiceUtils.PayloadValidatorConfig('RemoteStopTransactionResponse.json'), + ], + [ + OCPP16IncomingRequestCommand.RESERVE_NOW, + OCPP16ServiceUtils.PayloadValidatorConfig('ReserveNowResponse.json'), + ], + [ + OCPP16IncomingRequestCommand.RESET, + OCPP16ServiceUtils.PayloadValidatorConfig('ResetResponse.json'), + ], + [ + OCPP16IncomingRequestCommand.SET_CHARGING_PROFILE, + OCPP16ServiceUtils.PayloadValidatorConfig('SetChargingProfileResponse.json'), + ], + [ + OCPP16IncomingRequestCommand.TRIGGER_MESSAGE, + OCPP16ServiceUtils.PayloadValidatorConfig('TriggerMessageResponse.json'), + ], + [ + OCPP16IncomingRequestCommand.UNLOCK_CONNECTOR, + OCPP16ServiceUtils.PayloadValidatorConfig('UnlockConnectorResponse.json'), + ], + [ + OCPP16IncomingRequestCommand.UPDATE_FIRMWARE, + OCPP16ServiceUtils.PayloadValidatorConfig('UpdateFirmwareResponse.json'), + ], + ] + + /** + * Factory options for OCPP 1.6 Request Service + * @param moduleName - Name of the OCPP module + * @param methodName - Name of the method/command + * @returns Factory options object for OCPP 1.6 validators + */ + public static createRequestFactoryOptions = (moduleName: string, methodName: string) => + OCPP16ServiceUtils.PayloadValidatorOptions( + OCPPVersion.VERSION_16, + 'assets/json-schemas/ocpp/1.6', + moduleName, + methodName + ) + + /** + * OCPP 1.6 Request Service validator configurations + * @returns Array of validator configuration tuples + */ + public static createRequestPayloadConfigs = (): [ + OCPP16RequestCommand, + { schemaPath: string } + ][] => [ + [OCPP16RequestCommand.AUTHORIZE, OCPP16ServiceUtils.PayloadValidatorConfig('Authorize.json')], + [ + OCPP16RequestCommand.BOOT_NOTIFICATION, + OCPP16ServiceUtils.PayloadValidatorConfig('BootNotification.json'), + ], + [ + OCPP16RequestCommand.DATA_TRANSFER, + OCPP16ServiceUtils.PayloadValidatorConfig('DataTransfer.json'), + ], + [ + OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION, + OCPP16ServiceUtils.PayloadValidatorConfig('DiagnosticsStatusNotification.json'), + ], + [ + OCPP16RequestCommand.FIRMWARE_STATUS_NOTIFICATION, + OCPP16ServiceUtils.PayloadValidatorConfig('FirmwareStatusNotification.json'), + ], + [OCPP16RequestCommand.HEARTBEAT, OCPP16ServiceUtils.PayloadValidatorConfig('Heartbeat.json')], + [ + OCPP16RequestCommand.METER_VALUES, + OCPP16ServiceUtils.PayloadValidatorConfig('MeterValues.json'), + ], + [ + OCPP16RequestCommand.START_TRANSACTION, + OCPP16ServiceUtils.PayloadValidatorConfig('StartTransaction.json'), + ], + [ + OCPP16RequestCommand.STATUS_NOTIFICATION, + OCPP16ServiceUtils.PayloadValidatorConfig('StatusNotification.json'), + ], + [ + OCPP16RequestCommand.STOP_TRANSACTION, + OCPP16ServiceUtils.PayloadValidatorConfig('StopTransaction.json'), + ], + ] + + /** + * Factory options for OCPP 1.6 Response Service + * @param moduleName - Name of the OCPP module + * @param methodName - Name of the method/command + * @returns Factory options object for OCPP 1.6 response validators + */ + public static createResponseFactoryOptions = (moduleName: string, methodName: string) => + OCPP16ServiceUtils.PayloadValidatorOptions( + OCPPVersion.VERSION_16, + 'assets/json-schemas/ocpp/1.6', + moduleName, + methodName + ) + + /** + * OCPP 1.6 Response Service validator configurations + * @returns Array of validator configuration tuples + */ + public static createResponsePayloadConfigs = (): [ + OCPP16RequestCommand, + { schemaPath: string } + ][] => [ + [ + OCPP16RequestCommand.AUTHORIZE, + OCPP16ServiceUtils.PayloadValidatorConfig('AuthorizeResponse.json'), + ], + [ + OCPP16RequestCommand.BOOT_NOTIFICATION, + OCPP16ServiceUtils.PayloadValidatorConfig('BootNotificationResponse.json'), + ], + [ + OCPP16RequestCommand.DATA_TRANSFER, + OCPP16ServiceUtils.PayloadValidatorConfig('DataTransferResponse.json'), + ], + [ + OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION, + OCPP16ServiceUtils.PayloadValidatorConfig('DiagnosticsStatusNotificationResponse.json'), + ], + [ + OCPP16RequestCommand.FIRMWARE_STATUS_NOTIFICATION, + OCPP16ServiceUtils.PayloadValidatorConfig('FirmwareStatusNotificationResponse.json'), + ], + [ + OCPP16RequestCommand.HEARTBEAT, + OCPP16ServiceUtils.PayloadValidatorConfig('HeartbeatResponse.json'), + ], + [ + OCPP16RequestCommand.METER_VALUES, + OCPP16ServiceUtils.PayloadValidatorConfig('MeterValuesResponse.json'), + ], + [ + OCPP16RequestCommand.START_TRANSACTION, + OCPP16ServiceUtils.PayloadValidatorConfig('StartTransactionResponse.json'), + ], + [ + OCPP16RequestCommand.STATUS_NOTIFICATION, + OCPP16ServiceUtils.PayloadValidatorConfig('StatusNotificationResponse.json'), + ], + [ + OCPP16RequestCommand.STOP_TRANSACTION, + OCPP16ServiceUtils.PayloadValidatorConfig('StopTransactionResponse.json'), + ], + ] + public static hasReservation = ( chargingStation: ChargingStation, connectorId: number, @@ -418,7 +723,7 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils { moduleName?: string, methodName?: string ): JSONSchemaType { - return OCPPServiceUtils.parseJsonSchemaFile( + return OCPP16ServiceUtils.parseJsonSchemaFile( relativePath, OCPPVersion.VERSION_16, moduleName, diff --git a/src/charging-station/ocpp/2.0/OCPP20IncomingRequestService.ts b/src/charging-station/ocpp/2.0/OCPP20IncomingRequestService.ts index bde666dc..f6f68bfc 100644 --- a/src/charging-station/ocpp/2.0/OCPP20IncomingRequestService.ts +++ b/src/charging-station/ocpp/2.0/OCPP20IncomingRequestService.ts @@ -20,7 +20,6 @@ import { GetVariableStatusEnumType, type IncomingRequestHandler, type JsonType, - type OCPP20ClearCacheRequest, OCPP20ComponentName, OCPP20ConnectorStatusEnumType, OCPP20DeviceInfoVariableName, @@ -70,7 +69,7 @@ import { getVariableMetadata, VARIABLE_REGISTRY } from './OCPP20VariableRegistry const moduleName = 'OCPP20IncomingRequestService' export class OCPP20IncomingRequestService extends OCPPIncomingRequestService { - protected payloadValidateFunctions: Map> + protected payloadValidatorFunctions: Map> private readonly incomingRequestHandlers: Map< OCPP20IncomingRequestCommand, @@ -115,71 +114,11 @@ export class OCPP20IncomingRequestService extends OCPPIncomingRequestService { this.handleRequestSetVariables.bind(this) as unknown as IncomingRequestHandler, ], ]) - this.payloadValidateFunctions = new Map< - OCPP20IncomingRequestCommand, - ValidateFunction - >([ - [ - OCPP20IncomingRequestCommand.CLEAR_CACHE, - this.ajv.compile( - OCPP20ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/2.0/ClearCacheRequest.json', - moduleName, - 'constructor' - ) - ), - ], - [ - OCPP20IncomingRequestCommand.GET_BASE_REPORT, - this.ajv.compile( - OCPP20ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/2.0/GetBaseReportRequest.json', - moduleName, - 'constructor' - ) - ), - ], - [ - OCPP20IncomingRequestCommand.GET_VARIABLES, - this.ajv.compile( - OCPP20ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/2.0/GetVariablesRequest.json', - moduleName, - 'constructor' - ) - ), - ], - [ - OCPP20IncomingRequestCommand.REQUEST_STOP_TRANSACTION, - this.ajv.compile( - OCPP20ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/2.0/RequestStopTransactionRequest.json', - moduleName, - 'constructor' - ) - ), - ], - [ - OCPP20IncomingRequestCommand.RESET, - this.ajv.compile( - OCPP20ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/2.0/ResetRequest.json', - moduleName, - 'constructor' - ) - ), - ], - [ - OCPP20IncomingRequestCommand.SET_VARIABLES, - this.ajv.compile( - OCPP20ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/2.0/SetVariablesRequest.json', - moduleName, - 'constructor' - ) - ), - ], - ]) + this.payloadValidatorFunctions = OCPP20ServiceUtils.createPayloadValidatorMap( + OCPP20ServiceUtils.createIncomingRequestPayloadConfigs(), + OCPP20ServiceUtils.createIncomingRequestFactoryOptions(moduleName, 'constructor'), + this.ajv + ) // Handle incoming request events this.on( OCPP20IncomingRequestCommand.GET_BASE_REPORT, @@ -1555,7 +1494,7 @@ export class OCPP20IncomingRequestService extends OCPPIncomingRequestService { commandName: OCPP20IncomingRequestCommand, commandPayload: JsonType ): boolean { - if (this.payloadValidateFunctions.has(commandName)) { + if (this.payloadValidatorFunctions.has(commandName)) { return this.validateIncomingRequestPayload(chargingStation, commandName, commandPayload) } logger.warn( diff --git a/src/charging-station/ocpp/2.0/OCPP20RequestService.ts b/src/charging-station/ocpp/2.0/OCPP20RequestService.ts index 7d323cd7..b598efb2 100644 --- a/src/charging-station/ocpp/2.0/OCPP20RequestService.ts +++ b/src/charging-station/ocpp/2.0/OCPP20RequestService.ts @@ -10,11 +10,7 @@ import { ErrorType, type JsonObject, type JsonType, - type OCPP20BootNotificationRequest, - type OCPP20HeartbeatRequest, - type OCPP20NotifyReportRequest, OCPP20RequestCommand, - type OCPP20StatusNotificationRequest, OCPPVersion, type RequestParams, } from '../../../types/index.js' @@ -26,55 +22,18 @@ import { OCPP20ServiceUtils } from './OCPP20ServiceUtils.js' const moduleName = 'OCPP20RequestService' export class OCPP20RequestService extends OCPPRequestService { - protected payloadValidateFunctions: Map> + protected payloadValidatorFunctions: Map> public constructor (ocppResponseService: OCPPResponseService) { // if (new.target.name === moduleName) { // throw new TypeError(`Cannot construct ${new.target.name} instances directly`) // } super(OCPPVersion.VERSION_201, ocppResponseService) - this.payloadValidateFunctions = new Map>([ - [ - OCPP20RequestCommand.BOOT_NOTIFICATION, - this.ajv.compile( - OCPP20ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/2.0/BootNotificationRequest.json', - moduleName, - 'constructor' - ) - ), - ], - [ - OCPP20RequestCommand.HEARTBEAT, - this.ajv.compile( - OCPP20ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/2.0/HeartbeatRequest.json', - moduleName, - 'constructor' - ) - ), - ], - [ - OCPP20RequestCommand.NOTIFY_REPORT, - this.ajv.compile( - OCPP20ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/2.0/NotifyReportRequest.json', - moduleName, - 'constructor' - ) - ), - ], - [ - OCPP20RequestCommand.STATUS_NOTIFICATION, - this.ajv.compile( - OCPP20ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/2.0/StatusNotificationRequest.json', - moduleName, - 'constructor' - ) - ), - ], - ]) + this.payloadValidatorFunctions = OCPP20ServiceUtils.createPayloadValidatorMap( + OCPP20ServiceUtils.createRequestPayloadConfigs(), + OCPP20ServiceUtils.createRequestFactoryOptions(moduleName, 'constructor'), + this.ajv + ) this.buildRequestPayload = this.buildRequestPayload.bind(this) } diff --git a/src/charging-station/ocpp/2.0/OCPP20ResponseService.ts b/src/charging-station/ocpp/2.0/OCPP20ResponseService.ts index a0c7c659..e529c4a1 100644 --- a/src/charging-station/ocpp/2.0/OCPP20ResponseService.ts +++ b/src/charging-station/ocpp/2.0/OCPP20ResponseService.ts @@ -9,15 +9,11 @@ import { ErrorType, type JsonType, type OCPP20BootNotificationResponse, - type OCPP20ClearCacheResponse, - type OCPP20GetBaseReportResponse, type OCPP20HeartbeatResponse, OCPP20IncomingRequestCommand, type OCPP20NotifyReportResponse, OCPP20OptionalVariableName, OCPP20RequestCommand, - type OCPP20RequestStartTransactionResponse, - type OCPP20RequestStopTransactionResponse, type OCPP20StatusNotificationResponse, OCPPVersion, RegistrationStatusEnumType, @@ -35,7 +31,7 @@ export class OCPP20ResponseService extends OCPPResponseService { ValidateFunction > - protected payloadValidateFunctions: Map> + protected payloadValidatorFunctions: Map> private readonly responseHandlers: Map public constructor () { @@ -58,93 +54,17 @@ export class OCPP20ResponseService extends OCPPResponseService { this.handleResponseStatusNotification.bind(this) as ResponseHandler, ], ]) - this.payloadValidateFunctions = new Map>([ - [ - OCPP20RequestCommand.BOOT_NOTIFICATION, - this.ajv.compile( - OCPP20ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/2.0/BootNotificationResponse.json', - moduleName, - 'constructor' - ) - ), - ], - [ - OCPP20RequestCommand.HEARTBEAT, - this.ajv.compile( - OCPP20ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/2.0/HeartbeatResponse.json', - moduleName, - 'constructor' - ) - ), - ], - [ - OCPP20RequestCommand.NOTIFY_REPORT, - this.ajv.compile( - OCPP20ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/2.0/NotifyReportResponse.json', - moduleName, - 'constructor' - ) - ), - ], - [ - OCPP20RequestCommand.STATUS_NOTIFICATION, - this.ajv.compile( - OCPP20ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/2.0/StatusNotificationResponse.json', - moduleName, - 'constructor' - ) - ), - ], - ]) - this.incomingRequestResponsePayloadValidateFunctions = new Map< - OCPP20IncomingRequestCommand, - ValidateFunction - >([ - [ - OCPP20IncomingRequestCommand.CLEAR_CACHE, - this.ajvIncomingRequest.compile( - OCPP20ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/2.0/ClearCacheResponse.json', - moduleName, - 'constructor' - ) - ), - ], - [ - OCPP20IncomingRequestCommand.GET_BASE_REPORT, - this.ajvIncomingRequest.compile( - OCPP20ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/2.0/GetBaseReportResponse.json', - moduleName, - 'constructor' - ) - ), - ], - [ - OCPP20IncomingRequestCommand.REQUEST_START_TRANSACTION, - this.ajvIncomingRequest.compile( - OCPP20ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/2.0/RequestStartTransactionResponse.json', - moduleName, - 'constructor' - ) - ), - ], - [ - OCPP20IncomingRequestCommand.REQUEST_STOP_TRANSACTION, - this.ajvIncomingRequest.compile( - OCPP20ServiceUtils.parseJsonSchemaFile( - 'assets/json-schemas/ocpp/2.0/RequestStopTransactionResponse.json', - moduleName, - 'constructor' - ) - ), - ], - ]) + this.payloadValidatorFunctions = OCPP20ServiceUtils.createPayloadValidatorMap( + OCPP20ServiceUtils.createResponsePayloadConfigs(), + OCPP20ServiceUtils.createResponseFactoryOptions(moduleName, 'constructor'), + this.ajv + ) + this.incomingRequestResponsePayloadValidateFunctions = + OCPP20ServiceUtils.createPayloadValidatorMap( + OCPP20ServiceUtils.createIncomingRequestResponsePayloadConfigs(), + OCPP20ServiceUtils.createIncomingRequestResponseFactoryOptions(moduleName, 'constructor'), + this.ajvIncomingRequest + ) this.validatePayload = this.validatePayload.bind(this) } @@ -303,7 +223,7 @@ export class OCPP20ResponseService extends OCPPResponseService { commandName: OCPP20RequestCommand, payload: JsonType ): boolean { - if (this.payloadValidateFunctions.has(commandName)) { + if (this.payloadValidatorFunctions.has(commandName)) { logger.debug( `${chargingStation.logPrefix()} ${moduleName}.validatePayload: Validating '${commandName}' response payload` ) diff --git a/src/charging-station/ocpp/2.0/OCPP20ServiceUtils.ts b/src/charging-station/ocpp/2.0/OCPP20ServiceUtils.ts index b46b45dc..2ea07cdf 100644 --- a/src/charging-station/ocpp/2.0/OCPP20ServiceUtils.ts +++ b/src/charging-station/ocpp/2.0/OCPP20ServiceUtils.ts @@ -8,6 +8,7 @@ import { ConnectorStatusEnum, type GenericResponse, type JsonType, + OCPP20IncomingRequestCommand, OCPP20RequestCommand, OCPP20TransactionEventEnumType, type OCPP20TransactionEventRequest, @@ -20,6 +21,181 @@ import { OCPPServiceUtils, sendAndSetConnectorStatus } from '../OCPPServiceUtils import { OCPP20Constants } from './OCPP20Constants.js' export class OCPP20ServiceUtils extends OCPPServiceUtils { + /** + * Factory options for OCPP 2.0 Incoming Request Service + * @param moduleName - Name of the OCPP module + * @param methodName - Name of the method/command + * @returns Factory options object for OCPP 2.0 incoming request validators + */ + public static createIncomingRequestFactoryOptions = (moduleName: string, methodName: string) => + OCPP20ServiceUtils.PayloadValidatorOptions( + OCPPVersion.VERSION_201, + 'assets/json-schemas/ocpp/2.0', + moduleName, + methodName + ) + + /** + * OCPP 2.0 Incoming Request Service validator configurations + * @returns Array of validator configuration tuples + */ + public static createIncomingRequestPayloadConfigs = (): [ + OCPP20IncomingRequestCommand, + { schemaPath: string } + ][] => [ + [ + OCPP20IncomingRequestCommand.CLEAR_CACHE, + OCPP20ServiceUtils.PayloadValidatorConfig('ClearCacheRequest.json'), + ], + [ + OCPP20IncomingRequestCommand.GET_BASE_REPORT, + OCPP20ServiceUtils.PayloadValidatorConfig('GetBaseReportRequest.json'), + ], + [ + OCPP20IncomingRequestCommand.GET_VARIABLES, + OCPP20ServiceUtils.PayloadValidatorConfig('GetVariablesRequest.json'), + ], + [ + OCPP20IncomingRequestCommand.REQUEST_START_TRANSACTION, + OCPP20ServiceUtils.PayloadValidatorConfig('RequestStartTransactionRequest.json'), + ], + [ + OCPP20IncomingRequestCommand.REQUEST_STOP_TRANSACTION, + OCPP20ServiceUtils.PayloadValidatorConfig('RequestStopTransactionRequest.json'), + ], + [ + OCPP20IncomingRequestCommand.RESET, + OCPP20ServiceUtils.PayloadValidatorConfig('ResetRequest.json'), + ], + [ + OCPP20IncomingRequestCommand.SET_VARIABLES, + OCPP20ServiceUtils.PayloadValidatorConfig('SetVariablesRequest.json'), + ], + ] + + /** + * Factory options for OCPP 2.0 Incoming Request Response Service + * @param moduleName - Name of the OCPP module + * @param methodName - Name of the method/command + * @returns Factory options object for OCPP 2.0 incoming request response validators + */ + public static createIncomingRequestResponseFactoryOptions = ( + moduleName: string, + methodName: string + ) => + OCPP20ServiceUtils.PayloadValidatorOptions( + OCPPVersion.VERSION_201, + 'assets/json-schemas/ocpp/2.0', + moduleName, + methodName + ) + + /** + * Configuration for OCPP 2.0 Incoming Request Response validators + * @returns Array of validator configuration tuples + */ + public static createIncomingRequestResponsePayloadConfigs = (): [ + OCPP20IncomingRequestCommand, + { schemaPath: string } + ][] => [ + [ + OCPP20IncomingRequestCommand.CLEAR_CACHE, + OCPP20ServiceUtils.PayloadValidatorConfig('ClearCacheResponse.json'), + ], + [ + OCPP20IncomingRequestCommand.GET_BASE_REPORT, + OCPP20ServiceUtils.PayloadValidatorConfig('GetBaseReportResponse.json'), + ], + [ + OCPP20IncomingRequestCommand.REQUEST_START_TRANSACTION, + OCPP20ServiceUtils.PayloadValidatorConfig('RequestStartTransactionResponse.json'), + ], + [ + OCPP20IncomingRequestCommand.REQUEST_STOP_TRANSACTION, + OCPP20ServiceUtils.PayloadValidatorConfig('RequestStopTransactionResponse.json'), + ], + ] + + /** + * Factory options for OCPP 2.0 Request Service + * @param moduleName - Name of the OCPP module + * @param methodName - Name of the method/command + * @returns Factory options object for OCPP 2.0 validators + */ + public static createRequestFactoryOptions = (moduleName: string, methodName: string) => + OCPP20ServiceUtils.PayloadValidatorOptions( + OCPPVersion.VERSION_201, + 'assets/json-schemas/ocpp/2.0', + moduleName, + methodName + ) + + /** + * OCPP 2.0 Request Service validator configurations + * @returns Array of validator configuration tuples + */ + public static createRequestPayloadConfigs = (): [ + OCPP20RequestCommand, + { schemaPath: string } + ][] => [ + [ + OCPP20RequestCommand.BOOT_NOTIFICATION, + OCPP20ServiceUtils.PayloadValidatorConfig('BootNotificationRequest.json'), + ], + [ + OCPP20RequestCommand.HEARTBEAT, + OCPP20ServiceUtils.PayloadValidatorConfig('HeartbeatRequest.json'), + ], + [ + OCPP20RequestCommand.NOTIFY_REPORT, + OCPP20ServiceUtils.PayloadValidatorConfig('NotifyReportRequest.json'), + ], + [ + OCPP20RequestCommand.STATUS_NOTIFICATION, + OCPP20ServiceUtils.PayloadValidatorConfig('StatusNotificationRequest.json'), + ], + ] + + /** + * Factory options for OCPP 2.0 Response Service + * @param moduleName - Name of the OCPP module + * @param methodName - Name of the method/command + * @returns Factory options object for OCPP 2.0 response validators + */ + public static createResponseFactoryOptions = (moduleName: string, methodName: string) => + OCPP20ServiceUtils.PayloadValidatorOptions( + OCPPVersion.VERSION_201, + 'assets/json-schemas/ocpp/2.0', + moduleName, + methodName + ) + + /** + * OCPP 2.0 Response Service validator configurations + * @returns Array of validator configuration tuples + */ + public static createResponsePayloadConfigs = (): [ + OCPP20RequestCommand, + { schemaPath: string } + ][] => [ + [ + OCPP20RequestCommand.BOOT_NOTIFICATION, + OCPP20ServiceUtils.PayloadValidatorConfig('BootNotificationResponse.json'), + ], + [ + OCPP20RequestCommand.HEARTBEAT, + OCPP20ServiceUtils.PayloadValidatorConfig('HeartbeatResponse.json'), + ], + [ + OCPP20RequestCommand.NOTIFY_REPORT, + OCPP20ServiceUtils.PayloadValidatorConfig('NotifyReportResponse.json'), + ], + [ + OCPP20RequestCommand.STATUS_NOTIFICATION, + OCPP20ServiceUtils.PayloadValidatorConfig('StatusNotificationResponse.json'), + ], + ] + public static enforceMessageLimits< T extends { attributeType?: unknown; component: unknown; variable: unknown } >( @@ -101,7 +277,7 @@ export class OCPP20ServiceUtils extends OCPPServiceUtils { moduleName?: string, methodName?: string ): JSONSchemaType { - return OCPPServiceUtils.parseJsonSchemaFile( + return OCPP20ServiceUtils.parseJsonSchemaFile( relativePath, OCPPVersion.VERSION_201, moduleName, diff --git a/src/charging-station/ocpp/OCPPIncomingRequestService.ts b/src/charging-station/ocpp/OCPPIncomingRequestService.ts index 77633584..b5880026 100644 --- a/src/charging-station/ocpp/OCPPIncomingRequestService.ts +++ b/src/charging-station/ocpp/OCPPIncomingRequestService.ts @@ -25,7 +25,7 @@ const moduleName = 'OCPPIncomingRequestService' export abstract class OCPPIncomingRequestService extends EventEmitter { private static instance: null | OCPPIncomingRequestService = null protected readonly ajv: Ajv - protected abstract payloadValidateFunctions: Map< + protected abstract payloadValidatorFunctions: Map< IncomingRequestCommand, ValidateFunction > @@ -76,7 +76,7 @@ export abstract class OCPPIncomingRequestService extends EventEmitter { if (chargingStation.stationInfo?.ocppStrictCompliance === false) { return true } - const validate = this.payloadValidateFunctions.get(commandName) + const validate = this.payloadValidatorFunctions.get(commandName) if (validate?.(payload) === true) { return true } diff --git a/src/charging-station/ocpp/OCPPRequestService.ts b/src/charging-station/ocpp/OCPPRequestService.ts index d5b944f1..0d6d56b5 100644 --- a/src/charging-station/ocpp/OCPPRequestService.ts +++ b/src/charging-station/ocpp/OCPPRequestService.ts @@ -51,7 +51,7 @@ const defaultRequestParams: RequestParams = { export abstract class OCPPRequestService { private static instance: null | OCPPRequestService = null protected readonly ajv: Ajv - protected abstract payloadValidateFunctions: Map> + protected abstract payloadValidatorFunctions: Map> private readonly ocppResponseService: OCPPResponseService private readonly version: OCPPVersion @@ -488,13 +488,13 @@ export abstract class OCPPRequestService { if (chargingStation.stationInfo?.ocppStrictCompliance === false) { return true } - if (!this.payloadValidateFunctions.has(commandName as RequestCommand)) { + if (!this.payloadValidatorFunctions.has(commandName as RequestCommand)) { logger.warn( `${chargingStation.logPrefix()} ${moduleName}.validateRequestPayload: No JSON schema found for command '${commandName}' PDU validation` ) return true } - const validate = this.payloadValidateFunctions.get(commandName as RequestCommand) + const validate = this.payloadValidatorFunctions.get(commandName as RequestCommand) payload = clone(payload) convertDateToISOString(payload) if (validate?.(payload) === true) { diff --git a/src/charging-station/ocpp/OCPPResponseService.ts b/src/charging-station/ocpp/OCPPResponseService.ts index 42035849..d94bb33e 100644 --- a/src/charging-station/ocpp/OCPPResponseService.ts +++ b/src/charging-station/ocpp/OCPPResponseService.ts @@ -30,7 +30,7 @@ export abstract class OCPPResponseService { protected readonly ajv: Ajv protected readonly ajvIncomingRequest: Ajv protected emptyResponseHandler = Constants.EMPTY_FUNCTION - protected abstract payloadValidateFunctions: Map> + protected abstract payloadValidatorFunctions: Map> private readonly version: OCPPVersion protected constructor (version: OCPPVersion) { @@ -71,7 +71,7 @@ export abstract class OCPPResponseService { if (chargingStation.stationInfo?.ocppStrictCompliance === false) { return true } - const validate = this.payloadValidateFunctions.get(commandName) + const validate = this.payloadValidatorFunctions.get(commandName) if (validate?.(payload) === true) { return true } diff --git a/src/charging-station/ocpp/OCPPServiceUtils.ts b/src/charging-station/ocpp/OCPPServiceUtils.ts index c82e9a7e..ec16a6b5 100644 --- a/src/charging-station/ocpp/OCPPServiceUtils.ts +++ b/src/charging-station/ocpp/OCPPServiceUtils.ts @@ -1,5 +1,4 @@ -import type { ErrorObject, JSONSchemaType } from 'ajv' - +import _Ajv, { type ErrorObject, type JSONSchemaType, type ValidateFunction } from 'ajv' import { isDate } from 'date-fns' import { randomInt } from 'node:crypto' import { readFileSync } from 'node:fs' @@ -71,6 +70,10 @@ import { OCPP16Constants } from './1.6/OCPP16Constants.js' import { OCPP20Constants } from './2.0/OCPP20Constants.js' import { OCPPConstants } from './OCPPConstants.js' +type Ajv = _Ajv.default +// eslint-disable-next-line @typescript-eslint/no-redeclare +const Ajv = _Ajv.default + interface MultiPhaseMeasurandData { perPhaseTemplates: MeasurandPerPhaseSampledValueTemplates template: SampledValueTemplate @@ -403,8 +406,7 @@ const buildVoltageMeasurandValue = ( const voltageSampledValueTemplateValue = isNotEmptyString(voltageSampledValueTemplate.value) ? Number.parseInt(voltageSampledValueTemplate.value) - : // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - chargingStation.stationInfo.voltageOut! + : chargingStation.getVoltageOut() const fluctuationPercent = voltageSampledValueTemplate.fluctuationPercent ?? Constants.DEFAULT_FLUCTUATION_PERCENT const voltageMeasurandValue = getRandomFloatFluctuatedRounded( @@ -426,7 +428,7 @@ const addMainVoltageToMeterValue = ( if ( chargingStation.getNumberOfPhases() !== 3 || (chargingStation.getNumberOfPhases() === 3 && - chargingStation.stationInfo.mainVoltageMeterValues === true) + chargingStation.stationInfo?.mainVoltageMeterValues === true) ) { meterValue.sampledValue.push( buildSampledValue( @@ -458,8 +460,7 @@ const addPhaseVoltageToMeterValue = ( voltagePhaseLineToNeutralSampledValueTemplate.value ) ? Number.parseInt(voltagePhaseLineToNeutralSampledValueTemplate.value) - : // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - chargingStation.stationInfo.voltageOut! + : chargingStation.getVoltageOut() const fluctuationPhaseToNeutralPercent = voltagePhaseLineToNeutralSampledValueTemplate.fluctuationPercent ?? Constants.DEFAULT_FLUCTUATION_PERCENT @@ -493,9 +494,7 @@ const addLineToLineVoltageToMeterValue = ( : chargingStation.getNumberOfPhases().toString() }` as MeterValuePhase const voltagePhaseLineToLineValueRounded = roundTo( - Math.sqrt(chargingStation.getNumberOfPhases()) * - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - chargingStation.stationInfo.voltageOut!, + Math.sqrt(chargingStation.getNumberOfPhases()) * chargingStation.getVoltageOut(), 2 ) const voltagePhaseLineToLineSampledValueTemplate = getSampledValueTemplate( @@ -933,8 +932,7 @@ const buildCurrentMeasurandValue = ( connectorMaximumAmperage = ACElectricUtils.amperagePerPhaseFromPower( chargingStation.getNumberOfPhases(), connectorMaximumAvailablePower, - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - chargingStation.stationInfo.voltageOut! + chargingStation.getVoltageOut() ) if (chargingStation.getNumberOfPhases() === 3) { const defaultFluctuatedAmperagePerPhase = isNotEmptyString(currentTemplate.value) @@ -1036,8 +1034,7 @@ const buildCurrentMeasurandValue = ( case CurrentType.DC: connectorMaximumAmperage = DCElectricUtils.amperage( connectorMaximumAvailablePower, - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - chargingStation.stationInfo.voltageOut! + chargingStation.getVoltageOut() ) currentValues.allPhases = isNotEmptyString(currentTemplate.value) ? getRandomFloatFluctuatedRounded( @@ -1209,13 +1206,11 @@ export const buildMeterValue = ( ? ACElectricUtils.amperagePerPhaseFromPower( chargingStation.getNumberOfPhases(), connectorMaximumAvailablePower, - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - chargingStation.stationInfo.voltageOut! + chargingStation.getVoltageOut() ) : DCElectricUtils.amperage( connectorMaximumAvailablePower, - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - chargingStation.stationInfo.voltageOut! + chargingStation.getVoltageOut() ) const connectorMinimumAmperage = currentMeasurand.template.minimumValue ?? 0 @@ -1596,20 +1591,6 @@ const getSampledValueTemplate = ( ) } -function buildSampledValue ( - ocppVersion: OCPPVersion.VERSION_16 | undefined, - sampledValueTemplate: SampledValueTemplate, - value: number, - context?: MeterValueContext, - phase?: MeterValuePhase -): OCPP16SampledValue -function buildSampledValue ( - ocppVersion: OCPPVersion.VERSION_20 | OCPPVersion.VERSION_201 | undefined, - sampledValueTemplate: SampledValueTemplate, - value: number, - context?: MeterValueContext, - phase?: MeterValuePhase -): OCPP20SampledValue /** * Builds a sampled value object according to the specified OCPP version * @param ocppVersion - The OCPP version to use for formatting the sampled value @@ -1799,6 +1780,42 @@ export class OCPPServiceUtils { // This is intentional } + /** + * Creates a Map of compiled OCPP payload validators from configurations + * Reduces code duplication across OCPP services + * @param configs - Array of tuples containing command and validator configuration + * @param options - Factory options including OCPP version, schema directory, etc. + * @param options.ocppVersion - The OCPP version for schema validation + * @param options.schemaDir - Directory path containing JSON schemas + * @param options.moduleName - Name of the module for logging + * @param options.methodName - Name of the method for logging + * @param ajvInstance - Configured Ajv instance for validation + * @returns Map of commands to their compiled validation functions + */ + public static createPayloadValidatorMap( + configs: [Command, { schemaPath: string }][], + options: { + methodName: string + moduleName: string + ocppVersion: OCPPVersion + schemaDir: string + }, + ajvInstance: Ajv + ): Map> { + return new Map>( + configs.map(([command, config]) => { + const fullSchemaPath = `${options.schemaDir}/${config.schemaPath}` + const schema = OCPPServiceUtils.parseJsonSchemaFile( + fullSchemaPath, + options.ocppVersion, + options.moduleName, + options.methodName + ) + return [command, ajvInstance.compile(schema)] + }) + ) + } + public static isConnectorIdValid ( chargingStation: ChargingStation, ocppCommand: IncomingRequestCommand, @@ -1873,6 +1890,37 @@ export class OCPPServiceUtils { return false } + /** + * Configuration for a single payload validator + * @param schemaPath - Path to the JSON schema file + * @returns Configuration object for payload validator creation + */ + public static readonly PayloadValidatorConfig = (schemaPath: string) => + ({ + schemaPath, + }) as const + + /** + * Options for payload validator creation + * @param ocppVersion - The OCPP version + * @param schemaDir - Directory containing JSON schemas + * @param moduleName - Name of the OCPP module + * @param methodName - Name of the method/command + * @returns Options object for payload validator creation + */ + public static readonly PayloadValidatorOptions = ( + ocppVersion: OCPPVersion, + schemaDir: string, + moduleName: string, + methodName: string + ) => + ({ + methodName, + moduleName, + ocppVersion, + schemaDir, + }) as const + protected static parseJsonSchemaFile( relativePath: string, ocppVersion: OCPPVersion, -- 2.43.0