Hook the OCPP 2.0 stack into the main code
[e-mobility-charging-stations-simulator.git] / src / charging-station / ocpp / 2.0 / OCPP20RequestService.ts
1 // Partial Copyright Jerome Benoit. 2021-2023. 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 { OCPP20RequestCommand } from '../../../types/ocpp/2.0/Requests';
8 import { ErrorType } from '../../../types/ocpp/ErrorType';
9 import type { RequestParams } from '../../../types/ocpp/Requests';
10 import logger from '../../../utils/Logger';
11 import Utils from '../../../utils/Utils';
12 import type ChargingStation from '../../ChargingStation';
13 import OCPPRequestService from '../OCPPRequestService';
14 import type OCPPResponseService from '../OCPPResponseService';
15 import { OCPP20ServiceUtils } from './OCPP20ServiceUtils';
16
17 const moduleName = 'OCPP20RequestService';
18
19 export default class OCPP20RequestService extends OCPPRequestService {
20 private jsonSchemas: Map<OCPP20RequestCommand, JSONSchemaType<JsonObject>>;
21
22 public constructor(ocppResponseService: OCPPResponseService) {
23 if (new.target?.name === moduleName) {
24 throw new TypeError(`Cannot construct ${new.target?.name} instances directly`);
25 }
26 super(ocppResponseService);
27 this.jsonSchemas = new Map<OCPP20RequestCommand, JSONSchemaType<JsonObject>>();
28 this.buildRequestPayload.bind(this);
29 this.validatePayload.bind(this);
30 }
31
32 public async requestHandler<RequestType extends JsonType, ResponseType extends JsonType>(
33 chargingStation: ChargingStation,
34 commandName: OCPP20RequestCommand,
35 commandParams?: JsonType,
36 params?: RequestParams
37 ): Promise<ResponseType> {
38 if (OCPP20ServiceUtils.isRequestCommandSupported(chargingStation, commandName) === true) {
39 const requestPayload = this.buildRequestPayload<RequestType>(
40 chargingStation,
41 commandName,
42 commandParams
43 );
44 this.validatePayload(chargingStation, commandName, requestPayload);
45 return (await this.sendMessage(
46 chargingStation,
47 Utils.generateUUID(),
48 requestPayload,
49 commandName,
50 params
51 )) as unknown as ResponseType;
52 }
53 // OCPPError usage here is debatable: it's an error in the OCPP stack but not targeted to sendError().
54 throw new OCPPError(
55 ErrorType.NOT_SUPPORTED,
56 `Unsupported OCPP command '${commandName}'`,
57 commandName,
58 commandParams
59 );
60 }
61
62 private buildRequestPayload<Request extends JsonType>(
63 chargingStation: ChargingStation,
64 commandName: OCPP20RequestCommand,
65 commandParams?: JsonType
66 ): Request {
67 commandParams = commandParams as JsonObject;
68 switch (commandName) {
69 default:
70 // OCPPError usage here is debatable: it's an error in the OCPP stack but not targeted to sendError().
71 throw new OCPPError(
72 ErrorType.NOT_SUPPORTED,
73 // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
74 `Unsupported OCPP command '${commandName}'`,
75 commandName,
76 commandParams
77 );
78 }
79 }
80
81 private validatePayload<Request extends JsonType>(
82 chargingStation: ChargingStation,
83 commandName: OCPP20RequestCommand,
84 requestPayload: Request
85 ): boolean {
86 if (this.jsonSchemas.has(commandName)) {
87 return this.validateRequestPayload(
88 chargingStation,
89 commandName,
90 this.jsonSchemas.get(commandName),
91 requestPayload
92 );
93 }
94 logger.warn(
95 `${chargingStation.logPrefix()} ${moduleName}.validatePayload: No JSON schema found for command ${commandName} PDU validation`
96 );
97 return false;
98 }
99 }