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