/// <summary>
 /// <see cref="IsEvenlySpaced(BO.Energiemenge, TimeRange, Wertermittlungsverfahren, string, Mengeneinheit, bool)"/>
 /// </summary>
 /// <param name="em">Energiemenge</param>
 /// <param name="allowGaps"></param>
 /// <returns></returns>
 public static bool IsEvenlySpaced(this BO4E.BO.Energiemenge em, bool allowGaps = false)
 {
     if (!em.IsPure())
     {
         // Find all combinations of Wertermittlungsverfahren, obis and Mengeneinheit.
         // The Energiemenge is evenly spaced if each of the combinations is evenly spaced itself.
         using (MiniProfiler.Current.Step("Check all Werte/Einheit/OBIS combinations"))
         {
             ISet <Tuple <Wertermittlungsverfahren, string, Mengeneinheit> > combinations = GetWevObisMeCombinations(em);
             foreach (Tuple <Wertermittlungsverfahren, string, Mengeneinheit> combo in combinations)
             {
                 if (!em.IsEvenlySpaced(em.GetTimeRange(), combo.Item1, combo.Item2, combo.Item3, allowGaps))
                 {
                     return(false);
                 }
             }
         }
         return(true);
     }
     else
     {
         Verbrauch v = em.Energieverbrauch.FirstOrDefault();
         return(em.IsEvenlySpaced(em.GetTimeRange(), v.Wertermittlungsverfahren, v.Obiskennzahl, v.Einheit, allowGaps));
     }
 }
        /// <summary>
        /// Get average of Mengeneinheit for given time interval
        /// </summary>
        /// <param name="em">Energiemenge</param>
        /// <param name="reference">reference time frame</param>
        /// <param name="wev">Wertermittlungsverfahren</param>
        /// <param name="obiskennzahl">OBIS</param>
        /// <param name="me">an extensive or intensive unit</param>
        /// <returns>the average value or null if no Verbrauch overlapped with the specified time interval</returns>
        public static decimal?GetAverage(this BO4E.BO.Energiemenge em, TimeRange reference,
                                         Wertermittlungsverfahren wev, string obiskennzahl, Mengeneinheit me)
        {
            decimal?result             = null;
            decimal overallDenominator = 0.0M;

            foreach (Verbrauch v in em.Energieverbrauch.Where(v => v.Einheit == me))
            {
                decimal overlapFactor = GetOverlapFactor(new TimeRange(v.Startdatum, v.Enddatum), reference, true);
                if (result.HasValue)
                {
                    result += overlapFactor * v.Wert;
                }
                else
                {
                    result = v.Wert;
                }
                overallDenominator += overlapFactor;
            }
            if (result.HasValue)
            {
                return(result / overallDenominator);
            }
            else
            {
                return(result);
            }
        }
 /// <summary>
 /// normalise energiemenge-&gt;energieverbrauch consumption values to a given <paramref name="target"/> value
 /// </summary>
 /// <param name="em">Energiemenge</param>
 /// <param name="target">normalising constant (1.0 by default)</param>
 /// <returns>new Energiemenge object with normalised consumption values</returns>
 public static BO4E.BO.Energiemenge Normalise(this BO4E.BO.Energiemenge em, decimal target = 1.0M)
 {
     using (MiniProfiler.Current.Step(nameof(Normalise)))
     {
         BO4E.BO.Energiemenge result;
         decimal scalingFactor;
         Tuple <decimal, Mengeneinheit> totalConsumption;
         using (MiniProfiler.Current.Step("Calculating total consumption and normalisation factor."))
         {
             totalConsumption = em.GetTotalConsumption();
             result           = BusinessObjectExtensions.DeepClone <BO4E.BO.Energiemenge>(em);
             if (totalConsumption.Item1 != 0.0M)
             {
                 scalingFactor = target / totalConsumption.Item1;
             }
             else
             {
                 scalingFactor = 0.0M;
             }
         }
         using (MiniProfiler.Current.Step("Parallelised normalising of all values."))
         {
             Parallel.ForEach <Verbrauch>(result.Energieverbrauch.Where(v => v.Einheit == totalConsumption.Item2), v =>
             {
                 v.Wert = scalingFactor * v.Wert;
             });
         }
         return(result);
     }
 }
