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