DebugSessionManager.cs 2.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465
  1. using System;
  2. using System.Collections.Concurrent;
  3. using IvfTl.Hardware;
  4. namespace IvfTl.ControlHost.Debug
  5. {
  6. /// <summary>
  7. /// control 端调试会话后端:会话表 + 借用/归还 + 心跳续约 + 超时自动回收 + 命令分发。
  8. /// 安全地基(spec §5):绝不指望 operate 主动还,SweepExpired 超时回收兜底。
  9. /// </summary>
  10. public sealed class DebugSessionManager
  11. {
  12. private readonly Func<int, IHouseGate> _gateOf;
  13. private readonly Func<DateTime> _clock;
  14. private readonly int _ttlMs;
  15. private readonly Action<string> _log;
  16. private readonly ConcurrentDictionary<string, DebugSession> _sessions = new ConcurrentDictionary<string, DebugSession>();
  17. public DebugSessionManager(Func<int, IHouseGate> gateOf, Func<DateTime> clock, int ttlMs, Action<string> log)
  18. {
  19. _gateOf = gateOf; _clock = clock; _ttlMs = ttlMs; _log = log ?? (_ => { });
  20. }
  21. public DebugCommandResult Acquire(int houseSn)
  22. {
  23. var gate = _gateOf(houseSn);
  24. if (gate == null) return DebugCommandResult.Fail("NO_HANDLE", $"舱{houseSn}无闸门");
  25. var lease = gate.Acquire(HardwareUser.OperateDebug);
  26. if (lease == null) return DebugCommandResult.Fail("BUSY", $"舱{houseSn}被占用,借用超时");
  27. string sid = Guid.NewGuid().ToString("N");
  28. _sessions[sid] = new DebugSession(sid, houseSn, lease, _clock());
  29. _log($"[debug] acquire 舱{houseSn} sid={sid}");
  30. return DebugCommandResult.Okay(sid);
  31. }
  32. public DebugCommandResult Heartbeat(string sid)
  33. {
  34. if (sid != null && _sessions.TryGetValue(sid, out var s)) { s.LastSeen = _clock(); return DebugCommandResult.Okay(); }
  35. return DebugCommandResult.Fail("SESSION_EXPIRED", "会话不存在或已过期");
  36. }
  37. public DebugCommandResult Release(string sid)
  38. {
  39. if (sid != null && _sessions.TryRemove(sid, out var s))
  40. {
  41. try { s.Lease.Dispose(); } catch { }
  42. _log($"[debug] release sid={sid} 舱{s.HouseSn}");
  43. }
  44. return DebugCommandResult.Okay();
  45. }
  46. /// <summary>超时回收:LastSeen + ttl < now 的会话自动归还(spec §5.1)。</summary>
  47. public int SweepExpired()
  48. {
  49. int n = 0; var now = _clock();
  50. foreach (var kv in _sessions)
  51. {
  52. if ((now - kv.Value.LastSeen).TotalMilliseconds > _ttlMs)
  53. {
  54. if (_sessions.TryRemove(kv.Key, out var s))
  55. {
  56. try { s.Lease.Dispose(); } catch { }
  57. _log($"[debug] 会话超时自动回收 sid={kv.Key} 舱{s.HouseSn}");
  58. n++;
  59. }
  60. }
  61. }
  62. return n;
  63. }
  64. }
  65. }