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