Initial code cleanups for the future OCPP requests code optimization
[e-mobility-charging-stations-simulator.git] / src / charging-station / ocpp / 1.6 / OCPP16RequestService.ts
1 // Partial Copyright Jerome Benoit. 2021. All Rights Reserved.
2
3 import {
4 AuthorizeRequest,
5 OCPP16AuthorizeResponse,
6 OCPP16StartTransactionResponse,
7 OCPP16StopTransactionReason,
8 OCPP16StopTransactionResponse,
9 StartTransactionRequest,
10 StopTransactionRequest,
11 } from '../../../types/ocpp/1.6/Transaction';
12 import {
13 DiagnosticsStatusNotificationRequest,
14 HeartbeatRequest,
15 OCPP16BootNotificationRequest,
16 OCPP16RequestCommand,
17 StatusNotificationRequest,
18 } from '../../../types/ocpp/1.6/Requests';
19 import { MeterValuesRequest, OCPP16MeterValue } from '../../../types/ocpp/1.6/MeterValues';
20
21 import type ChargingStation from '../../ChargingStation';
22 import Constants from '../../../utils/Constants';
23 import { ErrorType } from '../../../types/ocpp/ErrorType';
24 import { JsonType } from '../../../types/JsonType';
25 import { OCPP16BootNotificationResponse } from '../../../types/ocpp/1.6/Responses';
26 import { OCPP16ChargePointErrorCode } from '../../../types/ocpp/1.6/ChargePointErrorCode';
27 import { OCPP16ChargePointStatus } from '../../../types/ocpp/1.6/ChargePointStatus';
28 import { OCPP16DiagnosticsStatus } from '../../../types/ocpp/1.6/DiagnosticsStatus';
29 import { OCPP16ServiceUtils } from './OCPP16ServiceUtils';
30 import OCPPError from '../../../exception/OCPPError';
31 import OCPPRequestService from '../OCPPRequestService';
32 import type OCPPResponseService from '../OCPPResponseService';
33 import { SendParams } from '../../../types/ocpp/Requests';
34 import Utils from '../../../utils/Utils';
35
36 const moduleName = 'OCPP16RequestService';
37
38 export default class OCPP16RequestService extends OCPPRequestService {
39 public constructor(chargingStation: ChargingStation, ocppResponseService: OCPPResponseService) {
40 if (new.target?.name === moduleName) {
41 throw new TypeError(`Cannot construct ${new.target?.name} instances directly`);
42 }
43 super(chargingStation, ocppResponseService);
44 }
45
46 public async sendHeartbeat(params?: SendParams): Promise<void> {
47 const payload: HeartbeatRequest = {};
48 await this.sendMessage(Utils.generateUUID(), payload, OCPP16RequestCommand.HEARTBEAT, params);
49 }
50
51 public async sendBootNotification(
52 chargePointModel: string,
53 chargePointVendor: string,
54 chargeBoxSerialNumber?: string,
55 firmwareVersion?: string,
56 chargePointSerialNumber?: string,
57 iccid?: string,
58 imsi?: string,
59 meterSerialNumber?: string,
60 meterType?: string,
61 params?: SendParams
62 ): Promise<OCPP16BootNotificationResponse> {
63 const payload: OCPP16BootNotificationRequest = {
64 chargePointModel,
65 chargePointVendor,
66 ...(!Utils.isUndefined(chargeBoxSerialNumber) && { chargeBoxSerialNumber }),
67 ...(!Utils.isUndefined(chargePointSerialNumber) && { chargePointSerialNumber }),
68 ...(!Utils.isUndefined(firmwareVersion) && { firmwareVersion }),
69 ...(!Utils.isUndefined(iccid) && { iccid }),
70 ...(!Utils.isUndefined(imsi) && { imsi }),
71 ...(!Utils.isUndefined(meterSerialNumber) && { meterSerialNumber }),
72 ...(!Utils.isUndefined(meterType) && { meterType }),
73 };
74 return (await this.sendMessage(
75 Utils.generateUUID(),
76 payload,
77 OCPP16RequestCommand.BOOT_NOTIFICATION,
78 { ...params, skipBufferingOnError: true }
79 )) as OCPP16BootNotificationResponse;
80 }
81
82 public async sendStatusNotification(
83 connectorId: number,
84 status: OCPP16ChargePointStatus,
85 errorCode: OCPP16ChargePointErrorCode = OCPP16ChargePointErrorCode.NO_ERROR
86 ): Promise<void> {
87 const payload: StatusNotificationRequest = {
88 connectorId,
89 errorCode,
90 status,
91 };
92 await this.sendMessage(Utils.generateUUID(), payload, OCPP16RequestCommand.STATUS_NOTIFICATION);
93 }
94
95 public async sendAuthorize(
96 connectorId: number,
97 idTag?: string
98 ): Promise<OCPP16AuthorizeResponse> {
99 const payload: AuthorizeRequest = {
100 ...(!Utils.isUndefined(idTag) ? { idTag } : { idTag: Constants.DEFAULT_IDTAG }),
101 };
102 this.chargingStation.getConnectorStatus(connectorId).authorizeIdTag = idTag;
103 return (await this.sendMessage(
104 Utils.generateUUID(),
105 payload,
106 OCPP16RequestCommand.AUTHORIZE
107 )) as OCPP16AuthorizeResponse;
108 }
109
110 public async sendStartTransaction(
111 connectorId: number,
112 idTag?: string
113 ): Promise<OCPP16StartTransactionResponse> {
114 const payload: StartTransactionRequest = {
115 connectorId,
116 ...(!Utils.isUndefined(idTag) ? { idTag } : { idTag: Constants.DEFAULT_IDTAG }),
117 meterStart: this.chargingStation.getEnergyActiveImportRegisterByConnectorId(connectorId),
118 timestamp: new Date().toISOString(),
119 };
120 return (await this.sendMessage(
121 Utils.generateUUID(),
122 payload,
123 OCPP16RequestCommand.START_TRANSACTION
124 )) as OCPP16StartTransactionResponse;
125 }
126
127 public async sendStopTransaction(
128 transactionId: number,
129 meterStop: number,
130 idTag?: string,
131 reason: OCPP16StopTransactionReason = OCPP16StopTransactionReason.NONE
132 ): Promise<OCPP16StopTransactionResponse> {
133 let connectorId: number;
134 for (const id of this.chargingStation.connectors.keys()) {
135 if (id > 0 && this.chargingStation.getConnectorStatus(id)?.transactionId === transactionId) {
136 connectorId = id;
137 break;
138 }
139 }
140 const transactionEndMeterValue = OCPP16ServiceUtils.buildTransactionEndMeterValue(
141 this.chargingStation,
142 connectorId,
143 meterStop
144 );
145 // FIXME: should be a callback, each OCPP commands implementation must do only one job
146 this.chargingStation.getBeginEndMeterValues() &&
147 this.chargingStation.getOcppStrictCompliance() &&
148 !this.chargingStation.getOutOfOrderEndMeterValues() &&
149 (await this.sendTransactionEndMeterValues(
150 connectorId,
151 transactionId,
152 transactionEndMeterValue
153 ));
154 const payload: StopTransactionRequest = {
155 transactionId,
156 ...(!Utils.isUndefined(idTag) && { idTag }),
157 meterStop,
158 timestamp: new Date().toISOString(),
159 ...(reason && { reason }),
160 ...(this.chargingStation.getTransactionDataMeterValues() && {
161 transactionData: OCPP16ServiceUtils.buildTransactionDataMeterValues(
162 this.chargingStation.getConnectorStatus(connectorId).transactionBeginMeterValue,
163 transactionEndMeterValue
164 ),
165 }),
166 };
167 return (await this.sendMessage(
168 Utils.generateUUID(),
169 payload,
170 OCPP16RequestCommand.STOP_TRANSACTION
171 )) as OCPP16StartTransactionResponse;
172 }
173
174 public async sendMeterValues(
175 connectorId: number,
176 transactionId: number,
177 interval: number
178 ): Promise<void> {
179 const meterValue = OCPP16ServiceUtils.buildMeterValue(
180 this.chargingStation,
181 connectorId,
182 transactionId,
183 interval
184 );
185 const payload: MeterValuesRequest = {
186 connectorId,
187 transactionId,
188 meterValue: [meterValue],
189 };
190 await this.sendMessage(Utils.generateUUID(), payload, OCPP16RequestCommand.METER_VALUES);
191 }
192
193 public async sendTransactionBeginMeterValues(
194 connectorId: number,
195 transactionId: number,
196 beginMeterValue: OCPP16MeterValue
197 ): Promise<void> {
198 const payload: MeterValuesRequest = {
199 connectorId,
200 transactionId,
201 meterValue: [beginMeterValue],
202 };
203 await this.sendMessage(Utils.generateUUID(), payload, OCPP16RequestCommand.METER_VALUES);
204 }
205
206 public async sendTransactionEndMeterValues(
207 connectorId: number,
208 transactionId: number,
209 endMeterValue: OCPP16MeterValue
210 ): Promise<void> {
211 const payload: MeterValuesRequest = {
212 connectorId,
213 transactionId,
214 meterValue: [endMeterValue],
215 };
216 await this.sendMessage(Utils.generateUUID(), payload, OCPP16RequestCommand.METER_VALUES);
217 }
218
219 public async sendDiagnosticsStatusNotification(
220 diagnosticsStatus: OCPP16DiagnosticsStatus
221 ): Promise<void> {
222 const payload: DiagnosticsStatusNotificationRequest = {
223 status: diagnosticsStatus,
224 };
225 await this.sendMessage(
226 Utils.generateUUID(),
227 payload,
228 OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION
229 );
230 }
231
232 private buildCommandPayload(
233 commandName: OCPP16RequestCommand,
234 commandParams: JsonType
235 ): JsonType {
236 switch (commandName) {
237 case OCPP16RequestCommand.AUTHORIZE:
238 return {
239 ...(!Utils.isUndefined(commandParams?.idTag)
240 ? { idTag: commandParams.idTag }
241 : { idTag: Constants.DEFAULT_IDTAG }),
242 } as AuthorizeRequest;
243 case OCPP16RequestCommand.BOOT_NOTIFICATION:
244 return {
245 chargePointModel: commandParams?.chargePointModel,
246 chargePointVendor: commandParams?.chargePointVendor,
247 ...(!Utils.isUndefined(commandParams?.chargeBoxSerialNumber) && {
248 chargeBoxSerialNumber: commandParams.chargeBoxSerialNumber,
249 }),
250 ...(!Utils.isUndefined(commandParams?.chargePointSerialNumber) && {
251 chargePointSerialNumber: commandParams.chargePointSerialNumber,
252 }),
253 ...(!Utils.isUndefined(commandParams?.firmwareVersion) && {
254 firmwareVersion: commandParams.firmwareVersion,
255 }),
256 ...(!Utils.isUndefined(commandParams?.iccid) && { iccid: commandParams.iccid }),
257 ...(!Utils.isUndefined(commandParams?.imsi) && { imsi: commandParams.imsi }),
258 ...(!Utils.isUndefined(commandParams?.meterSerialNumber) && {
259 meterSerialNumber: commandParams.meterSerialNumber,
260 }),
261 ...(!Utils.isUndefined(commandParams?.meterType) && {
262 meterType: commandParams.meterType,
263 }),
264 } as OCPP16BootNotificationRequest;
265 case OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION:
266 return {
267 status: commandParams?.diagnosticsStatus,
268 } as DiagnosticsStatusNotificationRequest;
269 case OCPP16RequestCommand.HEARTBEAT:
270 return {} as HeartbeatRequest;
271 case OCPP16RequestCommand.METER_VALUES:
272 return {
273 connectorId: commandParams?.connectorId,
274 transactionId: commandParams?.transactionId,
275 meterValue: Array.isArray(commandParams?.meterValues)
276 ? commandParams?.meterValues
277 : [commandParams?.meterValue],
278 } as MeterValuesRequest;
279 case OCPP16RequestCommand.STATUS_NOTIFICATION:
280 return {
281 connectorId: commandParams?.connectorId,
282 errorCode: commandParams?.errorCode,
283 status: commandParams?.status,
284 } as StatusNotificationRequest;
285 case OCPP16RequestCommand.START_TRANSACTION:
286 return {
287 connectorId: commandParams?.connectorId,
288 ...(!Utils.isUndefined(commandParams?.idTag)
289 ? { idTag: commandParams?.idTag }
290 : { idTag: Constants.DEFAULT_IDTAG }),
291 meterStart: this.chargingStation.getEnergyActiveImportRegisterByConnectorId(
292 commandParams?.connectorId as number
293 ),
294 timestamp: new Date().toISOString(),
295 } as StartTransactionRequest;
296 case OCPP16RequestCommand.STOP_TRANSACTION:
297 return {
298 transactionId: commandParams?.transactionId,
299 ...(!Utils.isUndefined(commandParams?.idTag) && { idTag: commandParams.idTag }),
300 meterStop: commandParams?.meterStop,
301 timestamp: new Date().toISOString(),
302 ...(commandParams?.reason && { reason: commandParams.reason }),
303 ...(this.chargingStation.getTransactionDataMeterValues() && {
304 transactionData: OCPP16ServiceUtils.buildTransactionDataMeterValues(
305 this.chargingStation.getConnectorStatus(commandParams?.connectorId as number)
306 .transactionBeginMeterValue,
307 OCPP16ServiceUtils.buildTransactionEndMeterValue(
308 this.chargingStation,
309 commandParams?.connectorId as number,
310 commandParams?.meterStop as number
311 )
312 ),
313 }),
314 } as StopTransactionRequest;
315 default:
316 throw new OCPPError(
317 ErrorType.NOT_SUPPORTED,
318 // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
319 `Unsupported OCPP command: ${commandName}`,
320 commandName,
321 { commandName }
322 );
323 }
324 }
325 }