Import cleanups
[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 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 type OCPP20BootNotificationRequest,
13 OCPP20RequestCommand,
14 } from '../../../types/ocpp/2.0/Requests';
15 import { ErrorType } from '../../../types/ocpp/ErrorType';
16 import { OCPPVersion } from '../../../types/ocpp/OCPPVersion';
17 import type { RequestParams } from '../../../types/ocpp/Requests';
18 import logger from '../../../utils/Logger';
19 import Utils from '../../../utils/Utils';
20 import type ChargingStation from '../../ChargingStation';
21 import OCPPRequestService from '../OCPPRequestService';
22 import type OCPPResponseService from '../OCPPResponseService';
23 import { OCPP20ServiceUtils } from './OCPP20ServiceUtils';
24
25 const moduleName = 'OCPP20RequestService';
26
27 export default class OCPP20RequestService extends OCPPRequestService {
28 private jsonSchemas: Map<OCPP20RequestCommand, JSONSchemaType<JsonObject>>;
29
30 public constructor(ocppResponseService: OCPPResponseService) {
31 if (new.target?.name === moduleName) {
32 throw new TypeError(`Cannot construct ${new.target?.name} instances directly`);
33 }
34 super(OCPPVersion.VERSION_20, ocppResponseService);
35 this.jsonSchemas = new Map<OCPP20RequestCommand, JSONSchemaType<JsonObject>>([
36 [
37 OCPP20RequestCommand.BOOT_NOTIFICATION,
38 JSON.parse(
39 fs.readFileSync(
40 path.resolve(
41 path.dirname(fileURLToPath(import.meta.url)),
42 '../../../assets/json-schemas/ocpp/2.0/BootNotificationRequest.json'
43 ),
44 'utf8'
45 )
46 ) as JSONSchemaType<OCPP20BootNotificationRequest>,
47 ],
48 ]);
49 this.buildRequestPayload.bind(this);
50 this.validatePayload.bind(this);
51 }
52
53 public async requestHandler<RequestType extends JsonType, ResponseType extends JsonType>(
54 chargingStation: ChargingStation,
55 commandName: OCPP20RequestCommand,
56 commandParams?: JsonType,
57 params?: RequestParams
58 ): Promise<ResponseType> {
59 if (OCPP20ServiceUtils.isRequestCommandSupported(chargingStation, commandName) === true) {
60 const requestPayload = this.buildRequestPayload<RequestType>(
61 chargingStation,
62 commandName,
63 commandParams
64 );
65 this.validatePayload(chargingStation, commandName, requestPayload);
66 return (await this.sendMessage(
67 chargingStation,
68 Utils.generateUUID(),
69 requestPayload,
70 commandName,
71 params
72 )) as unknown as ResponseType;
73 }
74 // OCPPError usage here is debatable: it's an error in the OCPP stack but not targeted to sendError().
75 throw new OCPPError(
76 ErrorType.NOT_SUPPORTED,
77 `Unsupported OCPP command '${commandName}'`,
78 commandName,
79 commandParams
80 );
81 }
82
83 private buildRequestPayload<Request extends JsonType>(
84 chargingStation: ChargingStation,
85 commandName: OCPP20RequestCommand,
86 commandParams?: JsonType
87 ): Request {
88 commandParams = commandParams as JsonObject;
89 switch (commandName) {
90 case OCPP20RequestCommand.BOOT_NOTIFICATION:
91 commandParams.modem = commandParams.modem as JsonObject;
92 return {
93 reason: commandParams?.reason,
94 chargingStation: {
95 model: commandParams?.model,
96 vendorName: commandParams?.vendorName,
97 ...(!Utils.isUndefined(commandParams?.firmwareVersion) && {
98 firmwareVersion: commandParams.firmwareVersion,
99 }),
100 ...(!Utils.isUndefined(commandParams?.serialNumber) && {
101 serialNumber: commandParams.serialNumber,
102 }),
103 modem: {
104 ...(!Utils.isUndefined(commandParams?.modem?.iccid) && {
105 iccid: commandParams.modem.iccid,
106 }),
107 ...(!Utils.isUndefined(commandParams?.modem?.imsi) && {
108 imsi: commandParams.modem.imsi,
109 }),
110 },
111 },
112 } as unknown as Request;
113 default:
114 // OCPPError usage here is debatable: it's an error in the OCPP stack but not targeted to sendError().
115 throw new OCPPError(
116 ErrorType.NOT_SUPPORTED,
117 // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
118 `Unsupported OCPP command '${commandName}'`,
119 commandName,
120 commandParams
121 );
122 }
123 }
124
125 private validatePayload<Request extends JsonType>(
126 chargingStation: ChargingStation,
127 commandName: OCPP20RequestCommand,
128 requestPayload: Request
129 ): boolean {
130 if (this.jsonSchemas.has(commandName)) {
131 return this.validateRequestPayload(
132 chargingStation,
133 commandName,
134 this.jsonSchemas.get(commandName),
135 requestPayload
136 );
137 }
138 logger.warn(
139 `${chargingStation.logPrefix()} ${moduleName}.validatePayload: No JSON schema found for command ${commandName} PDU validation`
140 );
141 return false;
142 }
143 }