|
@@ -15,12 +15,30 @@ namespace IvfTl.ControlHost.Debug
|
|
|
private readonly int _ttlMs;
|
|
private readonly int _ttlMs;
|
|
|
private readonly Action<string> _log;
|
|
private readonly Action<string> _log;
|
|
|
private readonly Func<int, (bool cultivating, int embryoCount)> _cultivationOf;
|
|
private readonly Func<int, (bool cultivating, int embryoCount)> _cultivationOf;
|
|
|
|
|
+ // (Critical 并发修复)"会话即将关闭"回调(参数=sid):在 Release/SweepExpired 回收某会话、
|
|
|
|
|
+ // Dispose lease 之前调用,通知标定先停并等当前孔跑完(Program.cs 装配时接到 calibMgr.StopAndWait)。
|
|
|
|
|
+ // 用可空 Action + 可后置 SetOnSessionClosing,打破"CalibrationManager 依赖 DebugSessionManager / 回调又要调
|
|
|
|
|
+ // CalibrationManager"的循环依赖(先 new debugMgr → new calibMgr(debugMgr) → debugMgr.SetOnSessionClosing(...))。
|
|
|
|
|
+ private volatile Action<string> _onSessionClosing;
|
|
|
private readonly ConcurrentDictionary<string, DebugSession> _sessions = new ConcurrentDictionary<string, DebugSession>();
|
|
private readonly ConcurrentDictionary<string, DebugSession> _sessions = new ConcurrentDictionary<string, DebugSession>();
|
|
|
public DebugSessionManager(Func<int, IHouseGate> gateOf, Func<DateTime> clock, int ttlMs, Action<string> log,
|
|
public DebugSessionManager(Func<int, IHouseGate> gateOf, Func<DateTime> clock, int ttlMs, Action<string> log,
|
|
|
- Func<int, (bool, int)> cultivationOf = null)
|
|
|
|
|
|
|
+ Func<int, (bool, int)> cultivationOf = null, Action<string> onSessionClosing = null)
|
|
|
{
|
|
{
|
|
|
_gateOf = gateOf; _clock = clock; _ttlMs = ttlMs; _log = log ?? (_ => { });
|
|
_gateOf = gateOf; _clock = clock; _ttlMs = ttlMs; _log = log ?? (_ => { });
|
|
|
_cultivationOf = cultivationOf;
|
|
_cultivationOf = cultivationOf;
|
|
|
|
|
+ _onSessionClosing = onSessionClosing;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /// <summary>(装配用)后置注入"会话即将关闭"回调,打破与 CalibrationManager 的循环依赖。</summary>
|
|
|
|
|
+ public void SetOnSessionClosing(Action<string> onSessionClosing) => _onSessionClosing = onSessionClosing;
|
|
|
|
|
+
|
|
|
|
|
+ // 会话关闭前钩子:Dispose lease 之前调,先停该 sid 标定并等当前孔跑完(回调内部吞异常,绝不阻断回收/Dispose)。
|
|
|
|
|
+ private void InvokeOnSessionClosing(string sid)
|
|
|
|
|
+ {
|
|
|
|
|
+ var cb = _onSessionClosing;
|
|
|
|
|
+ if (cb == null) return;
|
|
|
|
|
+ try { cb(sid); }
|
|
|
|
|
+ catch (Exception ex) { _log($"[debug] onSessionClosing 回调异常 sid={sid}: {ex.Message}"); }
|
|
|
}
|
|
}
|
|
|
public DebugCommandResult Acquire(int houseSn)
|
|
public DebugCommandResult Acquire(int houseSn)
|
|
|
{
|
|
{
|
|
@@ -54,6 +72,7 @@ namespace IvfTl.ControlHost.Debug
|
|
|
{
|
|
{
|
|
|
if (sid != null && _sessions.TryRemove(sid, out var s))
|
|
if (sid != null && _sessions.TryRemove(sid, out var s))
|
|
|
{
|
|
{
|
|
|
|
|
+ InvokeOnSessionClosing(sid); // (Critical)Dispose 前先停标定并等当前孔跑完,再恢复采集,消除争用
|
|
|
try { s.Lease.Dispose(); } catch (Exception ex) { _log($"[debug] dispose 异常 sid={sid} 舱{s.HouseSn}: {ex.Message}"); }
|
|
try { s.Lease.Dispose(); } catch (Exception ex) { _log($"[debug] dispose 异常 sid={sid} 舱{s.HouseSn}: {ex.Message}"); }
|
|
|
_log($"[debug] release sid={sid} 舱{s.HouseSn}");
|
|
_log($"[debug] release sid={sid} 舱{s.HouseSn}");
|
|
|
}
|
|
}
|
|
@@ -69,6 +88,7 @@ namespace IvfTl.ControlHost.Debug
|
|
|
{
|
|
{
|
|
|
if (_sessions.TryRemove(kv.Key, out var s))
|
|
if (_sessions.TryRemove(kv.Key, out var s))
|
|
|
{
|
|
{
|
|
|
|
|
+ InvokeOnSessionClosing(kv.Key); // (Critical)同 Release:超时回收路径也在 Dispose 前先停标定并等当前孔跑完
|
|
|
try { s.Lease.Dispose(); } catch (Exception ex) { _log($"[debug] dispose 异常 sid={kv.Key} 舱{s.HouseSn}: {ex.Message}"); }
|
|
try { s.Lease.Dispose(); } catch (Exception ex) { _log($"[debug] dispose 异常 sid={kv.Key} 舱{s.HouseSn}: {ex.Message}"); }
|
|
|
_log($"[debug] 会话超时自动回收 sid={kv.Key} 舱{s.HouseSn}");
|
|
_log($"[debug] 会话超时自动回收 sid={kv.Key} 舱{s.HouseSn}");
|
|
|
n++;
|
|
n++;
|