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