Add trigger message type feature flag in charging station template
[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,
e7aeea18 31 OCPP16AvailabilityType,
ef6fa3fb 32 OCPP16BootNotificationRequest,
e3018bc4 33 OCPP16ClearCacheRequest,
ef6fa3fb 34 OCPP16HeartbeatRequest,
e7aeea18 35 OCPP16IncomingRequestCommand,
c60ed4b8 36 OCPP16MessageTrigger,
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 (
3a13fc92
JB
284 chargingStation.getOcppStrictCompliance() === true &&
285 chargingStation.isInPendingState() === true &&
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 (
ed6cfcff
JB
301 chargingStation.isRegistered() === true ||
302 (chargingStation.getOcppStrictCompliance() === false &&
303 chargingStation.isInUnknownState() === true)
e7aeea18 304 ) {
65554cc3 305 if (
ed6cfcff
JB
306 this.incomingRequestHandlers.has(commandName) === true &&
307 OCPP16ServiceUtils.isIncomingRequestCommandSupported(chargingStation, commandName) === true
65554cc3 308 ) {
124f3553 309 try {
9c5c4195 310 this.validatePayload(chargingStation, commandName, commandPayload);
c75a6675 311 // Call the method to build the response
08f130a0
JB
312 response = await this.incomingRequestHandlers.get(commandName)(
313 chargingStation,
314 commandPayload
315 );
124f3553
JB
316 } catch (error) {
317 // Log
6c8f5d90
JB
318 logger.error(
319 `${chargingStation.logPrefix()} ${moduleName}.incomingRequestHandler: Handle incoming request error:`,
320 error
321 );
124f3553
JB
322 throw error;
323 }
324 } else {
325 // Throw exception
e7aeea18
JB
326 throw new OCPPError(
327 ErrorType.NOT_IMPLEMENTED,
e3018bc4 328 `${commandName} is not implemented to handle request PDU ${JSON.stringify(
e7aeea18
JB
329 commandPayload,
330 null,
331 2
332 )}`,
7369e417
JB
333 commandName,
334 commandPayload
e7aeea18 335 );
c0560973
JB
336 }
337 } else {
e7aeea18
JB
338 throw new OCPPError(
339 ErrorType.SECURITY_ERROR,
e3018bc4 340 `${commandName} cannot be issued to handle request PDU ${JSON.stringify(
e7aeea18
JB
341 commandPayload,
342 null,
343 2
344 )} while the charging station is not registered on the central server.`,
7369e417
JB
345 commandName,
346 commandPayload
e7aeea18 347 );
c0560973 348 }
c75a6675 349 // Send the built response
08f130a0
JB
350 await chargingStation.ocppRequestService.sendResponse(
351 chargingStation,
352 messageId,
353 response,
354 commandName
355 );
c0560973
JB
356 }
357
9c5c4195
JB
358 private validatePayload(
359 chargingStation: ChargingStation,
360 commandName: OCPP16IncomingRequestCommand,
361 commandPayload: JsonType
362 ): boolean {
363 if (this.jsonSchemas.has(commandName)) {
364 return this.validateIncomingRequestPayload(
365 chargingStation,
366 commandName,
367 this.jsonSchemas.get(commandName),
368 commandPayload
369 );
370 }
371 logger.warn(
ee307db5 372 `${chargingStation.logPrefix()} ${moduleName}.validatePayload: No JSON schema found for command ${commandName} PDU validation`
9c5c4195
JB
373 );
374 return false;
375 }
376
c0560973 377 // Simulate charging station restart
08f130a0
JB
378 private handleRequestReset(
379 chargingStation: ChargingStation,
380 commandPayload: ResetRequest
381 ): DefaultResponse {
64818750
JB
382 this.asyncResource
383 .runInAsyncScope(
384 chargingStation.reset.bind(chargingStation) as (
385 this: ChargingStation,
386 ...args: any[]
387 ) => Promise<void>,
388 chargingStation,
389 (commandPayload.type + 'Reset') as OCPP16StopTransactionReason
390 )
391 .catch(() => {
392 /* This is intentional */
393 });
e7aeea18 394 logger.info(
08f130a0 395 `${chargingStation.logPrefix()} ${
e7aeea18
JB
396 commandPayload.type
397 } reset command received, simulating it. The station will be back online in ${Utils.formatDurationMilliSeconds(
08f130a0 398 chargingStation.stationInfo.resetTime
e7aeea18
JB
399 )}`
400 );
c0560973
JB
401 return Constants.OCPP_RESPONSE_ACCEPTED;
402 }
403
a36bad10
JB
404 private handleRequestClearCache(chargingStation: ChargingStation): DefaultResponse {
405 chargingStation.authorizedTagsCache.deleteAuthorizedTags(
406 ChargingStationUtils.getAuthorizationFile(chargingStation.stationInfo)
407 );
c0560973
JB
408 return Constants.OCPP_RESPONSE_ACCEPTED;
409 }
410
e7aeea18 411 private async handleRequestUnlockConnector(
08f130a0 412 chargingStation: ChargingStation,
e7aeea18
JB
413 commandPayload: UnlockConnectorRequest
414 ): Promise<UnlockConnectorResponse> {
c0560973 415 const connectorId = commandPayload.connectorId;
c60ed4b8
JB
416 if (chargingStation.connectors.has(connectorId) === false) {
417 logger.error(
418 `${chargingStation.logPrefix()} Trying to unlock a non existing connector Id ${connectorId.toString()}`
419 );
420 return Constants.OCPP_RESPONSE_UNLOCK_NOT_SUPPORTED;
421 }
c0560973 422 if (connectorId === 0) {
e7aeea18 423 logger.error(
c60ed4b8 424 chargingStation.logPrefix() + ' Trying to unlock connector Id ' + connectorId.toString()
e7aeea18 425 );
c0560973
JB
426 return Constants.OCPP_RESPONSE_UNLOCK_NOT_SUPPORTED;
427 }
5e3cb728
JB
428 if (chargingStation.getConnectorStatus(connectorId)?.transactionStarted === true) {
429 const stopResponse = await chargingStation.stopTransactionOnConnector(
430 connectorId,
431 OCPP16StopTransactionReason.UNLOCK_COMMAND
432 );
c0560973
JB
433 if (stopResponse.idTagInfo?.status === OCPP16AuthorizationStatus.ACCEPTED) {
434 return Constants.OCPP_RESPONSE_UNLOCKED;
435 }
436 return Constants.OCPP_RESPONSE_UNLOCK_FAILED;
437 }
08f130a0 438 await chargingStation.ocppRequestService.requestHandler<
ef6fa3fb
JB
439 OCPP16StatusNotificationRequest,
440 OCPP16StatusNotificationResponse
08f130a0 441 >(chargingStation, OCPP16RequestCommand.STATUS_NOTIFICATION, {
ef6fa3fb
JB
442 connectorId,
443 status: OCPP16ChargePointStatus.AVAILABLE,
444 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
445 });
08f130a0 446 chargingStation.getConnectorStatus(connectorId).status = OCPP16ChargePointStatus.AVAILABLE;
c0560973
JB
447 return Constants.OCPP_RESPONSE_UNLOCKED;
448 }
449
e7aeea18 450 private handleRequestGetConfiguration(
08f130a0 451 chargingStation: ChargingStation,
e7aeea18
JB
452 commandPayload: GetConfigurationRequest
453 ): GetConfigurationResponse {
c0560973
JB
454 const configurationKey: OCPPConfigurationKey[] = [];
455 const unknownKey: string[] = [];
a723e7e9 456 if (Utils.isEmptyArray(commandPayload.key) === true) {
08f130a0 457 for (const configuration of chargingStation.ocppConfiguration.configurationKey) {
a723e7e9 458 if (Utils.isUndefined(configuration.visible) === true) {
7f7b65ca 459 configuration.visible = true;
c0560973 460 }
da8629bb 461 if (configuration.visible === false) {
c0560973
JB
462 continue;
463 }
464 configurationKey.push({
7f7b65ca
JB
465 key: configuration.key,
466 readonly: configuration.readonly,
467 value: configuration.value,
c0560973
JB
468 });
469 }
470 } else {
471 for (const key of commandPayload.key) {
17ac262c
JB
472 const keyFound = ChargingStationConfigurationUtils.getConfigurationKey(
473 chargingStation,
474 key
475 );
c0560973 476 if (keyFound) {
a723e7e9 477 if (Utils.isUndefined(keyFound.visible) === true) {
c0560973
JB
478 keyFound.visible = true;
479 }
a723e7e9 480 if (keyFound.visible === false) {
c0560973
JB
481 continue;
482 }
483 configurationKey.push({
484 key: keyFound.key,
485 readonly: keyFound.readonly,
486 value: keyFound.value,
487 });
488 } else {
489 unknownKey.push(key);
490 }
491 }
492 }
493 return {
494 configurationKey,
495 unknownKey,
496 };
497 }
498
e7aeea18 499 private handleRequestChangeConfiguration(
08f130a0 500 chargingStation: ChargingStation,
e7aeea18
JB
501 commandPayload: ChangeConfigurationRequest
502 ): ChangeConfigurationResponse {
17ac262c
JB
503 const keyToChange = ChargingStationConfigurationUtils.getConfigurationKey(
504 chargingStation,
505 commandPayload.key,
506 true
507 );
c0560973
JB
508 if (!keyToChange) {
509 return Constants.OCPP_CONFIGURATION_RESPONSE_NOT_SUPPORTED;
510 } else if (keyToChange && keyToChange.readonly) {
511 return Constants.OCPP_CONFIGURATION_RESPONSE_REJECTED;
512 } else if (keyToChange && !keyToChange.readonly) {
c0560973 513 let valueChanged = false;
a95873d8 514 if (keyToChange.value !== commandPayload.value) {
17ac262c
JB
515 ChargingStationConfigurationUtils.setConfigurationKeyValue(
516 chargingStation,
517 commandPayload.key,
518 commandPayload.value,
519 true
520 );
c0560973
JB
521 valueChanged = true;
522 }
523 let triggerHeartbeatRestart = false;
524 if (keyToChange.key === OCPP16StandardParametersKey.HeartBeatInterval && valueChanged) {
17ac262c
JB
525 ChargingStationConfigurationUtils.setConfigurationKeyValue(
526 chargingStation,
e7aeea18
JB
527 OCPP16StandardParametersKey.HeartbeatInterval,
528 commandPayload.value
529 );
c0560973
JB
530 triggerHeartbeatRestart = true;
531 }
532 if (keyToChange.key === OCPP16StandardParametersKey.HeartbeatInterval && valueChanged) {
17ac262c
JB
533 ChargingStationConfigurationUtils.setConfigurationKeyValue(
534 chargingStation,
e7aeea18
JB
535 OCPP16StandardParametersKey.HeartBeatInterval,
536 commandPayload.value
537 );
c0560973
JB
538 triggerHeartbeatRestart = true;
539 }
540 if (triggerHeartbeatRestart) {
08f130a0 541 chargingStation.restartHeartbeat();
c0560973
JB
542 }
543 if (keyToChange.key === OCPP16StandardParametersKey.WebSocketPingInterval && valueChanged) {
08f130a0 544 chargingStation.restartWebSocketPing();
c0560973
JB
545 }
546 if (keyToChange.reboot) {
547 return Constants.OCPP_CONFIGURATION_RESPONSE_REBOOT_REQUIRED;
548 }
549 return Constants.OCPP_CONFIGURATION_RESPONSE_ACCEPTED;
550 }
551 }
552
e7aeea18 553 private handleRequestSetChargingProfile(
08f130a0 554 chargingStation: ChargingStation,
e7aeea18
JB
555 commandPayload: SetChargingProfileRequest
556 ): SetChargingProfileResponse {
370ae4ee 557 if (
1789ba2c 558 OCPP16ServiceUtils.checkFeatureProfile(
08f130a0 559 chargingStation,
370ae4ee
JB
560 OCPP16SupportedFeatureProfiles.SmartCharging,
561 OCPP16IncomingRequestCommand.SET_CHARGING_PROFILE
1789ba2c 562 ) === false
370ae4ee 563 ) {
68cb8b91
JB
564 return Constants.OCPP_SET_CHARGING_PROFILE_RESPONSE_NOT_SUPPORTED;
565 }
1789ba2c 566 if (chargingStation.connectors.has(commandPayload.connectorId) === false) {
e7aeea18 567 logger.error(
08f130a0 568 `${chargingStation.logPrefix()} Trying to set charging profile(s) to a non existing connector Id ${
e7aeea18
JB
569 commandPayload.connectorId
570 }`
571 );
c0560973
JB
572 return Constants.OCPP_SET_CHARGING_PROFILE_RESPONSE_REJECTED;
573 }
e7aeea18
JB
574 if (
575 commandPayload.csChargingProfiles.chargingProfilePurpose ===
576 ChargingProfilePurposeType.CHARGE_POINT_MAX_PROFILE &&
577 commandPayload.connectorId !== 0
578 ) {
c0560973
JB
579 return Constants.OCPP_SET_CHARGING_PROFILE_RESPONSE_REJECTED;
580 }
e7aeea18
JB
581 if (
582 commandPayload.csChargingProfiles.chargingProfilePurpose ===
583 ChargingProfilePurposeType.TX_PROFILE &&
584 (commandPayload.connectorId === 0 ||
5e3cb728
JB
585 chargingStation.getConnectorStatus(commandPayload.connectorId)?.transactionStarted ===
586 false)
e7aeea18 587 ) {
db0af086
JB
588 logger.error(
589 `${chargingStation.logPrefix()} Trying to set transaction charging profile(s) on connector ${
590 commandPayload.connectorId
591 } without a started transaction`
592 );
c0560973
JB
593 return Constants.OCPP_SET_CHARGING_PROFILE_RESPONSE_REJECTED;
594 }
ed3d2808 595 OCPP16ServiceUtils.setChargingProfile(
7cb5b17f 596 chargingStation,
e7aeea18
JB
597 commandPayload.connectorId,
598 commandPayload.csChargingProfiles
599 );
600 logger.debug(
08f130a0 601 `${chargingStation.logPrefix()} Charging profile(s) set on connector id ${
ad8537a7
JB
602 commandPayload.connectorId
603 }, dump their stack: %j`,
08f130a0 604 chargingStation.getConnectorStatus(commandPayload.connectorId).chargingProfiles
e7aeea18 605 );
c0560973
JB
606 return Constants.OCPP_SET_CHARGING_PROFILE_RESPONSE_ACCEPTED;
607 }
608
e7aeea18 609 private handleRequestClearChargingProfile(
08f130a0 610 chargingStation: ChargingStation,
e7aeea18
JB
611 commandPayload: ClearChargingProfileRequest
612 ): ClearChargingProfileResponse {
370ae4ee 613 if (
a36bad10 614 OCPP16ServiceUtils.checkFeatureProfile(
08f130a0 615 chargingStation,
370ae4ee
JB
616 OCPP16SupportedFeatureProfiles.SmartCharging,
617 OCPP16IncomingRequestCommand.CLEAR_CHARGING_PROFILE
a36bad10 618 ) === false
370ae4ee 619 ) {
68cb8b91
JB
620 return Constants.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_UNKNOWN;
621 }
1789ba2c 622 if (chargingStation.connectors.has(commandPayload.connectorId) === false) {
e7aeea18 623 logger.error(
08f130a0 624 `${chargingStation.logPrefix()} Trying to clear a charging profile(s) to a non existing connector Id ${
e7aeea18
JB
625 commandPayload.connectorId
626 }`
627 );
c0560973
JB
628 return Constants.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_UNKNOWN;
629 }
1789ba2c 630 const connectorStatus = chargingStation.getConnectorStatus(commandPayload.connectorId);
658e2d16
JB
631 if (commandPayload.connectorId && !Utils.isEmptyArray(connectorStatus.chargingProfiles)) {
632 connectorStatus.chargingProfiles = [];
e7aeea18 633 logger.debug(
08f130a0 634 `${chargingStation.logPrefix()} Charging profile(s) cleared on connector id ${
ad8537a7
JB
635 commandPayload.connectorId
636 }, dump their stack: %j`,
658e2d16 637 connectorStatus.chargingProfiles
e7aeea18 638 );
c0560973
JB
639 return Constants.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_ACCEPTED;
640 }
641 if (!commandPayload.connectorId) {
642 let clearedCP = false;
08f130a0
JB
643 for (const connectorId of chargingStation.connectors.keys()) {
644 if (!Utils.isEmptyArray(chargingStation.getConnectorStatus(connectorId).chargingProfiles)) {
645 chargingStation
e7aeea18
JB
646 .getConnectorStatus(connectorId)
647 .chargingProfiles?.forEach((chargingProfile: OCPP16ChargingProfile, index: number) => {
648 let clearCurrentCP = false;
649 if (chargingProfile.chargingProfileId === commandPayload.id) {
650 clearCurrentCP = true;
651 }
652 if (
653 !commandPayload.chargingProfilePurpose &&
654 chargingProfile.stackLevel === commandPayload.stackLevel
655 ) {
656 clearCurrentCP = true;
657 }
658 if (
659 !chargingProfile.stackLevel &&
660 chargingProfile.chargingProfilePurpose === commandPayload.chargingProfilePurpose
661 ) {
662 clearCurrentCP = true;
663 }
664 if (
665 chargingProfile.stackLevel === commandPayload.stackLevel &&
666 chargingProfile.chargingProfilePurpose === commandPayload.chargingProfilePurpose
667 ) {
668 clearCurrentCP = true;
669 }
670 if (clearCurrentCP) {
ccb1d6e9 671 connectorStatus.chargingProfiles.splice(index, 1);
e7aeea18 672 logger.debug(
08f130a0 673 `${chargingStation.logPrefix()} Matching charging profile(s) cleared on connector id ${
ad8537a7
JB
674 commandPayload.connectorId
675 }, dump their stack: %j`,
658e2d16 676 connectorStatus.chargingProfiles
e7aeea18
JB
677 );
678 clearedCP = true;
679 }
680 });
c0560973
JB
681 }
682 }
683 if (clearedCP) {
684 return Constants.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_ACCEPTED;
685 }
686 }
687 return Constants.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_UNKNOWN;
688 }
689
e7aeea18 690 private async handleRequestChangeAvailability(
08f130a0 691 chargingStation: ChargingStation,
e7aeea18
JB
692 commandPayload: ChangeAvailabilityRequest
693 ): Promise<ChangeAvailabilityResponse> {
c0560973 694 const connectorId: number = commandPayload.connectorId;
c60ed4b8 695 if (chargingStation.connectors.has(connectorId) === false) {
e7aeea18 696 logger.error(
08f130a0 697 `${chargingStation.logPrefix()} Trying to change the availability of a non existing connector Id ${connectorId.toString()}`
e7aeea18 698 );
c0560973
JB
699 return Constants.OCPP_AVAILABILITY_RESPONSE_REJECTED;
700 }
e7aeea18
JB
701 const chargePointStatus: OCPP16ChargePointStatus =
702 commandPayload.type === OCPP16AvailabilityType.OPERATIVE
703 ? OCPP16ChargePointStatus.AVAILABLE
704 : OCPP16ChargePointStatus.UNAVAILABLE;
c0560973
JB
705 if (connectorId === 0) {
706 let response: ChangeAvailabilityResponse = Constants.OCPP_AVAILABILITY_RESPONSE_ACCEPTED;
08f130a0 707 for (const id of chargingStation.connectors.keys()) {
5e3cb728 708 if (chargingStation.getConnectorStatus(id)?.transactionStarted === true) {
c0560973
JB
709 response = Constants.OCPP_AVAILABILITY_RESPONSE_SCHEDULED;
710 }
08f130a0 711 chargingStation.getConnectorStatus(id).availability = commandPayload.type;
c0560973 712 if (response === Constants.OCPP_AVAILABILITY_RESPONSE_ACCEPTED) {
08f130a0 713 await chargingStation.ocppRequestService.requestHandler<
ef6fa3fb
JB
714 OCPP16StatusNotificationRequest,
715 OCPP16StatusNotificationResponse
08f130a0 716 >(chargingStation, OCPP16RequestCommand.STATUS_NOTIFICATION, {
ef6fa3fb
JB
717 connectorId: id,
718 status: chargePointStatus,
719 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
720 });
08f130a0 721 chargingStation.getConnectorStatus(id).status = chargePointStatus;
c0560973
JB
722 }
723 }
724 return response;
e7aeea18
JB
725 } else if (
726 connectorId > 0 &&
56eb297e
JB
727 (chargingStation.isChargingStationAvailable() === true ||
728 (chargingStation.isChargingStationAvailable() === false &&
e7aeea18
JB
729 commandPayload.type === OCPP16AvailabilityType.INOPERATIVE))
730 ) {
5e3cb728 731 if (chargingStation.getConnectorStatus(connectorId)?.transactionStarted === true) {
08f130a0 732 chargingStation.getConnectorStatus(connectorId).availability = commandPayload.type;
c0560973
JB
733 return Constants.OCPP_AVAILABILITY_RESPONSE_SCHEDULED;
734 }
08f130a0
JB
735 chargingStation.getConnectorStatus(connectorId).availability = commandPayload.type;
736 await chargingStation.ocppRequestService.requestHandler<
ef6fa3fb
JB
737 OCPP16StatusNotificationRequest,
738 OCPP16StatusNotificationResponse
08f130a0 739 >(chargingStation, OCPP16RequestCommand.STATUS_NOTIFICATION, {
ef6fa3fb
JB
740 connectorId,
741 status: chargePointStatus,
742 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
743 });
08f130a0 744 chargingStation.getConnectorStatus(connectorId).status = chargePointStatus;
c0560973
JB
745 return Constants.OCPP_AVAILABILITY_RESPONSE_ACCEPTED;
746 }
747 return Constants.OCPP_AVAILABILITY_RESPONSE_REJECTED;
748 }
749
e7aeea18 750 private async handleRequestRemoteStartTransaction(
08f130a0 751 chargingStation: ChargingStation,
e7aeea18
JB
752 commandPayload: RemoteStartTransactionRequest
753 ): Promise<DefaultResponse> {
658e2d16 754 const transactionConnectorId = commandPayload.connectorId;
1789ba2c 755 if (chargingStation.connectors.has(transactionConnectorId) === true) {
91a4f151
JB
756 const remoteStartTransactionLogMsg =
757 chargingStation.logPrefix() +
758 ' Transaction remotely STARTED on ' +
759 chargingStation.stationInfo.chargingStationId +
760 '#' +
761 transactionConnectorId.toString() +
762 " for idTag '" +
763 commandPayload.idTag +
764 "'";
08f130a0 765 await chargingStation.ocppRequestService.requestHandler<
ef6fa3fb
JB
766 OCPP16StatusNotificationRequest,
767 OCPP16StatusNotificationResponse
08f130a0 768 >(chargingStation, OCPP16RequestCommand.STATUS_NOTIFICATION, {
ef6fa3fb
JB
769 connectorId: transactionConnectorId,
770 status: OCPP16ChargePointStatus.PREPARING,
771 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
772 });
1789ba2c 773 const connectorStatus = chargingStation.getConnectorStatus(transactionConnectorId);
658e2d16 774 connectorStatus.status = OCPP16ChargePointStatus.PREPARING;
1789ba2c 775 if (chargingStation.isChargingStationAvailable() === true) {
e060fe58 776 // Check if authorized
1789ba2c 777 if (chargingStation.getAuthorizeRemoteTxRequests() === true) {
a7fc8211 778 let authorized = false;
e7aeea18 779 if (
1789ba2c
JB
780 chargingStation.getLocalAuthListEnabled() === true &&
781 chargingStation.hasAuthorizedTags() === true &&
9d7484a4
JB
782 chargingStation.authorizedTagsCache
783 .getAuthorizedTags(
784 ChargingStationUtils.getAuthorizationFile(chargingStation.stationInfo)
785 )
1789ba2c 786 .find((idTag) => idTag === commandPayload.idTag)
e7aeea18 787 ) {
658e2d16
JB
788 connectorStatus.localAuthorizeIdTag = commandPayload.idTag;
789 connectorStatus.idTagLocalAuthorized = true;
36f6a92e 790 authorized = true;
1789ba2c 791 } else if (chargingStation.getMustAuthorizeAtRemoteStart() === true) {
658e2d16 792 connectorStatus.authorizeIdTag = commandPayload.idTag;
2e3d65ae 793 const authorizeResponse: OCPP16AuthorizeResponse =
08f130a0 794 await chargingStation.ocppRequestService.requestHandler<
ef6fa3fb
JB
795 OCPP16AuthorizeRequest,
796 OCPP16AuthorizeResponse
08f130a0 797 >(chargingStation, OCPP16RequestCommand.AUTHORIZE, {
ef6fa3fb
JB
798 idTag: commandPayload.idTag,
799 });
a7fc8211
JB
800 if (authorizeResponse?.idTagInfo?.status === OCPP16AuthorizationStatus.ACCEPTED) {
801 authorized = true;
a7fc8211 802 }
71068fb9 803 } else {
e7aeea18 804 logger.warn(
08f130a0 805 `${chargingStation.logPrefix()} The charging station configuration expects authorize at remote start transaction but local authorization or authorize isn't enabled`
e7aeea18 806 );
a7fc8211 807 }
1789ba2c 808 if (authorized === true) {
a7fc8211 809 // Authorization successful, start transaction
e7aeea18
JB
810 if (
811 this.setRemoteStartTransactionChargingProfile(
08f130a0 812 chargingStation,
e7aeea18
JB
813 transactionConnectorId,
814 commandPayload.chargingProfile
1789ba2c 815 ) === true
e7aeea18 816 ) {
658e2d16 817 connectorStatus.transactionRemoteStarted = true;
e7aeea18
JB
818 if (
819 (
08f130a0 820 await chargingStation.ocppRequestService.requestHandler<
ef6fa3fb
JB
821 OCPP16StartTransactionRequest,
822 OCPP16StartTransactionResponse
08f130a0 823 >(chargingStation, OCPP16RequestCommand.START_TRANSACTION, {
ef6fa3fb
JB
824 connectorId: transactionConnectorId,
825 idTag: commandPayload.idTag,
826 })
e7aeea18
JB
827 ).idTagInfo.status === OCPP16AuthorizationStatus.ACCEPTED
828 ) {
91a4f151 829 logger.debug(remoteStartTransactionLogMsg);
e060fe58
JB
830 return Constants.OCPP_RESPONSE_ACCEPTED;
831 }
e7aeea18 832 return this.notifyRemoteStartTransactionRejected(
08f130a0 833 chargingStation,
e7aeea18
JB
834 transactionConnectorId,
835 commandPayload.idTag
836 );
e060fe58 837 }
e7aeea18 838 return this.notifyRemoteStartTransactionRejected(
08f130a0 839 chargingStation,
e7aeea18
JB
840 transactionConnectorId,
841 commandPayload.idTag
842 );
a7fc8211 843 }
e7aeea18 844 return this.notifyRemoteStartTransactionRejected(
08f130a0 845 chargingStation,
e7aeea18
JB
846 transactionConnectorId,
847 commandPayload.idTag
848 );
36f6a92e 849 }
a7fc8211 850 // No authorization check required, start transaction
e7aeea18
JB
851 if (
852 this.setRemoteStartTransactionChargingProfile(
08f130a0 853 chargingStation,
e7aeea18
JB
854 transactionConnectorId,
855 commandPayload.chargingProfile
1789ba2c 856 ) === true
e7aeea18 857 ) {
658e2d16 858 connectorStatus.transactionRemoteStarted = true;
e7aeea18
JB
859 if (
860 (
08f130a0 861 await chargingStation.ocppRequestService.requestHandler<
ef6fa3fb
JB
862 OCPP16StartTransactionRequest,
863 OCPP16StartTransactionResponse
08f130a0 864 >(chargingStation, OCPP16RequestCommand.START_TRANSACTION, {
ef6fa3fb
JB
865 connectorId: transactionConnectorId,
866 idTag: commandPayload.idTag,
867 })
e7aeea18
JB
868 ).idTagInfo.status === OCPP16AuthorizationStatus.ACCEPTED
869 ) {
91a4f151 870 logger.debug(remoteStartTransactionLogMsg);
e060fe58
JB
871 return Constants.OCPP_RESPONSE_ACCEPTED;
872 }
e7aeea18 873 return this.notifyRemoteStartTransactionRejected(
08f130a0 874 chargingStation,
e7aeea18
JB
875 transactionConnectorId,
876 commandPayload.idTag
877 );
e060fe58 878 }
e7aeea18 879 return this.notifyRemoteStartTransactionRejected(
08f130a0 880 chargingStation,
e7aeea18
JB
881 transactionConnectorId,
882 commandPayload.idTag
883 );
c0560973 884 }
e7aeea18 885 return this.notifyRemoteStartTransactionRejected(
08f130a0 886 chargingStation,
e7aeea18
JB
887 transactionConnectorId,
888 commandPayload.idTag
889 );
c0560973 890 }
08f130a0
JB
891 return this.notifyRemoteStartTransactionRejected(
892 chargingStation,
893 transactionConnectorId,
894 commandPayload.idTag
895 );
a7fc8211
JB
896 }
897
e7aeea18 898 private async notifyRemoteStartTransactionRejected(
08f130a0 899 chargingStation: ChargingStation,
e7aeea18
JB
900 connectorId: number,
901 idTag: string
902 ): Promise<DefaultResponse> {
903 if (
08f130a0 904 chargingStation.getConnectorStatus(connectorId).status !== OCPP16ChargePointStatus.AVAILABLE
e7aeea18 905 ) {
08f130a0 906 await chargingStation.ocppRequestService.requestHandler<
ef6fa3fb
JB
907 OCPP16StatusNotificationRequest,
908 OCPP16StatusNotificationResponse
08f130a0 909 >(chargingStation, OCPP16RequestCommand.STATUS_NOTIFICATION, {
ef6fa3fb
JB
910 connectorId,
911 status: OCPP16ChargePointStatus.AVAILABLE,
912 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
913 });
08f130a0 914 chargingStation.getConnectorStatus(connectorId).status = OCPP16ChargePointStatus.AVAILABLE;
e060fe58 915 }
e7aeea18 916 logger.warn(
08f130a0 917 chargingStation.logPrefix() +
e7aeea18
JB
918 ' Remote starting transaction REJECTED on connector Id ' +
919 connectorId.toString() +
91a4f151 920 ", idTag '" +
e7aeea18 921 idTag +
91a4f151 922 "', availability '" +
08f130a0 923 chargingStation.getConnectorStatus(connectorId).availability +
91a4f151
JB
924 "', status '" +
925 chargingStation.getConnectorStatus(connectorId).status +
926 "'"
e7aeea18 927 );
c0560973
JB
928 return Constants.OCPP_RESPONSE_REJECTED;
929 }
930
e7aeea18 931 private setRemoteStartTransactionChargingProfile(
08f130a0 932 chargingStation: ChargingStation,
e7aeea18
JB
933 connectorId: number,
934 cp: OCPP16ChargingProfile
935 ): boolean {
a7fc8211 936 if (cp && cp.chargingProfilePurpose === ChargingProfilePurposeType.TX_PROFILE) {
ed3d2808 937 OCPP16ServiceUtils.setChargingProfile(chargingStation, connectorId, cp);
e7aeea18 938 logger.debug(
08f130a0
JB
939 `${chargingStation.logPrefix()} Charging profile(s) set at remote start transaction on connector id ${connectorId}, dump their stack: %j`,
940 chargingStation.getConnectorStatus(connectorId).chargingProfiles
e7aeea18 941 );
a7fc8211
JB
942 return true;
943 } else if (cp && cp.chargingProfilePurpose !== ChargingProfilePurposeType.TX_PROFILE) {
e7aeea18 944 logger.warn(
08f130a0 945 `${chargingStation.logPrefix()} Not allowed to set ${
e7aeea18
JB
946 cp.chargingProfilePurpose
947 } charging profile(s) at remote start transaction`
948 );
a7fc8211 949 return false;
e060fe58
JB
950 } else if (!cp) {
951 return true;
a7fc8211
JB
952 }
953 }
954
e7aeea18 955 private async handleRequestRemoteStopTransaction(
08f130a0 956 chargingStation: ChargingStation,
e7aeea18
JB
957 commandPayload: RemoteStopTransactionRequest
958 ): Promise<DefaultResponse> {
c0560973 959 const transactionId = commandPayload.transactionId;
08f130a0 960 for (const connectorId of chargingStation.connectors.keys()) {
e7aeea18
JB
961 if (
962 connectorId > 0 &&
08f130a0 963 chargingStation.getConnectorStatus(connectorId)?.transactionId === transactionId
e7aeea18 964 ) {
08f130a0 965 await chargingStation.ocppRequestService.requestHandler<
ef6fa3fb
JB
966 OCPP16StatusNotificationRequest,
967 OCPP16StatusNotificationResponse
08f130a0 968 >(chargingStation, OCPP16RequestCommand.STATUS_NOTIFICATION, {
ef6fa3fb
JB
969 connectorId,
970 status: OCPP16ChargePointStatus.FINISHING,
971 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
972 });
08f130a0 973 chargingStation.getConnectorStatus(connectorId).status = OCPP16ChargePointStatus.FINISHING;
5e3cb728
JB
974 const stopResponse = await chargingStation.stopTransactionOnConnector(
975 connectorId,
a65319ba 976 OCPP16StopTransactionReason.REMOTE
5e3cb728
JB
977 );
978 if (stopResponse.idTagInfo?.status === OCPP16AuthorizationStatus.ACCEPTED) {
979 return Constants.OCPP_RESPONSE_ACCEPTED;
ef6fa3fb 980 }
5e3cb728 981 return Constants.OCPP_RESPONSE_REJECTED;
c0560973
JB
982 }
983 }
44b9b577 984 logger.warn(
08f130a0 985 chargingStation.logPrefix() +
e7aeea18
JB
986 ' Trying to remote stop a non existing transaction ' +
987 transactionId.toString()
988 );
c0560973
JB
989 return Constants.OCPP_RESPONSE_REJECTED;
990 }
47e22477 991
e7aeea18 992 private async handleRequestGetDiagnostics(
08f130a0 993 chargingStation: ChargingStation,
e7aeea18
JB
994 commandPayload: GetDiagnosticsRequest
995 ): Promise<GetDiagnosticsResponse> {
68cb8b91 996 if (
1789ba2c 997 OCPP16ServiceUtils.checkFeatureProfile(
08f130a0 998 chargingStation,
370ae4ee
JB
999 OCPP16SupportedFeatureProfiles.FirmwareManagement,
1000 OCPP16IncomingRequestCommand.GET_DIAGNOSTICS
1789ba2c 1001 ) === false
68cb8b91 1002 ) {
68cb8b91
JB
1003 return Constants.OCPP_RESPONSE_EMPTY;
1004 }
e7aeea18 1005 logger.debug(
08f130a0 1006 chargingStation.logPrefix() +
e7aeea18
JB
1007 ' ' +
1008 OCPP16IncomingRequestCommand.GET_DIAGNOSTICS +
1009 ' request received: %j',
1010 commandPayload
1011 );
a3868ec4 1012 const uri = new URL(commandPayload.location);
47e22477
JB
1013 if (uri.protocol.startsWith('ftp:')) {
1014 let ftpClient: Client;
1015 try {
e7aeea18 1016 const logFiles = fs
0d8140bd 1017 .readdirSync(path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../../../'))
e7aeea18
JB
1018 .filter((file) => file.endsWith('.log'))
1019 .map((file) => path.join('./', file));
08f130a0 1020 const diagnosticsArchive = chargingStation.stationInfo.chargingStationId + '_logs.tar.gz';
47e22477
JB
1021 tar.create({ gzip: true }, logFiles).pipe(fs.createWriteStream(diagnosticsArchive));
1022 ftpClient = new Client();
1023 const accessResponse = await ftpClient.access({
1024 host: uri.host,
e8191622
JB
1025 ...(!Utils.isEmptyString(uri.port) && { port: Utils.convertToInt(uri.port) }),
1026 ...(!Utils.isEmptyString(uri.username) && { user: uri.username }),
1027 ...(!Utils.isEmptyString(uri.password) && { password: uri.password }),
47e22477
JB
1028 });
1029 let uploadResponse: FTPResponse;
1030 if (accessResponse.code === 220) {
1031 // eslint-disable-next-line @typescript-eslint/no-misused-promises
1032 ftpClient.trackProgress(async (info) => {
e7aeea18 1033 logger.info(
08f130a0 1034 `${chargingStation.logPrefix()} ${
e7aeea18
JB
1035 info.bytes / 1024
1036 } bytes transferred from diagnostics archive ${info.name}`
1037 );
08f130a0 1038 await chargingStation.ocppRequestService.requestHandler<
ef6fa3fb
JB
1039 DiagnosticsStatusNotificationRequest,
1040 DiagnosticsStatusNotificationResponse
08f130a0 1041 >(chargingStation, OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION, {
ef6fa3fb
JB
1042 status: OCPP16DiagnosticsStatus.Uploading,
1043 });
47e22477 1044 });
e7aeea18 1045 uploadResponse = await ftpClient.uploadFrom(
0d8140bd
JB
1046 path.join(
1047 path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../../../'),
1048 diagnosticsArchive
1049 ),
e7aeea18
JB
1050 uri.pathname + diagnosticsArchive
1051 );
47e22477 1052 if (uploadResponse.code === 226) {
08f130a0 1053 await chargingStation.ocppRequestService.requestHandler<
ef6fa3fb
JB
1054 DiagnosticsStatusNotificationRequest,
1055 DiagnosticsStatusNotificationResponse
08f130a0 1056 >(chargingStation, OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION, {
ef6fa3fb
JB
1057 status: OCPP16DiagnosticsStatus.Uploaded,
1058 });
47e22477
JB
1059 if (ftpClient) {
1060 ftpClient.close();
1061 }
1062 return { fileName: diagnosticsArchive };
1063 }
e7aeea18
JB
1064 throw new OCPPError(
1065 ErrorType.GENERIC_ERROR,
1066 `Diagnostics transfer failed with error code ${accessResponse.code.toString()}${
1067 uploadResponse?.code && '|' + uploadResponse?.code.toString()
1068 }`,
1069 OCPP16IncomingRequestCommand.GET_DIAGNOSTICS
1070 );
47e22477 1071 }
e7aeea18
JB
1072 throw new OCPPError(
1073 ErrorType.GENERIC_ERROR,
1074 `Diagnostics transfer failed with error code ${accessResponse.code.toString()}${
1075 uploadResponse?.code && '|' + uploadResponse?.code.toString()
1076 }`,
1077 OCPP16IncomingRequestCommand.GET_DIAGNOSTICS
1078 );
47e22477 1079 } catch (error) {
08f130a0 1080 await chargingStation.ocppRequestService.requestHandler<
ef6fa3fb
JB
1081 DiagnosticsStatusNotificationRequest,
1082 DiagnosticsStatusNotificationResponse
08f130a0 1083 >(chargingStation, OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION, {
ef6fa3fb
JB
1084 status: OCPP16DiagnosticsStatus.UploadFailed,
1085 });
47e22477
JB
1086 if (ftpClient) {
1087 ftpClient.close();
1088 }
e7aeea18 1089 return this.handleIncomingRequestError(
08f130a0 1090 chargingStation,
e7aeea18
JB
1091 OCPP16IncomingRequestCommand.GET_DIAGNOSTICS,
1092 error as Error,
1093 { errorResponse: Constants.OCPP_RESPONSE_EMPTY }
1094 );
47e22477
JB
1095 }
1096 } else {
e7aeea18 1097 logger.error(
08f130a0 1098 `${chargingStation.logPrefix()} Unsupported protocol ${
e7aeea18
JB
1099 uri.protocol
1100 } to transfer the diagnostic logs archive`
1101 );
08f130a0 1102 await chargingStation.ocppRequestService.requestHandler<
ef6fa3fb
JB
1103 DiagnosticsStatusNotificationRequest,
1104 DiagnosticsStatusNotificationResponse
08f130a0 1105 >(chargingStation, OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION, {
ef6fa3fb
JB
1106 status: OCPP16DiagnosticsStatus.UploadFailed,
1107 });
47e22477
JB
1108 return Constants.OCPP_RESPONSE_EMPTY;
1109 }
1110 }
802cfa13 1111
e7aeea18 1112 private handleRequestTriggerMessage(
08f130a0 1113 chargingStation: ChargingStation,
e7aeea18
JB
1114 commandPayload: OCPP16TriggerMessageRequest
1115 ): OCPP16TriggerMessageResponse {
370ae4ee
JB
1116 if (
1117 !OCPP16ServiceUtils.checkFeatureProfile(
08f130a0 1118 chargingStation,
370ae4ee
JB
1119 OCPP16SupportedFeatureProfiles.RemoteTrigger,
1120 OCPP16IncomingRequestCommand.TRIGGER_MESSAGE
c60ed4b8
JB
1121 ) ||
1122 !OCPP16ServiceUtils.isMessageTriggerSupported(
1123 chargingStation,
1124 commandPayload.requestedMessage
370ae4ee
JB
1125 )
1126 ) {
68cb8b91
JB
1127 return Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_NOT_IMPLEMENTED;
1128 }
c60ed4b8
JB
1129 if (
1130 OCPP16ServiceUtils.isConnectorIdValid(
1131 chargingStation,
1132 OCPP16IncomingRequestCommand.TRIGGER_MESSAGE,
1133 commandPayload.connectorId
1134 )
1135 ) {
dc661702
JB
1136 return Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_REJECTED;
1137 }
802cfa13
JB
1138 try {
1139 switch (commandPayload.requestedMessage) {
c60ed4b8 1140 case OCPP16MessageTrigger.BootNotification:
802cfa13 1141 setTimeout(() => {
08f130a0 1142 chargingStation.ocppRequestService
f7f98c68 1143 .requestHandler<OCPP16BootNotificationRequest, OCPP16BootNotificationResponse>(
08f130a0 1144 chargingStation,
6a8b180d 1145 OCPP16RequestCommand.BOOT_NOTIFICATION,
8bfbc743 1146 chargingStation.bootNotificationRequest,
6a8b180d 1147 { skipBufferingOnError: true, triggerMessage: true }
e7aeea18 1148 )
8bfbc743
JB
1149 .then((response) => {
1150 chargingStation.bootNotificationResponse = response;
ae711c83 1151 })
e7aeea18
JB
1152 .catch(() => {
1153 /* This is intentional */
1154 });
802cfa13
JB
1155 }, Constants.OCPP_TRIGGER_MESSAGE_DELAY);
1156 return Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_ACCEPTED;
c60ed4b8 1157 case OCPP16MessageTrigger.Heartbeat:
802cfa13 1158 setTimeout(() => {
08f130a0 1159 chargingStation.ocppRequestService
f7f98c68 1160 .requestHandler<OCPP16HeartbeatRequest, OCPP16HeartbeatResponse>(
08f130a0 1161 chargingStation,
ef6fa3fb
JB
1162 OCPP16RequestCommand.HEARTBEAT,
1163 null,
1164 {
1165 triggerMessage: true,
1166 }
1167 )
e7aeea18
JB
1168 .catch(() => {
1169 /* This is intentional */
1170 });
802cfa13
JB
1171 }, Constants.OCPP_TRIGGER_MESSAGE_DELAY);
1172 return Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_ACCEPTED;
c60ed4b8 1173 case OCPP16MessageTrigger.StatusNotification:
dc661702
JB
1174 setTimeout(() => {
1175 if (commandPayload?.connectorId) {
08f130a0 1176 chargingStation.ocppRequestService
dc661702 1177 .requestHandler<OCPP16StatusNotificationRequest, OCPP16StatusNotificationResponse>(
08f130a0 1178 chargingStation,
dc661702
JB
1179 OCPP16RequestCommand.STATUS_NOTIFICATION,
1180 {
1181 connectorId: commandPayload.connectorId,
1182 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
08f130a0 1183 status: chargingStation.getConnectorStatus(commandPayload.connectorId).status,
dc661702
JB
1184 },
1185 {
1186 triggerMessage: true,
1187 }
1188 )
1189 .catch(() => {
1190 /* This is intentional */
1191 });
1192 } else {
08f130a0
JB
1193 for (const connectorId of chargingStation.connectors.keys()) {
1194 chargingStation.ocppRequestService
dc661702
JB
1195 .requestHandler<
1196 OCPP16StatusNotificationRequest,
1197 OCPP16StatusNotificationResponse
1198 >(
08f130a0 1199 chargingStation,
dc661702
JB
1200 OCPP16RequestCommand.STATUS_NOTIFICATION,
1201 {
1202 connectorId,
1203 errorCode: OCPP16ChargePointErrorCode.NO_ERROR,
08f130a0 1204 status: chargingStation.getConnectorStatus(connectorId).status,
dc661702
JB
1205 },
1206 {
1207 triggerMessage: true,
1208 }
1209 )
1210 .catch(() => {
1211 /* This is intentional */
1212 });
1213 }
1214 }
1215 }, Constants.OCPP_TRIGGER_MESSAGE_DELAY);
1216 return Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_ACCEPTED;
802cfa13
JB
1217 default:
1218 return Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_NOT_IMPLEMENTED;
1219 }
1220 } catch (error) {
e7aeea18 1221 return this.handleIncomingRequestError(
08f130a0 1222 chargingStation,
e7aeea18
JB
1223 OCPP16IncomingRequestCommand.TRIGGER_MESSAGE,
1224 error as Error,
1225 { errorResponse: Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_REJECTED }
1226 );
802cfa13
JB
1227 }
1228 }
c0560973 1229}