Strict null check fixes
[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 (
588 !Utils.isEmptyArray(chargingStation.getConnectorStatus(connectorId)?.chargingProfiles)
589 ) {
590 chargingStation
591 .getConnectorStatus(connectorId)
592 ?.chargingProfiles?.forEach((chargingProfile: OCPP16ChargingProfile, index: number) => {
593 let clearCurrentCP = false;
594 if (chargingProfile.chargingProfileId === commandPayload.id) {
595 clearCurrentCP = true;
596 }
597 if (
598 !commandPayload.chargingProfilePurpose &&
599 chargingProfile.stackLevel === commandPayload.stackLevel
600 ) {
601 clearCurrentCP = true;
602 }
603 if (
604 !chargingProfile.stackLevel &&
605 chargingProfile.chargingProfilePurpose === commandPayload.chargingProfilePurpose
606 ) {
607 clearCurrentCP = true;
608 }
609 if (
610 chargingProfile.stackLevel === commandPayload.stackLevel &&
611 chargingProfile.chargingProfilePurpose === commandPayload.chargingProfilePurpose
612 ) {
613 clearCurrentCP = true;
614 }
615 if (clearCurrentCP) {
616 connectorStatus?.chargingProfiles?.splice(index, 1);
617 logger.debug(
618 `${chargingStation.logPrefix()} Matching charging profile(s) cleared: %j`,
619 chargingProfile
620 );
621 clearedCP = true;
622 }
623 });
624 }
625 }
626 if (clearedCP) {
627 return OCPPConstants.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_ACCEPTED;
628 }
629 }
630 return OCPPConstants.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_UNKNOWN;
631 }
632
633 private async handleRequestChangeAvailability(
634 chargingStation: ChargingStation,
635 commandPayload: ChangeAvailabilityRequest
636 ): Promise<ChangeAvailabilityResponse> {
637 const connectorId: number = commandPayload.connectorId;
638 if (chargingStation.connectors.has(connectorId) === false) {
639 logger.error(
640 `${chargingStation.logPrefix()} Trying to change the availability of a non existing connector Id ${connectorId.toString()}`
641 );
642 return OCPPConstants.OCPP_AVAILABILITY_RESPONSE_REJECTED;
643 }
644 const chargePointStatus: OCPP16ChargePointStatus =
645 commandPayload.type === OCPP16AvailabilityType.OPERATIVE
646 ? OCPP16ChargePointStatus.AVAILABLE
647 : OCPP16ChargePointStatus.UNAVAILABLE;
648 if (connectorId === 0) {
649 let response: ChangeAvailabilityResponse = OCPPConstants.OCPP_AVAILABILITY_RESPONSE_ACCEPTED;
650 for (const id of chargingStation.connectors.keys()) {
651 if (chargingStation.getConnectorStatus(id)?.transactionStarted === true) {
652 response = OCPPConstants.OCPP_AVAILABILITY_RESPONSE_SCHEDULED;
653 }
654 chargingStation.getConnectorStatus(id).availability = commandPayload.type;
655 if (response === OCPPConstants.OCPP_AVAILABILITY_RESPONSE_ACCEPTED) {
656 await chargingStation.ocppRequestService.requestHandler<
657 OCPP16StatusNotificationRequest,
658 OCPP16StatusNotificationResponse
659 >(chargingStation, OCPP16RequestCommand.STATUS_NOTIFICATION, {
660 connectorId: id,
661 status: chargePointStatus,
662 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
663 });
664 chargingStation.getConnectorStatus(id).status = chargePointStatus;
665 }
666 }
667 return response;
668 } else if (
669 connectorId > 0 &&
670 (chargingStation.isChargingStationAvailable() === true ||
671 (chargingStation.isChargingStationAvailable() === false &&
672 commandPayload.type === OCPP16AvailabilityType.INOPERATIVE))
673 ) {
674 if (chargingStation.getConnectorStatus(connectorId)?.transactionStarted === true) {
675 chargingStation.getConnectorStatus(connectorId).availability = commandPayload.type;
676 return OCPPConstants.OCPP_AVAILABILITY_RESPONSE_SCHEDULED;
677 }
678 chargingStation.getConnectorStatus(connectorId).availability = commandPayload.type;
679 await chargingStation.ocppRequestService.requestHandler<
680 OCPP16StatusNotificationRequest,
681 OCPP16StatusNotificationResponse
682 >(chargingStation, OCPP16RequestCommand.STATUS_NOTIFICATION, {
683 connectorId,
684 status: chargePointStatus,
685 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
686 });
687 chargingStation.getConnectorStatus(connectorId).status = chargePointStatus;
688 return OCPPConstants.OCPP_AVAILABILITY_RESPONSE_ACCEPTED;
689 }
690 return OCPPConstants.OCPP_AVAILABILITY_RESPONSE_REJECTED;
691 }
692
693 private async handleRequestRemoteStartTransaction(
694 chargingStation: ChargingStation,
695 commandPayload: RemoteStartTransactionRequest
696 ): Promise<GenericResponse> {
697 const transactionConnectorId = commandPayload.connectorId;
698 if (chargingStation.connectors.has(transactionConnectorId) === true) {
699 const remoteStartTransactionLogMsg = `${chargingStation.logPrefix()} Transaction remotely STARTED on ${
700 chargingStation.stationInfo.chargingStationId
701 }#${transactionConnectorId.toString()} for idTag '${commandPayload.idTag}'`;
702 await chargingStation.ocppRequestService.requestHandler<
703 OCPP16StatusNotificationRequest,
704 OCPP16StatusNotificationResponse
705 >(chargingStation, OCPP16RequestCommand.STATUS_NOTIFICATION, {
706 connectorId: transactionConnectorId,
707 status: OCPP16ChargePointStatus.PREPARING,
708 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
709 });
710 const connectorStatus = chargingStation.getConnectorStatus(transactionConnectorId);
711 connectorStatus.status = OCPP16ChargePointStatus.PREPARING;
712 if (chargingStation.isChargingStationAvailable() === true) {
713 // Check if authorized
714 if (chargingStation.getAuthorizeRemoteTxRequests() === true) {
715 let authorized = false;
716 if (
717 chargingStation.getLocalAuthListEnabled() === true &&
718 chargingStation.hasAuthorizedTags() === true &&
719 chargingStation.authorizedTagsCache
720 .getAuthorizedTags(
721 ChargingStationUtils.getAuthorizationFile(chargingStation.stationInfo)
722 )
723 ?.find((idTag) => idTag === commandPayload.idTag)
724 ) {
725 connectorStatus.localAuthorizeIdTag = commandPayload.idTag;
726 connectorStatus.idTagLocalAuthorized = true;
727 authorized = true;
728 } else if (chargingStation.getMustAuthorizeAtRemoteStart() === true) {
729 connectorStatus.authorizeIdTag = commandPayload.idTag;
730 const authorizeResponse: OCPP16AuthorizeResponse =
731 await chargingStation.ocppRequestService.requestHandler<
732 OCPP16AuthorizeRequest,
733 OCPP16AuthorizeResponse
734 >(chargingStation, OCPP16RequestCommand.AUTHORIZE, {
735 idTag: commandPayload.idTag,
736 });
737 if (authorizeResponse?.idTagInfo?.status === OCPP16AuthorizationStatus.ACCEPTED) {
738 authorized = true;
739 }
740 } else {
741 logger.warn(
742 `${chargingStation.logPrefix()} The charging station configuration expects authorize at remote start transaction but local authorization or authorize isn't enabled`
743 );
744 }
745 if (authorized === true) {
746 // Authorization successful, start transaction
747 if (
748 this.setRemoteStartTransactionChargingProfile(
749 chargingStation,
750 transactionConnectorId,
751 commandPayload.chargingProfile
752 ) === true
753 ) {
754 connectorStatus.transactionRemoteStarted = true;
755 if (
756 (
757 await chargingStation.ocppRequestService.requestHandler<
758 OCPP16StartTransactionRequest,
759 OCPP16StartTransactionResponse
760 >(chargingStation, OCPP16RequestCommand.START_TRANSACTION, {
761 connectorId: transactionConnectorId,
762 idTag: commandPayload.idTag,
763 })
764 ).idTagInfo.status === OCPP16AuthorizationStatus.ACCEPTED
765 ) {
766 logger.debug(remoteStartTransactionLogMsg);
767 return OCPPConstants.OCPP_RESPONSE_ACCEPTED;
768 }
769 return this.notifyRemoteStartTransactionRejected(
770 chargingStation,
771 transactionConnectorId,
772 commandPayload.idTag
773 );
774 }
775 return this.notifyRemoteStartTransactionRejected(
776 chargingStation,
777 transactionConnectorId,
778 commandPayload.idTag
779 );
780 }
781 return this.notifyRemoteStartTransactionRejected(
782 chargingStation,
783 transactionConnectorId,
784 commandPayload.idTag
785 );
786 }
787 // No authorization check required, start transaction
788 if (
789 this.setRemoteStartTransactionChargingProfile(
790 chargingStation,
791 transactionConnectorId,
792 commandPayload.chargingProfile
793 ) === true
794 ) {
795 connectorStatus.transactionRemoteStarted = true;
796 if (
797 (
798 await chargingStation.ocppRequestService.requestHandler<
799 OCPP16StartTransactionRequest,
800 OCPP16StartTransactionResponse
801 >(chargingStation, OCPP16RequestCommand.START_TRANSACTION, {
802 connectorId: transactionConnectorId,
803 idTag: commandPayload.idTag,
804 })
805 ).idTagInfo.status === OCPP16AuthorizationStatus.ACCEPTED
806 ) {
807 logger.debug(remoteStartTransactionLogMsg);
808 return OCPPConstants.OCPP_RESPONSE_ACCEPTED;
809 }
810 return this.notifyRemoteStartTransactionRejected(
811 chargingStation,
812 transactionConnectorId,
813 commandPayload.idTag
814 );
815 }
816 return this.notifyRemoteStartTransactionRejected(
817 chargingStation,
818 transactionConnectorId,
819 commandPayload.idTag
820 );
821 }
822 return this.notifyRemoteStartTransactionRejected(
823 chargingStation,
824 transactionConnectorId,
825 commandPayload.idTag
826 );
827 }
828 return this.notifyRemoteStartTransactionRejected(
829 chargingStation,
830 transactionConnectorId,
831 commandPayload.idTag
832 );
833 }
834
835 private async notifyRemoteStartTransactionRejected(
836 chargingStation: ChargingStation,
837 connectorId: number,
838 idTag: string
839 ): Promise<GenericResponse> {
840 if (
841 chargingStation.getConnectorStatus(connectorId)?.status !== OCPP16ChargePointStatus.AVAILABLE
842 ) {
843 await chargingStation.ocppRequestService.requestHandler<
844 OCPP16StatusNotificationRequest,
845 OCPP16StatusNotificationResponse
846 >(chargingStation, OCPP16RequestCommand.STATUS_NOTIFICATION, {
847 connectorId,
848 status: OCPP16ChargePointStatus.AVAILABLE,
849 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
850 });
851 chargingStation.getConnectorStatus(connectorId).status = OCPP16ChargePointStatus.AVAILABLE;
852 }
853 logger.warn(
854 `${chargingStation.logPrefix()} Remote starting transaction REJECTED on connector Id ${connectorId.toString()}, idTag '${idTag}', availability '${
855 chargingStation.getConnectorStatus(connectorId)?.availability
856 }', status '${chargingStation.getConnectorStatus(connectorId)?.status}'`
857 );
858 return OCPPConstants.OCPP_RESPONSE_REJECTED;
859 }
860
861 private setRemoteStartTransactionChargingProfile(
862 chargingStation: ChargingStation,
863 connectorId: number,
864 cp: OCPP16ChargingProfile
865 ): boolean {
866 if (cp && cp.chargingProfilePurpose === ChargingProfilePurposeType.TX_PROFILE) {
867 OCPP16ServiceUtils.setChargingProfile(chargingStation, connectorId, cp);
868 logger.debug(
869 `${chargingStation.logPrefix()} Charging profile(s) set at remote start transaction on connector id ${connectorId}: %j`,
870 cp
871 );
872 return true;
873 } else if (cp && cp.chargingProfilePurpose !== ChargingProfilePurposeType.TX_PROFILE) {
874 logger.warn(
875 `${chargingStation.logPrefix()} Not allowed to set ${
876 cp.chargingProfilePurpose
877 } charging profile(s) at remote start transaction`
878 );
879 return false;
880 } else if (!cp) {
881 return true;
882 }
883 }
884
885 private async handleRequestRemoteStopTransaction(
886 chargingStation: ChargingStation,
887 commandPayload: RemoteStopTransactionRequest
888 ): Promise<GenericResponse> {
889 const transactionId = commandPayload.transactionId;
890 for (const connectorId of chargingStation.connectors.keys()) {
891 if (
892 connectorId > 0 &&
893 chargingStation.getConnectorStatus(connectorId)?.transactionId === transactionId
894 ) {
895 await chargingStation.ocppRequestService.requestHandler<
896 OCPP16StatusNotificationRequest,
897 OCPP16StatusNotificationResponse
898 >(chargingStation, OCPP16RequestCommand.STATUS_NOTIFICATION, {
899 connectorId,
900 status: OCPP16ChargePointStatus.FINISHING,
901 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
902 });
903 chargingStation.getConnectorStatus(connectorId).status = OCPP16ChargePointStatus.FINISHING;
904 const stopResponse = await chargingStation.stopTransactionOnConnector(
905 connectorId,
906 OCPP16StopTransactionReason.REMOTE
907 );
908 if (stopResponse.idTagInfo?.status === OCPP16AuthorizationStatus.ACCEPTED) {
909 return OCPPConstants.OCPP_RESPONSE_ACCEPTED;
910 }
911 return OCPPConstants.OCPP_RESPONSE_REJECTED;
912 }
913 }
914 logger.warn(
915 `${chargingStation.logPrefix()} Trying to remote stop a non existing transaction ${transactionId.toString()}`
916 );
917 return OCPPConstants.OCPP_RESPONSE_REJECTED;
918 }
919
920 private handleRequestUpdateFirmware(
921 chargingStation: ChargingStation,
922 commandPayload: OCPP16UpdateFirmwareRequest
923 ): OCPP16UpdateFirmwareResponse {
924 if (
925 OCPP16ServiceUtils.checkFeatureProfile(
926 chargingStation,
927 OCPP16SupportedFeatureProfiles.FirmwareManagement,
928 OCPP16IncomingRequestCommand.UPDATE_FIRMWARE
929 ) === false
930 ) {
931 logger.warn(
932 `${chargingStation.logPrefix()} ${moduleName}.handleRequestUpdateFirmware: Cannot simulate firmware update: feature profile not supported`
933 );
934 return OCPPConstants.OCPP_RESPONSE_EMPTY;
935 }
936 if (
937 !Utils.isNullOrUndefined(chargingStation.stationInfo.firmwareStatus) &&
938 chargingStation.stationInfo.firmwareStatus !== OCPP16FirmwareStatus.Installed
939 ) {
940 logger.warn(
941 `${chargingStation.logPrefix()} ${moduleName}.handleRequestUpdateFirmware: Cannot simulate firmware update: firmware update is already in progress`
942 );
943 return OCPPConstants.OCPP_RESPONSE_EMPTY;
944 }
945 const retrieveDate = Utils.convertToDate(commandPayload.retrieveDate);
946 const now = Date.now();
947 if (retrieveDate?.getTime() <= now) {
948 this.asyncResource
949 .runInAsyncScope(
950 this.updateFirmware.bind(this) as (
951 this: OCPP16IncomingRequestService,
952 ...args: any[]
953 ) => Promise<void>,
954 this,
955 chargingStation
956 )
957 .catch(() => {
958 /* This is intentional */
959 });
960 } else {
961 setTimeout(() => {
962 this.updateFirmware(chargingStation).catch(() => {
963 /* Intentional */
964 });
965 }, retrieveDate?.getTime() - now);
966 }
967 return OCPPConstants.OCPP_RESPONSE_EMPTY;
968 }
969
970 private async updateFirmware(
971 chargingStation: ChargingStation,
972 maxDelay = 30,
973 minDelay = 15
974 ): Promise<void> {
975 chargingStation.stopAutomaticTransactionGenerator();
976 for (const connectorId of chargingStation.connectors.keys()) {
977 if (
978 connectorId > 0 &&
979 chargingStation.getConnectorStatus(connectorId)?.transactionStarted === false
980 ) {
981 await chargingStation.ocppRequestService.requestHandler<
982 OCPP16StatusNotificationRequest,
983 OCPP16StatusNotificationResponse
984 >(chargingStation, OCPP16RequestCommand.STATUS_NOTIFICATION, {
985 connectorId,
986 status: OCPP16ChargePointStatus.UNAVAILABLE,
987 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
988 });
989 chargingStation.getConnectorStatus(connectorId).status =
990 OCPP16ChargePointStatus.UNAVAILABLE;
991 }
992 }
993 if (
994 chargingStation.stationInfo?.firmwareUpgrade?.failureStatus &&
995 !Utils.isEmptyString(chargingStation.stationInfo?.firmwareUpgrade?.failureStatus)
996 ) {
997 await chargingStation.ocppRequestService.requestHandler<
998 OCPP16FirmwareStatusNotificationRequest,
999 OCPP16FirmwareStatusNotificationResponse
1000 >(chargingStation, OCPP16RequestCommand.FIRMWARE_STATUS_NOTIFICATION, {
1001 status: chargingStation.stationInfo?.firmwareUpgrade?.failureStatus,
1002 });
1003 return;
1004 }
1005 await chargingStation.ocppRequestService.requestHandler<
1006 OCPP16FirmwareStatusNotificationRequest,
1007 OCPP16FirmwareStatusNotificationResponse
1008 >(chargingStation, OCPP16RequestCommand.FIRMWARE_STATUS_NOTIFICATION, {
1009 status: OCPP16FirmwareStatus.Downloading,
1010 });
1011 chargingStation.stationInfo.firmwareStatus = OCPP16FirmwareStatus.Downloading;
1012 await Utils.sleep(Utils.getRandomInteger(maxDelay, minDelay) * 1000);
1013 await chargingStation.ocppRequestService.requestHandler<
1014 OCPP16FirmwareStatusNotificationRequest,
1015 OCPP16FirmwareStatusNotificationResponse
1016 >(chargingStation, OCPP16RequestCommand.FIRMWARE_STATUS_NOTIFICATION, {
1017 status: OCPP16FirmwareStatus.Downloaded,
1018 });
1019 chargingStation.stationInfo.firmwareStatus = OCPP16FirmwareStatus.Downloaded;
1020 await Utils.sleep(Utils.getRandomInteger(maxDelay, minDelay) * 1000);
1021 await chargingStation.ocppRequestService.requestHandler<
1022 OCPP16FirmwareStatusNotificationRequest,
1023 OCPP16FirmwareStatusNotificationResponse
1024 >(chargingStation, OCPP16RequestCommand.FIRMWARE_STATUS_NOTIFICATION, {
1025 status: OCPP16FirmwareStatus.Installing,
1026 });
1027 chargingStation.stationInfo.firmwareStatus = OCPP16FirmwareStatus.Installing;
1028 if (chargingStation.stationInfo?.firmwareUpgrade?.reset === true) {
1029 await Utils.sleep(Utils.getRandomInteger(maxDelay, minDelay) * 1000);
1030 await chargingStation.reset(OCPP16StopTransactionReason.REBOOT);
1031 }
1032 }
1033
1034 private async handleRequestGetDiagnostics(
1035 chargingStation: ChargingStation,
1036 commandPayload: GetDiagnosticsRequest
1037 ): Promise<GetDiagnosticsResponse> {
1038 if (
1039 OCPP16ServiceUtils.checkFeatureProfile(
1040 chargingStation,
1041 OCPP16SupportedFeatureProfiles.FirmwareManagement,
1042 OCPP16IncomingRequestCommand.GET_DIAGNOSTICS
1043 ) === false
1044 ) {
1045 logger.warn(
1046 `${chargingStation.logPrefix()} ${moduleName}.handleRequestGetDiagnostics: Cannot get diagnostics: feature profile not supported`
1047 );
1048 return OCPPConstants.OCPP_RESPONSE_EMPTY;
1049 }
1050 const uri = new URL(commandPayload.location);
1051 if (uri.protocol.startsWith('ftp:')) {
1052 let ftpClient: Client;
1053 try {
1054 const logFiles = fs
1055 .readdirSync(path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../../../'))
1056 .filter((file) => file.endsWith('.log'))
1057 .map((file) => path.join('./', file));
1058 const diagnosticsArchive = `${chargingStation.stationInfo.chargingStationId}_logs.tar.gz`;
1059 tar.create({ gzip: true }, logFiles).pipe(fs.createWriteStream(diagnosticsArchive));
1060 ftpClient = new Client();
1061 const accessResponse = await ftpClient.access({
1062 host: uri.host,
1063 ...(!Utils.isEmptyString(uri.port) && { port: Utils.convertToInt(uri.port) }),
1064 ...(!Utils.isEmptyString(uri.username) && { user: uri.username }),
1065 ...(!Utils.isEmptyString(uri.password) && { password: uri.password }),
1066 });
1067 let uploadResponse: FTPResponse;
1068 if (accessResponse.code === 220) {
1069 ftpClient.trackProgress((info) => {
1070 logger.info(
1071 `${chargingStation.logPrefix()} ${
1072 info.bytes / 1024
1073 } bytes transferred from diagnostics archive ${info.name}`
1074 );
1075 chargingStation.ocppRequestService
1076 .requestHandler<
1077 OCPP16DiagnosticsStatusNotificationRequest,
1078 OCPP16DiagnosticsStatusNotificationResponse
1079 >(chargingStation, OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION, {
1080 status: OCPP16DiagnosticsStatus.Uploading,
1081 })
1082 .catch((error) => {
1083 logger.error(
1084 `${chargingStation.logPrefix()} ${moduleName}.handleRequestGetDiagnostics: Error while sending '${
1085 OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION
1086 }'`,
1087 error
1088 );
1089 });
1090 });
1091 uploadResponse = await ftpClient.uploadFrom(
1092 path.join(
1093 path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../../../'),
1094 diagnosticsArchive
1095 ),
1096 `${uri.pathname}${diagnosticsArchive}`
1097 );
1098 if (uploadResponse.code === 226) {
1099 await chargingStation.ocppRequestService.requestHandler<
1100 OCPP16DiagnosticsStatusNotificationRequest,
1101 OCPP16DiagnosticsStatusNotificationResponse
1102 >(chargingStation, OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION, {
1103 status: OCPP16DiagnosticsStatus.Uploaded,
1104 });
1105 if (ftpClient) {
1106 ftpClient.close();
1107 }
1108 return { fileName: diagnosticsArchive };
1109 }
1110 throw new OCPPError(
1111 ErrorType.GENERIC_ERROR,
1112 `Diagnostics transfer failed with error code ${accessResponse.code.toString()}${
1113 uploadResponse?.code && `|${uploadResponse?.code.toString()}`
1114 }`,
1115 OCPP16IncomingRequestCommand.GET_DIAGNOSTICS
1116 );
1117 }
1118 throw new OCPPError(
1119 ErrorType.GENERIC_ERROR,
1120 `Diagnostics transfer failed with error code ${accessResponse.code.toString()}${
1121 uploadResponse?.code && `|${uploadResponse?.code.toString()}`
1122 }`,
1123 OCPP16IncomingRequestCommand.GET_DIAGNOSTICS
1124 );
1125 } catch (error) {
1126 await chargingStation.ocppRequestService.requestHandler<
1127 OCPP16DiagnosticsStatusNotificationRequest,
1128 OCPP16DiagnosticsStatusNotificationResponse
1129 >(chargingStation, OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION, {
1130 status: OCPP16DiagnosticsStatus.UploadFailed,
1131 });
1132 if (ftpClient) {
1133 ftpClient.close();
1134 }
1135 return this.handleIncomingRequestError(
1136 chargingStation,
1137 OCPP16IncomingRequestCommand.GET_DIAGNOSTICS,
1138 error as Error,
1139 { errorResponse: OCPPConstants.OCPP_RESPONSE_EMPTY }
1140 );
1141 }
1142 } else {
1143 logger.error(
1144 `${chargingStation.logPrefix()} Unsupported protocol ${
1145 uri.protocol
1146 } to transfer the diagnostic logs archive`
1147 );
1148 await chargingStation.ocppRequestService.requestHandler<
1149 OCPP16DiagnosticsStatusNotificationRequest,
1150 OCPP16DiagnosticsStatusNotificationResponse
1151 >(chargingStation, OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION, {
1152 status: OCPP16DiagnosticsStatus.UploadFailed,
1153 });
1154 return OCPPConstants.OCPP_RESPONSE_EMPTY;
1155 }
1156 }
1157
1158 private handleRequestTriggerMessage(
1159 chargingStation: ChargingStation,
1160 commandPayload: OCPP16TriggerMessageRequest
1161 ): OCPP16TriggerMessageResponse {
1162 if (
1163 !OCPP16ServiceUtils.checkFeatureProfile(
1164 chargingStation,
1165 OCPP16SupportedFeatureProfiles.RemoteTrigger,
1166 OCPP16IncomingRequestCommand.TRIGGER_MESSAGE
1167 ) ||
1168 !OCPP16ServiceUtils.isMessageTriggerSupported(
1169 chargingStation,
1170 commandPayload.requestedMessage
1171 )
1172 ) {
1173 return OCPPConstants.OCPP_TRIGGER_MESSAGE_RESPONSE_NOT_IMPLEMENTED;
1174 }
1175 if (
1176 !OCPP16ServiceUtils.isConnectorIdValid(
1177 chargingStation,
1178 OCPP16IncomingRequestCommand.TRIGGER_MESSAGE,
1179 commandPayload.connectorId
1180 )
1181 ) {
1182 return OCPPConstants.OCPP_TRIGGER_MESSAGE_RESPONSE_REJECTED;
1183 }
1184 try {
1185 switch (commandPayload.requestedMessage) {
1186 case OCPP16MessageTrigger.BootNotification:
1187 setTimeout(() => {
1188 chargingStation.ocppRequestService
1189 .requestHandler<OCPP16BootNotificationRequest, OCPP16BootNotificationResponse>(
1190 chargingStation,
1191 OCPP16RequestCommand.BOOT_NOTIFICATION,
1192 chargingStation.bootNotificationRequest,
1193 { skipBufferingOnError: true, triggerMessage: true }
1194 )
1195 .then((response) => {
1196 chargingStation.bootNotificationResponse = response;
1197 })
1198 .catch(() => {
1199 /* This is intentional */
1200 });
1201 }, Constants.OCPP_TRIGGER_MESSAGE_DELAY);
1202 return OCPPConstants.OCPP_TRIGGER_MESSAGE_RESPONSE_ACCEPTED;
1203 case OCPP16MessageTrigger.Heartbeat:
1204 setTimeout(() => {
1205 chargingStation.ocppRequestService
1206 .requestHandler<OCPP16HeartbeatRequest, OCPP16HeartbeatResponse>(
1207 chargingStation,
1208 OCPP16RequestCommand.HEARTBEAT,
1209 null,
1210 {
1211 triggerMessage: true,
1212 }
1213 )
1214 .catch(() => {
1215 /* This is intentional */
1216 });
1217 }, Constants.OCPP_TRIGGER_MESSAGE_DELAY);
1218 return OCPPConstants.OCPP_TRIGGER_MESSAGE_RESPONSE_ACCEPTED;
1219 case OCPP16MessageTrigger.StatusNotification:
1220 setTimeout(() => {
1221 if (commandPayload?.connectorId) {
1222 chargingStation.ocppRequestService
1223 .requestHandler<OCPP16StatusNotificationRequest, OCPP16StatusNotificationResponse>(
1224 chargingStation,
1225 OCPP16RequestCommand.STATUS_NOTIFICATION,
1226 {
1227 connectorId: commandPayload.connectorId,
1228 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
1229 status: chargingStation.getConnectorStatus(commandPayload.connectorId)?.status,
1230 },
1231 {
1232 triggerMessage: true,
1233 }
1234 )
1235 .catch(() => {
1236 /* This is intentional */
1237 });
1238 } else {
1239 for (const connectorId of chargingStation.connectors.keys()) {
1240 chargingStation.ocppRequestService
1241 .requestHandler<
1242 OCPP16StatusNotificationRequest,
1243 OCPP16StatusNotificationResponse
1244 >(
1245 chargingStation,
1246 OCPP16RequestCommand.STATUS_NOTIFICATION,
1247 {
1248 connectorId,
1249 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
1250 status: chargingStation.getConnectorStatus(connectorId)?.status,
1251 },
1252 {
1253 triggerMessage: true,
1254 }
1255 )
1256 .catch(() => {
1257 /* This is intentional */
1258 });
1259 }
1260 }
1261 }, Constants.OCPP_TRIGGER_MESSAGE_DELAY);
1262 return OCPPConstants.OCPP_TRIGGER_MESSAGE_RESPONSE_ACCEPTED;
1263 default:
1264 return OCPPConstants.OCPP_TRIGGER_MESSAGE_RESPONSE_NOT_IMPLEMENTED;
1265 }
1266 } catch (error) {
1267 return this.handleIncomingRequestError(
1268 chargingStation,
1269 OCPP16IncomingRequestCommand.TRIGGER_MESSAGE,
1270 error as Error,
1271 { errorResponse: OCPPConstants.OCPP_TRIGGER_MESSAGE_RESPONSE_REJECTED }
1272 );
1273 }
1274 }
1275
1276 private handleRequestDataTransfer(
1277 chargingStation: ChargingStation,
1278 commandPayload: OCPP16DataTransferRequest
1279 ): OCPP16DataTransferResponse {
1280 try {
1281 if (Object.values(OCPP16DataTransferVendorId).includes(commandPayload.vendorId)) {
1282 return {
1283 status: OCPP16DataTransferStatus.ACCEPTED,
1284 };
1285 }
1286 return {
1287 status: OCPP16DataTransferStatus.UNKNOWN_VENDOR_ID,
1288 };
1289 } catch (error) {
1290 return this.handleIncomingRequestError(
1291 chargingStation,
1292 OCPP16IncomingRequestCommand.DATA_TRANSFER,
1293 error as Error,
1294 { errorResponse: OCPPConstants.OCPP_DATA_TRANSFER_RESPONSE_REJECTED }
1295 );
1296 }
1297 }
1298
1299 private parseJsonSchemaFile<T extends JsonType>(relativePath: string): JSONSchemaType<T> {
1300 return JSON.parse(
1301 fs.readFileSync(
1302 path.resolve(path.dirname(fileURLToPath(import.meta.url)), relativePath),
1303 'utf8'
1304 )
1305 ) as JSONSchemaType<T>;
1306 }
1307 }