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