fb87345d00d9ccba36f3fa0164c0d21be3236453
[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 (
734 chargingStation.isChargingStationAvailable() === true &&
735 chargingStation.isConnectorAvailable(transactionConnectorId) === true
736 ) {
737 // Check if authorized
738 if (chargingStation.getAuthorizeRemoteTxRequests() === true) {
739 let authorized = false;
740 if (
741 chargingStation.getLocalAuthListEnabled() === true &&
742 chargingStation.hasAuthorizedTags() === true &&
743 Utils.isNotEmptyString(
744 chargingStation.authorizedTagsCache
745 .getAuthorizedTags(
746 ChargingStationUtils.getAuthorizationFile(chargingStation.stationInfo)
747 )
748 ?.find((idTag) => idTag === commandPayload.idTag)
749 )
750 ) {
751 connectorStatus.localAuthorizeIdTag = commandPayload.idTag;
752 connectorStatus.idTagLocalAuthorized = true;
753 authorized = true;
754 } else if (chargingStation.getMustAuthorizeAtRemoteStart() === true) {
755 connectorStatus.authorizeIdTag = commandPayload.idTag;
756 const authorizeResponse: OCPP16AuthorizeResponse =
757 await chargingStation.ocppRequestService.requestHandler<
758 OCPP16AuthorizeRequest,
759 OCPP16AuthorizeResponse
760 >(chargingStation, OCPP16RequestCommand.AUTHORIZE, {
761 idTag: commandPayload.idTag,
762 });
763 if (authorizeResponse?.idTagInfo?.status === OCPP16AuthorizationStatus.ACCEPTED) {
764 authorized = true;
765 }
766 } else {
767 logger.warn(
768 `${chargingStation.logPrefix()} The charging station configuration expects authorize at remote start transaction but local authorization or authorize isn't enabled`
769 );
770 }
771 if (authorized === true) {
772 // Authorization successful, start transaction
773 if (
774 this.setRemoteStartTransactionChargingProfile(
775 chargingStation,
776 transactionConnectorId,
777 commandPayload.chargingProfile
778 ) === true
779 ) {
780 connectorStatus.transactionRemoteStarted = true;
781 if (
782 (
783 await chargingStation.ocppRequestService.requestHandler<
784 OCPP16StartTransactionRequest,
785 OCPP16StartTransactionResponse
786 >(chargingStation, OCPP16RequestCommand.START_TRANSACTION, {
787 connectorId: transactionConnectorId,
788 idTag: commandPayload.idTag,
789 })
790 ).idTagInfo.status === OCPP16AuthorizationStatus.ACCEPTED
791 ) {
792 logger.debug(remoteStartTransactionLogMsg);
793 return OCPPConstants.OCPP_RESPONSE_ACCEPTED;
794 }
795 return this.notifyRemoteStartTransactionRejected(
796 chargingStation,
797 transactionConnectorId,
798 commandPayload.idTag
799 );
800 }
801 return this.notifyRemoteStartTransactionRejected(
802 chargingStation,
803 transactionConnectorId,
804 commandPayload.idTag
805 );
806 }
807 return this.notifyRemoteStartTransactionRejected(
808 chargingStation,
809 transactionConnectorId,
810 commandPayload.idTag
811 );
812 }
813 // No authorization check required, start transaction
814 if (
815 this.setRemoteStartTransactionChargingProfile(
816 chargingStation,
817 transactionConnectorId,
818 commandPayload.chargingProfile
819 ) === true
820 ) {
821 connectorStatus.transactionRemoteStarted = true;
822 if (
823 (
824 await chargingStation.ocppRequestService.requestHandler<
825 OCPP16StartTransactionRequest,
826 OCPP16StartTransactionResponse
827 >(chargingStation, OCPP16RequestCommand.START_TRANSACTION, {
828 connectorId: transactionConnectorId,
829 idTag: commandPayload.idTag,
830 })
831 ).idTagInfo.status === OCPP16AuthorizationStatus.ACCEPTED
832 ) {
833 logger.debug(remoteStartTransactionLogMsg);
834 return OCPPConstants.OCPP_RESPONSE_ACCEPTED;
835 }
836 return this.notifyRemoteStartTransactionRejected(
837 chargingStation,
838 transactionConnectorId,
839 commandPayload.idTag
840 );
841 }
842 return this.notifyRemoteStartTransactionRejected(
843 chargingStation,
844 transactionConnectorId,
845 commandPayload.idTag
846 );
847 }
848 return this.notifyRemoteStartTransactionRejected(
849 chargingStation,
850 transactionConnectorId,
851 commandPayload.idTag
852 );
853 }
854 return this.notifyRemoteStartTransactionRejected(
855 chargingStation,
856 transactionConnectorId,
857 commandPayload.idTag
858 );
859 }
860
861 private async notifyRemoteStartTransactionRejected(
862 chargingStation: ChargingStation,
863 connectorId: number,
864 idTag: string
865 ): Promise<GenericResponse> {
866 if (
867 chargingStation.getConnectorStatus(connectorId)?.status !== OCPP16ChargePointStatus.Available
868 ) {
869 await chargingStation.ocppRequestService.requestHandler<
870 OCPP16StatusNotificationRequest,
871 OCPP16StatusNotificationResponse
872 >(chargingStation, OCPP16RequestCommand.STATUS_NOTIFICATION, {
873 connectorId,
874 status: OCPP16ChargePointStatus.Available,
875 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
876 });
877 chargingStation.getConnectorStatus(connectorId).status = OCPP16ChargePointStatus.Available;
878 }
879 logger.warn(
880 `${chargingStation.logPrefix()} Remote starting transaction REJECTED on connector Id ${connectorId.toString()}, idTag '${idTag}', availability '${
881 chargingStation.getConnectorStatus(connectorId)?.availability
882 }', status '${chargingStation.getConnectorStatus(connectorId)?.status}'`
883 );
884 return OCPPConstants.OCPP_RESPONSE_REJECTED;
885 }
886
887 private setRemoteStartTransactionChargingProfile(
888 chargingStation: ChargingStation,
889 connectorId: number,
890 cp: OCPP16ChargingProfile
891 ): boolean {
892 if (cp && cp.chargingProfilePurpose === OCPP16ChargingProfilePurposeType.TX_PROFILE) {
893 OCPP16ServiceUtils.setChargingProfile(chargingStation, connectorId, cp);
894 logger.debug(
895 `${chargingStation.logPrefix()} Charging profile(s) set at remote start transaction on connector id ${connectorId}: %j`,
896 cp
897 );
898 return true;
899 } else if (cp && cp.chargingProfilePurpose !== OCPP16ChargingProfilePurposeType.TX_PROFILE) {
900 logger.warn(
901 `${chargingStation.logPrefix()} Not allowed to set ${
902 cp.chargingProfilePurpose
903 } charging profile(s) at remote start transaction`
904 );
905 return false;
906 } else if (!cp) {
907 return true;
908 }
909 }
910
911 private async handleRequestRemoteStopTransaction(
912 chargingStation: ChargingStation,
913 commandPayload: RemoteStopTransactionRequest
914 ): Promise<GenericResponse> {
915 const transactionId = commandPayload.transactionId;
916 for (const connectorId of chargingStation.connectors.keys()) {
917 if (
918 connectorId > 0 &&
919 chargingStation.getConnectorStatus(connectorId)?.transactionId === transactionId
920 ) {
921 await chargingStation.ocppRequestService.requestHandler<
922 OCPP16StatusNotificationRequest,
923 OCPP16StatusNotificationResponse
924 >(chargingStation, OCPP16RequestCommand.STATUS_NOTIFICATION, {
925 connectorId,
926 status: OCPP16ChargePointStatus.Finishing,
927 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
928 });
929 chargingStation.getConnectorStatus(connectorId).status = OCPP16ChargePointStatus.Finishing;
930 const stopResponse = await chargingStation.stopTransactionOnConnector(
931 connectorId,
932 OCPP16StopTransactionReason.REMOTE
933 );
934 if (stopResponse.idTagInfo?.status === OCPP16AuthorizationStatus.ACCEPTED) {
935 return OCPPConstants.OCPP_RESPONSE_ACCEPTED;
936 }
937 return OCPPConstants.OCPP_RESPONSE_REJECTED;
938 }
939 }
940 logger.warn(
941 `${chargingStation.logPrefix()} Trying to remote stop a non existing transaction ${transactionId.toString()}`
942 );
943 return OCPPConstants.OCPP_RESPONSE_REJECTED;
944 }
945
946 private handleRequestUpdateFirmware(
947 chargingStation: ChargingStation,
948 commandPayload: OCPP16UpdateFirmwareRequest
949 ): OCPP16UpdateFirmwareResponse {
950 if (
951 OCPP16ServiceUtils.checkFeatureProfile(
952 chargingStation,
953 OCPP16SupportedFeatureProfiles.FirmwareManagement,
954 OCPP16IncomingRequestCommand.UPDATE_FIRMWARE
955 ) === false
956 ) {
957 logger.warn(
958 `${chargingStation.logPrefix()} ${moduleName}.handleRequestUpdateFirmware: Cannot simulate firmware update: feature profile not supported`
959 );
960 return OCPPConstants.OCPP_RESPONSE_EMPTY;
961 }
962 if (
963 !Utils.isNullOrUndefined(chargingStation.stationInfo.firmwareStatus) &&
964 chargingStation.stationInfo.firmwareStatus !== OCPP16FirmwareStatus.Installed
965 ) {
966 logger.warn(
967 `${chargingStation.logPrefix()} ${moduleName}.handleRequestUpdateFirmware: Cannot simulate firmware update: firmware update is already in progress`
968 );
969 return OCPPConstants.OCPP_RESPONSE_EMPTY;
970 }
971 const retrieveDate = Utils.convertToDate(commandPayload.retrieveDate);
972 const now = Date.now();
973 if (retrieveDate?.getTime() <= now) {
974 this.runInAsyncScope(
975 this.updateFirmwareSimulation.bind(this) as (
976 this: OCPP16IncomingRequestService,
977 ...args: any[]
978 ) => Promise<void>,
979 this,
980 chargingStation
981 ).catch(Constants.EMPTY_FUNCTION);
982 } else {
983 setTimeout(() => {
984 this.updateFirmwareSimulation(chargingStation).catch(Constants.EMPTY_FUNCTION);
985 }, retrieveDate?.getTime() - now);
986 }
987 return OCPPConstants.OCPP_RESPONSE_EMPTY;
988 }
989
990 private async updateFirmwareSimulation(
991 chargingStation: ChargingStation,
992 maxDelay = 30,
993 minDelay = 15
994 ): Promise<void> {
995 chargingStation.stopAutomaticTransactionGenerator();
996 for (const connectorId of chargingStation.connectors.keys()) {
997 if (
998 connectorId > 0 &&
999 chargingStation.getConnectorStatus(connectorId)?.transactionStarted === false
1000 ) {
1001 await chargingStation.ocppRequestService.requestHandler<
1002 OCPP16StatusNotificationRequest,
1003 OCPP16StatusNotificationResponse
1004 >(chargingStation, OCPP16RequestCommand.STATUS_NOTIFICATION, {
1005 connectorId,
1006 status: OCPP16ChargePointStatus.Unavailable,
1007 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
1008 });
1009 chargingStation.getConnectorStatus(connectorId).status =
1010 OCPP16ChargePointStatus.Unavailable;
1011 }
1012 }
1013 await chargingStation.ocppRequestService.requestHandler<
1014 OCPP16FirmwareStatusNotificationRequest,
1015 OCPP16FirmwareStatusNotificationResponse
1016 >(chargingStation, OCPP16RequestCommand.FIRMWARE_STATUS_NOTIFICATION, {
1017 status: OCPP16FirmwareStatus.Downloading,
1018 });
1019 chargingStation.stationInfo.firmwareStatus = OCPP16FirmwareStatus.Downloading;
1020 if (
1021 chargingStation.stationInfo?.firmwareUpgrade?.failureStatus ===
1022 OCPP16FirmwareStatus.DownloadFailed
1023 ) {
1024 await Utils.sleep(Utils.getRandomInteger(maxDelay, minDelay) * 1000);
1025 await chargingStation.ocppRequestService.requestHandler<
1026 OCPP16FirmwareStatusNotificationRequest,
1027 OCPP16FirmwareStatusNotificationResponse
1028 >(chargingStation, OCPP16RequestCommand.FIRMWARE_STATUS_NOTIFICATION, {
1029 status: chargingStation.stationInfo?.firmwareUpgrade?.failureStatus,
1030 });
1031 chargingStation.stationInfo.firmwareStatus =
1032 chargingStation.stationInfo?.firmwareUpgrade?.failureStatus;
1033 return;
1034 }
1035 await Utils.sleep(Utils.getRandomInteger(maxDelay, minDelay) * 1000);
1036 await chargingStation.ocppRequestService.requestHandler<
1037 OCPP16FirmwareStatusNotificationRequest,
1038 OCPP16FirmwareStatusNotificationResponse
1039 >(chargingStation, OCPP16RequestCommand.FIRMWARE_STATUS_NOTIFICATION, {
1040 status: OCPP16FirmwareStatus.Downloaded,
1041 });
1042 chargingStation.stationInfo.firmwareStatus = OCPP16FirmwareStatus.Downloaded;
1043 let transactionsStarted: boolean;
1044 do {
1045 let trxCount = 0;
1046 for (const connectorId of chargingStation.connectors.keys()) {
1047 if (
1048 connectorId > 0 &&
1049 chargingStation.getConnectorStatus(connectorId)?.transactionStarted === true
1050 ) {
1051 trxCount++;
1052 }
1053 }
1054 if (trxCount > 0) {
1055 const waitTime = 15 * 1000;
1056 logger.debug(
1057 `${chargingStation.logPrefix()} ${moduleName}.updateFirmwareSimulation: ${trxCount} transaction(s) in progress, waiting ${
1058 waitTime / 1000
1059 } seconds before continuing firmware update simulation`
1060 );
1061 await Utils.sleep(waitTime);
1062 transactionsStarted = true;
1063 } else {
1064 for (const connectorId of chargingStation.connectors.keys()) {
1065 if (
1066 connectorId > 0 &&
1067 chargingStation.getConnectorStatus(connectorId)?.status !==
1068 OCPP16ChargePointStatus.Unavailable
1069 ) {
1070 await chargingStation.ocppRequestService.requestHandler<
1071 OCPP16StatusNotificationRequest,
1072 OCPP16StatusNotificationResponse
1073 >(chargingStation, OCPP16RequestCommand.STATUS_NOTIFICATION, {
1074 connectorId,
1075 status: OCPP16ChargePointStatus.Unavailable,
1076 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
1077 });
1078 chargingStation.getConnectorStatus(connectorId).status =
1079 OCPP16ChargePointStatus.Unavailable;
1080 }
1081 }
1082 transactionsStarted = false;
1083 }
1084 } while (transactionsStarted);
1085 await Utils.sleep(Utils.getRandomInteger(maxDelay, minDelay) * 1000);
1086 await chargingStation.ocppRequestService.requestHandler<
1087 OCPP16FirmwareStatusNotificationRequest,
1088 OCPP16FirmwareStatusNotificationResponse
1089 >(chargingStation, OCPP16RequestCommand.FIRMWARE_STATUS_NOTIFICATION, {
1090 status: OCPP16FirmwareStatus.Installing,
1091 });
1092 chargingStation.stationInfo.firmwareStatus = OCPP16FirmwareStatus.Installing;
1093 if (
1094 chargingStation.stationInfo?.firmwareUpgrade?.failureStatus ===
1095 OCPP16FirmwareStatus.InstallationFailed
1096 ) {
1097 await Utils.sleep(Utils.getRandomInteger(maxDelay, minDelay) * 1000);
1098 await chargingStation.ocppRequestService.requestHandler<
1099 OCPP16FirmwareStatusNotificationRequest,
1100 OCPP16FirmwareStatusNotificationResponse
1101 >(chargingStation, OCPP16RequestCommand.FIRMWARE_STATUS_NOTIFICATION, {
1102 status: chargingStation.stationInfo?.firmwareUpgrade?.failureStatus,
1103 });
1104 chargingStation.stationInfo.firmwareStatus =
1105 chargingStation.stationInfo?.firmwareUpgrade?.failureStatus;
1106 return;
1107 }
1108 if (chargingStation.stationInfo?.firmwareUpgrade?.reset === true) {
1109 await Utils.sleep(Utils.getRandomInteger(maxDelay, minDelay) * 1000);
1110 await chargingStation.reset(OCPP16StopTransactionReason.REBOOT);
1111 }
1112 }
1113
1114 private async handleRequestGetDiagnostics(
1115 chargingStation: ChargingStation,
1116 commandPayload: GetDiagnosticsRequest
1117 ): Promise<GetDiagnosticsResponse> {
1118 if (
1119 OCPP16ServiceUtils.checkFeatureProfile(
1120 chargingStation,
1121 OCPP16SupportedFeatureProfiles.FirmwareManagement,
1122 OCPP16IncomingRequestCommand.GET_DIAGNOSTICS
1123 ) === false
1124 ) {
1125 logger.warn(
1126 `${chargingStation.logPrefix()} ${moduleName}.handleRequestGetDiagnostics: Cannot get diagnostics: feature profile not supported`
1127 );
1128 return OCPPConstants.OCPP_RESPONSE_EMPTY;
1129 }
1130 const uri = new URL(commandPayload.location);
1131 if (uri.protocol.startsWith('ftp:')) {
1132 let ftpClient: Client;
1133 try {
1134 const logFiles = fs
1135 .readdirSync(path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../../../'))
1136 .filter((file) => file.endsWith('.log'))
1137 .map((file) => path.join('./', file));
1138 const diagnosticsArchive = `${chargingStation.stationInfo.chargingStationId}_logs.tar.gz`;
1139 tar.create({ gzip: true }, logFiles).pipe(fs.createWriteStream(diagnosticsArchive));
1140 ftpClient = new Client();
1141 const accessResponse = await ftpClient.access({
1142 host: uri.host,
1143 ...(Utils.isNotEmptyString(uri.port) && { port: Utils.convertToInt(uri.port) }),
1144 ...(Utils.isNotEmptyString(uri.username) && { user: uri.username }),
1145 ...(Utils.isNotEmptyString(uri.password) && { password: uri.password }),
1146 });
1147 let uploadResponse: FTPResponse;
1148 if (accessResponse.code === 220) {
1149 ftpClient.trackProgress((info) => {
1150 logger.info(
1151 `${chargingStation.logPrefix()} ${
1152 info.bytes / 1024
1153 } bytes transferred from diagnostics archive ${info.name}`
1154 );
1155 chargingStation.ocppRequestService
1156 .requestHandler<
1157 OCPP16DiagnosticsStatusNotificationRequest,
1158 OCPP16DiagnosticsStatusNotificationResponse
1159 >(chargingStation, OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION, {
1160 status: OCPP16DiagnosticsStatus.Uploading,
1161 })
1162 .catch((error) => {
1163 logger.error(
1164 `${chargingStation.logPrefix()} ${moduleName}.handleRequestGetDiagnostics: Error while sending '${
1165 OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION
1166 }'`,
1167 error
1168 );
1169 });
1170 });
1171 uploadResponse = await ftpClient.uploadFrom(
1172 path.join(
1173 path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../../../'),
1174 diagnosticsArchive
1175 ),
1176 `${uri.pathname}${diagnosticsArchive}`
1177 );
1178 if (uploadResponse.code === 226) {
1179 await chargingStation.ocppRequestService.requestHandler<
1180 OCPP16DiagnosticsStatusNotificationRequest,
1181 OCPP16DiagnosticsStatusNotificationResponse
1182 >(chargingStation, OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION, {
1183 status: OCPP16DiagnosticsStatus.Uploaded,
1184 });
1185 if (ftpClient) {
1186 ftpClient.close();
1187 }
1188 return { fileName: diagnosticsArchive };
1189 }
1190 throw new OCPPError(
1191 ErrorType.GENERIC_ERROR,
1192 `Diagnostics transfer failed with error code ${accessResponse.code.toString()}${
1193 uploadResponse?.code && `|${uploadResponse?.code.toString()}`
1194 }`,
1195 OCPP16IncomingRequestCommand.GET_DIAGNOSTICS
1196 );
1197 }
1198 throw new OCPPError(
1199 ErrorType.GENERIC_ERROR,
1200 `Diagnostics transfer failed with error code ${accessResponse.code.toString()}${
1201 uploadResponse?.code && `|${uploadResponse?.code.toString()}`
1202 }`,
1203 OCPP16IncomingRequestCommand.GET_DIAGNOSTICS
1204 );
1205 } catch (error) {
1206 await chargingStation.ocppRequestService.requestHandler<
1207 OCPP16DiagnosticsStatusNotificationRequest,
1208 OCPP16DiagnosticsStatusNotificationResponse
1209 >(chargingStation, OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION, {
1210 status: OCPP16DiagnosticsStatus.UploadFailed,
1211 });
1212 if (ftpClient) {
1213 ftpClient.close();
1214 }
1215 return this.handleIncomingRequestError(
1216 chargingStation,
1217 OCPP16IncomingRequestCommand.GET_DIAGNOSTICS,
1218 error as Error,
1219 { errorResponse: OCPPConstants.OCPP_RESPONSE_EMPTY }
1220 );
1221 }
1222 } else {
1223 logger.error(
1224 `${chargingStation.logPrefix()} Unsupported protocol ${
1225 uri.protocol
1226 } to transfer the diagnostic logs archive`
1227 );
1228 await chargingStation.ocppRequestService.requestHandler<
1229 OCPP16DiagnosticsStatusNotificationRequest,
1230 OCPP16DiagnosticsStatusNotificationResponse
1231 >(chargingStation, OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION, {
1232 status: OCPP16DiagnosticsStatus.UploadFailed,
1233 });
1234 return OCPPConstants.OCPP_RESPONSE_EMPTY;
1235 }
1236 }
1237
1238 private handleRequestTriggerMessage(
1239 chargingStation: ChargingStation,
1240 commandPayload: OCPP16TriggerMessageRequest
1241 ): OCPP16TriggerMessageResponse {
1242 if (
1243 !OCPP16ServiceUtils.checkFeatureProfile(
1244 chargingStation,
1245 OCPP16SupportedFeatureProfiles.RemoteTrigger,
1246 OCPP16IncomingRequestCommand.TRIGGER_MESSAGE
1247 ) ||
1248 !OCPP16ServiceUtils.isMessageTriggerSupported(
1249 chargingStation,
1250 commandPayload.requestedMessage
1251 )
1252 ) {
1253 return OCPPConstants.OCPP_TRIGGER_MESSAGE_RESPONSE_NOT_IMPLEMENTED;
1254 }
1255 if (
1256 !OCPP16ServiceUtils.isConnectorIdValid(
1257 chargingStation,
1258 OCPP16IncomingRequestCommand.TRIGGER_MESSAGE,
1259 commandPayload.connectorId
1260 )
1261 ) {
1262 return OCPPConstants.OCPP_TRIGGER_MESSAGE_RESPONSE_REJECTED;
1263 }
1264 try {
1265 switch (commandPayload.requestedMessage) {
1266 case OCPP16MessageTrigger.BootNotification:
1267 setTimeout(() => {
1268 chargingStation.ocppRequestService
1269 .requestHandler<OCPP16BootNotificationRequest, OCPP16BootNotificationResponse>(
1270 chargingStation,
1271 OCPP16RequestCommand.BOOT_NOTIFICATION,
1272 chargingStation.bootNotificationRequest,
1273 { skipBufferingOnError: true, triggerMessage: true }
1274 )
1275 .then((response) => {
1276 chargingStation.bootNotificationResponse = response;
1277 })
1278 .catch(Constants.EMPTY_FUNCTION);
1279 }, Constants.OCPP_TRIGGER_MESSAGE_DELAY);
1280 return OCPPConstants.OCPP_TRIGGER_MESSAGE_RESPONSE_ACCEPTED;
1281 case OCPP16MessageTrigger.Heartbeat:
1282 setTimeout(() => {
1283 chargingStation.ocppRequestService
1284 .requestHandler<OCPP16HeartbeatRequest, OCPP16HeartbeatResponse>(
1285 chargingStation,
1286 OCPP16RequestCommand.HEARTBEAT,
1287 null,
1288 {
1289 triggerMessage: true,
1290 }
1291 )
1292 .catch(Constants.EMPTY_FUNCTION);
1293 }, Constants.OCPP_TRIGGER_MESSAGE_DELAY);
1294 return OCPPConstants.OCPP_TRIGGER_MESSAGE_RESPONSE_ACCEPTED;
1295 case OCPP16MessageTrigger.StatusNotification:
1296 setTimeout(() => {
1297 if (!Utils.isNullOrUndefined(commandPayload?.connectorId)) {
1298 chargingStation.ocppRequestService
1299 .requestHandler<OCPP16StatusNotificationRequest, OCPP16StatusNotificationResponse>(
1300 chargingStation,
1301 OCPP16RequestCommand.STATUS_NOTIFICATION,
1302 {
1303 connectorId: commandPayload.connectorId,
1304 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
1305 status: chargingStation.getConnectorStatus(commandPayload.connectorId)?.status,
1306 },
1307 {
1308 triggerMessage: true,
1309 }
1310 )
1311 .catch(Constants.EMPTY_FUNCTION);
1312 } else {
1313 for (const connectorId of chargingStation.connectors.keys()) {
1314 chargingStation.ocppRequestService
1315 .requestHandler<
1316 OCPP16StatusNotificationRequest,
1317 OCPP16StatusNotificationResponse
1318 >(
1319 chargingStation,
1320 OCPP16RequestCommand.STATUS_NOTIFICATION,
1321 {
1322 connectorId,
1323 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
1324 status: chargingStation.getConnectorStatus(connectorId)?.status,
1325 },
1326 {
1327 triggerMessage: true,
1328 }
1329 )
1330 .catch(Constants.EMPTY_FUNCTION);
1331 }
1332 }
1333 }, Constants.OCPP_TRIGGER_MESSAGE_DELAY);
1334 return OCPPConstants.OCPP_TRIGGER_MESSAGE_RESPONSE_ACCEPTED;
1335 default:
1336 return OCPPConstants.OCPP_TRIGGER_MESSAGE_RESPONSE_NOT_IMPLEMENTED;
1337 }
1338 } catch (error) {
1339 return this.handleIncomingRequestError(
1340 chargingStation,
1341 OCPP16IncomingRequestCommand.TRIGGER_MESSAGE,
1342 error as Error,
1343 { errorResponse: OCPPConstants.OCPP_TRIGGER_MESSAGE_RESPONSE_REJECTED }
1344 );
1345 }
1346 }
1347
1348 private handleRequestDataTransfer(
1349 chargingStation: ChargingStation,
1350 commandPayload: OCPP16DataTransferRequest
1351 ): OCPP16DataTransferResponse {
1352 try {
1353 if (Object.values(OCPP16DataTransferVendorId).includes(commandPayload.vendorId)) {
1354 return {
1355 status: OCPP16DataTransferStatus.ACCEPTED,
1356 };
1357 }
1358 return {
1359 status: OCPP16DataTransferStatus.UNKNOWN_VENDOR_ID,
1360 };
1361 } catch (error) {
1362 return this.handleIncomingRequestError(
1363 chargingStation,
1364 OCPP16IncomingRequestCommand.DATA_TRANSFER,
1365 error as Error,
1366 { errorResponse: OCPPConstants.OCPP_DATA_TRANSFER_RESPONSE_REJECTED }
1367 );
1368 }
1369 }
1370 }