feat: add `deleteChargingStations` SRPC command to UI Services
[e-mobility-charging-stations-simulator.git] / src / charging-station / AutomaticTransactionGenerator.ts
CommitLineData
a19b897d 1// Partial Copyright Jerome Benoit. 2021-2024. All Rights Reserved.
c8eeb62b 2
66a7748d 3import { hoursToMilliseconds, secondsToMilliseconds } from 'date-fns'
be4c6702 4
66a7748d
JB
5import type { ChargingStation } from './ChargingStation.js'
6import { checkChargingStation } from './Helpers.js'
7import { IdTagsCache } from './IdTagsCache.js'
8import { isIdTagAuthorized } from './ocpp/index.js'
9import { BaseError } from '../exception/index.js'
10import { PerformanceStatistics } from '../performance/index.js'
e7aeea18
JB
11import {
12 AuthorizationStatus,
268a74bb 13 RequestCommand,
976d11ec
JB
14 type StartTransactionRequest,
15 type StartTransactionResponse,
268a74bb 16 type Status,
e7aeea18 17 StopTransactionReason,
66a7748d
JB
18 type StopTransactionResponse
19} from '../types/index.js'
9bf0ef23
JB
20import {
21 Constants,
40615072 22 clone,
6dde6c5f 23 convertToDate,
9bf0ef23
JB
24 formatDurationMilliSeconds,
25 getRandomInteger,
5dc7c990 26 isValidDate,
9bf0ef23
JB
27 logPrefix,
28 logger,
29 secureRandom,
66a7748d
JB
30 sleep
31} from '../utils/index.js'
6af9012e 32
d1ff8599 33export class AutomaticTransactionGenerator {
e7aeea18 34 private static readonly instances: Map<string, AutomaticTransactionGenerator> = new Map<
66a7748d
JB
35 string,
36 AutomaticTransactionGenerator
37 >()
10068088 38
66a7748d
JB
39 public readonly connectorsStatus: Map<number, Status>
40 public started: boolean
41 private starting: boolean
42 private stopping: boolean
43 private readonly chargingStation: ChargingStation
6af9012e 44
66a7748d
JB
45 private constructor (chargingStation: ChargingStation) {
46 this.started = false
47 this.starting = false
48 this.stopping = false
49 this.chargingStation = chargingStation
50 this.connectorsStatus = new Map<number, Status>()
51 this.initializeConnectorsStatus()
6af9012e
JB
52 }
53
66a7748d
JB
54 public static getInstance (
55 chargingStation: ChargingStation
1895299d 56 ): AutomaticTransactionGenerator | undefined {
5199f9fd
JB
57 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
58 if (!AutomaticTransactionGenerator.instances.has(chargingStation.stationInfo!.hashId)) {
e7aeea18 59 AutomaticTransactionGenerator.instances.set(
5199f9fd
JB
60 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
61 chargingStation.stationInfo!.hashId,
66a7748d
JB
62 new AutomaticTransactionGenerator(chargingStation)
63 )
73b9adec 64 }
5199f9fd
JB
65 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
66 return AutomaticTransactionGenerator.instances.get(chargingStation.stationInfo!.hashId)
73b9adec
JB
67 }
68
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}