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