/// <summary> /// Get TimeRange covered by Energiemenge /// </summary> /// <param name="menge">Energiemenge</param> /// <returns> /// TimeRange ranging from the earliest <see cref="Verbrauch.Startdatum" /> to the latest /// <see cref="Verbrauch.Enddatum" /> /// </returns> /// <returns></returns> public static TimeRange GetTimeRange(this BO.Energiemenge menge) { using (MiniProfiler.Current.Step(nameof(GetTimeRange))) { return(new TimeRange(menge.GetMinDate().UtcDateTime, menge.GetMaxDate().UtcDateTime)); } }
/// <summary> /// Test, if the Energiemenge is continuous within its own min/max range. /// <see cref="IsContinuous(BO4E.BO.Energiemenge,Itenso.TimePeriod.TimeRange)" /> /// </summary> /// <param name="em">Energiemenge</param> /// <returns> /// true iff Energiemenge has defined value for every point in time t in /// min(energieverbrauch.startdatum) <= t < max(energieverbrauch.enddatum); /// false otherwise /// </returns> public static bool IsContinuous(this BO.Energiemenge em) { return(IsContinuous(em, new TimeRange(em.GetMinDate().UtcDateTime, em.GetMaxDate().UtcDateTime))); }
/// <summary> /// Get a list of those time ranges within a reference, where no energieverbrauch entries are defined. /// </summary> /// <param name="em">Energiemenge</param> /// <param name="reference">reference time frame</param> /// <param name="wev">Wertermittlungsverfahren</param> /// <param name="obis">OBIS-Kennzahl</param> /// <param name="me">Mengeneinheit</param> /// <returns></returns> public static List <TimeRange> GetMissingTimeRanges(this BO.Energiemenge em, ITimeRange reference, Wertermittlungsverfahren wev, string obis, Mengeneinheit me) { using (MiniProfiler.Current.Step(nameof(GetMissingTimeRanges))) { IDictionary <Tuple <DateTime, DateTime>, Verbrauch> filteredVerbrauch; using (MiniProfiler.Current.Step( $"Filtering energieverbrauch on OBIS={obis}, WEV={wev}, Mengeneinheit={me}")) { filteredVerbrauch = em.Energieverbrauch .Where(v => v.Wertermittlungsverfahren == wev && v.Obiskennzahl == obis && v.Einheit == me) .ToDictionary(v => new Tuple <DateTime, DateTime>(v.Startdatum, v.Enddatum), v => v); } if (filteredVerbrauch.Count < 2) { throw new ArgumentException("Not enough entries in energieverbrauch to determine periodicity."); } if (!IsEvenlySpaced(em, reference, wev, obis, me, true)) { throw new ArgumentException( "The provided Energiemenge is not evenly spaced although gaps are allowed."); } var periodicity = GetTimeSpans(em, wev, obis, me).Min(); if ( Math.Abs((reference.Start - em.GetMinDate()).TotalMilliseconds % periodicity.TotalMilliseconds) != 0) { throw new ArgumentException( $"The absolute difference between reference.start ({reference.Start}) and the minimal date time in the Energiemenge ({em.GetMinDate()}) has to be an integer multiple of the periodicity {periodicity.TotalMilliseconds} but was {(reference.Start - em.GetMinDate()).TotalMilliseconds}."); } // since it's assured, that the energieverbrauch entries are evenly spaced it doesn't matter which entry we use to determine the duration. var duration = filteredVerbrauch.Values.Min(v => v.Enddatum) - filteredVerbrauch.Values.Min(v => v.Startdatum); var result = new List <TimeRange>(); using (MiniProfiler.Current.Step("Populating list with time slices in UTC")) { for (var dt = reference.Start; dt < reference.End; dt += periodicity) { // use a strict '==' instead of overlap. This is justified because all the other cases are considered beforehand switch (dt.Kind) { case DateTimeKind.Local: throw new ArgumentException("Local DateTime not supported!"); case DateTimeKind.Unspecified: dt = DateTime.SpecifyKind(dt, DateTimeKind.Utc); break; case DateTimeKind.Utc: break; } //using (MiniProfiler.Current.Step("linq where on filtered verbrauch")) //{ if (!filteredVerbrauch.ContainsKey( new Tuple <DateTime, DateTime>(dt, dt + duration))) // Where<Verbrauch>(v => v.startdatum == dt && v.enddatum == dt + duration).Any()) { result.Add(new TimeRange(dt, dt + duration)); } //} } } return(result); } }
/// <summary> /// Same as /// <see /// cref="GetTotalConsumption(BO4E.BO.Energiemenge,BO4E.ENUM.Wertermittlungsverfahren,string,BO4E.ENUM.Mengeneinheit)" /> /// but without auto-detected parameters. /// By default a the full length of the Energiemenge is taken into account. /// </summary> /// <param name="em">Energiemenge</param> /// <returns>Tuple of consumption value and unit of measurement</returns> public static Tuple <decimal, Mengeneinheit> GetTotalConsumption(this BO.Energiemenge em) { return(GetConsumption(em, new TimeRange(em.GetMinDate().UtcDateTime, em.GetMaxDate().UtcDateTime))); }