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