Fixes to OCPP command payload validation:
[e-mobility-charging-stations-simulator.git] / src / charging-station / ocpp / 1.6 / OCPP16IncomingRequestService.ts
1 // Partial Copyright Jerome Benoit. 2021-2023. All Rights Reserved.
2
3 import fs from 'fs';
4 import path from 'path';
5 import { URL, fileURLToPath } from 'url';
6
7 import type { JSONSchemaType } from 'ajv';
8 import { Client, type FTPResponse } from 'basic-ftp';
9 import tar from 'tar';
10
11 import OCPPError from '../../../exception/OCPPError';
12 import type { JsonObject, JsonType } from '../../../types/JsonType';
13 import { OCPP16ChargePointErrorCode } from '../../../types/ocpp/1.6/ChargePointErrorCode';
14 import { OCPP16ChargePointStatus } from '../../../types/ocpp/1.6/ChargePointStatus';
15 import {
16 ChargingProfilePurposeType,
17 type OCPP16ChargingProfile,
18 } from '../../../types/ocpp/1.6/ChargingProfile';
19 import {
20 OCPP16StandardParametersKey,
21 OCPP16SupportedFeatureProfiles,
22 } from '../../../types/ocpp/1.6/Configuration';
23 import { OCPP16DiagnosticsStatus } from '../../../types/ocpp/1.6/DiagnosticsStatus';
24 import {
25 type ChangeAvailabilityRequest,
26 type ChangeConfigurationRequest,
27 type ClearChargingProfileRequest,
28 type DiagnosticsStatusNotificationRequest,
29 type GetConfigurationRequest,
30 type GetDiagnosticsRequest,
31 OCPP16AvailabilityType,
32 type OCPP16BootNotificationRequest,
33 type OCPP16ClearCacheRequest,
34 type OCPP16DataTransferRequest,
35 OCPP16DataTransferVendorId,
36 type OCPP16HeartbeatRequest,
37 OCPP16IncomingRequestCommand,
38 OCPP16MessageTrigger,
39 OCPP16RequestCommand,
40 type OCPP16StatusNotificationRequest,
41 type OCPP16TriggerMessageRequest,
42 type OCPP16UpdateFirmwareRequest,
43 type RemoteStartTransactionRequest,
44 type RemoteStopTransactionRequest,
45 type ResetRequest,
46 type SetChargingProfileRequest,
47 type UnlockConnectorRequest,
48 } from '../../../types/ocpp/1.6/Requests';
49 import {
50 type ChangeAvailabilityResponse,
51 type ChangeConfigurationResponse,
52 type ClearChargingProfileResponse,
53 type DiagnosticsStatusNotificationResponse,
54 type GetConfigurationResponse,
55 type GetDiagnosticsResponse,
56 type OCPP16BootNotificationResponse,
57 type OCPP16DataTransferResponse,
58 OCPP16DataTransferStatus,
59 type OCPP16HeartbeatResponse,
60 type OCPP16StatusNotificationResponse,
61 type OCPP16TriggerMessageResponse,
62 type OCPP16UpdateFirmwareResponse,
63 type SetChargingProfileResponse,
64 type UnlockConnectorResponse,
65 } from '../../../types/ocpp/1.6/Responses';
66 import {
67 OCPP16AuthorizationStatus,
68 type OCPP16AuthorizeRequest,
69 type OCPP16AuthorizeResponse,
70 type OCPP16StartTransactionRequest,
71 type OCPP16StartTransactionResponse,
72 OCPP16StopTransactionReason,
73 } from '../../../types/ocpp/1.6/Transaction';
74 import type { OCPPConfigurationKey } from '../../../types/ocpp/Configuration';
75 import { ErrorType } from '../../../types/ocpp/ErrorType';
76 import { OCPPVersion } from '../../../types/ocpp/OCPPVersion';
77 import type { IncomingRequestHandler } from '../../../types/ocpp/Requests';
78 import type { DefaultResponse } from '../../../types/ocpp/Responses';
79 import Constants from '../../../utils/Constants';
80 import logger from '../../../utils/Logger';
81 import Utils from '../../../utils/Utils';
82 import type ChargingStation from '../../ChargingStation';
83 import { ChargingStationConfigurationUtils } from '../../ChargingStationConfigurationUtils';
84 import { ChargingStationUtils } from '../../ChargingStationUtils';
85 import OCPPConstants from '../OCPPConstants';
86 import OCPPIncomingRequestService from '../OCPPIncomingRequestService';
87 import { OCPP16ServiceUtils } from './OCPP16ServiceUtils';
88
89 const moduleName = 'OCPP16IncomingRequestService';
90
91 export default class OCPP16IncomingRequestService extends OCPPIncomingRequestService {
92 private incomingRequestHandlers: Map<OCPP16IncomingRequestCommand, IncomingRequestHandler>;
93 private jsonSchemas: Map<OCPP16IncomingRequestCommand, JSONSchemaType<JsonObject>>;
94
95 public constructor() {
96 if (new.target?.name === moduleName) {
97 throw new TypeError(`Cannot construct ${new.target?.name} instances directly`);
98 }
99 super(OCPPVersion.VERSION_16);
100 this.incomingRequestHandlers = new Map<OCPP16IncomingRequestCommand, IncomingRequestHandler>([
101 [OCPP16IncomingRequestCommand.RESET, this.handleRequestReset.bind(this)],
102 [OCPP16IncomingRequestCommand.CLEAR_CACHE, this.handleRequestClearCache.bind(this)],
103 [OCPP16IncomingRequestCommand.UNLOCK_CONNECTOR, this.handleRequestUnlockConnector.bind(this)],
104 [
105 OCPP16IncomingRequestCommand.GET_CONFIGURATION,
106 this.handleRequestGetConfiguration.bind(this),
107 ],
108 [
109 OCPP16IncomingRequestCommand.CHANGE_CONFIGURATION,
110 this.handleRequestChangeConfiguration.bind(this),
111 ],
112 [
113 OCPP16IncomingRequestCommand.SET_CHARGING_PROFILE,
114 this.handleRequestSetChargingProfile.bind(this),
115 ],
116 [
117 OCPP16IncomingRequestCommand.CLEAR_CHARGING_PROFILE,
118 this.handleRequestClearChargingProfile.bind(this),
119 ],
120 [
121 OCPP16IncomingRequestCommand.CHANGE_AVAILABILITY,
122 this.handleRequestChangeAvailability.bind(this),
123 ],
124 [
125 OCPP16IncomingRequestCommand.REMOTE_START_TRANSACTION,
126 this.handleRequestRemoteStartTransaction.bind(this),
127 ],
128 [
129 OCPP16IncomingRequestCommand.REMOTE_STOP_TRANSACTION,
130 this.handleRequestRemoteStopTransaction.bind(this),
131 ],
132 [OCPP16IncomingRequestCommand.GET_DIAGNOSTICS, this.handleRequestGetDiagnostics.bind(this)],
133 [OCPP16IncomingRequestCommand.TRIGGER_MESSAGE, this.handleRequestTriggerMessage.bind(this)],
134 [OCPP16IncomingRequestCommand.DATA_TRANSFER, this.handleRequestDataTransfer.bind(this)],
135 // [OCPP16IncomingRequestCommand.UPDATE_FIRMWARE, this.handleRequestUpdateFirmware.bind(this)],
136 ]);
137 this.jsonSchemas = new Map<OCPP16IncomingRequestCommand, JSONSchemaType<JsonObject>>([
138 [
139 OCPP16IncomingRequestCommand.RESET,
140 JSON.parse(
141 fs.readFileSync(
142 path.resolve(
143 path.dirname(fileURLToPath(import.meta.url)),
144 '../../../assets/json-schemas/ocpp/1.6/Reset.json'
145 ),
146 'utf8'
147 )
148 ) as JSONSchemaType<ResetRequest>,
149 ],
150 [
151 OCPP16IncomingRequestCommand.CLEAR_CACHE,
152 JSON.parse(
153 fs.readFileSync(
154 path.resolve(
155 path.dirname(fileURLToPath(import.meta.url)),
156 '../../../assets/json-schemas/ocpp/1.6/ClearCache.json'
157 ),
158 'utf8'
159 )
160 ) as JSONSchemaType<OCPP16ClearCacheRequest>,
161 ],
162 [
163 OCPP16IncomingRequestCommand.UNLOCK_CONNECTOR,
164 JSON.parse(
165 fs.readFileSync(
166 path.resolve(
167 path.dirname(fileURLToPath(import.meta.url)),
168 '../../../assets/json-schemas/ocpp/1.6/UnlockConnector.json'
169 ),
170 'utf8'
171 )
172 ) as JSONSchemaType<UnlockConnectorRequest>,
173 ],
174 [
175 OCPP16IncomingRequestCommand.GET_CONFIGURATION,
176 JSON.parse(
177 fs.readFileSync(
178 path.resolve(
179 path.dirname(fileURLToPath(import.meta.url)),
180 '../../../assets/json-schemas/ocpp/1.6/GetConfiguration.json'
181 ),
182 'utf8'
183 )
184 ) as JSONSchemaType<GetConfigurationRequest>,
185 ],
186 [
187 OCPP16IncomingRequestCommand.CHANGE_CONFIGURATION,
188 JSON.parse(
189 fs.readFileSync(
190 path.resolve(
191 path.dirname(fileURLToPath(import.meta.url)),
192 '../../../assets/json-schemas/ocpp/1.6/ChangeConfiguration.json'
193 ),
194 'utf8'
195 )
196 ) as JSONSchemaType<ChangeConfigurationRequest>,
197 ],
198 [
199 OCPP16IncomingRequestCommand.GET_DIAGNOSTICS,
200 JSON.parse(
201 fs.readFileSync(
202 path.resolve(
203 path.dirname(fileURLToPath(import.meta.url)),
204 '../../../assets/json-schemas/ocpp/1.6/GetDiagnostics.json'
205 ),
206 'utf8'
207 )
208 ) as JSONSchemaType<GetDiagnosticsRequest>,
209 ],
210 [
211 OCPP16IncomingRequestCommand.SET_CHARGING_PROFILE,
212 JSON.parse(
213 fs.readFileSync(
214 path.resolve(
215 path.dirname(fileURLToPath(import.meta.url)),
216 '../../../assets/json-schemas/ocpp/1.6/SetChargingProfile.json'
217 ),
218 'utf8'
219 )
220 ) as JSONSchemaType<SetChargingProfileRequest>,
221 ],
222 [
223 OCPP16IncomingRequestCommand.CLEAR_CHARGING_PROFILE,
224 JSON.parse(
225 fs.readFileSync(
226 path.resolve(
227 path.dirname(fileURLToPath(import.meta.url)),
228 '../../../assets/json-schemas/ocpp/1.6/ClearChargingProfile.json'
229 ),
230 'utf8'
231 )
232 ) as JSONSchemaType<ClearChargingProfileRequest>,
233 ],
234 [
235 OCPP16IncomingRequestCommand.CHANGE_AVAILABILITY,
236 JSON.parse(
237 fs.readFileSync(
238 path.resolve(
239 path.dirname(fileURLToPath(import.meta.url)),
240 '../../../assets/json-schemas/ocpp/1.6/ChangeAvailability.json'
241 ),
242 'utf8'
243 )
244 ) as JSONSchemaType<ChangeAvailabilityRequest>,
245 ],
246 [
247 OCPP16IncomingRequestCommand.REMOTE_START_TRANSACTION,
248 JSON.parse(
249 fs.readFileSync(
250 path.resolve(
251 path.dirname(fileURLToPath(import.meta.url)),
252 '../../../assets/json-schemas/ocpp/1.6/RemoteStartTransaction.json'
253 ),
254 'utf8'
255 )
256 ) as JSONSchemaType<RemoteStartTransactionRequest>,
257 ],
258 [
259 OCPP16IncomingRequestCommand.REMOTE_STOP_TRANSACTION,
260 JSON.parse(
261 fs.readFileSync(
262 path.resolve(
263 path.dirname(fileURLToPath(import.meta.url)),
264 '../../../assets/json-schemas/ocpp/1.6/RemoteStopTransaction.json'
265 ),
266 'utf8'
267 )
268 ) as JSONSchemaType<RemoteStopTransactionRequest>,
269 ],
270 [
271 OCPP16IncomingRequestCommand.TRIGGER_MESSAGE,
272 JSON.parse(
273 fs.readFileSync(
274 path.resolve(
275 path.dirname(fileURLToPath(import.meta.url)),
276 '../../../assets/json-schemas/ocpp/1.6/TriggerMessage.json'
277 ),
278 'utf8'
279 )
280 ) as JSONSchemaType<OCPP16TriggerMessageRequest>,
281 ],
282 [
283 OCPP16IncomingRequestCommand.DATA_TRANSFER,
284 JSON.parse(
285 fs.readFileSync(
286 path.resolve(
287 path.dirname(fileURLToPath(import.meta.url)),
288 '../../../assets/json-schemas/ocpp/1.6/DataTransfer.json'
289 ),
290 'utf8'
291 )
292 ) as JSONSchemaType<OCPP16DataTransferRequest>,
293 ],
294 ]);
295 this.validatePayload.bind(this);
296 }
297
298 public async incomingRequestHandler(
299 chargingStation: ChargingStation,
300 messageId: string,
301 commandName: OCPP16IncomingRequestCommand,
302 commandPayload: JsonType
303 ): Promise<void> {
304 let response: JsonType;
305 if (
306 chargingStation.getOcppStrictCompliance() === true &&
307 chargingStation.isInPendingState() === true &&
308 (commandName === OCPP16IncomingRequestCommand.REMOTE_START_TRANSACTION ||
309 commandName === OCPP16IncomingRequestCommand.REMOTE_STOP_TRANSACTION)
310 ) {
311 throw new OCPPError(
312 ErrorType.SECURITY_ERROR,
313 `${commandName} cannot be issued to handle request PDU ${JSON.stringify(
314 commandPayload,
315 null,
316 2
317 )} while the charging station is in pending state on the central server`,
318 commandName,
319 commandPayload
320 );
321 }
322 if (
323 chargingStation.isRegistered() === true ||
324 (chargingStation.getOcppStrictCompliance() === false &&
325 chargingStation.isInUnknownState() === true)
326 ) {
327 if (
328 this.incomingRequestHandlers.has(commandName) === true &&
329 OCPP16ServiceUtils.isIncomingRequestCommandSupported(chargingStation, commandName) === true
330 ) {
331 try {
332 this.validatePayload(chargingStation, commandName, commandPayload);
333 // Call the method to build the response
334 response = await this.incomingRequestHandlers.get(commandName)(
335 chargingStation,
336 commandPayload
337 );
338 } catch (error) {
339 // Log
340 logger.error(
341 `${chargingStation.logPrefix()} ${moduleName}.incomingRequestHandler: Handle incoming request error:`,
342 error
343 );
344 throw error;
345 }
346 } else {
347 // Throw exception
348 throw new OCPPError(
349 ErrorType.NOT_IMPLEMENTED,
350 `${commandName} is not implemented to handle request PDU ${JSON.stringify(
351 commandPayload,
352 null,
353 2
354 )}`,
355 commandName,
356 commandPayload
357 );
358 }
359 } else {
360 throw new OCPPError(
361 ErrorType.SECURITY_ERROR,
362 `${commandName} cannot be issued to handle request PDU ${JSON.stringify(
363 commandPayload,
364 null,
365 2
366 )} while the charging station is not registered on the central server.`,
367 commandName,
368 commandPayload
369 );
370 }
371 // Send the built response
372 await chargingStation.ocppRequestService.sendResponse(
373 chargingStation,
374 messageId,
375 response,
376 commandName
377 );
378 }
379
380 private validatePayload(
381 chargingStation: ChargingStation,
382 commandName: OCPP16IncomingRequestCommand,
383 commandPayload: JsonType
384 ): boolean {
385 if (this.jsonSchemas.has(commandName) === true) {
386 return this.validateIncomingRequestPayload(
387 chargingStation,
388 commandName,
389 this.jsonSchemas.get(commandName),
390 commandPayload
391 );
392 }
393 logger.warn(
394 `${chargingStation.logPrefix()} ${moduleName}.validatePayload: No JSON schema found for command ${commandName} PDU validation`
395 );
396 return false;
397 }
398
399 // Simulate charging station restart
400 private handleRequestReset(
401 chargingStation: ChargingStation,
402 commandPayload: ResetRequest
403 ): DefaultResponse {
404 this.asyncResource
405 .runInAsyncScope(
406 chargingStation.reset.bind(chargingStation) as (
407 this: ChargingStation,
408 ...args: any[]
409 ) => Promise<void>,
410 chargingStation,
411 (commandPayload.type + 'Reset') as OCPP16StopTransactionReason
412 )
413 .catch(() => {
414 /* This is intentional */
415 });
416 logger.info(
417 `${chargingStation.logPrefix()} ${
418 commandPayload.type
419 } reset command received, simulating it. The station will be back online in ${Utils.formatDurationMilliSeconds(
420 chargingStation.stationInfo.resetTime
421 )}`
422 );
423 return OCPPConstants.OCPP_RESPONSE_ACCEPTED;
424 }
425
426 private handleRequestClearCache(chargingStation: ChargingStation): DefaultResponse {
427 chargingStation.authorizedTagsCache.deleteAuthorizedTags(
428 ChargingStationUtils.getAuthorizationFile(chargingStation.stationInfo)
429 );
430 return OCPPConstants.OCPP_RESPONSE_ACCEPTED;
431 }
432
433 private async handleRequestUnlockConnector(
434 chargingStation: ChargingStation,
435 commandPayload: UnlockConnectorRequest
436 ): Promise<UnlockConnectorResponse> {
437 const connectorId = commandPayload.connectorId;
438 if (chargingStation.connectors.has(connectorId) === false) {
439 logger.error(
440 `${chargingStation.logPrefix()} Trying to unlock a non existing connector Id ${connectorId.toString()}`
441 );
442 return OCPPConstants.OCPP_RESPONSE_UNLOCK_NOT_SUPPORTED;
443 }
444 if (connectorId === 0) {
445 logger.error(
446 chargingStation.logPrefix() + ' Trying to unlock connector Id ' + connectorId.toString()
447 );
448 return OCPPConstants.OCPP_RESPONSE_UNLOCK_NOT_SUPPORTED;
449 }
450 if (chargingStation.getConnectorStatus(connectorId)?.transactionStarted === true) {
451 const stopResponse = await chargingStation.stopTransactionOnConnector(
452 connectorId,
453 OCPP16StopTransactionReason.UNLOCK_COMMAND
454 );
455 if (stopResponse.idTagInfo?.status === OCPP16AuthorizationStatus.ACCEPTED) {
456 return OCPPConstants.OCPP_RESPONSE_UNLOCKED;
457 }
458 return OCPPConstants.OCPP_RESPONSE_UNLOCK_FAILED;
459 }
460 await chargingStation.ocppRequestService.requestHandler<
461 OCPP16StatusNotificationRequest,
462 OCPP16StatusNotificationResponse
463 >(chargingStation, OCPP16RequestCommand.STATUS_NOTIFICATION, {
464 connectorId,
465 status: OCPP16ChargePointStatus.AVAILABLE,
466 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
467 });
468 chargingStation.getConnectorStatus(connectorId).status = OCPP16ChargePointStatus.AVAILABLE;
469 return OCPPConstants.OCPP_RESPONSE_UNLOCKED;
470 }
471
472 private handleRequestGetConfiguration(
473 chargingStation: ChargingStation,
474 commandPayload: GetConfigurationRequest
475 ): GetConfigurationResponse {
476 const configurationKey: OCPPConfigurationKey[] = [];
477 const unknownKey: string[] = [];
478 if (Utils.isEmptyArray(commandPayload.key) === true) {
479 for (const configuration of chargingStation.ocppConfiguration.configurationKey) {
480 if (Utils.isUndefined(configuration.visible) === true) {
481 configuration.visible = true;
482 }
483 if (configuration.visible === false) {
484 continue;
485 }
486 configurationKey.push({
487 key: configuration.key,
488 readonly: configuration.readonly,
489 value: configuration.value,
490 });
491 }
492 } else {
493 for (const key of commandPayload.key) {
494 const keyFound = ChargingStationConfigurationUtils.getConfigurationKey(
495 chargingStation,
496 key
497 );
498 if (keyFound) {
499 if (Utils.isUndefined(keyFound.visible) === true) {
500 keyFound.visible = true;
501 }
502 if (keyFound.visible === false) {
503 continue;
504 }
505 configurationKey.push({
506 key: keyFound.key,
507 readonly: keyFound.readonly,
508 value: keyFound.value,
509 });
510 } else {
511 unknownKey.push(key);
512 }
513 }
514 }
515 return {
516 configurationKey,
517 unknownKey,
518 };
519 }
520
521 private handleRequestChangeConfiguration(
522 chargingStation: ChargingStation,
523 commandPayload: ChangeConfigurationRequest
524 ): ChangeConfigurationResponse {
525 const keyToChange = ChargingStationConfigurationUtils.getConfigurationKey(
526 chargingStation,
527 commandPayload.key,
528 true
529 );
530 if (!keyToChange) {
531 return OCPPConstants.OCPP_CONFIGURATION_RESPONSE_NOT_SUPPORTED;
532 } else if (keyToChange && keyToChange.readonly) {
533 return OCPPConstants.OCPP_CONFIGURATION_RESPONSE_REJECTED;
534 } else if (keyToChange && !keyToChange.readonly) {
535 let valueChanged = false;
536 if (keyToChange.value !== commandPayload.value) {
537 ChargingStationConfigurationUtils.setConfigurationKeyValue(
538 chargingStation,
539 commandPayload.key,
540 commandPayload.value,
541 true
542 );
543 valueChanged = true;
544 }
545 let triggerHeartbeatRestart = false;
546 if (keyToChange.key === OCPP16StandardParametersKey.HeartBeatInterval && valueChanged) {
547 ChargingStationConfigurationUtils.setConfigurationKeyValue(
548 chargingStation,
549 OCPP16StandardParametersKey.HeartbeatInterval,
550 commandPayload.value
551 );
552 triggerHeartbeatRestart = true;
553 }
554 if (keyToChange.key === OCPP16StandardParametersKey.HeartbeatInterval && valueChanged) {
555 ChargingStationConfigurationUtils.setConfigurationKeyValue(
556 chargingStation,
557 OCPP16StandardParametersKey.HeartBeatInterval,
558 commandPayload.value
559 );
560 triggerHeartbeatRestart = true;
561 }
562 if (triggerHeartbeatRestart) {
563 chargingStation.restartHeartbeat();
564 }
565 if (keyToChange.key === OCPP16StandardParametersKey.WebSocketPingInterval && valueChanged) {
566 chargingStation.restartWebSocketPing();
567 }
568 if (keyToChange.reboot) {
569 return OCPPConstants.OCPP_CONFIGURATION_RESPONSE_REBOOT_REQUIRED;
570 }
571 return OCPPConstants.OCPP_CONFIGURATION_RESPONSE_ACCEPTED;
572 }
573 }
574
575 private handleRequestSetChargingProfile(
576 chargingStation: ChargingStation,
577 commandPayload: SetChargingProfileRequest
578 ): SetChargingProfileResponse {
579 if (
580 OCPP16ServiceUtils.checkFeatureProfile(
581 chargingStation,
582 OCPP16SupportedFeatureProfiles.SmartCharging,
583 OCPP16IncomingRequestCommand.SET_CHARGING_PROFILE
584 ) === false
585 ) {
586 return OCPPConstants.OCPP_SET_CHARGING_PROFILE_RESPONSE_NOT_SUPPORTED;
587 }
588 if (chargingStation.connectors.has(commandPayload.connectorId) === false) {
589 logger.error(
590 `${chargingStation.logPrefix()} Trying to set charging profile(s) to a non existing connector Id ${
591 commandPayload.connectorId
592 }`
593 );
594 return OCPPConstants.OCPP_SET_CHARGING_PROFILE_RESPONSE_REJECTED;
595 }
596 if (
597 commandPayload.csChargingProfiles.chargingProfilePurpose ===
598 ChargingProfilePurposeType.CHARGE_POINT_MAX_PROFILE &&
599 commandPayload.connectorId !== 0
600 ) {
601 return OCPPConstants.OCPP_SET_CHARGING_PROFILE_RESPONSE_REJECTED;
602 }
603 if (
604 commandPayload.csChargingProfiles.chargingProfilePurpose ===
605 ChargingProfilePurposeType.TX_PROFILE &&
606 (commandPayload.connectorId === 0 ||
607 chargingStation.getConnectorStatus(commandPayload.connectorId)?.transactionStarted ===
608 false)
609 ) {
610 logger.error(
611 `${chargingStation.logPrefix()} Trying to set transaction charging profile(s) on connector ${
612 commandPayload.connectorId
613 } without a started transaction`
614 );
615 return OCPPConstants.OCPP_SET_CHARGING_PROFILE_RESPONSE_REJECTED;
616 }
617 OCPP16ServiceUtils.setChargingProfile(
618 chargingStation,
619 commandPayload.connectorId,
620 commandPayload.csChargingProfiles
621 );
622 logger.debug(
623 `${chargingStation.logPrefix()} Charging profile(s) set on connector id ${
624 commandPayload.connectorId
625 }, dump their stack: %j`,
626 chargingStation.getConnectorStatus(commandPayload.connectorId).chargingProfiles
627 );
628 return OCPPConstants.OCPP_SET_CHARGING_PROFILE_RESPONSE_ACCEPTED;
629 }
630
631 private handleRequestClearChargingProfile(
632 chargingStation: ChargingStation,
633 commandPayload: ClearChargingProfileRequest
634 ): ClearChargingProfileResponse {
635 if (
636 OCPP16ServiceUtils.checkFeatureProfile(
637 chargingStation,
638 OCPP16SupportedFeatureProfiles.SmartCharging,
639 OCPP16IncomingRequestCommand.CLEAR_CHARGING_PROFILE
640 ) === false
641 ) {
642 return OCPPConstants.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_UNKNOWN;
643 }
644 if (chargingStation.connectors.has(commandPayload.connectorId) === false) {
645 logger.error(
646 `${chargingStation.logPrefix()} Trying to clear a charging profile(s) to a non existing connector Id ${
647 commandPayload.connectorId
648 }`
649 );
650 return OCPPConstants.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_UNKNOWN;
651 }
652 const connectorStatus = chargingStation.getConnectorStatus(commandPayload.connectorId);
653 if (commandPayload.connectorId && !Utils.isEmptyArray(connectorStatus.chargingProfiles)) {
654 connectorStatus.chargingProfiles = [];
655 logger.debug(
656 `${chargingStation.logPrefix()} Charging profile(s) cleared on connector id ${
657 commandPayload.connectorId
658 }, dump their stack: %j`,
659 connectorStatus.chargingProfiles
660 );
661 return OCPPConstants.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_ACCEPTED;
662 }
663 if (!commandPayload.connectorId) {
664 let clearedCP = false;
665 for (const connectorId of chargingStation.connectors.keys()) {
666 if (!Utils.isEmptyArray(chargingStation.getConnectorStatus(connectorId).chargingProfiles)) {
667 chargingStation
668 .getConnectorStatus(connectorId)
669 .chargingProfiles?.forEach((chargingProfile: OCPP16ChargingProfile, index: number) => {
670 let clearCurrentCP = false;
671 if (chargingProfile.chargingProfileId === commandPayload.id) {
672 clearCurrentCP = true;
673 }
674 if (
675 !commandPayload.chargingProfilePurpose &&
676 chargingProfile.stackLevel === commandPayload.stackLevel
677 ) {
678 clearCurrentCP = true;
679 }
680 if (
681 !chargingProfile.stackLevel &&
682 chargingProfile.chargingProfilePurpose === commandPayload.chargingProfilePurpose
683 ) {
684 clearCurrentCP = true;
685 }
686 if (
687 chargingProfile.stackLevel === commandPayload.stackLevel &&
688 chargingProfile.chargingProfilePurpose === commandPayload.chargingProfilePurpose
689 ) {
690 clearCurrentCP = true;
691 }
692 if (clearCurrentCP) {
693 connectorStatus.chargingProfiles.splice(index, 1);
694 logger.debug(
695 `${chargingStation.logPrefix()} Matching charging profile(s) cleared on connector id ${
696 commandPayload.connectorId
697 }, dump their stack: %j`,
698 connectorStatus.chargingProfiles
699 );
700 clearedCP = true;
701 }
702 });
703 }
704 }
705 if (clearedCP) {
706 return OCPPConstants.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_ACCEPTED;
707 }
708 }
709 return OCPPConstants.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_UNKNOWN;
710 }
711
712 private async handleRequestChangeAvailability(
713 chargingStation: ChargingStation,
714 commandPayload: ChangeAvailabilityRequest
715 ): Promise<ChangeAvailabilityResponse> {
716 const connectorId: number = commandPayload.connectorId;
717 if (chargingStation.connectors.has(connectorId) === false) {
718 logger.error(
719 `${chargingStation.logPrefix()} Trying to change the availability of a non existing connector Id ${connectorId.toString()}`
720 );
721 return OCPPConstants.OCPP_AVAILABILITY_RESPONSE_REJECTED;
722 }
723 const chargePointStatus: OCPP16ChargePointStatus =
724 commandPayload.type === OCPP16AvailabilityType.OPERATIVE
725 ? OCPP16ChargePointStatus.AVAILABLE
726 : OCPP16ChargePointStatus.UNAVAILABLE;
727 if (connectorId === 0) {
728 let response: ChangeAvailabilityResponse = OCPPConstants.OCPP_AVAILABILITY_RESPONSE_ACCEPTED;
729 for (const id of chargingStation.connectors.keys()) {
730 if (chargingStation.getConnectorStatus(id)?.transactionStarted === true) {
731 response = OCPPConstants.OCPP_AVAILABILITY_RESPONSE_SCHEDULED;
732 }
733 chargingStation.getConnectorStatus(id).availability = commandPayload.type;
734 if (response === OCPPConstants.OCPP_AVAILABILITY_RESPONSE_ACCEPTED) {
735 await chargingStation.ocppRequestService.requestHandler<
736 OCPP16StatusNotificationRequest,
737 OCPP16StatusNotificationResponse
738 >(chargingStation, OCPP16RequestCommand.STATUS_NOTIFICATION, {
739 connectorId: id,
740 status: chargePointStatus,
741 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
742 });
743 chargingStation.getConnectorStatus(id).status = chargePointStatus;
744 }
745 }
746 return response;
747 } else if (
748 connectorId > 0 &&
749 (chargingStation.isChargingStationAvailable() === true ||
750 (chargingStation.isChargingStationAvailable() === false &&
751 commandPayload.type === OCPP16AvailabilityType.INOPERATIVE))
752 ) {
753 if (chargingStation.getConnectorStatus(connectorId)?.transactionStarted === true) {
754 chargingStation.getConnectorStatus(connectorId).availability = commandPayload.type;
755 return OCPPConstants.OCPP_AVAILABILITY_RESPONSE_SCHEDULED;
756 }
757 chargingStation.getConnectorStatus(connectorId).availability = commandPayload.type;
758 await chargingStation.ocppRequestService.requestHandler<
759 OCPP16StatusNotificationRequest,
760 OCPP16StatusNotificationResponse
761 >(chargingStation, OCPP16RequestCommand.STATUS_NOTIFICATION, {
762 connectorId,
763 status: chargePointStatus,
764 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
765 });
766 chargingStation.getConnectorStatus(connectorId).status = chargePointStatus;
767 return OCPPConstants.OCPP_AVAILABILITY_RESPONSE_ACCEPTED;
768 }
769 return OCPPConstants.OCPP_AVAILABILITY_RESPONSE_REJECTED;
770 }
771
772 private async handleRequestRemoteStartTransaction(
773 chargingStation: ChargingStation,
774 commandPayload: RemoteStartTransactionRequest
775 ): Promise<DefaultResponse> {
776 const transactionConnectorId = commandPayload.connectorId;
777 if (chargingStation.connectors.has(transactionConnectorId) === true) {
778 const remoteStartTransactionLogMsg =
779 chargingStation.logPrefix() +
780 ' Transaction remotely STARTED on ' +
781 chargingStation.stationInfo.chargingStationId +
782 '#' +
783 transactionConnectorId.toString() +
784 " for idTag '" +
785 commandPayload.idTag +
786 "'";
787 await chargingStation.ocppRequestService.requestHandler<
788 OCPP16StatusNotificationRequest,
789 OCPP16StatusNotificationResponse
790 >(chargingStation, OCPP16RequestCommand.STATUS_NOTIFICATION, {
791 connectorId: transactionConnectorId,
792 status: OCPP16ChargePointStatus.PREPARING,
793 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
794 });
795 const connectorStatus = chargingStation.getConnectorStatus(transactionConnectorId);
796 connectorStatus.status = OCPP16ChargePointStatus.PREPARING;
797 if (chargingStation.isChargingStationAvailable() === true) {
798 // Check if authorized
799 if (chargingStation.getAuthorizeRemoteTxRequests() === true) {
800 let authorized = false;
801 if (
802 chargingStation.getLocalAuthListEnabled() === true &&
803 chargingStation.hasAuthorizedTags() === true &&
804 chargingStation.authorizedTagsCache
805 .getAuthorizedTags(
806 ChargingStationUtils.getAuthorizationFile(chargingStation.stationInfo)
807 )
808 .find((idTag) => idTag === commandPayload.idTag)
809 ) {
810 connectorStatus.localAuthorizeIdTag = commandPayload.idTag;
811 connectorStatus.idTagLocalAuthorized = true;
812 authorized = true;
813 } else if (chargingStation.getMustAuthorizeAtRemoteStart() === true) {
814 connectorStatus.authorizeIdTag = commandPayload.idTag;
815 const authorizeResponse: OCPP16AuthorizeResponse =
816 await chargingStation.ocppRequestService.requestHandler<
817 OCPP16AuthorizeRequest,
818 OCPP16AuthorizeResponse
819 >(chargingStation, OCPP16RequestCommand.AUTHORIZE, {
820 idTag: commandPayload.idTag,
821 });
822 if (authorizeResponse?.idTagInfo?.status === OCPP16AuthorizationStatus.ACCEPTED) {
823 authorized = true;
824 }
825 } else {
826 logger.warn(
827 `${chargingStation.logPrefix()} The charging station configuration expects authorize at remote start transaction but local authorization or authorize isn't enabled`
828 );
829 }
830 if (authorized === true) {
831 // Authorization successful, start transaction
832 if (
833 this.setRemoteStartTransactionChargingProfile(
834 chargingStation,
835 transactionConnectorId,
836 commandPayload.chargingProfile
837 ) === true
838 ) {
839 connectorStatus.transactionRemoteStarted = true;
840 if (
841 (
842 await chargingStation.ocppRequestService.requestHandler<
843 OCPP16StartTransactionRequest,
844 OCPP16StartTransactionResponse
845 >(chargingStation, OCPP16RequestCommand.START_TRANSACTION, {
846 connectorId: transactionConnectorId,
847 idTag: commandPayload.idTag,
848 })
849 ).idTagInfo.status === OCPP16AuthorizationStatus.ACCEPTED
850 ) {
851 logger.debug(remoteStartTransactionLogMsg);
852 return OCPPConstants.OCPP_RESPONSE_ACCEPTED;
853 }
854 return this.notifyRemoteStartTransactionRejected(
855 chargingStation,
856 transactionConnectorId,
857 commandPayload.idTag
858 );
859 }
860 return this.notifyRemoteStartTransactionRejected(
861 chargingStation,
862 transactionConnectorId,
863 commandPayload.idTag
864 );
865 }
866 return this.notifyRemoteStartTransactionRejected(
867 chargingStation,
868 transactionConnectorId,
869 commandPayload.idTag
870 );
871 }
872 // No authorization check required, start transaction
873 if (
874 this.setRemoteStartTransactionChargingProfile(
875 chargingStation,
876 transactionConnectorId,
877 commandPayload.chargingProfile
878 ) === true
879 ) {
880 connectorStatus.transactionRemoteStarted = true;
881 if (
882 (
883 await chargingStation.ocppRequestService.requestHandler<
884 OCPP16StartTransactionRequest,
885 OCPP16StartTransactionResponse
886 >(chargingStation, OCPP16RequestCommand.START_TRANSACTION, {
887 connectorId: transactionConnectorId,
888 idTag: commandPayload.idTag,
889 })
890 ).idTagInfo.status === OCPP16AuthorizationStatus.ACCEPTED
891 ) {
892 logger.debug(remoteStartTransactionLogMsg);
893 return OCPPConstants.OCPP_RESPONSE_ACCEPTED;
894 }
895 return this.notifyRemoteStartTransactionRejected(
896 chargingStation,
897 transactionConnectorId,
898 commandPayload.idTag
899 );
900 }
901 return this.notifyRemoteStartTransactionRejected(
902 chargingStation,
903 transactionConnectorId,
904 commandPayload.idTag
905 );
906 }
907 return this.notifyRemoteStartTransactionRejected(
908 chargingStation,
909 transactionConnectorId,
910 commandPayload.idTag
911 );
912 }
913 return this.notifyRemoteStartTransactionRejected(
914 chargingStation,
915 transactionConnectorId,
916 commandPayload.idTag
917 );
918 }
919
920 private async notifyRemoteStartTransactionRejected(
921 chargingStation: ChargingStation,
922 connectorId: number,
923 idTag: string
924 ): Promise<DefaultResponse> {
925 if (
926 chargingStation.getConnectorStatus(connectorId).status !== OCPP16ChargePointStatus.AVAILABLE
927 ) {
928 await chargingStation.ocppRequestService.requestHandler<
929 OCPP16StatusNotificationRequest,
930 OCPP16StatusNotificationResponse
931 >(chargingStation, OCPP16RequestCommand.STATUS_NOTIFICATION, {
932 connectorId,
933 status: OCPP16ChargePointStatus.AVAILABLE,
934 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
935 });
936 chargingStation.getConnectorStatus(connectorId).status = OCPP16ChargePointStatus.AVAILABLE;
937 }
938 logger.warn(
939 chargingStation.logPrefix() +
940 ' Remote starting transaction REJECTED on connector Id ' +
941 connectorId.toString() +
942 ", idTag '" +
943 idTag +
944 "', availability '" +
945 chargingStation.getConnectorStatus(connectorId).availability +
946 "', status '" +
947 chargingStation.getConnectorStatus(connectorId).status +
948 "'"
949 );
950 return OCPPConstants.OCPP_RESPONSE_REJECTED;
951 }
952
953 private setRemoteStartTransactionChargingProfile(
954 chargingStation: ChargingStation,
955 connectorId: number,
956 cp: OCPP16ChargingProfile
957 ): boolean {
958 if (cp && cp.chargingProfilePurpose === ChargingProfilePurposeType.TX_PROFILE) {
959 OCPP16ServiceUtils.setChargingProfile(chargingStation, connectorId, cp);
960 logger.debug(
961 `${chargingStation.logPrefix()} Charging profile(s) set at remote start transaction on connector id ${connectorId}, dump their stack: %j`,
962 chargingStation.getConnectorStatus(connectorId).chargingProfiles
963 );
964 return true;
965 } else if (cp && cp.chargingProfilePurpose !== ChargingProfilePurposeType.TX_PROFILE) {
966 logger.warn(
967 `${chargingStation.logPrefix()} Not allowed to set ${
968 cp.chargingProfilePurpose
969 } charging profile(s) at remote start transaction`
970 );
971 return false;
972 } else if (!cp) {
973 return true;
974 }
975 }
976
977 private async handleRequestRemoteStopTransaction(
978 chargingStation: ChargingStation,
979 commandPayload: RemoteStopTransactionRequest
980 ): Promise<DefaultResponse> {
981 const transactionId = commandPayload.transactionId;
982 for (const connectorId of chargingStation.connectors.keys()) {
983 if (
984 connectorId > 0 &&
985 chargingStation.getConnectorStatus(connectorId)?.transactionId === transactionId
986 ) {
987 await chargingStation.ocppRequestService.requestHandler<
988 OCPP16StatusNotificationRequest,
989 OCPP16StatusNotificationResponse
990 >(chargingStation, OCPP16RequestCommand.STATUS_NOTIFICATION, {
991 connectorId,
992 status: OCPP16ChargePointStatus.FINISHING,
993 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
994 });
995 chargingStation.getConnectorStatus(connectorId).status = OCPP16ChargePointStatus.FINISHING;
996 const stopResponse = await chargingStation.stopTransactionOnConnector(
997 connectorId,
998 OCPP16StopTransactionReason.REMOTE
999 );
1000 if (stopResponse.idTagInfo?.status === OCPP16AuthorizationStatus.ACCEPTED) {
1001 return OCPPConstants.OCPP_RESPONSE_ACCEPTED;
1002 }
1003 return OCPPConstants.OCPP_RESPONSE_REJECTED;
1004 }
1005 }
1006 logger.warn(
1007 chargingStation.logPrefix() +
1008 ' Trying to remote stop a non existing transaction ' +
1009 transactionId.toString()
1010 );
1011 return OCPPConstants.OCPP_RESPONSE_REJECTED;
1012 }
1013
1014 private handleRequestUpdateFirmware(
1015 chargingStation: ChargingStation,
1016 commandPayload: OCPP16UpdateFirmwareRequest
1017 ): OCPP16UpdateFirmwareResponse {
1018 if (
1019 OCPP16ServiceUtils.checkFeatureProfile(
1020 chargingStation,
1021 OCPP16SupportedFeatureProfiles.FirmwareManagement,
1022 OCPP16IncomingRequestCommand.UPDATE_FIRMWARE
1023 ) === false
1024 ) {
1025 return OCPPConstants.OCPP_RESPONSE_EMPTY;
1026 }
1027 logger.debug(
1028 chargingStation.logPrefix() +
1029 ' ' +
1030 OCPP16IncomingRequestCommand.UPDATE_FIRMWARE +
1031 ' request received: %j',
1032 commandPayload
1033 );
1034 }
1035
1036 private async handleRequestGetDiagnostics(
1037 chargingStation: ChargingStation,
1038 commandPayload: GetDiagnosticsRequest
1039 ): Promise<GetDiagnosticsResponse> {
1040 if (
1041 OCPP16ServiceUtils.checkFeatureProfile(
1042 chargingStation,
1043 OCPP16SupportedFeatureProfiles.FirmwareManagement,
1044 OCPP16IncomingRequestCommand.GET_DIAGNOSTICS
1045 ) === false
1046 ) {
1047 return OCPPConstants.OCPP_RESPONSE_EMPTY;
1048 }
1049 logger.debug(
1050 chargingStation.logPrefix() +
1051 ' ' +
1052 OCPP16IncomingRequestCommand.GET_DIAGNOSTICS +
1053 ' request received: %j',
1054 commandPayload
1055 );
1056 const uri = new URL(commandPayload.location);
1057 if (uri.protocol.startsWith('ftp:')) {
1058 let ftpClient: Client;
1059 try {
1060 const logFiles = fs
1061 .readdirSync(path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../../../'))
1062 .filter((file) => file.endsWith('.log'))
1063 .map((file) => path.join('./', file));
1064 const diagnosticsArchive = chargingStation.stationInfo.chargingStationId + '_logs.tar.gz';
1065 tar.create({ gzip: true }, logFiles).pipe(fs.createWriteStream(diagnosticsArchive));
1066 ftpClient = new Client();
1067 const accessResponse = await ftpClient.access({
1068 host: uri.host,
1069 ...(!Utils.isEmptyString(uri.port) && { port: Utils.convertToInt(uri.port) }),
1070 ...(!Utils.isEmptyString(uri.username) && { user: uri.username }),
1071 ...(!Utils.isEmptyString(uri.password) && { password: uri.password }),
1072 });
1073 let uploadResponse: FTPResponse;
1074 if (accessResponse.code === 220) {
1075 // eslint-disable-next-line @typescript-eslint/no-misused-promises
1076 ftpClient.trackProgress(async (info) => {
1077 logger.info(
1078 `${chargingStation.logPrefix()} ${
1079 info.bytes / 1024
1080 } bytes transferred from diagnostics archive ${info.name}`
1081 );
1082 await chargingStation.ocppRequestService.requestHandler<
1083 DiagnosticsStatusNotificationRequest,
1084 DiagnosticsStatusNotificationResponse
1085 >(chargingStation, OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION, {
1086 status: OCPP16DiagnosticsStatus.Uploading,
1087 });
1088 });
1089 uploadResponse = await ftpClient.uploadFrom(
1090 path.join(
1091 path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../../../'),
1092 diagnosticsArchive
1093 ),
1094 uri.pathname + diagnosticsArchive
1095 );
1096 if (uploadResponse.code === 226) {
1097 await chargingStation.ocppRequestService.requestHandler<
1098 DiagnosticsStatusNotificationRequest,
1099 DiagnosticsStatusNotificationResponse
1100 >(chargingStation, OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION, {
1101 status: OCPP16DiagnosticsStatus.Uploaded,
1102 });
1103 if (ftpClient) {
1104 ftpClient.close();
1105 }
1106 return { fileName: diagnosticsArchive };
1107 }
1108 throw new OCPPError(
1109 ErrorType.GENERIC_ERROR,
1110 `Diagnostics transfer failed with error code ${accessResponse.code.toString()}${
1111 uploadResponse?.code && '|' + uploadResponse?.code.toString()
1112 }`,
1113 OCPP16IncomingRequestCommand.GET_DIAGNOSTICS
1114 );
1115 }
1116 throw new OCPPError(
1117 ErrorType.GENERIC_ERROR,
1118 `Diagnostics transfer failed with error code ${accessResponse.code.toString()}${
1119 uploadResponse?.code && '|' + uploadResponse?.code.toString()
1120 }`,
1121 OCPP16IncomingRequestCommand.GET_DIAGNOSTICS
1122 );
1123 } catch (error) {
1124 await chargingStation.ocppRequestService.requestHandler<
1125 DiagnosticsStatusNotificationRequest,
1126 DiagnosticsStatusNotificationResponse
1127 >(chargingStation, OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION, {
1128 status: OCPP16DiagnosticsStatus.UploadFailed,
1129 });
1130 if (ftpClient) {
1131 ftpClient.close();
1132 }
1133 return this.handleIncomingRequestError(
1134 chargingStation,
1135 OCPP16IncomingRequestCommand.GET_DIAGNOSTICS,
1136 error as Error,
1137 { errorResponse: OCPPConstants.OCPP_RESPONSE_EMPTY }
1138 );
1139 }
1140 } else {
1141 logger.error(
1142 `${chargingStation.logPrefix()} Unsupported protocol ${
1143 uri.protocol
1144 } to transfer the diagnostic logs archive`
1145 );
1146 await chargingStation.ocppRequestService.requestHandler<
1147 DiagnosticsStatusNotificationRequest,
1148 DiagnosticsStatusNotificationResponse
1149 >(chargingStation, OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION, {
1150 status: OCPP16DiagnosticsStatus.UploadFailed,
1151 });
1152 return OCPPConstants.OCPP_RESPONSE_EMPTY;
1153 }
1154 }
1155
1156 private handleRequestTriggerMessage(
1157 chargingStation: ChargingStation,
1158 commandPayload: OCPP16TriggerMessageRequest
1159 ): OCPP16TriggerMessageResponse {
1160 if (
1161 !OCPP16ServiceUtils.checkFeatureProfile(
1162 chargingStation,
1163 OCPP16SupportedFeatureProfiles.RemoteTrigger,
1164 OCPP16IncomingRequestCommand.TRIGGER_MESSAGE
1165 ) ||
1166 !OCPP16ServiceUtils.isMessageTriggerSupported(
1167 chargingStation,
1168 commandPayload.requestedMessage
1169 )
1170 ) {
1171 return OCPPConstants.OCPP_TRIGGER_MESSAGE_RESPONSE_NOT_IMPLEMENTED;
1172 }
1173 if (
1174 !OCPP16ServiceUtils.isConnectorIdValid(
1175 chargingStation,
1176 OCPP16IncomingRequestCommand.TRIGGER_MESSAGE,
1177 commandPayload.connectorId
1178 )
1179 ) {
1180 return OCPPConstants.OCPP_TRIGGER_MESSAGE_RESPONSE_REJECTED;
1181 }
1182 try {
1183 switch (commandPayload.requestedMessage) {
1184 case OCPP16MessageTrigger.BootNotification:
1185 setTimeout(() => {
1186 chargingStation.ocppRequestService
1187 .requestHandler<OCPP16BootNotificationRequest, OCPP16BootNotificationResponse>(
1188 chargingStation,
1189 OCPP16RequestCommand.BOOT_NOTIFICATION,
1190 chargingStation.bootNotificationRequest,
1191 { skipBufferingOnError: true, triggerMessage: true }
1192 )
1193 .then((response) => {
1194 chargingStation.bootNotificationResponse = response;
1195 })
1196 .catch(() => {
1197 /* This is intentional */
1198 });
1199 }, Constants.OCPP_TRIGGER_MESSAGE_DELAY);
1200 return OCPPConstants.OCPP_TRIGGER_MESSAGE_RESPONSE_ACCEPTED;
1201 case OCPP16MessageTrigger.Heartbeat:
1202 setTimeout(() => {
1203 chargingStation.ocppRequestService
1204 .requestHandler<OCPP16HeartbeatRequest, OCPP16HeartbeatResponse>(
1205 chargingStation,
1206 OCPP16RequestCommand.HEARTBEAT,
1207 null,
1208 {
1209 triggerMessage: true,
1210 }
1211 )
1212 .catch(() => {
1213 /* This is intentional */
1214 });
1215 }, Constants.OCPP_TRIGGER_MESSAGE_DELAY);
1216 return OCPPConstants.OCPP_TRIGGER_MESSAGE_RESPONSE_ACCEPTED;
1217 case OCPP16MessageTrigger.StatusNotification:
1218 setTimeout(() => {
1219 if (commandPayload?.connectorId) {
1220 chargingStation.ocppRequestService
1221 .requestHandler<OCPP16StatusNotificationRequest, OCPP16StatusNotificationResponse>(
1222 chargingStation,
1223 OCPP16RequestCommand.STATUS_NOTIFICATION,
1224 {
1225 connectorId: commandPayload.connectorId,
1226 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
1227 status: chargingStation.getConnectorStatus(commandPayload.connectorId).status,
1228 },
1229 {
1230 triggerMessage: true,
1231 }
1232 )
1233 .catch(() => {
1234 /* This is intentional */
1235 });
1236 } else {
1237 for (const connectorId of chargingStation.connectors.keys()) {
1238 chargingStation.ocppRequestService
1239 .requestHandler<
1240 OCPP16StatusNotificationRequest,
1241 OCPP16StatusNotificationResponse
1242 >(
1243 chargingStation,
1244 OCPP16RequestCommand.STATUS_NOTIFICATION,
1245 {
1246 connectorId,
1247 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
1248 status: chargingStation.getConnectorStatus(connectorId).status,
1249 },
1250 {
1251 triggerMessage: true,
1252 }
1253 )
1254 .catch(() => {
1255 /* This is intentional */
1256 });
1257 }
1258 }
1259 }, Constants.OCPP_TRIGGER_MESSAGE_DELAY);
1260 return OCPPConstants.OCPP_TRIGGER_MESSAGE_RESPONSE_ACCEPTED;
1261 default:
1262 return OCPPConstants.OCPP_TRIGGER_MESSAGE_RESPONSE_NOT_IMPLEMENTED;
1263 }
1264 } catch (error) {
1265 return this.handleIncomingRequestError(
1266 chargingStation,
1267 OCPP16IncomingRequestCommand.TRIGGER_MESSAGE,
1268 error as Error,
1269 { errorResponse: OCPPConstants.OCPP_TRIGGER_MESSAGE_RESPONSE_REJECTED }
1270 );
1271 }
1272 }
1273
1274 private handleRequestDataTransfer(
1275 chargingStation: ChargingStation,
1276 commandPayload: OCPP16DataTransferRequest
1277 ): OCPP16DataTransferResponse {
1278 try {
1279 if (Object.values(OCPP16DataTransferVendorId).includes(commandPayload.vendorId)) {
1280 return {
1281 status: OCPP16DataTransferStatus.ACCEPTED,
1282 };
1283 }
1284 return {
1285 status: OCPP16DataTransferStatus.UNKNOWN_VENDOR_ID,
1286 };
1287 } catch (error) {
1288 return this.handleIncomingRequestError(
1289 chargingStation,
1290 OCPP16IncomingRequestCommand.DATA_TRANSFER,
1291 error as Error,
1292 { errorResponse: OCPPConstants.OCPP_DATA_TRANSFER_RESPONSE_REJECTED }
1293 );
1294 }
1295 }
1296 }