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