build(simulator): don't preserve modules
[e-mobility-charging-stations-simulator.git] / src / charging-station / ocpp / 1.6 / OCPP16RequestService.ts
1 // Partial Copyright Jerome Benoit. 2021-2023. All Rights Reserved.
2
3 import type { JSONSchemaType } from 'ajv';
4
5 import { OCPP16Constants } from './OCPP16Constants';
6 import { OCPP16ServiceUtils } from './OCPP16ServiceUtils';
7 import type { ChargingStation } from '../../../charging-station';
8 import { OCPPError } from '../../../exception';
9 import {
10 ErrorType,
11 type JsonObject,
12 type JsonType,
13 type OCPP16AuthorizeRequest,
14 type OCPP16BootNotificationRequest,
15 type OCPP16DataTransferRequest,
16 type OCPP16DiagnosticsStatusNotificationRequest,
17 type OCPP16FirmwareStatusNotificationRequest,
18 type OCPP16HeartbeatRequest,
19 type OCPP16MeterValuesRequest,
20 OCPP16RequestCommand,
21 type OCPP16StartTransactionRequest,
22 type OCPP16StatusNotificationRequest,
23 type OCPP16StopTransactionRequest,
24 OCPPVersion,
25 type RequestParams,
26 } from '../../../types';
27 import { Constants, Utils } from '../../../utils';
28 import { OCPPRequestService } from '../OCPPRequestService';
29 import type { OCPPResponseService } from '../OCPPResponseService';
30
31 const moduleName = 'OCPP16RequestService';
32
33 export class OCPP16RequestService extends OCPPRequestService {
34 protected jsonSchemas: Map<OCPP16RequestCommand, JSONSchemaType<JsonObject>>;
35
36 public constructor(ocppResponseService: OCPPResponseService) {
37 // if (new.target?.name === moduleName) {
38 // throw new TypeError(`Cannot construct ${new.target?.name} instances directly`);
39 // }
40 super(OCPPVersion.VERSION_16, ocppResponseService);
41 this.jsonSchemas = new Map<OCPP16RequestCommand, JSONSchemaType<JsonObject>>([
42 [
43 OCPP16RequestCommand.AUTHORIZE,
44 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16AuthorizeRequest>(
45 'assets/json-schemas/ocpp/1.6/Authorize.json',
46 moduleName,
47 'constructor'
48 ),
49 ],
50 [
51 OCPP16RequestCommand.BOOT_NOTIFICATION,
52 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16BootNotificationRequest>(
53 'assets/json-schemas/ocpp/1.6/BootNotification.json',
54 moduleName,
55 'constructor'
56 ),
57 ],
58 [
59 OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION,
60 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16DiagnosticsStatusNotificationRequest>(
61 'assets/json-schemas/ocpp/1.6/DiagnosticsStatusNotification.json',
62 moduleName,
63 'constructor'
64 ),
65 ],
66 [
67 OCPP16RequestCommand.HEARTBEAT,
68 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16HeartbeatRequest>(
69 'assets/json-schemas/ocpp/1.6/Heartbeat.json',
70 moduleName,
71 'constructor'
72 ),
73 ],
74 [
75 OCPP16RequestCommand.METER_VALUES,
76 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16MeterValuesRequest>(
77 'assets/json-schemas/ocpp/1.6/MeterValues.json',
78 moduleName,
79 'constructor'
80 ),
81 ],
82 [
83 OCPP16RequestCommand.STATUS_NOTIFICATION,
84 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16StatusNotificationRequest>(
85 'assets/json-schemas/ocpp/1.6/StatusNotification.json',
86 moduleName,
87 'constructor'
88 ),
89 ],
90 [
91 OCPP16RequestCommand.START_TRANSACTION,
92 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16StartTransactionRequest>(
93 'assets/json-schemas/ocpp/1.6/StartTransaction.json',
94 moduleName,
95 'constructor'
96 ),
97 ],
98 [
99 OCPP16RequestCommand.STOP_TRANSACTION,
100 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16StopTransactionRequest>(
101 'assets/json-schemas/ocpp/1.6/StopTransaction.json',
102 moduleName,
103 'constructor'
104 ),
105 ],
106 [
107 OCPP16RequestCommand.DATA_TRANSFER,
108 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16DataTransferRequest>(
109 'assets/json-schemas/ocpp/1.6/DataTransfer.json',
110 moduleName,
111 'constructor'
112 ),
113 ],
114 [
115 OCPP16RequestCommand.FIRMWARE_STATUS_NOTIFICATION,
116 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16FirmwareStatusNotificationRequest>(
117 'assets/json-schemas/ocpp/1.6/FirmwareStatusNotification.json',
118 moduleName,
119 'constructor'
120 ),
121 ],
122 ]);
123 this.buildRequestPayload = this.buildRequestPayload.bind(this) as <Request extends JsonType>(
124 chargingStation: ChargingStation,
125 commandName: OCPP16RequestCommand,
126 commandParams?: JsonType
127 ) => Request;
128 }
129
130 public async requestHandler<RequestType extends JsonType, ResponseType extends JsonType>(
131 chargingStation: ChargingStation,
132 commandName: OCPP16RequestCommand,
133 commandParams?: JsonType,
134 params?: RequestParams
135 ): Promise<ResponseType> {
136 // FIXME?: add sanity checks on charging station availability, connector availability, connector status, etc.
137 if (OCPP16ServiceUtils.isRequestCommandSupported(chargingStation, commandName) === true) {
138 return (await this.sendMessage(
139 chargingStation,
140 Utils.generateUUID(),
141 this.buildRequestPayload<RequestType>(chargingStation, commandName, commandParams),
142 commandName,
143 params
144 )) as ResponseType;
145 }
146 // OCPPError usage here is debatable: it's an error in the OCPP stack but not targeted to sendError().
147 throw new OCPPError(
148 ErrorType.NOT_SUPPORTED,
149 `Unsupported OCPP command '${commandName}'`,
150 commandName,
151 commandParams
152 );
153 }
154
155 private buildRequestPayload<Request extends JsonType>(
156 chargingStation: ChargingStation,
157 commandName: OCPP16RequestCommand,
158 commandParams?: JsonType
159 ): Request {
160 let connectorId: number;
161 let energyActiveImportRegister: number;
162 commandParams = commandParams as JsonObject;
163 switch (commandName) {
164 case OCPP16RequestCommand.BOOT_NOTIFICATION:
165 case OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION:
166 case OCPP16RequestCommand.FIRMWARE_STATUS_NOTIFICATION:
167 case OCPP16RequestCommand.METER_VALUES:
168 case OCPP16RequestCommand.STATUS_NOTIFICATION:
169 case OCPP16RequestCommand.DATA_TRANSFER:
170 return commandParams as unknown as Request;
171 case OCPP16RequestCommand.AUTHORIZE:
172 return {
173 idTag: Constants.DEFAULT_IDTAG,
174 ...commandParams,
175 } as unknown as Request;
176 case OCPP16RequestCommand.HEARTBEAT:
177 return OCPP16Constants.OCPP_REQUEST_EMPTY as unknown as Request;
178 case OCPP16RequestCommand.START_TRANSACTION:
179 return {
180 idTag: Constants.DEFAULT_IDTAG,
181 meterStart: chargingStation.getEnergyActiveImportRegisterByConnectorId(
182 commandParams?.connectorId as number,
183 true
184 ),
185 timestamp: new Date(),
186 ...commandParams,
187 } as unknown as Request;
188 case OCPP16RequestCommand.STOP_TRANSACTION:
189 chargingStation.getTransactionDataMeterValues() &&
190 (connectorId = chargingStation.getConnectorIdByTransactionId(
191 commandParams?.transactionId as number
192 ));
193 energyActiveImportRegister = chargingStation.getEnergyActiveImportRegisterByTransactionId(
194 commandParams?.transactionId as number,
195 true
196 );
197 return {
198 idTag: chargingStation.getTransactionIdTag(commandParams?.transactionId as number),
199 meterStop: energyActiveImportRegister,
200 timestamp: new Date(),
201 ...(chargingStation.getTransactionDataMeterValues() && {
202 transactionData: OCPP16ServiceUtils.buildTransactionDataMeterValues(
203 chargingStation.getConnectorStatus(connectorId).transactionBeginMeterValue,
204 OCPP16ServiceUtils.buildTransactionEndMeterValue(
205 chargingStation,
206 connectorId,
207 energyActiveImportRegister
208 )
209 ),
210 }),
211 ...commandParams,
212 } as unknown as Request;
213 default:
214 // OCPPError usage here is debatable: it's an error in the OCPP stack but not targeted to sendError().
215 throw new OCPPError(
216 ErrorType.NOT_SUPPORTED,
217 // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
218 `Unsupported OCPP command '${commandName}'`,
219 commandName,
220 commandParams
221 );
222 }
223 }
224 }