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