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