我有一些环境传感器,我想检测温度的突然变化,以及随着时间的推移缓慢的趋势......但是我想根据内存中的参数进行大部分数学计算,参数可能如下所示:(取决于改变)
(注:括号中的项目是在添加数据时实时计算的)
- 5 分钟(导数、最大值、最小值、平均值)+ 最近 3 小时的 36 个数据点
- 10 分钟(导数、最大值、最小值、平均值)+ 0 个数据点,计算基于 5 分钟样本
- 30 分钟(导数、最大值、最小值、平均值)+ 0 个数据点,计算基于 5 分钟样本
- 每小时(导数、最大值、最小值、平均值)+ 最近 1 天的 24 个数据点
- 每日(导数、最大值、最小值、平均值)+ 最近一个月的 32 个数据点
- 每月(导数、最大值、最小值、平均值)+过去一年的 12 个数据点
每个数据点都是一个两字节浮点数。因此每个传感器将消耗最多 124 个浮点,加上 24 个计算变量。我希望支持 .NET 嵌入式设备允许的尽可能多的传感器。
由于我在这个项目中使用嵌入式设备,因此我的内存以及 IO 和 CPU 能力都受到限制。
您将如何在 .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;
}
}
}
}
}
}