build(deps-dev): apply updates
[e-mobility-charging-stations-simulator.git] / src / charging-station / AutomaticTransactionGenerator.ts
CommitLineData
a19b897d 1// Partial Copyright Jerome Benoit. 2021-2024. All Rights Reserved.
c8eeb62b 2
66a7748d 3import { hoursToMilliseconds, secondsToMilliseconds } from 'date-fns'
be4c6702 4
66a7748d
JB
5import type { ChargingStation } from './ChargingStation.js'
6import { checkChargingStation } from './Helpers.js'
7import { IdTagsCache } from './IdTagsCache.js'
8import { isIdTagAuthorized } from './ocpp/index.js'
9import { BaseError } from '../exception/index.js'
10import { PerformanceStatistics } from '../performance/index.js'
e7aeea18
JB
11import {
12 AuthorizationStatus,
268a74bb 13 RequestCommand,
976d11ec
JB
14 type StartTransactionRequest,
15 type StartTransactionResponse,
268a74bb 16 type Status,
e7aeea18 17 StopTransactionReason,
66a7748d
JB
18 type StopTransactionResponse
19} from '../types/index.js'
9bf0ef23
JB
20import {
21 Constants,
40615072 22 clone,
6dde6c5f 23 convertToDate,
9bf0ef23
JB
24 formatDurationMilliSeconds,
25 getRandomInteger,
5dc7c990 26 isValidDate,
9bf0ef23
JB
27 logPrefix,
28 logger,
29 secureRandom,
66a7748d
JB
30 sleep
31} from '../utils/index.js'
6af9012e 32
d1ff8599 33export class AutomaticTransactionGenerator {
e7aeea18 34 private static readonly instances: Map<string, AutomaticTransactionGenerator> = new Map<
66a7748d
JB
35 string,
36 AutomaticTransactionGenerator
37 >()
10068088 38
66a7748d
JB
39 public readonly connectorsStatus: Map<number, Status>
40 public started: boolean
41 private starting: boolean
42 private stopping: boolean
43 private readonly chargingStation: ChargingStation
6af9012e 44
66a7748d
JB
45 private constructor (chargingStation: ChargingStation) {
46 this.started = false
47 this.starting = false
48 this.stopping = false
49 this.chargingStation = chargingStation
50 this.connectorsStatus = new Map<number, Status>()
51 this.initializeConnectorsStatus()
6af9012e
JB
52 }
53
66a7748d
JB
54 public static getInstance (
55 chargingStation: ChargingStation
1895299d 56 ): AutomaticTransactionGenerator | undefined {
5199f9fd
JB
57 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
58 if (!AutomaticTransactionGenerator.instances.has(chargingStation.stationInfo!.hashId)) {
e7aeea18 59 AutomaticTransactionGenerator.instances.set(
5199f9fd
JB
60 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
61 chargingStation.stationInfo!.hashId,
66a7748d
JB
62 new AutomaticTransactionGenerator(chargingStation)
63 )
73b9adec 64 }
5199f9fd
JB
65 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
66 return AutomaticTransactionGenerator.instances.get(chargingStation.stationInfo!.hashId)
73b9adec
JB
67 }
68
e054fc1c 69 public start (stopAbsoluteDuration?: boolean): void {
66a7748d
JB
70 if (!checkChargingStation(this.chargingStation, this.logPrefix())) {
71 return
d1c6c833 72 }
66a7748d
JB
73 if (this.started) {
74 logger.warn(`${this.logPrefix()} is already started`)
75 return
b809adf1 76 }
66a7748d
JB
77 if (this.starting) {
78 logger.warn(`${this.logPrefix()} is already starting`)
79 return
11353865 80 }
66a7748d 81 this.starting = true
e054fc1c 82 this.startConnectors(stopAbsoluteDuration)
66a7748d
JB
83 this.started = true
84 this.starting = false
6af9012e
JB
85 }
86
66a7748d
JB
87 public stop (): void {
88 if (!this.started) {
89 logger.warn(`${this.logPrefix()} is already stopped`)
90 return
265e4266 91 }
66a7748d
JB
92 if (this.stopping) {
93 logger.warn(`${this.logPrefix()} is already stopping`)
94 return
11353865 95 }
66a7748d
JB
96 this.stopping = true
97 this.stopConnectors()
98 this.started = false
99 this.stopping = false
6af9012e
JB
100 }
101
e054fc1c 102 public startConnector (connectorId: number, stopAbsoluteDuration?: boolean): void {
66a7748d
JB
103 if (!checkChargingStation(this.chargingStation, this.logPrefix(connectorId))) {
104 return
d1c6c833 105 }
66a7748d
JB
106 if (!this.connectorsStatus.has(connectorId)) {
107 logger.error(`${this.logPrefix(connectorId)} starting on non existing connector`)
108 throw new BaseError(`Connector ${connectorId} does not exist`)
a5e9befc
JB
109 }
110 if (this.connectorsStatus.get(connectorId)?.start === false) {
e054fc1c 111 this.internalStartConnector(connectorId, stopAbsoluteDuration).catch(Constants.EMPTY_FUNCTION)
ecb3869d 112 } else if (this.connectorsStatus.get(connectorId)?.start === true) {
66a7748d 113 logger.warn(`${this.logPrefix(connectorId)} is already started on connector`)
a5e9befc
JB
114 }
115 }
116
66a7748d
JB
117 public stopConnector (connectorId: number): void {
118 if (!this.connectorsStatus.has(connectorId)) {
119 logger.error(`${this.logPrefix(connectorId)} stopping on non existing connector`)
120 throw new BaseError(`Connector ${connectorId} does not exist`)
ba7965c4
JB
121 }
122 if (this.connectorsStatus.get(connectorId)?.start === true) {
66a7748d
JB
123 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
124 this.connectorsStatus.get(connectorId)!.start = false
ba7965c4 125 } else if (this.connectorsStatus.get(connectorId)?.start === false) {
66a7748d 126 logger.warn(`${this.logPrefix(connectorId)} is already stopped on connector`)
ba7965c4 127 }
a5e9befc
JB
128 }
129
e054fc1c 130 private startConnectors (stopAbsoluteDuration?: boolean): void {
e7aeea18 131 if (
5199f9fd 132 this.connectorsStatus.size > 0 &&
e7aeea18
JB
133 this.connectorsStatus.size !== this.chargingStation.getNumberOfConnectors()
134 ) {
66a7748d
JB
135 this.connectorsStatus.clear()
136 this.initializeConnectorsStatus()
54544ef1 137 }
4334db72
JB
138 if (this.chargingStation.hasEvses) {
139 for (const [evseId, evseStatus] of this.chargingStation.evses) {
140 if (evseId > 0) {
141 for (const connectorId of evseStatus.connectors.keys()) {
e054fc1c 142 this.startConnector(connectorId, stopAbsoluteDuration)
4334db72
JB
143 }
144 }
145 }
146 } else {
147 for (const connectorId of this.chargingStation.connectors.keys()) {
148 if (connectorId > 0) {
e054fc1c 149 this.startConnector(connectorId, stopAbsoluteDuration)
4334db72 150 }
72740232
JB
151 }
152 }
153 }
154
66a7748d 155 private stopConnectors (): void {
4334db72
JB
156 if (this.chargingStation.hasEvses) {
157 for (const [evseId, evseStatus] of this.chargingStation.evses) {
158 if (evseId > 0) {
159 for (const connectorId of evseStatus.connectors.keys()) {
66a7748d 160 this.stopConnector(connectorId)
4334db72
JB
161 }
162 }
163 }
164 } else {
165 for (const connectorId of this.chargingStation.connectors.keys()) {
166 if (connectorId > 0) {
66a7748d 167 this.stopConnector(connectorId)
4334db72 168 }
72740232
JB
169 }
170 }
171 }
172
e054fc1c
JB
173 private async internalStartConnector (
174 connectorId: number,
175 stopAbsoluteDuration?: boolean
176 ): Promise<void> {
177 this.setStartConnectorStatus(connectorId, stopAbsoluteDuration)
e7aeea18 178 logger.info(
44eb6026 179 `${this.logPrefix(
66a7748d 180 connectorId
9bf0ef23 181 )} started on connector and will run for ${formatDurationMilliSeconds(
66a7748d 182 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
e1d9a0f4 183 this.connectorsStatus.get(connectorId)!.stopDate!.getTime() -
66a7748d
JB
184 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
185 this.connectorsStatus.get(connectorId)!.startDate!.getTime()
186 )}`
187 )
1895299d 188 while (this.connectorsStatus.get(connectorId)?.start === true) {
66a7748d
JB
189 await this.waitChargingStationAvailable(connectorId)
190 await this.waitConnectorAvailable(connectorId)
7e3bde4f 191 await this.waitRunningTransactionStopped(connectorId)
0bd926c1 192 if (!this.canStartConnector(connectorId)) {
66a7748d
JB
193 this.stopConnector(connectorId)
194 break
9b4d0c70 195 }
be4c6702 196 const wait = secondsToMilliseconds(
9bf0ef23 197 getRandomInteger(
ac7f79af 198 this.chargingStation.getAutomaticTransactionGeneratorConfiguration()
5199f9fd 199 ?.maxDelayBetweenTwoTransactions,
ac7f79af 200 this.chargingStation.getAutomaticTransactionGeneratorConfiguration()
5199f9fd 201 ?.minDelayBetweenTwoTransactions
66a7748d
JB
202 )
203 )
204 logger.info(`${this.logPrefix(connectorId)} waiting for ${formatDurationMilliSeconds(wait)}`)
205 await sleep(wait)
206 const start = secureRandom()
ac7f79af
JB
207 if (
208 start <
5199f9fd
JB
209 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
210 this.chargingStation.getAutomaticTransactionGeneratorConfiguration()!.probabilityOfStart
ac7f79af 211 ) {
66a7748d
JB
212 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
213 this.connectorsStatus.get(connectorId)!.skippedConsecutiveTransactions = 0
6af9012e 214 // Start transaction
66a7748d 215 const startResponse = await this.startTransaction(connectorId)
5199f9fd 216 if (startResponse?.idTagInfo.status === AuthorizationStatus.ACCEPTED) {
6af9012e 217 // Wait until end of transaction
be4c6702 218 const waitTrxEnd = secondsToMilliseconds(
9bf0ef23 219 getRandomInteger(
5199f9fd
JB
220 this.chargingStation.getAutomaticTransactionGeneratorConfiguration()?.maxDuration,
221 this.chargingStation.getAutomaticTransactionGeneratorConfiguration()?.minDuration
66a7748d
JB
222 )
223 )
e7aeea18 224 logger.info(
a223d9be
JB
225 `${this.logPrefix(connectorId)} transaction started with id ${
226 this.chargingStation.getConnectorStatus(connectorId)?.transactionId
227 } and will stop in ${formatDurationMilliSeconds(waitTrxEnd)}`
66a7748d
JB
228 )
229 await sleep(waitTrxEnd)
230 await this.stopTransaction(connectorId)
6af9012e
JB
231 }
232 } else {
66a7748d 233 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
0a1dd746 234 ++this.connectorsStatus.get(connectorId)!.skippedConsecutiveTransactions
66a7748d 235 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
0a1dd746 236 ++this.connectorsStatus.get(connectorId)!.skippedTransactions
e7aeea18 237 logger.info(
a223d9be
JB
238 `${this.logPrefix(connectorId)} skipped consecutively ${
239 this.connectorsStatus.get(connectorId)?.skippedConsecutiveTransactions
240 }/${this.connectorsStatus.get(connectorId)?.skippedTransactions} transaction(s)`
66a7748d 241 )
6af9012e 242 }
66a7748d
JB
243 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
244 this.connectorsStatus.get(connectorId)!.lastRunDate = new Date()
7d75bee1 245 }
66a7748d
JB
246 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
247 this.connectorsStatus.get(connectorId)!.stoppedDate = new Date()
e7aeea18 248 logger.info(
44eb6026 249 `${this.logPrefix(
66a7748d 250 connectorId
9bf0ef23 251 )} stopped on connector and lasted for ${formatDurationMilliSeconds(
66a7748d 252 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
e1d9a0f4 253 this.connectorsStatus.get(connectorId)!.stoppedDate!.getTime() -
66a7748d
JB
254 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
255 this.connectorsStatus.get(connectorId)!.startDate!.getTime()
256 )}`
257 )
e7aeea18 258 logger.debug(
be9ee554 259 `${this.logPrefix(connectorId)} connector status: %j`,
66a7748d
JB
260 this.connectorsStatus.get(connectorId)
261 )
6af9012e
JB
262 }
263
e054fc1c
JB
264 private setStartConnectorStatus (
265 connectorId: number,
266 stopAbsoluteDuration = this.chargingStation.getAutomaticTransactionGeneratorConfiguration()
267 ?.stopAbsoluteDuration
268 ): void {
66a7748d
JB
269 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
270 this.connectorsStatus.get(connectorId)!.startDate = new Date()
46a830d2 271 if (
e054fc1c 272 stopAbsoluteDuration === false ||
46a830d2 273 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
5dc7c990 274 !isValidDate(this.connectorsStatus.get(connectorId)!.stopDate)
46a830d2 275 ) {
66a7748d 276 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
0193fdd3
JB
277 this.connectorsStatus.get(connectorId)!.stopDate = new Date(
278 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
279 this.connectorsStatus.get(connectorId)!.startDate!.getTime() +
280 hoursToMilliseconds(
281 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
282 this.chargingStation.getAutomaticTransactionGeneratorConfiguration()!.stopAfterHours
283 )
284 )
285 }
0a1dd746
JB
286 delete this.connectorsStatus.get(connectorId)?.stoppedDate
287 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
288 this.connectorsStatus.get(connectorId)!.skippedConsecutiveTransactions = 0
66a7748d
JB
289 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
290 this.connectorsStatus.get(connectorId)!.start = true
4dff3039
JB
291 }
292
66a7748d
JB
293 private canStartConnector (connectorId: number): boolean {
294 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
0bd926c1 295 if (new Date() > this.connectorsStatus.get(connectorId)!.stopDate!) {
0a1dd746
JB
296 logger.info(
297 `${this.logPrefix(
298 connectorId
299 )} entered in transaction loop while the ATG stop date has been reached`
300 )
66a7748d 301 return false
0bd926c1 302 }
66a7748d 303 if (!this.chargingStation.inAcceptedState()) {
0bd926c1
JB
304 logger.error(
305 `${this.logPrefix(
66a7748d
JB
306 connectorId
307 )} entered in transaction loop while the charging station is not in accepted state`
308 )
309 return false
0bd926c1 310 }
66a7748d 311 if (!this.chargingStation.isChargingStationAvailable()) {
0bd926c1
JB
312 logger.info(
313 `${this.logPrefix(
66a7748d
JB
314 connectorId
315 )} entered in transaction loop while the charging station is unavailable`
316 )
317 return false
0bd926c1 318 }
66a7748d 319 if (!this.chargingStation.isConnectorAvailable(connectorId)) {
0bd926c1
JB
320 logger.info(
321 `${this.logPrefix(
66a7748d
JB
322 connectorId
323 )} entered in transaction loop while the connector ${connectorId} is unavailable`
324 )
325 return false
0bd926c1 326 }
66a7748d 327 return true
0bd926c1
JB
328 }
329
66a7748d
JB
330 private async waitChargingStationAvailable (connectorId: number): Promise<void> {
331 let logged = false
60400e23
JB
332 while (!this.chargingStation.isChargingStationAvailable()) {
333 if (!logged) {
334 logger.info(
335 `${this.logPrefix(
66a7748d
JB
336 connectorId
337 )} transaction loop waiting for charging station to be available`
338 )
339 logged = true
60400e23 340 }
7e3bde4f 341 await sleep(Constants.DEFAULT_ATG_WAIT_TIME)
3e888c65
JB
342 }
343 }
344
66a7748d
JB
345 private async waitConnectorAvailable (connectorId: number): Promise<void> {
346 let logged = false
60400e23
JB
347 while (!this.chargingStation.isConnectorAvailable(connectorId)) {
348 if (!logged) {
349 logger.info(
350 `${this.logPrefix(
66a7748d
JB
351 connectorId
352 )} transaction loop waiting for connector ${connectorId} to be available`
353 )
354 logged = true
60400e23 355 }
7e3bde4f
JB
356 await sleep(Constants.DEFAULT_ATG_WAIT_TIME)
357 }
358 }
359
360 private async waitRunningTransactionStopped (connectorId: number): Promise<void> {
361 const connectorStatus = this.chargingStation.getConnectorStatus(connectorId)
362 let logged = false
363 while (connectorStatus?.transactionStarted === true) {
364 if (!logged) {
365 logger.info(
366 `${this.logPrefix(
367 connectorId
368 )} transaction loop waiting for running transaction ${connectorStatus.transactionId} on connector ${connectorId} to be stopped`
369 )
370 logged = true
371 }
372 await sleep(Constants.DEFAULT_ATG_WAIT_TIME)
3e888c65
JB
373 }
374 }
375
66a7748d 376 private initializeConnectorsStatus (): void {
4334db72
JB
377 if (this.chargingStation.hasEvses) {
378 for (const [evseId, evseStatus] of this.chargingStation.evses) {
379 if (evseId > 0) {
380 for (const connectorId of evseStatus.connectors.keys()) {
66a7748d 381 this.connectorsStatus.set(connectorId, this.getConnectorStatus(connectorId))
4334db72
JB
382 }
383 }
384 }
385 } else {
386 for (const connectorId of this.chargingStation.connectors.keys()) {
387 if (connectorId > 0) {
66a7748d 388 this.connectorsStatus.set(connectorId, this.getConnectorStatus(connectorId))
4334db72 389 }
4dff3039
JB
390 }
391 }
72740232
JB
392 }
393
66a7748d 394 private getConnectorStatus (connectorId: number): Status {
e3fbf1af
JB
395 const statusIndex = connectorId - 1
396 let connectorStatus: Status | undefined
397 if (this.chargingStation.getAutomaticTransactionGeneratorStatuses()?.[statusIndex] != null) {
398 connectorStatus = clone<Status>(
399 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
400 this.chargingStation.getAutomaticTransactionGeneratorStatuses()![statusIndex]
401 )
402 } else if (this.chargingStation.getAutomaticTransactionGeneratorStatuses() != null) {
703d80dd
JB
403 logger.warn(
404 `${this.logPrefix(connectorId)} no status found for connector #${connectorId} in charging station configuration file. New status will be created`
405 )
e3fbf1af 406 }
6dde6c5f 407 if (connectorStatus != null) {
3423c8a5
JB
408 connectorStatus.startDate = convertToDate(connectorStatus.startDate)
409 connectorStatus.lastRunDate = convertToDate(connectorStatus.lastRunDate)
410 connectorStatus.stopDate = convertToDate(connectorStatus.stopDate)
411 connectorStatus.stoppedDate = convertToDate(connectorStatus.stoppedDate)
6dde6c5f
JB
412 if (
413 !this.started &&
414 (connectorStatus.start ||
415 this.chargingStation.getAutomaticTransactionGeneratorConfiguration()?.enable !== true)
416 ) {
417 connectorStatus.start = false
418 }
419 }
5ced7e80
JB
420 return (
421 connectorStatus ?? {
422 start: false,
423 authorizeRequests: 0,
424 acceptedAuthorizeRequests: 0,
425 rejectedAuthorizeRequests: 0,
426 startTransactionRequests: 0,
427 acceptedStartTransactionRequests: 0,
428 rejectedStartTransactionRequests: 0,
429 stopTransactionRequests: 0,
430 acceptedStopTransactionRequests: 0,
431 rejectedStopTransactionRequests: 0,
432 skippedConsecutiveTransactions: 0,
66a7748d 433 skippedTransactions: 0
5ced7e80 434 }
66a7748d 435 )
5ced7e80
JB
436 }
437
66a7748d
JB
438 private async startTransaction (
439 connectorId: number
0afed85f 440 ): Promise<StartTransactionResponse | undefined> {
66a7748d
JB
441 const measureId = 'StartTransaction with ATG'
442 const beginId = PerformanceStatistics.beginMeasure(measureId)
443 let startResponse: StartTransactionResponse | undefined
f911a4af
JB
444 if (this.chargingStation.hasIdTags()) {
445 const idTag = IdTagsCache.getInstance().getIdTag(
66a7748d 446 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
5199f9fd 447 this.chargingStation.getAutomaticTransactionGeneratorConfiguration()!.idTagDistribution!,
aaf2bf9c 448 this.chargingStation,
66a7748d
JB
449 connectorId
450 )
5cf9050d 451 const startTransactionLogMsg = `${this.logPrefix(
66a7748d
JB
452 connectorId
453 )} start transaction with an idTag '${idTag}'`
ccb1d6e9 454 if (this.getRequireAuthorize()) {
66a7748d 455 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
0a1dd746 456 ++this.connectorsStatus.get(connectorId)!.authorizeRequests
041365be 457 if (await isIdTagAuthorized(this.chargingStation, connectorId, idTag)) {
66a7748d 458 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
0a1dd746 459 ++this.connectorsStatus.get(connectorId)!.acceptedAuthorizeRequests
66a7748d 460 logger.info(startTransactionLogMsg)
5fdab605 461 // Start transaction
f7f98c68 462 startResponse = await this.chargingStation.ocppRequestService.requestHandler<
66a7748d
JB
463 StartTransactionRequest,
464 StartTransactionResponse
08f130a0 465 >(this.chargingStation, RequestCommand.START_TRANSACTION, {
ef6fa3fb 466 connectorId,
66a7748d
JB
467 idTag
468 })
469 this.handleStartTransactionResponse(connectorId, startResponse)
470 PerformanceStatistics.endMeasure(measureId, beginId)
471 return startResponse
5fdab605 472 }
66a7748d 473 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
0a1dd746 474 ++this.connectorsStatus.get(connectorId)!.rejectedAuthorizeRequests
66a7748d
JB
475 PerformanceStatistics.endMeasure(measureId, beginId)
476 return startResponse
ef6076c1 477 }
66a7748d 478 logger.info(startTransactionLogMsg)
5fdab605 479 // Start transaction
f7f98c68 480 startResponse = await this.chargingStation.ocppRequestService.requestHandler<
66a7748d
JB
481 StartTransactionRequest,
482 StartTransactionResponse
08f130a0 483 >(this.chargingStation, RequestCommand.START_TRANSACTION, {
ef6fa3fb 484 connectorId,
66a7748d
JB
485 idTag
486 })
487 this.handleStartTransactionResponse(connectorId, startResponse)
488 PerformanceStatistics.endMeasure(measureId, beginId)
489 return startResponse
6af9012e 490 }
66a7748d 491 logger.info(`${this.logPrefix(connectorId)} start transaction without an idTag`)
f7f98c68 492 startResponse = await this.chargingStation.ocppRequestService.requestHandler<
66a7748d
JB
493 StartTransactionRequest,
494 StartTransactionResponse
495 >(this.chargingStation, RequestCommand.START_TRANSACTION, { connectorId })
496 this.handleStartTransactionResponse(connectorId, startResponse)
497 PerformanceStatistics.endMeasure(measureId, beginId)
498 return startResponse
6af9012e
JB
499 }
500
66a7748d 501 private async stopTransaction (
e7aeea18 502 connectorId: number,
66a7748d 503 reason = StopTransactionReason.LOCAL
e1d9a0f4 504 ): Promise<StopTransactionResponse | undefined> {
66a7748d
JB
505 const measureId = 'StopTransaction with ATG'
506 const beginId = PerformanceStatistics.beginMeasure(measureId)
507 let stopResponse: StopTransactionResponse | undefined
6d9876e7 508 if (this.chargingStation.getConnectorStatus(connectorId)?.transactionStarted === true) {
49563992 509 logger.info(
a223d9be
JB
510 `${this.logPrefix(connectorId)} stop transaction with id ${
511 this.chargingStation.getConnectorStatus(connectorId)?.transactionId
512 }`
66a7748d
JB
513 )
514 stopResponse = await this.chargingStation.stopTransactionOnConnector(connectorId, reason)
515 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
0a1dd746 516 ++this.connectorsStatus.get(connectorId)!.stopTransactionRequests
5199f9fd 517 if (stopResponse.idTagInfo?.status === AuthorizationStatus.ACCEPTED) {
66a7748d 518 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
0a1dd746 519 ++this.connectorsStatus.get(connectorId)!.acceptedStopTransactionRequests
6d9876e7 520 } else {
66a7748d 521 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
0a1dd746 522 ++this.connectorsStatus.get(connectorId)!.rejectedStopTransactionRequests
6d9876e7 523 }
0045cef5 524 } else {
66a7748d 525 const transactionId = this.chargingStation.getConnectorStatus(connectorId)?.transactionId
ff581359 526 logger.debug(
ba7965c4 527 `${this.logPrefix(connectorId)} stopping a not started transaction${
401fa922 528 transactionId != null ? ` with id ${transactionId}` : ''
66a7748d
JB
529 }`
530 )
0045cef5 531 }
66a7748d
JB
532 PerformanceStatistics.endMeasure(measureId, beginId)
533 return stopResponse
c0560973
JB
534 }
535
66a7748d 536 private getRequireAuthorize (): boolean {
ac7f79af
JB
537 return (
538 this.chargingStation.getAutomaticTransactionGeneratorConfiguration()?.requireAuthorize ?? true
66a7748d 539 )
ccb1d6e9
JB
540 }
541
66a7748d 542 private readonly logPrefix = (connectorId?: number): string => {
9bf0ef23 543 return logPrefix(
5199f9fd 544 ` ${this.chargingStation.stationInfo?.chargingStationId} | ATG${
401fa922 545 connectorId != null ? ` on connector #${connectorId}` : ''
66a7748d
JB
546 }:`
547 )
548 }
d9ac47ef 549
66a7748d 550 private handleStartTransactionResponse (
d9ac47ef 551 connectorId: number,
66a7748d 552 startResponse: StartTransactionResponse
d9ac47ef 553 ): void {
66a7748d 554 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
0a1dd746 555 ++this.connectorsStatus.get(connectorId)!.startTransactionRequests
5199f9fd 556 if (startResponse.idTagInfo.status === AuthorizationStatus.ACCEPTED) {
66a7748d 557 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
0a1dd746 558 ++this.connectorsStatus.get(connectorId)!.acceptedStartTransactionRequests
d9ac47ef 559 } else {
66a7748d
JB
560 logger.warn(`${this.logPrefix(connectorId)} start transaction rejected`)
561 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
0a1dd746 562 ++this.connectorsStatus.get(connectorId)!.rejectedStartTransactionRequests
d9ac47ef
JB
563 }
564 }
6af9012e 565}