0d657d99f7bd67b365660c3e2888653157485e5c
[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';
6 import type { ChargingStation } from '../../../charging-station';
7 import { OCPPError } from '../../../exception';
8 import {
9 ErrorType,
10 type IncomingRequestHandler,
11 type JsonType,
12 type OCPP20ClearCacheRequest,
13 OCPP20IncomingRequestCommand,
14 OCPPVersion,
15 } from '../../../types';
16 import { logger } from '../../../utils';
17 import { OCPPIncomingRequestService } from '../OCPPIncomingRequestService';
18
19 const moduleName = 'OCPP20IncomingRequestService';
20
21 export class OCPP20IncomingRequestService extends OCPPIncomingRequestService {
22 protected jsonSchemas: Map<OCPP20IncomingRequestCommand, JSONSchemaType<JsonType>>;
23 private incomingRequestHandlers: Map<OCPP20IncomingRequestCommand, IncomingRequestHandler>;
24
25 public constructor() {
26 // if (new.target?.name === moduleName) {
27 // throw new TypeError(`Cannot construct ${new.target?.name} instances directly`);
28 // }
29 super(OCPPVersion.VERSION_20);
30 this.incomingRequestHandlers = new Map<OCPP20IncomingRequestCommand, IncomingRequestHandler>([
31 [OCPP20IncomingRequestCommand.CLEAR_CACHE, this.handleRequestClearCache.bind(this)],
32 ]);
33 this.jsonSchemas = new Map<OCPP20IncomingRequestCommand, JSONSchemaType<JsonType>>([
34 [
35 OCPP20IncomingRequestCommand.CLEAR_CACHE,
36 OCPP20ServiceUtils.parseJsonSchemaFile<OCPP20ClearCacheRequest>(
37 'assets/json-schemas/ocpp/2.0/ClearCacheRequest.json',
38 moduleName,
39 'constructor',
40 ),
41 ],
42 ]);
43 this.validatePayload = this.validatePayload.bind(this) as (
44 chargingStation: ChargingStation,
45 commandName: OCPP20IncomingRequestCommand,
46 commandPayload: JsonType,
47 ) => boolean;
48 }
49
50 public async incomingRequestHandler<ReqType extends JsonType, ResType extends JsonType>(
51 chargingStation: ChargingStation,
52 messageId: string,
53 commandName: OCPP20IncomingRequestCommand,
54 commandPayload: ReqType,
55 ): Promise<void> {
56 let response: ResType;
57 if (
58 chargingStation.stationInfo?.ocppStrictCompliance === true &&
59 chargingStation.inPendingState() === true &&
60 (commandName === OCPP20IncomingRequestCommand.REQUEST_START_TRANSACTION ||
61 commandName === OCPP20IncomingRequestCommand.REQUEST_STOP_TRANSACTION)
62 ) {
63 throw new OCPPError(
64 ErrorType.SECURITY_ERROR,
65 `${commandName} cannot be issued to handle request PDU ${JSON.stringify(
66 commandPayload,
67 undefined,
68 2,
69 )} while the charging station is in pending state on the central server`,
70 commandName,
71 commandPayload,
72 );
73 }
74 if (
75 chargingStation.isRegistered() === true ||
76 (chargingStation.stationInfo?.ocppStrictCompliance === false &&
77 chargingStation.inUnknownState() === true)
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
86 response = (await this.incomingRequestHandlers.get(commandName)!(
87 chargingStation,
88 commandPayload,
89 )) as ResType;
90 } catch (error) {
91 // Log
92 logger.error(
93 `${chargingStation.logPrefix()} ${moduleName}.incomingRequestHandler: Handle incoming request error:`,
94 error,
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,
104 undefined,
105 2,
106 )}`,
107 commandName,
108 commandPayload,
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,
116 undefined,
117 2,
118 )} while the charging station is not registered on the central server.`,
119 commandName,
120 commandPayload,
121 );
122 }
123 // Send the built response
124 await chargingStation.ocppRequestService.sendResponse(
125 chargingStation,
126 messageId,
127 response,
128 commandName,
129 );
130 }
131
132 private validatePayload(
133 chargingStation: ChargingStation,
134 commandName: OCPP20IncomingRequestCommand,
135 commandPayload: JsonType,
136 ): boolean {
137 if (this.jsonSchemas.has(commandName) === true) {
138 return this.validateIncomingRequestPayload(
139 chargingStation,
140 commandName,
141 this.jsonSchemas.get(commandName)!,
142 commandPayload,
143 );
144 }
145 logger.warn(
146 `${chargingStation.logPrefix()} ${moduleName}.validatePayload: No JSON schema found for command '${commandName}' PDU validation`,
147 );
148 return false;
149 }
150 }