fix: fix ATG connector statuses date handling
[e-mobility-charging-stations-simulator.git] / src / charging-station / AutomaticTransactionGenerator.ts
CommitLineData
edd13439 1// Partial Copyright Jerome Benoit. 2021-2023. 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,
22 cloneObject,
6dde6c5f 23 convertToDate,
9bf0ef23
JB
24 formatDurationMilliSeconds,
25 getRandomInteger,
0a1dd746 26 isValidTime,
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
66a7748d
JB
69 public start (): void {
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
JB
81 this.starting = true
82 this.startConnectors()
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
66a7748d
JB
102 public startConnector (connectorId: number): void {
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) {
66a7748d 111 this.internalStartConnector(connectorId).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
66a7748d 130 private startConnectors (): 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()) {
66a7748d 142 this.startConnector(connectorId)
4334db72
JB
143 }
144 }
145 }
146 } else {
147 for (const connectorId of this.chargingStation.connectors.keys()) {
148 if (connectorId > 0) {
66a7748d 149 this.startConnector(connectorId)
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
66a7748d
JB
173 private async internalStartConnector (connectorId: number): Promise<void> {
174 this.setStartConnectorStatus(connectorId)
e7aeea18 175 logger.info(
44eb6026 176 `${this.logPrefix(
66a7748d 177 connectorId
9bf0ef23 178 )} started on connector and will run for ${formatDurationMilliSeconds(
66a7748d 179 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
e1d9a0f4 180 this.connectorsStatus.get(connectorId)!.stopDate!.getTime() -
66a7748d
JB
181 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
182 this.connectorsStatus.get(connectorId)!.startDate!.getTime()
183 )}`
184 )
1895299d 185 while (this.connectorsStatus.get(connectorId)?.start === true) {
66a7748d
JB
186 await this.waitChargingStationAvailable(connectorId)
187 await this.waitConnectorAvailable(connectorId)
0bd926c1 188 if (!this.canStartConnector(connectorId)) {
66a7748d
JB
189 this.stopConnector(connectorId)
190 break
9b4d0c70 191 }
be4c6702 192 const wait = secondsToMilliseconds(
9bf0ef23 193 getRandomInteger(
ac7f79af 194 this.chargingStation.getAutomaticTransactionGeneratorConfiguration()
5199f9fd 195 ?.maxDelayBetweenTwoTransactions,
ac7f79af 196 this.chargingStation.getAutomaticTransactionGeneratorConfiguration()
5199f9fd 197 ?.minDelayBetweenTwoTransactions
66a7748d
JB
198 )
199 )
200 logger.info(`${this.logPrefix(connectorId)} waiting for ${formatDurationMilliSeconds(wait)}`)
201 await sleep(wait)
202 const start = secureRandom()
ac7f79af
JB
203 if (
204 start <
5199f9fd
JB
205 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
206 this.chargingStation.getAutomaticTransactionGeneratorConfiguration()!.probabilityOfStart
ac7f79af 207 ) {
66a7748d
JB
208 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
209 this.connectorsStatus.get(connectorId)!.skippedConsecutiveTransactions = 0
6af9012e 210 // Start transaction
66a7748d 211 const startResponse = await this.startTransaction(connectorId)
5199f9fd 212 if (startResponse?.idTagInfo.status === AuthorizationStatus.ACCEPTED) {
6af9012e 213 // Wait until end of transaction
be4c6702 214 const waitTrxEnd = secondsToMilliseconds(
9bf0ef23 215 getRandomInteger(
5199f9fd
JB
216 this.chargingStation.getAutomaticTransactionGeneratorConfiguration()?.maxDuration,
217 this.chargingStation.getAutomaticTransactionGeneratorConfiguration()?.minDuration
66a7748d
JB
218 )
219 )
e7aeea18 220 logger.info(
1c9de2b9 221 `${this.logPrefix(
66a7748d 222 connectorId
1c9de2b9 223 )} transaction started with id ${this.chargingStation.getConnectorStatus(connectorId)
66a7748d
JB
224 ?.transactionId} and will stop in ${formatDurationMilliSeconds(waitTrxEnd)}`
225 )
226 await sleep(waitTrxEnd)
227 await this.stopTransaction(connectorId)
6af9012e
JB
228 }
229 } else {
66a7748d 230 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
0a1dd746 231 ++this.connectorsStatus.get(connectorId)!.skippedConsecutiveTransactions
66a7748d 232 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
0a1dd746 233 ++this.connectorsStatus.get(connectorId)!.skippedTransactions
e7aeea18 234 logger.info(
1c9de2b9 235 `${this.logPrefix(connectorId)} skipped consecutively ${this.connectorsStatus.get(
66a7748d 236 connectorId
1c9de2b9 237 )?.skippedConsecutiveTransactions}/${this.connectorsStatus.get(connectorId)
66a7748d
JB
238 ?.skippedTransactions} transaction(s)`
239 )
6af9012e 240 }
66a7748d
JB
241 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
242 this.connectorsStatus.get(connectorId)!.lastRunDate = new Date()
7d75bee1 243 }
66a7748d
JB
244 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
245 this.connectorsStatus.get(connectorId)!.stoppedDate = new Date()
e7aeea18 246 logger.info(
44eb6026 247 `${this.logPrefix(
66a7748d 248 connectorId
9bf0ef23 249 )} stopped on connector and lasted for ${formatDurationMilliSeconds(
66a7748d 250 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
e1d9a0f4 251 this.connectorsStatus.get(connectorId)!.stoppedDate!.getTime() -
66a7748d
JB
252 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
253 this.connectorsStatus.get(connectorId)!.startDate!.getTime()
254 )}`
255 )
e7aeea18 256 logger.debug(
be9ee554 257 `${this.logPrefix(connectorId)} connector status: %j`,
66a7748d
JB
258 this.connectorsStatus.get(connectorId)
259 )
6af9012e
JB
260 }
261
66a7748d 262 private setStartConnectorStatus (connectorId: number): void {
e7aeea18 263 const previousRunDuration =
0a1dd746 264 isValidTime(this.connectorsStatus.get(connectorId)?.startDate) &&
6dde6c5f
JB
265 isValidTime(this.connectorsStatus.get(connectorId)?.lastRunDate) &&
266 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
267 this.connectorsStatus.get(connectorId)!.lastRunDate! >
268 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
269 this.connectorsStatus.get(connectorId)!.startDate!
66a7748d
JB
270 ? // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
271 this.connectorsStatus.get(connectorId)!.lastRunDate!.getTime() -
272 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
e1d9a0f4 273 this.connectorsStatus.get(connectorId)!.startDate!.getTime()
66a7748d
JB
274 : 0
275 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
276 this.connectorsStatus.get(connectorId)!.startDate = new Date()
277 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
e1d9a0f4 278 this.connectorsStatus.get(connectorId)!.stopDate = new Date(
66a7748d 279 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
e1d9a0f4 280 this.connectorsStatus.get(connectorId)!.startDate!.getTime() +
be4c6702 281 hoursToMilliseconds(
5199f9fd
JB
282 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
283 this.chargingStation.getAutomaticTransactionGeneratorConfiguration()!.stopAfterHours
be4c6702 284 ) -
66a7748d
JB
285 previousRunDuration
286 )
0a1dd746
JB
287 delete this.connectorsStatus.get(connectorId)?.stoppedDate
288 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
289 this.connectorsStatus.get(connectorId)!.skippedConsecutiveTransactions = 0
66a7748d
JB
290 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
291 this.connectorsStatus.get(connectorId)!.start = true
4dff3039
JB
292 }
293
66a7748d
JB
294 private canStartConnector (connectorId: number): boolean {
295 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
0bd926c1 296 if (new Date() > this.connectorsStatus.get(connectorId)!.stopDate!) {
0a1dd746
JB
297 logger.info(
298 `${this.logPrefix(
299 connectorId
300 )} entered in transaction loop while the ATG stop date has been reached`
301 )
66a7748d 302 return false
0bd926c1 303 }
66a7748d 304 if (!this.chargingStation.inAcceptedState()) {
0bd926c1
JB
305 logger.error(
306 `${this.logPrefix(
66a7748d
JB
307 connectorId
308 )} entered in transaction loop while the charging station is not in accepted state`
309 )
310 return false
0bd926c1 311 }
66a7748d 312 if (!this.chargingStation.isChargingStationAvailable()) {
0bd926c1
JB
313 logger.info(
314 `${this.logPrefix(
66a7748d
JB
315 connectorId
316 )} entered in transaction loop while the charging station is unavailable`
317 )
318 return false
0bd926c1 319 }
66a7748d 320 if (!this.chargingStation.isConnectorAvailable(connectorId)) {
0bd926c1
JB
321 logger.info(
322 `${this.logPrefix(
66a7748d
JB
323 connectorId
324 )} entered in transaction loop while the connector ${connectorId} is unavailable`
325 )
326 return false
0bd926c1 327 }
66a7748d 328 return true
0bd926c1
JB
329 }
330
66a7748d
JB
331 private async waitChargingStationAvailable (connectorId: number): Promise<void> {
332 let logged = false
60400e23
JB
333 while (!this.chargingStation.isChargingStationAvailable()) {
334 if (!logged) {
335 logger.info(
336 `${this.logPrefix(
66a7748d
JB
337 connectorId
338 )} transaction loop waiting for charging station to be available`
339 )
340 logged = true
60400e23 341 }
66a7748d 342 await sleep(Constants.CHARGING_STATION_ATG_AVAILABILITY_TIME)
3e888c65
JB
343 }
344 }
345
66a7748d
JB
346 private async waitConnectorAvailable (connectorId: number): Promise<void> {
347 let logged = false
60400e23
JB
348 while (!this.chargingStation.isConnectorAvailable(connectorId)) {
349 if (!logged) {
350 logger.info(
351 `${this.logPrefix(
66a7748d
JB
352 connectorId
353 )} transaction loop waiting for connector ${connectorId} to be available`
354 )
355 logged = true
60400e23 356 }
66a7748d 357 await sleep(Constants.CHARGING_STATION_ATG_AVAILABILITY_TIME)
3e888c65
JB
358 }
359 }
360
66a7748d 361 private initializeConnectorsStatus (): void {
4334db72
JB
362 if (this.chargingStation.hasEvses) {
363 for (const [evseId, evseStatus] of this.chargingStation.evses) {
364 if (evseId > 0) {
365 for (const connectorId of evseStatus.connectors.keys()) {
66a7748d 366 this.connectorsStatus.set(connectorId, this.getConnectorStatus(connectorId))
4334db72
JB
367 }
368 }
369 }
370 } else {
371 for (const connectorId of this.chargingStation.connectors.keys()) {
372 if (connectorId > 0) {
66a7748d 373 this.connectorsStatus.set(connectorId, this.getConnectorStatus(connectorId))
4334db72 374 }
4dff3039
JB
375 }
376 }
72740232
JB
377 }
378
66a7748d
JB
379 private getConnectorStatus (connectorId: number): Status {
380 const connectorStatus =
0a1dd746 381 this.chargingStation.getAutomaticTransactionGeneratorStatuses()?.[connectorId - 1] != null
66a7748d
JB
382 ? cloneObject<Status>(
383 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
0a1dd746 384 this.chargingStation.getAutomaticTransactionGeneratorStatuses()![connectorId - 1]
a82d0329 385 )
66a7748d 386 : undefined
6dde6c5f
JB
387 if (connectorStatus != null) {
388 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
389 connectorStatus.startDate = convertToDate(connectorStatus.startDate)!
390 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
391 connectorStatus.lastRunDate = convertToDate(connectorStatus.lastRunDate)!
392 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
393 connectorStatus.stopDate = convertToDate(connectorStatus.stopDate)!
394 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
395 connectorStatus.stoppedDate = convertToDate(connectorStatus.stoppedDate)!
396 if (
397 !this.started &&
398 (connectorStatus.start ||
399 this.chargingStation.getAutomaticTransactionGeneratorConfiguration()?.enable !== true)
400 ) {
401 connectorStatus.start = false
402 }
403 }
5ced7e80
JB
404 return (
405 connectorStatus ?? {
406 start: false,
407 authorizeRequests: 0,
408 acceptedAuthorizeRequests: 0,
409 rejectedAuthorizeRequests: 0,
410 startTransactionRequests: 0,
411 acceptedStartTransactionRequests: 0,
412 rejectedStartTransactionRequests: 0,
413 stopTransactionRequests: 0,
414 acceptedStopTransactionRequests: 0,
415 rejectedStopTransactionRequests: 0,
416 skippedConsecutiveTransactions: 0,
66a7748d 417 skippedTransactions: 0
5ced7e80 418 }
66a7748d 419 )
5ced7e80
JB
420 }
421
66a7748d
JB
422 private async startTransaction (
423 connectorId: number
0afed85f 424 ): Promise<StartTransactionResponse | undefined> {
66a7748d
JB
425 const measureId = 'StartTransaction with ATG'
426 const beginId = PerformanceStatistics.beginMeasure(measureId)
427 let startResponse: StartTransactionResponse | undefined
f911a4af
JB
428 if (this.chargingStation.hasIdTags()) {
429 const idTag = IdTagsCache.getInstance().getIdTag(
66a7748d 430 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
5199f9fd 431 this.chargingStation.getAutomaticTransactionGeneratorConfiguration()!.idTagDistribution!,
aaf2bf9c 432 this.chargingStation,
66a7748d
JB
433 connectorId
434 )
5cf9050d 435 const startTransactionLogMsg = `${this.logPrefix(
66a7748d
JB
436 connectorId
437 )} start transaction with an idTag '${idTag}'`
ccb1d6e9 438 if (this.getRequireAuthorize()) {
66a7748d 439 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
0a1dd746 440 ++this.connectorsStatus.get(connectorId)!.authorizeRequests
041365be 441 if (await isIdTagAuthorized(this.chargingStation, connectorId, idTag)) {
66a7748d 442 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
0a1dd746 443 ++this.connectorsStatus.get(connectorId)!.acceptedAuthorizeRequests
66a7748d 444 logger.info(startTransactionLogMsg)
5fdab605 445 // Start transaction
f7f98c68 446 startResponse = await this.chargingStation.ocppRequestService.requestHandler<
66a7748d
JB
447 StartTransactionRequest,
448 StartTransactionResponse
08f130a0 449 >(this.chargingStation, RequestCommand.START_TRANSACTION, {
ef6fa3fb 450 connectorId,
66a7748d
JB
451 idTag
452 })
453 this.handleStartTransactionResponse(connectorId, startResponse)
454 PerformanceStatistics.endMeasure(measureId, beginId)
455 return startResponse
5fdab605 456 }
66a7748d 457 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
0a1dd746 458 ++this.connectorsStatus.get(connectorId)!.rejectedAuthorizeRequests
66a7748d
JB
459 PerformanceStatistics.endMeasure(measureId, beginId)
460 return startResponse
ef6076c1 461 }
66a7748d 462 logger.info(startTransactionLogMsg)
5fdab605 463 // Start transaction
f7f98c68 464 startResponse = await this.chargingStation.ocppRequestService.requestHandler<
66a7748d
JB
465 StartTransactionRequest,
466 StartTransactionResponse
08f130a0 467 >(this.chargingStation, RequestCommand.START_TRANSACTION, {
ef6fa3fb 468 connectorId,
66a7748d
JB
469 idTag
470 })
471 this.handleStartTransactionResponse(connectorId, startResponse)
472 PerformanceStatistics.endMeasure(measureId, beginId)
473 return startResponse
6af9012e 474 }
66a7748d 475 logger.info(`${this.logPrefix(connectorId)} start transaction without an idTag`)
f7f98c68 476 startResponse = await this.chargingStation.ocppRequestService.requestHandler<
66a7748d
JB
477 StartTransactionRequest,
478 StartTransactionResponse
479 >(this.chargingStation, RequestCommand.START_TRANSACTION, { connectorId })
480 this.handleStartTransactionResponse(connectorId, startResponse)
481 PerformanceStatistics.endMeasure(measureId, beginId)
482 return startResponse
6af9012e
JB
483 }
484
66a7748d 485 private async stopTransaction (
e7aeea18 486 connectorId: number,
66a7748d 487 reason = StopTransactionReason.LOCAL
e1d9a0f4 488 ): Promise<StopTransactionResponse | undefined> {
66a7748d
JB
489 const measureId = 'StopTransaction with ATG'
490 const beginId = PerformanceStatistics.beginMeasure(measureId)
491 let stopResponse: StopTransactionResponse | undefined
6d9876e7 492 if (this.chargingStation.getConnectorStatus(connectorId)?.transactionStarted === true) {
49563992
JB
493 logger.info(
494 `${this.logPrefix(
66a7748d 495 connectorId
49563992 496 )} stop transaction with id ${this.chargingStation.getConnectorStatus(connectorId)
66a7748d
JB
497 ?.transactionId}`
498 )
499 stopResponse = await this.chargingStation.stopTransactionOnConnector(connectorId, reason)
500 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
0a1dd746 501 ++this.connectorsStatus.get(connectorId)!.stopTransactionRequests
5199f9fd 502 if (stopResponse.idTagInfo?.status === AuthorizationStatus.ACCEPTED) {
66a7748d 503 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
0a1dd746 504 ++this.connectorsStatus.get(connectorId)!.acceptedStopTransactionRequests
6d9876e7 505 } else {
66a7748d 506 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
0a1dd746 507 ++this.connectorsStatus.get(connectorId)!.rejectedStopTransactionRequests
6d9876e7 508 }
0045cef5 509 } else {
66a7748d 510 const transactionId = this.chargingStation.getConnectorStatus(connectorId)?.transactionId
ff581359 511 logger.debug(
ba7965c4 512 `${this.logPrefix(connectorId)} stopping a not started transaction${
401fa922 513 transactionId != null ? ` with id ${transactionId}` : ''
66a7748d
JB
514 }`
515 )
0045cef5 516 }
66a7748d
JB
517 PerformanceStatistics.endMeasure(measureId, beginId)
518 return stopResponse
c0560973
JB
519 }
520
66a7748d 521 private getRequireAuthorize (): boolean {
ac7f79af
JB
522 return (
523 this.chargingStation.getAutomaticTransactionGeneratorConfiguration()?.requireAuthorize ?? true
66a7748d 524 )
ccb1d6e9
JB
525 }
526
66a7748d 527 private readonly logPrefix = (connectorId?: number): string => {
9bf0ef23 528 return logPrefix(
5199f9fd 529 ` ${this.chargingStation.stationInfo?.chargingStationId} | ATG${
401fa922 530 connectorId != null ? ` on connector #${connectorId}` : ''
66a7748d
JB
531 }:`
532 )
533 }
d9ac47ef 534
66a7748d 535 private handleStartTransactionResponse (
d9ac47ef 536 connectorId: number,
66a7748d 537 startResponse: StartTransactionResponse
d9ac47ef 538 ): void {
66a7748d 539 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
0a1dd746 540 ++this.connectorsStatus.get(connectorId)!.startTransactionRequests
5199f9fd 541 if (startResponse.idTagInfo.status === AuthorizationStatus.ACCEPTED) {
66a7748d 542 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
0a1dd746 543 ++this.connectorsStatus.get(connectorId)!.acceptedStartTransactionRequests
d9ac47ef 544 } else {
66a7748d
JB
545 logger.warn(`${this.logPrefix(connectorId)} start transaction rejected`)
546 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
0a1dd746 547 ++this.connectorsStatus.get(connectorId)!.rejectedStartTransactionRequests
d9ac47ef
JB
548 }
549 }
6af9012e 550}