using System; using System.Threading; namespace IvfTl.Hardware.Impl { /// /// 按舱借用闸门实现。同一舱同一时刻只有一个使用者持有 lease。 /// 优先级:前台(OperateDebug/AutoFocus) > 后台(ControlCapture)。 /// · 前台申请借用时,先置暂停标志并触发 OnPauseCapture(采集线程每节拍据此让路); /// · 用 SemaphoreSlim(1) 保证舱级独占;后台采集 Acquire 用短超时,拿不到即返回 null 让调用方跳过本轮; /// · 归还(lease.Dispose) 时若已无前台占用则触发 OnResumeCapture。 /// ⚠ 真机验证 V-012:借用→暂停→归还恢复时序(调试↔采集切换不占用/不死锁)。 /// public sealed class HouseGateImpl : IHouseGate { private readonly SemaphoreSlim _sem = new SemaphoreSlim(1, 1); private readonly object _stateLock = new object(); private readonly Func _serialFactory; private readonly Func _cameraFactory; private int _foregroundHolders = 0; // 当前前台借用计数(>0 时采集应暂停) public HouseGateImpl(int houseSn, Func serialFactory, Func cameraFactory) { HouseSn = houseSn; _serialFactory = serialFactory; _cameraFactory = cameraFactory; } public int HouseSn { get; } public bool IsCapturePaused { get; private set; } public event Action OnPauseCapture; public event Action OnResumeCapture; public IHardwareLease Acquire(HardwareUser user, int timeoutMs = 30000) { bool foreground = user != HardwareUser.ControlCapture; // 前台借用:先标记暂停采集,让后台节拍尽快让路,再排队拿独占锁。 if (foreground) { MarkPause(); } bool got = _sem.Wait(timeoutMs); if (!got) { // 拿不到:前台撤销暂停标记(避免误挂起采集);后台直接返回 null 让调用方跳过本轮。 if (foreground) MarkResume(); return null; } return new HardwareLeaseImpl(this, user, _serialFactory(), _cameraFactory(), foreground); } public bool TryAcquire(HardwareUser user, out IHardwareLease lease) { bool foreground = user != HardwareUser.ControlCapture; if (foreground) MarkPause(); if (!_sem.Wait(0)) { if (foreground) MarkResume(); lease = null; return false; } lease = new HardwareLeaseImpl(this, user, _serialFactory(), _cameraFactory(), foreground); return true; } public void PauseCapture() => MarkPause(); public void ResumeCapture() => MarkResume(); private void MarkPause() { bool fire = false; lock (_stateLock) { _foregroundHolders++; if (!IsCapturePaused) { IsCapturePaused = true; fire = true; } } if (fire) OnPauseCapture?.Invoke(); } private void MarkResume() { bool fire = false; lock (_stateLock) { if (_foregroundHolders > 0) _foregroundHolders--; if (_foregroundHolders == 0 && IsCapturePaused) { IsCapturePaused = false; fire = true; } } if (fire) OnResumeCapture?.Invoke(); } // 归还:释放独占锁;若是前台借用,撤销其暂停占用并按需恢复采集。 internal void Release(bool wasForeground) { try { _sem.Release(); } catch { } if (wasForeground) MarkResume(); } } /// 借用凭证实现。Dispose 即归还闸门并(前台)触发 ResumeCapture。 internal sealed class HardwareLeaseImpl : IHardwareLease { private readonly HouseGateImpl _gate; private readonly bool _foreground; private int _disposed = 0; public HardwareLeaseImpl(HouseGateImpl gate, HardwareUser owner, ISerialChannel serial, ICamera camera, bool foreground) { _gate = gate; _foreground = foreground; Owner = owner; Serial = serial; Camera = camera; } public HardwareUser Owner { get; } public ISerialChannel Serial { get; } public ICamera Camera { get; } public void Dispose() { // 幂等:多次 Dispose / using 异常路径只归还一次。 if (Interlocked.Exchange(ref _disposed, 1) == 1) return; _gate.Release(_foreground); } } }