Beispiel #4
0
 /// <summary>
 /// Our SAP CDS has a bug: When there's a change from non-DST to DST the <see cref="Verbrauch.Enddatum"/> is set
 /// to the first second of the DST period. To
 /// </summary>
 /// <param name="em"></param>
 public static void FixSapCDSBug(this BO4E.BO.Energiemenge em)
 {
     using (MiniProfiler.Current.Step("Fix SAP CDS Bug (Energiemenge)"))
     {
         if (em.Energieverbrauch != null && !em.HasBeenSanitized())
         {
             using (MiniProfiler.Current.Step($"for each Verbrauch entry: {nameof(FixSapCDSBug)}"))
             {
                 foreach (var v in em.Energieverbrauch)
                 {
                     v.FixSapCdsBug();
                 }
                 //em.energieverbrauch = em.energieverbrauch.Select(v => Verbrauch.FixSapCdsBug(v)).ToList();
             }
             using (MiniProfiler.Current.Step("for list as a whole"))
             {
                 foreach (var relevantEnddatum in em.Energieverbrauch.Where(v =>
                 {
                     var localEnd = DateTime.SpecifyKind(v.Enddatum, DateTimeKind.Unspecified);      // ToDo: Check .UtcDateTime
                     var localStart = DateTime.SpecifyKind(v.Startdatum, DateTimeKind.Unspecified);
                     return(!CentralEuropeStandardTime.CENTRAL_EUROPE_STANDARD_TIME.IsDaylightSavingTime(localStart) && CentralEuropeStandardTime.CENTRAL_EUROPE_STANDARD_TIME.IsDaylightSavingTime(localEnd));
                     //return !localStart.IsDaylightSavingTime() && localEnd.IsDaylightSavingTime();
                 }).Select(v => v.Enddatum))
                 {
                     var intervalSize = em.Energieverbrauch.Where(v => v.Enddatum == relevantEnddatum).Select(v => (v.Enddatum - v.Startdatum).TotalSeconds).Min();
                     foreach (var v in em.Energieverbrauch.Where(v => v.Enddatum == relevantEnddatum))
                     {
                         v.Enddatum = v.Startdatum.AddSeconds(intervalSize);
                     }
                 }
                 if (em.Energieverbrauch.Count(v => (v.Enddatum - v.Startdatum).TotalMinutes == -45) > 1)
                 {
                     /*foreach (var dstAffected in em.energieverbrauch.Where(v => (v.enddatum - v.startdatum).TotalMinutes != -45))
                      * {
                      *  Verbrauch anythingButEnddatum = dstAffected.DeepClone<Verbrauch>();
                      *  anythingButEnddatum.enddatum = DateTime.MinValue;
                      *  anythingButEnddatum.wert = 0;
                      *  foreach(var v in em.energieverbrauch.Where(v=>
                      *  {
                      *      var comp = v.DeepClone<Verbrauch>();
                      *      comp.enddatum = DateTime.MinValue;
                      *      comp.wert = 0;
                      *      return comp.Equals(anythingButEnddatum);
                      *  }))
                      *  {
                      *      int a = 0;
                      *  }
                      * }*/
                 }
             }
             if (em.UserProperties == null)
             {
                 em.UserProperties = new Dictionary <string, JToken>();
             }
             em.UserProperties[SAP_SANITIZED_USERPROPERTY_KEY] = true;
         }
     }
 }
Beispiel #5
0
 /// <summary>
 /// test if Energiemenge has only one <see cref="Wertermittlungsverfahren"/>
 /// </summary>
 /// <param name="em">Energiemenge</param>
 /// <returns>true iff the Energiemenge-&gt;energieverbrauch list has at most one distinct Wertermittlungsverfahren</returns>
 public static bool IsPureWertermittlungsverfahren(this BO4E.BO.Energiemenge em)
 {
     using (MiniProfiler.Current.Step(nameof(IsPureWertermittlungsverfahren)))
     {
         ISet <Wertermittlungsverfahren> wefs = new HashSet <Wertermittlungsverfahren>();
         em.Energieverbrauch.All <Verbrauch>(v => wefs.Add(v.Wertermittlungsverfahren));
         return(wefs.Count <= 1);
     }
 }
Beispiel #6
0
 /// <summary>
 /// test if Energiemenge has only one Obiskennzahl
 /// </summary>
 /// <param name="em">Energiemenge</param>
 /// <returns>true iff the Energiemenge-&gt;energieverbrauch list has at most one distinct Obiskennzahl</returns>
 public static bool IsPureObisKennzahl(this BO4E.BO.Energiemenge em)
 {
     using (MiniProfiler.Current.Step(nameof(IsPureObisKennzahl)))
     {
         ISet <string> obisKzs = new HashSet <string>();
         em.Energieverbrauch.All <Verbrauch>(v => obisKzs.Add(v.Obiskennzahl));
         return(obisKzs.Count <= 1);
     }
 }
Beispiel #7
0
        /// <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 timeframe</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 BO4E.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 <Verbrauch>(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.");
                }
                TimeSpan periodicity = GetTimeSpans(em, wev, obis, me).Min <TimeSpan>();
                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.
                TimeSpan         duration = filteredVerbrauch.Values.Min(v => v.Enddatum) - filteredVerbrauch.Values.Min(v => v.Startdatum);
                List <TimeRange> result   = new List <TimeRange>();
                using (MiniProfiler.Current.Step("Populating list with time slices in UTC"))
                {
                    for (DateTime 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);
            }
        }
