Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 6 additions & 21 deletions CumulusMX.Extensions/DataReporter/DataReporterSettingsBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,17 @@

namespace CumulusMX.Extensions.DataReporter
{
public class DataReporterSettingsBase : IDataReporterSettings
public abstract class DataReporterSettingsBase : IDataReporterSettings
{
public int GetValue(string key, int defaultValue)
{
throw new System.NotImplementedException();
}
public abstract int GetValue(string key, int defaultValue);

public string GetValue(string key, string defaultValue)
{
throw new System.NotImplementedException();
}
public abstract string GetValue(string key, string defaultValue);

public double GetValue(string key, double defaultValue)
{
throw new System.NotImplementedException();
}
public abstract double GetValue(string key, double defaultValue);

public byte[] GetValue(string key, byte[] defaultValue)
{
throw new System.NotImplementedException();
}
public abstract byte[] GetValue(string key, byte[] defaultValue);

public bool GetValue(string key, bool defaultValue)
{
throw new System.NotImplementedException();
}
public abstract bool GetValue(string key, bool defaultValue);

public Setting this[string key] => new Setting(GetValue(key,string.Empty));
}
Expand Down
2 changes: 1 addition & 1 deletion CumulusMX.Extensions/Station/IStatistic.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ namespace CumulusMX.Extensions.Station
{
public interface IStatistic<TBase>
{
void Add(TBase sample);
void Add(DateTime timestamp, TBase sample);
TBase Latest { get; }
TBase OneHourMaximum { get; }
TBase ThreeHourMaximum { get; }
Expand Down
2 changes: 1 addition & 1 deletion CumulusMX.Extensions/Station/IWeatherDataStatistics.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public interface IWeatherDataStatistics
IStatistic<Speed> RainRate { get; set; }
IStatistic<Length> Rain { get; set; }
IStatistic<Irradiance> SolarRadiation { get; set; }
IStatistic<int> UvIndex { get; set; }
IStatistic<double> UvIndex { get; set; }
Dictionary<string,IStatistic<IQuantity>> Extra { get; set; }


Expand Down
1 change: 1 addition & 0 deletions CumulusMX/CumulusMX.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.2</TargetFramework>
<LangVersion>7.3</LangVersion>
</PropertyGroup>

<ItemGroup>
Expand Down
119 changes: 119 additions & 0 deletions CumulusMX/Data/MaxMinAverageUnit.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
using System;
using System.Dynamic;
using Autofac.Core.Activators.Reflection;
using UnitsNet;

namespace CumulusMX.Data
{
internal class MaxMinAverageUnit<TBase, TUnitType>
where TUnitType : Enum where TBase : IComparable, IQuantity<TUnitType>
{
private int _count = 0;
private int _nonZero = 0;
private TBase _minimum;
private TBase _maximum;

public TBase Minimum
{
get
{
if (_count == 0)
throw new InvalidOperationException("An empty set has no minimum.");
return _minimum;
}
private set
{
_minimum = value;
}

}

public TBase Maximum
{
get
{
if (_count == 0)
throw new InvalidOperationException("An empty set has no maximum.");
return _maximum;
}
private set
{
_maximum = value;
}

}

public TBase Average
{
get
{
if (_count == 0)
throw new DivideByZeroException("Cannot get the average of 0 values.");
if (!_averageValid) CalculateAverage();
return _average;
}
}

public TBase Total
{
get
{
if (!_unitTotalValid) CreateTotal();
return _unitTotal;
}
}

public int NonZero => _nonZero;

private TBase _average;
private bool _averageValid = false;
private double _total;
private TBase _unitTotal;
private bool _unitTotalValid = false;

private readonly TUnitType _itemOne;
private readonly TBase _zeroQuantity;

public MaxMinAverageUnit()
{
_itemOne = (TUnitType) Enum.ToObject(typeof(TUnitType), 1);
_zeroQuantity = (TBase)Activator.CreateInstance(typeof(TBase), 0, _itemOne);
Reset();
}

public void Reset()
{
_count = 0;
_total = 0;
_averageValid = false;
_unitTotal = _zeroQuantity;
_unitTotalValid = true;
_nonZero = 0;
}

public void AddValue(TBase newValue)
{
_count++;
_total += newValue.As(_itemOne);
if (newValue.CompareTo(Maximum) > 0 || _count == 1) Maximum = newValue;
if (newValue.CompareTo(Minimum) < 0 || _count == 1) Minimum = newValue;
_averageValid = false;
_unitTotalValid = false;
if (newValue.CompareTo(_zeroQuantity) != 0)
_nonZero++;
}

private void CalculateAverage()
{
double avgValue = _total / _count;
_average = (TBase) Activator.CreateInstance(typeof(TBase), avgValue, _itemOne);
_averageValid = true;
}

private void CreateTotal()
{
_unitTotal = (TBase)Activator.CreateInstance(typeof(TBase), _total, _itemOne);
_unitTotalValid = true;
}
}
}
185 changes: 185 additions & 0 deletions CumulusMX/Data/StatisticUnit.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using CumulusMX.Extensions.Station;
using UnitsNet;
using Unosquare.Swan;

namespace CumulusMX.Data
{
public class StatisticUnit<TBase,TUnitType> : IStatistic<TBase> where TBase : IComparable, IQuantity<TUnitType> where TUnitType : Enum
{
private Dictionary<DateTime, TBase> _sampleHistory = new Dictionary<DateTime, TBase>();
private DateTime _lastSampleTime = DateTime.Now;
private TBase _lastValue;
private MaxMinAverageUnit<TBase, TUnitType> _day;
private MaxMinAverageUnit<TBase, TUnitType> _yesterday;
private MaxMinAverageUnit<TBase, TUnitType> _month;
private MaxMinAverageUnit<TBase, TUnitType> _lastMonth;
private MaxMinAverageUnit<TBase, TUnitType> _year;
private MaxMinAverageUnit<TBase, TUnitType> _lastYear;
private double _oneHourChange;
private double _threeHourChange;
private double _24HourTotal;
private TBase _oneHourMaximum;
private TBase _threeHourMaximum;

private TimeSpan _dayNonZero = TimeSpan.Zero;
private TimeSpan _monthNonZero = TimeSpan.Zero;
private TimeSpan _yearNonZero = TimeSpan.Zero;

private readonly TUnitType _itemOne;
private readonly TBase _zeroQuantity;

public StatisticUnit()
{
_itemOne = (TUnitType)Enum.ToObject(typeof(TUnitType), 1);
_zeroQuantity = (TBase)Activator.CreateInstance(typeof(TBase), 0, _itemOne);

}

/// <summary>
/// Adds a new sample to the statistics set, and updates aggregates. This will be called a lot, it needs to be fast.
/// </summary>
/// <param name="timestamp">The DateTime when the observation was taken.</param>
/// <param name="sample">The observed value.</param>
public void Add(DateTime timestamp,TBase sample)
{
if (timestamp.Year > _lastSampleTime.Year)
ResetYearValues();
if (timestamp.Year * 12 + timestamp.Month > _lastSampleTime.Year * 12 + _lastSampleTime.Month)
ResetMonthValues();
if (timestamp.DayOfYear != _lastSampleTime.DayOfYear)
ResetDayValues();

_sampleHistory.Add(timestamp,sample);
_lastValue = sample;

_day.AddValue(sample);
_month.AddValue(sample);
_year.AddValue(sample);

// Update rolling 24 hour total
var rolledOff = _sampleHistory
.Where(x => x.Key >= _lastSampleTime.AddHours(-24) && x.Key < timestamp.AddHours(-24));

foreach (var oldValue in rolledOff)
{
_24HourTotal -= oldValue.Value.As(_itemOne);
}
_24HourTotal += sample.As(_itemOne);

UpdateMaximumAndChange(3, sample, _lastSampleTime, timestamp, ref _threeHourChange, ref _threeHourMaximum);
UpdateMaximumAndChange(1, sample, _lastSampleTime, timestamp, ref _oneHourChange, ref _oneHourMaximum);

if (sample.CompareTo(_zeroQuantity) != 0)
{
var newSpan = timestamp - _lastSampleTime;
_dayNonZero += newSpan;
_monthNonZero += newSpan;
_yearNonZero += newSpan;
}

_lastSampleTime = timestamp;
}

private void UpdateMaximumAndChange(int months, TBase sample, DateTime lastSample, DateTime thisSample, ref double change, ref TBase maximum)
{
var oldSamples = _sampleHistory.Where(x => x.Key >= lastSample.AddHours(-1 * months)).OrderBy(x => x.Key);
if (!oldSamples.Any())
return;

change = sample.As(_itemOne) - oldSamples.First().Value.As(_itemOne);

var rolledOff = _sampleHistory
.Where(x => x.Key >= lastSample.AddHours(-1*months) && x.Key < thisSample.AddHours(-1*months));

if (sample.CompareTo(maximum) > 0)
maximum = sample;
else
{
bool maxInvalid = false;
foreach (var oldValue in rolledOff)
{
if (oldValue.Value.Equals(maximum))
maxInvalid = true;
}

if (!maxInvalid) return;

var historyWindow = _sampleHistory
.Where(x => x.Key > thisSample.AddHours(-1 * months) && x.Key <= thisSample);
maximum = sample;
foreach (var oldSample in historyWindow)
{
if (oldSample.Value.CompareTo(maximum) > 0)
maximum = oldSample.Value;
}
}
}

private void ResetDayValues()
{
_yesterday = _day.DeepClone();
_day.Reset();
_dayNonZero = TimeSpan.Zero;
}

private void ResetMonthValues()
{
_lastMonth = _month.DeepClone();
_month.Reset();
_monthNonZero = TimeSpan.Zero;
}

private void ResetYearValues()
{
_lastYear = _year.DeepClone();
_year.Reset();
_yearNonZero = TimeSpan.Zero;
}

public TBase Latest => _lastValue;

public TBase OneHourMaximum => _oneHourMaximum;

public TBase ThreeHourMaximum => _threeHourMaximum;

public TBase OneHourChange => (TBase)Activator.CreateInstance(typeof(TBase), _oneHourChange, _itemOne);

public TBase ThreeHourChange => (TBase)Activator.CreateInstance(typeof(TBase), _threeHourChange, _itemOne);

public TBase DayMaximum => _day.Maximum;

public TBase DayMinimum => _day.Minimum;

public TBase DayAverage => _day.Average;

public TBase MonthMaximum => _month.Maximum;

public TBase MonthMinimum => _month.Minimum;

public TBase MonthAverage => _month.Average;

public TBase YearMaximum => _year.Maximum;

public TBase YearMinimum => _year.Minimum;

public TBase YearAverage => _year.Average;

public TBase Last24hTotal => (TBase)Activator.CreateInstance(typeof(TBase), _24HourTotal, _itemOne);

public TBase DayTotal => _day.Total;

public TBase MonthTotal => _month.Total;

public TBase YearTotal => _year.Total;

public TimeSpan DayNonZero => _dayNonZero;

public TimeSpan MonthNonZero => _monthNonZero;

public TimeSpan YearNonZero => _yearNonZero;
}
}
Loading