У меня есть несколько датчиков окружающей среды, и я хочу обнаруживать внезапные изменения температуры и медленные тенденции с течением времени... однако я хотел бы выполнять большую часть математических операций на основе того, что находится в памяти, с параметрами, которые могут выглядеть следующим образом: (с учетом сдача)
(примечание: элементы в скобках вычисляются в реальном времени по мере добавления данных)
- 5 минут (производная, максимум, минимум, среднее) + 36 точек данных за последние 3 часа
- 10 минут (производная, максимум, минимум, среднее) + 0 точек данных, расчет основан на 5-минутной выборке
- 30 минут (производная, максимум, минимум, среднее) + 0 точек данных, расчет основан на 5-минутной выборке
- Почасовая (производная, макс., мин., средняя) + 24 точки данных за последний 1 день
- Ежедневно (производная, макс, мин, среднее) + 32 точки данных за последний месяц
- Ежемесячно (производная, макс, мин, среднее) + 12 точек данных за прошлый год
Каждая точка данных представляет собой двухбайтовое число с плавающей запятой. Таким образом, каждый датчик будет потреблять до 124 чисел с плавающей запятой плюс 24 вычисляемых переменных. Я хотел бы поддерживать столько датчиков, сколько позволяет встроенное устройство .NET.
Поскольку я использую встроенное устройство для этого проекта, моя память ограничена, а также мощность ввода-вывода и процессора.
Как бы вы реализовали это в .NET? До сих пор я создал пару структур и назвал их «TrackableFloat
», где вставка значения приводит к тому, что старое удаляется из массива, и выполняется пересчет.
Единственное, что делает это более сложным, чем могло бы быть, это то, что для любого датчика не возвращаются данные, тогда эта точка данных должна быть исключена/игнорирована из всех последующих вычислений в реальном времени.
Когда все сказано и сделано, если какое-либо из значений: (производное, максимальное, минимальное, среднее) достигает предварительно определенного значения, тогда срабатывает событие .NET.
Я думаю, что кто-то сочтет это интересной проблемой и хотел бы услышать, как они подошли бы к ее реализации.
Вы бы использовали класс или структуру?
Как бы вы инициировали расчеты? (События наиболее вероятны)
Как будут срабатывать оповещения?
Как бы вы хранили данные по уровням?
Есть ли библиотека, которая уже делает что-то подобное? (возможно, это должен быть мой первый вопрос)
Как бы вы эффективно вычислили производную?
Вот мой первый взлом, и он не полностью соответствует спецификации, но очень эффективен. Было бы интересно услышать ваши мысли.
enum UnitToTrackEnum
{
Minute,
FiveMinute,
TenMinute,
FifteenMinute,
Hour,
Day,
Week,
Month,
unknown
}
class TrackableFloat
{
object Gate = new object();
UnitToTrackEnum trackingMode = UnitToTrackEnum.unknown;
int ValidFloats = 0;
float[] FloatsToTrack;
public TrackableFloat(int HistoryLength, UnitToTrackEnum unitToTrack)
{
if (unitToTrack == UnitToTrackEnum.unknown)
throw new InvalidOperationException("You must not have an unknown measure of time to track.");
FloatsToTrack = new float[HistoryLength];
foreach (var i in FloatsToTrack)
{
float[i] = float.MaxValue;
}
trackingMode = unitToTrack;
Min = float.MaxValue;
Max = float.MinValue;
Sum = 0;
}
public void Add(DateTime dt, float value)
{
int RoundedDTUnit = 0;
switch (trackingMode)
{
case UnitToTrackEnum.Minute:
{
RoundedDTUnit = dt.Minute;
break;
}
case UnitToTrackEnum.FiveMinute:
{
RoundedDTUnit = System.Math.Abs(dt.Minute / 5);
break;
}
case UnitToTrackEnum.TenMinute:
{
RoundedDTUnit = System.Math.Abs(dt.Minute / 10);
break;
}
case UnitToTrackEnum.FifteenMinute:
{
RoundedDTUnit = System.Math.Abs(dt.Minute / 15);
break;
}
case UnitToTrackEnum.Hour:
{
RoundedDTUnit = dt.Hour;
break;
}
case UnitToTrackEnum.Day:
{
RoundedDTUnit = dt.Day;
break;
}
case UnitToTrackEnum.Week:
{
//RoundedDTUnit = System.Math.Abs( );
break;
}
case UnitToTrackEnum.Month:
{
RoundedDTUnit = dt.Month;
break;
}
case UnitToTrackEnum.unknown:
{
throw new InvalidOperationException("You must not have an unknown measure of time to track.");
}
default:
break;
}
bool DoRefreshMaxMin = false;
if (FloatsToTrack.Length < RoundedDTUnit)
{
if (value == float.MaxValue || value == float.MinValue)
{
// If invalid data...
lock (Gate)
{
// Get rid of old data...
var OldValue = FloatsToTrack[RoundedDTUnit];
if (OldValue != float.MaxValue || OldValue != float.MinValue)
{
Sum -= OldValue;
ValidFloats--;
if (OldValue == Max || OldValue == Min)
DoRefreshMaxMin = true;
}
// Save new data
FloatsToTrack[RoundedDTUnit] = value;
}
}
else
{
lock (Gate)
{
// Get rid of old data...
var OldValue = FloatsToTrack[RoundedDTUnit];
if (OldValue != float.MaxValue || OldValue != float.MinValue)
{
Sum -= OldValue;
ValidFloats--;
}
// Save new data
FloatsToTrack[RoundedDTUnit] = value;
Sum += value;
ValidFloats++;
if (value < Min)
Min = value;
if (value > Max)
Max = value;
if (OldValue == Max || OldValue == Min)
DoRefreshMaxMin = true;
}
}
// Function is placed here to avoid a deadlock
if (DoRefreshMaxMin == true)
RefreshMaxMin();
}
else
{
throw new IndexOutOfRangeException("Index " + RoundedDTUnit + " is out of range for tracking mode: " + trackingMode.ToString());
}
}
public float Sum { get; set; }
public float Average
{
get
{
if (ValidFloats > 0)
return Sum / ValidFloats;
else
return float.MaxValue;
}
}
public float Min { get; set; }
public float Max { get; set; }
public float Derivative { get; set; }
public void RefreshCounters()
{
lock (Gate)
{
float sum = 0;
ValidFloats = 0;
Min = float.MaxValue;
Max = float.MinValue;
foreach (var i in FloatsToTrack)
{
if (i != float.MaxValue || i != float.MinValue)
{
if (Min == float.MaxValue)
{
Min = i;
Max = i;
}
sum += i;
ValidFloats++;
if (i < Min)
Min = i;
if (i > Max)
Max = i;
}
}
Sum = sum;
}
}
public void RefreshMaxMin()
{
if (ValidFloats > 0)
{
Min = float.MaxValue;
Max = float.MinValue;
lock (Gate)
{
foreach (var i in FloatsToTrack)
{
if (i != float.MaxValue || i != float.MinValue)
{
if (i < Min)
Min = i;
if (i > Max)
Max = i;
}
}
}
}
}
}