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