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