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