Beispiel #8
0
        /// <summary>
        /// <see cref="GetMissingTimeRanges(BO.Energiemenge, TimeRange, Wertermittlungsverfahren, string, Mengeneinheit)"/>
        /// </summary>
        /// <param name="em">Energiemenge</param>
        /// <param name="reference">reference time frame</param>
        /// <returns></returns>
        public static List <TimeRange> GetMissingTimeRanges(this BO4E.BO.Energiemenge em, TimeRange reference)
        {
            if (!em.IsPure())
            {
                throw new ArgumentException("The Energiemenge you provided is not pure. Consider using the overloaded method.");
            }
            Verbrauch v = em.Energieverbrauch.FirstOrDefault();

            return(GetMissingTimeRanges(em, reference, v.Wertermittlungsverfahren, v.Obiskennzahl, v.Einheit));
        }
Beispiel #9
0
        /// <summary>
        /// Get percentage of time range covered by all Wertermittlungsverfahren/OBIS/Mengeneinheit
        /// combinations, that are present in the Energiemenge-&gt;energieverbrauch array.
        /// </summary>
        /// <param name="em">Energiemenge</param>
        /// <param name="reference">reference</param>
        /// <returns></returns>
        public static decimal GetJointCoverage(this BO4E.BO.Energiemenge em, TimeRange reference)
        {
            ISet <Tuple <Wertermittlungsverfahren, string, Mengeneinheit> > combinations = GetWevObisMeCombinations(em);
            decimal jointCoverage = em.Energieverbrauch
                                    //.AsParallel<Verbrauch>()
                                    .Where <Verbrauch>(v => combinations.Contains(Tuple.Create <Wertermittlungsverfahren, string, Mengeneinheit>(v.Wertermittlungsverfahren, v.Obiskennzahl, v.Einheit)))
                                    .Sum(v => GetOverlapFactor(new TimeRange(v.Startdatum, v.Enddatum), reference, true));

            return(jointCoverage - (combinations.Count - 1));
        }
Beispiel #10
0
 /// <summary>
 /// Get Zeitraum covered by Energiemenge.
 /// </summary>
 /// <param name="menge">Energiemenge</param>
 /// <returns>Zeitraum ranging from the earliest <see cref="Verbrauch.Startdatum"/> to the latest <see cref="Verbrauch.Enddatum"/></returns>
 public static Zeitraum GetZeitraum(this BO4E.BO.Energiemenge menge)
 {
     using (MiniProfiler.Current.Step(nameof(GetZeitraum)))
     {
         Zeitraum zeitraum = new Zeitraum
         {
             Startdatum = GetMinDate(menge),
             Enddatum   = GetMaxDate(menge)
         };
         return(zeitraum);
     }
 }
Beispiel #11
0
 /// <summary>
 /// Returns the consumption of a given kind of Mengeneinheit within the specified reference time range.
 /// </summary>
 /// <param name="em">Energiemenge</param>
 /// <param name="reference">reference time frame</param>
 /// <param name="wev">Wertermittlungsverfahren</param>
 /// <param name="obiskennzahl">OBIS number</param>
 /// <param name="me">an extensive unit (e.g. "kWh")</param>
 /// <returns>the consumption within the give time slice <paramref name="reference"/> in the unit passed as <paramref name="me"/></returns>
 public static decimal GetConsumption(this BO4E.BO.Energiemenge em, ITimeRange reference,
                                      Wertermittlungsverfahren wev, string obiskennzahl, Mengeneinheit me)
 {
     if (!me.IsExtensive())
     {
         throw new ArgumentException($"The Mengeneinheit {me} isn't extensive. Calculating a consumption doesn't make sense.");
     }
     return(em.Energieverbrauch
            .Where(v => v.Wertermittlungsverfahren == wev && v.Obiskennzahl == obiskennzahl && v.Einheit == me)
            //.AsParallel<Verbrauch>()
            .Sum(v => GetOverlapFactor(new TimeRange(v.Startdatum, v.Enddatum), reference, false) * v.Wert));
 }
Beispiel #12
0
        private static HashSet <TimeSpan> GetTimeSpans(this BO4E.BO.Energiemenge em)
        {
            HashSet <TimeSpan> result = new HashSet <TimeSpan>();
            List <Verbrauch>   vlist  = new List <Verbrauch>(em.Energieverbrauch);

            vlist.Sort(new VerbrauchDateTimeComparer());
            for (int i = 1; i < vlist.Count; i++)
            {
                result.Add(vlist[i].Startdatum - vlist[i - 1].Startdatum);
                result.Add(vlist[i].Enddatum - vlist[i - 1].Enddatum);
            }
            return(result);
        }
Beispiel #13
0
        private static HashSet <TimeSpan> GetTimeSpans(this BO4E.BO.Energiemenge em, Wertermittlungsverfahren wev, string obis, Mengeneinheit me)
        {
            HashSet <TimeSpan> result = new HashSet <TimeSpan>();
            List <Verbrauch>   vlist  = new List <Verbrauch>(em.Energieverbrauch);

            vlist.Sort(new VerbrauchDateTimeComparer());
            vlist = vlist.Where <Verbrauch>(v => v.Wertermittlungsverfahren == wev && v.Obiskennzahl == obis && v.Einheit == me).ToList <Verbrauch>();
            for (int i = 1; i < vlist.Count; i++)
            {
                result.Add(vlist[i].Startdatum - vlist[i - 1].Startdatum);
                result.Add(vlist[i].Enddatum - vlist[i - 1].Enddatum);
            }
            return(result);
        }
