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