SDK 的 Bug 修复

This commit is contained in:
2026-01-17 00:03:16 +08:00
parent 97a322960a
commit 2a331d769f
10 changed files with 131 additions and 38 deletions

View File

@@ -66,6 +66,8 @@ public class CameraManager : IDisposable, IAsyncDisposable
throw new InvalidOperationException($"设备 ID {config.Id} 已存在");
}
_coordinator.Register(device);
// 动态激活逻辑:引擎已启动时,新设备直接标记为运行状态
if (_isEngineStarted)
device.IsRunning = true;
@@ -211,6 +213,14 @@ public class CameraManager : IDisposable, IAsyncDisposable
// 1. 审计
_sysLog.Debug($"[Core] 响应设备配置更新请求, ID:{deviceId}.");
// ============================================================
// 【核心修复:手动解除冷冻】
// [原因] 用户已干预配置,无论之前是否认证失败,都应立即重置标记
// 这样下一次 Coordinator (5秒内) 扫描时,会因为 IsAuthFailed == false
// 且经过了 NormalRetryMs (30s) 而立即尝试拉起。
// ============================================================
device.ResetResilience();
// 2. 创建副本进行对比
var oldConfig = device.Config;
var newConfig = oldConfig.DeepCopy();
@@ -247,13 +257,14 @@ public class CameraManager : IDisposable, IAsyncDisposable
bool wasRunning = device.IsRunning;
// A. 彻底停止
if (device.IsOnline) await device.StopAsync();
if (device.IsActived) await device.StopAsync();
// B. 写入新配置
device.UpdateConfig(newConfig);
// C. 自动重启
if (wasRunning) await device.StartAsync();
if (wasRunning)
await device.StartAsync();
}
else
{
@@ -263,7 +274,7 @@ public class CameraManager : IDisposable, IAsyncDisposable
device.UpdateConfig(newConfig);
// B. 在线应用策略
if (device.IsOnline)
if (device.IsActived)
{
var options = new DynamicStreamOptions
{

View File

@@ -78,6 +78,8 @@ public class SmartFrame : IDisposable
ResetDerivatives();
}
private int _isReturned = 0; // 0: 激活中, 1: 已归还池
/// <summary>
/// [生产者调用] 从帧池取出时激活帧
/// 功能:初始化引用计数,标记激活时间戳
@@ -86,6 +88,7 @@ public class SmartFrame : IDisposable
{
// 激活后引用计数设为 1代表生产者驱动/管道)持有该帧
_refCount = 1;
_isReturned = 0; // 激活时重置归还标记
// 记录帧被取出池的时间,用于后续延迟计算
Timestamp = DateTime.Now;
}
@@ -155,11 +158,15 @@ public class SmartFrame : IDisposable
// 原子递减:线程安全,确保计数准确
if (Interlocked.Decrement(ref _refCount) <= 0)
{
// 1. 彻底清理衍生数据TargetMat 通常是 new 出来的,必须 Dispose
ResetDerivatives();
// 2. 关键:原子抢占归还权。只有成功将 _isReturned 从 0 变为 1 的线程才能执行归还逻辑。
if (Interlocked.CompareExchange(ref _isReturned, 1, 0) == 0)
{
// 3. 彻底清理衍生数据TargetMat 必须释放)
ResetDerivatives();
// 2. 归还到池中复用 (InternalMat 不释放,继续保留在内存池中)
_pool.Return(this);
// 4. 安全归还到池中
_pool.Return(this);
}
}
}

View File

@@ -136,6 +136,10 @@ public class CameraCoordinator
#region --- (Reconciliation Logic) ---
private const int NormalRetryMs = 30000; // 普通网络故障30秒后重试
private const int FatalRetryMs = 900000; // 认证类致命故障15分钟后重试 (或保持 0直到手动重置)
/// <summary>
/// 相机状态调和(核心自愈逻辑 - 修复版)
/// 功能:校验相机物理连接、流状态,执行启动/停止/复位操作,确保状态一致性
@@ -146,10 +150,20 @@ public class CameraCoordinator
{
// 1. 计算距离上次收到帧的时间(秒)
long nowTick = Environment.TickCount64;
// --- [新增] 分级冷冻判定逻辑 ---
long elapsed = nowTick - cam.LastStartAttemptTick;
// 如果是认证失败致命15分钟内不准动
if (cam.IsAuthFailed && elapsed < FatalRetryMs) return;
// 如果是普通网络问题30秒内不准动
if (!cam.IsAuthFailed && elapsed < NormalRetryMs) return;
double secondsSinceLastFrame = (nowTick - cam.LastFrameTick) / 1000.0;
// 2. 判定流是否正常:设备在线 + 5秒内有帧
bool isFlowing = cam.IsOnline && secondsSinceLastFrame < StreamAliveThresholdSeconds;
bool isFlowing = cam.IsActived && secondsSinceLastFrame < StreamAliveThresholdSeconds;
// 3. 判定物理连接是否正常:流正常则直接判定在线;否则执行 Ping+TCP 探测
// (注意:如果哨兵已经更新了 Ping 状态ProbeHardwareAsync 内部也可以优化为直接读取,
@@ -159,7 +173,7 @@ public class CameraCoordinator
// 4. 状态调和决策:根据物理状态与设备状态的差异执行对应操作
// 场景 A: 物理在线 + 设备离线 + 用户要求运行 -> 执行启动
if (isPhysicalOk && !cam.IsOnline && cam.IsRunning)
if (isPhysicalOk && !cam.IsActived && cam.IsRunning)
{
// 加登录锁防止冲突
bool lockTaken = false;
@@ -167,10 +181,30 @@ public class CameraCoordinator
{
await _sdkLoginLock.WaitAsync(token).ConfigureAwait(false);
lockTaken = true;
// 双重校验:防止等待锁期间状态已变更
if (!cam.IsOnline)
if (!cam.IsActived)
{
await cam.StartAsync().ConfigureAwait(false);
cam.MarkStartAttempt();
try
{
await cam.StartAsync().ConfigureAwait(false);
// 成功后复位致命标记
cam.IsAuthFailed = false;
}
catch (CameraException ex) when (ex.ErrorCode == CameraErrorCode.InvalidCredentials)
{
// [新增] 识别到致命密码错误,打上标记,触发 15 分钟长冷冻
cam.IsAuthFailed = true;
_sysLog.Fatal($"[Coordinator] 设备 {cam.Id} 认证失败(密码错),已进入 15 分钟保护性冷冻以防封 IP。");
}
catch (Exception ex)
{
// 普通异常维持普通冷冻30秒
_sysLog.Warning($"[Coordinator] 设备 {cam.Id} 启动失败: {ex.Message}");
}
}
}
finally
@@ -182,13 +216,13 @@ public class CameraCoordinator
}
}
// 场景 B: 物理离线 + 设备在线 -> 执行强制停止
else if (!isPhysicalOk && cam.IsOnline)
else if (!isPhysicalOk && cam.IsActived)
{
await cam.StopAsync().ConfigureAwait(false);
}
// 场景 C: 物理在线 + 设备在线 + 流中断 + 【用户要求运行】 -> 判定为僵死
// 【关键修复】:增加了 && cam.IsRunning 判定,防止待机状态下被误复位
else if (isPhysicalOk && cam.IsOnline && !isFlowing && cam.IsRunning) // [cite: 504]
else if (isPhysicalOk && cam.IsActived && !isFlowing && cam.IsRunning) // [cite: 504]
{
_sysLog.Warning($"[Coordinator] [自愈] 设备 {cam.Id} 僵死({secondsSinceLastFrame:F1}秒无帧),复位中...");
await cam.StopAsync().ConfigureAwait(false);