fix: restart metervalues interval if MeterValueSampleInterval is changed
[e-mobility-charging-stations-simulator.git] / src / charging-station / ocpp / 1.6 / OCPP16IncomingRequestService.ts
1 // Partial Copyright Jerome Benoit. 2021-2024. All Rights Reserved.
2
3 import { randomInt } from 'node:crypto'
4 import { createWriteStream, readdirSync } from 'node:fs'
5 import { dirname, extname, join, resolve } from 'node:path'
6 import { fileURLToPath, URL } from 'node:url'
7
8 import type { ValidateFunction } from 'ajv'
9 import { Client, type FTPResponse } from 'basic-ftp'
10 import {
11 addSeconds,
12 differenceInSeconds,
13 type Interval,
14 isDate,
15 secondsToMilliseconds
16 } from 'date-fns'
17 import { maxTime } from 'date-fns/constants'
18 import { isEmpty } from 'rambda'
19 import { create } from 'tar'
20
21 import {
22 canProceedChargingProfile,
23 type ChargingStation,
24 checkChargingStation,
25 getConfigurationKey,
26 getConnectorChargingProfiles,
27 prepareChargingProfileKind,
28 removeExpiredReservations,
29 resetAuthorizeConnectorStatus,
30 setConfigurationKeyValue
31 } from '../../../charging-station/index.js'
32 import { OCPPError } from '../../../exception/index.js'
33 import {
34 type ChangeConfigurationRequest,
35 type ChangeConfigurationResponse,
36 ConfigurationSection,
37 ErrorType,
38 type GenericResponse,
39 GenericStatus,
40 type GetConfigurationRequest,
41 type GetConfigurationResponse,
42 type GetDiagnosticsRequest,
43 type GetDiagnosticsResponse,
44 type IncomingRequestHandler,
45 type JsonType,
46 type LogConfiguration,
47 OCPP16AuthorizationStatus,
48 OCPP16AvailabilityType,
49 type OCPP16BootNotificationRequest,
50 type OCPP16BootNotificationResponse,
51 type OCPP16CancelReservationRequest,
52 type OCPP16ChangeAvailabilityRequest,
53 type OCPP16ChangeAvailabilityResponse,
54 OCPP16ChargePointErrorCode,
55 OCPP16ChargePointStatus,
56 type OCPP16ChargingProfile,
57 OCPP16ChargingProfilePurposeType,
58 type OCPP16ChargingSchedule,
59 type OCPP16ClearCacheRequest,
60 type OCPP16ClearChargingProfileRequest,
61 type OCPP16ClearChargingProfileResponse,
62 type OCPP16DataTransferRequest,
63 type OCPP16DataTransferResponse,
64 OCPP16DataTransferVendorId,
65 OCPP16DiagnosticsStatus,
66 type OCPP16DiagnosticsStatusNotificationRequest,
67 type OCPP16DiagnosticsStatusNotificationResponse,
68 OCPP16FirmwareStatus,
69 type OCPP16FirmwareStatusNotificationRequest,
70 type OCPP16FirmwareStatusNotificationResponse,
71 type OCPP16GetCompositeScheduleRequest,
72 type OCPP16GetCompositeScheduleResponse,
73 type OCPP16HeartbeatRequest,
74 type OCPP16HeartbeatResponse,
75 OCPP16IncomingRequestCommand,
76 OCPP16MessageTrigger,
77 OCPP16RequestCommand,
78 type OCPP16ReserveNowRequest,
79 type OCPP16ReserveNowResponse,
80 OCPP16StandardParametersKey,
81 type OCPP16StartTransactionRequest,
82 type OCPP16StartTransactionResponse,
83 type OCPP16StatusNotificationRequest,
84 type OCPP16StatusNotificationResponse,
85 OCPP16StopTransactionReason,
86 OCPP16SupportedFeatureProfiles,
87 type OCPP16TriggerMessageRequest,
88 type OCPP16TriggerMessageResponse,
89 OCPP16TriggerMessageStatus,
90 type OCPP16UpdateFirmwareRequest,
91 type OCPP16UpdateFirmwareResponse,
92 type OCPPConfigurationKey,
93 OCPPVersion,
94 type RemoteStartTransactionRequest,
95 type RemoteStopTransactionRequest,
96 ReservationTerminationReason,
97 type ResetRequest,
98 type SetChargingProfileRequest,
99 type SetChargingProfileResponse,
100 type UnlockConnectorRequest,
101 type UnlockConnectorResponse
102 } from '../../../types/index.js'
103 import {
104 Configuration,
105 Constants,
106 convertToDate,
107 convertToInt,
108 formatDurationMilliSeconds,
109 handleIncomingRequestError,
110 isAsyncFunction,
111 isNotEmptyArray,
112 isNotEmptyString,
113 logger,
114 sleep
115 } from '../../../utils/index.js'
116 import { OCPPIncomingRequestService } from '../OCPPIncomingRequestService.js'
117 import { OCPP16Constants } from './OCPP16Constants.js'
118 import { OCPP16ServiceUtils } from './OCPP16ServiceUtils.js'
119
120 const moduleName = 'OCPP16IncomingRequestService'
121
122 export class OCPP16IncomingRequestService extends OCPPIncomingRequestService {
123 protected payloadValidateFunctions: Map<OCPP16IncomingRequestCommand, ValidateFunction<JsonType>>
124
125 private readonly incomingRequestHandlers: Map<
126 OCPP16IncomingRequestCommand,
127 IncomingRequestHandler
128 >
129
130 public constructor () {
131 // if (new.target.name === moduleName) {
132 // throw new TypeError(`Cannot construct ${new.target.name} instances directly`)
133 // }
134 super(OCPPVersion.VERSION_16)
135 this.incomingRequestHandlers = new Map<OCPP16IncomingRequestCommand, IncomingRequestHandler>([
136 [
137 OCPP16IncomingRequestCommand.RESET,
138 this.handleRequestReset.bind(this) as unknown as IncomingRequestHandler
139 ],
140 [
141 OCPP16IncomingRequestCommand.CLEAR_CACHE,
142 this.handleRequestClearCache.bind(this) as IncomingRequestHandler
143 ],
144 [
145 OCPP16IncomingRequestCommand.UNLOCK_CONNECTOR,
146 this.handleRequestUnlockConnector.bind(this) as unknown as IncomingRequestHandler
147 ],
148 [
149 OCPP16IncomingRequestCommand.GET_CONFIGURATION,
150 this.handleRequestGetConfiguration.bind(this) as IncomingRequestHandler
151 ],
152 [
153 OCPP16IncomingRequestCommand.CHANGE_CONFIGURATION,
154 this.handleRequestChangeConfiguration.bind(this) as unknown as IncomingRequestHandler
155 ],
156 [
157 OCPP16IncomingRequestCommand.GET_COMPOSITE_SCHEDULE,
158 this.handleRequestGetCompositeSchedule.bind(this) as unknown as IncomingRequestHandler
159 ],
160 [
161 OCPP16IncomingRequestCommand.SET_CHARGING_PROFILE,
162 this.handleRequestSetChargingProfile.bind(this) as unknown as IncomingRequestHandler
163 ],
164 [
165 OCPP16IncomingRequestCommand.CLEAR_CHARGING_PROFILE,
166 this.handleRequestClearChargingProfile.bind(this) as IncomingRequestHandler
167 ],
168 [
169 OCPP16IncomingRequestCommand.CHANGE_AVAILABILITY,
170 this.handleRequestChangeAvailability.bind(this) as unknown as IncomingRequestHandler
171 ],
172 [
173 OCPP16IncomingRequestCommand.REMOTE_START_TRANSACTION,
174 this.handleRequestRemoteStartTransaction.bind(this) as unknown as IncomingRequestHandler
175 ],
176 [
177 OCPP16IncomingRequestCommand.REMOTE_STOP_TRANSACTION,
178 this.handleRequestRemoteStopTransaction.bind(this) as unknown as IncomingRequestHandler
179 ],
180 [
181 OCPP16IncomingRequestCommand.GET_DIAGNOSTICS,
182 this.handleRequestGetDiagnostics.bind(this) as IncomingRequestHandler
183 ],
184 [
185 OCPP16IncomingRequestCommand.TRIGGER_MESSAGE,
186 this.handleRequestTriggerMessage.bind(this) as unknown as IncomingRequestHandler
187 ],
188 [
189 OCPP16IncomingRequestCommand.DATA_TRANSFER,
190 this.handleRequestDataTransfer.bind(this) as unknown as IncomingRequestHandler
191 ],
192 [
193 OCPP16IncomingRequestCommand.UPDATE_FIRMWARE,
194 this.handleRequestUpdateFirmware.bind(this) as unknown as IncomingRequestHandler
195 ],
196 [
197 OCPP16IncomingRequestCommand.RESERVE_NOW,
198 this.handleRequestReserveNow.bind(this) as unknown as IncomingRequestHandler
199 ],
200 [
201 OCPP16IncomingRequestCommand.CANCEL_RESERVATION,
202 this.handleRequestCancelReservation.bind(this) as unknown as IncomingRequestHandler
203 ]
204 ])
205 this.payloadValidateFunctions = new Map<
206 OCPP16IncomingRequestCommand,
207 ValidateFunction<JsonType>
208 >([
209 [
210 OCPP16IncomingRequestCommand.RESET,
211 this.ajv
212 .compile(
213 OCPP16ServiceUtils.parseJsonSchemaFile<ResetRequest>(
214 'assets/json-schemas/ocpp/1.6/Reset.json',
215 moduleName,
216 'constructor'
217 )
218 )
219 .bind(this)
220 ],
221 [
222 OCPP16IncomingRequestCommand.CLEAR_CACHE,
223 this.ajv
224 .compile(
225 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16ClearCacheRequest>(
226 'assets/json-schemas/ocpp/1.6/ClearCache.json',
227 moduleName,
228 'constructor'
229 )
230 )
231 .bind(this)
232 ],
233 [
234 OCPP16IncomingRequestCommand.UNLOCK_CONNECTOR,
235 this.ajv
236 .compile(
237 OCPP16ServiceUtils.parseJsonSchemaFile<UnlockConnectorRequest>(
238 'assets/json-schemas/ocpp/1.6/UnlockConnector.json',
239 moduleName,
240 'constructor'
241 )
242 )
243 .bind(this)
244 ],
245 [
246 OCPP16IncomingRequestCommand.GET_CONFIGURATION,
247 this.ajv
248 .compile(
249 OCPP16ServiceUtils.parseJsonSchemaFile<GetConfigurationRequest>(
250 'assets/json-schemas/ocpp/1.6/GetConfiguration.json',
251 moduleName,
252 'constructor'
253 )
254 )
255 .bind(this)
256 ],
257 [
258 OCPP16IncomingRequestCommand.CHANGE_CONFIGURATION,
259 this.ajv
260 .compile(
261 OCPP16ServiceUtils.parseJsonSchemaFile<ChangeConfigurationRequest>(
262 'assets/json-schemas/ocpp/1.6/ChangeConfiguration.json',
263 moduleName,
264 'constructor'
265 )
266 )
267 .bind(this)
268 ],
269 [
270 OCPP16IncomingRequestCommand.GET_DIAGNOSTICS,
271 this.ajv
272 .compile(
273 OCPP16ServiceUtils.parseJsonSchemaFile<GetDiagnosticsRequest>(
274 'assets/json-schemas/ocpp/1.6/GetDiagnostics.json',
275 moduleName,
276 'constructor'
277 )
278 )
279 .bind(this)
280 ],
281 [
282 OCPP16IncomingRequestCommand.GET_COMPOSITE_SCHEDULE,
283 this.ajv
284 .compile(
285 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16GetCompositeScheduleRequest>(
286 'assets/json-schemas/ocpp/1.6/GetCompositeSchedule.json',
287 moduleName,
288 'constructor'
289 )
290 )
291 .bind(this)
292 ],
293 [
294 OCPP16IncomingRequestCommand.SET_CHARGING_PROFILE,
295 this.ajv
296 .compile(
297 OCPP16ServiceUtils.parseJsonSchemaFile<SetChargingProfileRequest>(
298 'assets/json-schemas/ocpp/1.6/SetChargingProfile.json',
299 moduleName,
300 'constructor'
301 )
302 )
303 .bind(this)
304 ],
305 [
306 OCPP16IncomingRequestCommand.CLEAR_CHARGING_PROFILE,
307 this.ajv
308 .compile(
309 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16ClearChargingProfileRequest>(
310 'assets/json-schemas/ocpp/1.6/ClearChargingProfile.json',
311 moduleName,
312 'constructor'
313 )
314 )
315 .bind(this)
316 ],
317 [
318 OCPP16IncomingRequestCommand.CHANGE_AVAILABILITY,
319 this.ajv
320 .compile(
321 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16ChangeAvailabilityRequest>(
322 'assets/json-schemas/ocpp/1.6/ChangeAvailability.json',
323 moduleName,
324 'constructor'
325 )
326 )
327 .bind(this)
328 ],
329 [
330 OCPP16IncomingRequestCommand.REMOTE_START_TRANSACTION,
331 this.ajv
332 .compile(
333 OCPP16ServiceUtils.parseJsonSchemaFile<RemoteStartTransactionRequest>(
334 'assets/json-schemas/ocpp/1.6/RemoteStartTransaction.json',
335 moduleName,
336 'constructor'
337 )
338 )
339 .bind(this)
340 ],
341 [
342 OCPP16IncomingRequestCommand.REMOTE_STOP_TRANSACTION,
343 this.ajv
344 .compile(
345 OCPP16ServiceUtils.parseJsonSchemaFile<RemoteStopTransactionRequest>(
346 'assets/json-schemas/ocpp/1.6/RemoteStopTransaction.json',
347 moduleName,
348 'constructor'
349 )
350 )
351 .bind(this)
352 ],
353 [
354 OCPP16IncomingRequestCommand.TRIGGER_MESSAGE,
355 this.ajv
356 .compile(
357 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16TriggerMessageRequest>(
358 'assets/json-schemas/ocpp/1.6/TriggerMessage.json',
359 moduleName,
360 'constructor'
361 )
362 )
363 .bind(this)
364 ],
365 [
366 OCPP16IncomingRequestCommand.DATA_TRANSFER,
367 this.ajv
368 .compile(
369 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16DataTransferRequest>(
370 'assets/json-schemas/ocpp/1.6/DataTransfer.json',
371 moduleName,
372 'constructor'
373 )
374 )
375 .bind(this)
376 ],
377 [
378 OCPP16IncomingRequestCommand.UPDATE_FIRMWARE,
379 this.ajv
380 .compile(
381 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16UpdateFirmwareRequest>(
382 'assets/json-schemas/ocpp/1.6/UpdateFirmware.json',
383 moduleName,
384 'constructor'
385 )
386 )
387 .bind(this)
388 ],
389 [
390 OCPP16IncomingRequestCommand.RESERVE_NOW,
391 this.ajv
392 .compile(
393 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16ReserveNowRequest>(
394 'assets/json-schemas/ocpp/1.6/ReserveNow.json',
395 moduleName,
396 'constructor'
397 )
398 )
399 .bind(this)
400 ],
401 [
402 OCPP16IncomingRequestCommand.CANCEL_RESERVATION,
403 this.ajv
404 .compile(
405 OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16CancelReservationRequest>(
406 'assets/json-schemas/ocpp/1.6/CancelReservation.json',
407 moduleName,
408 'constructor'
409 )
410 )
411 .bind(this)
412 ]
413 ])
414 // Handle incoming request events
415 this.on(
416 OCPP16IncomingRequestCommand.REMOTE_START_TRANSACTION,
417 (
418 chargingStation: ChargingStation,
419 request: RemoteStartTransactionRequest,
420 response: GenericResponse
421 ) => {
422 if (response.status === GenericStatus.Accepted) {
423 const { connectorId, idTag } = request
424 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
425 chargingStation.getConnectorStatus(connectorId!)!.transactionRemoteStarted = true
426 chargingStation.ocppRequestService
427 .requestHandler<Partial<OCPP16StartTransactionRequest>, OCPP16StartTransactionResponse>(
428 chargingStation,
429 OCPP16RequestCommand.START_TRANSACTION,
430 {
431 connectorId,
432 idTag
433 }
434 )
435 .then(response => {
436 if (response.idTagInfo.status === OCPP16AuthorizationStatus.ACCEPTED) {
437 logger.debug(
438 `${chargingStation.logPrefix()} Remote start transaction ACCEPTED on ${
439 chargingStation.stationInfo?.chargingStationId
440 }#${connectorId} for idTag '${idTag}'`
441 )
442 } else {
443 logger.debug(
444 `${chargingStation.logPrefix()} Remote start transaction REJECTED on ${
445 chargingStation.stationInfo?.chargingStationId
446 }#${connectorId} for idTag '${idTag}'`
447 )
448 }
449 })
450 .catch((error: unknown) => {
451 logger.error(
452 `${chargingStation.logPrefix()} ${moduleName}.constructor: Remote start transaction error:`,
453 error
454 )
455 })
456 }
457 }
458 )
459 this.on(
460 OCPP16IncomingRequestCommand.REMOTE_STOP_TRANSACTION,
461 (
462 chargingStation: ChargingStation,
463 request: RemoteStopTransactionRequest,
464 response: GenericResponse
465 ) => {
466 if (response.status === GenericStatus.Accepted) {
467 const { transactionId } = request
468 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
469 const connectorId = chargingStation.getConnectorIdByTransactionId(transactionId)!
470 OCPP16ServiceUtils.remoteStopTransaction(chargingStation, connectorId)
471 .then(response => {
472 if (response.status === GenericStatus.Accepted) {
473 logger.debug(
474 `${chargingStation.logPrefix()} Remote stop transaction ACCEPTED on ${
475 chargingStation.stationInfo?.chargingStationId
476 }#${connectorId} for transaction '${transactionId}'`
477 )
478 } else {
479 logger.debug(
480 `${chargingStation.logPrefix()} Remote stop transaction REJECTED on ${
481 chargingStation.stationInfo?.chargingStationId
482 }#${connectorId} for transaction '${transactionId}'`
483 )
484 }
485 })
486 .catch((error: unknown) => {
487 logger.error(
488 `${chargingStation.logPrefix()} ${moduleName}.constructor: Remote stop transaction error:`,
489 error
490 )
491 })
492 }
493 }
494 )
495 this.on(
496 OCPP16IncomingRequestCommand.TRIGGER_MESSAGE,
497 (
498 chargingStation: ChargingStation,
499 request: OCPP16TriggerMessageRequest,
500 response: OCPP16TriggerMessageResponse
501 ) => {
502 if (response.status !== OCPP16TriggerMessageStatus.ACCEPTED) {
503 return
504 }
505 const { requestedMessage, connectorId } = request
506 const errorHandler = (error: unknown): void => {
507 logger.error(
508 `${chargingStation.logPrefix()} ${moduleName}.constructor: Trigger ${requestedMessage} error:`,
509 error
510 )
511 }
512 switch (requestedMessage) {
513 case OCPP16MessageTrigger.BootNotification:
514 chargingStation.ocppRequestService
515 .requestHandler<
516 OCPP16BootNotificationRequest,
517 OCPP16BootNotificationResponse
518 >(chargingStation, OCPP16RequestCommand.BOOT_NOTIFICATION, chargingStation.bootNotificationRequest as OCPP16BootNotificationRequest, { skipBufferingOnError: true, triggerMessage: true })
519 .catch(errorHandler)
520 break
521 case OCPP16MessageTrigger.Heartbeat:
522 chargingStation.ocppRequestService
523 .requestHandler<OCPP16HeartbeatRequest, OCPP16HeartbeatResponse>(
524 chargingStation,
525 OCPP16RequestCommand.HEARTBEAT,
526 undefined,
527 {
528 triggerMessage: true
529 }
530 )
531 .catch(errorHandler)
532 break
533 case OCPP16MessageTrigger.StatusNotification:
534 if (connectorId != null) {
535 chargingStation.ocppRequestService
536 .requestHandler<OCPP16StatusNotificationRequest, OCPP16StatusNotificationResponse>(
537 chargingStation,
538 OCPP16RequestCommand.STATUS_NOTIFICATION,
539 {
540 connectorId,
541 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
542 status: chargingStation.getConnectorStatus(connectorId)
543 ?.status as OCPP16ChargePointStatus
544 },
545 {
546 triggerMessage: true
547 }
548 )
549 .catch(errorHandler)
550 } else if (chargingStation.hasEvses) {
551 for (const evseStatus of chargingStation.evses.values()) {
552 for (const [id, connectorStatus] of evseStatus.connectors) {
553 chargingStation.ocppRequestService
554 .requestHandler<
555 OCPP16StatusNotificationRequest,
556 OCPP16StatusNotificationResponse
557 >(
558 chargingStation,
559 OCPP16RequestCommand.STATUS_NOTIFICATION,
560 {
561 connectorId: id,
562 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
563 status: connectorStatus.status as OCPP16ChargePointStatus
564 },
565 {
566 triggerMessage: true
567 }
568 )
569 .catch(errorHandler)
570 }
571 }
572 } else {
573 for (const [id, connectorStatus] of chargingStation.connectors) {
574 chargingStation.ocppRequestService
575 .requestHandler<
576 OCPP16StatusNotificationRequest,
577 OCPP16StatusNotificationResponse
578 >(
579 chargingStation,
580 OCPP16RequestCommand.STATUS_NOTIFICATION,
581 {
582 connectorId: id,
583 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
584 status: connectorStatus.status as OCPP16ChargePointStatus
585 },
586 {
587 triggerMessage: true
588 }
589 )
590 .catch(errorHandler)
591 }
592 }
593 break
594 }
595 }
596 )
597 this.validatePayload = this.validatePayload.bind(this)
598 }
599
600 public async incomingRequestHandler<ReqType extends JsonType, ResType extends JsonType>(
601 chargingStation: ChargingStation,
602 messageId: string,
603 commandName: OCPP16IncomingRequestCommand,
604 commandPayload: ReqType
605 ): Promise<void> {
606 let response: ResType
607 if (
608 chargingStation.stationInfo?.ocppStrictCompliance === true &&
609 chargingStation.inPendingState() &&
610 (commandName === OCPP16IncomingRequestCommand.REMOTE_START_TRANSACTION ||
611 commandName === OCPP16IncomingRequestCommand.REMOTE_STOP_TRANSACTION)
612 ) {
613 throw new OCPPError(
614 ErrorType.SECURITY_ERROR,
615 `${commandName} cannot be issued to handle request PDU ${JSON.stringify(
616 commandPayload,
617 undefined,
618 2
619 )} while the charging station is in pending state on the central server`,
620 commandName,
621 commandPayload
622 )
623 }
624 if (
625 chargingStation.isRegistered() ||
626 (chargingStation.stationInfo?.ocppStrictCompliance === false &&
627 chargingStation.inUnknownState())
628 ) {
629 if (
630 this.incomingRequestHandlers.has(commandName) &&
631 OCPP16ServiceUtils.isIncomingRequestCommandSupported(chargingStation, commandName)
632 ) {
633 try {
634 this.validatePayload(chargingStation, commandName, commandPayload)
635 // Call the method to build the response
636 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
637 const incomingRequestHandler = this.incomingRequestHandlers.get(commandName)!
638 if (isAsyncFunction(incomingRequestHandler)) {
639 response = (await incomingRequestHandler(chargingStation, commandPayload)) as ResType
640 } else {
641 response = incomingRequestHandler(chargingStation, commandPayload) as ResType
642 }
643 } catch (error) {
644 // Log
645 logger.error(
646 `${chargingStation.logPrefix()} ${moduleName}.incomingRequestHandler: Handle incoming request error:`,
647 error
648 )
649 throw error
650 }
651 } else {
652 // Throw exception
653 throw new OCPPError(
654 ErrorType.NOT_IMPLEMENTED,
655 `${commandName} is not implemented to handle request PDU ${JSON.stringify(
656 commandPayload,
657 undefined,
658 2
659 )}`,
660 commandName,
661 commandPayload
662 )
663 }
664 } else {
665 throw new OCPPError(
666 ErrorType.SECURITY_ERROR,
667 `${commandName} cannot be issued to handle request PDU ${JSON.stringify(
668 commandPayload,
669 undefined,
670 2
671 )} while the charging station is not registered on the central server`,
672 commandName,
673 commandPayload
674 )
675 }
676 // Send the built response
677 await chargingStation.ocppRequestService.sendResponse(
678 chargingStation,
679 messageId,
680 response,
681 commandName
682 )
683 // Emit command name event to allow delayed handling
684 this.emit(commandName, chargingStation, commandPayload, response)
685 }
686
687 private validatePayload (
688 chargingStation: ChargingStation,
689 commandName: OCPP16IncomingRequestCommand,
690 commandPayload: JsonType
691 ): boolean {
692 if (this.payloadValidateFunctions.has(commandName)) {
693 return this.validateIncomingRequestPayload(chargingStation, commandName, commandPayload)
694 }
695 logger.warn(
696 `${chargingStation.logPrefix()} ${moduleName}.validatePayload: No JSON schema validation function found for command '${commandName}' PDU validation`
697 )
698 return false
699 }
700
701 // Simulate charging station restart
702 private handleRequestReset (
703 chargingStation: ChargingStation,
704 commandPayload: ResetRequest
705 ): GenericResponse {
706 const { type } = commandPayload
707 chargingStation
708 .reset(`${type}Reset` as OCPP16StopTransactionReason)
709 .catch(Constants.EMPTY_FUNCTION)
710 logger.info(
711 `${chargingStation.logPrefix()} ${type} reset command received, simulating it. The station will be back online in ${formatDurationMilliSeconds(
712 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
713 chargingStation.stationInfo!.resetTime!
714 )}`
715 )
716 return OCPP16Constants.OCPP_RESPONSE_ACCEPTED
717 }
718
719 private async handleRequestUnlockConnector (
720 chargingStation: ChargingStation,
721 commandPayload: UnlockConnectorRequest
722 ): Promise<UnlockConnectorResponse> {
723 const { connectorId } = commandPayload
724 if (!chargingStation.hasConnector(connectorId)) {
725 logger.error(
726 `${chargingStation.logPrefix()} Trying to unlock a non existing connector id ${connectorId}`
727 )
728 return OCPP16Constants.OCPP_RESPONSE_UNLOCK_NOT_SUPPORTED
729 }
730 if (connectorId === 0) {
731 logger.error(`${chargingStation.logPrefix()} Trying to unlock connector id ${connectorId}`)
732 return OCPP16Constants.OCPP_RESPONSE_UNLOCK_NOT_SUPPORTED
733 }
734 if (chargingStation.getConnectorStatus(connectorId)?.transactionStarted === true) {
735 const stopResponse = await chargingStation.stopTransactionOnConnector(
736 connectorId,
737 OCPP16StopTransactionReason.UNLOCK_COMMAND
738 )
739 if (stopResponse.idTagInfo?.status === OCPP16AuthorizationStatus.ACCEPTED) {
740 return OCPP16Constants.OCPP_RESPONSE_UNLOCKED
741 }
742 return OCPP16Constants.OCPP_RESPONSE_UNLOCK_FAILED
743 }
744 await OCPP16ServiceUtils.sendAndSetConnectorStatus(
745 chargingStation,
746 connectorId,
747 OCPP16ChargePointStatus.Available
748 )
749 return OCPP16Constants.OCPP_RESPONSE_UNLOCKED
750 }
751
752 private handleRequestGetConfiguration (
753 chargingStation: ChargingStation,
754 commandPayload: GetConfigurationRequest
755 ): GetConfigurationResponse {
756 const { key } = commandPayload
757 const configurationKey: OCPPConfigurationKey[] = []
758 const unknownKey: string[] = []
759 if (key == null) {
760 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
761 for (const configKey of chargingStation.ocppConfiguration!.configurationKey!) {
762 if (!OCPP16ServiceUtils.isConfigurationKeyVisible(configKey)) {
763 continue
764 }
765 configurationKey.push({
766 key: configKey.key,
767 readonly: configKey.readonly,
768 value: configKey.value
769 })
770 }
771 } else if (isNotEmptyArray(key)) {
772 for (const k of key) {
773 const keyFound = getConfigurationKey(chargingStation, k, true)
774 if (keyFound != null) {
775 if (!OCPP16ServiceUtils.isConfigurationKeyVisible(keyFound)) {
776 continue
777 }
778 configurationKey.push({
779 key: keyFound.key,
780 readonly: keyFound.readonly,
781 value: keyFound.value
782 })
783 } else {
784 unknownKey.push(k)
785 }
786 }
787 }
788 return {
789 configurationKey,
790 unknownKey
791 }
792 }
793
794 private handleRequestChangeConfiguration (
795 chargingStation: ChargingStation,
796 commandPayload: ChangeConfigurationRequest
797 ): ChangeConfigurationResponse {
798 const { key, value } = commandPayload
799 const keyToChange = getConfigurationKey(chargingStation, key, true)
800 if (keyToChange?.readonly === true) {
801 return OCPP16Constants.OCPP_CONFIGURATION_RESPONSE_REJECTED
802 } else if (keyToChange?.readonly === false) {
803 let valueChanged = false
804 if (keyToChange.value !== value) {
805 setConfigurationKeyValue(chargingStation, key, value, true)
806 valueChanged = true
807 }
808 let triggerHeartbeatRestart = false
809 if (
810 (keyToChange.key as OCPP16StandardParametersKey) ===
811 OCPP16StandardParametersKey.HeartBeatInterval &&
812 valueChanged
813 ) {
814 setConfigurationKeyValue(
815 chargingStation,
816 OCPP16StandardParametersKey.HeartbeatInterval,
817 value
818 )
819 triggerHeartbeatRestart = true
820 }
821 if (
822 (keyToChange.key as OCPP16StandardParametersKey) ===
823 OCPP16StandardParametersKey.HeartbeatInterval &&
824 valueChanged
825 ) {
826 setConfigurationKeyValue(
827 chargingStation,
828 OCPP16StandardParametersKey.HeartBeatInterval,
829 value
830 )
831 triggerHeartbeatRestart = true
832 }
833 if (triggerHeartbeatRestart) {
834 chargingStation.restartHeartbeat()
835 }
836 if (
837 (keyToChange.key as OCPP16StandardParametersKey) ===
838 OCPP16StandardParametersKey.WebSocketPingInterval &&
839 valueChanged
840 ) {
841 chargingStation.restartWebSocketPing()
842 }
843 if (
844 (keyToChange.key as OCPP16StandardParametersKey) ===
845 OCPP16StandardParametersKey.MeterValueSampleInterval &&
846 chargingStation.getNumberOfRunningTransactions() > 0 &&
847 valueChanged
848 ) {
849 for (
850 let connectorId = 1;
851 connectorId <= chargingStation.getNumberOfConnectors();
852 connectorId++
853 ) {
854 if (chargingStation.getConnectorStatus(connectorId)?.transactionStarted === true) {
855 chargingStation.restartMeterValues(
856 connectorId,
857 secondsToMilliseconds(convertToInt(value))
858 )
859 }
860 }
861 }
862 if (keyToChange.reboot === true) {
863 return OCPP16Constants.OCPP_CONFIGURATION_RESPONSE_REBOOT_REQUIRED
864 }
865 return OCPP16Constants.OCPP_CONFIGURATION_RESPONSE_ACCEPTED
866 }
867 return OCPP16Constants.OCPP_CONFIGURATION_RESPONSE_NOT_SUPPORTED
868 }
869
870 private handleRequestSetChargingProfile (
871 chargingStation: ChargingStation,
872 commandPayload: SetChargingProfileRequest
873 ): SetChargingProfileResponse {
874 if (
875 !OCPP16ServiceUtils.checkFeatureProfile(
876 chargingStation,
877 OCPP16SupportedFeatureProfiles.SmartCharging,
878 OCPP16IncomingRequestCommand.SET_CHARGING_PROFILE
879 )
880 ) {
881 return OCPP16Constants.OCPP_SET_CHARGING_PROFILE_RESPONSE_NOT_SUPPORTED
882 }
883 const { connectorId, csChargingProfiles } = commandPayload
884 if (!chargingStation.hasConnector(connectorId)) {
885 logger.error(
886 `${chargingStation.logPrefix()} Trying to set charging profile(s) to a non existing connector id ${connectorId}`
887 )
888 return OCPP16Constants.OCPP_SET_CHARGING_PROFILE_RESPONSE_REJECTED
889 }
890 if (
891 csChargingProfiles.chargingProfilePurpose ===
892 OCPP16ChargingProfilePurposeType.CHARGE_POINT_MAX_PROFILE &&
893 connectorId !== 0
894 ) {
895 return OCPP16Constants.OCPP_SET_CHARGING_PROFILE_RESPONSE_REJECTED
896 }
897 if (
898 csChargingProfiles.chargingProfilePurpose === OCPP16ChargingProfilePurposeType.TX_PROFILE &&
899 connectorId === 0
900 ) {
901 logger.error(
902 `${chargingStation.logPrefix()} Trying to set transaction charging profile(s) on connector ${connectorId}`
903 )
904 return OCPP16Constants.OCPP_SET_CHARGING_PROFILE_RESPONSE_REJECTED
905 }
906 const connectorStatus = chargingStation.getConnectorStatus(connectorId)
907 if (
908 csChargingProfiles.chargingProfilePurpose === OCPP16ChargingProfilePurposeType.TX_PROFILE &&
909 connectorId > 0 &&
910 connectorStatus?.transactionStarted === false
911 ) {
912 logger.error(
913 `${chargingStation.logPrefix()} Trying to set transaction charging profile(s) on connector ${connectorId} without a started transaction`
914 )
915 return OCPP16Constants.OCPP_SET_CHARGING_PROFILE_RESPONSE_REJECTED
916 }
917 if (
918 csChargingProfiles.chargingProfilePurpose === OCPP16ChargingProfilePurposeType.TX_PROFILE &&
919 connectorId > 0 &&
920 connectorStatus?.transactionStarted === true &&
921 csChargingProfiles.transactionId !== connectorStatus.transactionId
922 ) {
923 logger.error(
924 `${chargingStation.logPrefix()} Trying to set transaction charging profile(s) on connector ${connectorId} with a different transaction id ${
925 csChargingProfiles.transactionId
926 } than the started transaction id ${connectorStatus.transactionId}`
927 )
928 return OCPP16Constants.OCPP_SET_CHARGING_PROFILE_RESPONSE_REJECTED
929 }
930 OCPP16ServiceUtils.setChargingProfile(chargingStation, connectorId, csChargingProfiles)
931 logger.debug(
932 `${chargingStation.logPrefix()} Charging profile(s) set on connector id ${connectorId}: %j`,
933 csChargingProfiles
934 )
935 return OCPP16Constants.OCPP_SET_CHARGING_PROFILE_RESPONSE_ACCEPTED
936 }
937
938 private handleRequestGetCompositeSchedule (
939 chargingStation: ChargingStation,
940 commandPayload: OCPP16GetCompositeScheduleRequest
941 ): OCPP16GetCompositeScheduleResponse {
942 if (
943 !OCPP16ServiceUtils.checkFeatureProfile(
944 chargingStation,
945 OCPP16SupportedFeatureProfiles.SmartCharging,
946 OCPP16IncomingRequestCommand.GET_COMPOSITE_SCHEDULE
947 )
948 ) {
949 return OCPP16Constants.OCPP_RESPONSE_REJECTED
950 }
951 const { connectorId, duration, chargingRateUnit } = commandPayload
952 if (!chargingStation.hasConnector(connectorId)) {
953 logger.error(
954 `${chargingStation.logPrefix()} Trying to get composite schedule to a non existing connector id ${connectorId}`
955 )
956 return OCPP16Constants.OCPP_RESPONSE_REJECTED
957 }
958 if (connectorId === 0) {
959 logger.error(
960 `${chargingStation.logPrefix()} Get composite schedule on connector id ${connectorId} is not yet supported`
961 )
962 return OCPP16Constants.OCPP_RESPONSE_REJECTED
963 }
964 if (chargingRateUnit != null) {
965 logger.warn(
966 `${chargingStation.logPrefix()} Get composite schedule with a specified rate unit is not yet supported, no conversion will be done`
967 )
968 }
969 const connectorStatus = chargingStation.getConnectorStatus(connectorId)
970 if (
971 isEmpty(connectorStatus?.chargingProfiles) &&
972 isEmpty(chargingStation.getConnectorStatus(0)?.chargingProfiles)
973 ) {
974 return OCPP16Constants.OCPP_RESPONSE_REJECTED
975 }
976 const currentDate = new Date()
977 const compositeScheduleInterval: Interval = {
978 start: currentDate,
979 end: addSeconds(currentDate, duration)
980 }
981 // Get charging profiles sorted by connector id then stack level
982 const chargingProfiles: OCPP16ChargingProfile[] = getConnectorChargingProfiles(
983 chargingStation,
984 connectorId
985 )
986 let previousCompositeSchedule: OCPP16ChargingSchedule | undefined
987 let compositeSchedule: OCPP16ChargingSchedule | undefined
988 for (const chargingProfile of chargingProfiles) {
989 if (chargingProfile.chargingSchedule.startSchedule == null) {
990 logger.debug(
991 `${chargingStation.logPrefix()} ${moduleName}.handleRequestGetCompositeSchedule: Charging profile id ${
992 chargingProfile.chargingProfileId
993 } has no startSchedule defined. Trying to set it to the connector current transaction start date`
994 )
995 // OCPP specifies that if startSchedule is not defined, it should be relative to start of the connector transaction
996 chargingProfile.chargingSchedule.startSchedule = connectorStatus?.transactionStart
997 }
998 if (!isDate(chargingProfile.chargingSchedule.startSchedule)) {
999 logger.warn(
1000 `${chargingStation.logPrefix()} ${moduleName}.handleRequestGetCompositeSchedule: Charging profile id ${
1001 chargingProfile.chargingProfileId
1002 } startSchedule property is not a Date instance. Trying to convert it to a Date instance`
1003 )
1004 chargingProfile.chargingSchedule.startSchedule = convertToDate(
1005 chargingProfile.chargingSchedule.startSchedule
1006 )
1007 }
1008 if (chargingProfile.chargingSchedule.duration == null) {
1009 logger.debug(
1010 `${chargingStation.logPrefix()} ${moduleName}.handleRequestGetCompositeSchedule: Charging profile id ${
1011 chargingProfile.chargingProfileId
1012 } has no duration defined and will be set to the maximum time allowed`
1013 )
1014 // OCPP specifies that if duration is not defined, it should be infinite
1015 chargingProfile.chargingSchedule.duration = differenceInSeconds(
1016 maxTime,
1017 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1018 chargingProfile.chargingSchedule.startSchedule!
1019 )
1020 }
1021 if (
1022 !prepareChargingProfileKind(
1023 connectorStatus,
1024 chargingProfile,
1025 compositeScheduleInterval.start,
1026 chargingStation.logPrefix()
1027 )
1028 ) {
1029 continue
1030 }
1031 if (
1032 !canProceedChargingProfile(
1033 chargingProfile,
1034 compositeScheduleInterval.start,
1035 chargingStation.logPrefix()
1036 )
1037 ) {
1038 continue
1039 }
1040 compositeSchedule = OCPP16ServiceUtils.composeChargingSchedules(
1041 previousCompositeSchedule,
1042 chargingProfile.chargingSchedule,
1043 compositeScheduleInterval
1044 )
1045 previousCompositeSchedule = compositeSchedule
1046 }
1047 if (compositeSchedule != null) {
1048 return {
1049 status: GenericStatus.Accepted,
1050 scheduleStart: compositeSchedule.startSchedule,
1051 connectorId,
1052 chargingSchedule: compositeSchedule
1053 }
1054 }
1055 return OCPP16Constants.OCPP_RESPONSE_REJECTED
1056 }
1057
1058 private handleRequestClearChargingProfile (
1059 chargingStation: ChargingStation,
1060 commandPayload: OCPP16ClearChargingProfileRequest
1061 ): OCPP16ClearChargingProfileResponse {
1062 if (
1063 !OCPP16ServiceUtils.checkFeatureProfile(
1064 chargingStation,
1065 OCPP16SupportedFeatureProfiles.SmartCharging,
1066 OCPP16IncomingRequestCommand.CLEAR_CHARGING_PROFILE
1067 )
1068 ) {
1069 return OCPP16Constants.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_UNKNOWN
1070 }
1071 const { connectorId } = commandPayload
1072 if (connectorId != null) {
1073 if (!chargingStation.hasConnector(connectorId)) {
1074 logger.error(
1075 `${chargingStation.logPrefix()} Trying to clear a charging profile(s) to a non existing connector id ${connectorId}`
1076 )
1077 return OCPP16Constants.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_UNKNOWN
1078 }
1079 const connectorStatus = chargingStation.getConnectorStatus(connectorId)
1080 if (isNotEmptyArray(connectorStatus?.chargingProfiles)) {
1081 connectorStatus.chargingProfiles = []
1082 logger.debug(
1083 `${chargingStation.logPrefix()} Charging profile(s) cleared on connector id ${connectorId}`
1084 )
1085 return OCPP16Constants.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_ACCEPTED
1086 }
1087 } else {
1088 let clearedCP = false
1089 if (chargingStation.hasEvses) {
1090 for (const evseStatus of chargingStation.evses.values()) {
1091 for (const status of evseStatus.connectors.values()) {
1092 const clearedConnectorCP = OCPP16ServiceUtils.clearChargingProfiles(
1093 chargingStation,
1094 commandPayload,
1095 status.chargingProfiles
1096 )
1097 if (clearedConnectorCP && !clearedCP) {
1098 clearedCP = true
1099 }
1100 }
1101 }
1102 } else {
1103 for (const id of chargingStation.connectors.keys()) {
1104 const clearedConnectorCP = OCPP16ServiceUtils.clearChargingProfiles(
1105 chargingStation,
1106 commandPayload,
1107 chargingStation.getConnectorStatus(id)?.chargingProfiles
1108 )
1109 if (clearedConnectorCP && !clearedCP) {
1110 clearedCP = true
1111 }
1112 }
1113 }
1114 if (clearedCP) {
1115 return OCPP16Constants.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_ACCEPTED
1116 }
1117 }
1118 return OCPP16Constants.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_UNKNOWN
1119 }
1120
1121 private async handleRequestChangeAvailability (
1122 chargingStation: ChargingStation,
1123 commandPayload: OCPP16ChangeAvailabilityRequest
1124 ): Promise<OCPP16ChangeAvailabilityResponse> {
1125 const { connectorId, type } = commandPayload
1126 if (!chargingStation.hasConnector(connectorId)) {
1127 logger.error(
1128 `${chargingStation.logPrefix()} Trying to change the availability of a non existing connector id ${connectorId}`
1129 )
1130 return OCPP16Constants.OCPP_AVAILABILITY_RESPONSE_REJECTED
1131 }
1132 const chargePointStatus: OCPP16ChargePointStatus =
1133 type === OCPP16AvailabilityType.Operative
1134 ? OCPP16ChargePointStatus.Available
1135 : OCPP16ChargePointStatus.Unavailable
1136 if (connectorId === 0) {
1137 let response: OCPP16ChangeAvailabilityResponse | undefined
1138 if (chargingStation.hasEvses) {
1139 for (const evseStatus of chargingStation.evses.values()) {
1140 response = await OCPP16ServiceUtils.changeAvailability(
1141 chargingStation,
1142 [...evseStatus.connectors.keys()],
1143 chargePointStatus,
1144 type
1145 )
1146 }
1147 } else {
1148 response = await OCPP16ServiceUtils.changeAvailability(
1149 chargingStation,
1150 [...chargingStation.connectors.keys()],
1151 chargePointStatus,
1152 type
1153 )
1154 }
1155 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1156 return response!
1157 } else if (
1158 connectorId > 0 &&
1159 (chargingStation.isChargingStationAvailable() ||
1160 (!chargingStation.isChargingStationAvailable() &&
1161 type === OCPP16AvailabilityType.Inoperative))
1162 ) {
1163 if (chargingStation.getConnectorStatus(connectorId)?.transactionStarted === true) {
1164 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1165 chargingStation.getConnectorStatus(connectorId)!.availability = type
1166 return OCPP16Constants.OCPP_AVAILABILITY_RESPONSE_SCHEDULED
1167 }
1168 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1169 chargingStation.getConnectorStatus(connectorId)!.availability = type
1170 await OCPP16ServiceUtils.sendAndSetConnectorStatus(
1171 chargingStation,
1172 connectorId,
1173 chargePointStatus
1174 )
1175 return OCPP16Constants.OCPP_AVAILABILITY_RESPONSE_ACCEPTED
1176 }
1177 return OCPP16Constants.OCPP_AVAILABILITY_RESPONSE_REJECTED
1178 }
1179
1180 private async handleRequestRemoteStartTransaction (
1181 chargingStation: ChargingStation,
1182 commandPayload: RemoteStartTransactionRequest
1183 ): Promise<GenericResponse> {
1184 if (commandPayload.connectorId == null) {
1185 for (
1186 let connectorId = 1;
1187 connectorId <= chargingStation.getNumberOfConnectors();
1188 connectorId++
1189 ) {
1190 if (
1191 chargingStation.getConnectorStatus(connectorId)?.transactionStarted === false &&
1192 !OCPP16ServiceUtils.hasReservation(chargingStation, connectorId, commandPayload.idTag)
1193 ) {
1194 commandPayload.connectorId = connectorId
1195 break
1196 }
1197 }
1198 if (commandPayload.connectorId == null) {
1199 logger.debug(
1200 `${chargingStation.logPrefix()} Remote start transaction REJECTED on ${
1201 chargingStation.stationInfo?.chargingStationId
1202 }, idTag '${commandPayload.idTag}': no available connector found`
1203 )
1204 return OCPP16Constants.OCPP_RESPONSE_REJECTED
1205 }
1206 }
1207 const { connectorId: transactionConnectorId, idTag, chargingProfile } = commandPayload
1208 if (!chargingStation.hasConnector(transactionConnectorId)) {
1209 return this.notifyRemoteStartTransactionRejected(
1210 chargingStation,
1211 transactionConnectorId,
1212 idTag
1213 )
1214 }
1215 if (
1216 !chargingStation.isChargingStationAvailable() ||
1217 !chargingStation.isConnectorAvailable(transactionConnectorId)
1218 ) {
1219 return this.notifyRemoteStartTransactionRejected(
1220 chargingStation,
1221 transactionConnectorId,
1222 idTag
1223 )
1224 }
1225 // idTag authorization check required
1226 if (
1227 chargingStation.getAuthorizeRemoteTxRequests() &&
1228 !(await OCPP16ServiceUtils.isIdTagAuthorized(chargingStation, transactionConnectorId, idTag))
1229 ) {
1230 return this.notifyRemoteStartTransactionRejected(
1231 chargingStation,
1232 transactionConnectorId,
1233 idTag
1234 )
1235 }
1236 if (
1237 chargingProfile != null &&
1238 !this.setRemoteStartTransactionChargingProfile(
1239 chargingStation,
1240 transactionConnectorId,
1241 chargingProfile
1242 )
1243 ) {
1244 return this.notifyRemoteStartTransactionRejected(
1245 chargingStation,
1246 transactionConnectorId,
1247 idTag
1248 )
1249 }
1250 logger.debug(
1251 `${chargingStation.logPrefix()} Remote start transaction ACCEPTED on ${
1252 chargingStation.stationInfo?.chargingStationId
1253 }#${transactionConnectorId}}, idTag '${idTag}'`
1254 )
1255 return OCPP16Constants.OCPP_RESPONSE_ACCEPTED
1256 }
1257
1258 private notifyRemoteStartTransactionRejected (
1259 chargingStation: ChargingStation,
1260 connectorId: number,
1261 idTag: string
1262 ): GenericResponse {
1263 const connectorStatus = chargingStation.getConnectorStatus(connectorId)
1264 logger.debug(
1265 `${chargingStation.logPrefix()} Remote start transaction REJECTED on ${
1266 chargingStation.stationInfo?.chargingStationId
1267 }#${connectorId}, idTag '${idTag}', availability '${
1268 connectorStatus?.availability
1269 }', status '${connectorStatus?.status}'`
1270 )
1271 return OCPP16Constants.OCPP_RESPONSE_REJECTED
1272 }
1273
1274 private setRemoteStartTransactionChargingProfile (
1275 chargingStation: ChargingStation,
1276 connectorId: number,
1277 chargingProfile: OCPP16ChargingProfile
1278 ): boolean {
1279 if (chargingProfile.chargingProfilePurpose === OCPP16ChargingProfilePurposeType.TX_PROFILE) {
1280 OCPP16ServiceUtils.setChargingProfile(chargingStation, connectorId, chargingProfile)
1281 logger.debug(
1282 `${chargingStation.logPrefix()} Charging profile(s) set at remote start transaction on ${
1283 chargingStation.stationInfo?.chargingStationId
1284 }#${connectorId}`,
1285 chargingProfile
1286 )
1287 return true
1288 }
1289 logger.debug(
1290 `${chargingStation.logPrefix()} Not allowed to set ${
1291 chargingProfile.chargingProfilePurpose
1292 } charging profile(s) at remote start transaction`
1293 )
1294 return false
1295 }
1296
1297 private handleRequestRemoteStopTransaction (
1298 chargingStation: ChargingStation,
1299 commandPayload: RemoteStopTransactionRequest
1300 ): GenericResponse {
1301 const { transactionId } = commandPayload
1302 if (chargingStation.getConnectorIdByTransactionId(transactionId) != null) {
1303 logger.debug(
1304 `${chargingStation.logPrefix()} Remote stop transaction ACCEPTED for transactionId '${transactionId}'`
1305 )
1306 return OCPP16Constants.OCPP_RESPONSE_ACCEPTED
1307 }
1308 logger.debug(
1309 `${chargingStation.logPrefix()} Remote stop transaction REJECTED for transactionId '${transactionId}'`
1310 )
1311 return OCPP16Constants.OCPP_RESPONSE_REJECTED
1312 }
1313
1314 private handleRequestUpdateFirmware (
1315 chargingStation: ChargingStation,
1316 commandPayload: OCPP16UpdateFirmwareRequest
1317 ): OCPP16UpdateFirmwareResponse {
1318 if (
1319 !OCPP16ServiceUtils.checkFeatureProfile(
1320 chargingStation,
1321 OCPP16SupportedFeatureProfiles.FirmwareManagement,
1322 OCPP16IncomingRequestCommand.UPDATE_FIRMWARE
1323 )
1324 ) {
1325 logger.warn(
1326 `${chargingStation.logPrefix()} ${moduleName}.handleRequestUpdateFirmware: Cannot simulate firmware update: feature profile not supported`
1327 )
1328 return OCPP16Constants.OCPP_RESPONSE_EMPTY
1329 }
1330 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1331 commandPayload.retrieveDate = convertToDate(commandPayload.retrieveDate)!
1332 const { retrieveDate } = commandPayload
1333 if (
1334 chargingStation.stationInfo?.firmwareStatus != null &&
1335 chargingStation.stationInfo.firmwareStatus !== OCPP16FirmwareStatus.Installed
1336 ) {
1337 logger.warn(
1338 `${chargingStation.logPrefix()} ${moduleName}.handleRequestUpdateFirmware: Cannot simulate firmware update: firmware update is already in progress`
1339 )
1340 return OCPP16Constants.OCPP_RESPONSE_EMPTY
1341 }
1342 const now = Date.now()
1343 if (retrieveDate.getTime() <= now) {
1344 this.updateFirmwareSimulation(chargingStation).catch(Constants.EMPTY_FUNCTION)
1345 } else {
1346 setTimeout(() => {
1347 this.updateFirmwareSimulation(chargingStation).catch(Constants.EMPTY_FUNCTION)
1348 }, retrieveDate.getTime() - now)
1349 }
1350 return OCPP16Constants.OCPP_RESPONSE_EMPTY
1351 }
1352
1353 private async updateFirmwareSimulation (
1354 chargingStation: ChargingStation,
1355 maxDelay = 30,
1356 minDelay = 15
1357 ): Promise<void> {
1358 if (!checkChargingStation(chargingStation, chargingStation.logPrefix())) {
1359 return
1360 }
1361 if (chargingStation.hasEvses) {
1362 for (const [evseId, evseStatus] of chargingStation.evses) {
1363 if (evseId > 0) {
1364 for (const [connectorId, connectorStatus] of evseStatus.connectors) {
1365 if (connectorStatus.transactionStarted === false) {
1366 await OCPP16ServiceUtils.sendAndSetConnectorStatus(
1367 chargingStation,
1368 connectorId,
1369 OCPP16ChargePointStatus.Unavailable
1370 )
1371 }
1372 }
1373 }
1374 }
1375 } else {
1376 for (const connectorId of chargingStation.connectors.keys()) {
1377 if (
1378 connectorId > 0 &&
1379 chargingStation.getConnectorStatus(connectorId)?.transactionStarted === false
1380 ) {
1381 await OCPP16ServiceUtils.sendAndSetConnectorStatus(
1382 chargingStation,
1383 connectorId,
1384 OCPP16ChargePointStatus.Unavailable
1385 )
1386 }
1387 }
1388 }
1389 await chargingStation.ocppRequestService.requestHandler<
1390 OCPP16FirmwareStatusNotificationRequest,
1391 OCPP16FirmwareStatusNotificationResponse
1392 >(chargingStation, OCPP16RequestCommand.FIRMWARE_STATUS_NOTIFICATION, {
1393 status: OCPP16FirmwareStatus.Downloading
1394 })
1395 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1396 chargingStation.stationInfo!.firmwareStatus = OCPP16FirmwareStatus.Downloading
1397 if (
1398 chargingStation.stationInfo?.firmwareUpgrade?.failureStatus ===
1399 OCPP16FirmwareStatus.DownloadFailed
1400 ) {
1401 await sleep(secondsToMilliseconds(randomInt(minDelay, maxDelay)))
1402 await chargingStation.ocppRequestService.requestHandler<
1403 OCPP16FirmwareStatusNotificationRequest,
1404 OCPP16FirmwareStatusNotificationResponse
1405 >(chargingStation, OCPP16RequestCommand.FIRMWARE_STATUS_NOTIFICATION, {
1406 status: chargingStation.stationInfo.firmwareUpgrade.failureStatus
1407 })
1408 chargingStation.stationInfo.firmwareStatus =
1409 chargingStation.stationInfo.firmwareUpgrade.failureStatus
1410 return
1411 }
1412 await sleep(secondsToMilliseconds(randomInt(minDelay, maxDelay)))
1413 await chargingStation.ocppRequestService.requestHandler<
1414 OCPP16FirmwareStatusNotificationRequest,
1415 OCPP16FirmwareStatusNotificationResponse
1416 >(chargingStation, OCPP16RequestCommand.FIRMWARE_STATUS_NOTIFICATION, {
1417 status: OCPP16FirmwareStatus.Downloaded
1418 })
1419 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1420 chargingStation.stationInfo!.firmwareStatus = OCPP16FirmwareStatus.Downloaded
1421 let wasTransactionsStarted = false
1422 let transactionsStarted: boolean
1423 do {
1424 const runningTransactions = chargingStation.getNumberOfRunningTransactions()
1425 if (runningTransactions > 0) {
1426 const waitTime = secondsToMilliseconds(15)
1427 logger.debug(
1428 `${chargingStation.logPrefix()} ${moduleName}.updateFirmwareSimulation: ${runningTransactions} transaction(s) in progress, waiting ${formatDurationMilliSeconds(
1429 waitTime
1430 )} before continuing firmware update simulation`
1431 )
1432 await sleep(waitTime)
1433 transactionsStarted = true
1434 wasTransactionsStarted = true
1435 } else {
1436 if (chargingStation.hasEvses) {
1437 for (const [evseId, evseStatus] of chargingStation.evses) {
1438 if (evseId > 0) {
1439 for (const [connectorId, connectorStatus] of evseStatus.connectors) {
1440 if (connectorStatus.status !== OCPP16ChargePointStatus.Unavailable) {
1441 await OCPP16ServiceUtils.sendAndSetConnectorStatus(
1442 chargingStation,
1443 connectorId,
1444 OCPP16ChargePointStatus.Unavailable
1445 )
1446 }
1447 }
1448 }
1449 }
1450 } else {
1451 for (const connectorId of chargingStation.connectors.keys()) {
1452 if (
1453 connectorId > 0 &&
1454 chargingStation.getConnectorStatus(connectorId)?.status !==
1455 OCPP16ChargePointStatus.Unavailable
1456 ) {
1457 await OCPP16ServiceUtils.sendAndSetConnectorStatus(
1458 chargingStation,
1459 connectorId,
1460 OCPP16ChargePointStatus.Unavailable
1461 )
1462 }
1463 }
1464 }
1465 transactionsStarted = false
1466 }
1467 } while (transactionsStarted)
1468 !wasTransactionsStarted && (await sleep(secondsToMilliseconds(randomInt(minDelay, maxDelay))))
1469 if (!checkChargingStation(chargingStation, chargingStation.logPrefix())) {
1470 return
1471 }
1472 await chargingStation.ocppRequestService.requestHandler<
1473 OCPP16FirmwareStatusNotificationRequest,
1474 OCPP16FirmwareStatusNotificationResponse
1475 >(chargingStation, OCPP16RequestCommand.FIRMWARE_STATUS_NOTIFICATION, {
1476 status: OCPP16FirmwareStatus.Installing
1477 })
1478 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1479 chargingStation.stationInfo!.firmwareStatus = OCPP16FirmwareStatus.Installing
1480 if (
1481 chargingStation.stationInfo?.firmwareUpgrade?.failureStatus ===
1482 OCPP16FirmwareStatus.InstallationFailed
1483 ) {
1484 await sleep(secondsToMilliseconds(randomInt(minDelay, maxDelay)))
1485 await chargingStation.ocppRequestService.requestHandler<
1486 OCPP16FirmwareStatusNotificationRequest,
1487 OCPP16FirmwareStatusNotificationResponse
1488 >(chargingStation, OCPP16RequestCommand.FIRMWARE_STATUS_NOTIFICATION, {
1489 status: chargingStation.stationInfo.firmwareUpgrade.failureStatus
1490 })
1491 chargingStation.stationInfo.firmwareStatus =
1492 chargingStation.stationInfo.firmwareUpgrade.failureStatus
1493 return
1494 }
1495 if (chargingStation.stationInfo?.firmwareUpgrade?.reset === true) {
1496 await sleep(secondsToMilliseconds(randomInt(minDelay, maxDelay)))
1497 await chargingStation.reset(OCPP16StopTransactionReason.REBOOT)
1498 }
1499 }
1500
1501 private async handleRequestGetDiagnostics (
1502 chargingStation: ChargingStation,
1503 commandPayload: GetDiagnosticsRequest
1504 ): Promise<GetDiagnosticsResponse> {
1505 if (
1506 !OCPP16ServiceUtils.checkFeatureProfile(
1507 chargingStation,
1508 OCPP16SupportedFeatureProfiles.FirmwareManagement,
1509 OCPP16IncomingRequestCommand.GET_DIAGNOSTICS
1510 )
1511 ) {
1512 logger.warn(
1513 `${chargingStation.logPrefix()} ${moduleName}.handleRequestGetDiagnostics: Cannot get diagnostics: feature profile not supported`
1514 )
1515 return OCPP16Constants.OCPP_RESPONSE_EMPTY
1516 }
1517 const { location } = commandPayload
1518 const uri = new URL(location)
1519 if (uri.protocol.startsWith('ftp:')) {
1520 let ftpClient: Client | undefined
1521 try {
1522 const logConfiguration = Configuration.getConfigurationSection<LogConfiguration>(
1523 ConfigurationSection.log
1524 )
1525 const logFiles = readdirSync(
1526 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1527 resolve((fileURLToPath(import.meta.url), '../', dirname(logConfiguration.file!)))
1528 )
1529 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1530 .filter(file => file.endsWith(extname(logConfiguration.file!)))
1531 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1532 .map(file => join(dirname(logConfiguration.file!), file))
1533 const diagnosticsArchive = `${chargingStation.stationInfo?.chargingStationId}_logs.tar.gz`
1534 create({ gzip: true }, logFiles).pipe(createWriteStream(diagnosticsArchive))
1535 ftpClient = new Client()
1536 const accessResponse = await ftpClient.access({
1537 host: uri.hostname,
1538 ...(isNotEmptyString(uri.port) && { port: convertToInt(uri.port) }),
1539 ...(isNotEmptyString(uri.username) && { user: uri.username }),
1540 ...(isNotEmptyString(uri.password) && { password: uri.password })
1541 })
1542 let uploadResponse: FTPResponse | undefined
1543 if (accessResponse.code === 220) {
1544 ftpClient.trackProgress(info => {
1545 logger.info(
1546 `${chargingStation.logPrefix()} ${moduleName}.handleRequestGetDiagnostics: ${
1547 info.bytes / 1024
1548 } bytes transferred from diagnostics archive ${info.name}`
1549 )
1550 chargingStation.ocppRequestService
1551 .requestHandler<
1552 OCPP16DiagnosticsStatusNotificationRequest,
1553 OCPP16DiagnosticsStatusNotificationResponse
1554 >(chargingStation, OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION, {
1555 status: OCPP16DiagnosticsStatus.Uploading
1556 })
1557 .catch((error: unknown) => {
1558 logger.error(
1559 `${chargingStation.logPrefix()} ${moduleName}.handleRequestGetDiagnostics: Error while sending '${
1560 OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION
1561 }'`,
1562 error
1563 )
1564 })
1565 })
1566 uploadResponse = await ftpClient.uploadFrom(
1567 join(resolve(dirname(fileURLToPath(import.meta.url)), '../'), diagnosticsArchive),
1568 `${uri.pathname}${diagnosticsArchive}`
1569 )
1570 if (uploadResponse.code === 226) {
1571 await chargingStation.ocppRequestService.requestHandler<
1572 OCPP16DiagnosticsStatusNotificationRequest,
1573 OCPP16DiagnosticsStatusNotificationResponse
1574 >(chargingStation, OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION, {
1575 status: OCPP16DiagnosticsStatus.Uploaded
1576 })
1577 ftpClient.close()
1578 return { fileName: diagnosticsArchive }
1579 }
1580 throw new OCPPError(
1581 ErrorType.GENERIC_ERROR,
1582 `Diagnostics transfer failed with error code ${accessResponse.code}|${uploadResponse.code}`,
1583 OCPP16IncomingRequestCommand.GET_DIAGNOSTICS
1584 )
1585 }
1586 throw new OCPPError(
1587 ErrorType.GENERIC_ERROR,
1588 `Diagnostics transfer failed with error code ${accessResponse.code}|${uploadResponse?.code}`,
1589 OCPP16IncomingRequestCommand.GET_DIAGNOSTICS
1590 )
1591 } catch (error) {
1592 await chargingStation.ocppRequestService.requestHandler<
1593 OCPP16DiagnosticsStatusNotificationRequest,
1594 OCPP16DiagnosticsStatusNotificationResponse
1595 >(chargingStation, OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION, {
1596 status: OCPP16DiagnosticsStatus.UploadFailed
1597 })
1598 ftpClient?.close()
1599 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1600 return handleIncomingRequestError<GetDiagnosticsResponse>(
1601 chargingStation,
1602 OCPP16IncomingRequestCommand.GET_DIAGNOSTICS,
1603 error as Error,
1604 { errorResponse: OCPP16Constants.OCPP_RESPONSE_EMPTY }
1605 )!
1606 }
1607 } else {
1608 logger.error(
1609 `${chargingStation.logPrefix()} Unsupported protocol ${
1610 uri.protocol
1611 } to transfer the diagnostic logs archive`
1612 )
1613 await chargingStation.ocppRequestService.requestHandler<
1614 OCPP16DiagnosticsStatusNotificationRequest,
1615 OCPP16DiagnosticsStatusNotificationResponse
1616 >(chargingStation, OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION, {
1617 status: OCPP16DiagnosticsStatus.UploadFailed
1618 })
1619 return OCPP16Constants.OCPP_RESPONSE_EMPTY
1620 }
1621 }
1622
1623 private handleRequestTriggerMessage (
1624 chargingStation: ChargingStation,
1625 commandPayload: OCPP16TriggerMessageRequest
1626 ): OCPP16TriggerMessageResponse {
1627 const { requestedMessage, connectorId } = commandPayload
1628 if (
1629 !OCPP16ServiceUtils.checkFeatureProfile(
1630 chargingStation,
1631 OCPP16SupportedFeatureProfiles.RemoteTrigger,
1632 OCPP16IncomingRequestCommand.TRIGGER_MESSAGE
1633 ) ||
1634 !OCPP16ServiceUtils.isMessageTriggerSupported(chargingStation, requestedMessage)
1635 ) {
1636 return OCPP16Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_NOT_IMPLEMENTED
1637 }
1638 if (
1639 !OCPP16ServiceUtils.isConnectorIdValid(
1640 chargingStation,
1641 OCPP16IncomingRequestCommand.TRIGGER_MESSAGE,
1642 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1643 connectorId!
1644 )
1645 ) {
1646 return OCPP16Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_REJECTED
1647 }
1648 switch (requestedMessage) {
1649 case OCPP16MessageTrigger.BootNotification:
1650 case OCPP16MessageTrigger.Heartbeat:
1651 case OCPP16MessageTrigger.StatusNotification:
1652 return OCPP16Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_ACCEPTED
1653 default:
1654 return OCPP16Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_NOT_IMPLEMENTED
1655 }
1656 }
1657
1658 private handleRequestDataTransfer (
1659 chargingStation: ChargingStation,
1660 commandPayload: OCPP16DataTransferRequest
1661 ): OCPP16DataTransferResponse {
1662 const { vendorId } = commandPayload
1663 try {
1664 if (Object.values(OCPP16DataTransferVendorId).includes(vendorId)) {
1665 return OCPP16Constants.OCPP_DATA_TRANSFER_RESPONSE_ACCEPTED
1666 }
1667 return OCPP16Constants.OCPP_DATA_TRANSFER_RESPONSE_UNKNOWN_VENDOR_ID
1668 } catch (error) {
1669 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1670 return handleIncomingRequestError<OCPP16DataTransferResponse>(
1671 chargingStation,
1672 OCPP16IncomingRequestCommand.DATA_TRANSFER,
1673 error as Error,
1674 { errorResponse: OCPP16Constants.OCPP_DATA_TRANSFER_RESPONSE_REJECTED }
1675 )!
1676 }
1677 }
1678
1679 private async handleRequestReserveNow (
1680 chargingStation: ChargingStation,
1681 commandPayload: OCPP16ReserveNowRequest
1682 ): Promise<OCPP16ReserveNowResponse> {
1683 if (
1684 !OCPP16ServiceUtils.checkFeatureProfile(
1685 chargingStation,
1686 OCPP16SupportedFeatureProfiles.Reservation,
1687 OCPP16IncomingRequestCommand.RESERVE_NOW
1688 )
1689 ) {
1690 return OCPP16Constants.OCPP_RESERVATION_RESPONSE_REJECTED
1691 }
1692 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1693 commandPayload.expiryDate = convertToDate(commandPayload.expiryDate)!
1694 const { reservationId, idTag, connectorId } = commandPayload
1695 if (!chargingStation.hasConnector(connectorId)) {
1696 logger.error(
1697 `${chargingStation.logPrefix()} Trying to reserve a non existing connector id ${connectorId}`
1698 )
1699 return OCPP16Constants.OCPP_RESERVATION_RESPONSE_REJECTED
1700 }
1701 if (connectorId > 0 && !chargingStation.isConnectorAvailable(connectorId)) {
1702 return OCPP16Constants.OCPP_RESERVATION_RESPONSE_REJECTED
1703 }
1704 if (connectorId === 0 && !chargingStation.getReserveConnectorZeroSupported()) {
1705 return OCPP16Constants.OCPP_RESERVATION_RESPONSE_REJECTED
1706 }
1707 if (!(await OCPP16ServiceUtils.isIdTagAuthorized(chargingStation, connectorId, idTag))) {
1708 return OCPP16Constants.OCPP_RESERVATION_RESPONSE_REJECTED
1709 }
1710 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1711 const connectorStatus = chargingStation.getConnectorStatus(connectorId)!
1712 resetAuthorizeConnectorStatus(connectorStatus)
1713 let response: OCPP16ReserveNowResponse
1714 try {
1715 await removeExpiredReservations(chargingStation)
1716 switch (connectorStatus.status) {
1717 case OCPP16ChargePointStatus.Faulted:
1718 response = OCPP16Constants.OCPP_RESERVATION_RESPONSE_FAULTED
1719 break
1720 case OCPP16ChargePointStatus.Preparing:
1721 case OCPP16ChargePointStatus.Charging:
1722 case OCPP16ChargePointStatus.SuspendedEV:
1723 case OCPP16ChargePointStatus.SuspendedEVSE:
1724 case OCPP16ChargePointStatus.Finishing:
1725 response = OCPP16Constants.OCPP_RESERVATION_RESPONSE_OCCUPIED
1726 break
1727 case OCPP16ChargePointStatus.Unavailable:
1728 response = OCPP16Constants.OCPP_RESERVATION_RESPONSE_UNAVAILABLE
1729 break
1730 case OCPP16ChargePointStatus.Reserved:
1731 if (!chargingStation.isConnectorReservable(reservationId, idTag, connectorId)) {
1732 response = OCPP16Constants.OCPP_RESERVATION_RESPONSE_OCCUPIED
1733 break
1734 }
1735 // eslint-disable-next-line no-fallthrough
1736 default:
1737 if (!chargingStation.isConnectorReservable(reservationId, idTag)) {
1738 response = OCPP16Constants.OCPP_RESERVATION_RESPONSE_OCCUPIED
1739 break
1740 }
1741 await chargingStation.addReservation({
1742 id: commandPayload.reservationId,
1743 ...commandPayload
1744 })
1745 response = OCPP16Constants.OCPP_RESERVATION_RESPONSE_ACCEPTED
1746 break
1747 }
1748 return response
1749 } catch (error) {
1750 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1751 chargingStation.getConnectorStatus(connectorId)!.status = OCPP16ChargePointStatus.Available
1752 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1753 return handleIncomingRequestError<OCPP16ReserveNowResponse>(
1754 chargingStation,
1755 OCPP16IncomingRequestCommand.RESERVE_NOW,
1756 error as Error,
1757 { errorResponse: OCPP16Constants.OCPP_RESERVATION_RESPONSE_FAULTED }
1758 )!
1759 }
1760 }
1761
1762 private async handleRequestCancelReservation (
1763 chargingStation: ChargingStation,
1764 commandPayload: OCPP16CancelReservationRequest
1765 ): Promise<GenericResponse> {
1766 if (
1767 !OCPP16ServiceUtils.checkFeatureProfile(
1768 chargingStation,
1769 OCPP16SupportedFeatureProfiles.Reservation,
1770 OCPP16IncomingRequestCommand.CANCEL_RESERVATION
1771 )
1772 ) {
1773 return OCPP16Constants.OCPP_CANCEL_RESERVATION_RESPONSE_REJECTED
1774 }
1775 try {
1776 const { reservationId } = commandPayload
1777 const reservation = chargingStation.getReservationBy('reservationId', reservationId)
1778 if (reservation == null) {
1779 logger.debug(
1780 `${chargingStation.logPrefix()} Reservation with id ${reservationId} does not exist on charging station`
1781 )
1782 return OCPP16Constants.OCPP_CANCEL_RESERVATION_RESPONSE_REJECTED
1783 }
1784 await chargingStation.removeReservation(
1785 reservation,
1786 ReservationTerminationReason.RESERVATION_CANCELED
1787 )
1788 return OCPP16Constants.OCPP_CANCEL_RESERVATION_RESPONSE_ACCEPTED
1789 } catch (error) {
1790 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1791 return handleIncomingRequestError<GenericResponse>(
1792 chargingStation,
1793 OCPP16IncomingRequestCommand.CANCEL_RESERVATION,
1794 error as Error,
1795 {
1796 errorResponse: OCPP16Constants.OCPP_CANCEL_RESERVATION_RESPONSE_REJECTED
1797 }
1798 )!
1799 }
1800 }
1801 }