У меня есть несколько датчиков окружающей среды, и я хочу обнаруживать внезапные изменения температуры и медленные тренды с течением времени ... однако я хотел бы выполнить большую часть математических операций, основываясь на том, что находится в памяти, с параметрами, которые могут выглядеть следующим образом: (возможны изменения)
(примечание: элементы в скобках рассчитываются в режиме реального времени при добавлении данных)
- 5 минут (производная, максимальная, минимальная, средняя) + 36 точек данных за последние 3 часа
- 10 минут (производная, максимальная, минимальная, средняя) + 0 точек данных, расчет основан на 5-минутной выборке
- 30 минут (производная, максимальная, минимальная, средняя) + 0 точек данных, расчет основан на 5-минутной выборке
- Ежечасно (производная, макс., Мин., Средн.) + 24 точки данных за самый текущий 1 день
- Ежедневно (производная, макс., Мин., Средн.) + 32 точки данных за самый текущий месяц
- Ежемесячно (производная, максимальная, минимальная, средняя) + 12 точек данных за прошлый год
Каждая точка данных представляет собой двухбайтовое число с плавающей запятой. Таким образом, каждый датчик будет потреблять до 124 значений с плавающей запятой плюс 24 вычисляемых переменных. Я бы хотел поддерживать столько датчиков, сколько позволяет устройство .NET.
Так как я использую встроенное устройство для этого проекта, моя память ограничена, так же как и мой ввод-вывод и мощность процессора.
Как бы вы реализовали это в .NET? До сих пор я создал пару структур и назвал их «TrackableFloat
», когда вставка значения приводит к тому, что старое значение выпадает из массива, и выполняется пересчет.
Единственное, что делает это более
сложнее, чем было бы, это то, что
ни по одному датчику не отчитывается
данные, то эта точка данных должна быть
исключено / проигнорировано из всех последующих
вычисления в реальном времени.
Когда все сказано и сделано, если какое-либо из значений: (производная, макс., Мин., Avg) достигнет предопределенного значения, событие .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;
}
}
}
}
}
}