Beispiel #14
0
        /// <summary>
        /// Get ratio of overlap between given Energiemenge and a reference.
        /// Method is basically just another name for <see cref="GetOverlapFactor(TimeRange, TimeRange, bool)"/>
        /// </summary>
        /// <param name="em">Energiemenge</param>
        /// <param name="reference">reference time range</param>
        /// <param name="obisKz">OBIS</param>
        /// <param name="mengeneinheit">unit of measurement</param>
        /// <param name="wev">type of measurement</param>
        /// <param name="decimalRounding">post decimals</param>
        /// <returns>value between 0 (no overlap) and 1.0 (100% overlap)</returns>
        public static decimal GetCoverage(this BO4E.BO.Energiemenge em, ITimeRange reference,
                                          Wertermittlungsverfahren wev, string obisKz, Mengeneinheit mengeneinheit, int decimalRounding = 10)
        {
            decimal exactResult;

            using (MiniProfiler.Current.Step($"calculating coverage for list with {em.Energieverbrauch.Count} entries."))
            {
                exactResult = em.Energieverbrauch
                              //.AsParallel<Verbrauch>()
                              .Where <Verbrauch>(v => v.Einheit == mengeneinheit && v.Obiskennzahl == obisKz && v.Wertermittlungsverfahren == wev)
                              .Sum(v => GetOverlapFactor(new TimeRange(v.Startdatum, v.Enddatum), reference, true));
            }
            return(Math.Round(exactResult, decimalRounding));
        }
Beispiel #15
0
        /// <summary>
        /// tests if the method <see cref="Verbrauch.FixSapCdsBug"/> has been executed yet.
        /// </summary>
        /// <returns>true if Energiemenge has been sanitized</returns>
        private static bool HasBeenSanitized(this BO4E.BO.Energiemenge em)
        {
            bool sanitized;

            if (em.UserProperties == null || !em.UserProperties.TryGetValue(SAP_SANITIZED_USERPROPERTY_KEY, out JToken sapSanitizedToken))
            {
                sanitized = false;
            }
            else
            {
                sanitized = sapSanitizedToken.Value <bool>();
            }
            return(sanitized);
        }
Beispiel #16
0
 /// <summary>
 /// Get Average (<see cref="GetAverage(BO.Energiemenge, TimeRange, Wertermittlungsverfahren, string, Mengeneinheit)"/>)
 /// for a pure Energiemenge with automatically found parameters.
 /// </summary>
 /// <seealso cref="IsPure(BO4E.BO.Energiemenge)"/>
 /// <param name="em">Energiemenge</param>
 /// <returns>Tuple of average value and unit of measurement</returns>
 public static Tuple <decimal?, Mengeneinheit> GetAverage(this BO4E.BO.Energiemenge em)
 {
     if (!IsPure(em))
     {
         throw new ArgumentException("Energiemenge is not pure.");
     }
     else if (em.Energieverbrauch.Count == 0)
     {
         return(Tuple.Create <decimal?, Mengeneinheit>(null, Mengeneinheit.KW));
     }
     else
     {
         Verbrauch v = em.Energieverbrauch.First <Verbrauch>();
         return(Tuple.Create <decimal?, Mengeneinheit>(em.GetAverage(v.Wertermittlungsverfahren, v.Obiskennzahl, v.Einheit), v.Einheit));
     }
 }
Beispiel #17
0
 /// <summary>
 /// Get percentage of time range covered by pure Energiemenge.
 /// </summary>
 /// <param name="em">pure Energiemenge</param>
 /// <param name="reference">time frame reference</param>
 /// <returns>value between 0 (only coverage for 1 point in time) and 1.0 (100% coverage)</returns>
 public static decimal GetCoverage(this BO4E.BO.Energiemenge em, ITimeRange reference)
 {
     using (MiniProfiler.Current.Step(nameof(GetCoverage)))
     {
         if (!IsPure(em))
         {
             throw new ArgumentException("The Energiemenge is not pure. Cannot determine parameters.");
         }
         if (em.Energieverbrauch.Count == 0)
         {
             return(0.0M);
         }
         Verbrauch v = em.Energieverbrauch.First <Verbrauch>();
         return(em.GetCoverage(reference, v.Wertermittlungsverfahren, v.Obiskennzahl, v.Einheit));
     }
 }
Beispiel #18
0
        /// <summary>
        /// Returns the load in an intensive unit for a given point in time.
        /// </summary>
        /// <param name="em">Energiemenge</param>
        /// <param name="me">an intensive unit (e.g. "kW")</param>
        /// <param name="dt">point in time</param>
        /// <returns>load if Energiemenge BO contains value for specified date time<paramref name="dt"/>, null otherwise</returns>
        public static decimal?GetLoad(this BO4E.BO.Energiemenge em, Mengeneinheit me, DateTime dt)
        {
            if (!me.IsIntensive())
            {
                throw new ArgumentException($"The Mengeneinheit {me} isn't intensive. Calculating the value for a specific point in time doesn't make sense.");
            }
            decimal?result = null;

            foreach (Verbrauch v in em.Energieverbrauch.Where(v => v.Startdatum <= dt && dt < v.Enddatum))
            {
                if (result.HasValue)
                {
                    result += v.Wert;
                }
                else
                {
                    result = v.Wert;
                }
            }
            return(result);
        }
