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