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