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