8360122b2710a7b8736a05d1ac8e705fc3063034
[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 await chargingStation.ocppRequestService.requestHandler<
1011 OCPP16FirmwareStatusNotificationRequest,
1012 OCPP16FirmwareStatusNotificationResponse
1013 >(chargingStation, OCPP16RequestCommand.FIRMWARE_STATUS_NOTIFICATION, {
1014 status: OCPP16FirmwareStatus.Downloading,
1015 });
1016 chargingStation.stationInfo.firmwareStatus = OCPP16FirmwareStatus.Downloading;
1017 if (
1018 chargingStation.stationInfo?.firmwareUpgrade?.failureStatus ===
1019 OCPP16FirmwareStatus.DownloadFailed
1020 ) {
1021 await Utils.sleep(Utils.getRandomInteger(maxDelay, minDelay) * 1000);
1022 await chargingStation.ocppRequestService.requestHandler<
1023 OCPP16FirmwareStatusNotificationRequest,
1024 OCPP16FirmwareStatusNotificationResponse
1025 >(chargingStation, OCPP16RequestCommand.FIRMWARE_STATUS_NOTIFICATION, {
1026 status: chargingStation.stationInfo?.firmwareUpgrade?.failureStatus,
1027 });
1028 chargingStation.stationInfo.firmwareStatus =
1029 chargingStation.stationInfo?.firmwareUpgrade?.failureStatus;
1030 return;
1031 }
1032 await Utils.sleep(Utils.getRandomInteger(maxDelay, minDelay) * 1000);
1033 await chargingStation.ocppRequestService.requestHandler<
1034 OCPP16FirmwareStatusNotificationRequest,
1035 OCPP16FirmwareStatusNotificationResponse
1036 >(chargingStation, OCPP16RequestCommand.FIRMWARE_STATUS_NOTIFICATION, {
1037 status: OCPP16FirmwareStatus.Downloaded,
1038 });
1039 chargingStation.stationInfo.firmwareStatus = OCPP16FirmwareStatus.Downloaded;
1040 await Utils.sleep(Utils.getRandomInteger(maxDelay, minDelay) * 1000);
1041 await chargingStation.ocppRequestService.requestHandler<
1042 OCPP16FirmwareStatusNotificationRequest,
1043 OCPP16FirmwareStatusNotificationResponse
1044 >(chargingStation, OCPP16RequestCommand.FIRMWARE_STATUS_NOTIFICATION, {
1045 status: OCPP16FirmwareStatus.Installing,
1046 });
1047 chargingStation.stationInfo.firmwareStatus = OCPP16FirmwareStatus.Installing;
1048 if (
1049 chargingStation.stationInfo?.firmwareUpgrade?.failureStatus ===
1050 OCPP16FirmwareStatus.InstallationFailed
1051 ) {
1052 await Utils.sleep(Utils.getRandomInteger(maxDelay, minDelay) * 1000);
1053 await chargingStation.ocppRequestService.requestHandler<
1054 OCPP16FirmwareStatusNotificationRequest,
1055 OCPP16FirmwareStatusNotificationResponse
1056 >(chargingStation, OCPP16RequestCommand.FIRMWARE_STATUS_NOTIFICATION, {
1057 status: chargingStation.stationInfo?.firmwareUpgrade?.failureStatus,
1058 });
1059 chargingStation.stationInfo.firmwareStatus =
1060 chargingStation.stationInfo?.firmwareUpgrade?.failureStatus;
1061 return;
1062 }
1063 if (chargingStation.stationInfo?.firmwareUpgrade?.reset === true) {
1064 await Utils.sleep(Utils.getRandomInteger(maxDelay, minDelay) * 1000);
1065 await chargingStation.reset(OCPP16StopTransactionReason.REBOOT);
1066 }
1067 }
1068
1069 private async handleRequestGetDiagnostics(
1070 chargingStation: ChargingStation,
1071 commandPayload: GetDiagnosticsRequest
1072 ): Promise<GetDiagnosticsResponse> {
1073 if (
1074 OCPP16ServiceUtils.checkFeatureProfile(
1075 chargingStation,
1076 OCPP16SupportedFeatureProfiles.FirmwareManagement,
1077 OCPP16IncomingRequestCommand.GET_DIAGNOSTICS
1078 ) === false
1079 ) {
1080 logger.warn(
1081 `${chargingStation.logPrefix()} ${moduleName}.handleRequestGetDiagnostics: Cannot get diagnostics: feature profile not supported`
1082 );
1083 return OCPPConstants.OCPP_RESPONSE_EMPTY;
1084 }
1085 const uri = new URL(commandPayload.location);
1086 if (uri.protocol.startsWith('ftp:')) {
1087 let ftpClient: Client;
1088 try {
1089 const logFiles = fs
1090 .readdirSync(path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../../../'))
1091 .filter((file) => file.endsWith('.log'))
1092 .map((file) => path.join('./', file));
1093 const diagnosticsArchive = `${chargingStation.stationInfo.chargingStationId}_logs.tar.gz`;
1094 tar.create({ gzip: true }, logFiles).pipe(fs.createWriteStream(diagnosticsArchive));
1095 ftpClient = new Client();
1096 const accessResponse = await ftpClient.access({
1097 host: uri.host,
1098 ...(Utils.isNotEmptyString(uri.port) && { port: Utils.convertToInt(uri.port) }),
1099 ...(Utils.isNotEmptyString(uri.username) && { user: uri.username }),
1100 ...(Utils.isNotEmptyString(uri.password) && { password: uri.password }),
1101 });
1102 let uploadResponse: FTPResponse;
1103 if (accessResponse.code === 220) {
1104 ftpClient.trackProgress((info) => {
1105 logger.info(
1106 `${chargingStation.logPrefix()} ${
1107 info.bytes / 1024
1108 } bytes transferred from diagnostics archive ${info.name}`
1109 );
1110 chargingStation.ocppRequestService
1111 .requestHandler<
1112 OCPP16DiagnosticsStatusNotificationRequest,
1113 OCPP16DiagnosticsStatusNotificationResponse
1114 >(chargingStation, OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION, {
1115 status: OCPP16DiagnosticsStatus.Uploading,
1116 })
1117 .catch((error) => {
1118 logger.error(
1119 `${chargingStation.logPrefix()} ${moduleName}.handleRequestGetDiagnostics: Error while sending '${
1120 OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION
1121 }'`,
1122 error
1123 );
1124 });
1125 });
1126 uploadResponse = await ftpClient.uploadFrom(
1127 path.join(
1128 path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../../../'),
1129 diagnosticsArchive
1130 ),
1131 `${uri.pathname}${diagnosticsArchive}`
1132 );
1133 if (uploadResponse.code === 226) {
1134 await chargingStation.ocppRequestService.requestHandler<
1135 OCPP16DiagnosticsStatusNotificationRequest,
1136 OCPP16DiagnosticsStatusNotificationResponse
1137 >(chargingStation, OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION, {
1138 status: OCPP16DiagnosticsStatus.Uploaded,
1139 });
1140 if (ftpClient) {
1141 ftpClient.close();
1142 }
1143 return { fileName: diagnosticsArchive };
1144 }
1145 throw new OCPPError(
1146 ErrorType.GENERIC_ERROR,
1147 `Diagnostics transfer failed with error code ${accessResponse.code.toString()}${
1148 uploadResponse?.code && `|${uploadResponse?.code.toString()}`
1149 }`,
1150 OCPP16IncomingRequestCommand.GET_DIAGNOSTICS
1151 );
1152 }
1153 throw new OCPPError(
1154 ErrorType.GENERIC_ERROR,
1155 `Diagnostics transfer failed with error code ${accessResponse.code.toString()}${
1156 uploadResponse?.code && `|${uploadResponse?.code.toString()}`
1157 }`,
1158 OCPP16IncomingRequestCommand.GET_DIAGNOSTICS
1159 );
1160 } catch (error) {
1161 await chargingStation.ocppRequestService.requestHandler<
1162 OCPP16DiagnosticsStatusNotificationRequest,
1163 OCPP16DiagnosticsStatusNotificationResponse
1164 >(chargingStation, OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION, {
1165 status: OCPP16DiagnosticsStatus.UploadFailed,
1166 });
1167 if (ftpClient) {
1168 ftpClient.close();
1169 }
1170 return this.handleIncomingRequestError(
1171 chargingStation,
1172 OCPP16IncomingRequestCommand.GET_DIAGNOSTICS,
1173 error as Error,
1174 { errorResponse: OCPPConstants.OCPP_RESPONSE_EMPTY }
1175 );
1176 }
1177 } else {
1178 logger.error(
1179 `${chargingStation.logPrefix()} Unsupported protocol ${
1180 uri.protocol
1181 } to transfer the diagnostic logs archive`
1182 );
1183 await chargingStation.ocppRequestService.requestHandler<
1184 OCPP16DiagnosticsStatusNotificationRequest,
1185 OCPP16DiagnosticsStatusNotificationResponse
1186 >(chargingStation, OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION, {
1187 status: OCPP16DiagnosticsStatus.UploadFailed,
1188 });
1189 return OCPPConstants.OCPP_RESPONSE_EMPTY;
1190 }
1191 }
1192
1193 private handleRequestTriggerMessage(
1194 chargingStation: ChargingStation,
1195 commandPayload: OCPP16TriggerMessageRequest
1196 ): OCPP16TriggerMessageResponse {
1197 if (
1198 !OCPP16ServiceUtils.checkFeatureProfile(
1199 chargingStation,
1200 OCPP16SupportedFeatureProfiles.RemoteTrigger,
1201 OCPP16IncomingRequestCommand.TRIGGER_MESSAGE
1202 ) ||
1203 !OCPP16ServiceUtils.isMessageTriggerSupported(
1204 chargingStation,
1205 commandPayload.requestedMessage
1206 )
1207 ) {
1208 return OCPPConstants.OCPP_TRIGGER_MESSAGE_RESPONSE_NOT_IMPLEMENTED;
1209 }
1210 if (
1211 !OCPP16ServiceUtils.isConnectorIdValid(
1212 chargingStation,
1213 OCPP16IncomingRequestCommand.TRIGGER_MESSAGE,
1214 commandPayload.connectorId
1215 )
1216 ) {
1217 return OCPPConstants.OCPP_TRIGGER_MESSAGE_RESPONSE_REJECTED;
1218 }
1219 try {
1220 switch (commandPayload.requestedMessage) {
1221 case OCPP16MessageTrigger.BootNotification:
1222 setTimeout(() => {
1223 chargingStation.ocppRequestService
1224 .requestHandler<OCPP16BootNotificationRequest, OCPP16BootNotificationResponse>(
1225 chargingStation,
1226 OCPP16RequestCommand.BOOT_NOTIFICATION,
1227 chargingStation.bootNotificationRequest,
1228 { skipBufferingOnError: true, triggerMessage: true }
1229 )
1230 .then((response) => {
1231 chargingStation.bootNotificationResponse = response;
1232 })
1233 .catch(Constants.EMPTY_FUNCTION);
1234 }, Constants.OCPP_TRIGGER_MESSAGE_DELAY);
1235 return OCPPConstants.OCPP_TRIGGER_MESSAGE_RESPONSE_ACCEPTED;
1236 case OCPP16MessageTrigger.Heartbeat:
1237 setTimeout(() => {
1238 chargingStation.ocppRequestService
1239 .requestHandler<OCPP16HeartbeatRequest, OCPP16HeartbeatResponse>(
1240 chargingStation,
1241 OCPP16RequestCommand.HEARTBEAT,
1242 null,
1243 {
1244 triggerMessage: true,
1245 }
1246 )
1247 .catch(Constants.EMPTY_FUNCTION);
1248 }, Constants.OCPP_TRIGGER_MESSAGE_DELAY);
1249 return OCPPConstants.OCPP_TRIGGER_MESSAGE_RESPONSE_ACCEPTED;
1250 case OCPP16MessageTrigger.StatusNotification:
1251 setTimeout(() => {
1252 if (!Utils.isNullOrUndefined(commandPayload?.connectorId)) {
1253 chargingStation.ocppRequestService
1254 .requestHandler<OCPP16StatusNotificationRequest, OCPP16StatusNotificationResponse>(
1255 chargingStation,
1256 OCPP16RequestCommand.STATUS_NOTIFICATION,
1257 {
1258 connectorId: commandPayload.connectorId,
1259 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
1260 status: chargingStation.getConnectorStatus(commandPayload.connectorId)?.status,
1261 },
1262 {
1263 triggerMessage: true,
1264 }
1265 )
1266 .catch(Constants.EMPTY_FUNCTION);
1267 } else {
1268 for (const connectorId of chargingStation.connectors.keys()) {
1269 chargingStation.ocppRequestService
1270 .requestHandler<
1271 OCPP16StatusNotificationRequest,
1272 OCPP16StatusNotificationResponse
1273 >(
1274 chargingStation,
1275 OCPP16RequestCommand.STATUS_NOTIFICATION,
1276 {
1277 connectorId,
1278 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
1279 status: chargingStation.getConnectorStatus(connectorId)?.status,
1280 },
1281 {
1282 triggerMessage: true,
1283 }
1284 )
1285 .catch(Constants.EMPTY_FUNCTION);
1286 }
1287 }
1288 }, Constants.OCPP_TRIGGER_MESSAGE_DELAY);
1289 return OCPPConstants.OCPP_TRIGGER_MESSAGE_RESPONSE_ACCEPTED;
1290 default:
1291 return OCPPConstants.OCPP_TRIGGER_MESSAGE_RESPONSE_NOT_IMPLEMENTED;
1292 }
1293 } catch (error) {
1294 return this.handleIncomingRequestError(
1295 chargingStation,
1296 OCPP16IncomingRequestCommand.TRIGGER_MESSAGE,
1297 error as Error,
1298 { errorResponse: OCPPConstants.OCPP_TRIGGER_MESSAGE_RESPONSE_REJECTED }
1299 );
1300 }
1301 }
1302
1303 private handleRequestDataTransfer(
1304 chargingStation: ChargingStation,
1305 commandPayload: OCPP16DataTransferRequest
1306 ): OCPP16DataTransferResponse {
1307 try {
1308 if (Object.values(OCPP16DataTransferVendorId).includes(commandPayload.vendorId)) {
1309 return {
1310 status: OCPP16DataTransferStatus.ACCEPTED,
1311 };
1312 }
1313 return {
1314 status: OCPP16DataTransferStatus.UNKNOWN_VENDOR_ID,
1315 };
1316 } catch (error) {
1317 return this.handleIncomingRequestError(
1318 chargingStation,
1319 OCPP16IncomingRequestCommand.DATA_TRANSFER,
1320 error as Error,
1321 { errorResponse: OCPPConstants.OCPP_DATA_TRANSFER_RESPONSE_REJECTED }
1322 );
1323 }
1324 }
1325 }