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