move data stores into their own folders like the go project
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
using System.Collections.Concurrent;
|
||||
|
||||
namespace SafelyYou.Data;
|
||||
|
||||
public readonly record struct DeviceId(string Value);
|
||||
|
||||
|
||||
public sealed class KnownDevices
|
||||
{
|
||||
private readonly HashSet<DeviceId> _knownDevices;
|
||||
|
||||
public KnownDevices(HashSet<DeviceId> knownDevices) => _knownDevices = knownDevices;
|
||||
|
||||
public bool Contains(DeviceId deviceId) => _knownDevices.Contains(deviceId);
|
||||
}
|
||||
|
||||
public sealed class DeviceHeartBeatHistory
|
||||
{
|
||||
private readonly Lock _lock = new();
|
||||
|
||||
public enum AddResult { Added, OutOfSequence }
|
||||
|
||||
public long NumberOfHeartBeats { get; private set; }
|
||||
public DateTimeOffset? FirstHeartBeat { get; private set; }
|
||||
public DateTimeOffset? LastHeartBeat { get; private set; }
|
||||
|
||||
|
||||
public AddResult Add(DateTimeOffset sentAt)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
// Reject heartbeats that arrive before the latest registered time.
|
||||
// Gaps are fine: uptime can be derived from first, last, and count.
|
||||
if (LastHeartBeat is null)
|
||||
{
|
||||
FirstHeartBeat = sentAt;
|
||||
LastHeartBeat = sentAt;
|
||||
NumberOfHeartBeats = 1;
|
||||
return AddResult.Added;
|
||||
}
|
||||
|
||||
if (sentAt < LastHeartBeat)
|
||||
return AddResult.OutOfSequence;
|
||||
|
||||
// note: does not account for rejecting heartbeats that come between the one minute window.
|
||||
LastHeartBeat = sentAt;
|
||||
NumberOfHeartBeats++;
|
||||
return AddResult.Added;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class HeartBeatHistories
|
||||
{
|
||||
private readonly ConcurrentDictionary<DeviceId, DeviceHeartBeatHistory> _histories = new();
|
||||
private readonly Lock _lock = new ();
|
||||
|
||||
public DeviceHeartBeatHistory.AddResult Add(DeviceId deviceId, DateTimeOffset sentAt)
|
||||
{
|
||||
if (!_histories.TryGetValue(deviceId, out var history))
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
history = new();
|
||||
_histories[deviceId] = history;
|
||||
}
|
||||
}
|
||||
|
||||
return history.Add(sentAt);
|
||||
}
|
||||
|
||||
public double Uptime(DeviceId deviceId)
|
||||
{
|
||||
if (!_histories.TryGetValue(deviceId, out var history)) return 0;
|
||||
|
||||
var heartBeatMinutes =
|
||||
(history.FirstHeartBeat, history.LastHeartBeat) switch
|
||||
{
|
||||
({} first, {} second ) => (second - first).TotalMinutes,
|
||||
(not null, null) => 1,
|
||||
(null, _) => 0
|
||||
};
|
||||
|
||||
if (heartBeatMinutes == 0) return 0L;
|
||||
|
||||
var uptime = (history.NumberOfHeartBeats / heartBeatMinutes) * 100L;
|
||||
return uptime;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user