Commit | Line | Data |
---|---|---|
edd13439 | 1 | // Partial Copyright Jerome Benoit. 2021-2023. All Rights Reserved. |
c8eeb62b | 2 | |
130783a7 | 3 | import type { JSONSchemaType } from 'ajv'; |
ef9e3b33 | 4 | import { |
f1e3871b | 5 | type Interval, |
ef9e3b33 JB |
6 | addSeconds, |
7 | areIntervalsOverlapping, | |
8 | differenceInSeconds, | |
9 | isAfter, | |
10 | isBefore, | |
11 | isWithinInterval, | |
12 | } from 'date-fns'; | |
130783a7 | 13 | |
366f75f6 | 14 | import { OCPP16Constants } from './OCPP16Constants'; |
90aceaf6 JB |
15 | import { |
16 | type ChargingStation, | |
17 | hasFeatureProfile, | |
18 | hasReservationExpired, | |
19 | } from '../../../charging-station'; | |
e7aeea18 | 20 | import { |
d19b10a8 | 21 | type GenericResponse, |
268a74bb | 22 | type JsonType, |
d19b10a8 | 23 | OCPP16AuthorizationStatus, |
366f75f6 JB |
24 | OCPP16AvailabilityType, |
25 | type OCPP16ChangeAvailabilityResponse, | |
26 | OCPP16ChargePointStatus, | |
268a74bb | 27 | type OCPP16ChargingProfile, |
ef9e3b33 | 28 | type OCPP16ChargingSchedule, |
41f3983a | 29 | type OCPP16ClearChargingProfileRequest, |
268a74bb | 30 | type OCPP16IncomingRequestCommand, |
27782dbc | 31 | type OCPP16MeterValue, |
41f3983a JB |
32 | OCPP16MeterValueContext, |
33 | OCPP16MeterValueUnit, | |
370ae4ee | 34 | OCPP16RequestCommand, |
268a74bb | 35 | OCPP16StandardParametersKey, |
d19b10a8 | 36 | OCPP16StopTransactionReason, |
268a74bb JB |
37 | type OCPP16SupportedFeatureProfiles, |
38 | OCPPVersion, | |
268a74bb | 39 | } from '../../../types'; |
41f3983a | 40 | import { isNotEmptyArray, isNullOrUndefined, logger, roundTo } from '../../../utils'; |
4c3c0d59 | 41 | import { OCPPServiceUtils } from '../OCPPServiceUtils'; |
6ed92bc1 | 42 | |
7bc31f9c | 43 | export class OCPP16ServiceUtils extends OCPPServiceUtils { |
370ae4ee JB |
44 | public static checkFeatureProfile( |
45 | chargingStation: ChargingStation, | |
46 | featureProfile: OCPP16SupportedFeatureProfiles, | |
5edd8ba0 | 47 | command: OCPP16RequestCommand | OCPP16IncomingRequestCommand, |
370ae4ee | 48 | ): boolean { |
d8093be1 | 49 | if (!hasFeatureProfile(chargingStation, featureProfile)) { |
370ae4ee JB |
50 | logger.warn( |
51 | `${chargingStation.logPrefix()} Trying to '${command}' without '${featureProfile}' feature enabled in ${ | |
52 | OCPP16StandardParametersKey.SupportedFeatureProfiles | |
5edd8ba0 | 53 | } in configuration`, |
370ae4ee JB |
54 | ); |
55 | return false; | |
56 | } | |
57 | return true; | |
58 | } | |
59 | ||
e7aeea18 JB |
60 | public static buildTransactionBeginMeterValue( |
61 | chargingStation: ChargingStation, | |
62 | connectorId: number, | |
5edd8ba0 | 63 | meterStart: number, |
e7aeea18 | 64 | ): OCPP16MeterValue { |
fd0c36fa | 65 | const meterValue: OCPP16MeterValue = { |
c38f0ced | 66 | timestamp: new Date(), |
fd0c36fa JB |
67 | sampledValue: [], |
68 | }; | |
9ccca265 | 69 | // Energy.Active.Import.Register measurand (default) |
ed3d2808 | 70 | const sampledValueTemplate = OCPP16ServiceUtils.getSampledValueTemplate( |
492cf6ab | 71 | chargingStation, |
5edd8ba0 | 72 | connectorId, |
492cf6ab | 73 | ); |
41f3983a JB |
74 | const unitDivider = |
75 | sampledValueTemplate?.unit === OCPP16MeterValueUnit.KILO_WATT_HOUR ? 1000 : 1; | |
e7aeea18 JB |
76 | meterValue.sampledValue.push( |
77 | OCPP16ServiceUtils.buildSampledValue( | |
e1d9a0f4 | 78 | sampledValueTemplate!, |
9bf0ef23 | 79 | roundTo((meterStart ?? 0) / unitDivider, 4), |
41f3983a | 80 | OCPP16MeterValueContext.TRANSACTION_BEGIN, |
5edd8ba0 | 81 | ), |
e7aeea18 | 82 | ); |
fd0c36fa JB |
83 | return meterValue; |
84 | } | |
85 | ||
e7aeea18 JB |
86 | public static buildTransactionDataMeterValues( |
87 | transactionBeginMeterValue: OCPP16MeterValue, | |
5edd8ba0 | 88 | transactionEndMeterValue: OCPP16MeterValue, |
e7aeea18 | 89 | ): OCPP16MeterValue[] { |
fd0c36fa JB |
90 | const meterValues: OCPP16MeterValue[] = []; |
91 | meterValues.push(transactionBeginMeterValue); | |
92 | meterValues.push(transactionEndMeterValue); | |
93 | return meterValues; | |
94 | } | |
7bc31f9c | 95 | |
d19b10a8 JB |
96 | public static remoteStopTransaction = async ( |
97 | chargingStation: ChargingStation, | |
98 | connectorId: number, | |
99 | ): Promise<GenericResponse> => { | |
100 | await OCPP16ServiceUtils.sendAndSetConnectorStatus( | |
101 | chargingStation, | |
102 | connectorId, | |
103 | OCPP16ChargePointStatus.Finishing, | |
104 | ); | |
105 | const stopResponse = await chargingStation.stopTransactionOnConnector( | |
106 | connectorId, | |
107 | OCPP16StopTransactionReason.REMOTE, | |
108 | ); | |
109 | if (stopResponse.idTagInfo?.status === OCPP16AuthorizationStatus.ACCEPTED) { | |
110 | return OCPP16Constants.OCPP_RESPONSE_ACCEPTED; | |
111 | } | |
112 | return OCPP16Constants.OCPP_RESPONSE_REJECTED; | |
113 | }; | |
114 | ||
366f75f6 JB |
115 | public static changeAvailability = async ( |
116 | chargingStation: ChargingStation, | |
225e32b0 | 117 | connectorIds: number[], |
366f75f6 JB |
118 | chargePointStatus: OCPP16ChargePointStatus, |
119 | availabilityType: OCPP16AvailabilityType, | |
120 | ): Promise<OCPP16ChangeAvailabilityResponse> => { | |
225e32b0 JB |
121 | const responses: OCPP16ChangeAvailabilityResponse[] = []; |
122 | for (const connectorId of connectorIds) { | |
123 | let response: OCPP16ChangeAvailabilityResponse = | |
124 | OCPP16Constants.OCPP_AVAILABILITY_RESPONSE_ACCEPTED; | |
125 | const connectorStatus = chargingStation.getConnectorStatus(connectorId)!; | |
126 | if (connectorStatus?.transactionStarted === true) { | |
127 | response = OCPP16Constants.OCPP_AVAILABILITY_RESPONSE_SCHEDULED; | |
128 | } | |
129 | connectorStatus.availability = availabilityType; | |
130 | if (response === OCPP16Constants.OCPP_AVAILABILITY_RESPONSE_ACCEPTED) { | |
131 | await OCPP16ServiceUtils.sendAndSetConnectorStatus( | |
132 | chargingStation, | |
133 | connectorId, | |
134 | chargePointStatus, | |
135 | ); | |
136 | } | |
137 | responses.push(response); | |
366f75f6 | 138 | } |
3b0ed034 | 139 | if (responses.includes(OCPP16Constants.OCPP_AVAILABILITY_RESPONSE_SCHEDULED)) { |
225e32b0 | 140 | return OCPP16Constants.OCPP_AVAILABILITY_RESPONSE_SCHEDULED; |
366f75f6 | 141 | } |
225e32b0 | 142 | return OCPP16Constants.OCPP_AVAILABILITY_RESPONSE_ACCEPTED; |
366f75f6 JB |
143 | }; |
144 | ||
ed3d2808 JB |
145 | public static setChargingProfile( |
146 | chargingStation: ChargingStation, | |
147 | connectorId: number, | |
5edd8ba0 | 148 | cp: OCPP16ChargingProfile, |
ed3d2808 | 149 | ): void { |
9bf0ef23 | 150 | if (isNullOrUndefined(chargingStation.getConnectorStatus(connectorId)?.chargingProfiles)) { |
ed3d2808 | 151 | logger.error( |
5edd8ba0 | 152 | `${chargingStation.logPrefix()} Trying to set a charging profile on connector id ${connectorId} with an uninitialized charging profiles array attribute, applying deferred initialization`, |
ed3d2808 | 153 | ); |
e1d9a0f4 | 154 | chargingStation.getConnectorStatus(connectorId)!.chargingProfiles = []; |
ed3d2808 | 155 | } |
72092cfc JB |
156 | if ( |
157 | Array.isArray(chargingStation.getConnectorStatus(connectorId)?.chargingProfiles) === false | |
158 | ) { | |
ed3d2808 | 159 | logger.error( |
bbb55ee4 | 160 | `${chargingStation.logPrefix()} Trying to set a charging profile on connector id ${connectorId} with an improper attribute type for the charging profiles array, applying proper type deferred initialization`, |
ed3d2808 | 161 | ); |
e1d9a0f4 | 162 | chargingStation.getConnectorStatus(connectorId)!.chargingProfiles = []; |
ed3d2808 JB |
163 | } |
164 | let cpReplaced = false; | |
9bf0ef23 | 165 | if (isNotEmptyArray(chargingStation.getConnectorStatus(connectorId)?.chargingProfiles)) { |
ed3d2808 JB |
166 | chargingStation |
167 | .getConnectorStatus(connectorId) | |
72092cfc | 168 | ?.chargingProfiles?.forEach((chargingProfile: OCPP16ChargingProfile, index: number) => { |
ed3d2808 JB |
169 | if ( |
170 | chargingProfile.chargingProfileId === cp.chargingProfileId || | |
171 | (chargingProfile.stackLevel === cp.stackLevel && | |
172 | chargingProfile.chargingProfilePurpose === cp.chargingProfilePurpose) | |
173 | ) { | |
e1d9a0f4 | 174 | chargingStation.getConnectorStatus(connectorId)!.chargingProfiles![index] = cp; |
ed3d2808 JB |
175 | cpReplaced = true; |
176 | } | |
177 | }); | |
178 | } | |
72092cfc | 179 | !cpReplaced && chargingStation.getConnectorStatus(connectorId)?.chargingProfiles?.push(cp); |
ed3d2808 JB |
180 | } |
181 | ||
73d87be1 JB |
182 | public static clearChargingProfiles = ( |
183 | chargingStation: ChargingStation, | |
41f3983a | 184 | commandPayload: OCPP16ClearChargingProfileRequest, |
73d87be1 JB |
185 | chargingProfiles: OCPP16ChargingProfile[] | undefined, |
186 | ): boolean => { | |
0d1f33ba | 187 | const { id, chargingProfilePurpose, stackLevel } = commandPayload; |
73d87be1 JB |
188 | let clearedCP = false; |
189 | if (isNotEmptyArray(chargingProfiles)) { | |
190 | chargingProfiles?.forEach((chargingProfile: OCPP16ChargingProfile, index: number) => { | |
191 | let clearCurrentCP = false; | |
0d1f33ba | 192 | if (chargingProfile.chargingProfileId === id) { |
73d87be1 JB |
193 | clearCurrentCP = true; |
194 | } | |
0d1f33ba | 195 | if (!chargingProfilePurpose && chargingProfile.stackLevel === stackLevel) { |
73d87be1 JB |
196 | clearCurrentCP = true; |
197 | } | |
0d1f33ba | 198 | if (!stackLevel && chargingProfile.chargingProfilePurpose === chargingProfilePurpose) { |
73d87be1 JB |
199 | clearCurrentCP = true; |
200 | } | |
201 | if ( | |
0d1f33ba JB |
202 | chargingProfile.stackLevel === stackLevel && |
203 | chargingProfile.chargingProfilePurpose === chargingProfilePurpose | |
73d87be1 JB |
204 | ) { |
205 | clearCurrentCP = true; | |
206 | } | |
207 | if (clearCurrentCP) { | |
208 | chargingProfiles.splice(index, 1); | |
209 | logger.debug( | |
210 | `${chargingStation.logPrefix()} Matching charging profile(s) cleared: %j`, | |
211 | chargingProfile, | |
212 | ); | |
213 | clearedCP = true; | |
214 | } | |
215 | }); | |
216 | } | |
217 | return clearedCP; | |
218 | }; | |
219 | ||
ef9e3b33 | 220 | public static composeChargingSchedules = ( |
4abf6441 JB |
221 | chargingScheduleHigher: OCPP16ChargingSchedule | undefined, |
222 | chargingScheduleLower: OCPP16ChargingSchedule | undefined, | |
d632062f | 223 | compositeInterval: Interval, |
ef9e3b33 | 224 | ): OCPP16ChargingSchedule | undefined => { |
4abf6441 | 225 | if (!chargingScheduleHigher && !chargingScheduleLower) { |
ef9e3b33 JB |
226 | return undefined; |
227 | } | |
4abf6441 | 228 | if (chargingScheduleHigher && !chargingScheduleLower) { |
d632062f | 229 | return OCPP16ServiceUtils.composeChargingSchedule(chargingScheduleHigher, compositeInterval); |
ef9e3b33 | 230 | } |
4abf6441 | 231 | if (!chargingScheduleHigher && chargingScheduleLower) { |
d632062f | 232 | return OCPP16ServiceUtils.composeChargingSchedule(chargingScheduleLower, compositeInterval); |
ef9e3b33 | 233 | } |
4abf6441 | 234 | const compositeChargingScheduleHigher: OCPP16ChargingSchedule | undefined = |
d632062f | 235 | OCPP16ServiceUtils.composeChargingSchedule(chargingScheduleHigher!, compositeInterval); |
4abf6441 | 236 | const compositeChargingScheduleLower: OCPP16ChargingSchedule | undefined = |
d632062f | 237 | OCPP16ServiceUtils.composeChargingSchedule(chargingScheduleLower!, compositeInterval); |
4abf6441 JB |
238 | const compositeChargingScheduleHigherInterval: Interval = { |
239 | start: compositeChargingScheduleHigher!.startSchedule!, | |
ef9e3b33 | 240 | end: addSeconds( |
4abf6441 JB |
241 | compositeChargingScheduleHigher!.startSchedule!, |
242 | compositeChargingScheduleHigher!.duration!, | |
ef9e3b33 JB |
243 | ), |
244 | }; | |
4abf6441 JB |
245 | const compositeChargingScheduleLowerInterval: Interval = { |
246 | start: compositeChargingScheduleLower!.startSchedule!, | |
ef9e3b33 | 247 | end: addSeconds( |
4abf6441 JB |
248 | compositeChargingScheduleLower!.startSchedule!, |
249 | compositeChargingScheduleLower!.duration!, | |
ef9e3b33 JB |
250 | ), |
251 | }; | |
4abf6441 JB |
252 | const higherFirst = isBefore( |
253 | compositeChargingScheduleHigherInterval.start, | |
254 | compositeChargingScheduleLowerInterval.start, | |
255 | ); | |
ef9e3b33 JB |
256 | if ( |
257 | !areIntervalsOverlapping( | |
4abf6441 JB |
258 | compositeChargingScheduleHigherInterval, |
259 | compositeChargingScheduleLowerInterval, | |
ef9e3b33 JB |
260 | ) |
261 | ) { | |
262 | return { | |
4abf6441 JB |
263 | ...compositeChargingScheduleLower, |
264 | ...compositeChargingScheduleHigher!, | |
265 | startSchedule: higherFirst | |
266 | ? (compositeChargingScheduleHigherInterval.start as Date) | |
267 | : (compositeChargingScheduleLowerInterval.start as Date), | |
268 | duration: higherFirst | |
269 | ? differenceInSeconds( | |
270 | compositeChargingScheduleLowerInterval.end, | |
271 | compositeChargingScheduleHigherInterval.start, | |
272 | ) | |
273 | : differenceInSeconds( | |
274 | compositeChargingScheduleHigherInterval.end, | |
275 | compositeChargingScheduleLowerInterval.start, | |
276 | ), | |
277 | chargingSchedulePeriod: [ | |
278 | ...compositeChargingScheduleHigher!.chargingSchedulePeriod.map((schedulePeriod) => { | |
279 | return { | |
280 | ...schedulePeriod, | |
281 | startPeriod: higherFirst | |
282 | ? 0 | |
283 | : schedulePeriod.startPeriod + | |
284 | differenceInSeconds( | |
285 | compositeChargingScheduleHigherInterval.start, | |
286 | compositeChargingScheduleLowerInterval.start, | |
287 | ), | |
288 | }; | |
289 | }), | |
290 | ...compositeChargingScheduleLower!.chargingSchedulePeriod.map((schedulePeriod) => { | |
291 | return { | |
292 | ...schedulePeriod, | |
293 | startPeriod: higherFirst | |
294 | ? schedulePeriod.startPeriod + | |
295 | differenceInSeconds( | |
296 | compositeChargingScheduleLowerInterval.start, | |
297 | compositeChargingScheduleHigherInterval.start, | |
298 | ) | |
299 | : 0, | |
300 | }; | |
301 | }), | |
302 | ].sort((a, b) => a.startPeriod - b.startPeriod), | |
ef9e3b33 JB |
303 | }; |
304 | } | |
4abf6441 JB |
305 | return { |
306 | ...compositeChargingScheduleLower, | |
307 | ...compositeChargingScheduleHigher!, | |
308 | startSchedule: higherFirst | |
309 | ? (compositeChargingScheduleHigherInterval.start as Date) | |
310 | : (compositeChargingScheduleLowerInterval.start as Date), | |
311 | duration: higherFirst | |
312 | ? differenceInSeconds( | |
313 | compositeChargingScheduleLowerInterval.end, | |
314 | compositeChargingScheduleHigherInterval.start, | |
315 | ) | |
316 | : differenceInSeconds( | |
317 | compositeChargingScheduleHigherInterval.end, | |
318 | compositeChargingScheduleLowerInterval.start, | |
319 | ), | |
320 | chargingSchedulePeriod: [ | |
321 | ...compositeChargingScheduleHigher!.chargingSchedulePeriod.map((schedulePeriod) => { | |
322 | return { | |
323 | ...schedulePeriod, | |
324 | startPeriod: higherFirst | |
325 | ? 0 | |
326 | : schedulePeriod.startPeriod + | |
327 | differenceInSeconds( | |
328 | compositeChargingScheduleHigherInterval.start, | |
329 | compositeChargingScheduleLowerInterval.start, | |
330 | ), | |
331 | }; | |
332 | }), | |
333 | ...compositeChargingScheduleLower!.chargingSchedulePeriod | |
c4ab56ba | 334 | .filter((schedulePeriod, index) => { |
4abf6441 JB |
335 | if ( |
336 | higherFirst && | |
337 | isWithinInterval( | |
338 | addSeconds( | |
339 | compositeChargingScheduleLowerInterval.start, | |
340 | schedulePeriod.startPeriod, | |
341 | ), | |
342 | { | |
343 | start: compositeChargingScheduleLowerInterval.start, | |
344 | end: compositeChargingScheduleHigherInterval.end, | |
345 | }, | |
346 | ) | |
347 | ) { | |
348 | return false; | |
349 | } | |
c4ab56ba JB |
350 | if ( |
351 | higherFirst && | |
352 | index < compositeChargingScheduleLower!.chargingSchedulePeriod.length - 1 && | |
353 | !isWithinInterval( | |
354 | addSeconds( | |
355 | compositeChargingScheduleLowerInterval.start, | |
356 | schedulePeriod.startPeriod, | |
357 | ), | |
358 | { | |
359 | start: compositeChargingScheduleLowerInterval.start, | |
360 | end: compositeChargingScheduleHigherInterval.end, | |
361 | }, | |
362 | ) && | |
363 | isWithinInterval( | |
364 | addSeconds( | |
365 | compositeChargingScheduleLowerInterval.start, | |
366 | compositeChargingScheduleLower!.chargingSchedulePeriod[index + 1].startPeriod, | |
367 | ), | |
368 | { | |
369 | start: compositeChargingScheduleLowerInterval.start, | |
370 | end: compositeChargingScheduleHigherInterval.end, | |
371 | }, | |
372 | ) | |
373 | ) { | |
c4ab56ba JB |
374 | return false; |
375 | } | |
4abf6441 JB |
376 | if ( |
377 | !higherFirst && | |
378 | isWithinInterval( | |
379 | addSeconds( | |
380 | compositeChargingScheduleLowerInterval.start, | |
381 | schedulePeriod.startPeriod, | |
382 | ), | |
383 | { | |
384 | start: compositeChargingScheduleHigherInterval.start, | |
385 | end: compositeChargingScheduleLowerInterval.end, | |
386 | }, | |
387 | ) | |
388 | ) { | |
389 | return false; | |
390 | } | |
391 | return true; | |
392 | }) | |
0e14e1d4 JB |
393 | .map((schedulePeriod, index) => { |
394 | if (index === 0 && schedulePeriod.startPeriod !== 0) { | |
395 | schedulePeriod.startPeriod = 0; | |
396 | } | |
4abf6441 JB |
397 | return { |
398 | ...schedulePeriod, | |
399 | startPeriod: higherFirst | |
400 | ? schedulePeriod.startPeriod + | |
401 | differenceInSeconds( | |
402 | compositeChargingScheduleLowerInterval.start, | |
403 | compositeChargingScheduleHigherInterval.start, | |
404 | ) | |
405 | : 0, | |
406 | }; | |
407 | }), | |
408 | ].sort((a, b) => a.startPeriod - b.startPeriod), | |
409 | }; | |
ef9e3b33 JB |
410 | }; |
411 | ||
90aceaf6 JB |
412 | public static hasReservation = ( |
413 | chargingStation: ChargingStation, | |
414 | connectorId: number, | |
415 | idTag: string, | |
416 | ): boolean => { | |
417 | const connectorReservation = chargingStation.getReservationBy('connectorId', connectorId); | |
418 | const chargingStationReservation = chargingStation.getReservationBy('connectorId', 0); | |
419 | if ( | |
420 | (chargingStation.getConnectorStatus(connectorId)?.status === | |
421 | OCPP16ChargePointStatus.Reserved && | |
422 | connectorReservation && | |
56563a3c | 423 | !hasReservationExpired(connectorReservation) && |
90aceaf6 | 424 | // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing |
56563a3c | 425 | connectorReservation?.idTag === idTag) || |
90aceaf6 JB |
426 | (chargingStation.getConnectorStatus(0)?.status === OCPP16ChargePointStatus.Reserved && |
427 | chargingStationReservation && | |
56563a3c JB |
428 | !hasReservationExpired(chargingStationReservation) && |
429 | chargingStationReservation?.idTag === idTag) | |
90aceaf6 | 430 | ) { |
88499f52 JB |
431 | logger.debug( |
432 | `${chargingStation.logPrefix()} Connector id ${connectorId} has a valid reservation for idTag ${idTag}: %j`, | |
433 | connectorReservation ?? chargingStationReservation, | |
434 | ); | |
56563a3c | 435 | return true; |
90aceaf6 | 436 | } |
56563a3c | 437 | return false; |
90aceaf6 JB |
438 | }; |
439 | ||
1b271a54 JB |
440 | public static parseJsonSchemaFile<T extends JsonType>( |
441 | relativePath: string, | |
442 | moduleName?: string, | |
5edd8ba0 | 443 | methodName?: string, |
1b271a54 | 444 | ): JSONSchemaType<T> { |
7164966d | 445 | return super.parseJsonSchemaFile<T>( |
51022aa0 | 446 | relativePath, |
1b271a54 JB |
447 | OCPPVersion.VERSION_16, |
448 | moduleName, | |
5edd8ba0 | 449 | methodName, |
7164966d | 450 | ); |
130783a7 JB |
451 | } |
452 | ||
ef9e3b33 JB |
453 | private static composeChargingSchedule = ( |
454 | chargingSchedule: OCPP16ChargingSchedule, | |
d632062f | 455 | compositeInterval: Interval, |
ef9e3b33 JB |
456 | ): OCPP16ChargingSchedule | undefined => { |
457 | const chargingScheduleInterval: Interval = { | |
458 | start: chargingSchedule.startSchedule!, | |
459 | end: addSeconds(chargingSchedule.startSchedule!, chargingSchedule.duration!), | |
460 | }; | |
d632062f | 461 | if (areIntervalsOverlapping(chargingScheduleInterval, compositeInterval)) { |
ef9e3b33 | 462 | chargingSchedule.chargingSchedulePeriod.sort((a, b) => a.startPeriod - b.startPeriod); |
d632062f | 463 | if (isBefore(chargingScheduleInterval.start, compositeInterval.start)) { |
ef9e3b33 JB |
464 | return { |
465 | ...chargingSchedule, | |
d632062f JB |
466 | startSchedule: compositeInterval.start as Date, |
467 | duration: differenceInSeconds( | |
468 | chargingScheduleInterval.end, | |
469 | compositeInterval.start as Date, | |
470 | ), | |
0e14e1d4 JB |
471 | chargingSchedulePeriod: chargingSchedule.chargingSchedulePeriod |
472 | .filter((schedulePeriod, index) => { | |
ef9e3b33 JB |
473 | if ( |
474 | isWithinInterval( | |
475 | addSeconds(chargingScheduleInterval.start, schedulePeriod.startPeriod)!, | |
d632062f | 476 | compositeInterval, |
ef9e3b33 JB |
477 | ) |
478 | ) { | |
479 | return true; | |
480 | } | |
481 | if ( | |
482 | index < chargingSchedule.chargingSchedulePeriod.length - 1 && | |
483 | !isWithinInterval( | |
484 | addSeconds(chargingScheduleInterval.start, schedulePeriod.startPeriod), | |
d632062f | 485 | compositeInterval, |
ef9e3b33 JB |
486 | ) && |
487 | isWithinInterval( | |
488 | addSeconds( | |
489 | chargingScheduleInterval.start, | |
490 | chargingSchedule.chargingSchedulePeriod[index + 1].startPeriod, | |
491 | ), | |
d632062f | 492 | compositeInterval, |
ef9e3b33 JB |
493 | ) |
494 | ) { | |
ef9e3b33 JB |
495 | return true; |
496 | } | |
497 | return false; | |
0e14e1d4 JB |
498 | }) |
499 | .map((schedulePeriod, index) => { | |
500 | if (index === 0 && schedulePeriod.startPeriod !== 0) { | |
501 | schedulePeriod.startPeriod = 0; | |
502 | } | |
503 | return schedulePeriod; | |
504 | }), | |
ef9e3b33 JB |
505 | }; |
506 | } | |
d632062f | 507 | if (isAfter(chargingScheduleInterval.end, compositeInterval.end)) { |
ef9e3b33 JB |
508 | return { |
509 | ...chargingSchedule, | |
d632062f JB |
510 | duration: differenceInSeconds( |
511 | compositeInterval.end as Date, | |
512 | chargingScheduleInterval.start, | |
513 | ), | |
ef9e3b33 JB |
514 | chargingSchedulePeriod: chargingSchedule.chargingSchedulePeriod.filter((schedulePeriod) => |
515 | isWithinInterval( | |
516 | addSeconds(chargingScheduleInterval.start, schedulePeriod.startPeriod)!, | |
d632062f | 517 | compositeInterval, |
ef9e3b33 JB |
518 | ), |
519 | ), | |
520 | }; | |
521 | } | |
522 | return chargingSchedule; | |
523 | } | |
524 | }; | |
6ed92bc1 | 525 | } |