Commit | Line | Data |
---|---|---|
edd13439 | 1 | // Partial Copyright Jerome Benoit. 2021-2023. All Rights Reserved. |
c8eeb62b | 2 | |
01f4001e | 3 | import { AsyncResource } from 'node:async_hooks'; |
d4b944ae | 4 | |
be4c6702 JB |
5 | import { hoursToMilliseconds, secondsToMilliseconds } from 'date-fns'; |
6 | ||
4c3c0d59 | 7 | import type { ChargingStation } from './ChargingStation'; |
fba11dc6 | 8 | import { checkChargingStation } from './ChargingStationUtils'; |
4c3c0d59 | 9 | import { IdTagsCache } from './IdTagsCache'; |
268a74bb | 10 | import { BaseError } from '../exception'; |
b84bca85 | 11 | import { PerformanceStatistics } from '../performance'; |
e7aeea18 JB |
12 | import { |
13 | AuthorizationStatus, | |
976d11ec JB |
14 | type AuthorizeRequest, |
15 | type AuthorizeResponse, | |
9b4d0c70 | 16 | ConnectorStatusEnum, |
268a74bb | 17 | RequestCommand, |
976d11ec JB |
18 | type StartTransactionRequest, |
19 | type StartTransactionResponse, | |
268a74bb | 20 | type Status, |
e7aeea18 | 21 | StopTransactionReason, |
976d11ec | 22 | type StopTransactionResponse, |
268a74bb | 23 | } from '../types'; |
9bf0ef23 JB |
24 | import { |
25 | Constants, | |
26 | cloneObject, | |
27 | formatDurationMilliSeconds, | |
28 | getRandomInteger, | |
29 | isNullOrUndefined, | |
30 | logPrefix, | |
31 | logger, | |
32 | secureRandom, | |
33 | sleep, | |
34 | } from '../utils'; | |
6af9012e | 35 | |
d4b944ae JB |
36 | const moduleName = 'AutomaticTransactionGenerator'; |
37 | ||
268a74bb | 38 | export class AutomaticTransactionGenerator extends AsyncResource { |
e7aeea18 JB |
39 | private static readonly instances: Map<string, AutomaticTransactionGenerator> = new Map< |
40 | string, | |
41 | AutomaticTransactionGenerator | |
42 | >(); | |
10068088 | 43 | |
5e3cb728 | 44 | public readonly connectorsStatus: Map<number, Status>; |
265e4266 | 45 | public started: boolean; |
11353865 JB |
46 | private starting: boolean; |
47 | private stopping: boolean; | |
9e23580d | 48 | private readonly chargingStation: ChargingStation; |
6af9012e | 49 | |
ac7f79af | 50 | private constructor(chargingStation: ChargingStation) { |
d4b944ae | 51 | super(moduleName); |
aa428a31 | 52 | this.started = false; |
11353865 JB |
53 | this.starting = false; |
54 | this.stopping = false; | |
ad2f27c3 | 55 | this.chargingStation = chargingStation; |
7807ccf2 JB |
56 | this.connectorsStatus = new Map<number, Status>(); |
57 | this.initializeConnectorsStatus(); | |
6af9012e JB |
58 | } |
59 | ||
fa7bccf4 | 60 | public static getInstance( |
5edd8ba0 | 61 | chargingStation: ChargingStation, |
1895299d | 62 | ): AutomaticTransactionGenerator | undefined { |
4dff3039 | 63 | if (AutomaticTransactionGenerator.instances.has(chargingStation.stationInfo.hashId) === false) { |
e7aeea18 | 64 | AutomaticTransactionGenerator.instances.set( |
51c83d6f | 65 | chargingStation.stationInfo.hashId, |
5edd8ba0 | 66 | new AutomaticTransactionGenerator(chargingStation), |
e7aeea18 | 67 | ); |
73b9adec | 68 | } |
51c83d6f | 69 | return AutomaticTransactionGenerator.instances.get(chargingStation.stationInfo.hashId); |
73b9adec JB |
70 | } |
71 | ||
7d75bee1 | 72 | public start(): void { |
fba11dc6 | 73 | if (checkChargingStation(this.chargingStation, this.logPrefix()) === false) { |
d1c6c833 JB |
74 | return; |
75 | } | |
a5e9befc | 76 | if (this.started === true) { |
ba7965c4 | 77 | logger.warn(`${this.logPrefix()} is already started`); |
b809adf1 JB |
78 | return; |
79 | } | |
11353865 JB |
80 | if (this.starting === true) { |
81 | logger.warn(`${this.logPrefix()} is already starting`); | |
82 | return; | |
83 | } | |
84 | this.starting = true; | |
72740232 | 85 | this.startConnectors(); |
265e4266 | 86 | this.started = true; |
11353865 | 87 | this.starting = false; |
6af9012e JB |
88 | } |
89 | ||
0045cef5 | 90 | public stop(): void { |
a5e9befc | 91 | if (this.started === false) { |
ba7965c4 | 92 | logger.warn(`${this.logPrefix()} is already stopped`); |
265e4266 JB |
93 | return; |
94 | } | |
11353865 JB |
95 | if (this.stopping === true) { |
96 | logger.warn(`${this.logPrefix()} is already stopping`); | |
97 | return; | |
98 | } | |
99 | this.stopping = true; | |
72740232 | 100 | this.stopConnectors(); |
265e4266 | 101 | this.started = false; |
11353865 | 102 | this.stopping = false; |
6af9012e JB |
103 | } |
104 | ||
a5e9befc | 105 | public startConnector(connectorId: number): void { |
fba11dc6 | 106 | if (checkChargingStation(this.chargingStation, this.logPrefix(connectorId)) === false) { |
d1c6c833 JB |
107 | return; |
108 | } | |
7807ccf2 | 109 | if (this.connectorsStatus.has(connectorId) === false) { |
a03a128d | 110 | logger.error(`${this.logPrefix(connectorId)} starting on non existing connector`); |
7807ccf2 | 111 | throw new BaseError(`Connector ${connectorId} does not exist`); |
a5e9befc JB |
112 | } |
113 | if (this.connectorsStatus.get(connectorId)?.start === false) { | |
d4b944ae | 114 | this.runInAsyncScope( |
e6159ce8 JB |
115 | this.internalStartConnector.bind(this) as ( |
116 | this: AutomaticTransactionGenerator, | |
e843aa40 | 117 | ...args: unknown[] |
64818750 | 118 | ) => Promise<void>, |
d4b944ae | 119 | this, |
5edd8ba0 | 120 | connectorId, |
59b6ed8d | 121 | ).catch(Constants.EMPTY_FUNCTION); |
ecb3869d | 122 | } else if (this.connectorsStatus.get(connectorId)?.start === true) { |
ba7965c4 | 123 | logger.warn(`${this.logPrefix(connectorId)} is already started on connector`); |
a5e9befc JB |
124 | } |
125 | } | |
126 | ||
127 | public stopConnector(connectorId: number): void { | |
7807ccf2 | 128 | if (this.connectorsStatus.has(connectorId) === false) { |
a03a128d | 129 | logger.error(`${this.logPrefix(connectorId)} stopping on non existing connector`); |
7807ccf2 | 130 | throw new BaseError(`Connector ${connectorId} does not exist`); |
ba7965c4 JB |
131 | } |
132 | if (this.connectorsStatus.get(connectorId)?.start === true) { | |
e1d9a0f4 | 133 | this.connectorsStatus.get(connectorId)!.start = false; |
ba7965c4 JB |
134 | } else if (this.connectorsStatus.get(connectorId)?.start === false) { |
135 | logger.warn(`${this.logPrefix(connectorId)} is already stopped on connector`); | |
136 | } | |
a5e9befc JB |
137 | } |
138 | ||
72740232 | 139 | private startConnectors(): void { |
e7aeea18 JB |
140 | if ( |
141 | this.connectorsStatus?.size > 0 && | |
142 | this.connectorsStatus.size !== this.chargingStation.getNumberOfConnectors() | |
143 | ) { | |
54544ef1 | 144 | this.connectorsStatus.clear(); |
7807ccf2 | 145 | this.initializeConnectorsStatus(); |
54544ef1 | 146 | } |
4334db72 JB |
147 | if (this.chargingStation.hasEvses) { |
148 | for (const [evseId, evseStatus] of this.chargingStation.evses) { | |
149 | if (evseId > 0) { | |
150 | for (const connectorId of evseStatus.connectors.keys()) { | |
151 | this.startConnector(connectorId); | |
152 | } | |
153 | } | |
154 | } | |
155 | } else { | |
156 | for (const connectorId of this.chargingStation.connectors.keys()) { | |
157 | if (connectorId > 0) { | |
158 | this.startConnector(connectorId); | |
159 | } | |
72740232 JB |
160 | } |
161 | } | |
162 | } | |
163 | ||
164 | private stopConnectors(): void { | |
4334db72 JB |
165 | if (this.chargingStation.hasEvses) { |
166 | for (const [evseId, evseStatus] of this.chargingStation.evses) { | |
167 | if (evseId > 0) { | |
168 | for (const connectorId of evseStatus.connectors.keys()) { | |
169 | this.stopConnector(connectorId); | |
170 | } | |
171 | } | |
172 | } | |
173 | } else { | |
174 | for (const connectorId of this.chargingStation.connectors.keys()) { | |
175 | if (connectorId > 0) { | |
176 | this.stopConnector(connectorId); | |
177 | } | |
72740232 JB |
178 | } |
179 | } | |
180 | } | |
181 | ||
83a3286a | 182 | private async internalStartConnector(connectorId: number): Promise<void> { |
083fb002 | 183 | this.setStartConnectorStatus(connectorId); |
e7aeea18 | 184 | logger.info( |
44eb6026 | 185 | `${this.logPrefix( |
5edd8ba0 | 186 | connectorId, |
9bf0ef23 | 187 | )} started on connector and will run for ${formatDurationMilliSeconds( |
e1d9a0f4 JB |
188 | this.connectorsStatus.get(connectorId)!.stopDate!.getTime() - |
189 | this.connectorsStatus.get(connectorId)!.startDate!.getTime(), | |
5edd8ba0 | 190 | )}`, |
e7aeea18 | 191 | ); |
1895299d | 192 | while (this.connectorsStatus.get(connectorId)?.start === true) { |
e1d9a0f4 | 193 | if (new Date() > this.connectorsStatus.get(connectorId)!.stopDate!) { |
9664ec50 | 194 | this.stopConnector(connectorId); |
17991e8c JB |
195 | break; |
196 | } | |
f7c2994d | 197 | if (this.chargingStation.inAcceptedState() === false) { |
e7aeea18 | 198 | logger.error( |
44eb6026 | 199 | `${this.logPrefix( |
5edd8ba0 JB |
200 | connectorId, |
201 | )} entered in transaction loop while the charging station is not in accepted state`, | |
e7aeea18 | 202 | ); |
9664ec50 | 203 | this.stopConnector(connectorId); |
17991e8c JB |
204 | break; |
205 | } | |
1789ba2c | 206 | if (this.chargingStation.isChargingStationAvailable() === false) { |
e7aeea18 | 207 | logger.info( |
44eb6026 | 208 | `${this.logPrefix( |
5edd8ba0 JB |
209 | connectorId, |
210 | )} entered in transaction loop while the charging station is unavailable`, | |
e7aeea18 | 211 | ); |
9664ec50 | 212 | this.stopConnector(connectorId); |
ab5f4b03 JB |
213 | break; |
214 | } | |
1789ba2c | 215 | if (this.chargingStation.isConnectorAvailable(connectorId) === false) { |
e7aeea18 JB |
216 | logger.info( |
217 | `${this.logPrefix( | |
5edd8ba0 JB |
218 | connectorId, |
219 | )} entered in transaction loop while the connector ${connectorId} is unavailable`, | |
e7aeea18 | 220 | ); |
9c7195b2 | 221 | this.stopConnector(connectorId); |
17991e8c JB |
222 | break; |
223 | } | |
9b4d0c70 JB |
224 | if ( |
225 | this.chargingStation.getConnectorStatus(connectorId)?.status === | |
226 | ConnectorStatusEnum.Unavailable | |
227 | ) { | |
228 | logger.info( | |
229 | `${this.logPrefix( | |
5edd8ba0 JB |
230 | connectorId, |
231 | )} entered in transaction loop while the connector ${connectorId} status is unavailable`, | |
9b4d0c70 JB |
232 | ); |
233 | this.stopConnector(connectorId); | |
234 | break; | |
235 | } | |
c0560973 | 236 | if (!this.chargingStation?.ocppRequestService) { |
e7aeea18 JB |
237 | logger.info( |
238 | `${this.logPrefix( | |
5edd8ba0 JB |
239 | connectorId, |
240 | )} transaction loop waiting for charging station service to be initialized`, | |
e7aeea18 | 241 | ); |
c0560973 | 242 | do { |
9bf0ef23 | 243 | await sleep(Constants.CHARGING_STATION_ATG_INITIALIZATION_TIME); |
c0560973 JB |
244 | } while (!this.chargingStation?.ocppRequestService); |
245 | } | |
be4c6702 | 246 | const wait = secondsToMilliseconds( |
9bf0ef23 | 247 | getRandomInteger( |
ac7f79af | 248 | this.chargingStation.getAutomaticTransactionGeneratorConfiguration() |
86b46b49 | 249 | .maxDelayBetweenTwoTransactions, |
ac7f79af | 250 | this.chargingStation.getAutomaticTransactionGeneratorConfiguration() |
5edd8ba0 | 251 | .minDelayBetweenTwoTransactions, |
be4c6702 JB |
252 | ), |
253 | ); | |
9bf0ef23 JB |
254 | logger.info(`${this.logPrefix(connectorId)} waiting for ${formatDurationMilliSeconds(wait)}`); |
255 | await sleep(wait); | |
256 | const start = secureRandom(); | |
ac7f79af JB |
257 | if ( |
258 | start < | |
259 | this.chargingStation.getAutomaticTransactionGeneratorConfiguration().probabilityOfStart | |
260 | ) { | |
e1d9a0f4 | 261 | this.connectorsStatus.get(connectorId)!.skippedConsecutiveTransactions = 0; |
6af9012e | 262 | // Start transaction |
aef1b33a | 263 | const startResponse = await this.startTransaction(connectorId); |
0afed85f | 264 | if (startResponse?.idTagInfo?.status === AuthorizationStatus.ACCEPTED) { |
6af9012e | 265 | // Wait until end of transaction |
be4c6702 | 266 | const waitTrxEnd = secondsToMilliseconds( |
9bf0ef23 | 267 | getRandomInteger( |
86b46b49 | 268 | this.chargingStation.getAutomaticTransactionGeneratorConfiguration().maxDuration, |
5edd8ba0 | 269 | this.chargingStation.getAutomaticTransactionGeneratorConfiguration().minDuration, |
be4c6702 JB |
270 | ), |
271 | ); | |
e7aeea18 | 272 | logger.info( |
54ebb82c | 273 | `${this.logPrefix(connectorId)} transaction started with id ${this.chargingStation |
44eb6026 | 274 | .getConnectorStatus(connectorId) |
9bf0ef23 | 275 | ?.transactionId?.toString()} and will stop in ${formatDurationMilliSeconds( |
5edd8ba0 JB |
276 | waitTrxEnd, |
277 | )}`, | |
e7aeea18 | 278 | ); |
9bf0ef23 | 279 | await sleep(waitTrxEnd); |
6af9012e | 280 | // Stop transaction |
e7aeea18 | 281 | logger.info( |
54ebb82c | 282 | `${this.logPrefix(connectorId)} stop transaction with id ${this.chargingStation |
44eb6026 | 283 | .getConnectorStatus(connectorId) |
5edd8ba0 | 284 | ?.transactionId?.toString()}`, |
e7aeea18 | 285 | ); |
85d20667 | 286 | await this.stopTransaction(connectorId); |
6af9012e JB |
287 | } |
288 | } else { | |
e1d9a0f4 JB |
289 | ++this.connectorsStatus.get(connectorId)!.skippedConsecutiveTransactions!; |
290 | ++this.connectorsStatus.get(connectorId)!.skippedTransactions!; | |
e7aeea18 | 291 | logger.info( |
44eb6026 JB |
292 | `${this.logPrefix(connectorId)} skipped consecutively ${this.connectorsStatus |
293 | .get(connectorId) | |
1895299d | 294 | ?.skippedConsecutiveTransactions?.toString()}/${this.connectorsStatus |
44eb6026 | 295 | .get(connectorId) |
5edd8ba0 | 296 | ?.skippedTransactions?.toString()} transaction(s)`, |
e7aeea18 | 297 | ); |
6af9012e | 298 | } |
e1d9a0f4 | 299 | this.connectorsStatus.get(connectorId)!.lastRunDate = new Date(); |
7d75bee1 | 300 | } |
e1d9a0f4 | 301 | this.connectorsStatus.get(connectorId)!.stoppedDate = new Date(); |
e7aeea18 | 302 | logger.info( |
44eb6026 | 303 | `${this.logPrefix( |
5edd8ba0 | 304 | connectorId, |
9bf0ef23 | 305 | )} stopped on connector and lasted for ${formatDurationMilliSeconds( |
e1d9a0f4 JB |
306 | this.connectorsStatus.get(connectorId)!.stoppedDate!.getTime() - |
307 | this.connectorsStatus.get(connectorId)!.startDate!.getTime(), | |
5edd8ba0 | 308 | )}`, |
e7aeea18 JB |
309 | ); |
310 | logger.debug( | |
be9ee554 | 311 | `${this.logPrefix(connectorId)} connector status: %j`, |
5edd8ba0 | 312 | this.connectorsStatus.get(connectorId), |
e7aeea18 | 313 | ); |
6af9012e JB |
314 | } |
315 | ||
083fb002 | 316 | private setStartConnectorStatus(connectorId: number): void { |
e1d9a0f4 | 317 | this.connectorsStatus.get(connectorId)!.skippedConsecutiveTransactions = 0; |
e7aeea18 | 318 | const previousRunDuration = |
72092cfc JB |
319 | this.connectorsStatus.get(connectorId)?.startDate && |
320 | this.connectorsStatus.get(connectorId)?.lastRunDate | |
e1d9a0f4 JB |
321 | ? this.connectorsStatus.get(connectorId)!.lastRunDate!.getTime() - |
322 | this.connectorsStatus.get(connectorId)!.startDate!.getTime() | |
e7aeea18 | 323 | : 0; |
e1d9a0f4 JB |
324 | this.connectorsStatus.get(connectorId)!.startDate = new Date(); |
325 | this.connectorsStatus.get(connectorId)!.stopDate = new Date( | |
326 | this.connectorsStatus.get(connectorId)!.startDate!.getTime() + | |
be4c6702 JB |
327 | hoursToMilliseconds( |
328 | this.chargingStation.getAutomaticTransactionGeneratorConfiguration().stopAfterHours, | |
329 | ) - | |
5edd8ba0 | 330 | previousRunDuration, |
e7aeea18 | 331 | ); |
e1d9a0f4 | 332 | this.connectorsStatus.get(connectorId)!.start = true; |
4dff3039 JB |
333 | } |
334 | ||
7807ccf2 | 335 | private initializeConnectorsStatus(): void { |
4334db72 JB |
336 | if (this.chargingStation.hasEvses) { |
337 | for (const [evseId, evseStatus] of this.chargingStation.evses) { | |
338 | if (evseId > 0) { | |
339 | for (const connectorId of evseStatus.connectors.keys()) { | |
5ced7e80 | 340 | this.connectorsStatus.set(connectorId, this.getConnectorStatus(connectorId)); |
4334db72 JB |
341 | } |
342 | } | |
343 | } | |
344 | } else { | |
345 | for (const connectorId of this.chargingStation.connectors.keys()) { | |
346 | if (connectorId > 0) { | |
5ced7e80 | 347 | this.connectorsStatus.set(connectorId, this.getConnectorStatus(connectorId)); |
4334db72 | 348 | } |
4dff3039 JB |
349 | } |
350 | } | |
72740232 JB |
351 | } |
352 | ||
5ced7e80 | 353 | private getConnectorStatus(connectorId: number): Status { |
bdc9dc79 JB |
354 | const connectorStatus = this.chargingStation.getAutomaticTransactionGeneratorStatuses() |
355 | ? cloneObject(this.chargingStation.getAutomaticTransactionGeneratorStatuses()!)[connectorId] | |
356 | : undefined; | |
5ced7e80 JB |
357 | delete connectorStatus?.startDate; |
358 | delete connectorStatus?.lastRunDate; | |
359 | delete connectorStatus?.stopDate; | |
360 | delete connectorStatus?.stoppedDate; | |
361 | return ( | |
362 | connectorStatus ?? { | |
363 | start: false, | |
364 | authorizeRequests: 0, | |
365 | acceptedAuthorizeRequests: 0, | |
366 | rejectedAuthorizeRequests: 0, | |
367 | startTransactionRequests: 0, | |
368 | acceptedStartTransactionRequests: 0, | |
369 | rejectedStartTransactionRequests: 0, | |
370 | stopTransactionRequests: 0, | |
371 | acceptedStopTransactionRequests: 0, | |
372 | rejectedStopTransactionRequests: 0, | |
373 | skippedConsecutiveTransactions: 0, | |
374 | skippedTransactions: 0, | |
375 | } | |
376 | ); | |
377 | } | |
378 | ||
e7aeea18 | 379 | private async startTransaction( |
5edd8ba0 | 380 | connectorId: number, |
0afed85f | 381 | ): Promise<StartTransactionResponse | undefined> { |
aef1b33a JB |
382 | const measureId = 'StartTransaction with ATG'; |
383 | const beginId = PerformanceStatistics.beginMeasure(measureId); | |
e1d9a0f4 | 384 | let startResponse: StartTransactionResponse | undefined; |
f911a4af JB |
385 | if (this.chargingStation.hasIdTags()) { |
386 | const idTag = IdTagsCache.getInstance().getIdTag( | |
e1d9a0f4 | 387 | this.chargingStation.getAutomaticTransactionGeneratorConfiguration().idTagDistribution!, |
aaf2bf9c | 388 | this.chargingStation, |
5edd8ba0 | 389 | connectorId, |
aaf2bf9c | 390 | ); |
5cf9050d | 391 | const startTransactionLogMsg = `${this.logPrefix( |
5edd8ba0 | 392 | connectorId, |
ba7965c4 | 393 | )} start transaction with an idTag '${idTag}'`; |
ccb1d6e9 | 394 | if (this.getRequireAuthorize()) { |
e1d9a0f4 | 395 | this.chargingStation.getConnectorStatus(connectorId)!.authorizeIdTag = idTag; |
f4bf2abd | 396 | // Authorize idTag |
2e3d65ae | 397 | const authorizeResponse: AuthorizeResponse = |
f7f98c68 | 398 | await this.chargingStation.ocppRequestService.requestHandler< |
ef6fa3fb JB |
399 | AuthorizeRequest, |
400 | AuthorizeResponse | |
08f130a0 | 401 | >(this.chargingStation, RequestCommand.AUTHORIZE, { |
ef6fa3fb JB |
402 | idTag, |
403 | }); | |
e1d9a0f4 | 404 | ++this.connectorsStatus.get(connectorId)!.authorizeRequests!; |
5fdab605 | 405 | if (authorizeResponse?.idTagInfo?.status === AuthorizationStatus.ACCEPTED) { |
e1d9a0f4 | 406 | ++this.connectorsStatus.get(connectorId)!.acceptedAuthorizeRequests!; |
5cf9050d | 407 | logger.info(startTransactionLogMsg); |
5fdab605 | 408 | // Start transaction |
f7f98c68 | 409 | startResponse = await this.chargingStation.ocppRequestService.requestHandler< |
ef6fa3fb JB |
410 | StartTransactionRequest, |
411 | StartTransactionResponse | |
08f130a0 | 412 | >(this.chargingStation, RequestCommand.START_TRANSACTION, { |
ef6fa3fb JB |
413 | connectorId, |
414 | idTag, | |
415 | }); | |
d9ac47ef | 416 | this.handleStartTransactionResponse(connectorId, startResponse); |
aef1b33a JB |
417 | PerformanceStatistics.endMeasure(measureId, beginId); |
418 | return startResponse; | |
5fdab605 | 419 | } |
e1d9a0f4 | 420 | ++this.connectorsStatus.get(connectorId)!.rejectedAuthorizeRequests!; |
aef1b33a | 421 | PerformanceStatistics.endMeasure(measureId, beginId); |
0afed85f | 422 | return startResponse; |
ef6076c1 | 423 | } |
5cf9050d | 424 | logger.info(startTransactionLogMsg); |
5fdab605 | 425 | // Start transaction |
f7f98c68 | 426 | startResponse = await this.chargingStation.ocppRequestService.requestHandler< |
ef6fa3fb JB |
427 | StartTransactionRequest, |
428 | StartTransactionResponse | |
08f130a0 | 429 | >(this.chargingStation, RequestCommand.START_TRANSACTION, { |
ef6fa3fb JB |
430 | connectorId, |
431 | idTag, | |
432 | }); | |
d9ac47ef | 433 | this.handleStartTransactionResponse(connectorId, startResponse); |
aef1b33a JB |
434 | PerformanceStatistics.endMeasure(measureId, beginId); |
435 | return startResponse; | |
6af9012e | 436 | } |
5cf9050d | 437 | logger.info(`${this.logPrefix(connectorId)} start transaction without an idTag`); |
f7f98c68 | 438 | startResponse = await this.chargingStation.ocppRequestService.requestHandler< |
ef6fa3fb JB |
439 | StartTransactionRequest, |
440 | StartTransactionResponse | |
08f130a0 | 441 | >(this.chargingStation, RequestCommand.START_TRANSACTION, { connectorId }); |
431b6bd5 | 442 | this.handleStartTransactionResponse(connectorId, startResponse); |
aef1b33a JB |
443 | PerformanceStatistics.endMeasure(measureId, beginId); |
444 | return startResponse; | |
6af9012e JB |
445 | } |
446 | ||
e7aeea18 JB |
447 | private async stopTransaction( |
448 | connectorId: number, | |
5edd8ba0 | 449 | reason: StopTransactionReason = StopTransactionReason.LOCAL, |
e1d9a0f4 | 450 | ): Promise<StopTransactionResponse | undefined> { |
aef1b33a JB |
451 | const measureId = 'StopTransaction with ATG'; |
452 | const beginId = PerformanceStatistics.beginMeasure(measureId); | |
e1d9a0f4 | 453 | let stopResponse: StopTransactionResponse | undefined; |
6d9876e7 | 454 | if (this.chargingStation.getConnectorStatus(connectorId)?.transactionStarted === true) { |
5e3cb728 | 455 | stopResponse = await this.chargingStation.stopTransactionOnConnector(connectorId, reason); |
e1d9a0f4 | 456 | ++this.connectorsStatus.get(connectorId)!.stopTransactionRequests!; |
0afed85f | 457 | if (stopResponse?.idTagInfo?.status === AuthorizationStatus.ACCEPTED) { |
e1d9a0f4 | 458 | ++this.connectorsStatus.get(connectorId)!.acceptedStopTransactionRequests!; |
6d9876e7 | 459 | } else { |
e1d9a0f4 | 460 | ++this.connectorsStatus.get(connectorId)!.rejectedStopTransactionRequests!; |
6d9876e7 | 461 | } |
0045cef5 | 462 | } else { |
1895299d | 463 | const transactionId = this.chargingStation.getConnectorStatus(connectorId)?.transactionId; |
e7aeea18 | 464 | logger.warn( |
ba7965c4 | 465 | `${this.logPrefix(connectorId)} stopping a not started transaction${ |
9bf0ef23 | 466 | !isNullOrUndefined(transactionId) ? ` with id ${transactionId?.toString()}` : '' |
5edd8ba0 | 467 | }`, |
e7aeea18 | 468 | ); |
0045cef5 | 469 | } |
aef1b33a JB |
470 | PerformanceStatistics.endMeasure(measureId, beginId); |
471 | return stopResponse; | |
c0560973 JB |
472 | } |
473 | ||
ccb1d6e9 | 474 | private getRequireAuthorize(): boolean { |
ac7f79af JB |
475 | return ( |
476 | this.chargingStation.getAutomaticTransactionGeneratorConfiguration()?.requireAuthorize ?? true | |
477 | ); | |
ccb1d6e9 JB |
478 | } |
479 | ||
8b7072dc | 480 | private logPrefix = (connectorId?: number): string => { |
9bf0ef23 | 481 | return logPrefix( |
6cd85def | 482 | ` ${this.chargingStation.stationInfo.chargingStationId} | ATG${ |
e1d9a0f4 | 483 | !isNullOrUndefined(connectorId) ? ` on connector #${connectorId!.toString()}` : '' |
5edd8ba0 | 484 | }:`, |
6cd85def | 485 | ); |
8b7072dc | 486 | }; |
d9ac47ef JB |
487 | |
488 | private handleStartTransactionResponse( | |
489 | connectorId: number, | |
5edd8ba0 | 490 | startResponse: StartTransactionResponse, |
d9ac47ef | 491 | ): void { |
e1d9a0f4 | 492 | ++this.connectorsStatus.get(connectorId)!.startTransactionRequests!; |
d9ac47ef | 493 | if (startResponse?.idTagInfo?.status === AuthorizationStatus.ACCEPTED) { |
e1d9a0f4 | 494 | ++this.connectorsStatus.get(connectorId)!.acceptedStartTransactionRequests!; |
d9ac47ef | 495 | } else { |
44eb6026 | 496 | logger.warn(`${this.logPrefix(connectorId)} start transaction rejected`); |
e1d9a0f4 | 497 | ++this.connectorsStatus.get(connectorId)!.rejectedStartTransactionRequests!; |
d9ac47ef JB |
498 | } |
499 | } | |
6af9012e | 500 | } |