a502322188
손상된 .git 히스토리(missing tree)로 재초기화 후 작업트리 전체 커밋. .claude/ 만 제외(로컬 에이전트 설정). 구 저장소 백업(.git_corrupt_backup/) 포함. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
56 lines
2.1 KiB
C#
56 lines
2.1 KiB
C#
using System.Collections.Concurrent;
|
|
using System.Net.Sockets;
|
|
using ErvProtocol;
|
|
|
|
namespace ErvCollector.Storage
|
|
{
|
|
// 현장별 활성 소켓 + 최신 상태 보관. 제어 프레임을 해당 현장 EW11 소켓으로 송신.
|
|
public sealed class SiteHub
|
|
{
|
|
readonly ConcurrentDictionary<string, NetworkStream> _sockets = new();
|
|
readonly ConcurrentDictionary<string, StatusRecord> _last = new();
|
|
readonly ConcurrentDictionary<string, DateTime> _lastSeenUtc = new();
|
|
|
|
public event Action<string>? Log;
|
|
|
|
public void SetSocket(string site, NetworkStream s) { _sockets[site] = s; }
|
|
public void RemoveSocket(string site, NetworkStream s)
|
|
{
|
|
// 현재 등록된 것이 이 스트림일 때만 제거(재연결 레이스 방지)
|
|
if (_sockets.TryGetValue(site, out var cur) && ReferenceEquals(cur, s))
|
|
_sockets.TryRemove(site, out _);
|
|
}
|
|
|
|
public void SetStatus(string site, StatusRecord rec)
|
|
{
|
|
_last[site] = rec;
|
|
_lastSeenUtc[site] = DateTime.UtcNow;
|
|
}
|
|
|
|
public StatusRecord? GetStatus(string site) => _last.TryGetValue(site, out var r) ? r : null;
|
|
public DateTime LastSeen(string site) => _lastSeenUtc.TryGetValue(site, out var t) ? t : DateTime.MinValue;
|
|
public bool IsOnline(string site) =>
|
|
_sockets.ContainsKey(site) && (DateTime.UtcNow - LastSeen(site)) < TimeSpan.FromSeconds(30);
|
|
|
|
// 제어 프레임 송신. 성공 true.
|
|
public bool TrySend(string site, byte[] frame)
|
|
{
|
|
if (!_sockets.TryGetValue(site, out var stream))
|
|
{
|
|
Log?.Invoke($"[{site}] 제어 실패: 연결 없음");
|
|
return false;
|
|
}
|
|
try
|
|
{
|
|
lock (stream) { stream.Write(frame, 0, frame.Length); stream.Flush(); }
|
|
return true;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Log?.Invoke($"[{site}] 제어 송신 오류: {ex.Message}");
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|