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