Beispiel #19
0
        /// <summary>
        /// Test, if the single entries/intervals of the energieverbrauch array share the same duration and spacing in time.
        /// </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>
        /// <param name="allowGaps">set true to allow gaps</param>
        /// <returns>True, if all energieverbrauch entries have the same length and their start and enddatum are evenly spaced.
        /// Also true, if there less than 2 entries in the energieverbrauch array.</returns>
        public static bool IsEvenlySpaced(this BO4E.BO.Energiemenge em, ITimeRange reference, Wertermittlungsverfahren wev, string obis, Mengeneinheit me, bool allowGaps = false)
        {
            HashSet <TimeSpan> startEndDatumPeriods;

            using (MiniProfiler.Current.Step("finding time spans"))
            {
                startEndDatumPeriods = GetTimeSpans(em, wev, obis, me);
            }
            if (startEndDatumPeriods.Count < 2)
            {
                return(true);
            }
            if (allowGaps)
            {
                // each time difference must be a multiple of the smallest difference.
                using (MiniProfiler.Current.Step("Iterating over all time spans"))
                {
                    double minDiff = startEndDatumPeriods.Min <TimeSpan>().TotalSeconds;
                    foreach (TimeSpan ts in startEndDatumPeriods)
                    {
                        if (Math.Abs(ts.TotalSeconds % minDiff) != 0)
                        {
                            // use profiler as logger:
                            using (MiniProfiler.Current.Step($"Found TimeSpan {ts} with a duration of {ts.TotalSeconds}. This is no multiple of {minDiff} => not evenly spaced."))
                            {
                                return(false);
                            }
                        }
                    }
                }
                return(true);
            }
            else
            {
                // there must be only 1 time difference between all the elements
                return(startEndDatumPeriods.Count <= 1);
            }
        }
Beispiel #20
0
        /// <summary>
        /// <see cref="GetCompletenessReport(BO.Energiemenge, ITimeRange, Wertermittlungsverfahren, string, Mengeneinheit)"/>
        /// for pure Energiemengen within their own time range.
        /// </summary>
        /// <param name="em">Energiemenge</param>
        /// <returns><see cref="GetCompletenessReport(BO.Energiemenge, ITimeRange, Wertermittlungsverfahren, string, Mengeneinheit)"/></returns>
        public static CompletenessReport GetCompletenessReport(this BO4E.BO.Energiemenge em)
        {
            if (!em.IsPure())
            {
                throw new ArgumentException("The provided Energiemenge is not pure. Please use overloaded method GetCompletenessReport(... , wertermittlungsverfahren, obiskennzahl, mengeneinheit).");
            }
            Verbrauch v;

            try
            {
                v = em.Energieverbrauch.First <Verbrauch>();
            }
            catch (InvalidOperationException)
            {
                return(new CompletenessReport()
                {
                    Coverage = null,
                    LokationsId = em.LokationsId,
                    ErrorMessage = "energieverbrauch is empty"
                });
            }
            return(em.GetCompletenessReport(em.GetTimeRange(), v.Wertermittlungsverfahren, v.Obiskennzahl, v.Einheit));
        }
