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