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