Beispiel #21
0
 /// <summary>
 /// Get consumption in given time reference frame. Trying to automatically determine parameters and forward to <see cref="BO4E.BO.Energiemenge.GetConsumption(BO.Energiemenge, TimeRange, Wertermittlungsverfahren, string, Mengeneinheit)"/>.
 /// </summary>
 /// <param name="em">Energiemenge</param>
 /// <param name="reference">time reference frame</param>
 /// <returns>Tuple of consumption value and automatically determined unit of measurement</returns>
 public static Tuple <decimal, Mengeneinheit> GetConsumption(this BO4E.BO.Energiemenge em, ITimeRange reference)
 {
     using (MiniProfiler.Current.Step(nameof(GetConsumption)))
     {
         if (!IsPure(em))
         {
             throw new ArgumentException("The Energiemenge is not pure.");
         }
         if (em.Energieverbrauch.Count == 0)
         {
             return(Tuple.Create <decimal, Mengeneinheit>(0.0M, Mengeneinheit.ANZAHL));
         }
         ISet <Mengeneinheit> einheiten = new HashSet <Mengeneinheit>(em.Energieverbrauch.Select(x => x.Einheit));
         if (einheiten.Count > 1)
         {
             // z.B. kWh und Wh oder Monat und Jahr... Die liefern IsPure==true.
             throw new NotImplementedException("Converting different units of same type is not supported yet.");
         }
         Verbrauch v           = em.Energieverbrauch.First <Verbrauch>();
         decimal   consumption = em.GetConsumption(reference, v.Wertermittlungsverfahren, v.Obiskennzahl, v.Einheit);
         return(Tuple.Create <decimal, Mengeneinheit>(consumption, v.Einheit));
     }
 }
        /// <summary>
        /// Returns a <see cref="PlausibilityReport"/> that compares <paramref name="emReference"/> with <paramref name="emOther"/>.
        /// within the interval defined in <paramref name="timeframe"/>.
        /// </summary>
        /// <param name="emReference">reference Energiemenge (reference = used for normalisation)</param>
        /// <param name="emOther">other Energiemenge</param>
        /// <param name="timeframe">time frame to be analysed. If null, the overlap of <paramref name="emReference"/> and <paramref name="emOther"/> is used.</param>
        /// <param name="ignoreLocation">By default (false) an ArgumentException is thrown if the <see cref="BO4E.BO.Energiemenge.LokationsId"/> do not match. Setting this flag suppresses the error.</param>
        /// <returns>a <see cref="PlausibilityReport"/></returns>
        public static PlausibilityReport GetPlausibilityReport(this BO4E.BO.Energiemenge emReference, BO4E.BO.Energiemenge emOther, ITimeRange timeframe = null, bool ignoreLocation = false)
        {
            using (MiniProfiler.Current.Step(nameof(GetPlausibilityReport)))
            {
                TimeRange trReference = emReference.GetTimeRange();
                TimeRange trOther     = emOther.GetTimeRange();
                if (timeframe == null)
                {
                    ITimeRange overlap = trReference.GetIntersection(trOther);
                    if (!ignoreLocation)
                    {
                        if (!(emReference.LokationsId == emOther.LokationsId && emReference.LokationsTyp == emOther.LokationsTyp))
                        {
                            throw new ArgumentException($"locations do not match! '{emReference.LokationsId}' ({emReference.LokationsTyp}) != '{emOther.LokationsId}' ({emOther.LokationsTyp})");
                        }
                    }
                    timeframe = overlap;
                }
                Tuple <decimal, Mengeneinheit> consumptionReference;
                Tuple <decimal, Mengeneinheit> consumptionOtherRaw;
                using (MiniProfiler.Current.Step("Get Consumptions for overlap:"))
                {
                    consumptionReference = emReference.GetConsumption(timeframe);
                    consumptionOtherRaw  = emOther.GetConsumption(timeframe);
                }

                Tuple <decimal, Mengeneinheit> consumptionOther;
                if (consumptionReference.Item2 != consumptionOtherRaw.Item2)
                {
                    // unit mismatch
                    if (consumptionReference.Item2.IsConvertibleTo(consumptionOtherRaw.Item2))
                    {
                        consumptionOther = new Tuple <decimal, Mengeneinheit>(consumptionOtherRaw.Item1 * consumptionOtherRaw.Item2.GetConversionFactor(consumptionReference.Item2), consumptionReference.Item2);
                    }
                    else
                    {
                        throw new ArgumentException($"The unit {consumptionOtherRaw.Item2} is not comparable to {consumptionReference.Item2}!");
                    }
                }
                else
                {
                    consumptionOther = consumptionOtherRaw;
                }

                decimal absoluteDeviation = consumptionOther.Item1 - consumptionReference.Item1;
                decimal?relativeDeviation;
                try
                {
                    relativeDeviation = absoluteDeviation / consumptionReference.Item1;
                }
                catch (DivideByZeroException)
                {
                    relativeDeviation = null;
                }

                Verbrauch vReference = emReference.Energieverbrauch.FirstOrDefault(); // copies obiskennzahl, wertermittlungsverfahren...
                vReference.Wert       = consumptionReference.Item1;
                vReference.Einheit    = consumptionReference.Item2;
                vReference.Startdatum = timeframe.Start;
                vReference.Enddatum   = timeframe.End;

                Verbrauch vOther = emOther.Energieverbrauch.FirstOrDefault(); // copies obiskennzahl, wertermittlungsverfahren...
                vOther.Wert       = consumptionOther.Item1;
                vOther.Einheit    = consumptionOther.Item2;
                vOther.Startdatum = timeframe.Start;
                vOther.Enddatum   = timeframe.End;

                var pr = new PlausibilityReport()
                {
                    LokationsId        = emReference.LokationsId,
                    ReferenceTimeFrame = new BO4E.COM.Zeitraum()
                    {
                        Startdatum = timeframe.Start, Enddatum = timeframe.End
                    },
                    VerbrauchReference       = vReference,
                    VerbrauchOther           = vOther,
                    AbsoluteDeviation        = Math.Abs(absoluteDeviation),
                    AbsoluteDeviationEinheit = consumptionReference.Item2
                };
                if (relativeDeviation.HasValue)
                {
                    pr.RelativeDeviation = Math.Round(relativeDeviation.Value, 4);
                }
                else
                {
                    pr.RelativeDeviation = null;
                }
                return(pr);
            }
        }
        /// <summary>
        /// Get Monthly Completeness Reports for overall time range defined in <paramref name="config"/>.
        /// </summary>
        /// <param name="em">Energiemenge</param>
        /// <param name="config">configuration that contains the overall time range in <see cref="PlausibilityReportConfiguration.Timeframe"/></param>
        /// <returns></returns>
        public static IDictionary <ITimeRange, PlausibilityReport> GetMonthlyPlausibilityReports(this BO4E.BO.Energiemenge em, PlausibilityReportConfiguration config)
        {
            if (config == null)
            {
                throw new ArgumentNullException(nameof(config));
            }
            if (config.Timeframe == null)
            {
                throw new ArgumentNullException(nameof(config.Timeframe));
            }
            var slices = GetLocalMonthlySlices(new TimeRange()
            {
                Start = config.Timeframe.Startdatum.Value,
                End   = config.Timeframe.Enddatum.Value
            });

            return(em.GetSlicedPlausibilityReports(config, slices));
        }
        /// <summary>
        /// creates a dictionary of completeness reports for a given list of reference time ranges.
        /// </summary>
        /// <param name="em">Energiemenge</param>
        /// <param name="ranges">list of ranges for which the completeness reports are generated</param>
        /// <returns></returns>
        public static IDictionary <ITimeRange, PlausibilityReport> GetSlicedPlausibilityReports(this BO4E.BO.Energiemenge em, PlausibilityReportConfiguration config, IEnumerable <ITimeRange> ranges)
        {
            if (ranges == null)
            {
                throw new ArgumentNullException(nameof(ranges), "list of time ranges must not be null");
            }
            Dictionary <ITimeRange, PlausibilityReport> result = new Dictionary <ITimeRange, PlausibilityReport>();

            foreach (var range in ranges)
            {
                var localConfig = JsonConvert.DeserializeObject <PlausibilityReportConfiguration>(JsonConvert.SerializeObject(config));
                localConfig.Timeframe = new Zeitraum()
                {
                    Startdatum = range.Start,
                    Enddatum   = range.End
                };
                var subResult = GetPlausibilityReport(em, localConfig);
                result.Add(range, subResult);
            }
            return(result);
        }
 /// <summary>
 /// same as <see cref="GetPlausibilityReport(BO.Energiemenge, BO.Energiemenge, ITimeRange, bool)"/> but with a strongly typed container as input.
 /// </summary>
 /// <param name="config">container containing the relevant data</param>
 /// <returns></returns>
 public static PlausibilityReport GetPlausibilityReport(this BO4E.BO.Energiemenge energiemenge, PlausibilityReport.PlausibilityReportConfiguration config)
 {
     return(energiemenge.GetPlausibilityReport(config.Other, new TimeRange(config.Timeframe.Startdatum.Value, config.Timeframe.Enddatum.Value), config.IgnoreLocation));
 }
