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