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