Fixes to OCA OCPP 2.0.1 JSON schemas
[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 }
51
52 public async requestHandler<RequestType extends JsonType, ResponseType extends JsonType>(
53 chargingStation: ChargingStation,
54 commandName: OCPP20RequestCommand,
55 commandParams?: JsonType,
56 params?: RequestParams
57 ): Promise<ResponseType> {
58 if (OCPP20ServiceUtils.isRequestCommandSupported(chargingStation, commandName) === true) {
59 const requestPayload = this.buildRequestPayload<RequestType>(
60 chargingStation,
61 commandName,
62 commandParams
63 );
64 return (await this.sendMessage(
65 chargingStation,
66 Utils.generateUUID(),
67 requestPayload,
68 commandName,
69 params
70 )) as unknown as ResponseType;
71 }
72 // OCPPError usage here is debatable: it's an error in the OCPP stack but not targeted to sendError().
73 throw new OCPPError(
74 ErrorType.NOT_SUPPORTED,
75 `Unsupported OCPP command '${commandName}'`,
76 commandName,
77 commandParams
78 );
79 }
80
81 protected getRequestPayloadValidationSchema(
82 chargingStation: ChargingStation,
83 commandName: OCPP20RequestCommand
84 ): JSONSchemaType<JsonObject> | false {
85 if (this.jsonSchemas.has(commandName) === true) {
86 return this.jsonSchemas.get(commandName);
87 }
88 logger.warn(
89 `${chargingStation.logPrefix()} ${moduleName}.getPayloadValidationSchema: No JSON schema found for command ${commandName} PDU validation`
90 );
91 return false;
92 }
93
94 private buildRequestPayload<Request extends JsonType>(
95 chargingStation: ChargingStation,
96 commandName: OCPP20RequestCommand,
97 commandParams?: JsonType
98 ): Request {
99 commandParams = commandParams as JsonObject;
100 switch (commandName) {
101 case OCPP20RequestCommand.BOOT_NOTIFICATION:
102 commandParams.chargingStation = commandParams.chargingStation as JsonObject;
103 commandParams.chargingStation.modem = commandParams.chargingStation.modem as JsonObject;
104 return {
105 reason: commandParams?.reason,
106 chargingStation: {
107 model: commandParams?.chargingStation?.model,
108 vendorName: commandParams?.chargingStation?.vendorName,
109 ...(!Utils.isUndefined(commandParams?.chargingStation?.firmwareVersion) && {
110 firmwareVersion: commandParams.chargingStation?.firmwareVersion,
111 }),
112 ...(!Utils.isUndefined(commandParams?.chargingStation?.serialNumber) && {
113 serialNumber: commandParams.chargingStation?.serialNumber,
114 }),
115 ...(!Utils.isUndefined(commandParams?.chargingStation?.modem) && {
116 modem: {
117 ...(!Utils.isUndefined(commandParams?.chargingStation?.modem?.iccid) && {
118 iccid: commandParams.chargingStation.modem.iccid,
119 }),
120 ...(!Utils.isUndefined(commandParams?.chargingStation?.modem?.imsi) && {
121 imsi: commandParams.chargingStation.modem.imsi,
122 }),
123 },
124 }),
125 },
126 } as unknown as Request;
127 default:
128 // OCPPError usage here is debatable: it's an error in the OCPP stack but not targeted to sendError().
129 throw new OCPPError(
130 ErrorType.NOT_SUPPORTED,
131 // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
132 `Unsupported OCPP command '${commandName}'`,
133 commandName,
134 commandParams
135 );
136 }
137 }
138 }