-import { AuthorizeResponse, StartTransactionResponse, StopTransactionReason, StopTransactionResponse } from '../../types/ocpp/Transaction';
-import { DiagnosticsStatus, IncomingRequestCommand, RequestCommand } from '../../types/ocpp/Requests';
-
-import { BootNotificationResponse } from '../../types/ocpp/Responses';
-import { ChargePointErrorCode } from '../../types/ocpp/ChargePointErrorCode';
-import { ChargePointStatus } from '../../types/ocpp/ChargePointStatus';
-import ChargingStation from '../ChargingStation';
-import Constants from '../../utils/Constants';
-import { ErrorType } from '../../types/ocpp/ErrorType';
-import { MessageType } from '../../types/ocpp/MessageType';
-import { MeterValue } from '../../types/ocpp/MeterValues';
-import OCPPError from './OCPPError';
-import OCPPResponseService from './OCPPResponseService';
-import PerformanceStatistics from '../../performance/PerformanceStatistics';
-import logger from '../../utils/Logger';
-
-export default abstract class OCPPRequestService {
- public chargingStation: ChargingStation;
- protected ocppResponseService: OCPPResponseService;
-
- constructor(chargingStation: ChargingStation, ocppResponseService: OCPPResponseService) {
- this.chargingStation = chargingStation;
- this.ocppResponseService = ocppResponseService;
+import _Ajv, { type ValidateFunction } from 'ajv'
+import _ajvFormats from 'ajv-formats'
+
+import type { ChargingStation } from '../../charging-station/index.js'
+import { OCPPError } from '../../exception/index.js'
+import { PerformanceStatistics } from '../../performance/index.js'
+import {
+ ChargingStationEvents,
+ type ErrorCallback,
+ type ErrorResponse,
+ ErrorType,
+ type IncomingRequestCommand,
+ type JsonType,
+ MessageType,
+ type OCPPVersion,
+ type OutgoingRequest,
+ RequestCommand,
+ type RequestParams,
+ type Response,
+ type ResponseCallback,
+ type ResponseType
+} from '../../types/index.js'
+import {
+ clone,
+ formatDurationMilliSeconds,
+ handleSendMessageError,
+ logger
+} from '../../utils/index.js'
+import { OCPPConstants } from './OCPPConstants.js'
+import type { OCPPResponseService } from './OCPPResponseService.js'
+import { OCPPServiceUtils } from './OCPPServiceUtils.js'
+type Ajv = _Ajv.default
+// eslint-disable-next-line @typescript-eslint/no-redeclare
+const Ajv = _Ajv.default
+const ajvFormats = _ajvFormats.default
+
+const moduleName = 'OCPPRequestService'
+
+const defaultRequestParams: RequestParams = {
+ skipBufferingOnError: false,
+ triggerMessage: false,
+ throwError: false
+}
+
+export abstract class OCPPRequestService {
+ private static instance: OCPPRequestService | null = null
+ private readonly version: OCPPVersion
+ private readonly ocppResponseService: OCPPResponseService
+ protected readonly ajv: Ajv
+ protected abstract payloadValidateFunctions: Map<RequestCommand, ValidateFunction<JsonType>>
+
+ protected constructor (version: OCPPVersion, ocppResponseService: OCPPResponseService) {
+ this.version = version
+ this.ajv = new Ajv({
+ keywords: ['javaType'],
+ multipleOfPrecision: 2
+ })
+ ajvFormats(this.ajv)
+ this.ocppResponseService = ocppResponseService
+ this.requestHandler = this.requestHandler.bind(this)
+ this.sendMessage = this.sendMessage.bind(this)
+ this.sendResponse = this.sendResponse.bind(this)
+ this.sendError = this.sendError.bind(this)
+ this.internalSendMessage = this.internalSendMessage.bind(this)
+ this.buildMessageToSend = this.buildMessageToSend.bind(this)
+ this.validateRequestPayload = this.validateRequestPayload.bind(this)
+ this.validateIncomingRequestResponsePayload =
+ this.validateIncomingRequestResponsePayload.bind(this)
+ }
+
+ public static getInstance<T extends OCPPRequestService>(
+ this: new (ocppResponseService: OCPPResponseService) => T,
+ ocppResponseService: OCPPResponseService
+ ): T {
+ if (OCPPRequestService.instance === null) {
+ OCPPRequestService.instance = new this(ocppResponseService)
+ }
+ return OCPPRequestService.instance as T
+ }
+
+ public async sendResponse (
+ chargingStation: ChargingStation,
+ messageId: string,
+ messagePayload: JsonType,
+ commandName: IncomingRequestCommand
+ ): Promise<ResponseType> {
+ try {
+ // Send response message
+ return await this.internalSendMessage(
+ chargingStation,
+ messageId,
+ messagePayload,
+ MessageType.CALL_RESULT_MESSAGE,
+ commandName
+ )
+ } catch (error) {
+ handleSendMessageError(chargingStation, commandName, error as Error, {
+ throwError: true
+ })
+ return null
+ }
+ }
+
+ public async sendError (
+ chargingStation: ChargingStation,
+ messageId: string,
+ ocppError: OCPPError,
+ commandName: RequestCommand | IncomingRequestCommand
+ ): Promise<ResponseType> {
+ try {
+ // Send error message
+ return await this.internalSendMessage(
+ chargingStation,
+ messageId,
+ ocppError,
+ MessageType.CALL_ERROR_MESSAGE,
+ commandName
+ )
+ } catch (error) {
+ handleSendMessageError(chargingStation, commandName, error as Error)
+ return null
+ }
+ }
+
+ protected async sendMessage (
+ chargingStation: ChargingStation,
+ messageId: string,
+ messagePayload: JsonType,
+ commandName: RequestCommand,
+ params?: RequestParams
+ ): Promise<ResponseType> {
+ params = {
+ ...defaultRequestParams,
+ ...params
+ }
+ try {
+ return await this.internalSendMessage(
+ chargingStation,
+ messageId,
+ messagePayload,
+ MessageType.CALL_MESSAGE,
+ commandName,
+ params
+ )
+ } catch (error) {
+ handleSendMessageError(chargingStation, commandName, error as Error, {
+ throwError: params.throwError
+ })
+ return null
+ }
+ }
+
+ private validateRequestPayload<T extends JsonType>(
+ chargingStation: ChargingStation,
+ commandName: RequestCommand | IncomingRequestCommand,
+ payload: T
+ ): boolean {
+ if (chargingStation.stationInfo?.ocppStrictCompliance === false) {
+ return true
+ }
+ if (!this.payloadValidateFunctions.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)
+ payload = clone<T>(payload)
+ OCPPServiceUtils.convertDateToISOString<T>(payload)
+ if (validate?.(payload) === true) {
+ return true
+ }
+ logger.error(
+ `${chargingStation.logPrefix()} ${moduleName}.validateRequestPayload: Command '${commandName}' request PDU is invalid: %j`,
+ validate?.errors
+ )
+ // OCPPError usage here is debatable: it's an error in the OCPP stack but not targeted to sendError().
+ throw new OCPPError(
+ OCPPServiceUtils.ajvErrorsToErrorType(validate?.errors),
+ 'Request PDU is invalid',
+ commandName,
+ JSON.stringify(validate?.errors, undefined, 2)
+ )