Beispiel #26
0
        /// <summary>
        /// Generate a <see cref="CompletenessReport"/> for the given parameters.
        /// </summary>
        /// <param name="em">Energiemenge</param>
        /// <param name="reference">reference time frame</param>
        /// <param name="wev">Wertermittlungsverfahren</param>
        /// <param name="obiskennzahl">OBIS Kennzahl</param>
        /// <param name="einheit">Mengeneinheit</param>
        /// <returns>the completeness report</returns>
        public static CompletenessReport GetCompletenessReport(this BO4E.BO.Energiemenge em, ITimeRange reference, Wertermittlungsverfahren wev, string obiskennzahl, Mengeneinheit einheit)
        {
            CompletenessReport result;

            using (MiniProfiler.Current.Step("create completeness report skeleton + find the coverage"))
            {
                result = new CompletenessReport
                {
                    LokationsId = em.LokationsId,
                    Einheit     = einheit,
                    Coverage    = GetCoverage(em, reference, wev, obiskennzahl, einheit),
                    wertermittlungsverfahren = wev,
                    Obiskennzahl             = obiskennzahl,
                    ReferenceTimeFrame       = new Zeitraum
                    {
                        Startdatum = DateTime.SpecifyKind(reference.Start, DateTimeKind.Utc),
                        Enddatum   = DateTime.SpecifyKind(reference.End, DateTimeKind.Utc)
                    },
                };
            }
            if (em.Energieverbrauch != null && em.Energieverbrauch.Count > 0)
            {
                /*using (MiniProfiler.Current.Step("populating time slices of/with missing/null values"))
                 * {
                 *  result.values = em.GetMissingTimeRanges(reference, wev, obis, einheit)
                 *      .Select(mtr => new CompletenessReport.BasicVerbrauch
                 *      {
                 *          startdatum = DateTime.SpecifyKind(mtr.Start, DateTimeKind.Utc),
                 *          enddatum = DateTime.SpecifyKind(mtr.End, DateTimeKind.Utc),
                 *          wert = null
                 *      }).ToList<CompletenessReport.BasicVerbrauch>();
                 * }
                 * using (MiniProfiler.Current.Step("populating time slices existing values"))
                 * {
                 *  result.values.AddRange(
                 *  em.energieverbrauch
                 *  //.AsParallel<Verbrauch>()
                 *  .Where(v => v.obiskennzahl == obis && v.einheit == einheit && v.wertermittlungsverfahren == wev)
                 *  .Select(v => new CompletenessReport.BasicVerbrauch
                 *  {
                 *      startdatum = DateTime.SpecifyKind(v.startdatum, DateTimeKind.Utc),
                 *      enddatum = DateTime.SpecifyKind(v.enddatum, DateTimeKind.Utc),
                 *      wert = v.wert
                 *  })
                 *  .ToList<CompletenessReport.BasicVerbrauch>());
                 * }*/
                using (MiniProfiler.Current.Step("Setting aggregated gaps"))
                {
                    var        nonNullValues = new TimePeriodCollection(em.Energieverbrauch.Select(v => new TimeRange(v.Startdatum, v.Enddatum)));
                    ITimeRange limits;
                    if (result.ReferenceTimeFrame != null && result.ReferenceTimeFrame.Startdatum.HasValue && result.ReferenceTimeFrame.Enddatum.HasValue)
                    {
                        limits = new TimeRange(result.ReferenceTimeFrame.Startdatum.Value, result.ReferenceTimeFrame.Enddatum.Value);
                    }
                    else
                    {
                        limits = null;
                    }
                    var gaps = (new TimeGapCalculator <TimeRange>()).GetGaps(nonNullValues, limits: limits);
                    result.Gaps = gaps.Select(gap => new CompletenessReport.BasicVerbrauch()
                    {
                        Startdatum = gap.Start,
                        Enddatum   = gap.End,
                        Wert       = null
                    }).ToList();
                }

                /*using (MiniProfiler.Current.Step("sorting result"))
                 * {
                 *  result.values.Sort(new BasicVerbrauchDateTimeComparer());
                 * }*/
                if (em.IsPure(checkUserProperties: true))
                {
                    try
                    {
                        foreach (var kvp in em.Energieverbrauch.Where(v => v.UserProperties != null).SelectMany(v => v.UserProperties))
                        {
                            if (result.UserProperties == null)
                            {
                                result.UserProperties = new Dictionary <string, JToken>();
                            }
                            if (!result.UserProperties.ContainsKey(kvp.Key))
                            {
                                result.UserProperties.Add(kvp.Key, kvp.Value);
                            }
                        }
                    }
                    catch (InvalidOperationException)
                    {
                        // ok, there's no Verbrauch with user properties.
                    }
                }
            }

            /*else
             * {
             *  result.coverage = null;
             *  result._errorMessage = "energieverbrauch is empty";
             * }*/
            return(result);
        }
