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