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);
}
}
}