增加大华驱动

This commit is contained in:
2026-01-17 19:17:49 +08:00
parent 0b4c6fe913
commit 927ba09f66
14 changed files with 162157 additions and 21 deletions

View File

@@ -103,14 +103,26 @@ namespace SHH.CameraSdk
// 因为帧进入异步处理环节,必须保证不会被外部提前释放
frame.AddRef();
// 关键操作2哈希分片路由
// 基于 DeviceId 取模,确保同一设备的帧始终分配给同一个 Worker
// 核心价值:保证单设备的帧处理顺序与原始流顺序一致
int workerIndex = (int)(Math.Abs(deviceId) % _workerCount);
var targetWorker = _workers[workerIndex];
try
{
// 关键操作2哈希分片路由
int workerIndex = (int)(Math.Abs(deviceId) % _workerCount);
var targetWorker = _workers[workerIndex];
// 将帧投递到目标 Worker 的任务队列
targetWorker.Post(deviceId, frame, decision);
// 将帧投递到目标 Worker 的任务队列
targetWorker.Post(deviceId, frame, decision);
}
catch (Exception ex)
{
// =========================================================
// 【修复 3】: 引用计数回滚
// 如果投递失败(例如计算 Index 溢出、Worker 列表为空等极端情况),
// 必须把刚才 AddRef 的一次计数释放掉,否则该帧将永久无法回池(内存泄漏)。
// =========================================================
_sysLog.Error(ex, $"[Core] 帧入队失败,回滚引用计数: DeviceId={deviceId}");
frame.Dispose();
throw; // 继续抛出异常通知上层
}
}
#endregion
@@ -218,43 +230,68 @@ namespace SHH.CameraSdk
/// Worker 线程的核心处理循环
/// 持续消费队列任务,直到收到取消信号
/// </summary>
// Modified: [BaseWorker.cs] 增加深度防御检查,防止处理僵尸帧
private void ProcessLoop()
{
try
{
// GetConsumingEnumerable阻塞式消费队列支持取消令牌
foreach (var taskItem in _taskQueue.GetConsumingEnumerable(_cts.Token))
{
// 关键操作:使用 using 语句自动释放引用
// 无论处理成功/失败,都会保证 Dispose 被调用,形成引用计数闭环
// 自动释放引用
using (var frame = taskItem.Frame)
{
try
{
// 调用子类实现的具体图像处理算法
// =========================================================
// 【修复 1】: 僵尸帧防御 (Zombie Frame Defense)
// 如果帧虽然还在引用计数内,但其内部的 OpenCV 内存已被销毁,
// 严禁调用 PerformAction否则直接 AccessViolation 崩溃。
// =========================================================
if (frame.InternalMat == null || frame.InternalMat.IsDisposed)
{
_gRpcLog.Warning($"[BaseWorker] 拦截到已销毁的帧: DeviceId={taskItem.DeviceId}, 丢弃.");
continue;
}
// =========================================================
// 【修复 2】: 空帧防御 (Empty Frame Defense)
// 大华 SDK 偶尔会输出 0x0 或空数据Resize 对空图操作必崩。
// =========================================================
if (frame.InternalMat.Empty())
{
_gRpcLog.Warning($"[BaseWorker] 拦截到空帧(Empty): DeviceId={taskItem.DeviceId}, 丢弃.");
continue;
}
// 只有检查通过,才执行具体的算法
PerformAction(taskItem.DeviceId, frame, taskItem.Decision);
// 通知父集群:当前帧处理完成,准备传递到下一个环节
// 通知完成
NotifyFinished(taskItem.DeviceId, frame, taskItem.Decision);
}
catch (ObjectDisposedException)
{
// 捕获特定的“对象已释放”异常,防止线程退出
_gRpcLog.Warning($"[BaseWorker] 帧在处理过程中被释放: DeviceId={taskItem.DeviceId}");
}
catch (Exception ex)
{
_gRpcLog.Information($"[gRpc] 帧处理异常BaseWorker 异常消息: {ex.Message}.");
_gRpcLog.Error(ex, $"[BaseWorker] 帧处理逻辑异常: DeviceId={taskItem.DeviceId}");
// 异常保底策略:即使处理失败,也透传帧到下一个环节,保证流水线不中断
NotifyFinished(taskItem.DeviceId, frame, taskItem.Decision);
// 即使处理失败,也可以选择是否继续传递,视业务而定
// NotifyFinished(taskItem.DeviceId, frame, taskItem.Decision);
}
}
}
}
catch (OperationCanceledException)
{
// 正常取消:线程退出,无需报错
_gRpcLog.Information($"[gRpc] BaseWorker 处理循环已正常终止.");
}
catch (Exception ex)
{
_gRpcLog.Error($"[gRpc] BaseWorker 处理循环异常终止.");
// 只有极其严重的系统级错误才会走到这里
_gRpcLog.Fatal(ex, $"[gRpc] BaseWorker 处理循环异常崩溃!");
}
}