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