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