/// <summary> /// Calculates the next valid range for the current day /// </summary> /// <param name="reading">The IntervalReading at the end of the new range.</param> /// <param name="endCurrentReading">The end date of the current range.</param> /// <param name="startReading">The IntervalReading at the start of the new range.</param> /// <param name="start">The start date of the current range.</param> /// <param name="end">The global end date of the current day.</param> /// <param name="dayTimeProfiles">The dayTimeProfiles for the current day.</param> /// <param name="meterReading">The meterReading object of the current original value list.</param> /// <param name="profile">The current SpecialDayProfile.</param> /// <param name="currentDay">The current AccountingDay object.</param> /// <param name="i">The current index value of the parent loop.</param> /// <param name="latestReading">The current meterReading value.</param> /// <param name="latestTariffId">The last valid tariff id.</param> /// <returns>The rangeData of the found range</returns> public (IntervalReading reading, MeasuringRange range, int index, long latestReading) GetNextRange(IntervalReading reading, DateTime endCurrentReading, IntervalReading startReading, DateTime start, DateTime end, List <DayTimeProfile> dayTimeProfiles, MeterReading meterReading, SpecialDayProfile profile, AccountingDay currentDay, int i, long latestReading, ushort latestTariffId) { var endReading = SetConcreteIntervalReading(reading, endCurrentReading); var range = new MeasuringRange(); // Normal situation: reading is valid. if (endReading.reading != null && IsStatusValid(endReading.reading)) { range = new MeasuringRange(start, endReading.end, (ushort)dayTimeProfiles[i - 1].TariffNumber, (endReading.reading.Value.Value - startReading.Value.Value)); } else // A gap was found { // Looking for the next valid IntervalReading var result = FindLastValidTime(start, end, profile, dayTimeProfiles, meterReading, i - 1); endReading = SetIntervalReading(meterReading, result.end, result.index, dayTimeProfiles.Count); if (result.end < end && endReading.reading != null) // An IntervalReading was found which is bigger than startReading but smaller than the detected gap. { range = new MeasuringRange(start, endReading.end, (ushort)dayTimeProfiles[result.index].TariffNumber, (endReading.reading.Value.Value - startReading.Value.Value)); } else { // Count in error register if (endReading.reading != null && IsStatusValid(endReading.reading)) { range = new MeasuringRange(start, endReading.end, (endReading.reading.Value.Value - startReading.Value.Value)); } else { // No closing valid IntervalReading for the current day was found (There was a gap at the last tariff change) latestTariffId = 63; endReading.reading = startReading; // No valid IntervalReading was found. Reset range to mark it as invalid. } } // 1 Measurement period at tariff change is missing if ((i - 1) == result.index) { latestReading = this.GetLatestReading(latestReading, endReading.reading); latestTariffId = range.TariffId; currentDay.Add(range, new ObisId(meterReading.ReadingType.ObisCode)); var j = result.index; var nextDate = ModelExtensions.GetDateTimeFromSpecialDayProfile(profile, dayTimeProfiles[j + 1]); var nextReading = SetIntervalReading(meterReading, nextDate, j + 1, dayTimeProfiles.Count); while (nextReading.reading == null || !IsStatusValid(nextReading.reading)) { j++; if (j < dayTimeProfiles.Count - 1) { nextDate = ModelExtensions.GetDateTimeFromSpecialDayProfile(profile, dayTimeProfiles[j + 1]); nextReading = SetIntervalReading(meterReading, nextDate, j + 1, dayTimeProfiles.Count); } else { nextDate = ModelExtensions.GetDateTimeFromSpecialDayProfile(profile, dayTimeProfiles[j]); nextReading = SetIntervalReading(meterReading, nextDate, j, dayTimeProfiles.Count); if (nextReading.reading == null || !IsStatusValid(endReading.reading)) { nextReading = endReading; break; } } } // Count in error register range = new MeasuringRange(endReading.end, nextReading.end, (nextReading.reading.Value.Value - endReading.reading.Value.Value)); endReading = nextReading; result.index = j + 1; } // Protect for the special case i == dayTimeProfiles.Count -1 because it could lead to an endless loop. if (i != dayTimeProfiles.Count - 1) { i = result.index; } } // Check if the range object is empty if (this.IsRangeEmpty(range)) { range.TariffId = latestTariffId; } return(endReading.reading, range, i, latestReading); }
/// <summary> /// Searches the next valid startReading (In case the first value or values of the day are gaps) /// </summary> /// <param name="start">the current timestamp.</param> /// <param name="dayTimeProfiles">The dayTimeProfiles for the current day.</param> /// <param name="profile">The specialDayProfile of the current day.</param> /// <param name="meterReading">The meterReading object of the current original value list.</param> /// <param name="currentDay">The current AccountingDay object.</param> /// <param name="dtpIndex">The current dayTimeProfiles index.</param> /// <param name="latestReading">The current meterReading value.</param> /// <param name="lastTariffId">The last valid tariff id.</param> /// <returns>The next valid start value.</returns> public (IntervalReading startReading, int index, long latestReading, ushort tariffId) GetStartReading(DateTime start, List <DayTimeProfile> dayTimeProfiles, SpecialDayProfile profile, MeterReading meterReading, AccountingDay currentDay, int dtpIndex, long latestReading, ushort lastTariffId) { var dayStart = start; var startReading = new IntervalReading(); var index = dtpIndex; for (int i = 1; i < dayTimeProfiles.Count; i++) { start = ModelExtensions.GetDateTimeFromSpecialDayProfile(profile, dayTimeProfiles[i]); startReading = meterReading.GetIntervalReadingFromDate(start); // If the gap reaches the next tariff change if (dayTimeProfiles[i].TariffNumber != lastTariffId && lastTariffId != 63) { lastTariffId = 63; } // Checks if a startReading is found if (startReading != null && IsStatusValid(startReading)) { currentDay.Add(new MeasuringRange() { Start = dayStart, End = start, Amount = (startReading.Value.Value - latestReading), TariffId = lastTariffId }, new ObisId(meterReading.ReadingType.ObisCode)); index = i; latestReading = GetLatestReading(latestReading, startReading); lastTariffId = (ushort)dayTimeProfiles[i].TariffNumber; break; } else { index = i; } } // if no startReading for the whole day was found, set index to 96 if ((startReading == null || !IsStatusValid(startReading)) && index == dayTimeProfiles.Count - 1) { var nextDay0OClock = start.AddSeconds(900); startReading = meterReading.GetIntervalReadingFromDate(nextDay0OClock); // Check if a value for the next day at 0:00 o Clock exists if (startReading != null && IsStatusValid(startReading)) { currentDay.Add(new MeasuringRange() { Start = dayStart, End = nextDay0OClock, Amount = startReading.Value.Value - latestReading, TariffId = lastTariffId }, new ObisId(meterReading.ReadingType.ObisCode)); } index = dayTimeProfiles.Count; } return(startReading, index, latestReading, lastTariffId); }
/// <summary> /// The main calculation method for every day in the billing period. /// </summary> /// <param name="profile">The current SpecialDayProfile</param> /// <param name="dayProfiles">A List of all DayProfiles</param> /// <param name="meterReading">The MeterReading instance with the raw data.</param> /// <param name="supplier">Contains the calculation data.</param> /// <param name="latestReading">The last valid value of an IntervalReading.</param> /// <param name="latestTariffId">The last vaild tariff.</param> /// <returns>The calculated AccountingSection</returns> public (AccountingDay day, long latestReading, ushort tariffId) GetDayData(SpecialDayProfile profile, List <DayProfile> dayProfiles, MeterReading meterReading, UsagePointLieferant supplier, long latestReading, ushort latestTariffId) { var registers = supplier.GetRegister(); this.UpdateReadingTypeFromOriginalValueList(registers); var currentDay = new AccountingDay(registers); // Every SpecialDayProfile is linked to a dayProfile which contains dayTimeProfiles. // This dayTimeProfiles are needed because they contain the tariff change information of the day. var dayTimeProfiles = GetValidDayTimeProfiles(dayProfiles, profile); var start = ModelExtensions.GetDateTimeFromSpecialDayProfile(profile, dayTimeProfiles[0]); var index = 1; var startReading = meterReading.GetIntervalReadingFromDate(start); // If the current day has a gap at 00:00 if (startReading == null || !IsStatusValid(startReading)) { var startData = this.GetStartReading(start, dayTimeProfiles, profile, meterReading, currentDay, index, latestReading, latestTariffId); index = startData.index; startReading = startData.startReading; start = startData.startReading != null && IsStatusValid(startData.startReading) ? startData.startReading.TargetTime.Value : start; latestTariffId = startData.tariffId; latestReading = startData.latestReading; } // Check whether dayTimeProfiles is null or empty this.CheckInitSettings(dayTimeProfiles); if (startReading != null && IsStatusValid(startReading)) { currentDay.Reading = new Reading() { Amount = startReading.Value, ObisCode = new ObisId(meterReading.ReadingType.ObisCode) }; currentDay.Start = profile.SpecialDayDate.GetDate(); } var endReading = SetConcreteIntervalReading(null, DateTime.MinValue); for (var i = index; i < dayTimeProfiles.Count; i++) { DateTime end; // Check if the tariff number changes if (dayTimeProfiles[i - 1].TariffNumber != dayTimeProfiles[i].TariffNumber) { // Set the end of the current range end = ModelExtensions.GetDateTimeFromSpecialDayProfile(profile, dayTimeProfiles[i]); endReading = SetIntervalReading(meterReading, end, i, dayTimeProfiles.Count); var rangeData = GetNextRange(endReading.reading, endReading.end, startReading, start, end, dayTimeProfiles, meterReading, profile, currentDay, i, latestReading, latestTariffId); latestReading = rangeData.latestReading; start = rangeData.reading.TargetTime.Value; startReading = rangeData.reading; i = rangeData.index; latestTariffId = rangeData.range.TariffId; latestReading = this.GetLatestReading(latestReading, rangeData.reading); if (!this.IsRangeEmpty(rangeData.range)) { currentDay.Add(rangeData.range, new ObisId(meterReading.ReadingType.ObisCode)); } } // If there is no tariff change at the current timestamp else { // Check if it is the last value of the current day if (i == dayTimeProfiles.Count - 1) { end = ModelExtensions.GetDateTimeFromSpecialDayProfile(profile, dayTimeProfiles[i]); endReading = SetIntervalReading(meterReading, end, i, dayTimeProfiles.Count); var rangeData = GetNextRange(endReading.reading, endReading.end, startReading, start, end, dayTimeProfiles, meterReading, profile, currentDay, i, latestReading, latestTariffId); latestReading = rangeData.latestReading; start = end; startReading = rangeData.reading; i = rangeData.index; latestTariffId = rangeData.range.TariffId; latestReading = this.GetLatestReading(latestReading, rangeData.reading); if (!this.IsRangeEmpty(rangeData.range)) { currentDay.Add(rangeData.range, new ObisId(meterReading.ReadingType.ObisCode)); } } } } return(currentDay, latestReading, latestTariffId); }