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