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