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