build: properly workaround Ajv TS type definitions bug
[e-mobility-charging-stations-simulator.git] / src / charging-station / ocpp / OCPPRequestService.ts
CommitLineData
f5a1ff8c
JB
1import _Ajv, { type JSONSchemaType, type ValidateFunction } from 'ajv';
2import _ajvFormats from 'ajv-formats';
b52c969d 3
a6ef1ece
JB
4import { OCPPConstants } from './OCPPConstants.js';
5import type { OCPPResponseService } from './OCPPResponseService.js';
6import { OCPPServiceUtils } from './OCPPServiceUtils.js';
7import type { ChargingStation } from '../../charging-station/index.js';
8import { OCPPError } from '../../exception/index.js';
9import { PerformanceStatistics } from '../../performance/index.js';
e7aeea18 10import {
8baae8ee 11 ChargingStationEvents,
27782dbc 12 type ErrorCallback,
268a74bb
JB
13 type ErrorResponse,
14 ErrorType,
27782dbc 15 type IncomingRequestCommand,
268a74bb
JB
16 type JsonType,
17 MessageType,
18 type OCPPVersion,
27782dbc 19 type OutgoingRequest,
e7aeea18 20 RequestCommand,
27782dbc 21 type RequestParams,
268a74bb 22 type Response,
27782dbc 23 type ResponseCallback,
e0b0ee21 24 type ResponseType,
a6ef1ece 25} from '../../types/index.js';
2b94ad12
JB
26import {
27 cloneObject,
28 formatDurationMilliSeconds,
29 handleSendMessageError,
30 isNullOrUndefined,
31 logger,
a6ef1ece 32} from '../../utils/index.js';
f5a1ff8c
JB
33type Ajv = _Ajv.default;
34const Ajv = _Ajv.default;
35const ajvFormats = _ajvFormats.default;
c0560973 36
e3018bc4
JB
37const moduleName = 'OCPPRequestService';
38
b9da1bc2
JB
39const defaultRequestParams: RequestParams = {
40 skipBufferingOnError: false,
41 triggerMessage: false,
42 throwError: false,
43};
44
268a74bb 45export abstract class OCPPRequestService {
08f130a0 46 private static instance: OCPPRequestService | null = null;
d270cc87 47 private readonly version: OCPPVersion;
012ae1a9 48 private readonly ajv: Ajv;
9f2e3130 49 private readonly ocppResponseService: OCPPResponseService;
291b5ec8
JB
50 private readonly jsonValidateFunctions: Map<RequestCommand, ValidateFunction<JsonType>>;
51 protected abstract jsonSchemas: Map<RequestCommand, JSONSchemaType<JsonType>>;
c0560973 52
d270cc87
JB
53 protected constructor(version: OCPPVersion, ocppResponseService: OCPPResponseService) {
54 this.version = version;
45988780 55 this.ajv = new Ajv({
98fc1389 56 keywords: ['javaType'],
45988780
JB
57 multipleOfPrecision: 2,
58 });
9952c548 59 ajvFormats(this.ajv);
291b5ec8 60 this.jsonValidateFunctions = new Map<RequestCommand, ValidateFunction<JsonType>>();
d270cc87 61 this.ocppResponseService = ocppResponseService;
31f59c6d 62 this.requestHandler = this.requestHandler.bind(this) as <
e1d9a0f4 63 // eslint-disable-next-line @typescript-eslint/no-unused-vars
31f59c6d 64 ReqType extends JsonType,
5edd8ba0 65 ResType extends JsonType,
31f59c6d
JB
66 >(
67 chargingStation: ChargingStation,
68 commandName: RequestCommand,
69 commandParams?: JsonType,
5edd8ba0 70 params?: RequestParams,
31f59c6d
JB
71 ) => Promise<ResType>;
72 this.sendMessage = this.sendMessage.bind(this) as (
73 chargingStation: ChargingStation,
74 messageId: string,
75 messagePayload: JsonType,
76 commandName: RequestCommand,
5edd8ba0 77 params?: RequestParams,
31f59c6d
JB
78 ) => Promise<ResponseType>;
79 this.sendResponse = this.sendResponse.bind(this) as (
80 chargingStation: ChargingStation,
81 messageId: string,
82 messagePayload: JsonType,
5edd8ba0 83 commandName: IncomingRequestCommand,
31f59c6d
JB
84 ) => Promise<ResponseType>;
85 this.sendError = this.sendError.bind(this) as (
86 chargingStation: ChargingStation,
87 messageId: string,
88 ocppError: OCPPError,
5edd8ba0 89 commandName: RequestCommand | IncomingRequestCommand,
31f59c6d
JB
90 ) => Promise<ResponseType>;
91 this.internalSendMessage = this.internalSendMessage.bind(this) as (
92 chargingStation: ChargingStation,
93 messageId: string,
94 messagePayload: JsonType | OCPPError,
95 messageType: MessageType,
96 commandName: RequestCommand | IncomingRequestCommand,
5edd8ba0 97 params?: RequestParams,
31f59c6d
JB
98 ) => Promise<ResponseType>;
99 this.buildMessageToSend = this.buildMessageToSend.bind(this) as (
100 chargingStation: ChargingStation,
101 messageId: string,
102 messagePayload: JsonType | OCPPError,
103 messageType: MessageType,
104 commandName: RequestCommand | IncomingRequestCommand,
31f59c6d 105 ) => string;
291b5ec8 106 this.validateRequestPayload = this.validateRequestPayload.bind(this) as <T extends JsonType>(
31f59c6d
JB
107 chargingStation: ChargingStation,
108 commandName: RequestCommand | IncomingRequestCommand,
5edd8ba0 109 payload: T,
31f59c6d
JB
110 ) => boolean;
111 this.validateIncomingRequestResponsePayload = this.validateIncomingRequestResponsePayload.bind(
5edd8ba0 112 this,
291b5ec8 113 ) as <T extends JsonType>(
31f59c6d
JB
114 chargingStation: ChargingStation,
115 commandName: RequestCommand | IncomingRequestCommand,
5edd8ba0 116 payload: T,
31f59c6d 117 ) => boolean;
c0560973
JB
118 }
119
e7aeea18 120 public static getInstance<T extends OCPPRequestService>(
08f130a0 121 this: new (ocppResponseService: OCPPResponseService) => T,
5edd8ba0 122 ocppResponseService: OCPPResponseService,
e7aeea18 123 ): T {
1ca780f9 124 if (OCPPRequestService.instance === null) {
08f130a0 125 OCPPRequestService.instance = new this(ocppResponseService);
9f2e3130 126 }
08f130a0 127 return OCPPRequestService.instance as T;
9f2e3130
JB
128 }
129
c75a6675 130 public async sendResponse(
08f130a0 131 chargingStation: ChargingStation,
e7aeea18 132 messageId: string,
5cc4b63b 133 messagePayload: JsonType,
5edd8ba0 134 commandName: IncomingRequestCommand,
5eaabe90 135 ): Promise<ResponseType> {
5e0c67e8 136 try {
c75a6675 137 // Send response message
e7aeea18 138 return await this.internalSendMessage(
08f130a0 139 chargingStation,
e7aeea18
JB
140 messageId,
141 messagePayload,
142 MessageType.CALL_RESULT_MESSAGE,
5edd8ba0 143 commandName,
e7aeea18 144 );
5e0c67e8 145 } catch (error) {
fa5995d6 146 handleSendMessageError(chargingStation, commandName, error as Error, {
07561812 147 throwError: true,
dc922667 148 });
e1d9a0f4 149 return null;
5e0c67e8
JB
150 }
151 }
152
e7aeea18 153 public async sendError(
08f130a0 154 chargingStation: ChargingStation,
e7aeea18
JB
155 messageId: string,
156 ocppError: OCPPError,
5edd8ba0 157 commandName: RequestCommand | IncomingRequestCommand,
e7aeea18 158 ): Promise<ResponseType> {
5e0c67e8
JB
159 try {
160 // Send error message
e7aeea18 161 return await this.internalSendMessage(
08f130a0 162 chargingStation,
e7aeea18
JB
163 messageId,
164 ocppError,
165 MessageType.CALL_ERROR_MESSAGE,
5edd8ba0 166 commandName,
e7aeea18 167 );
5e0c67e8 168 } catch (error) {
fa5995d6 169 handleSendMessageError(chargingStation, commandName, error as Error);
e1d9a0f4 170 return null;
5e0c67e8
JB
171 }
172 }
173
e7aeea18 174 protected async sendMessage(
08f130a0 175 chargingStation: ChargingStation,
e7aeea18 176 messageId: string,
5cc4b63b 177 messagePayload: JsonType,
e7aeea18 178 commandName: RequestCommand,
7f3decca 179 params?: RequestParams,
e7aeea18 180 ): Promise<ResponseType> {
7b5dbe91 181 params = {
b9da1bc2 182 ...defaultRequestParams,
7b5dbe91
JB
183 ...params,
184 };
5e0c67e8 185 try {
e7aeea18 186 return await this.internalSendMessage(
08f130a0 187 chargingStation,
e7aeea18
JB
188 messageId,
189 messagePayload,
190 MessageType.CALL_MESSAGE,
191 commandName,
5edd8ba0 192 params,
e7aeea18 193 );
5e0c67e8 194 } catch (error) {
fa5995d6 195 handleSendMessageError(chargingStation, commandName, error as Error, {
8ec8e3d0
JB
196 throwError: params.throwError,
197 });
e1d9a0f4 198 return null;
5e0c67e8
JB
199 }
200 }
201
291b5ec8 202 private validateRequestPayload<T extends JsonType>(
b52c969d 203 chargingStation: ChargingStation,
45988780 204 commandName: RequestCommand | IncomingRequestCommand,
5edd8ba0 205 payload: T,
b52c969d 206 ): boolean {
5398cecf 207 if (chargingStation.stationInfo?.ocppStrictCompliance === false) {
b52c969d
JB
208 return true;
209 }
b3fc3ff5
JB
210 if (this.jsonSchemas.has(commandName as RequestCommand) === false) {
211 logger.warn(
5edd8ba0 212 `${chargingStation.logPrefix()} ${moduleName}.validateRequestPayload: No JSON schema found for command '${commandName}' PDU validation`,
b3fc3ff5 213 );
45988780
JB
214 return true;
215 }
0b0ca54f 216 const validate = this.getJsonRequestValidateFunction<T>(commandName as RequestCommand);
9bf0ef23 217 payload = cloneObject<T>(payload);
1799761a 218 OCPPServiceUtils.convertDateToISOString<T>(payload);
b52c969d
JB
219 if (validate(payload)) {
220 return true;
221 }
222 logger.error(
45988780 223 `${chargingStation.logPrefix()} ${moduleName}.validateRequestPayload: Command '${commandName}' request PDU is invalid: %j`,
5edd8ba0 224 validate.errors,
b52c969d 225 );
e909d2a7 226 // OCPPError usage here is debatable: it's an error in the OCPP stack but not targeted to sendError().
b52c969d 227 throw new OCPPError(
9ff486f4 228 OCPPServiceUtils.ajvErrorsToErrorType(validate.errors),
b52c969d
JB
229 'Request PDU is invalid',
230 commandName,
4ed03b6e 231 JSON.stringify(validate.errors, undefined, 2),
b52c969d
JB
232 );
233 }
234
0b0ca54f
JB
235 private getJsonRequestValidateFunction<T extends JsonType>(commandName: RequestCommand) {
236 if (this.jsonValidateFunctions.has(commandName) === false) {
237 this.jsonValidateFunctions.set(
238 commandName,
239 this.ajv.compile<T>(this.jsonSchemas.get(commandName)!).bind(this),
240 );
241 }
242 return this.jsonValidateFunctions.get(commandName)!;
243 }
244
291b5ec8 245 private validateIncomingRequestResponsePayload<T extends JsonType>(
b3fc3ff5
JB
246 chargingStation: ChargingStation,
247 commandName: RequestCommand | IncomingRequestCommand,
5edd8ba0 248 payload: T,
b3fc3ff5 249 ): boolean {
5398cecf 250 if (chargingStation.stationInfo?.ocppStrictCompliance === false) {
b3fc3ff5
JB
251 return true;
252 }
253 if (
254 this.ocppResponseService.jsonIncomingRequestResponseSchemas.has(
5edd8ba0 255 commandName as IncomingRequestCommand,
b3fc3ff5
JB
256 ) === false
257 ) {
258 logger.warn(
5edd8ba0 259 `${chargingStation.logPrefix()} ${moduleName}.validateIncomingRequestResponsePayload: No JSON schema found for command '${commandName}' PDU validation`,
b3fc3ff5
JB
260 );
261 return true;
262 }
0b0ca54f 263 const validate = this.getJsonRequestResponseValidateFunction<T>(
ec0eebcc 264 commandName as IncomingRequestCommand,
0b0ca54f 265 );
9bf0ef23 266 payload = cloneObject<T>(payload);
b3fc3ff5
JB
267 OCPPServiceUtils.convertDateToISOString<T>(payload);
268 if (validate(payload)) {
269 return true;
270 }
271 logger.error(
02887891 272 `${chargingStation.logPrefix()} ${moduleName}.validateIncomingRequestResponsePayload: Command '${commandName}' reponse PDU is invalid: %j`,
5edd8ba0 273 validate.errors,
b3fc3ff5
JB
274 );
275 // OCPPError usage here is debatable: it's an error in the OCPP stack but not targeted to sendError().
276 throw new OCPPError(
9ff486f4 277 OCPPServiceUtils.ajvErrorsToErrorType(validate.errors),
b3fc3ff5
JB
278 'Response PDU is invalid',
279 commandName,
4ed03b6e 280 JSON.stringify(validate.errors, undefined, 2),
b3fc3ff5
JB
281 );
282 }
283
0b0ca54f
JB
284 private getJsonRequestResponseValidateFunction<T extends JsonType>(
285 commandName: IncomingRequestCommand,
286 ) {
287 if (
288 this.ocppResponseService.jsonIncomingRequestResponseValidateFunctions.has(commandName) ===
289 false
290 ) {
291 this.ocppResponseService.jsonIncomingRequestResponseValidateFunctions.set(
292 commandName,
293 this.ajv
294 .compile<T>(this.ocppResponseService.jsonIncomingRequestResponseSchemas.get(commandName)!)
295 .bind(this),
296 );
297 }
298 return this.ocppResponseService.jsonIncomingRequestResponseValidateFunctions.get(commandName)!;
299 }
300
e7aeea18 301 private async internalSendMessage(
08f130a0 302 chargingStation: ChargingStation,
e7aeea18 303 messageId: string,
5cc4b63b 304 messagePayload: JsonType | OCPPError,
e7aeea18 305 messageType: MessageType,
72092cfc 306 commandName: RequestCommand | IncomingRequestCommand,
7f3decca 307 params?: RequestParams,
e7aeea18 308 ): Promise<ResponseType> {
7b5dbe91 309 params = {
b9da1bc2 310 ...defaultRequestParams,
7b5dbe91
JB
311 ...params,
312 };
e7aeea18 313 if (
f7c2994d 314 (chargingStation.inUnknownState() === true &&
3a13fc92 315 commandName === RequestCommand.BOOT_NOTIFICATION) ||
5398cecf 316 (chargingStation.stationInfo?.ocppStrictCompliance === false &&
f7c2994d
JB
317 chargingStation.inUnknownState() === true) ||
318 chargingStation.inAcceptedState() === true ||
319 (chargingStation.inPendingState() === true &&
3a13fc92 320 (params.triggerMessage === true || messageType === MessageType.CALL_RESULT_MESSAGE))
e7aeea18 321 ) {
caad9d6b
JB
322 // eslint-disable-next-line @typescript-eslint/no-this-alias
323 const self = this;
324 // Send a message through wsConnection
5b2721db 325 return new Promise<ResponseType>((resolve, reject) => {
1b2acf4e
JB
326 /**
327 * Function that will receive the request's response
328 *
329 * @param payload -
330 * @param requestPayload -
331 */
332 const responseCallback = (payload: JsonType, requestPayload: JsonType): void => {
333 if (chargingStation.stationInfo?.enableStatistics === true) {
334 chargingStation.performanceStatistics?.addRequestStatistic(
335 commandName,
336 MessageType.CALL_RESULT_MESSAGE,
e8a92d57 337 );
1b2acf4e
JB
338 }
339 // Handle the request's response
340 self.ocppResponseService
341 .responseHandler(
342 chargingStation,
343 commandName as RequestCommand,
344 payload,
345 requestPayload,
346 )
347 .then(() => {
348 resolve(payload);
349 })
b7ee97c1 350 .catch(reject)
1b2acf4e
JB
351 .finally(() => {
352 chargingStation.requests.delete(messageId);
8baae8ee 353 chargingStation.emit(ChargingStationEvents.updated);
1b2acf4e
JB
354 });
355 };
e8a92d57 356
1b2acf4e
JB
357 /**
358 * Function that will receive the request's error response
359 *
9d7b5fa3 360 * @param ocppError -
1b2acf4e
JB
361 * @param requestStatistic -
362 */
9d7b5fa3 363 const errorCallback = (ocppError: OCPPError, requestStatistic = true): void => {
1b2acf4e
JB
364 if (requestStatistic === true && chargingStation.stationInfo?.enableStatistics === true) {
365 chargingStation.performanceStatistics?.addRequestStatistic(
366 commandName,
367 MessageType.CALL_ERROR_MESSAGE,
368 );
764d2c91 369 }
1b2acf4e
JB
370 logger.error(
371 `${chargingStation.logPrefix()} Error occurred at ${OCPPServiceUtils.getMessageTypeString(
372 messageType,
373 )} command ${commandName} with PDU %j:`,
e7aeea18 374 messagePayload,
9d7b5fa3 375 ocppError,
e7aeea18 376 );
1b2acf4e 377 chargingStation.requests.delete(messageId);
8baae8ee 378 chargingStation.emit(ChargingStationEvents.updated);
9d7b5fa3
JB
379 reject(ocppError);
380 };
381
3c80de96
JB
382 const handleSendError = (ocppError: OCPPError): void => {
383 if (params?.skipBufferingOnError === false) {
384 // Buffer
385 chargingStation.bufferMessage(messageToSend);
386 if (messageType === MessageType.CALL_MESSAGE) {
387 this.cacheRequestPromise(
388 chargingStation,
389 messageId,
390 messagePayload as JsonType,
391 commandName,
392 responseCallback,
393 errorCallback,
394 );
395 }
9aa1a33f
JB
396 } else if (
397 params?.skipBufferingOnError === true &&
398 messageType === MessageType.CALL_MESSAGE
399 ) {
2b94ad12
JB
400 // Remove request from the cache
401 chargingStation.requests.delete(messageId);
d42379d8 402 }
2b94ad12 403 return reject(ocppError);
d42379d8
JB
404 };
405
1b2acf4e
JB
406 if (chargingStation.stationInfo?.enableStatistics === true) {
407 chargingStation.performanceStatistics?.addRequestStatistic(commandName, messageType);
408 }
409 const messageToSend = this.buildMessageToSend(
410 chargingStation,
411 messageId,
412 messagePayload,
413 messageType,
414 commandName,
1b2acf4e 415 );
1b2acf4e 416 // Check if wsConnection opened
1a32c36b 417 if (chargingStation.isWebSocketConnectionOpened() === true) {
1b2acf4e 418 const beginId = PerformanceStatistics.beginMeasure(commandName);
1a32c36b 419 const sendTimeout = setTimeout(() => {
2b94ad12 420 return handleSendError(
1a32c36b
JB
421 new OCPPError(
422 ErrorType.GENERIC_ERROR,
2b94ad12
JB
423 `Timeout ${formatDurationMilliSeconds(
424 OCPPConstants.OCPP_WEBSOCKET_TIMEOUT,
425 )} reached for ${
426 params?.skipBufferingOnError === false ? '' : 'non '
42b8cf5c 427 }buffered message id '${messageId}' with content '${messageToSend}'`,
1a32c36b 428 commandName,
7375968c 429 (messagePayload as OCPPError).details,
1a32c36b 430 ),
1a32c36b
JB
431 );
432 }, OCPPConstants.OCPP_WEBSOCKET_TIMEOUT);
433 chargingStation.wsConnection?.send(messageToSend, (error?: Error) => {
d42379d8 434 PerformanceStatistics.endMeasure(commandName, beginId);
82fa1110 435 clearTimeout(sendTimeout);
d42379d8
JB
436 if (isNullOrUndefined(error)) {
437 logger.debug(
438 `${chargingStation.logPrefix()} >> Command '${commandName}' sent ${OCPPServiceUtils.getMessageTypeString(
439 messageType,
440 )} payload: ${messageToSend}`,
441 );
442 if (messageType === MessageType.CALL_MESSAGE) {
443 this.cacheRequestPromise(
444 chargingStation,
445 messageId,
446 messagePayload as JsonType,
447 commandName,
448 responseCallback,
449 errorCallback,
450 );
69dae411
JB
451 } else {
452 // Resolve response
d42379d8
JB
453 return resolve(messagePayload);
454 }
455 } else if (error) {
3c80de96
JB
456 return handleSendError(
457 new OCPPError(
458 ErrorType.GENERIC_ERROR,
459 `WebSocket errored for ${
460 params?.skipBufferingOnError === false ? '' : 'non '
461 }buffered message id '${messageId}' with content '${messageToSend}'`,
462 commandName,
463 { name: error.name, message: error.message, stack: error.stack },
464 ),
1a32c36b 465 );
1a32c36b 466 }
1a32c36b 467 });
82fa1110 468 } else {
3c80de96
JB
469 return handleSendError(
470 new OCPPError(
471 ErrorType.GENERIC_ERROR,
472 `WebSocket closed for ${
473 params?.skipBufferingOnError === false ? '' : 'non '
474 }buffered message id '${messageId}' with content '${messageToSend}'`,
475 commandName,
e3fd8f6d 476 (messagePayload as OCPPError).details,
3c80de96 477 ),
9d7b5fa3 478 );
1b2acf4e 479 }
1b2acf4e 480 });
caad9d6b 481 }
e7aeea18
JB
482 throw new OCPPError(
483 ErrorType.SECURITY_ERROR,
05e2446f 484 `Cannot send command ${commandName} PDU when the charging station is in ${chargingStation?.bootNotificationResponse?.status} state on the central server`,
5edd8ba0 485 commandName,
e7aeea18 486 );
c0560973
JB
487 }
488
e7aeea18 489 private buildMessageToSend(
08f130a0 490 chargingStation: ChargingStation,
e7aeea18 491 messageId: string,
5cc4b63b 492 messagePayload: JsonType | OCPPError,
e7aeea18 493 messageType: MessageType,
72092cfc 494 commandName: RequestCommand | IncomingRequestCommand,
e7aeea18 495 ): string {
e7accadb
JB
496 let messageToSend: string;
497 // Type of message
498 switch (messageType) {
499 // Request
500 case MessageType.CALL_MESSAGE:
501 // Build request
291b5ec8 502 this.validateRequestPayload(chargingStation, commandName, messagePayload as JsonType);
b3ec7bc1
JB
503 messageToSend = JSON.stringify([
504 messageType,
505 messageId,
506 commandName,
507 messagePayload,
508 ] as OutgoingRequest);
e7accadb
JB
509 break;
510 // Response
511 case MessageType.CALL_RESULT_MESSAGE:
512 // Build response
02887891
JB
513 this.validateIncomingRequestResponsePayload(
514 chargingStation,
515 commandName,
291b5ec8 516 messagePayload as JsonType,
02887891 517 );
b3ec7bc1 518 messageToSend = JSON.stringify([messageType, messageId, messagePayload] as Response);
e7accadb
JB
519 break;
520 // Error Message
521 case MessageType.CALL_ERROR_MESSAGE:
522 // Build Error Message
e7aeea18
JB
523 messageToSend = JSON.stringify([
524 messageType,
525 messageId,
7375968c
JB
526 (messagePayload as OCPPError).code,
527 (messagePayload as OCPPError).message,
528 (messagePayload as OCPPError).details ?? {
529 command: (messagePayload as OCPPError).command ?? commandName,
530 },
b3ec7bc1 531 ] as ErrorResponse);
e7accadb
JB
532 break;
533 }
534 return messageToSend;
535 }
536
82fa1110
JB
537 private cacheRequestPromise(
538 chargingStation: ChargingStation,
82fa1110
JB
539 messageId: string,
540 messagePayload: JsonType,
541 commandName: RequestCommand | IncomingRequestCommand,
54a8fbc7
JB
542 responseCallback: ResponseCallback,
543 errorCallback: ErrorCallback,
82fa1110
JB
544 ): void {
545 chargingStation.requests.set(messageId, [
546 responseCallback,
547 errorCallback,
548 commandName,
549 messagePayload,
550 ]);
551 }
552
ef6fa3fb 553 // eslint-disable-next-line @typescript-eslint/no-unused-vars
e0b0ee21 554 public abstract requestHandler<ReqType extends JsonType, ResType extends JsonType>(
08f130a0 555 chargingStation: ChargingStation,
94a464f9 556 commandName: RequestCommand,
e1d9a0f4 557 // FIXME: should be ReqType
5cc4b63b 558 commandParams?: JsonType,
5edd8ba0 559 params?: RequestParams,
e0b0ee21 560 ): Promise<ResType>;
c0560973 561}