using System.Collections.Concurrent;
using Fleet.Core;
using Monitor.Protocol;
namespace Monitor.Backend.Application;
/// Agent 眼中的一个已连接 Probe(Agent 在事件和心跳里上报)。
public sealed record ConnectedProbeInfo(string Id, string? Version, string? Remote, long SinceMs, PlatformInfo? Platform = null);
/// Agent 的运行时状态(只在内存中):连着哪些 Probe、最近的进程内计数、端点变化次数。
public sealed class AgentRuntimeState
{
private readonly object _gate = new();
private int _endpointChanges;
private IReadOnlyList _probes = [];
/// 当前连着这个 Agent 的 Probe(每个 Probe 一项)。
public IReadOnlyList Probes => Volatile.Read(ref _probes);
public bool ProbeConnected => Probes.Count > 0;
public bool IsConnected(string probeId) => Probes.Any(p => p.Id == probeId);
/// 事件 probe_connected:事件里带着 Probe 的版本和地址;心跳里的完整快照随后会再校正一次。
public void AddProbe(ConnectedProbeInfo info)
{
lock (_gate)
{
Volatile.Write(ref _probes, [.. _probes.Where(p => p.Id != info.Id), info]);
}
}
public void RemoveProbe(string id)
{
lock (_gate)
{
Volatile.Write(ref _probes, [.. _probes.Where(p => p.Id != id)]);
}
}
/// 心跳里的完整快照:覆盖事件推算出的结果。
public void ReplaceProbes(IEnumerable probes)
{
lock (_gate)
{
Volatile.Write(ref _probes, [.. probes.OrderBy(p => p.SinceMs).ThenBy(p => p.Id, StringComparer.Ordinal)]);
}
}
public void ClearProbes() => ReplaceProbes([]);
/// 最新的主机资源占用快照,以及收到它的时间。
public HostSnapshot? Host { get; set; }
public long HostReceivedAtMs { get; set; }
public AgentHeartbeatExtra? LastHeartbeat { get; set; }
public long LastHeartbeatAtMs { get; set; }
public int EndpointChanges => Volatile.Read(ref _endpointChanges);
public void CountEndpointChange() => Interlocked.Increment(ref _endpointChanges);
}
public sealed class AgentRuntime
{
private readonly ConcurrentDictionary _states = new();
public AgentRuntimeState Get(NodeId agent) => _states.GetOrAdd(agent, _ => new AgentRuntimeState());
public bool TryGet(NodeId agent, out AgentRuntimeState state) => _states.TryGetValue(agent, out state!);
public void Remove(NodeId agent) => _states.TryRemove(agent, out _);
/// 某个 Probe 现在连着哪些 Agent。
public IReadOnlyList<(NodeId Agent, ConnectedProbeInfo Probe)> ConnectionsOf(string probeId)
{
var result = new List<(NodeId, ConnectedProbeInfo)>();
foreach ((NodeId agent, AgentRuntimeState state) in _states)
{
foreach (ConnectedProbeInfo probe in state.Probes)
{
if (probe.Id == probeId)
{
result.Add((agent, probe));
}
}
}
return result;
}
/// Agent 断开时,它对 Probe 会话的认知也随之失效。
public void MarkOffline(NodeId agent)
{
if (_states.TryGetValue(agent, out AgentRuntimeState? state))
{
state.ClearProbes();
}
}
}