/// <summary> /// If a intervalReading to the tariff change timestamp is needed, this method will be called. /// </summary> /// <param name="meterReading">The raw data.</param> /// <param name="end">The date for the meterReading</param> /// <param name="index">the index of the parent loop</param> /// <returns>An intervalReading or null</returns> public (IntervalReading reading, DateTime end) SetIntervalReading(MeterReading meterReading, DateTime end, int index, int dayTimeProfilesCount) { var date = LocalSetLastReading(end, index, dayTimeProfilesCount); var reading = meterReading.GetIntervalReadingFromDate(date); // If there is a gap at 0:00 o'clock the next day if (date > end && (reading == null || !IsStatusValid(reading))) { date = end; reading = meterReading.GetIntervalReadingFromDate(date); } return(reading, date); }
/// <summary> /// Is called from FindLastValidTime when no matching vaule was found. /// </summary> /// <param name="end">The current endpoint of the tariff stage range.</param> /// <param name="profile">The used SpecialDayProfile instance.</param> /// <param name="dayTimeProfiles">The used DayTime profile.</param> /// <param name="meterReading">The raw data.</param> /// <param name="index">For marking the parent loop index.</param> /// <returns>The matching DateTime and the new index for the parent loop.</returns> public (DateTime end, int index) FindNextValidTime(DateTime end, SpecialDayProfile profile, List <DayTimeProfile> dayTimeProfiles, MeterReading meterReading, int index) { DateTime result = end; var match = false; var helpindex = index; while (!match) { if (helpindex >= dayTimeProfiles.Count) { helpindex = helpindex - 1; break; } result = ModelExtensions.GetDateTimeFromSpecialDayProfile(profile, dayTimeProfiles[helpindex]); var reading = meterReading.GetIntervalReadingFromDate(result); if (reading != null && IsStatusValid(reading)) { match = true; } else { helpindex++; } } return(result, helpindex); }
/// <summary> /// If no coresonding value to the tarffif change time is found this method will be called. /// </summary> /// <param name="start">The beginning of the tariff stage range.</param> /// <param name="end">The current endpoint of the tariff stage range.</param> /// <param name="profile">The used SpecialDayProfile instance.</param> /// <param name="dayTimeProfiles">The used DayTime profile.</param> /// <param name="meterReading">The raw data.</param> /// <param name="index">Start index for iterating over the measurement list.</param> /// <returns>The matching DateTime and the new index for the parent loop.</returns> public (DateTime end, int index) FindLastValidTime(DateTime start, DateTime end, SpecialDayProfile profile, List <DayTimeProfile> dayTimeProfiles, MeterReading meterReading, int index) { var result = end; var match = false; var helpindex = index; while (!match) { // Get the next lower value // Example: If there is no value at 5:00 then it is set to 4:45 and so on until a value // was found or start is reached. If start is reached the next upper value is searched (FindNextValidTime) result = ModelExtensions.GetDateTimeFromSpecialDayProfile(profile, dayTimeProfiles[helpindex]); if (result == start) { (result, helpindex) = this.FindNextValidTime(end, profile, dayTimeProfiles, meterReading, index); if (helpindex + 1 == dayTimeProfiles.Count) { break; } } var reading = meterReading.GetIntervalReadingFromDate(result); if (reading != null && IsStatusValid(reading)) { match = true; } else { helpindex--; if (helpindex < 0) { throw new InvalidOperationException( "Die PTB oder FNN Stati aller Messwerte sind kritisch oder fatal."); } } } return(result, helpindex); }
/// <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); }