Merge branch 'main' into issue39-ocpp2
[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 != null &&
922 csChargingProfiles.transactionId !== connectorStatus.transactionId
923 ) {
924 logger.error(
925 `${chargingStation.logPrefix()} Trying to set transaction charging profile(s) on connector ${connectorId} with a different transaction id ${
926 csChargingProfiles.transactionId
927 } than the started transaction id ${connectorStatus.transactionId}`
928 )
929 return OCPP16Constants.OCPP_SET_CHARGING_PROFILE_RESPONSE_REJECTED
930 }
931 OCPP16ServiceUtils.setChargingProfile(chargingStation, connectorId, csChargingProfiles)
932 logger.debug(
933 `${chargingStation.logPrefix()} Charging profile(s) set on connector id ${connectorId}: %j`,
934 csChargingProfiles
935 )
936 return OCPP16Constants.OCPP_SET_CHARGING_PROFILE_RESPONSE_ACCEPTED
937 }
938
939 private handleRequestGetCompositeSchedule (
940 chargingStation: ChargingStation,
941 commandPayload: OCPP16GetCompositeScheduleRequest
942 ): OCPP16GetCompositeScheduleResponse {
943 if (
944 !OCPP16ServiceUtils.checkFeatureProfile(
945 chargingStation,
946 OCPP16SupportedFeatureProfiles.SmartCharging,
947 OCPP16IncomingRequestCommand.GET_COMPOSITE_SCHEDULE
948 )
949 ) {
950 return OCPP16Constants.OCPP_RESPONSE_REJECTED
951 }
952 const { connectorId, duration, chargingRateUnit } = commandPayload
953 if (!chargingStation.hasConnector(connectorId)) {
954 logger.error(
955 `${chargingStation.logPrefix()} Trying to get composite schedule to a non existing connector id ${connectorId}`
956 )
957 return OCPP16Constants.OCPP_RESPONSE_REJECTED
958 }
959 if (connectorId === 0) {
960 logger.error(
961 `${chargingStation.logPrefix()} Get composite schedule on connector id ${connectorId} is not yet supported`
962 )
963 return OCPP16Constants.OCPP_RESPONSE_REJECTED
964 }
965 if (chargingRateUnit != null) {
966 logger.warn(
967 `${chargingStation.logPrefix()} Get composite schedule with a specified rate unit is not yet supported, no conversion will be done`
968 )
969 }
970 const connectorStatus = chargingStation.getConnectorStatus(connectorId)
971 if (
972 isEmpty(connectorStatus?.chargingProfiles) &&
973 isEmpty(chargingStation.getConnectorStatus(0)?.chargingProfiles)
974 ) {
975 return OCPP16Constants.OCPP_RESPONSE_REJECTED
976 }
977 const currentDate = new Date()
978 const compositeScheduleInterval: Interval = {
979 start: currentDate,
980 end: addSeconds(currentDate, duration)
981 }
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 (
1280 chargingProfile.chargingProfilePurpose === OCPP16ChargingProfilePurposeType.TX_PROFILE &&
1281 chargingProfile.transactionId == null
1282 ) {
1283 OCPP16ServiceUtils.setChargingProfile(chargingStation, connectorId, chargingProfile)
1284 logger.debug(
1285 `${chargingStation.logPrefix()} Charging profile(s) set at remote start transaction on ${
1286 chargingStation.stationInfo?.chargingStationId
1287 }#${connectorId}`,
1288 chargingProfile
1289 )
1290 return true
1291 }
1292 logger.debug(
1293 `${chargingStation.logPrefix()} Not allowed to set ${
1294 chargingProfile.chargingProfilePurpose
1295 } charging profile(s)${chargingProfile.transactionId != null ? ' with transactionId set' : ''} at remote start transaction`
1296 )
1297 return false
1298 }
1299
1300 private handleRequestRemoteStopTransaction (
1301 chargingStation: ChargingStation,
1302 commandPayload: RemoteStopTransactionRequest
1303 ): GenericResponse {
1304 const { transactionId } = commandPayload
1305 if (chargingStation.getConnectorIdByTransactionId(transactionId) != null) {
1306 logger.debug(
1307 `${chargingStation.logPrefix()} Remote stop transaction ACCEPTED for transactionId '${transactionId}'`
1308 )
1309 return OCPP16Constants.OCPP_RESPONSE_ACCEPTED
1310 }
1311 logger.debug(
1312 `${chargingStation.logPrefix()} Remote stop transaction REJECTED for transactionId '${transactionId}'`
1313 )
1314 return OCPP16Constants.OCPP_RESPONSE_REJECTED
1315 }
1316
1317 private handleRequestUpdateFirmware (
1318 chargingStation: ChargingStation,
1319 commandPayload: OCPP16UpdateFirmwareRequest
1320 ): OCPP16UpdateFirmwareResponse {
1321 if (
1322 !OCPP16ServiceUtils.checkFeatureProfile(
1323 chargingStation,
1324 OCPP16SupportedFeatureProfiles.FirmwareManagement,
1325 OCPP16IncomingRequestCommand.UPDATE_FIRMWARE
1326 )
1327 ) {
1328 logger.warn(
1329 `${chargingStation.logPrefix()} ${moduleName}.handleRequestUpdateFirmware: Cannot simulate firmware update: feature profile not supported`
1330 )
1331 return OCPP16Constants.OCPP_RESPONSE_EMPTY
1332 }
1333 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1334 commandPayload.retrieveDate = convertToDate(commandPayload.retrieveDate)!
1335 const { retrieveDate } = commandPayload
1336 if (
1337 chargingStation.stationInfo?.firmwareStatus != null &&
1338 chargingStation.stationInfo.firmwareStatus !== OCPP16FirmwareStatus.Installed
1339 ) {
1340 logger.warn(
1341 `${chargingStation.logPrefix()} ${moduleName}.handleRequestUpdateFirmware: Cannot simulate firmware update: firmware update is already in progress`
1342 )
1343 return OCPP16Constants.OCPP_RESPONSE_EMPTY
1344 }
1345 const now = Date.now()
1346 if (retrieveDate.getTime() <= now) {
1347 this.updateFirmwareSimulation(chargingStation).catch(Constants.EMPTY_FUNCTION)
1348 } else {
1349 setTimeout(() => {
1350 this.updateFirmwareSimulation(chargingStation).catch(Constants.EMPTY_FUNCTION)
1351 }, retrieveDate.getTime() - now)
1352 }
1353 return OCPP16Constants.OCPP_RESPONSE_EMPTY
1354 }
1355
1356 private async updateFirmwareSimulation (
1357 chargingStation: ChargingStation,
1358 maxDelay = 30,
1359 minDelay = 15
1360 ): Promise<void> {
1361 if (!checkChargingStation(chargingStation, chargingStation.logPrefix())) {
1362 return
1363 }
1364 if (chargingStation.hasEvses) {
1365 for (const [evseId, evseStatus] of chargingStation.evses) {
1366 if (evseId > 0) {
1367 for (const [connectorId, connectorStatus] of evseStatus.connectors) {
1368 if (connectorStatus.transactionStarted === false) {
1369 await OCPP16ServiceUtils.sendAndSetConnectorStatus(
1370 chargingStation,
1371 connectorId,
1372 OCPP16ChargePointStatus.Unavailable
1373 )
1374 }
1375 }
1376 }
1377 }
1378 } else {
1379 for (const connectorId of chargingStation.connectors.keys()) {
1380 if (
1381 connectorId > 0 &&
1382 chargingStation.getConnectorStatus(connectorId)?.transactionStarted === false
1383 ) {
1384 await OCPP16ServiceUtils.sendAndSetConnectorStatus(
1385 chargingStation,
1386 connectorId,
1387 OCPP16ChargePointStatus.Unavailable
1388 )
1389 }
1390 }
1391 }
1392 await chargingStation.ocppRequestService.requestHandler<
1393 OCPP16FirmwareStatusNotificationRequest,
1394 OCPP16FirmwareStatusNotificationResponse
1395 >(chargingStation, OCPP16RequestCommand.FIRMWARE_STATUS_NOTIFICATION, {
1396 status: OCPP16FirmwareStatus.Downloading
1397 })
1398 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1399 chargingStation.stationInfo!.firmwareStatus = OCPP16FirmwareStatus.Downloading
1400 if (
1401 chargingStation.stationInfo?.firmwareUpgrade?.failureStatus ===
1402 OCPP16FirmwareStatus.DownloadFailed
1403 ) {
1404 await sleep(secondsToMilliseconds(randomInt(minDelay, maxDelay)))
1405 await chargingStation.ocppRequestService.requestHandler<
1406 OCPP16FirmwareStatusNotificationRequest,
1407 OCPP16FirmwareStatusNotificationResponse
1408 >(chargingStation, OCPP16RequestCommand.FIRMWARE_STATUS_NOTIFICATION, {
1409 status: chargingStation.stationInfo.firmwareUpgrade.failureStatus
1410 })
1411 chargingStation.stationInfo.firmwareStatus =
1412 chargingStation.stationInfo.firmwareUpgrade.failureStatus
1413 return
1414 }
1415 await sleep(secondsToMilliseconds(randomInt(minDelay, maxDelay)))
1416 await chargingStation.ocppRequestService.requestHandler<
1417 OCPP16FirmwareStatusNotificationRequest,
1418 OCPP16FirmwareStatusNotificationResponse
1419 >(chargingStation, OCPP16RequestCommand.FIRMWARE_STATUS_NOTIFICATION, {
1420 status: OCPP16FirmwareStatus.Downloaded
1421 })
1422 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1423 chargingStation.stationInfo!.firmwareStatus = OCPP16FirmwareStatus.Downloaded
1424 let wasTransactionsStarted = false
1425 let transactionsStarted: boolean
1426 do {
1427 const runningTransactions = chargingStation.getNumberOfRunningTransactions()
1428 if (runningTransactions > 0) {
1429 const waitTime = secondsToMilliseconds(15)
1430 logger.debug(
1431 `${chargingStation.logPrefix()} ${moduleName}.updateFirmwareSimulation: ${runningTransactions} transaction(s) in progress, waiting ${formatDurationMilliSeconds(
1432 waitTime
1433 )} before continuing firmware update simulation`
1434 )
1435 await sleep(waitTime)
1436 transactionsStarted = true
1437 wasTransactionsStarted = true
1438 } else {
1439 if (chargingStation.hasEvses) {
1440 for (const [evseId, evseStatus] of chargingStation.evses) {
1441 if (evseId > 0) {
1442 for (const [connectorId, connectorStatus] of evseStatus.connectors) {
1443 if (connectorStatus.status !== OCPP16ChargePointStatus.Unavailable) {
1444 await OCPP16ServiceUtils.sendAndSetConnectorStatus(
1445 chargingStation,
1446 connectorId,
1447 OCPP16ChargePointStatus.Unavailable
1448 )
1449 }
1450 }
1451 }
1452 }
1453 } else {
1454 for (const connectorId of chargingStation.connectors.keys()) {
1455 if (
1456 connectorId > 0 &&
1457 chargingStation.getConnectorStatus(connectorId)?.status !==
1458 OCPP16ChargePointStatus.Unavailable
1459 ) {
1460 await OCPP16ServiceUtils.sendAndSetConnectorStatus(
1461 chargingStation,
1462 connectorId,
1463 OCPP16ChargePointStatus.Unavailable
1464 )
1465 }
1466 }
1467 }
1468 transactionsStarted = false
1469 }
1470 } while (transactionsStarted)
1471 !wasTransactionsStarted && (await sleep(secondsToMilliseconds(randomInt(minDelay, maxDelay))))
1472 if (!checkChargingStation(chargingStation, chargingStation.logPrefix())) {
1473 return
1474 }
1475 await chargingStation.ocppRequestService.requestHandler<
1476 OCPP16FirmwareStatusNotificationRequest,
1477 OCPP16FirmwareStatusNotificationResponse
1478 >(chargingStation, OCPP16RequestCommand.FIRMWARE_STATUS_NOTIFICATION, {
1479 status: OCPP16FirmwareStatus.Installing
1480 })
1481 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1482 chargingStation.stationInfo!.firmwareStatus = OCPP16FirmwareStatus.Installing
1483 if (
1484 chargingStation.stationInfo?.firmwareUpgrade?.failureStatus ===
1485 OCPP16FirmwareStatus.InstallationFailed
1486 ) {
1487 await sleep(secondsToMilliseconds(randomInt(minDelay, maxDelay)))
1488 await chargingStation.ocppRequestService.requestHandler<
1489 OCPP16FirmwareStatusNotificationRequest,
1490 OCPP16FirmwareStatusNotificationResponse
1491 >(chargingStation, OCPP16RequestCommand.FIRMWARE_STATUS_NOTIFICATION, {
1492 status: chargingStation.stationInfo.firmwareUpgrade.failureStatus
1493 })
1494 chargingStation.stationInfo.firmwareStatus =
1495 chargingStation.stationInfo.firmwareUpgrade.failureStatus
1496 return
1497 }
1498 if (chargingStation.stationInfo?.firmwareUpgrade?.reset === true) {
1499 await sleep(secondsToMilliseconds(randomInt(minDelay, maxDelay)))
1500 await chargingStation.reset(OCPP16StopTransactionReason.REBOOT)
1501 }
1502 }
1503
1504 private async handleRequestGetDiagnostics (
1505 chargingStation: ChargingStation,
1506 commandPayload: GetDiagnosticsRequest
1507 ): Promise<GetDiagnosticsResponse> {
1508 if (
1509 !OCPP16ServiceUtils.checkFeatureProfile(
1510 chargingStation,
1511 OCPP16SupportedFeatureProfiles.FirmwareManagement,
1512 OCPP16IncomingRequestCommand.GET_DIAGNOSTICS
1513 )
1514 ) {
1515 logger.warn(
1516 `${chargingStation.logPrefix()} ${moduleName}.handleRequestGetDiagnostics: Cannot get diagnostics: feature profile not supported`
1517 )
1518 return OCPP16Constants.OCPP_RESPONSE_EMPTY
1519 }
1520 const { location } = commandPayload
1521 const uri = new URL(location)
1522 if (uri.protocol.startsWith('ftp:')) {
1523 let ftpClient: Client | undefined
1524 try {
1525 const logConfiguration = Configuration.getConfigurationSection<LogConfiguration>(
1526 ConfigurationSection.log
1527 )
1528 const logFiles = readdirSync(
1529 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1530 resolve((fileURLToPath(import.meta.url), '../', dirname(logConfiguration.file!)))
1531 )
1532 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1533 .filter(file => file.endsWith(extname(logConfiguration.file!)))
1534 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1535 .map(file => join(dirname(logConfiguration.file!), file))
1536 const diagnosticsArchive = `${chargingStation.stationInfo?.chargingStationId}_logs.tar.gz`
1537 create({ gzip: true }, logFiles).pipe(createWriteStream(diagnosticsArchive))
1538 ftpClient = new Client()
1539 const accessResponse = await ftpClient.access({
1540 host: uri.hostname,
1541 ...(isNotEmptyString(uri.port) && { port: convertToInt(uri.port) }),
1542 ...(isNotEmptyString(uri.username) && { user: uri.username }),
1543 ...(isNotEmptyString(uri.password) && { password: uri.password })
1544 })
1545 let uploadResponse: FTPResponse | undefined
1546 if (accessResponse.code === 220) {
1547 ftpClient.trackProgress(info => {
1548 logger.info(
1549 `${chargingStation.logPrefix()} ${moduleName}.handleRequestGetDiagnostics: ${
1550 info.bytes / 1024
1551 } bytes transferred from diagnostics archive ${info.name}`
1552 )
1553 chargingStation.ocppRequestService
1554 .requestHandler<
1555 OCPP16DiagnosticsStatusNotificationRequest,
1556 OCPP16DiagnosticsStatusNotificationResponse
1557 >(chargingStation, OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION, {
1558 status: OCPP16DiagnosticsStatus.Uploading
1559 })
1560 .catch((error: unknown) => {
1561 logger.error(
1562 `${chargingStation.logPrefix()} ${moduleName}.handleRequestGetDiagnostics: Error while sending '${
1563 OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION
1564 }'`,
1565 error
1566 )
1567 })
1568 })
1569 uploadResponse = await ftpClient.uploadFrom(
1570 join(resolve(dirname(fileURLToPath(import.meta.url)), '../'), diagnosticsArchive),
1571 `${uri.pathname}${diagnosticsArchive}`
1572 )
1573 if (uploadResponse.code === 226) {
1574 await chargingStation.ocppRequestService.requestHandler<
1575 OCPP16DiagnosticsStatusNotificationRequest,
1576 OCPP16DiagnosticsStatusNotificationResponse
1577 >(chargingStation, OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION, {
1578 status: OCPP16DiagnosticsStatus.Uploaded
1579 })
1580 ftpClient.close()
1581 return { fileName: diagnosticsArchive }
1582 }
1583 throw new OCPPError(
1584 ErrorType.GENERIC_ERROR,
1585 `Diagnostics transfer failed with error code ${accessResponse.code}|${uploadResponse.code}`,
1586 OCPP16IncomingRequestCommand.GET_DIAGNOSTICS
1587 )
1588 }
1589 throw new OCPPError(
1590 ErrorType.GENERIC_ERROR,
1591 `Diagnostics transfer failed with error code ${accessResponse.code}|${uploadResponse?.code}`,
1592 OCPP16IncomingRequestCommand.GET_DIAGNOSTICS
1593 )
1594 } catch (error) {
1595 await chargingStation.ocppRequestService.requestHandler<
1596 OCPP16DiagnosticsStatusNotificationRequest,
1597 OCPP16DiagnosticsStatusNotificationResponse
1598 >(chargingStation, OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION, {
1599 status: OCPP16DiagnosticsStatus.UploadFailed
1600 })
1601 ftpClient?.close()
1602 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1603 return handleIncomingRequestError<GetDiagnosticsResponse>(
1604 chargingStation,
1605 OCPP16IncomingRequestCommand.GET_DIAGNOSTICS,
1606 error as Error,
1607 { errorResponse: OCPP16Constants.OCPP_RESPONSE_EMPTY }
1608 )!
1609 }
1610 } else {
1611 logger.error(
1612 `${chargingStation.logPrefix()} Unsupported protocol ${
1613 uri.protocol
1614 } to transfer the diagnostic logs archive`
1615 )
1616 await chargingStation.ocppRequestService.requestHandler<
1617 OCPP16DiagnosticsStatusNotificationRequest,
1618 OCPP16DiagnosticsStatusNotificationResponse
1619 >(chargingStation, OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION, {
1620 status: OCPP16DiagnosticsStatus.UploadFailed
1621 })
1622 return OCPP16Constants.OCPP_RESPONSE_EMPTY
1623 }
1624 }
1625
1626 private handleRequestTriggerMessage (
1627 chargingStation: ChargingStation,
1628 commandPayload: OCPP16TriggerMessageRequest
1629 ): OCPP16TriggerMessageResponse {
1630 const { requestedMessage, connectorId } = commandPayload
1631 if (
1632 !OCPP16ServiceUtils.checkFeatureProfile(
1633 chargingStation,
1634 OCPP16SupportedFeatureProfiles.RemoteTrigger,
1635 OCPP16IncomingRequestCommand.TRIGGER_MESSAGE
1636 ) ||
1637 !OCPP16ServiceUtils.isMessageTriggerSupported(chargingStation, requestedMessage)
1638 ) {
1639 return OCPP16Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_NOT_IMPLEMENTED
1640 }
1641 if (
1642 !OCPP16ServiceUtils.isConnectorIdValid(
1643 chargingStation,
1644 OCPP16IncomingRequestCommand.TRIGGER_MESSAGE,
1645 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1646 connectorId!
1647 )
1648 ) {
1649 return OCPP16Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_REJECTED
1650 }
1651 switch (requestedMessage) {
1652 case OCPP16MessageTrigger.BootNotification:
1653 case OCPP16MessageTrigger.Heartbeat:
1654 case OCPP16MessageTrigger.StatusNotification:
1655 return OCPP16Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_ACCEPTED
1656 default:
1657 return OCPP16Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_NOT_IMPLEMENTED
1658 }
1659 }
1660
1661 private handleRequestDataTransfer (
1662 chargingStation: ChargingStation,
1663 commandPayload: OCPP16DataTransferRequest
1664 ): OCPP16DataTransferResponse {
1665 const { vendorId } = commandPayload
1666 try {
1667 if (Object.values(OCPP16DataTransferVendorId).includes(vendorId)) {
1668 return OCPP16Constants.OCPP_DATA_TRANSFER_RESPONSE_ACCEPTED
1669 }
1670 return OCPP16Constants.OCPP_DATA_TRANSFER_RESPONSE_UNKNOWN_VENDOR_ID
1671 } catch (error) {
1672 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1673 return handleIncomingRequestError<OCPP16DataTransferResponse>(
1674 chargingStation,
1675 OCPP16IncomingRequestCommand.DATA_TRANSFER,
1676 error as Error,
1677 { errorResponse: OCPP16Constants.OCPP_DATA_TRANSFER_RESPONSE_REJECTED }
1678 )!
1679 }
1680 }
1681
1682 private async handleRequestReserveNow (
1683 chargingStation: ChargingStation,
1684 commandPayload: OCPP16ReserveNowRequest
1685 ): Promise<OCPP16ReserveNowResponse> {
1686 if (
1687 !OCPP16ServiceUtils.checkFeatureProfile(
1688 chargingStation,
1689 OCPP16SupportedFeatureProfiles.Reservation,
1690 OCPP16IncomingRequestCommand.RESERVE_NOW
1691 )
1692 ) {
1693 return OCPP16Constants.OCPP_RESERVATION_RESPONSE_REJECTED
1694 }
1695 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1696 commandPayload.expiryDate = convertToDate(commandPayload.expiryDate)!
1697 const { reservationId, idTag, connectorId } = commandPayload
1698 if (!chargingStation.hasConnector(connectorId)) {
1699 logger.error(
1700 `${chargingStation.logPrefix()} Trying to reserve a non existing connector id ${connectorId}`
1701 )
1702 return OCPP16Constants.OCPP_RESERVATION_RESPONSE_REJECTED
1703 }
1704 if (connectorId > 0 && !chargingStation.isConnectorAvailable(connectorId)) {
1705 return OCPP16Constants.OCPP_RESERVATION_RESPONSE_REJECTED
1706 }
1707 if (connectorId === 0 && !chargingStation.getReserveConnectorZeroSupported()) {
1708 return OCPP16Constants.OCPP_RESERVATION_RESPONSE_REJECTED
1709 }
1710 if (!(await OCPP16ServiceUtils.isIdTagAuthorized(chargingStation, connectorId, idTag))) {
1711 return OCPP16Constants.OCPP_RESERVATION_RESPONSE_REJECTED
1712 }
1713 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1714 const connectorStatus = chargingStation.getConnectorStatus(connectorId)!
1715 resetAuthorizeConnectorStatus(connectorStatus)
1716 let response: OCPP16ReserveNowResponse
1717 try {
1718 await removeExpiredReservations(chargingStation)
1719 switch (connectorStatus.status) {
1720 case OCPP16ChargePointStatus.Faulted:
1721 response = OCPP16Constants.OCPP_RESERVATION_RESPONSE_FAULTED
1722 break
1723 case OCPP16ChargePointStatus.Preparing:
1724 case OCPP16ChargePointStatus.Charging:
1725 case OCPP16ChargePointStatus.SuspendedEV:
1726 case OCPP16ChargePointStatus.SuspendedEVSE:
1727 case OCPP16ChargePointStatus.Finishing:
1728 response = OCPP16Constants.OCPP_RESERVATION_RESPONSE_OCCUPIED
1729 break
1730 case OCPP16ChargePointStatus.Unavailable:
1731 response = OCPP16Constants.OCPP_RESERVATION_RESPONSE_UNAVAILABLE
1732 break
1733 case OCPP16ChargePointStatus.Reserved:
1734 if (!chargingStation.isConnectorReservable(reservationId, idTag, connectorId)) {
1735 response = OCPP16Constants.OCPP_RESERVATION_RESPONSE_OCCUPIED
1736 break
1737 }
1738 // eslint-disable-next-line no-fallthrough
1739 default:
1740 if (!chargingStation.isConnectorReservable(reservationId, idTag)) {
1741 response = OCPP16Constants.OCPP_RESERVATION_RESPONSE_OCCUPIED
1742 break
1743 }
1744 await chargingStation.addReservation({
1745 id: commandPayload.reservationId,
1746 ...commandPayload
1747 })
1748 response = OCPP16Constants.OCPP_RESERVATION_RESPONSE_ACCEPTED
1749 break
1750 }
1751 return response
1752 } catch (error) {
1753 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1754 chargingStation.getConnectorStatus(connectorId)!.status = OCPP16ChargePointStatus.Available
1755 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1756 return handleIncomingRequestError<OCPP16ReserveNowResponse>(
1757 chargingStation,
1758 OCPP16IncomingRequestCommand.RESERVE_NOW,
1759 error as Error,
1760 { errorResponse: OCPP16Constants.OCPP_RESERVATION_RESPONSE_FAULTED }
1761 )!
1762 }
1763 }
1764
1765 private async handleRequestCancelReservation (
1766 chargingStation: ChargingStation,
1767 commandPayload: OCPP16CancelReservationRequest
1768 ): Promise<GenericResponse> {
1769 if (
1770 !OCPP16ServiceUtils.checkFeatureProfile(
1771 chargingStation,
1772 OCPP16SupportedFeatureProfiles.Reservation,
1773 OCPP16IncomingRequestCommand.CANCEL_RESERVATION
1774 )
1775 ) {
1776 return OCPP16Constants.OCPP_CANCEL_RESERVATION_RESPONSE_REJECTED
1777 }
1778 try {
1779 const { reservationId } = commandPayload
1780 const reservation = chargingStation.getReservationBy('reservationId', reservationId)
1781 if (reservation == null) {
1782 logger.debug(
1783 `${chargingStation.logPrefix()} Reservation with id ${reservationId} does not exist on charging station`
1784 )
1785 return OCPP16Constants.OCPP_CANCEL_RESERVATION_RESPONSE_REJECTED
1786 }
1787 await chargingStation.removeReservation(
1788 reservation,
1789 ReservationTerminationReason.RESERVATION_CANCELED
1790 )
1791 return OCPP16Constants.OCPP_CANCEL_RESERVATION_RESPONSE_ACCEPTED
1792 } catch (error) {
1793 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1794 return handleIncomingRequestError<GenericResponse>(
1795 chargingStation,
1796 OCPP16IncomingRequestCommand.CANCEL_RESERVATION,
1797 error as Error,
1798 {
1799 errorResponse: OCPP16Constants.OCPP_CANCEL_RESERVATION_RESPONSE_REJECTED
1800 }
1801 )!
1802 }
1803 }
1804 }