fix: ensure the second stage at handling incoming request is executed
[e-mobility-charging-stations-simulator.git] / src / charging-station / ocpp / 2.0 / OCPP20IncomingRequestService.ts
CommitLineData
a19b897d 1// Partial Copyright Jerome Benoit. 2021-2024. All Rights Reserved.
953d6b02 2
24d15716 3import type { ValidateFunction } from 'ajv'
953d6b02 4
66a7748d
JB
5import { OCPP20ServiceUtils } from './OCPP20ServiceUtils.js'
6import type { ChargingStation } from '../../../charging-station/index.js'
7import { OCPPError } from '../../../exception/index.js'
d270cc87 8import {
268a74bb
JB
9 ErrorType,
10 type IncomingRequestHandler,
268a74bb 11 type JsonType,
81533a20 12 type OCPP20ClearCacheRequest,
d270cc87 13 OCPP20IncomingRequestCommand,
66a7748d
JB
14 OCPPVersion
15} from '../../../types/index.js'
16import { logger } from '../../../utils/index.js'
17import { OCPPIncomingRequestService } from '../OCPPIncomingRequestService.js'
953d6b02 18
66a7748d 19const moduleName = 'OCPP20IncomingRequestService'
953d6b02 20
268a74bb 21export class OCPP20IncomingRequestService extends OCPPIncomingRequestService {
d5490a13 22 protected payloadValidateFunctions: Map<OCPP20IncomingRequestCommand, ValidateFunction<JsonType>>
24d15716 23
66a7748d
JB
24 private readonly incomingRequestHandlers: Map<
25 OCPP20IncomingRequestCommand,
26 IncomingRequestHandler
27 >
953d6b02 28
66a7748d 29 public constructor () {
5199f9fd
JB
30 // if (new.target.name === moduleName) {
31 // throw new TypeError(`Cannot construct ${new.target.name} instances directly`)
b768993d 32 // }
66a7748d 33 super(OCPPVersion.VERSION_20)
d270cc87 34 this.incomingRequestHandlers = new Map<OCPP20IncomingRequestCommand, IncomingRequestHandler>([
66a7748d
JB
35 [OCPP20IncomingRequestCommand.CLEAR_CACHE, this.handleRequestClearCache.bind(this)]
36 ])
d5490a13 37 this.payloadValidateFunctions = new Map<
24d15716
JB
38 OCPP20IncomingRequestCommand,
39 ValidateFunction<JsonType>
40 >([
d270cc87
JB
41 [
42 OCPP20IncomingRequestCommand.CLEAR_CACHE,
24d15716
JB
43 this.ajv
44 .compile(
45 OCPP20ServiceUtils.parseJsonSchemaFile<OCPP20ClearCacheRequest>(
46 'assets/json-schemas/ocpp/2.0/ClearCacheRequest.json',
47 moduleName,
48 'constructor'
49 )
50 )
51 .bind(this)
66a7748d
JB
52 ]
53 ])
ba9a56a6 54 this.validatePayload = this.validatePayload.bind(this)
953d6b02
JB
55 }
56
9429aa42 57 public async incomingRequestHandler<ReqType extends JsonType, ResType extends JsonType>(
953d6b02
JB
58 chargingStation: ChargingStation,
59 messageId: string,
60 commandName: OCPP20IncomingRequestCommand,
66a7748d 61 commandPayload: ReqType
953d6b02 62 ): Promise<void> {
66a7748d 63 let response: ResType
953d6b02 64 if (
5398cecf 65 chargingStation.stationInfo?.ocppStrictCompliance === true &&
66a7748d 66 chargingStation.inPendingState() &&
81533a20
JB
67 (commandName === OCPP20IncomingRequestCommand.REQUEST_START_TRANSACTION ||
68 commandName === OCPP20IncomingRequestCommand.REQUEST_STOP_TRANSACTION)
953d6b02
JB
69 ) {
70 throw new OCPPError(
71 ErrorType.SECURITY_ERROR,
72 `${commandName} cannot be issued to handle request PDU ${JSON.stringify(
73 commandPayload,
4ed03b6e 74 undefined,
66a7748d 75 2
953d6b02
JB
76 )} while the charging station is in pending state on the central server`,
77 commandName,
66a7748d
JB
78 commandPayload
79 )
953d6b02
JB
80 }
81 if (
66a7748d 82 chargingStation.isRegistered() ||
5398cecf 83 (chargingStation.stationInfo?.ocppStrictCompliance === false &&
66a7748d 84 chargingStation.inUnknownState())
953d6b02
JB
85 ) {
86 if (
66a7748d
JB
87 this.incomingRequestHandlers.has(commandName) &&
88 OCPP20ServiceUtils.isIncomingRequestCommandSupported(chargingStation, commandName)
953d6b02
JB
89 ) {
90 try {
66a7748d 91 this.validatePayload(chargingStation, commandName, commandPayload)
953d6b02 92 // Call the method to build the response
66a7748d 93 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
9429aa42 94 response = (await this.incomingRequestHandlers.get(commandName)!(
953d6b02 95 chargingStation,
66a7748d
JB
96 commandPayload
97 )) as ResType
953d6b02
JB
98 } catch (error) {
99 // Log
100 logger.error(
101 `${chargingStation.logPrefix()} ${moduleName}.incomingRequestHandler: Handle incoming request error:`,
66a7748d
JB
102 error
103 )
104 throw error
953d6b02
JB
105 }
106 } else {
107 // Throw exception
108 throw new OCPPError(
109 ErrorType.NOT_IMPLEMENTED,
110 `${commandName} is not implemented to handle request PDU ${JSON.stringify(
111 commandPayload,
4ed03b6e 112 undefined,
66a7748d 113 2
953d6b02
JB
114 )}`,
115 commandName,
66a7748d
JB
116 commandPayload
117 )
953d6b02
JB
118 }
119 } else {
120 throw new OCPPError(
121 ErrorType.SECURITY_ERROR,
122 `${commandName} cannot be issued to handle request PDU ${JSON.stringify(
123 commandPayload,
4ed03b6e 124 undefined,
66a7748d 125 2
953d6b02
JB
126 )} while the charging station is not registered on the central server.`,
127 commandName,
66a7748d
JB
128 commandPayload
129 )
953d6b02
JB
130 }
131 // Send the built response
132 await chargingStation.ocppRequestService.sendResponse(
133 chargingStation,
134 messageId,
135 response,
66a7748d
JB
136 commandName
137 )
1b7eb386 138 this.emit(commandName, chargingStation, commandPayload, response)
953d6b02
JB
139 }
140
66a7748d 141 private validatePayload (
953d6b02
JB
142 chargingStation: ChargingStation,
143 commandName: OCPP20IncomingRequestCommand,
66a7748d 144 commandPayload: JsonType
953d6b02 145 ): boolean {
d5490a13 146 if (this.payloadValidateFunctions.has(commandName)) {
24d15716 147 return this.validateIncomingRequestPayload(chargingStation, commandName, commandPayload)
953d6b02
JB
148 }
149 logger.warn(
24d15716 150 `${chargingStation.logPrefix()} ${moduleName}.validatePayload: No JSON schema validation function found for command '${commandName}' PDU validation`
66a7748d
JB
151 )
152 return false
953d6b02
JB
153 }
154}