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