增加大华驱动
This commit is contained in:
@@ -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 处理循环异常崩溃!");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user