Add initial classes structure for the OCPP 2.0 stack
[e-mobility-charging-stations-simulator.git] / src / charging-station / ocpp / 2.0 / OCPP20IncomingRequestService.ts
1 // Partial Copyright Jerome Benoit. 2021. All Rights Reserved.
2
3 import type { JSONSchemaType } from 'ajv';
4
5 import OCPPError from '../../../exception/OCPPError';
6 import type { JsonObject, JsonType } from '../../../types/JsonType';
7 import type { OCPP20IncomingRequestCommand } from '../../../types/ocpp/2.0/Requests';
8 import { ErrorType } from '../../../types/ocpp/ErrorType';
9 import type { IncomingRequestHandler } from '../../../types/ocpp/Requests';
10 import logger from '../../../utils/Logger';
11 import type ChargingStation from '../../ChargingStation';
12 import OCPPIncomingRequestService from '../OCPPIncomingRequestService';
13 import { OCPP20ServiceUtils } from './OCPP20ServiceUtils';
14
15 const moduleName = 'OCPP20IncomingRequestService';
16
17 export default class OCPP20IncomingRequestService extends OCPPIncomingRequestService {
18 private incomingRequestHandlers: Map<OCPP20IncomingRequestCommand, IncomingRequestHandler>;
19 private jsonSchemas: Map<OCPP20IncomingRequestCommand, JSONSchemaType<JsonObject>>;
20
21 public constructor() {
22 if (new.target?.name === moduleName) {
23 throw new TypeError(`Cannot construct ${new.target?.name} instances directly`);
24 }
25 super();
26 this.incomingRequestHandlers = new Map<OCPP20IncomingRequestCommand, IncomingRequestHandler>();
27 this.jsonSchemas = new Map<OCPP20IncomingRequestCommand, JSONSchemaType<JsonObject>>();
28 this.validatePayload.bind(this);
29 }
30
31 public async incomingRequestHandler(
32 chargingStation: ChargingStation,
33 messageId: string,
34 commandName: OCPP20IncomingRequestCommand,
35 commandPayload: JsonType
36 ): Promise<void> {
37 let response: JsonType;
38 if (
39 chargingStation.getOcppStrictCompliance() === true &&
40 chargingStation.isInPendingState() === true /* &&
41 (commandName === OCPP20IncomingRequestCommand.REMOTE_START_TRANSACTION ||
42 commandName === OCPP20IncomingRequestCommand.REMOTE_STOP_TRANSACTION ) */
43 ) {
44 throw new OCPPError(
45 ErrorType.SECURITY_ERROR,
46 `${commandName} cannot be issued to handle request PDU ${JSON.stringify(
47 commandPayload,
48 null,
49 2
50 )} while the charging station is in pending state on the central server`,
51 commandName,
52 commandPayload
53 );
54 }
55 if (
56 chargingStation.isRegistered() === true ||
57 (chargingStation.getOcppStrictCompliance() === false &&
58 chargingStation.isInUnknownState() === true)
59 ) {
60 if (
61 this.incomingRequestHandlers.has(commandName) === true &&
62 OCPP20ServiceUtils.isIncomingRequestCommandSupported(chargingStation, commandName) === true
63 ) {
64 try {
65 this.validatePayload(chargingStation, commandName, commandPayload);
66 // Call the method to build the response
67 response = await this.incomingRequestHandlers.get(commandName)(
68 chargingStation,
69 commandPayload
70 );
71 } catch (error) {
72 // Log
73 logger.error(
74 `${chargingStation.logPrefix()} ${moduleName}.incomingRequestHandler: Handle incoming request error:`,
75 error
76 );
77 throw error;
78 }
79 } else {
80 // Throw exception
81 throw new OCPPError(
82 ErrorType.NOT_IMPLEMENTED,
83 `${commandName} is not implemented to handle request PDU ${JSON.stringify(
84 commandPayload,
85 null,
86 2
87 )}`,
88 commandName,
89 commandPayload
90 );
91 }
92 } else {
93 throw new OCPPError(
94 ErrorType.SECURITY_ERROR,
95 `${commandName} cannot be issued to handle request PDU ${JSON.stringify(
96 commandPayload,
97 null,
98 2
99 )} while the charging station is not registered on the central server.`,
100 commandName,
101 commandPayload
102 );
103 }
104 // Send the built response
105 await chargingStation.ocppRequestService.sendResponse(
106 chargingStation,
107 messageId,
108 response,
109 commandName
110 );
111 }
112
113 private validatePayload(
114 chargingStation: ChargingStation,
115 commandName: OCPP20IncomingRequestCommand,
116 commandPayload: JsonType
117 ): boolean {
118 if (this.jsonSchemas.has(commandName)) {
119 return this.validateIncomingRequestPayload(
120 chargingStation,
121 commandName,
122 this.jsonSchemas.get(commandName),
123 commandPayload
124 );
125 }
126 logger.warn(
127 `${chargingStation.logPrefix()} ${moduleName}.validatePayload: No JSON schema found for command ${commandName} PDU validation`
128 );
129 return false;
130 }
131 }