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