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