5b94ece886b900710d4085b6e075cde1871b00e2
[e-mobility-charging-stations-simulator.git] / src / charging-station / ocpp / 1.6 / OCPP16IncomingRequestService.ts
1 // Partial Copyright Jerome Benoit. 2021. All Rights Reserved.
2
3 import * as url from 'url';
4
5 import { ChangeAvailabilityRequest, ChangeConfigurationRequest, ClearChargingProfileRequest, GetConfigurationRequest, GetDiagnosticsRequest, OCPP16AvailabilityType, OCPP16IncomingRequestCommand, RemoteStartTransactionRequest, RemoteStopTransactionRequest, ResetRequest, SetChargingProfileRequest, UnlockConnectorRequest } from '../../../types/ocpp/1.6/Requests';
6 import { ChangeAvailabilityResponse, ChangeConfigurationResponse, ClearChargingProfileResponse, GetConfigurationResponse, GetDiagnosticsResponse, SetChargingProfileResponse, UnlockConnectorResponse } from '../../../types/ocpp/1.6/Responses';
7 import { ChargingProfilePurposeType, OCPP16ChargingProfile } from '../../../types/ocpp/1.6/ChargingProfile';
8 import { Client, FTPResponse } from 'basic-ftp';
9 import { IncomingRequestCommand, RequestCommand } from '../../../types/ocpp/Requests';
10 import { OCPP16AuthorizationStatus, OCPP16StopTransactionReason } from '../../../types/ocpp/1.6/Transaction';
11
12 import Constants from '../../../utils/Constants';
13 import { DefaultResponse } from '../../../types/ocpp/Responses';
14 import { ErrorType } from '../../../types/ocpp/ErrorType';
15 import { MessageType } from '../../../types/ocpp/MessageType';
16 import { OCPP16ChargePointStatus } from '../../../types/ocpp/1.6/ChargePointStatus';
17 import { OCPP16DiagnosticsStatus } from '../../../types/ocpp/1.6/DiagnosticsStatus';
18 import { OCPP16StandardParametersKey } from '../../../types/ocpp/1.6/Configuration';
19 import { OCPPConfigurationKey } from '../../../types/ocpp/Configuration';
20 import OCPPError from '../OCPPError';
21 import OCPPIncomingRequestService from '../OCPPIncomingRequestService';
22 import Utils from '../../../utils/Utils';
23 import fs from 'fs';
24 import logger from '../../../utils/Logger';
25 import path from 'path';
26 import tar from 'tar';
27
28 export default class OCPP16IncomingRequestService extends OCPPIncomingRequestService {
29 public async handleRequest(messageId: string, commandName: OCPP16IncomingRequestCommand, commandPayload: Record<string, unknown>): Promise<void> {
30 let response;
31 const methodName = `handleRequest${commandName}`;
32 // Call
33 if (typeof this[methodName] === 'function') {
34 try {
35 // Call the method to build the response
36 response = await this[methodName](commandPayload);
37 } catch (error) {
38 // Log
39 logger.error(this.chargingStation.logPrefix() + ' Handle request error: %j', error);
40 // Send back an error response to inform backend
41 await this.chargingStation.ocppRequestService.sendError(messageId, error, commandName);
42 throw error;
43 }
44 } else {
45 // Throw exception
46 const error = new OCPPError(ErrorType.NOT_IMPLEMENTED, `${commandName} is not implemented to handle payload ${JSON.stringify(commandPayload, null, 2)}`);
47 await this.chargingStation.ocppRequestService.sendError(messageId, error, commandName);
48 throw error;
49 }
50 // Send the built response
51 await this.chargingStation.ocppRequestService.sendMessage(messageId, response, MessageType.CALL_RESULT_MESSAGE, commandName);
52 }
53
54 // Simulate charging station restart
55 private handleRequestReset(commandPayload: ResetRequest): DefaultResponse {
56 // eslint-disable-next-line @typescript-eslint/no-misused-promises
57 setImmediate(async (): Promise<void> => {
58 await this.chargingStation.stop(commandPayload.type + 'Reset' as OCPP16StopTransactionReason);
59 await Utils.sleep(this.chargingStation.stationInfo.resetTime);
60 this.chargingStation.start();
61 });
62 logger.info(`${this.chargingStation.logPrefix()} ${commandPayload.type} reset command received, simulating it. The station will be back online in ${Utils.milliSecondsToHHMMSS(this.chargingStation.stationInfo.resetTime)}`);
63 return Constants.OCPP_RESPONSE_ACCEPTED;
64 }
65
66 private handleRequestClearCache(): DefaultResponse {
67 return Constants.OCPP_RESPONSE_ACCEPTED;
68 }
69
70 private async handleRequestUnlockConnector(commandPayload: UnlockConnectorRequest): Promise<UnlockConnectorResponse> {
71 const connectorId = commandPayload.connectorId;
72 if (connectorId === 0) {
73 logger.error(this.chargingStation.logPrefix() + ' Trying to unlock connector ' + connectorId.toString());
74 return Constants.OCPP_RESPONSE_UNLOCK_NOT_SUPPORTED;
75 }
76 if (this.chargingStation.getConnector(connectorId)?.transactionStarted) {
77 const transactionId = this.chargingStation.getConnector(connectorId).transactionId;
78 const stopResponse = await this.chargingStation.ocppRequestService.sendStopTransaction(transactionId,
79 this.chargingStation.getEnergyActiveImportRegisterByTransactionId(transactionId),
80 this.chargingStation.getTransactionIdTag(transactionId),
81 OCPP16StopTransactionReason.UNLOCK_COMMAND);
82 if (stopResponse.idTagInfo?.status === OCPP16AuthorizationStatus.ACCEPTED) {
83 return Constants.OCPP_RESPONSE_UNLOCKED;
84 }
85 return Constants.OCPP_RESPONSE_UNLOCK_FAILED;
86 }
87 await this.chargingStation.ocppRequestService.sendStatusNotification(connectorId, OCPP16ChargePointStatus.AVAILABLE);
88 this.chargingStation.getConnector(connectorId).status = OCPP16ChargePointStatus.AVAILABLE;
89 return Constants.OCPP_RESPONSE_UNLOCKED;
90 }
91
92 private handleRequestGetConfiguration(commandPayload: GetConfigurationRequest): GetConfigurationResponse {
93 const configurationKey: OCPPConfigurationKey[] = [];
94 const unknownKey: string[] = [];
95 if (Utils.isEmptyArray(commandPayload.key)) {
96 for (const configuration of this.chargingStation.configuration.configurationKey) {
97 if (Utils.isUndefined(configuration.visible)) {
98 configuration.visible = true;
99 }
100 if (!configuration.visible) {
101 continue;
102 }
103 configurationKey.push({
104 key: configuration.key,
105 readonly: configuration.readonly,
106 value: configuration.value,
107 });
108 }
109 } else {
110 for (const key of commandPayload.key) {
111 const keyFound = this.chargingStation.getConfigurationKey(key);
112 if (keyFound) {
113 if (Utils.isUndefined(keyFound.visible)) {
114 keyFound.visible = true;
115 }
116 if (!keyFound.visible) {
117 continue;
118 }
119 configurationKey.push({
120 key: keyFound.key,
121 readonly: keyFound.readonly,
122 value: keyFound.value,
123 });
124 } else {
125 unknownKey.push(key);
126 }
127 }
128 }
129 return {
130 configurationKey,
131 unknownKey,
132 };
133 }
134
135 private handleRequestChangeConfiguration(commandPayload: ChangeConfigurationRequest): ChangeConfigurationResponse {
136 // JSON request fields type sanity check
137 if (!Utils.isString(commandPayload.key)) {
138 logger.error(`${this.chargingStation.logPrefix()} ${RequestCommand.CHANGE_CONFIGURATION} request key field is not a string:`, commandPayload);
139 }
140 if (!Utils.isString(commandPayload.value)) {
141 logger.error(`${this.chargingStation.logPrefix()} ${RequestCommand.CHANGE_CONFIGURATION} request value field is not a string:`, commandPayload);
142 }
143 const keyToChange = this.chargingStation.getConfigurationKey(commandPayload.key, true);
144 if (!keyToChange) {
145 return Constants.OCPP_CONFIGURATION_RESPONSE_NOT_SUPPORTED;
146 } else if (keyToChange && keyToChange.readonly) {
147 return Constants.OCPP_CONFIGURATION_RESPONSE_REJECTED;
148 } else if (keyToChange && !keyToChange.readonly) {
149 const keyIndex = this.chargingStation.configuration.configurationKey.indexOf(keyToChange);
150 let valueChanged = false;
151 if (this.chargingStation.configuration.configurationKey[keyIndex].value !== commandPayload.value) {
152 this.chargingStation.configuration.configurationKey[keyIndex].value = commandPayload.value;
153 valueChanged = true;
154 }
155 let triggerHeartbeatRestart = false;
156 if (keyToChange.key === OCPP16StandardParametersKey.HeartBeatInterval && valueChanged) {
157 this.chargingStation.setConfigurationKeyValue(OCPP16StandardParametersKey.HeartbeatInterval, commandPayload.value);
158 triggerHeartbeatRestart = true;
159 }
160 if (keyToChange.key === OCPP16StandardParametersKey.HeartbeatInterval && valueChanged) {
161 this.chargingStation.setConfigurationKeyValue(OCPP16StandardParametersKey.HeartBeatInterval, commandPayload.value);
162 triggerHeartbeatRestart = true;
163 }
164 if (triggerHeartbeatRestart) {
165 this.chargingStation.restartHeartbeat();
166 }
167 if (keyToChange.key === OCPP16StandardParametersKey.WebSocketPingInterval && valueChanged) {
168 this.chargingStation.restartWebSocketPing();
169 }
170 if (keyToChange.reboot) {
171 return Constants.OCPP_CONFIGURATION_RESPONSE_REBOOT_REQUIRED;
172 }
173 return Constants.OCPP_CONFIGURATION_RESPONSE_ACCEPTED;
174 }
175 }
176
177 private handleRequestSetChargingProfile(commandPayload: SetChargingProfileRequest): SetChargingProfileResponse {
178 if (!this.chargingStation.getConnector(commandPayload.connectorId)) {
179 logger.error(`${this.chargingStation.logPrefix()} Trying to set charging profile(s) to a non existing connector Id ${commandPayload.connectorId}`);
180 return Constants.OCPP_SET_CHARGING_PROFILE_RESPONSE_REJECTED;
181 }
182 if (commandPayload.csChargingProfiles.chargingProfilePurpose === ChargingProfilePurposeType.CHARGE_POINT_MAX_PROFILE && commandPayload.connectorId !== 0) {
183 return Constants.OCPP_SET_CHARGING_PROFILE_RESPONSE_REJECTED;
184 }
185 if (commandPayload.csChargingProfiles.chargingProfilePurpose === ChargingProfilePurposeType.TX_PROFILE && (commandPayload.connectorId === 0 || !this.chargingStation.getConnector(commandPayload.connectorId)?.transactionStarted)) {
186 return Constants.OCPP_SET_CHARGING_PROFILE_RESPONSE_REJECTED;
187 }
188 this.chargingStation.setChargingProfile(commandPayload.connectorId, commandPayload.csChargingProfiles);
189 logger.debug(`${this.chargingStation.logPrefix()} Charging profile(s) set, dump their stack: %j`, this.chargingStation.getConnector(commandPayload.connectorId).chargingProfiles);
190 return Constants.OCPP_SET_CHARGING_PROFILE_RESPONSE_ACCEPTED;
191 }
192
193 private handleRequestClearChargingProfile(commandPayload: ClearChargingProfileRequest): ClearChargingProfileResponse {
194 if (!this.chargingStation.getConnector(commandPayload.connectorId)) {
195 logger.error(`${this.chargingStation.logPrefix()} Trying to clear a charging profile(s) to a non existing connector Id ${commandPayload.connectorId}`);
196 return Constants.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_UNKNOWN;
197 }
198 if (commandPayload.connectorId && !Utils.isEmptyArray(this.chargingStation.getConnector(commandPayload.connectorId).chargingProfiles)) {
199 this.chargingStation.getConnector(commandPayload.connectorId).chargingProfiles = [];
200 logger.debug(`${this.chargingStation.logPrefix()} Charging profile(s) cleared, dump their stack: %j`, this.chargingStation.getConnector(commandPayload.connectorId).chargingProfiles);
201 return Constants.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_ACCEPTED;
202 }
203 if (!commandPayload.connectorId) {
204 let clearedCP = false;
205 for (const connector in this.chargingStation.connectors) {
206 if (!Utils.isEmptyArray(this.chargingStation.getConnector(Utils.convertToInt(connector)).chargingProfiles)) {
207 this.chargingStation.getConnector(Utils.convertToInt(connector)).chargingProfiles?.forEach((chargingProfile: OCPP16ChargingProfile, index: number) => {
208 let clearCurrentCP = false;
209 if (chargingProfile.chargingProfileId === commandPayload.id) {
210 clearCurrentCP = true;
211 }
212 if (!commandPayload.chargingProfilePurpose && chargingProfile.stackLevel === commandPayload.stackLevel) {
213 clearCurrentCP = true;
214 }
215 if (!chargingProfile.stackLevel && chargingProfile.chargingProfilePurpose === commandPayload.chargingProfilePurpose) {
216 clearCurrentCP = true;
217 }
218 if (chargingProfile.stackLevel === commandPayload.stackLevel && chargingProfile.chargingProfilePurpose === commandPayload.chargingProfilePurpose) {
219 clearCurrentCP = true;
220 }
221 if (clearCurrentCP) {
222 this.chargingStation.getConnector(commandPayload.connectorId).chargingProfiles[index] = {} as OCPP16ChargingProfile;
223 logger.debug(`${this.chargingStation.logPrefix()} Charging profile(s) cleared, dump their stack: %j`, this.chargingStation.getConnector(commandPayload.connectorId).chargingProfiles);
224 clearedCP = true;
225 }
226 });
227 }
228 }
229 if (clearedCP) {
230 return Constants.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_ACCEPTED;
231 }
232 }
233 return Constants.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_UNKNOWN;
234 }
235
236 private async handleRequestChangeAvailability(commandPayload: ChangeAvailabilityRequest): Promise<ChangeAvailabilityResponse> {
237 const connectorId: number = commandPayload.connectorId;
238 if (!this.chargingStation.getConnector(connectorId)) {
239 logger.error(`${this.chargingStation.logPrefix()} Trying to change the availability of a non existing connector Id ${connectorId.toString()}`);
240 return Constants.OCPP_AVAILABILITY_RESPONSE_REJECTED;
241 }
242 const chargePointStatus: OCPP16ChargePointStatus = commandPayload.type === OCPP16AvailabilityType.OPERATIVE ? OCPP16ChargePointStatus.AVAILABLE : OCPP16ChargePointStatus.UNAVAILABLE;
243 if (connectorId === 0) {
244 let response: ChangeAvailabilityResponse = Constants.OCPP_AVAILABILITY_RESPONSE_ACCEPTED;
245 for (const connector in this.chargingStation.connectors) {
246 if (this.chargingStation.getConnector(Utils.convertToInt(connector)).transactionStarted) {
247 response = Constants.OCPP_AVAILABILITY_RESPONSE_SCHEDULED;
248 }
249 this.chargingStation.getConnector(Utils.convertToInt(connector)).availability = commandPayload.type;
250 if (response === Constants.OCPP_AVAILABILITY_RESPONSE_ACCEPTED) {
251 await this.chargingStation.ocppRequestService.sendStatusNotification(Utils.convertToInt(connector), chargePointStatus);
252 this.chargingStation.getConnector(Utils.convertToInt(connector)).status = chargePointStatus;
253 }
254 }
255 return response;
256 } else if (connectorId > 0 && (this.chargingStation.getConnector(0).availability === OCPP16AvailabilityType.OPERATIVE || (this.chargingStation.getConnector(0).availability === OCPP16AvailabilityType.INOPERATIVE && commandPayload.type === OCPP16AvailabilityType.INOPERATIVE))) {
257 if (this.chargingStation.getConnector(connectorId)?.transactionStarted) {
258 this.chargingStation.getConnector(connectorId).availability = commandPayload.type;
259 return Constants.OCPP_AVAILABILITY_RESPONSE_SCHEDULED;
260 }
261 this.chargingStation.getConnector(connectorId).availability = commandPayload.type;
262 await this.chargingStation.ocppRequestService.sendStatusNotification(connectorId, chargePointStatus);
263 this.chargingStation.getConnector(connectorId).status = chargePointStatus;
264 return Constants.OCPP_AVAILABILITY_RESPONSE_ACCEPTED;
265 }
266 return Constants.OCPP_AVAILABILITY_RESPONSE_REJECTED;
267 }
268
269 private async handleRequestRemoteStartTransaction(commandPayload: RemoteStartTransactionRequest): Promise<DefaultResponse> {
270 const transactionConnectorId: number = commandPayload.connectorId;
271 if (transactionConnectorId) {
272 await this.chargingStation.ocppRequestService.sendStatusNotification(transactionConnectorId, OCPP16ChargePointStatus.PREPARING);
273 this.chargingStation.getConnector(transactionConnectorId).status = OCPP16ChargePointStatus.PREPARING;
274 if (this.chargingStation.isChargingStationAvailable() && this.chargingStation.isConnectorAvailable(transactionConnectorId)) {
275 // Check if authorized
276 if (this.chargingStation.getAuthorizeRemoteTxRequests()) {
277 let authorized = false;
278 if (this.chargingStation.getLocalAuthListEnabled() && this.chargingStation.hasAuthorizedTags()
279 && this.chargingStation.authorizedTags.find((value) => value === commandPayload.idTag)) {
280 authorized = true;
281 }
282 if (!authorized || (authorized && this.chargingStation.getMayAuthorizeAtRemoteStart())) {
283 const authorizeResponse = await this.chargingStation.ocppRequestService.sendAuthorize(transactionConnectorId, commandPayload.idTag);
284 if (authorizeResponse?.idTagInfo?.status === OCPP16AuthorizationStatus.ACCEPTED) {
285 authorized = true;
286 } else {
287 authorized = false;
288 }
289 }
290 if (authorized) {
291 // Authorization successful, start transaction
292 if (this.setRemoteStartTransactionChargingProfile(transactionConnectorId, commandPayload.chargingProfile)) {
293 if ((await this.chargingStation.ocppRequestService.sendStartTransaction(transactionConnectorId, commandPayload.idTag)).idTagInfo.status === OCPP16AuthorizationStatus.ACCEPTED) {
294 logger.debug(this.chargingStation.logPrefix() + ' Transaction remotely STARTED on ' + this.chargingStation.stationInfo.chargingStationId + '#' + transactionConnectorId.toString() + ' for idTag ' + commandPayload.idTag);
295 return Constants.OCPP_RESPONSE_ACCEPTED;
296 }
297 return this.notifyRemoteStartTransactionRejected(transactionConnectorId, commandPayload.idTag);
298 }
299 return this.notifyRemoteStartTransactionRejected(transactionConnectorId, commandPayload.idTag);
300 }
301 return this.notifyRemoteStartTransactionRejected(transactionConnectorId, commandPayload.idTag);
302 }
303 // No authorization check required, start transaction
304 if (this.setRemoteStartTransactionChargingProfile(transactionConnectorId, commandPayload.chargingProfile)) {
305 if ((await this.chargingStation.ocppRequestService.sendStartTransaction(transactionConnectorId, commandPayload.idTag)).idTagInfo.status === OCPP16AuthorizationStatus.ACCEPTED) {
306 logger.debug(this.chargingStation.logPrefix() + ' Transaction remotely STARTED on ' + this.chargingStation.stationInfo.chargingStationId + '#' + transactionConnectorId.toString() + ' for idTag ' + commandPayload.idTag);
307 return Constants.OCPP_RESPONSE_ACCEPTED;
308 }
309 return this.notifyRemoteStartTransactionRejected(transactionConnectorId, commandPayload.idTag);
310 }
311 return this.notifyRemoteStartTransactionRejected(transactionConnectorId, commandPayload.idTag);
312 }
313 return this.notifyRemoteStartTransactionRejected(transactionConnectorId, commandPayload.idTag);
314 }
315 return this.notifyRemoteStartTransactionRejected(transactionConnectorId, commandPayload.idTag);
316 }
317
318 private async notifyRemoteStartTransactionRejected(connectorId: number, idTag: string): Promise<DefaultResponse> {
319 if (this.chargingStation.getConnector(connectorId).status !== OCPP16ChargePointStatus.AVAILABLE) {
320 await this.chargingStation.ocppRequestService.sendStatusNotification(connectorId, OCPP16ChargePointStatus.AVAILABLE);
321 this.chargingStation.getConnector(connectorId).status = OCPP16ChargePointStatus.AVAILABLE;
322 }
323 logger.warn(this.chargingStation.logPrefix() + ' Remote starting transaction REJECTED on connector Id ' + connectorId.toString() + ', idTag ' + idTag + ', availability ' + this.chargingStation.getConnector(connectorId).availability + ', status ' + this.chargingStation.getConnector(connectorId).status);
324 return Constants.OCPP_RESPONSE_REJECTED;
325 }
326
327 private setRemoteStartTransactionChargingProfile(connectorId: number, cp: OCPP16ChargingProfile): boolean {
328 if (cp && cp.chargingProfilePurpose === ChargingProfilePurposeType.TX_PROFILE) {
329 this.chargingStation.setChargingProfile(connectorId, cp);
330 logger.debug(`${this.chargingStation.logPrefix()} Charging profile(s) set at remote start transaction, dump their stack: %j`, this.chargingStation.getConnector(connectorId).chargingProfiles);
331 return true;
332 } else if (cp && cp.chargingProfilePurpose !== ChargingProfilePurposeType.TX_PROFILE) {
333 logger.warn(`${this.chargingStation.logPrefix()} Not allowed to set ${cp.chargingProfilePurpose} charging profile(s) at remote start transaction`);
334 return false;
335 } else if (!cp) {
336 return true;
337 }
338 }
339
340 private async handleRequestRemoteStopTransaction(commandPayload: RemoteStopTransactionRequest): Promise<DefaultResponse> {
341 const transactionId = commandPayload.transactionId;
342 for (const connector in this.chargingStation.connectors) {
343 if (Utils.convertToInt(connector) > 0 && this.chargingStation.getConnector(Utils.convertToInt(connector))?.transactionId === transactionId) {
344 await this.chargingStation.ocppRequestService.sendStatusNotification(Utils.convertToInt(connector), OCPP16ChargePointStatus.FINISHING);
345 this.chargingStation.getConnector(Utils.convertToInt(connector)).status = OCPP16ChargePointStatus.FINISHING;
346 await this.chargingStation.ocppRequestService.sendStopTransaction(transactionId, this.chargingStation.getEnergyActiveImportRegisterByTransactionId(transactionId),
347 this.chargingStation.getTransactionIdTag(transactionId));
348 return Constants.OCPP_RESPONSE_ACCEPTED;
349 }
350 }
351 logger.info(this.chargingStation.logPrefix() + ' Trying to remote stop a non existing transaction ' + transactionId.toString());
352 return Constants.OCPP_RESPONSE_REJECTED;
353 }
354
355 private async handleRequestGetDiagnostics(commandPayload: GetDiagnosticsRequest): Promise<GetDiagnosticsResponse> {
356 logger.debug(this.chargingStation.logPrefix() + ' ' + IncomingRequestCommand.GET_DIAGNOSTICS + ' request received: %j', commandPayload);
357 const uri = new url.URL(commandPayload.location);
358 if (uri.protocol.startsWith('ftp:')) {
359 let ftpClient: Client;
360 try {
361 const logFiles = fs.readdirSync(path.resolve(__dirname, '../../../../')).filter((file) => file.endsWith('.log')).map((file) => path.join('./', file));
362 const diagnosticsArchive = this.chargingStation.stationInfo.chargingStationId + '_logs.tar.gz';
363 tar.create({ gzip: true }, logFiles).pipe(fs.createWriteStream(diagnosticsArchive));
364 ftpClient = new Client();
365 const accessResponse = await ftpClient.access({
366 host: uri.host,
367 ...(uri.port !== '') && { port: Utils.convertToInt(uri.port) },
368 ...(uri.username !== '') && { user: uri.username },
369 ...(uri.password !== '') && { password: uri.password },
370 });
371 let uploadResponse: FTPResponse;
372 if (accessResponse.code === 220) {
373 // eslint-disable-next-line @typescript-eslint/no-misused-promises
374 ftpClient.trackProgress(async (info) => {
375 logger.info(`${this.chargingStation.logPrefix()} ${info.bytes / 1024} bytes transferred from diagnostics archive ${info.name}`);
376 await this.chargingStation.ocppRequestService.sendDiagnosticsStatusNotification(OCPP16DiagnosticsStatus.Uploading);
377 });
378 uploadResponse = await ftpClient.uploadFrom(path.join(path.resolve(__dirname, '../../../../'), diagnosticsArchive), uri.pathname + diagnosticsArchive);
379 if (uploadResponse.code === 226) {
380 await this.chargingStation.ocppRequestService.sendDiagnosticsStatusNotification(OCPP16DiagnosticsStatus.Uploaded);
381 if (ftpClient) {
382 ftpClient.close();
383 }
384 return { fileName: diagnosticsArchive };
385 }
386 throw new OCPPError(ErrorType.GENERIC_ERROR, `Diagnostics transfer failed with error code ${accessResponse.code.toString()}${uploadResponse?.code && '|' + uploadResponse?.code.toString()}`);
387 }
388 throw new OCPPError(ErrorType.GENERIC_ERROR, `Diagnostics transfer failed with error code ${accessResponse.code.toString()}${uploadResponse?.code && '|' + uploadResponse?.code.toString()}`);
389 } catch (error) {
390 await this.chargingStation.ocppRequestService.sendDiagnosticsStatusNotification(OCPP16DiagnosticsStatus.UploadFailed);
391 if (ftpClient) {
392 ftpClient.close();
393 }
394 return this.handleIncomingRequestError(IncomingRequestCommand.GET_DIAGNOSTICS, error, Constants.OCPP_RESPONSE_EMPTY);
395 }
396 } else {
397 logger.error(`${this.chargingStation.logPrefix()} Unsupported protocol ${uri.protocol} to transfer the diagnostic logs archive`);
398 await this.chargingStation.ocppRequestService.sendDiagnosticsStatusNotification(OCPP16DiagnosticsStatus.UploadFailed);
399 return Constants.OCPP_RESPONSE_EMPTY;
400 }
401 }
402 }