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