Beispiel #27
0
        /// <summary>
        /// Get Monthly Completeness Reports for <paramref name="overallTimeRange"/>.
        /// </summary>
        /// <param name="em">Energiemenge</param>
        /// <param name="overallTimeRange"></param>
        /// <param name="useParallelExecution">set true to internally use parallel linq</param>
        /// <returns></returns>
        public static IDictionary <ITimeRange, CompletenessReport> GetMonthlyCompletenessReports(this BO4E.BO.Energiemenge em, ITimeRange overallTimeRange, bool useParallelExecution = false)
        {
            var slices = GetLocalMonthlySlices(overallTimeRange);

            return(em.GetSlicedCompletenessReports(slices, useParallelExecution));
        }
Beispiel #28
0
 /// <summary>
 /// Generate a <see cref="CompletenessReport"/> for the given configuration. Same as <see cref="GetCompletenessReport(BO.Energiemenge, TimeRange, Wertermittlungsverfahren, string, Mengeneinheit)"/> but with all parameters in a configuration container instead of loose arguments.
 /// </summary>
 /// <param name="em">Energiemenge</param>
 /// <param name="config">configuration container</param>
 /// <returns></returns>
 public static CompletenessReport GetCompletenessReport(this BO4E.BO.Energiemenge em, CompletenessReport.CompletenessReportConfiguration config)
 {
     return(em.GetCompletenessReport(new TimeRange(config.ReferenceTimeFrame.Startdatum.Value, config.ReferenceTimeFrame.Enddatum.Value), config.Wertermittlungsverfahren, config.Obis, config.Einheit));
 }
Beispiel #29
0
 /// <summary>
 /// Same as <see cref="GetTotalConsumption(BO.Energiemenge, Wertermittlungsverfahren, string, Mengeneinheit)"/> but without autodetected 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 BO4E.BO.Energiemenge em)
 {
     return(GetConsumption(em, new TimeRange(em.GetMinDate().UtcDateTime, em.GetMaxDate().UtcDateTime)));
 }
Beispiel #30
0
 /// <summary>
 /// Test if the energiemenge contains only extensive consumption units
 /// </summary>
 /// <param name="em">Energiemenge</param>
 /// <returns>true iff all <paramref name="em"/>-&gt;energieverbrauch entries are extensive</returns>
 public static bool IsExtensive(this BO4E.BO.Energiemenge em)
 {
     return(em.IsPureMengeneinheit() && em.Energieverbrauch.First <Verbrauch>().Einheit.IsExtensive());
 }