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