refactor: switch eslint configuration to strict type checking
[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)) {
5199f9fd 169 for (const [index, chargingProfile] of chargingStation
ed3d2808 170 .getConnectorStatus(connectorId)
5199f9fd
JB
171 ?.chargingProfiles?.entries() ?? []) {
172 if (
173 chargingProfile.chargingProfileId === cp.chargingProfileId ||
174 (chargingProfile.stackLevel === cp.stackLevel &&
175 chargingProfile.chargingProfilePurpose === cp.chargingProfilePurpose)
176 ) {
177 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
178 chargingStation.getConnectorStatus(connectorId)!.chargingProfiles![index] = cp
179 cpReplaced = true
180 }
181 }
ed3d2808 182 }
66a7748d 183 !cpReplaced && chargingStation.getConnectorStatus(connectorId)?.chargingProfiles?.push(cp)
ed3d2808
JB
184 }
185
73d87be1
JB
186 public static clearChargingProfiles = (
187 chargingStation: ChargingStation,
41f3983a 188 commandPayload: OCPP16ClearChargingProfileRequest,
66a7748d 189 chargingProfiles: OCPP16ChargingProfile[] | undefined
73d87be1 190 ): boolean => {
66a7748d
JB
191 const { id, chargingProfilePurpose, stackLevel } = commandPayload
192 let clearedCP = false
73d87be1
JB
193 if (isNotEmptyArray(chargingProfiles)) {
194 chargingProfiles?.forEach((chargingProfile: OCPP16ChargingProfile, index: number) => {
66a7748d 195 let clearCurrentCP = false
0d1f33ba 196 if (chargingProfile.chargingProfileId === id) {
66a7748d 197 clearCurrentCP = true
73d87be1 198 }
66a7748d
JB
199 if (chargingProfilePurpose == null && chargingProfile.stackLevel === stackLevel) {
200 clearCurrentCP = true
73d87be1 201 }
66a7748d
JB
202 if (
203 stackLevel == null &&
204 chargingProfile.chargingProfilePurpose === chargingProfilePurpose
205 ) {
206 clearCurrentCP = true
73d87be1
JB
207 }
208 if (
0d1f33ba
JB
209 chargingProfile.stackLevel === stackLevel &&
210 chargingProfile.chargingProfilePurpose === chargingProfilePurpose
73d87be1 211 ) {
66a7748d 212 clearCurrentCP = true
73d87be1
JB
213 }
214 if (clearCurrentCP) {
66a7748d 215 chargingProfiles.splice(index, 1)
73d87be1
JB
216 logger.debug(
217 `${chargingStation.logPrefix()} Matching charging profile(s) cleared: %j`,
66a7748d
JB
218 chargingProfile
219 )
220 clearedCP = true
73d87be1 221 }
66a7748d 222 })
73d87be1 223 }
66a7748d
JB
224 return clearedCP
225 }
73d87be1 226
ef9e3b33 227 public static composeChargingSchedules = (
4abf6441
JB
228 chargingScheduleHigher: OCPP16ChargingSchedule | undefined,
229 chargingScheduleLower: OCPP16ChargingSchedule | undefined,
66a7748d 230 compositeInterval: Interval
ef9e3b33 231 ): OCPP16ChargingSchedule | undefined => {
66a7748d
JB
232 if (chargingScheduleHigher == null && chargingScheduleLower == null) {
233 return undefined
ef9e3b33 234 }
66a7748d
JB
235 if (chargingScheduleHigher != null && chargingScheduleLower == null) {
236 return OCPP16ServiceUtils.composeChargingSchedule(chargingScheduleHigher, compositeInterval)
ef9e3b33 237 }
66a7748d
JB
238 if (chargingScheduleHigher == null && chargingScheduleLower != null) {
239 return OCPP16ServiceUtils.composeChargingSchedule(chargingScheduleLower, compositeInterval)
ef9e3b33 240 }
4abf6441 241 const compositeChargingScheduleHigher: OCPP16ChargingSchedule | undefined =
66a7748d
JB
242 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
243 OCPP16ServiceUtils.composeChargingSchedule(chargingScheduleHigher!, compositeInterval)
4abf6441 244 const compositeChargingScheduleLower: OCPP16ChargingSchedule | undefined =
66a7748d
JB
245 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
246 OCPP16ServiceUtils.composeChargingSchedule(chargingScheduleLower!, compositeInterval)
4abf6441 247 const compositeChargingScheduleHigherInterval: Interval = {
66a7748d 248 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
4abf6441 249 start: compositeChargingScheduleHigher!.startSchedule!,
ef9e3b33 250 end: addSeconds(
66a7748d 251 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
4abf6441 252 compositeChargingScheduleHigher!.startSchedule!,
66a7748d
JB
253 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
254 compositeChargingScheduleHigher!.duration!
255 )
256 }
4abf6441 257 const compositeChargingScheduleLowerInterval: Interval = {
66a7748d 258 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
4abf6441 259 start: compositeChargingScheduleLower!.startSchedule!,
ef9e3b33 260 end: addSeconds(
66a7748d 261 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
4abf6441 262 compositeChargingScheduleLower!.startSchedule!,
66a7748d
JB
263 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
264 compositeChargingScheduleLower!.duration!
265 )
266 }
4abf6441
JB
267 const higherFirst = isBefore(
268 compositeChargingScheduleHigherInterval.start,
66a7748d
JB
269 compositeChargingScheduleLowerInterval.start
270 )
ef9e3b33
JB
271 if (
272 !areIntervalsOverlapping(
4abf6441 273 compositeChargingScheduleHigherInterval,
66a7748d 274 compositeChargingScheduleLowerInterval
ef9e3b33
JB
275 )
276 ) {
277 return {
4abf6441 278 ...compositeChargingScheduleLower,
66a7748d 279 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
4abf6441
JB
280 ...compositeChargingScheduleHigher!,
281 startSchedule: higherFirst
282 ? (compositeChargingScheduleHigherInterval.start as Date)
283 : (compositeChargingScheduleLowerInterval.start as Date),
284 duration: higherFirst
285 ? differenceInSeconds(
66a7748d
JB
286 compositeChargingScheduleLowerInterval.end,
287 compositeChargingScheduleHigherInterval.start
288 )
4abf6441 289 : differenceInSeconds(
66a7748d
JB
290 compositeChargingScheduleHigherInterval.end,
291 compositeChargingScheduleLowerInterval.start
292 ),
4abf6441 293 chargingSchedulePeriod: [
66a7748d 294 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
4abf6441
JB
295 ...compositeChargingScheduleHigher!.chargingSchedulePeriod.map((schedulePeriod) => {
296 return {
297 ...schedulePeriod,
298 startPeriod: higherFirst
299 ? 0
300 : schedulePeriod.startPeriod +
301 differenceInSeconds(
302 compositeChargingScheduleHigherInterval.start,
66a7748d
JB
303 compositeChargingScheduleLowerInterval.start
304 )
305 }
4abf6441 306 }),
66a7748d 307 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
4abf6441
JB
308 ...compositeChargingScheduleLower!.chargingSchedulePeriod.map((schedulePeriod) => {
309 return {
310 ...schedulePeriod,
311 startPeriod: higherFirst
312 ? schedulePeriod.startPeriod +
313 differenceInSeconds(
314 compositeChargingScheduleLowerInterval.start,
66a7748d 315 compositeChargingScheduleHigherInterval.start
4abf6441 316 )
66a7748d
JB
317 : 0
318 }
319 })
320 ].sort((a, b) => a.startPeriod - b.startPeriod)
321 }
ef9e3b33 322 }
4abf6441
JB
323 return {
324 ...compositeChargingScheduleLower,
66a7748d 325 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
4abf6441
JB
326 ...compositeChargingScheduleHigher!,
327 startSchedule: higherFirst
328 ? (compositeChargingScheduleHigherInterval.start as Date)
329 : (compositeChargingScheduleLowerInterval.start as Date),
330 duration: higherFirst
331 ? differenceInSeconds(
66a7748d
JB
332 compositeChargingScheduleLowerInterval.end,
333 compositeChargingScheduleHigherInterval.start
334 )
4abf6441 335 : differenceInSeconds(
66a7748d
JB
336 compositeChargingScheduleHigherInterval.end,
337 compositeChargingScheduleLowerInterval.start
338 ),
4abf6441 339 chargingSchedulePeriod: [
66a7748d 340 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
4abf6441
JB
341 ...compositeChargingScheduleHigher!.chargingSchedulePeriod.map((schedulePeriod) => {
342 return {
343 ...schedulePeriod,
344 startPeriod: higherFirst
345 ? 0
346 : schedulePeriod.startPeriod +
347 differenceInSeconds(
348 compositeChargingScheduleHigherInterval.start,
66a7748d
JB
349 compositeChargingScheduleLowerInterval.start
350 )
351 }
4abf6441 352 }),
66a7748d 353 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
4abf6441 354 ...compositeChargingScheduleLower!.chargingSchedulePeriod
c4ab56ba 355 .filter((schedulePeriod, index) => {
4abf6441
JB
356 if (
357 higherFirst &&
358 isWithinInterval(
359 addSeconds(
360 compositeChargingScheduleLowerInterval.start,
66a7748d 361 schedulePeriod.startPeriod
4abf6441
JB
362 ),
363 {
364 start: compositeChargingScheduleLowerInterval.start,
66a7748d
JB
365 end: compositeChargingScheduleHigherInterval.end
366 }
4abf6441
JB
367 )
368 ) {
66a7748d 369 return false
4abf6441 370 }
c4ab56ba
JB
371 if (
372 higherFirst &&
66a7748d 373 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
c4ab56ba
JB
374 index < compositeChargingScheduleLower!.chargingSchedulePeriod.length - 1 &&
375 !isWithinInterval(
376 addSeconds(
377 compositeChargingScheduleLowerInterval.start,
66a7748d 378 schedulePeriod.startPeriod
c4ab56ba
JB
379 ),
380 {
381 start: compositeChargingScheduleLowerInterval.start,
66a7748d
JB
382 end: compositeChargingScheduleHigherInterval.end
383 }
c4ab56ba
JB
384 ) &&
385 isWithinInterval(
386 addSeconds(
387 compositeChargingScheduleLowerInterval.start,
66a7748d
JB
388 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
389 compositeChargingScheduleLower!.chargingSchedulePeriod[index + 1].startPeriod
c4ab56ba
JB
390 ),
391 {
392 start: compositeChargingScheduleLowerInterval.start,
66a7748d
JB
393 end: compositeChargingScheduleHigherInterval.end
394 }
c4ab56ba
JB
395 )
396 ) {
66a7748d 397 return false
c4ab56ba 398 }
4abf6441
JB
399 if (
400 !higherFirst &&
401 isWithinInterval(
402 addSeconds(
403 compositeChargingScheduleLowerInterval.start,
66a7748d 404 schedulePeriod.startPeriod
4abf6441
JB
405 ),
406 {
407 start: compositeChargingScheduleHigherInterval.start,
66a7748d
JB
408 end: compositeChargingScheduleLowerInterval.end
409 }
4abf6441
JB
410 )
411 ) {
66a7748d 412 return false
4abf6441 413 }
66a7748d 414 return true
4abf6441 415 })
0e14e1d4
JB
416 .map((schedulePeriod, index) => {
417 if (index === 0 && schedulePeriod.startPeriod !== 0) {
66a7748d 418 schedulePeriod.startPeriod = 0
0e14e1d4 419 }
4abf6441
JB
420 return {
421 ...schedulePeriod,
422 startPeriod: higherFirst
423 ? schedulePeriod.startPeriod +
424 differenceInSeconds(
425 compositeChargingScheduleLowerInterval.start,
66a7748d 426 compositeChargingScheduleHigherInterval.start
4abf6441 427 )
66a7748d
JB
428 : 0
429 }
430 })
431 ].sort((a, b) => a.startPeriod - b.startPeriod)
432 }
433 }
ef9e3b33 434
563e40ce
JB
435 public static isConfigurationKeyVisible (key: ConfigurationKey): boolean {
436 if (key.visible == null) {
d0ed7db9 437 return true
563e40ce
JB
438 }
439 return key.visible
440 }
441
90aceaf6
JB
442 public static hasReservation = (
443 chargingStation: ChargingStation,
444 connectorId: number,
66a7748d 445 idTag: string
90aceaf6 446 ): boolean => {
66a7748d
JB
447 const connectorReservation = chargingStation.getReservationBy('connectorId', connectorId)
448 const chargingStationReservation = chargingStation.getReservationBy('connectorId', 0)
90aceaf6
JB
449 if (
450 (chargingStation.getConnectorStatus(connectorId)?.status ===
451 OCPP16ChargePointStatus.Reserved &&
66a7748d 452 connectorReservation != null &&
56563a3c 453 !hasReservationExpired(connectorReservation) &&
5199f9fd 454 connectorReservation.idTag === idTag) ||
90aceaf6 455 (chargingStation.getConnectorStatus(0)?.status === OCPP16ChargePointStatus.Reserved &&
66a7748d 456 chargingStationReservation != null &&
56563a3c 457 !hasReservationExpired(chargingStationReservation) &&
5199f9fd 458 chargingStationReservation.idTag === idTag)
90aceaf6 459 ) {
88499f52
JB
460 logger.debug(
461 `${chargingStation.logPrefix()} Connector id ${connectorId} has a valid reservation for idTag ${idTag}: %j`,
66a7748d
JB
462 connectorReservation ?? chargingStationReservation
463 )
464 return true
90aceaf6 465 }
66a7748d
JB
466 return false
467 }
90aceaf6 468
1b271a54
JB
469 public static parseJsonSchemaFile<T extends JsonType>(
470 relativePath: string,
471 moduleName?: string,
66a7748d 472 methodName?: string
1b271a54 473 ): JSONSchemaType<T> {
7164966d 474 return super.parseJsonSchemaFile<T>(
51022aa0 475 relativePath,
1b271a54
JB
476 OCPPVersion.VERSION_16,
477 moduleName,
66a7748d
JB
478 methodName
479 )
130783a7
JB
480 }
481
66a7748d 482 private static readonly composeChargingSchedule = (
ef9e3b33 483 chargingSchedule: OCPP16ChargingSchedule,
66a7748d 484 compositeInterval: Interval
ef9e3b33
JB
485 ): OCPP16ChargingSchedule | undefined => {
486 const chargingScheduleInterval: Interval = {
66a7748d 487 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
ef9e3b33 488 start: chargingSchedule.startSchedule!,
66a7748d
JB
489 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
490 end: addSeconds(chargingSchedule.startSchedule!, chargingSchedule.duration!)
491 }
d632062f 492 if (areIntervalsOverlapping(chargingScheduleInterval, compositeInterval)) {
66a7748d 493 chargingSchedule.chargingSchedulePeriod.sort((a, b) => a.startPeriod - b.startPeriod)
d632062f 494 if (isBefore(chargingScheduleInterval.start, compositeInterval.start)) {
ef9e3b33
JB
495 return {
496 ...chargingSchedule,
d632062f
JB
497 startSchedule: compositeInterval.start as Date,
498 duration: differenceInSeconds(
499 chargingScheduleInterval.end,
66a7748d 500 compositeInterval.start as Date
d632062f 501 ),
0e14e1d4
JB
502 chargingSchedulePeriod: chargingSchedule.chargingSchedulePeriod
503 .filter((schedulePeriod, index) => {
ef9e3b33
JB
504 if (
505 isWithinInterval(
66a7748d 506 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
ef9e3b33 507 addSeconds(chargingScheduleInterval.start, schedulePeriod.startPeriod)!,
66a7748d 508 compositeInterval
ef9e3b33
JB
509 )
510 ) {
66a7748d 511 return true
ef9e3b33
JB
512 }
513 if (
514 index < chargingSchedule.chargingSchedulePeriod.length - 1 &&
515 !isWithinInterval(
516 addSeconds(chargingScheduleInterval.start, schedulePeriod.startPeriod),
66a7748d 517 compositeInterval
ef9e3b33
JB
518 ) &&
519 isWithinInterval(
520 addSeconds(
521 chargingScheduleInterval.start,
66a7748d 522 chargingSchedule.chargingSchedulePeriod[index + 1].startPeriod
ef9e3b33 523 ),
66a7748d 524 compositeInterval
ef9e3b33
JB
525 )
526 ) {
66a7748d 527 return true
ef9e3b33 528 }
66a7748d 529 return false
0e14e1d4
JB
530 })
531 .map((schedulePeriod, index) => {
532 if (index === 0 && schedulePeriod.startPeriod !== 0) {
66a7748d 533 schedulePeriod.startPeriod = 0
0e14e1d4 534 }
66a7748d
JB
535 return schedulePeriod
536 })
537 }
ef9e3b33 538 }
d632062f 539 if (isAfter(chargingScheduleInterval.end, compositeInterval.end)) {
ef9e3b33
JB
540 return {
541 ...chargingSchedule,
d632062f
JB
542 duration: differenceInSeconds(
543 compositeInterval.end as Date,
66a7748d 544 chargingScheduleInterval.start
d632062f 545 ),
ef9e3b33
JB
546 chargingSchedulePeriod: chargingSchedule.chargingSchedulePeriod.filter((schedulePeriod) =>
547 isWithinInterval(
66a7748d 548 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
ef9e3b33 549 addSeconds(chargingScheduleInterval.start, schedulePeriod.startPeriod)!,
66a7748d
JB
550 compositeInterval
551 )
552 )
553 }
ef9e3b33 554 }
66a7748d 555 return chargingSchedule
ef9e3b33 556 }
66a7748d 557 }
6ed92bc1 558}