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