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