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