-import _Ajv, { type JSONSchemaType, type ValidateFunction } from 'ajv'
+import _Ajv, { type ValidateFunction } from 'ajv'
import _ajvFormats from 'ajv-formats'
-import { OCPPConstants } from './OCPPConstants.js'
-import type { OCPPResponseService } from './OCPPResponseService.js'
-import { OCPPServiceUtils } from './OCPPServiceUtils.js'
import type { ChargingStation } from '../../charging-station/index.js'
import { OCPPError } from '../../exception/index.js'
import { PerformanceStatistics } from '../../performance/index.js'
type ResponseType
} from '../../types/index.js'
import {
- cloneObject,
+ 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
export abstract class OCPPRequestService {
private static instance: OCPPRequestService | null = null
private readonly version: OCPPVersion
- private readonly ajv: Ajv
private readonly ocppResponseService: OCPPResponseService
- private readonly jsonValidateFunctions: Map<RequestCommand, ValidateFunction<JsonType>>
- protected abstract jsonSchemas: Map<RequestCommand, JSONSchemaType<JsonType>>
+ protected readonly ajv: Ajv
+ protected abstract payloadValidateFunctions: Map<RequestCommand, ValidateFunction<JsonType>>
protected constructor (version: OCPPVersion, ocppResponseService: OCPPResponseService) {
this.version = version
multipleOfPrecision: 2
})
ajvFormats(this.ajv)
- this.jsonValidateFunctions = new Map<RequestCommand, ValidateFunction<JsonType>>()
this.ocppResponseService = ocppResponseService
- this.requestHandler = this.requestHandler.bind(this) as <
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- ReqType extends JsonType,
- ResType extends JsonType
- >(
- chargingStation: ChargingStation,
- commandName: RequestCommand,
- commandParams?: JsonType,
- params?: RequestParams
- ) => Promise<ResType>
- this.sendMessage = this.sendMessage.bind(this) as (
- chargingStation: ChargingStation,
- messageId: string,
- messagePayload: JsonType,
- commandName: RequestCommand,
- params?: RequestParams
- ) => Promise<ResponseType>
- this.sendResponse = this.sendResponse.bind(this) as (
- chargingStation: ChargingStation,
- messageId: string,
- messagePayload: JsonType,
- commandName: IncomingRequestCommand
- ) => Promise<ResponseType>
- this.sendError = this.sendError.bind(this) as (
- chargingStation: ChargingStation,
- messageId: string,
- ocppError: OCPPError,
- commandName: RequestCommand | IncomingRequestCommand
- ) => Promise<ResponseType>
- this.internalSendMessage = this.internalSendMessage.bind(this) as (
- chargingStation: ChargingStation,
- messageId: string,
- messagePayload: JsonType | OCPPError,
- messageType: MessageType,
- commandName: RequestCommand | IncomingRequestCommand,
- params?: RequestParams
- ) => Promise<ResponseType>
- this.buildMessageToSend = this.buildMessageToSend.bind(this) as (
- chargingStation: ChargingStation,
- messageId: string,
- messagePayload: JsonType | OCPPError,
- messageType: MessageType,
- commandName: RequestCommand | IncomingRequestCommand
- ) => string
- this.validateRequestPayload = this.validateRequestPayload.bind(this) as <T extends JsonType>(
- chargingStation: ChargingStation,
- commandName: RequestCommand | IncomingRequestCommand,
- payload: T
- ) => boolean
- this.validateIncomingRequestResponsePayload = this.validateIncomingRequestResponsePayload.bind(
- this
- ) as <T extends JsonType>(
- chargingStation: ChargingStation,
- commandName: RequestCommand | IncomingRequestCommand,
- payload: T
- ) => boolean
+ 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>(
if (chargingStation.stationInfo?.ocppStrictCompliance === false) {
return true
}
- if (!this.jsonSchemas.has(commandName as RequestCommand)) {
+ 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.getJsonRequestValidateFunction<T>(commandName as RequestCommand)
- payload = cloneObject<T>(payload)
+ const validate = this.payloadValidateFunctions.get(commandName as RequestCommand)
+ payload = clone<T>(payload)
OCPPServiceUtils.convertDateToISOString<T>(payload)
- if (validate(payload)) {
+ if (validate?.(payload) === true) {
return true
}
logger.error(
`${chargingStation.logPrefix()} ${moduleName}.validateRequestPayload: Command '${commandName}' request PDU is invalid: %j`,
- validate.errors
+ 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),
+ OCPPServiceUtils.ajvErrorsToErrorType(validate?.errors),
'Request PDU is invalid',
commandName,
- JSON.stringify(validate.errors, undefined, 2)
+ JSON.stringify(validate?.errors, undefined, 2)
)
}
- private getJsonRequestValidateFunction<T extends JsonType>(
- commandName: RequestCommand
- ): ValidateFunction<JsonType> {
- if (!this.jsonValidateFunctions.has(commandName)) {
- this.jsonValidateFunctions.set(
- commandName,
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- this.ajv.compile<T>(this.jsonSchemas.get(commandName)!).bind(this)
- )
- }
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- return this.jsonValidateFunctions.get(commandName)!
- }
-
private validateIncomingRequestResponsePayload<T extends JsonType>(
chargingStation: ChargingStation,
commandName: RequestCommand | IncomingRequestCommand,
return true
}
if (
- !this.ocppResponseService.jsonIncomingRequestResponseSchemas.has(
+ !this.ocppResponseService.incomingRequestResponsePayloadValidateFunctions.has(
commandName as IncomingRequestCommand
)
) {
logger.warn(
- `${chargingStation.logPrefix()} ${moduleName}.validateIncomingRequestResponsePayload: No JSON schema found for command '${commandName}' PDU validation`
+ `${chargingStation.logPrefix()} ${moduleName}.validateIncomingRequestResponsePayload: No JSON schema validation function found for command '${commandName}' PDU validation`
)
return true
}
- const validate = this.getJsonRequestResponseValidateFunction<T>(
+ const validate = this.ocppResponseService.incomingRequestResponsePayloadValidateFunctions.get(
commandName as IncomingRequestCommand
)
- payload = cloneObject<T>(payload)
+ payload = clone<T>(payload)
OCPPServiceUtils.convertDateToISOString<T>(payload)
- if (validate(payload)) {
+ if (validate?.(payload) === true) {
return true
}
logger.error(
- `${chargingStation.logPrefix()} ${moduleName}.validateIncomingRequestResponsePayload: Command '${commandName}' reponse PDU is invalid: %j`,
- validate.errors
+ `${chargingStation.logPrefix()} ${moduleName}.validateIncomingRequestResponsePayload: Command '${commandName}' response 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),
+ OCPPServiceUtils.ajvErrorsToErrorType(validate?.errors),
'Response PDU is invalid',
commandName,
- JSON.stringify(validate.errors, undefined, 2)
+ JSON.stringify(validate?.errors, undefined, 2)
)
}
- private getJsonRequestResponseValidateFunction<T extends JsonType>(
- commandName: IncomingRequestCommand
- ): ValidateFunction<JsonType> {
- if (!this.ocppResponseService.jsonIncomingRequestResponseValidateFunctions.has(commandName)) {
- this.ocppResponseService.jsonIncomingRequestResponseValidateFunctions.set(
- commandName,
- this.ajv
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- .compile<T>(this.ocppResponseService.jsonIncomingRequestResponseSchemas.get(commandName)!)
- .bind(this)
- )
- }
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- return this.ocppResponseService.jsonIncomingRequestResponseValidateFunctions.get(commandName)!
- }
-
private async internalSendMessage (
chargingStation: ChargingStation,
messageId: string,
// eslint-disable-next-line @typescript-eslint/no-this-alias
const self = this
// Send a message through wsConnection
- return await new Promise<ResponseType>((resolve, reject) => {
+ return await new Promise<ResponseType>((resolve, reject: (reason?: unknown) => void) => {
/**
* Function that will receive the request's response
*
}
const handleSendError = (ocppError: OCPPError): void => {
- if (params?.skipBufferingOnError === false) {
+ if (params.skipBufferingOnError === false) {
// Buffer
chargingStation.bufferMessage(messageToSend)
if (messageType === MessageType.CALL_MESSAGE) {
- this.cacheRequestPromise(
+ this.setCachedRequest(
chargingStation,
messageId,
messagePayload as JsonType,
)
}
} else if (
- params?.skipBufferingOnError === true &&
+ params.skipBufferingOnError === true &&
messageType === MessageType.CALL_MESSAGE
) {
// Remove request from the cache
`Timeout ${formatDurationMilliSeconds(
OCPPConstants.OCPP_WEBSOCKET_TIMEOUT
)} reached for ${
- params?.skipBufferingOnError === false ? '' : 'non '
+ params.skipBufferingOnError === false ? '' : 'non '
}buffered message id '${messageId}' with content '${messageToSend}'`,
commandName,
(messagePayload as OCPPError).details
)} payload: ${messageToSend}`
)
if (messageType === MessageType.CALL_MESSAGE) {
- this.cacheRequestPromise(
+ this.setCachedRequest(
chargingStation,
messageId,
messagePayload as JsonType,
new OCPPError(
ErrorType.GENERIC_ERROR,
`WebSocket errored for ${
- params?.skipBufferingOnError === false ? '' : 'non '
+ params.skipBufferingOnError === false ? '' : 'non '
}buffered message id '${messageId}' with content '${messageToSend}'`,
commandName,
{ name: error.name, message: error.message, stack: error.stack }
new OCPPError(
ErrorType.GENERIC_ERROR,
`WebSocket closed for ${
- params?.skipBufferingOnError === false ? '' : 'non '
+ params.skipBufferingOnError === false ? '' : 'non '
}buffered message id '${messageId}' with content '${messageToSend}'`,
commandName,
(messagePayload as OCPPError).details
messageToSend = JSON.stringify([
messageType,
messageId,
- commandName,
- messagePayload
- ] as OutgoingRequest)
+ commandName as RequestCommand,
+ messagePayload as JsonType
+ ] satisfies OutgoingRequest)
break
// Response
case MessageType.CALL_RESULT_MESSAGE:
commandName,
messagePayload as JsonType
)
- messageToSend = JSON.stringify([messageType, messageId, messagePayload] as Response)
+ messageToSend = JSON.stringify([
+ messageType,
+ messageId,
+ messagePayload as JsonType
+ ] satisfies Response)
break
// Error Message
case MessageType.CALL_ERROR_MESSAGE:
(messagePayload as OCPPError).code,
(messagePayload as OCPPError).message,
(messagePayload as OCPPError).details ?? {
- command: (messagePayload as OCPPError).command ?? commandName
+ command: (messagePayload as OCPPError).command
}
- ] as ErrorResponse)
+ ] satisfies ErrorResponse)
break
}
return messageToSend
}
- private cacheRequestPromise (
+ private setCachedRequest (
chargingStation: ChargingStation,
messageId: string,
messagePayload: JsonType,