Add initial classes structure for the OCPP 2.0 stack
[e-mobility-charging-stations-simulator.git] / src / charging-station / ocpp / 2.0 / OCPP20ResponseService.ts
1 // Partial Copyright Jerome Benoit. 2021. 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 { ResponseHandler } from '../../../types/ocpp/Responses';
10 import logger from '../../../utils/Logger';
11 import type ChargingStation from '../../ChargingStation';
12 import OCPPResponseService from '../OCPPResponseService';
13 import { OCPP20ServiceUtils } from './OCPP20ServiceUtils';
14
15 const moduleName = 'OCPP20ResponseService';
16
17 export default class OCPP20ResponseService extends OCPPResponseService {
18 private responseHandlers: Map<OCPP20RequestCommand, ResponseHandler>;
19 private jsonSchemas: Map<OCPP20RequestCommand, JSONSchemaType<JsonObject>>;
20
21 public constructor() {
22 if (new.target?.name === moduleName) {
23 throw new TypeError(`Cannot construct ${new.target?.name} instances directly`);
24 }
25 super();
26 this.responseHandlers = new Map<OCPP20RequestCommand, ResponseHandler>();
27 this.jsonSchemas = new Map<OCPP20RequestCommand, JSONSchemaType<JsonObject>>();
28 this.validatePayload.bind(this);
29 }
30
31 public async responseHandler(
32 chargingStation: ChargingStation,
33 commandName: OCPP20RequestCommand,
34 payload: JsonType,
35 requestPayload: JsonType
36 ): Promise<void> {
37 if (
38 chargingStation.isRegistered() === true /* ||
39 commandName === OCPP20RequestCommand.BOOT_NOTIFICATION */
40 ) {
41 if (
42 this.responseHandlers.has(commandName) === true &&
43 OCPP20ServiceUtils.isRequestCommandSupported(chargingStation, commandName) === true
44 ) {
45 try {
46 this.validatePayload(chargingStation, commandName, payload);
47 await this.responseHandlers.get(commandName)(chargingStation, payload, requestPayload);
48 } catch (error) {
49 logger.error(
50 `${chargingStation.logPrefix()} ${moduleName}.responseHandler: Handle response error:`,
51 error
52 );
53 throw error;
54 }
55 } else {
56 // Throw exception
57 throw new OCPPError(
58 ErrorType.NOT_IMPLEMENTED,
59 `${commandName} is not implemented to handle response PDU ${JSON.stringify(
60 payload,
61 null,
62 2
63 )}`,
64 commandName,
65 payload
66 );
67 }
68 } else {
69 throw new OCPPError(
70 ErrorType.SECURITY_ERROR,
71 `${commandName} cannot be issued to handle response PDU ${JSON.stringify(
72 payload,
73 null,
74 2
75 )} while the charging station is not registered on the central server. `,
76 commandName,
77 payload
78 );
79 }
80 }
81
82 private validatePayload(
83 chargingStation: ChargingStation,
84 commandName: OCPP20RequestCommand,
85 payload: JsonType
86 ): boolean {
87 if (this.jsonSchemas.has(commandName)) {
88 return this.validateResponsePayload(
89 chargingStation,
90 commandName,
91 this.jsonSchemas.get(commandName),
92 payload
93 );
94 }
95 logger.warn(
96 `${chargingStation.logPrefix()} ${moduleName}.validatePayload: No JSON schema found for command ${commandName} PDU validation`
97 );
98 return false;
99 }
100 }