核心代码格式化
This commit is contained in:
@@ -3,58 +3,31 @@
|
||||
namespace SHH.CameraSdk;
|
||||
|
||||
/// <summary>
|
||||
/// [架构基类] 工业级视频源抽象核心 (V3.3.4 严格匹配版)
|
||||
/// [架构基类] 工业级视频源抽象核心 (V3.5.0 严格匹配版)
|
||||
/// <para>当前模块: AiVideo | 核心原则: 低耦合、高并发、零拷贝</para>
|
||||
/// 核心职责:
|
||||
/// <para>1. 提供线程安全的生命周期管理(启动/停止/销毁)</para>
|
||||
/// <para>2. 实现状态变更的可靠分发与异常隔离</para>
|
||||
/// <para>3. 支持配置热更新与动态参数应用</para>
|
||||
/// <para>4. 内置 FPS/码率统计、心跳保活、审计日志能力</para>
|
||||
/// <para>1. 生命周期管理:基于 <see cref="SemaphoreSlim"/> 实现 Start/Stop/UpdateConfig 的线程安全串行化</para>
|
||||
/// <para>2. 状态分发系统:利用 <see cref="Channel{T}"/> (DropOldest策略) 实现高性能、非阻塞的状态变更通知</para>
|
||||
/// <para>3. 遥测统计引擎:内置原子级 FPS 计算与 Mbps 带宽监控,支持网络层与解码层双路流量计费</para>
|
||||
/// <para>4. 弹性自愈机制:集成 IsAuthFailed 冻结期与物理网络 (Ping) 状态同步,支持 Coordinator 级自动重连</para>
|
||||
/// <para>5. 审计与追踪:内置环形审计日志 (Max:100),记录配置变更、动态参数应用及驱动层异常</para>
|
||||
/// 关键修复记录:
|
||||
/// <para>✅ [Bug A] 死锁免疫:所有 await 均添加 ConfigureAwait(false),解除 UI 线程依赖</para>
|
||||
/// <para>✅ [Bug π] 管道安全:Dispose 采用优雅关闭策略,确保剩余状态消息被消费</para>
|
||||
/// <para>✅ [编译修复] 补全 CloneConfig 中 Transport/VendorArguments 的深拷贝逻辑</para>
|
||||
/// <para>✅ [V3.3.5] 资源防御:在 DisposeAsync 中强化生命周期锁,防止销毁期间触发重入</para>
|
||||
/// <para>✅ [V3.3.5] 自愈增强:引入 Environment.TickCount64 宽限期机制,解决启动瞬间心跳超时误判</para>
|
||||
/// <para>✅ [Bug A] 死锁免疫:强制所有 await 添加 ConfigureAwait(false),彻底解除对同步上下文的依赖</para>
|
||||
/// <para>✅ [Bug π] 管道安全:Dispose 采用 TryComplete 优雅关闭策略,确保缓冲区剩余状态消息被完全消费</para>
|
||||
/// </summary>
|
||||
public abstract class BaseVideoSource : IVideoSource, IAsyncDisposable, IDeviceConnectivity
|
||||
{
|
||||
// [新增] 物理在线状态(专门给 Ping 使用)
|
||||
private volatile bool _isPhysicalOnline;
|
||||
public bool IsPhysicalOnline => _isPhysicalOnline;
|
||||
#region --- 1. 核心字段与锁机制 (Fields & Locks) ---
|
||||
|
||||
/// <summary>
|
||||
/// 图像预处理配置(缩放、增量等)
|
||||
/// 放置在基类中,确保所有接入协议(HIK/DH/RTSP)均可共享处理逻辑
|
||||
/// </summary>
|
||||
public PreprocessConfig PreprocessSettings { get; set; }
|
||||
= new PreprocessConfig();
|
||||
|
||||
string IDeviceConnectivity.IpAddress => _config.IpAddress;
|
||||
|
||||
// 允许哨兵从外部更新 _isOnline 字段
|
||||
void IDeviceConnectivity.SetNetworkStatus(bool isOnline)
|
||||
{
|
||||
if (_isPhysicalOnline != isOnline)
|
||||
{
|
||||
_isPhysicalOnline = isOnline;
|
||||
// 触发状态变更是为了通知 UI 更新绿色小圆点,但不改变 Status
|
||||
// 注意:这里传 _status 保持原样,只变消息
|
||||
StatusChanged?.Invoke(this, new StatusChangedEventArgs(_status, isOnline ? "物理网络恢复" : "物理网络中断"));
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract ILogger _sdkLog { get; }
|
||||
|
||||
#region --- 1. 核心配置与锁机制 (Core Config & Locks) ---
|
||||
|
||||
/// <summary>
|
||||
/// 核心配置对象(支持热更新,去除 readonly 修饰符)
|
||||
/// 核心配置对象(支持热更新)
|
||||
/// 注意:外部修改需通过 UpdateConfig 方法,确保线程安全
|
||||
/// </summary>
|
||||
protected VideoSourceConfig _config;
|
||||
|
||||
/// <summary>
|
||||
/// 状态同步锁
|
||||
/// 作用:保护 _status 字段的读写原子性,防止多线程竞争导致状态不一致
|
||||
/// </summary>
|
||||
/// <summary>状态同步锁:保护 _status 字段的读写原子性,防止多线程竞争导致状态不一致</summary>
|
||||
private readonly object _stateSyncRoot = new();
|
||||
|
||||
/// <summary>
|
||||
@@ -64,9 +37,11 @@ public abstract class BaseVideoSource : IVideoSource, IAsyncDisposable, IDeviceC
|
||||
/// </summary>
|
||||
private readonly SemaphoreSlim _lifecycleLock = new(1, 1);
|
||||
|
||||
#endregion
|
||||
/// <summary> 跟踪上一个未完成的生命周期任务 </summary>
|
||||
private Task _pendingLifecycleTask = Task.CompletedTask;
|
||||
|
||||
#region --- 2. 内部状态与基础设施 (Internal States & Infrastructure) ---
|
||||
/// <summary> 物理在线状态(由哨兵 Ping 更新) </summary>
|
||||
private volatile bool _isPhysicalOnline;
|
||||
|
||||
/// <summary> 设备在线状态标志(volatile 确保多线程可见性) </summary>
|
||||
private volatile bool _isActived;
|
||||
@@ -74,6 +49,13 @@ public abstract class BaseVideoSource : IVideoSource, IAsyncDisposable, IDeviceC
|
||||
/// <summary> 视频源核心状态(受 _stateSyncRoot 保护) </summary>
|
||||
private VideoSourceStatus _status = VideoSourceStatus.Disconnected;
|
||||
|
||||
/// <summary> 资源销毁标记 </summary>
|
||||
private volatile bool _isDisposed = false;
|
||||
|
||||
#endregion
|
||||
|
||||
#region --- 2. 状态分发基础设施 (Status Channel) ---
|
||||
|
||||
/// <summary>
|
||||
/// 状态通知有界通道
|
||||
/// 特性:DropOldest 策略,消费者过载时丢弃旧状态,防止内存溢出
|
||||
@@ -87,15 +69,60 @@ public abstract class BaseVideoSource : IVideoSource, IAsyncDisposable, IDeviceC
|
||||
/// <summary> 状态分发任务引用(用于 Dispose 时优雅等待) </summary>
|
||||
private Task? _distributorTask;
|
||||
|
||||
#endregion
|
||||
|
||||
#region --- 3. 遥测与性能统计 (Telemetry & Stats) ---
|
||||
|
||||
/// <summary> 最后一帧接收的系统 Tick(单调时钟,不受系统时间修改影响) </summary>
|
||||
private long _lastFrameTick = 0;
|
||||
|
||||
/// <summary> 生命周期内接收的总帧数 </summary>
|
||||
private long _totalFramesReceived = 0;
|
||||
|
||||
/// <summary> FPS 计算临时计数器 </summary>
|
||||
private int _tempFrameCounter = 0;
|
||||
|
||||
/// <summary> 上次 FPS 计算的 Tick 时间 </summary>
|
||||
private long _lastFpsCalcTick = 0;
|
||||
|
||||
/// <summary> 实时码率 (Mbps) </summary>
|
||||
protected double _currentBitrate = 0;
|
||||
|
||||
/// <summary> 码率计算临时字节计数器 </summary>
|
||||
private long _tempByteCounter = 0;
|
||||
|
||||
#endregion
|
||||
|
||||
#region --- 4. 审计日志与自愈标记 (Audit & Resilience) ---
|
||||
|
||||
/// <summary> 审计日志列表(线程安全访问) </summary>
|
||||
private readonly List<string> _auditLogs = new();
|
||||
|
||||
/// <summary> 最大日志条数(滚动清除,防止内存溢出) </summary>
|
||||
private const int MaxAuditLogCount = 100;
|
||||
|
||||
/// <summary>
|
||||
/// 认证类致命错误标记(如密码错、用户锁定)
|
||||
/// 作用:触发 15 分钟长冷冻期,防止 IP 被相机锁定
|
||||
/// </summary>
|
||||
public bool IsAuthFailed { get; set; }
|
||||
|
||||
/// <summary>上次尝试执行 StartAsync 的系统 Tick 时间 (单调时钟)</summary>
|
||||
private long _lastStartAttemptTick = 0;
|
||||
|
||||
#endregion
|
||||
|
||||
#region --- 5. 公开事件 (Events) ---
|
||||
|
||||
/// <summary> 视频帧回调事件(热路径,低延迟分发) </summary>
|
||||
public event Action<object>? FrameReceived;
|
||||
|
||||
/// <summary> 状态变更事件(对外异步暴露状态通知) </summary>
|
||||
public event EventHandler<StatusChangedEventArgs>? StatusChanged;
|
||||
|
||||
#endregion
|
||||
|
||||
#region --- 3. 公开属性 (Public Properties) ---
|
||||
#region --- 6. 公开属性 (Properties) ---
|
||||
|
||||
/// <summary> 视频源唯一标识 </summary>
|
||||
public long Id => _config.Id;
|
||||
@@ -108,10 +135,7 @@ public abstract class BaseVideoSource : IVideoSource, IAsyncDisposable, IDeviceC
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_stateSyncRoot)
|
||||
{
|
||||
return _status;
|
||||
}
|
||||
lock (_stateSyncRoot) return _status;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,57 +145,78 @@ public abstract class BaseVideoSource : IVideoSource, IAsyncDisposable, IDeviceC
|
||||
/// <summary> 设备在线状态 </summary>
|
||||
public bool IsActived => _isActived;
|
||||
|
||||
/// <summary> 物理在线状态(Ping 结果) </summary>
|
||||
public bool IsPhysicalOnline => _isPhysicalOnline;
|
||||
|
||||
/// <summary> 设备元数据(能力集、通道信息等) </summary>
|
||||
public DeviceMetadata Metadata { get; protected set; } = new();
|
||||
|
||||
/// <summary> 状态变更事件(对外暴露状态通知) </summary>
|
||||
public event EventHandler<StatusChangedEventArgs>? StatusChanged;
|
||||
/// <summary> 帧控制器(用于帧分发策略管理) </summary>
|
||||
public FrameController Controller { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// 图像预处理配置(缩放、增量等)
|
||||
/// 放置在基类中,确保所有接入协议(HIK/DH/RTSP)均可共享处理逻辑
|
||||
/// </summary>
|
||||
public PreprocessConfig PreprocessSettings { get; set; }
|
||||
= new PreprocessConfig();
|
||||
|
||||
/// <summary> 最后一帧接收的 Tick 时间戳(线程安全读取) </summary>
|
||||
public long LastFrameTick => Interlocked.Read(ref _lastFrameTick);
|
||||
|
||||
#endregion
|
||||
|
||||
#region --- 4. 遥测统计属性 (Telemetry Properties) ---
|
||||
|
||||
/// <summary> 生命周期内接收的总帧数 </summary>
|
||||
private long _totalFramesReceived = 0;
|
||||
|
||||
/// <summary> FPS 计算临时计数器 </summary>
|
||||
private int _tempFrameCounter = 0;
|
||||
|
||||
/// <summary> 上次 FPS 计算的 Tick 时间 </summary>
|
||||
private long _lastFpsCalcTick = 0;
|
||||
|
||||
// 提供一个最近一秒的输入帧率参考值
|
||||
public int NominalInputFps => (int)Math.Round(RealFps);
|
||||
/// <summary> 生命周期总帧数(线程安全读取) </summary>
|
||||
public long TotalFrames => Interlocked.Read(ref _totalFramesReceived);
|
||||
|
||||
/// <summary> 实时 FPS(每秒更新一次) </summary>
|
||||
public double RealFps { get; private set; } = 0.0;
|
||||
|
||||
// 提供一个最近一秒的输入帧率参考值
|
||||
public int NominalInputFps => (int)Math.Round(RealFps);
|
||||
|
||||
/// <summary> 实时码率 (Mbps) </summary>
|
||||
protected double _currentBitrate = 0;
|
||||
public double RealBitrate => _currentBitrate;
|
||||
|
||||
/// <summary> 码率计算临时字节计数器 </summary>
|
||||
private long _tempByteCounter = 0;
|
||||
/// <summary> 视频宽度 </summary>
|
||||
public int Width { get; protected set; }
|
||||
|
||||
/// <summary> 生命周期总帧数(线程安全读取) </summary>
|
||||
public long TotalFrames => Interlocked.Read(ref _totalFramesReceived);
|
||||
/// <summary> 视频高度 </summary>
|
||||
public int Height { get; protected set; }
|
||||
|
||||
/// <summary> 满足接口要求的 IP 地址 </summary>
|
||||
string IDeviceConnectivity.IpAddress => _config.IpAddress;
|
||||
|
||||
/// <summary> 上次启动尝试的时间戳 </summary>
|
||||
public long LastStartAttemptTick => Interlocked.Read(ref _lastStartAttemptTick);
|
||||
|
||||
#endregion
|
||||
|
||||
#region --- 5. 审计日志系统 (Audit Log System) ---
|
||||
#region --- 7. 抽象成员 (Abstracts) ---
|
||||
|
||||
/// <summary> 审计日志列表(线程安全访问) </summary>
|
||||
private readonly List<string> _auditLogs = new();
|
||||
/// <summary> 子类特定的日志记录器 </summary>
|
||||
protected abstract ILogger _sdkLog { get; }
|
||||
|
||||
/// <summary> 最大日志条数(滚动清除,防止内存溢出) </summary>
|
||||
private const int MaxAuditLogCount = 100;
|
||||
/// <summary>
|
||||
/// 驱动层启动逻辑(子类必须实现)
|
||||
/// 包含:设备登录、码流订阅、取流线程启动等
|
||||
/// </summary>
|
||||
/// <param name="token">取消令牌</param>
|
||||
protected abstract Task OnStartAsync(CancellationToken token);
|
||||
|
||||
/// <summary>
|
||||
/// 驱动层停止逻辑(子类必须实现)
|
||||
/// 包含:码流停止、设备登出、资源释放等
|
||||
/// </summary>
|
||||
protected abstract Task OnStopAsync();
|
||||
|
||||
/// <summary>
|
||||
/// 驱动层元数据获取逻辑(子类必须实现)
|
||||
/// </summary>
|
||||
/// <returns>设备元数据</returns>
|
||||
protected abstract Task<DeviceMetadata> OnFetchMetadataAsync();
|
||||
|
||||
#endregion
|
||||
|
||||
#region --- 6. 构造函数 (Constructor) ---
|
||||
#region --- 8. 构造函数 (Constructor) ---
|
||||
|
||||
/// <summary>
|
||||
/// 初始化视频源基础设施
|
||||
@@ -204,67 +249,7 @@ public abstract class BaseVideoSource : IVideoSource, IAsyncDisposable, IDeviceC
|
||||
|
||||
#endregion
|
||||
|
||||
#region --- 7. 配置管理 (Config Management) ---
|
||||
|
||||
/// <summary>
|
||||
/// 热更新视频源配置(线程安全)
|
||||
/// 新配置将在下次启动/重连时生效
|
||||
/// </summary>
|
||||
/// <param name="newConfig">新的视频源配置</param>
|
||||
public void UpdateConfig(VideoSourceConfig newConfig)
|
||||
{
|
||||
if (newConfig == null) return;
|
||||
|
||||
// 加生命周期锁:防止与启动/停止操作并发
|
||||
_lifecycleLock.Wait();
|
||||
try
|
||||
{
|
||||
// 深拷贝新配置,隔离外部引用
|
||||
_config = newConfig.DeepCopy();
|
||||
|
||||
// 写入审计日志
|
||||
AddAuditLog($"配置已更新 [IP:{_config.IpAddress}],生效时机:{(_isActived ? "下次重连" : "下次启动")}");
|
||||
Debug.WriteLine($"[ConfigUpdated] 设备 {Id} 配置落地完成");
|
||||
}
|
||||
finally
|
||||
{
|
||||
_lifecycleLock.Release();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 配置深拷贝辅助方法(确保引用类型独立)
|
||||
/// </summary>
|
||||
/// <param name="source">源配置</param>
|
||||
/// <returns>深拷贝后的新配置</returns>
|
||||
private VideoSourceConfig CloneConfig(VideoSourceConfig source)
|
||||
{
|
||||
return new VideoSourceConfig
|
||||
{
|
||||
Id = source.Id,
|
||||
Brand = source.Brand,
|
||||
IpAddress = source.IpAddress,
|
||||
Port = source.Port,
|
||||
Username = source.Username,
|
||||
Password = source.Password,
|
||||
ChannelIndex = source.ChannelIndex,
|
||||
StreamType = source.StreamType,
|
||||
Transport = source.Transport,
|
||||
ConnectionTimeoutMs = source.ConnectionTimeoutMs,
|
||||
MainboardIp = source.MainboardIp,
|
||||
MainboardPort = source.MainboardPort,
|
||||
RtspPath = source.RtspPath,
|
||||
RenderHandle = source.RenderHandle,
|
||||
// Dictionary 深拷贝:防止外部修改影响内部
|
||||
VendorArguments = source.VendorArguments != null
|
||||
? new Dictionary<string, string>(source.VendorArguments)
|
||||
: new Dictionary<string, string>()
|
||||
};
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region --- 8. 生命周期控制 (Lifecycle Control) ---
|
||||
#region --- 9. 生命周期控制 (Lifecycle) ---
|
||||
|
||||
/// <summary>
|
||||
/// 异步启动设备连接
|
||||
@@ -339,6 +324,66 @@ public abstract class BaseVideoSource : IVideoSource, IAsyncDisposable, IDeviceC
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region --- 10. 配置与元数据管理 (Config & Metadata) ---
|
||||
|
||||
/// <summary>
|
||||
/// 热更新视频源配置(线程安全)
|
||||
/// 新配置将在下次启动/重连时生效
|
||||
/// </summary>
|
||||
/// <param name="newConfig">新的视频源配置</param>
|
||||
public void UpdateConfig(VideoSourceConfig newConfig)
|
||||
{
|
||||
if (newConfig == null) return;
|
||||
|
||||
// 加生命周期锁:防止与启动/停止操作并发
|
||||
_lifecycleLock.Wait();
|
||||
try
|
||||
{
|
||||
// 深拷贝新配置,隔离外部引用
|
||||
_config = newConfig.DeepCopy();
|
||||
|
||||
// 写入审计日志
|
||||
AddAuditLog($"配置已更新 [IP:{_config.IpAddress}],生效时机:{(_isActived ? "下次重连" : "下次启动")}");
|
||||
Debug.WriteLine($"[ConfigUpdated] 设备 {Id} 配置落地完成");
|
||||
}
|
||||
finally
|
||||
{
|
||||
_lifecycleLock.Release();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 配置深拷贝辅助方法(确保引用类型独立)
|
||||
/// </summary>
|
||||
/// <param name="source">源配置</param>
|
||||
/// <returns>深拷贝后的新配置</returns>
|
||||
private VideoSourceConfig CloneConfig(VideoSourceConfig source)
|
||||
{
|
||||
return new VideoSourceConfig
|
||||
{
|
||||
Id = source.Id,
|
||||
Brand = source.Brand,
|
||||
IpAddress = source.IpAddress,
|
||||
Port = source.Port,
|
||||
Username = source.Username,
|
||||
Password = source.Password,
|
||||
ChannelIndex = source.ChannelIndex,
|
||||
StreamType = source.StreamType,
|
||||
Transport = source.Transport,
|
||||
ConnectionTimeoutMs = source.ConnectionTimeoutMs,
|
||||
MainboardIp = source.MainboardIp,
|
||||
MainboardPort = source.MainboardPort,
|
||||
RtspPath = source.RtspPath,
|
||||
RenderHandle = source.RenderHandle,
|
||||
// Dictionary 深拷贝:防止外部修改影响内部
|
||||
VendorArguments = source.VendorArguments != null
|
||||
? new Dictionary<string, string>(source.VendorArguments)
|
||||
: new Dictionary<string, string>()
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 刷新设备元数据,对比差异并更新
|
||||
/// </summary>
|
||||
@@ -422,7 +467,7 @@ public abstract class BaseVideoSource : IVideoSource, IAsyncDisposable, IDeviceC
|
||||
|
||||
#endregion
|
||||
|
||||
#region --- 9. 帧处理与状态管理 (Frame Processing & Status Management) ---
|
||||
#region --- 11. 帧处理与遥测上报 (Data Handling) ---
|
||||
|
||||
/// <summary>
|
||||
/// 检查是否存在帧订阅者(性能优化)
|
||||
@@ -432,17 +477,10 @@ public abstract class BaseVideoSource : IVideoSource, IAsyncDisposable, IDeviceC
|
||||
protected bool HasFrameSubscribers() => FrameReceived != null;
|
||||
|
||||
/// <summary>
|
||||
/// 上报驱动层异常,触发重连自愈逻辑
|
||||
/// 触发帧回调事件(热路径优化)
|
||||
/// </summary>
|
||||
/// <param name="ex">相机统一异常</param>
|
||||
protected void ReportError(CameraException ex)
|
||||
{
|
||||
if (!_isActived) return;
|
||||
|
||||
// 标记离线并更新状态为重连中
|
||||
_isActived = false;
|
||||
UpdateStatus(VideoSourceStatus.Reconnecting, $"SDK异常: {ex.Message}", ex);
|
||||
}
|
||||
/// <param name="frameData">帧数据(如 Mat/SmartFrame)</param>
|
||||
protected void RaiseFrameReceived(object frameData) => FrameReceived?.Invoke(frameData);
|
||||
|
||||
/// <summary>
|
||||
/// 标记数据接收(心跳保活 + 双路统计)
|
||||
@@ -509,11 +547,49 @@ public abstract class BaseVideoSource : IVideoSource, IAsyncDisposable, IDeviceC
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region --- 12. 状态管理与分发 (Status Distributor) ---
|
||||
|
||||
/// <summary>
|
||||
/// 触发帧回调事件(热路径优化)
|
||||
/// 更新设备状态并写入分发队列
|
||||
/// </summary>
|
||||
/// <param name="frameData">帧数据(如 Mat/SmartFrame)</param>
|
||||
protected void RaiseFrameReceived(object frameData) => FrameReceived?.Invoke(frameData);
|
||||
/// <param name="status">新状态</param>
|
||||
/// <param name="msg">状态描述</param>
|
||||
/// <param name="ex">关联异常(可选)</param>
|
||||
protected void UpdateStatus(VideoSourceStatus status, string msg, CameraException? ex = null)
|
||||
{
|
||||
lock (_stateSyncRoot)
|
||||
{
|
||||
// 更新内部状态
|
||||
_status = status;
|
||||
|
||||
// 写入状态队列(满时自动丢弃旧数据)
|
||||
_ = _statusQueue.Writer.TryWrite(new StatusChangedEventArgs(
|
||||
status,
|
||||
msg,
|
||||
ex,
|
||||
ex?.RawErrorCode)
|
||||
{
|
||||
NewHandle = ex?.Context.TryGetValue("NativeHandle", out var handle) == true ? (IntPtr)handle : null
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 允许哨兵从外部更新 _isOnline 字段
|
||||
/// </summary>
|
||||
/// <param name="isOnline"></param>
|
||||
void IDeviceConnectivity.SetNetworkStatus(bool isOnline)
|
||||
{
|
||||
if (_isPhysicalOnline != isOnline)
|
||||
{
|
||||
_isPhysicalOnline = isOnline;
|
||||
// 触发状态变更是为了通知 UI 更新绿色小圆点,但不改变 Status
|
||||
// 注意:这里传 _status 保持原样,只变消息
|
||||
StatusChanged?.Invoke(this, new StatusChangedEventArgs(_status, isOnline ? "物理网络恢复" : "物理网络中断"));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 后台状态分发循环(单线程消费状态队列)
|
||||
@@ -562,33 +638,45 @@ public abstract class BaseVideoSource : IVideoSource, IAsyncDisposable, IDeviceC
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新设备状态并写入分发队列
|
||||
/// 上报驱动层异常,触发重连自愈逻辑
|
||||
/// </summary>
|
||||
/// <param name="status">新状态</param>
|
||||
/// <param name="msg">状态描述</param>
|
||||
/// <param name="ex">关联异常(可选)</param>
|
||||
protected void UpdateStatus(VideoSourceStatus status, string msg, CameraException? ex = null)
|
||||
/// <param name="ex">相机统一异常</param>
|
||||
protected void ReportError(CameraException ex)
|
||||
{
|
||||
lock (_stateSyncRoot)
|
||||
{
|
||||
// 更新内部状态
|
||||
_status = status;
|
||||
if (!_isActived) return;
|
||||
|
||||
// 写入状态队列(满时自动丢弃旧数据)
|
||||
_ = _statusQueue.Writer.TryWrite(new StatusChangedEventArgs(
|
||||
status,
|
||||
msg,
|
||||
ex,
|
||||
ex?.RawErrorCode)
|
||||
{
|
||||
NewHandle = ex?.Context.TryGetValue("NativeHandle", out var handle) == true ? (IntPtr)handle : null
|
||||
});
|
||||
}
|
||||
// 标记离线并更新状态为重连中
|
||||
_isActived = false;
|
||||
UpdateStatus(VideoSourceStatus.Reconnecting, $"SDK异常: {ex.Message}", ex);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region --- 10. 审计日志辅助 (Audit Log Helpers) ---
|
||||
#region --- 13. 自愈与审计辅助 (Resilience & Audit) ---
|
||||
|
||||
/// <summary>
|
||||
/// 更新最后一次启动尝试的时间戳为当前时间
|
||||
/// </summary>
|
||||
public void MarkStartAttempt()
|
||||
{
|
||||
Interlocked.Exchange(ref _lastStartAttemptTick, Environment.TickCount64);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 强制重置自愈相关的冷却与错误标记
|
||||
/// 用于用户手动干预(如修改密码)后,使协调器能立即触发下一次尝试
|
||||
/// </summary>
|
||||
public void ResetResilience()
|
||||
{
|
||||
// 1. 清除认证失败标记
|
||||
IsAuthFailed = false;
|
||||
|
||||
// 2. 将尝试时间戳归零
|
||||
// 这样在 Coordinator 中计算 elapsed = now - 0,结果会远大于 30s
|
||||
Interlocked.Exchange(ref _lastStartAttemptTick, 0);
|
||||
|
||||
_sdkLog.Debug($"[Sdk] 设备 {Id} 自愈状态已人工重置");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加审计日志(线程安全)
|
||||
@@ -621,32 +709,18 @@ public abstract class BaseVideoSource : IVideoSource, IAsyncDisposable, IDeviceC
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region --- 11. 抽象方法 (Abstract Methods) ---
|
||||
|
||||
/// <summary>
|
||||
/// 驱动层启动逻辑(子类必须实现)
|
||||
/// 包含:设备登录、码流订阅、取流线程启动等
|
||||
/// 清空审计日志
|
||||
/// </summary>
|
||||
/// <param name="token">取消令牌</param>
|
||||
protected abstract Task OnStartAsync(CancellationToken token);
|
||||
|
||||
/// <summary>
|
||||
/// 驱动层停止逻辑(子类必须实现)
|
||||
/// 包含:码流停止、设备登出、资源释放等
|
||||
/// </summary>
|
||||
protected abstract Task OnStopAsync();
|
||||
|
||||
/// <summary>
|
||||
/// 驱动层元数据获取逻辑(子类必须实现)
|
||||
/// </summary>
|
||||
/// <returns>设备元数据</returns>
|
||||
protected abstract Task<DeviceMetadata> OnFetchMetadataAsync();
|
||||
public void ClearAuditLogs()
|
||||
{
|
||||
_auditLogs.Clear();
|
||||
AddAuditLog("用户清空了审计日志");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region --- 12. 资源清理 (Resource Disposal) ---
|
||||
#region --- 14. 资源释放 (Disposal) ---
|
||||
|
||||
/// <summary>
|
||||
/// 同步销毁入口(死锁免疫)
|
||||
@@ -661,8 +735,6 @@ public abstract class BaseVideoSource : IVideoSource, IAsyncDisposable, IDeviceC
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
private volatile bool _isDisposed = false;
|
||||
|
||||
/// <summary>
|
||||
/// 异步销毁资源(优雅关闭)
|
||||
/// </summary>
|
||||
@@ -714,65 +786,4 @@ public abstract class BaseVideoSource : IVideoSource, IAsyncDisposable, IDeviceC
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region --- 13. 内部字段与补全属性 ---
|
||||
|
||||
/// <summary> 跟踪上一个未完成的生命周期任务 </summary>
|
||||
private Task _pendingLifecycleTask = Task.CompletedTask;
|
||||
|
||||
/// <summary> 帧控制器(用于帧分发策略管理) </summary>
|
||||
public FrameController Controller { get; protected set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region --- 14. 自愈辅助字段 (Resilience Helpers) ---
|
||||
|
||||
/// <summary>
|
||||
/// 认证类致命错误标记(如密码错、用户锁定)
|
||||
/// 作用:触发 15 分钟长冷冻期,防止 IP 被相机锁定
|
||||
/// </summary>
|
||||
public bool IsAuthFailed { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 上次尝试执行 StartAsync 的系统 Tick 时间 (单调时钟)
|
||||
/// </summary>
|
||||
private long _lastStartAttemptTick = 0;
|
||||
public long LastStartAttemptTick => Interlocked.Read(ref _lastStartAttemptTick);
|
||||
|
||||
/// <summary>
|
||||
/// 更新最后一次启动尝试的时间戳为当前时间
|
||||
/// </summary>
|
||||
public void MarkStartAttempt()
|
||||
{
|
||||
Interlocked.Exchange(ref _lastStartAttemptTick, Environment.TickCount64);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 强制重置自愈相关的冷却与错误标记
|
||||
/// 用于用户手动干预(如修改密码)后,使协调器能立即触发下一次尝试
|
||||
/// </summary>
|
||||
public void ResetResilience()
|
||||
{
|
||||
// 1. 清除认证失败标记
|
||||
IsAuthFailed = false;
|
||||
|
||||
// 2. 将尝试时间戳归零
|
||||
// 这样在 Coordinator 中计算 elapsed = now - 0,结果会远大于 30s
|
||||
Interlocked.Exchange(ref _lastStartAttemptTick, 0);
|
||||
|
||||
_sdkLog.Debug($"[Sdk] 设备 {Id} 自愈状态已人工重置");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
// 自动从 SmartFrame 中提取
|
||||
public int Width { get; protected set; }
|
||||
public int Height { get; protected set; }
|
||||
|
||||
public void ClearAuditLogs()
|
||||
{
|
||||
_auditLogs.Clear();
|
||||
AddAuditLog("用户清空了审计日志");
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user