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