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