Commit | Line | Data |
---|---|---|
edd13439 | 1 | // Partial Copyright Jerome Benoit. 2021-2023. All Rights Reserved. |
c8eeb62b | 2 | |
01f4001e | 3 | import { AsyncResource } from 'node:async_hooks'; |
d4b944ae | 4 | |
be4c6702 JB |
5 | import { hoursToMilliseconds, secondsToMilliseconds } from 'date-fns'; |
6 | ||
4c3c0d59 | 7 | import type { ChargingStation } from './ChargingStation'; |
08b58f00 | 8 | import { checkChargingStation } from './Helpers'; |
4c3c0d59 | 9 | import { IdTagsCache } from './IdTagsCache'; |
ae725be3 | 10 | import { OCPPServiceUtils } from './ocpp'; |
268a74bb | 11 | import { BaseError } from '../exception'; |
b84bca85 | 12 | import { PerformanceStatistics } from '../performance'; |
e7aeea18 JB |
13 | import { |
14 | AuthorizationStatus, | |
9b4d0c70 | 15 | ConnectorStatusEnum, |
268a74bb | 16 | RequestCommand, |
976d11ec JB |
17 | type StartTransactionRequest, |
18 | type StartTransactionResponse, | |
268a74bb | 19 | type Status, |
e7aeea18 | 20 | StopTransactionReason, |
976d11ec | 21 | type StopTransactionResponse, |
268a74bb | 22 | } from '../types'; |
9bf0ef23 JB |
23 | import { |
24 | Constants, | |
25 | cloneObject, | |
26 | formatDurationMilliSeconds, | |
27 | getRandomInteger, | |
28 | isNullOrUndefined, | |
29 | logPrefix, | |
30 | logger, | |
31 | secureRandom, | |
32 | sleep, | |
33 | } from '../utils'; | |
6af9012e | 34 | |
d4b944ae JB |
35 | const moduleName = 'AutomaticTransactionGenerator'; |
36 | ||
268a74bb | 37 | export class AutomaticTransactionGenerator extends AsyncResource { |
e7aeea18 JB |
38 | private static readonly instances: Map<string, AutomaticTransactionGenerator> = new Map< |
39 | string, | |
40 | AutomaticTransactionGenerator | |
41 | >(); | |
10068088 | 42 | |
5e3cb728 | 43 | public readonly connectorsStatus: Map<number, Status>; |
265e4266 | 44 | public started: boolean; |
11353865 JB |
45 | private starting: boolean; |
46 | private stopping: boolean; | |
9e23580d | 47 | private readonly chargingStation: ChargingStation; |
6af9012e | 48 | |
ac7f79af | 49 | private constructor(chargingStation: ChargingStation) { |
d4b944ae | 50 | super(moduleName); |
aa428a31 | 51 | this.started = false; |
11353865 JB |
52 | this.starting = false; |
53 | this.stopping = false; | |
ad2f27c3 | 54 | this.chargingStation = chargingStation; |
7807ccf2 JB |
55 | this.connectorsStatus = new Map<number, Status>(); |
56 | this.initializeConnectorsStatus(); | |
6af9012e JB |
57 | } |
58 | ||
fa7bccf4 | 59 | public static getInstance( |
5edd8ba0 | 60 | chargingStation: ChargingStation, |
1895299d | 61 | ): AutomaticTransactionGenerator | undefined { |
4dff3039 | 62 | if (AutomaticTransactionGenerator.instances.has(chargingStation.stationInfo.hashId) === false) { |
e7aeea18 | 63 | AutomaticTransactionGenerator.instances.set( |
51c83d6f | 64 | chargingStation.stationInfo.hashId, |
5edd8ba0 | 65 | new AutomaticTransactionGenerator(chargingStation), |
e7aeea18 | 66 | ); |
73b9adec | 67 | } |
51c83d6f | 68 | return AutomaticTransactionGenerator.instances.get(chargingStation.stationInfo.hashId); |
73b9adec JB |
69 | } |
70 | ||
7d75bee1 | 71 | public start(): void { |
fba11dc6 | 72 | if (checkChargingStation(this.chargingStation, this.logPrefix()) === false) { |
d1c6c833 JB |
73 | return; |
74 | } | |
a5e9befc | 75 | if (this.started === true) { |
ba7965c4 | 76 | logger.warn(`${this.logPrefix()} is already started`); |
b809adf1 JB |
77 | return; |
78 | } | |
11353865 JB |
79 | if (this.starting === true) { |
80 | logger.warn(`${this.logPrefix()} is already starting`); | |
81 | return; | |
82 | } | |
83 | this.starting = true; | |
72740232 | 84 | this.startConnectors(); |
265e4266 | 85 | this.started = true; |
11353865 | 86 | this.starting = false; |
6af9012e JB |
87 | } |
88 | ||
49563992 | 89 | public async stop(): Promise<void> { |
a5e9befc | 90 | if (this.started === false) { |
ba7965c4 | 91 | logger.warn(`${this.logPrefix()} is already stopped`); |
265e4266 JB |
92 | return; |
93 | } | |
11353865 JB |
94 | if (this.stopping === true) { |
95 | logger.warn(`${this.logPrefix()} is already stopping`); | |
96 | return; | |
97 | } | |
98 | this.stopping = true; | |
49563992 | 99 | await this.stopConnectors(); |
265e4266 | 100 | this.started = false; |
11353865 | 101 | this.stopping = false; |
6af9012e JB |
102 | } |
103 | ||
a5e9befc | 104 | public startConnector(connectorId: number): void { |
fba11dc6 | 105 | if (checkChargingStation(this.chargingStation, this.logPrefix(connectorId)) === false) { |
d1c6c833 JB |
106 | return; |
107 | } | |
7807ccf2 | 108 | if (this.connectorsStatus.has(connectorId) === false) { |
a03a128d | 109 | logger.error(`${this.logPrefix(connectorId)} starting on non existing connector`); |
7807ccf2 | 110 | throw new BaseError(`Connector ${connectorId} does not exist`); |
a5e9befc JB |
111 | } |
112 | if (this.connectorsStatus.get(connectorId)?.start === false) { | |
d4b944ae | 113 | this.runInAsyncScope( |
e6159ce8 JB |
114 | this.internalStartConnector.bind(this) as ( |
115 | this: AutomaticTransactionGenerator, | |
e843aa40 | 116 | ...args: unknown[] |
64818750 | 117 | ) => Promise<void>, |
d4b944ae | 118 | this, |
5edd8ba0 | 119 | connectorId, |
59b6ed8d | 120 | ).catch(Constants.EMPTY_FUNCTION); |
ecb3869d | 121 | } else if (this.connectorsStatus.get(connectorId)?.start === true) { |
ba7965c4 | 122 | logger.warn(`${this.logPrefix(connectorId)} is already started on connector`); |
a5e9befc JB |
123 | } |
124 | } | |
125 | ||
49563992 | 126 | public async stopConnector(connectorId: number): Promise<void> { |
7807ccf2 | 127 | if (this.connectorsStatus.has(connectorId) === false) { |
a03a128d | 128 | logger.error(`${this.logPrefix(connectorId)} stopping on non existing connector`); |
7807ccf2 | 129 | throw new BaseError(`Connector ${connectorId} does not exist`); |
ba7965c4 JB |
130 | } |
131 | if (this.connectorsStatus.get(connectorId)?.start === true) { | |
e1d9a0f4 | 132 | this.connectorsStatus.get(connectorId)!.start = false; |
49563992 | 133 | await this.stopTransaction(connectorId); |
ba7965c4 JB |
134 | } else if (this.connectorsStatus.get(connectorId)?.start === false) { |
135 | logger.warn(`${this.logPrefix(connectorId)} is already stopped on connector`); | |
136 | } | |
a5e9befc JB |
137 | } |
138 | ||
72740232 | 139 | private startConnectors(): void { |
e7aeea18 JB |
140 | if ( |
141 | this.connectorsStatus?.size > 0 && | |
142 | this.connectorsStatus.size !== this.chargingStation.getNumberOfConnectors() | |
143 | ) { | |
54544ef1 | 144 | this.connectorsStatus.clear(); |
7807ccf2 | 145 | this.initializeConnectorsStatus(); |
54544ef1 | 146 | } |
4334db72 JB |
147 | if (this.chargingStation.hasEvses) { |
148 | for (const [evseId, evseStatus] of this.chargingStation.evses) { | |
149 | if (evseId > 0) { | |
150 | for (const connectorId of evseStatus.connectors.keys()) { | |
151 | this.startConnector(connectorId); | |
152 | } | |
153 | } | |
154 | } | |
155 | } else { | |
156 | for (const connectorId of this.chargingStation.connectors.keys()) { | |
157 | if (connectorId > 0) { | |
158 | this.startConnector(connectorId); | |
159 | } | |
72740232 JB |
160 | } |
161 | } | |
162 | } | |
163 | ||
49563992 | 164 | private async stopConnectors(): Promise<void> { |
4334db72 JB |
165 | if (this.chargingStation.hasEvses) { |
166 | for (const [evseId, evseStatus] of this.chargingStation.evses) { | |
167 | if (evseId > 0) { | |
168 | for (const connectorId of evseStatus.connectors.keys()) { | |
49563992 | 169 | await this.stopConnector(connectorId); |
4334db72 JB |
170 | } |
171 | } | |
172 | } | |
173 | } else { | |
174 | for (const connectorId of this.chargingStation.connectors.keys()) { | |
175 | if (connectorId > 0) { | |
49563992 | 176 | await this.stopConnector(connectorId); |
4334db72 | 177 | } |
72740232 JB |
178 | } |
179 | } | |
180 | } | |
181 | ||
83a3286a | 182 | private async internalStartConnector(connectorId: number): Promise<void> { |
083fb002 | 183 | this.setStartConnectorStatus(connectorId); |
e7aeea18 | 184 | logger.info( |
44eb6026 | 185 | `${this.logPrefix( |
5edd8ba0 | 186 | connectorId, |
9bf0ef23 | 187 | )} started on connector and will run for ${formatDurationMilliSeconds( |
e1d9a0f4 JB |
188 | this.connectorsStatus.get(connectorId)!.stopDate!.getTime() - |
189 | this.connectorsStatus.get(connectorId)!.startDate!.getTime(), | |
5edd8ba0 | 190 | )}`, |
e7aeea18 | 191 | ); |
1895299d | 192 | while (this.connectorsStatus.get(connectorId)?.start === true) { |
0bd926c1 | 193 | if (!this.canStartConnector(connectorId)) { |
49563992 | 194 | await this.stopConnector(connectorId); |
9b4d0c70 JB |
195 | break; |
196 | } | |
c0560973 | 197 | if (!this.chargingStation?.ocppRequestService) { |
e7aeea18 JB |
198 | logger.info( |
199 | `${this.logPrefix( | |
5edd8ba0 JB |
200 | connectorId, |
201 | )} transaction loop waiting for charging station service to be initialized`, | |
e7aeea18 | 202 | ); |
c0560973 | 203 | do { |
9bf0ef23 | 204 | await sleep(Constants.CHARGING_STATION_ATG_INITIALIZATION_TIME); |
c0560973 JB |
205 | } while (!this.chargingStation?.ocppRequestService); |
206 | } | |
be4c6702 | 207 | const wait = secondsToMilliseconds( |
9bf0ef23 | 208 | getRandomInteger( |
ac7f79af | 209 | this.chargingStation.getAutomaticTransactionGeneratorConfiguration() |
86b46b49 | 210 | .maxDelayBetweenTwoTransactions, |
ac7f79af | 211 | this.chargingStation.getAutomaticTransactionGeneratorConfiguration() |
5edd8ba0 | 212 | .minDelayBetweenTwoTransactions, |
be4c6702 JB |
213 | ), |
214 | ); | |
9bf0ef23 JB |
215 | logger.info(`${this.logPrefix(connectorId)} waiting for ${formatDurationMilliSeconds(wait)}`); |
216 | await sleep(wait); | |
217 | const start = secureRandom(); | |
ac7f79af JB |
218 | if ( |
219 | start < | |
220 | this.chargingStation.getAutomaticTransactionGeneratorConfiguration().probabilityOfStart | |
221 | ) { | |
e1d9a0f4 | 222 | this.connectorsStatus.get(connectorId)!.skippedConsecutiveTransactions = 0; |
6af9012e | 223 | // Start transaction |
aef1b33a | 224 | const startResponse = await this.startTransaction(connectorId); |
0afed85f | 225 | if (startResponse?.idTagInfo?.status === AuthorizationStatus.ACCEPTED) { |
6af9012e | 226 | // Wait until end of transaction |
be4c6702 | 227 | const waitTrxEnd = secondsToMilliseconds( |
9bf0ef23 | 228 | getRandomInteger( |
86b46b49 | 229 | this.chargingStation.getAutomaticTransactionGeneratorConfiguration().maxDuration, |
5edd8ba0 | 230 | this.chargingStation.getAutomaticTransactionGeneratorConfiguration().minDuration, |
be4c6702 JB |
231 | ), |
232 | ); | |
e7aeea18 | 233 | logger.info( |
1c9de2b9 JB |
234 | `${this.logPrefix( |
235 | connectorId, | |
236 | )} transaction started with id ${this.chargingStation.getConnectorStatus(connectorId) | |
237 | ?.transactionId} and will stop in ${formatDurationMilliSeconds(waitTrxEnd)}`, | |
e7aeea18 | 238 | ); |
9bf0ef23 | 239 | await sleep(waitTrxEnd); |
85d20667 | 240 | await this.stopTransaction(connectorId); |
6af9012e JB |
241 | } |
242 | } else { | |
e1d9a0f4 JB |
243 | ++this.connectorsStatus.get(connectorId)!.skippedConsecutiveTransactions!; |
244 | ++this.connectorsStatus.get(connectorId)!.skippedTransactions!; | |
e7aeea18 | 245 | logger.info( |
1c9de2b9 JB |
246 | `${this.logPrefix(connectorId)} skipped consecutively ${this.connectorsStatus.get( |
247 | connectorId, | |
248 | )?.skippedConsecutiveTransactions}/${this.connectorsStatus.get(connectorId) | |
249 | ?.skippedTransactions} transaction(s)`, | |
e7aeea18 | 250 | ); |
6af9012e | 251 | } |
e1d9a0f4 | 252 | this.connectorsStatus.get(connectorId)!.lastRunDate = new Date(); |
7d75bee1 | 253 | } |
e1d9a0f4 | 254 | this.connectorsStatus.get(connectorId)!.stoppedDate = new Date(); |
e7aeea18 | 255 | logger.info( |
44eb6026 | 256 | `${this.logPrefix( |
5edd8ba0 | 257 | connectorId, |
9bf0ef23 | 258 | )} stopped on connector and lasted for ${formatDurationMilliSeconds( |
e1d9a0f4 JB |
259 | this.connectorsStatus.get(connectorId)!.stoppedDate!.getTime() - |
260 | this.connectorsStatus.get(connectorId)!.startDate!.getTime(), | |
5edd8ba0 | 261 | )}`, |
e7aeea18 JB |
262 | ); |
263 | logger.debug( | |
be9ee554 | 264 | `${this.logPrefix(connectorId)} connector status: %j`, |
5edd8ba0 | 265 | this.connectorsStatus.get(connectorId), |
e7aeea18 | 266 | ); |
6af9012e JB |
267 | } |
268 | ||
083fb002 | 269 | private setStartConnectorStatus(connectorId: number): void { |
e1d9a0f4 | 270 | this.connectorsStatus.get(connectorId)!.skippedConsecutiveTransactions = 0; |
e7aeea18 | 271 | const previousRunDuration = |
72092cfc JB |
272 | this.connectorsStatus.get(connectorId)?.startDate && |
273 | this.connectorsStatus.get(connectorId)?.lastRunDate | |
e1d9a0f4 JB |
274 | ? this.connectorsStatus.get(connectorId)!.lastRunDate!.getTime() - |
275 | this.connectorsStatus.get(connectorId)!.startDate!.getTime() | |
e7aeea18 | 276 | : 0; |
e1d9a0f4 JB |
277 | this.connectorsStatus.get(connectorId)!.startDate = new Date(); |
278 | this.connectorsStatus.get(connectorId)!.stopDate = new Date( | |
279 | this.connectorsStatus.get(connectorId)!.startDate!.getTime() + | |
be4c6702 JB |
280 | hoursToMilliseconds( |
281 | this.chargingStation.getAutomaticTransactionGeneratorConfiguration().stopAfterHours, | |
282 | ) - | |
5edd8ba0 | 283 | previousRunDuration, |
e7aeea18 | 284 | ); |
e1d9a0f4 | 285 | this.connectorsStatus.get(connectorId)!.start = true; |
4dff3039 JB |
286 | } |
287 | ||
0bd926c1 JB |
288 | private canStartConnector(connectorId: number): boolean { |
289 | if (new Date() > this.connectorsStatus.get(connectorId)!.stopDate!) { | |
290 | return false; | |
291 | } | |
292 | if (this.chargingStation.inAcceptedState() === false) { | |
293 | logger.error( | |
294 | `${this.logPrefix( | |
295 | connectorId, | |
296 | )} entered in transaction loop while the charging station is not in accepted state`, | |
297 | ); | |
298 | return false; | |
299 | } | |
300 | if (this.chargingStation.isChargingStationAvailable() === false) { | |
301 | logger.info( | |
302 | `${this.logPrefix( | |
303 | connectorId, | |
304 | )} entered in transaction loop while the charging station is unavailable`, | |
305 | ); | |
306 | return false; | |
307 | } | |
308 | if (this.chargingStation.isConnectorAvailable(connectorId) === false) { | |
309 | logger.info( | |
310 | `${this.logPrefix( | |
311 | connectorId, | |
312 | )} entered in transaction loop while the connector ${connectorId} is unavailable`, | |
313 | ); | |
314 | return false; | |
315 | } | |
316 | if ( | |
317 | this.chargingStation.getConnectorStatus(connectorId)?.status === | |
318 | ConnectorStatusEnum.Unavailable | |
319 | ) { | |
320 | logger.info( | |
321 | `${this.logPrefix( | |
322 | connectorId, | |
323 | )} entered in transaction loop while the connector ${connectorId} status is unavailable`, | |
324 | ); | |
325 | return false; | |
326 | } | |
327 | return true; | |
328 | } | |
329 | ||
7807ccf2 | 330 | private initializeConnectorsStatus(): void { |
4334db72 JB |
331 | if (this.chargingStation.hasEvses) { |
332 | for (const [evseId, evseStatus] of this.chargingStation.evses) { | |
333 | if (evseId > 0) { | |
334 | for (const connectorId of evseStatus.connectors.keys()) { | |
5ced7e80 | 335 | this.connectorsStatus.set(connectorId, this.getConnectorStatus(connectorId)); |
4334db72 JB |
336 | } |
337 | } | |
338 | } | |
339 | } else { | |
340 | for (const connectorId of this.chargingStation.connectors.keys()) { | |
341 | if (connectorId > 0) { | |
5ced7e80 | 342 | this.connectorsStatus.set(connectorId, this.getConnectorStatus(connectorId)); |
4334db72 | 343 | } |
4dff3039 JB |
344 | } |
345 | } | |
72740232 JB |
346 | } |
347 | ||
5ced7e80 | 348 | private getConnectorStatus(connectorId: number): Status { |
0a0da58d JB |
349 | const connectorStatus = this.chargingStation.getAutomaticTransactionGeneratorStatuses()?.[ |
350 | connectorId | |
351 | ] | |
a82d0329 JB |
352 | ? cloneObject<Status>( |
353 | this.chargingStation.getAutomaticTransactionGeneratorStatuses()![connectorId], | |
354 | ) | |
bdc9dc79 | 355 | : undefined; |
5ced7e80 JB |
356 | delete connectorStatus?.startDate; |
357 | delete connectorStatus?.lastRunDate; | |
358 | delete connectorStatus?.stopDate; | |
359 | delete connectorStatus?.stoppedDate; | |
9ceba264 | 360 | if (!this.started && connectorStatus?.start === true) { |
10687422 JB |
361 | connectorStatus.start = false; |
362 | } | |
5ced7e80 JB |
363 | return ( |
364 | connectorStatus ?? { | |
365 | start: false, | |
366 | authorizeRequests: 0, | |
367 | acceptedAuthorizeRequests: 0, | |
368 | rejectedAuthorizeRequests: 0, | |
369 | startTransactionRequests: 0, | |
370 | acceptedStartTransactionRequests: 0, | |
371 | rejectedStartTransactionRequests: 0, | |
372 | stopTransactionRequests: 0, | |
373 | acceptedStopTransactionRequests: 0, | |
374 | rejectedStopTransactionRequests: 0, | |
375 | skippedConsecutiveTransactions: 0, | |
376 | skippedTransactions: 0, | |
377 | } | |
378 | ); | |
379 | } | |
380 | ||
e7aeea18 | 381 | private async startTransaction( |
5edd8ba0 | 382 | connectorId: number, |
0afed85f | 383 | ): Promise<StartTransactionResponse | undefined> { |
aef1b33a JB |
384 | const measureId = 'StartTransaction with ATG'; |
385 | const beginId = PerformanceStatistics.beginMeasure(measureId); | |
e1d9a0f4 | 386 | let startResponse: StartTransactionResponse | undefined; |
f911a4af JB |
387 | if (this.chargingStation.hasIdTags()) { |
388 | const idTag = IdTagsCache.getInstance().getIdTag( | |
e1d9a0f4 | 389 | this.chargingStation.getAutomaticTransactionGeneratorConfiguration().idTagDistribution!, |
aaf2bf9c | 390 | this.chargingStation, |
5edd8ba0 | 391 | connectorId, |
aaf2bf9c | 392 | ); |
5cf9050d | 393 | const startTransactionLogMsg = `${this.logPrefix( |
5edd8ba0 | 394 | connectorId, |
ba7965c4 | 395 | )} start transaction with an idTag '${idTag}'`; |
ccb1d6e9 | 396 | if (this.getRequireAuthorize()) { |
e1d9a0f4 | 397 | ++this.connectorsStatus.get(connectorId)!.authorizeRequests!; |
ae725be3 | 398 | if (await OCPPServiceUtils.isIdTagAuthorized(this.chargingStation, connectorId, idTag)) { |
e1d9a0f4 | 399 | ++this.connectorsStatus.get(connectorId)!.acceptedAuthorizeRequests!; |
5cf9050d | 400 | logger.info(startTransactionLogMsg); |
5fdab605 | 401 | // Start transaction |
f7f98c68 | 402 | startResponse = await this.chargingStation.ocppRequestService.requestHandler< |
ef6fa3fb JB |
403 | StartTransactionRequest, |
404 | StartTransactionResponse | |
08f130a0 | 405 | >(this.chargingStation, RequestCommand.START_TRANSACTION, { |
ef6fa3fb JB |
406 | connectorId, |
407 | idTag, | |
408 | }); | |
d9ac47ef | 409 | this.handleStartTransactionResponse(connectorId, startResponse); |
aef1b33a JB |
410 | PerformanceStatistics.endMeasure(measureId, beginId); |
411 | return startResponse; | |
5fdab605 | 412 | } |
e1d9a0f4 | 413 | ++this.connectorsStatus.get(connectorId)!.rejectedAuthorizeRequests!; |
aef1b33a | 414 | PerformanceStatistics.endMeasure(measureId, beginId); |
0afed85f | 415 | return startResponse; |
ef6076c1 | 416 | } |
5cf9050d | 417 | logger.info(startTransactionLogMsg); |
5fdab605 | 418 | // Start transaction |
f7f98c68 | 419 | startResponse = await this.chargingStation.ocppRequestService.requestHandler< |
ef6fa3fb JB |
420 | StartTransactionRequest, |
421 | StartTransactionResponse | |
08f130a0 | 422 | >(this.chargingStation, RequestCommand.START_TRANSACTION, { |
ef6fa3fb JB |
423 | connectorId, |
424 | idTag, | |
425 | }); | |
d9ac47ef | 426 | this.handleStartTransactionResponse(connectorId, startResponse); |
aef1b33a JB |
427 | PerformanceStatistics.endMeasure(measureId, beginId); |
428 | return startResponse; | |
6af9012e | 429 | } |
5cf9050d | 430 | logger.info(`${this.logPrefix(connectorId)} start transaction without an idTag`); |
f7f98c68 | 431 | startResponse = await this.chargingStation.ocppRequestService.requestHandler< |
ef6fa3fb JB |
432 | StartTransactionRequest, |
433 | StartTransactionResponse | |
08f130a0 | 434 | >(this.chargingStation, RequestCommand.START_TRANSACTION, { connectorId }); |
431b6bd5 | 435 | this.handleStartTransactionResponse(connectorId, startResponse); |
aef1b33a JB |
436 | PerformanceStatistics.endMeasure(measureId, beginId); |
437 | return startResponse; | |
6af9012e JB |
438 | } |
439 | ||
e7aeea18 JB |
440 | private async stopTransaction( |
441 | connectorId: number, | |
5edd8ba0 | 442 | reason: StopTransactionReason = StopTransactionReason.LOCAL, |
e1d9a0f4 | 443 | ): Promise<StopTransactionResponse | undefined> { |
aef1b33a JB |
444 | const measureId = 'StopTransaction with ATG'; |
445 | const beginId = PerformanceStatistics.beginMeasure(measureId); | |
e1d9a0f4 | 446 | let stopResponse: StopTransactionResponse | undefined; |
6d9876e7 | 447 | if (this.chargingStation.getConnectorStatus(connectorId)?.transactionStarted === true) { |
49563992 JB |
448 | logger.info( |
449 | `${this.logPrefix( | |
450 | connectorId, | |
451 | )} stop transaction with id ${this.chargingStation.getConnectorStatus(connectorId) | |
452 | ?.transactionId}`, | |
453 | ); | |
5e3cb728 | 454 | stopResponse = await this.chargingStation.stopTransactionOnConnector(connectorId, reason); |
e1d9a0f4 | 455 | ++this.connectorsStatus.get(connectorId)!.stopTransactionRequests!; |
0afed85f | 456 | if (stopResponse?.idTagInfo?.status === AuthorizationStatus.ACCEPTED) { |
e1d9a0f4 | 457 | ++this.connectorsStatus.get(connectorId)!.acceptedStopTransactionRequests!; |
6d9876e7 | 458 | } else { |
e1d9a0f4 | 459 | ++this.connectorsStatus.get(connectorId)!.rejectedStopTransactionRequests!; |
6d9876e7 | 460 | } |
0045cef5 | 461 | } else { |
1895299d | 462 | const transactionId = this.chargingStation.getConnectorStatus(connectorId)?.transactionId; |
ff581359 | 463 | logger.debug( |
ba7965c4 | 464 | `${this.logPrefix(connectorId)} stopping a not started transaction${ |
1c9de2b9 | 465 | !isNullOrUndefined(transactionId) ? ` with id ${transactionId}` : '' |
5edd8ba0 | 466 | }`, |
e7aeea18 | 467 | ); |
0045cef5 | 468 | } |
aef1b33a JB |
469 | PerformanceStatistics.endMeasure(measureId, beginId); |
470 | return stopResponse; | |
c0560973 JB |
471 | } |
472 | ||
ccb1d6e9 | 473 | private getRequireAuthorize(): boolean { |
ac7f79af JB |
474 | return ( |
475 | this.chargingStation.getAutomaticTransactionGeneratorConfiguration()?.requireAuthorize ?? true | |
476 | ); | |
ccb1d6e9 JB |
477 | } |
478 | ||
8b7072dc | 479 | private logPrefix = (connectorId?: number): string => { |
9bf0ef23 | 480 | return logPrefix( |
6cd85def | 481 | ` ${this.chargingStation.stationInfo.chargingStationId} | ATG${ |
1c9de2b9 | 482 | !isNullOrUndefined(connectorId) ? ` on connector #${connectorId}` : '' |
5edd8ba0 | 483 | }:`, |
6cd85def | 484 | ); |
8b7072dc | 485 | }; |
d9ac47ef JB |
486 | |
487 | private handleStartTransactionResponse( | |
488 | connectorId: number, | |
5edd8ba0 | 489 | startResponse: StartTransactionResponse, |
d9ac47ef | 490 | ): void { |
e1d9a0f4 | 491 | ++this.connectorsStatus.get(connectorId)!.startTransactionRequests!; |
d9ac47ef | 492 | if (startResponse?.idTagInfo?.status === AuthorizationStatus.ACCEPTED) { |
e1d9a0f4 | 493 | ++this.connectorsStatus.get(connectorId)!.acceptedStartTransactionRequests!; |
d9ac47ef | 494 | } else { |
44eb6026 | 495 | logger.warn(`${this.logPrefix(connectorId)} start transaction rejected`); |
e1d9a0f4 | 496 | ++this.connectorsStatus.get(connectorId)!.rejectedStartTransactionRequests!; |
d9ac47ef JB |
497 | } |
498 | } | |
6af9012e | 499 | } |