using HalconDotNet; using LB_SmartVisionCommon; using LB_VisionProcesses.Cameras.LBCameras; using MVSDK_Net; using Sunny.UI; using System; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.Drawing.Imaging; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; namespace LB_VisionProcesses.Cameras.HRCameras { /// /// 华睿2D工业相机实现类 /// 基于MVSDK_Net SDK封装 /// public class HRCamera : BaseCamera { #region 私有字段 private MyCamera _camera; // 华睿相机设备对象 private Thread _grabThread; // 图像采集线程 private bool _isGrabbing; // 采集状态标志 private bool _threadRunning; // 线程运行标志 private bool _handleCreated = false; // 句柄是否已创建 private Thread _callbackThread; // 回调处理线程 private List _frameList; // 图像缓存列表 private readonly object _frameLock = new object(); // 帧缓存锁 // 新增:CollectedImages操作锁,保证线程安全 private readonly object _collectedImagesLock = new object(); private IMVDefine.IMV_EPixelType type = IMVDefine.IMV_EPixelType.gvspPixelMono8; private IMVDefine.IMV_PixelConvertParam stPixelConvertParam = new IMVDefine.IMV_PixelConvertParam(); HObject Hobj = new HObject(); IntPtr pTemp = IntPtr.Zero; IntPtr pConvertDstBuffer = IntPtr.Zero; int nConvertBufSize = 0; private IMVDefine.IMV_FrameCallBack frameCallBack; // CopyMemory API声明 [System.Runtime.InteropServices.DllImport("kernel32.dll", EntryPoint = "RtlMoveMemory")] private static extern void CopyMemory(IntPtr dest, IntPtr src, uint count); #endregion #region 构造函数和析构函数 /// /// 构造函数 /// public HRCamera() { _camera = new MyCamera(); _frameList = new List(); Brand = CameraBrand.HRCamera; _isGrabbing = false; _threadRunning = false; } /// /// 析构函数 /// ~HRCamera() { Dispose(false); } #endregion #region 设备管理操作 /// /// 获取相机SN枚举列表 /// /// 相机SN列表 public override List GetListEnum() { List cameraList = new List(); try { // 枚举所有设备 IMVDefine.IMV_DeviceList deviceList = new IMVDefine.IMV_DeviceList(); IMVDefine.IMV_EInterfaceType interfaceType = IMVDefine.IMV_EInterfaceType.interfaceTypeAll; int result = MyCamera.IMV_EnumDevices(ref deviceList, (uint)interfaceType); if (result == IMVDefine.IMV_OK && deviceList.nDevNum > 0) { for (int i = 0; i < deviceList.nDevNum; i++) { IMVDefine.IMV_DeviceInfo deviceInfo = (IMVDefine.IMV_DeviceInfo) Marshal.PtrToStructure( deviceList.pDevInfo + Marshal.SizeOf(typeof(IMVDefine.IMV_DeviceInfo)) * i, typeof(IMVDefine.IMV_DeviceInfo)); string cameraInfo = $"{deviceInfo.cameraName}[{deviceInfo.serialNumber}]"; cameraList.Add(cameraInfo); } } else { // 记录日志或抛出异常 //throw new Exception($"枚举设备失败,错误码:{result}"); AsyncLogHelper.Error("枚举设备失败!"); System.Diagnostics.Debug.WriteLine("枚举设备失败!"); } } catch (Exception ex) { // 记录错误日志 System.Diagnostics.Debug.WriteLine($"获取相机列表失败:{ex.Message}"); AsyncLogHelper.Error($"获取相机列表失败:{ex.Message}"); throw; } return cameraList; } /// /// 初始化相机设备 /// /// 相机序列号 /// 窗口句柄(可选) /// 初始化是否成功 public override bool InitDevice(string SN, object Handle = null) { try { // 确保彻底关闭和清理 if (_camera != null) { if (_camera.IMV_IsOpen()) { _camera.IMV_Close(); } if (_handleCreated) { _camera.IMV_DestroyHandle(); _handleCreated = false; } } // 枚举设备并匹配SN List cameraList = GetListEnum(); int cameraIndex = -1; for (int i = 0; i < cameraList.Count; i++) { if (cameraList[i].Contains(SN)) { cameraIndex = i; break; } } if (cameraIndex == -1) { AsyncLogHelper.Error($"未找到序列号为 {SN} 的相机"); throw new Exception($"未找到序列号为 {SN} 的相机"); } // 创建设备句柄 int result = _camera.IMV_CreateHandle(IMVDefine.IMV_ECreateHandleMode.modeByIndex, cameraIndex); if (result != IMVDefine.IMV_OK) { AsyncLogHelper.Error($"创建设备句柄失败,错误码:{result}"); throw new Exception($"创建设备句柄失败,错误码:{result}"); } _handleCreated = true; // 打开设备 result = _camera.IMV_Open(); if (result != IMVDefine.IMV_OK) { AsyncLogHelper.Error($"打开相机失败,错误码:{result}"); throw new Exception($"打开相机失败,错误码:{result}"); } // 设置设备属性 this.SN = SN; isGrabbing = false; // 注册数据帧回调函数 // Register data frame callback function frameCallBack = onGetFrame; int res = _camera.IMV_AttachGrabbing(frameCallBack, IntPtr.Zero); if (res != IMVDefine.IMV_OK) { System.Diagnostics.Debug.WriteLine("Attach grabbing failed! ErrorCode:[{0}]", res); AsyncLogHelper.Error("Attach grabbing failed! ErrorCode:[{0}]" + res); } // 设置缓存个数为12 _camera.IMV_SetBufferCount(12); return true; } catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"初始化相机失败:{ex.Message}"); AsyncLogHelper.Error($"初始化相机失败:{ex.Message}"); return false; } } // 数据帧回调函数 // Data frame callback function private void onGetFrame(ref IMVDefine.IMV_Frame frame, IntPtr pUser) { if (frame.frameHandle == IntPtr.Zero) { AsyncLogHelper.Info(SN + "frame is NULL"); return; } try { // 1. 将帧转换为Bitmap Bitmap bitmap = ConvertFrameToBitmap(frame); // 释放原始帧数据(SDK层面释放) _camera.IMV_ReleaseFrame(ref frame); // 2. 空值校验:转换失败则直接返回 if (bitmap == null) { AsyncLogHelper.Warn(SN + "帧转换为Bitmap失败,跳过处理"); return; } // 3. 获取/创建线程安全队列 var queue = CollectedImages.GetOrAdd(SN, new ConcurrentQueue()); AsyncLogHelper.Info(SN + $"图像已加入缓存,当前缓存数量:{CollectedImages[SN].Count}"); // 4. 队列限流,防止内存溢出 if (queue.Count >= MAX_QUEUE_CAPACITY) { if (queue.TryDequeue(out Bitmap old)) { old.Dispose(); // 丢弃最旧帧,释放内存 AsyncLogHelper.Warn($"HRCamera[{SN}]: 队列已满,自动丢弃最旧帧"); } } // 5. 入队 queue.Enqueue(bitmap); AsyncLogHelper.Info($"LBCamera[{SN}]: 图像入队,当前队列:{queue.Count}"); // 6. 启动队列(单例,避免多线程重复) StartConsumeQueue(); //Task.Factory.StartNew(() => //{ // CallBackImg = (Bitmap)bitmap.Clone(); // if (CallBackImg == null) // { // return; // } // if (GetTriggerMode(out TriggerMode mode, out TriggerSource source)) // { // if (mode == TriggerMode.On && source != TriggerSource.Software) // TriggerRunMessageReceived?.Invoke(SN, source.ToString()); // 触发运行事件 // } // bitmap.Dispose(); //}); } catch { } AsyncLogHelper.Info(SN + "Get frame blockId = {0}" + frame.frameInfo.blockId); } /// /// 启动队列(保证单线程) /// private void StartConsumeQueue() { // 使用轻量级判断,避免重复启动消费任务 if (CollectedImages.TryGetValue(SN, out var queue) && !queue.IsEmpty) { Task.Factory.StartNew(ProcessImageQueue, TaskCreationOptions.LongRunning); } } /// /// 处理CollectedImages中的缓存图像 /// 核心逻辑:遍历取第一个图像 -> 赋值给CallBackImg -> 触发事件 -> 释放并移除 /// private void ProcessImageQueue() { try { if (!CollectedImages.TryGetValue(SN, out var queue) || queue.IsEmpty) return; // 短锁:仅出队,不阻塞生产 while (queue.TryDequeue(out Bitmap bitmap)) { using (bitmap) // 自动释放:using 是最安全的方式 { try { // 关键:事件传递克隆对象,绝对安全,不传递原资源 using (Bitmap clone = (Bitmap)bitmap.Clone()) { // 触发图像事件 ImageGrabbed?.Invoke(this, new LBCameraEventArgs(SN, clone, true)); CallBackImg = (Bitmap)clone.Clone(); } // 触发模式判断 if (GetTriggerMode(out TriggerMode mode, out TriggerSource source)) { if (mode == TriggerMode.On && source != TriggerSource.Software) { TriggerRunMessageReceived?.Invoke(SN, source.ToString()); AsyncLogHelper.Info($"LBCamera[{SN}]: 硬触发事件 - {source}"); } } } catch (Exception ex) { AsyncLogHelper.Error($"LBCamera[{SN}]: 处理单帧图像异常 - {ex.Message}", ex); continue; // 单帧异常,继续处理下一帧 } } } } catch (Exception ex) { AsyncLogHelper.Error($"LBCamera[{SN}]: 消费队列异常 - {ex.Message}", ex); } } /// /// 图像是否为Mono格式 /// /// /// private bool IsMonoPixelFormat(IMVDefine.IMV_EPixelType enType) { switch (enType) { case IMVDefine.IMV_EPixelType.gvspPixelMono8: case IMVDefine.IMV_EPixelType.gvspPixelMono10: case IMVDefine.IMV_EPixelType.gvspPixelMono10Packed: case IMVDefine.IMV_EPixelType.gvspPixelMono12: case IMVDefine.IMV_EPixelType.gvspPixelMono12Packed: return true; default: return false; } } /// /// 图像是否为彩色 /// /// /// private bool IsColorPixelFormat(IMVDefine.IMV_EPixelType enType) { switch (enType) { case IMVDefine.IMV_EPixelType.gvspPixelRGB8: case IMVDefine.IMV_EPixelType.gvspPixelBGR8: case IMVDefine.IMV_EPixelType.gvspPixelRGBA8: case IMVDefine.IMV_EPixelType.gvspPixelBGRA8: case IMVDefine.IMV_EPixelType.gvspPixelYUV422_8: case IMVDefine.IMV_EPixelType.gvspPixelYUV422_8_UYVY: case IMVDefine.IMV_EPixelType.gvspPixelBayGR8: case IMVDefine.IMV_EPixelType.gvspPixelBayRG8: case IMVDefine.IMV_EPixelType.gvspPixelBayGB8: case IMVDefine.IMV_EPixelType.gvspPixelBayBG8: case IMVDefine.IMV_EPixelType.gvspPixelBayGB10: case IMVDefine.IMV_EPixelType.gvspPixelBayGB10Packed: case IMVDefine.IMV_EPixelType.gvspPixelBayBG10: case IMVDefine.IMV_EPixelType.gvspPixelBayBG10Packed: case IMVDefine.IMV_EPixelType.gvspPixelBayRG10: case IMVDefine.IMV_EPixelType.gvspPixelBayRG10Packed: case IMVDefine.IMV_EPixelType.gvspPixelBayGR10: case IMVDefine.IMV_EPixelType.gvspPixelBayGR10Packed: case IMVDefine.IMV_EPixelType.gvspPixelBayGB12: case IMVDefine.IMV_EPixelType.gvspPixelBayGB12Packed: case IMVDefine.IMV_EPixelType.gvspPixelBayBG12: case IMVDefine.IMV_EPixelType.gvspPixelBayBG12Packed: case IMVDefine.IMV_EPixelType.gvspPixelBayRG12: case IMVDefine.IMV_EPixelType.gvspPixelBayRG12Packed: case IMVDefine.IMV_EPixelType.gvspPixelBayGR12: case IMVDefine.IMV_EPixelType.gvspPixelBayGR12Packed: return true; default: return false; } } /// /// 关闭相机设备 /// /// 关闭是否成功 public override bool CloseDevice() { try { if (_camera == null) return true; // 停止采集 if (_camera.IMV_IsGrabbing()) { StopGrabbing(); } // 关闭设备 if (_camera.IMV_IsOpen()) { int result = _camera.IMV_Close(); if (result != IMVDefine.IMV_OK) { System.Diagnostics.Debug.WriteLine($"关闭相机失败,错误码:{result}"); AsyncLogHelper.Info(SN + $"关闭相机失败,错误码:{result}"); } } // 销毁句柄 if (_handleCreated) { _camera.IMV_DestroyHandle(); _handleCreated = false; } // 释放资源 _isGrabbing = false; isGrabbing = false; // 清理帧缓存 lock (_frameLock) { foreach (var frame in _frameList) { var tempFrame = frame; // 创建临时变量 _camera.IMV_ReleaseFrame(ref tempFrame); } _frameList.Clear(); } return true; } catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"关闭相机失败:{ex.Message}"); AsyncLogHelper.Info(SN + $"关闭相机失败:{ex.Message}"); return false; } } #endregion #region 图像采集操作 /// /// 开始图像采集 /// /// 采集启动是否成功 public override bool StartGrabbing() { try { if (_camera == null || !_camera.IMV_IsOpen()) { AsyncLogHelper.Error(SN + "相机未打开"); throw new Exception(SN + "相机未打开"); } // 停止现有采集 if (_isGrabbing) { StopGrabbing(); } // 启动采集 int result = _camera.IMV_StartGrabbing(); if (result != IMVDefine.IMV_OK) { AsyncLogHelper.Error(SN + $"启动采集失败,错误码:{result}"); throw new Exception(SN + $"启动采集失败,错误码:{result}"); } _isGrabbing = true; isGrabbing = true; // 启动采集线程 _threadRunning = true; _grabThread = new Thread(GrabThreadProc); _grabThread.IsBackground = true; _grabThread.Start(); return true; } catch (Exception ex) { System.Diagnostics.Debug.WriteLine(SN + $"启动采集失败:{ex.Message}"); AsyncLogHelper.Error(SN + $"启动采集失败:{ex.Message}"); _isGrabbing = false; isGrabbing = false; return false; } } /// /// 停止图像采集 /// /// 停止是否成功 public override bool StopGrabbing() { try { if (_camera == null || !_isGrabbing) return true; // 停止线程 _threadRunning = false; if (_grabThread != null && _grabThread.IsAlive) { _grabThread.Join(1000); _grabThread = null; } // 停止采集 int result = _camera.IMV_StopGrabbing(); if (result != IMVDefine.IMV_OK) { System.Diagnostics.Debug.WriteLine(SN + $"停止采集失败,错误码:{result}"); AsyncLogHelper.Info(SN + $"停止采集失败,错误码:{result}"); } _isGrabbing = false; isGrabbing = false; // 清理缓存 lock (_frameLock) { foreach (var frame in _frameList) { var tempFrame = frame; // 创建临时变量 _camera.IMV_ReleaseFrame(ref tempFrame); } _frameList.Clear(); } return true; } catch (Exception ex) { System.Diagnostics.Debug.WriteLine(SN + $"停止采集失败:{ex.Message}"); AsyncLogHelper.Error(SN + $"停止采集失败:{ex.Message}"); return false; } } /// /// 执行软触发 /// /// 触发是否成功 public override bool SoftTrigger() { try { if (_camera == null || !_camera.IMV_IsOpen()) { AsyncLogHelper.Error(SN + "相机未打开"); throw new Exception(SN + "相机未打开"); } int result = _camera.IMV_ExecuteCommandFeature("TriggerSoftware"); return result == IMVDefine.IMV_OK; } catch (Exception ex) { System.Diagnostics.Debug.WriteLine(SN + $"软触发失败:{ex.Message}"); AsyncLogHelper.Error(SN + $"软触发失败:{ex.Message}"); return false; } } #endregion #region 参数设置和获取 /// /// 设置触发模式 /// /// 触发模式 /// 触发源 /// 设置是否成功 public override bool SetTriggerMode(TriggerMode mode, TriggerSource source = TriggerSource.Line0) { try { if (_camera == null || !_camera.IMV_IsOpen()) { AsyncLogHelper.Error(SN + "相机未打开"); throw new Exception(SN + "相机未打开"); } // 设置触发模式 string triggerMode = mode == TriggerMode.On ? "On" : "Off"; int result = _camera.IMV_SetEnumFeatureSymbol("TriggerMode", triggerMode); if (result != IMVDefine.IMV_OK) { AsyncLogHelper.Error(SN + $"设置触发模式失败,错误码:{result}"); throw new Exception(SN + $"设置触发模式失败,错误码:{result}"); } // 设置触发源 if (mode == TriggerMode.On) { string triggerSource = GetTriggerSourceString(source); result = _camera.IMV_SetEnumFeatureSymbol("TriggerSource", triggerSource); if (result != IMVDefine.IMV_OK) { AsyncLogHelper.Error(SN + $"设置触发源失败,错误码:{result}"); throw new Exception(SN + $"设置触发源失败,错误码:{result}"); } } return true; } catch (Exception ex) { System.Diagnostics.Debug.WriteLine(SN + $"设置触发模式失败:{ex.Message}"); AsyncLogHelper.Error(SN + $"设置触发模式失败:{ex.Message}"); return false; } } /// /// 获取触发模式 /// /// 触发模式 /// 触发源 /// 获取是否成功 public override bool GetTriggerMode(out TriggerMode mode, out TriggerSource source) { mode = TriggerMode.Off; source = TriggerSource.Software; try { if (_camera == null || !_camera.IMV_IsOpen()) { AsyncLogHelper.Error(SN + "相机未打开"); throw new Exception(SN + "相机未打开"); } // 获取触发模式 IMVDefine.IMV_String triggerMode = new IMVDefine.IMV_String(); int result = _camera.IMV_GetEnumFeatureSymbol("TriggerMode", ref triggerMode); if (result == IMVDefine.IMV_OK) { mode = triggerMode.str == "On" ? TriggerMode.On : TriggerMode.Off; } // 获取触发源 IMVDefine.IMV_String triggerSource = new IMVDefine.IMV_String(); result = _camera.IMV_GetEnumFeatureSymbol("TriggerSource", ref triggerSource); if (result == IMVDefine.IMV_OK) { source = GetTriggerSourceFromString(triggerSource.str); } return true; } catch (Exception ex) { System.Diagnostics.Debug.WriteLine(SN + $"获取触发模式失败:{ex.Message}"); AsyncLogHelper.Error(SN + $"获取触发模式失败:{ex.Message}"); return false; } } /// /// 设置曝光时间 /// /// 曝光时间(微秒) /// 设置是否成功 public override bool SetExpouseTime(double value) { try { if (_camera == null || !_camera.IMV_IsOpen()) { AsyncLogHelper.Error(SN + "相机未打开"); throw new Exception(SN + "相机未打开"); } // 验证曝光时间范围 double minExp = 0, maxExp = 0; int result = _camera.IMV_GetDoubleFeatureMin("ExposureTime", ref minExp); if (result != IMVDefine.IMV_OK) { AsyncLogHelper.Error(SN + $"获取曝光时间最小值失败,错误码:{result}"); throw new Exception(SN + $"获取曝光时间最小值失败,错误码:{result}"); } result = _camera.IMV_GetDoubleFeatureMax("ExposureTime", ref maxExp); if (result != IMVDefine.IMV_OK) { AsyncLogHelper.Error(SN + $"获取曝光时间最大值失败,错误码:{result}"); throw new Exception(SN + $"获取曝光时间最大值失败,错误码:{result}"); } if (value < minExp || value > maxExp) { AsyncLogHelper.Error(SN + $"曝光时间超出范围,有效范围:{minExp} - {maxExp}"); throw new Exception(SN + $"曝光时间超出范围,有效范围:{minExp} - {maxExp}"); } // 设置曝光时间 result = _camera.IMV_SetDoubleFeatureValue("ExposureTime", value); if (result != IMVDefine.IMV_OK) { AsyncLogHelper.Error(SN + $"设置曝光时间失败,错误码:{result}"); throw new Exception(SN + $"设置曝光时间失败,错误码:{result}"); } return true; } catch (Exception ex) { AsyncLogHelper.Error(SN + $"设置曝光时间失败:{ex.Message}"); System.Diagnostics.Debug.WriteLine(SN + $"设置曝光时间失败:{ex.Message}"); return false; } } /// /// 获取曝光时间 /// /// 曝光时间 /// 获取是否成功 public override bool GetExpouseTime(out double value) { value = 0; try { if (_camera == null || !_camera.IMV_IsOpen()) { AsyncLogHelper.Error(SN + "相机未打开"); throw new Exception(SN + "相机未打开"); } int result = _camera.IMV_GetDoubleFeatureValue("ExposureTime", ref value); return result == IMVDefine.IMV_OK; } catch (Exception ex) { System.Diagnostics.Debug.WriteLine(SN + $"获取曝光时间失败:{ex.Message}"); AsyncLogHelper.Error(SN + $"获取曝光时间失败:{ex.Message}"); return false; } } /// /// 设置增益 /// /// 增益值 /// 设置是否成功 public override bool SetGain(double gain) { try { if (_camera == null || !_camera.IMV_IsOpen()) { AsyncLogHelper.Error(SN + "相机未打开"); throw new Exception(SN + "相机未打开"); } string gainFeature = _camera.IMV_FeatureIsValid("Gain") ? "Gain" : "GainRaw"; // 验证增益范围 double minGain = 0, maxGain = 0; int result = _camera.IMV_GetDoubleFeatureMin(gainFeature, ref minGain); if (result != IMVDefine.IMV_OK) { AsyncLogHelper.Error(SN + $"获取增益最小值失败,错误码:{result}"); throw new Exception(SN + $"获取增益最小值失败,错误码:{result}"); } result = _camera.IMV_GetDoubleFeatureMax(gainFeature, ref maxGain); if (result != IMVDefine.IMV_OK) { AsyncLogHelper.Error(SN + $"获取增益最大值失败,错误码:{result}"); throw new Exception(SN + $"获取增益最大值失败,错误码:{result}"); } if (gain < minGain) gain = minGain; if (gain > maxGain) gain = maxGain; // 设置增益 result = _camera.IMV_SetDoubleFeatureValue(gainFeature, gain); if (result != IMVDefine.IMV_OK) { AsyncLogHelper.Error(SN + $"设置增益失败,错误码:{result}"); throw new Exception(SN + $"设置增益失败,错误码:{result}"); } return true; } catch (Exception ex) { System.Diagnostics.Debug.WriteLine(SN + $"设置增益失败:{ex.Message}"); AsyncLogHelper.Error(SN + $"设置增益失败:{ex.Message}"); return false; } } /// /// 获取增益值 /// /// 增益值 /// 获取是否成功 public override bool GetGain(out double gain) { gain = 0; try { if (_camera == null || !_camera.IMV_IsOpen()) { AsyncLogHelper.Error(SN + "相机未打开"); throw new Exception(SN + "相机未打开"); } string gainFeature = _camera.IMV_FeatureIsValid("Gain") ? "Gain" : "GainRaw"; int result = _camera.IMV_GetDoubleFeatureValue(gainFeature, ref gain); return result == IMVDefine.IMV_OK; } catch (Exception ex) { System.Diagnostics.Debug.WriteLine(SN + $"获取增益失败:{ex.Message}"); AsyncLogHelper.Error(SN + $"获取增益失败:{ex.Message}"); return false; } } /// /// 设置触发极性 /// /// 触发极性 /// 是否成功 public override bool SetTriggerPolarity(TriggerPolarity polarity) { try { if (_camera == null || !_camera.IMV_IsOpen()) return false; string activation = (polarity == TriggerPolarity.RisingEdge || polarity == TriggerPolarity.HighLevel) ? "RisingEdge" : "FallingEdge"; int result = _camera.IMV_SetEnumFeatureSymbol("TriggerActivation", activation); return result == IMVDefine.IMV_OK; } catch (Exception ex) { System.Diagnostics.Debug.WriteLine(SN + $"设置触发极性失败:{ex.Message}"); AsyncLogHelper.Error(SN + $"设置触发极性失败:{ex.Message}"); return false; } } /// /// 获取触发极性 /// /// 触发极性 /// 是否成功 public override bool GetTriggerPolarity(out TriggerPolarity polarity) { polarity = TriggerPolarity.RisingEdge; try { if (_camera == null || !_camera.IMV_IsOpen()) return false; IMVDefine.IMV_String activation = new IMVDefine.IMV_String(); int result = _camera.IMV_GetEnumFeatureSymbol("TriggerActivation", ref activation); if (result == IMVDefine.IMV_OK) { polarity = activation.str == "RisingEdge" ? TriggerPolarity.RisingEdge : TriggerPolarity.FallingEdge; return true; } return false; } catch (Exception ex) { System.Diagnostics.Debug.WriteLine(SN + $"获取触发极性失败:{ex.Message}"); AsyncLogHelper.Error(SN + $"获取触发极性失败:{ex.Message}"); return false; } } /// /// 设置触发滤波时间 (us) /// public override bool SetTriggerFliter(double flitertime) { try { if (_camera == null || !_camera.IMV_IsOpen()) return false; // 华睿相机通常使用 LineDebouncerTime 控制滤波 if (_camera.IMV_FeatureIsValid("LineDebouncerTime")) { int result = _camera.IMV_SetDoubleFeatureValue("LineDebouncerTime", flitertime); return result == IMVDefine.IMV_OK; } return false; } catch (Exception ex) { System.Diagnostics.Debug.WriteLine(SN + $"设置触发滤波失败:{ex.Message}"); AsyncLogHelper.Error(SN + $"设置触发滤波失败:{ex.Message}"); return false; } } /// /// 获取触发滤波时间 (us) /// public override bool GetTriggerFliter(out double flitertime) { flitertime = 0; try { if (_camera == null || !_camera.IMV_IsOpen()) return false; if (_camera.IMV_FeatureIsValid("LineDebouncerTime")) { int result = _camera.IMV_GetDoubleFeatureValue("LineDebouncerTime", ref flitertime); return result == IMVDefine.IMV_OK; } return false; } catch (Exception ex) { System.Diagnostics.Debug.WriteLine(SN + $"获取触发滤波失败:{ex.Message}"); AsyncLogHelper.Error(SN + $"获取触发滤波失败:{ex.Message}"); return false; } } /// /// 设置触发延时 (us) /// public override bool SetTriggerDelay(double delay) { try { if (_camera == null || !_camera.IMV_IsOpen()) return false; if (_camera.IMV_FeatureIsValid("TriggerDelay")) { int result = _camera.IMV_SetDoubleFeatureValue("TriggerDelay", delay); return result == IMVDefine.IMV_OK; } return false; } catch (Exception ex) { System.Diagnostics.Debug.WriteLine(SN + $"设置触发延时失败:{ex.Message}"); AsyncLogHelper.Error(SN + $"设置触发延时失败:{ex.Message}"); return false; } } /// /// 获取触发延时 (us) /// public override bool GetTriggerDelay(out double delay) { delay = 0; try { if (_camera == null || !_camera.IMV_IsOpen()) return false; if (_camera.IMV_FeatureIsValid("TriggerDelay")) { int result = _camera.IMV_GetDoubleFeatureValue("TriggerDelay", ref delay); return result == IMVDefine.IMV_OK; } return false; } catch (Exception ex) { System.Diagnostics.Debug.WriteLine(SN + $"获取触发延时失败:{ex.Message}"); AsyncLogHelper.Error(SN + $"获取触发延时失败:{ex.Message}"); return false; } } /// /// 设置信号线模式 /// public override bool SetLineMode(IOLines line, LineMode mode) { try { if (_camera == null || !_camera.IMV_IsOpen()) return false; // 选择线路 int result = _camera.IMV_SetEnumFeatureSymbol("LineSelector", line.ToString()); if (result != IMVDefine.IMV_OK) return false; // 设置模式 string lineMode = mode == LineMode.Input ? "Input" : "Output"; result = _camera.IMV_SetEnumFeatureSymbol("LineMode", lineMode); return result == IMVDefine.IMV_OK; } catch (Exception ex) { System.Diagnostics.Debug.WriteLine(SN + $"设置信号线模式失败:{ex.Message}"); AsyncLogHelper.Error(SN + $"设置信号线模式失败:{ex.Message}"); return false; } } /// /// 设置信号线电平状态 /// public override bool SetLineStatus(IOLines line, LineStatus linestatus) { try { if (_camera == null || !_camera.IMV_IsOpen()) return false; // 仅对输出线路有效 int result = _camera.IMV_SetEnumFeatureSymbol("LineSelector", line.ToString()); if (result != IMVDefine.IMV_OK) return false; bool status = linestatus == LineStatus.Hight; result = _camera.IMV_SetBoolFeatureValue("UserOutputValue", status); return result == IMVDefine.IMV_OK; } catch (Exception ex) { System.Diagnostics.Debug.WriteLine(SN + $"设置信号线状态失败:{ex.Message}"); AsyncLogHelper.Error(SN + $"设置信号线状态失败:{ex.Message}"); return false; } } /// /// 获取信号线电平状态 /// public override bool GetLineStatus(IOLines line, out LineStatus lineStatus) { lineStatus = LineStatus.Low; try { if (_camera == null || !_camera.IMV_IsOpen()) return false; int result = _camera.IMV_SetEnumFeatureSymbol("LineSelector", line.ToString()); if (result != IMVDefine.IMV_OK) return false; bool status = false; result = _camera.IMV_GetBoolFeatureValue("LineStatus", ref status); if (result == IMVDefine.IMV_OK) { lineStatus = status ? LineStatus.Hight : LineStatus.Low; return true; } return false; } catch (Exception ex) { System.Diagnostics.Debug.WriteLine(SN + $"获取信号线状态失败:{ex.Message}"); AsyncLogHelper.Error(SN + $"获取信号线状态失败:{ex.Message}"); return false; } } /// /// 自动白平衡 /// public override bool AutoBalanceWhite() { try { if (_camera == null || !_camera.IMV_IsOpen()) return false; if (_camera.IMV_FeatureIsValid("BalanceWhiteAuto")) { int result = _camera.IMV_SetEnumFeatureSymbol("BalanceWhiteAuto", "Once"); return result == IMVDefine.IMV_OK; } return false; } catch (Exception ex) { System.Diagnostics.Debug.WriteLine(SN + $"自动白平衡失败:{ex.Message}"); AsyncLogHelper.Error(SN + $"自动白平衡失败:{ex.Message}"); return false; } } #endregion #region 采集和转换辅助方法 /// /// 图像采集线程处理函数 /// private void GrabThreadProc() { while (_threadRunning) { IMVDefine.IMV_Frame frame = new IMVDefine.IMV_Frame(); try { // 获取图像帧 int result = _camera.IMV_GetFrame(ref frame, 1000); if (result == IMVDefine.IMV_OK) { // 处理图像帧 ProcessFrame(frame); } else { // 即使获取失败,也尝试释放帧,防止SDK内部缓存泄露 // 注意:frame是每次新建的,如果GetFrame没填充,这里释放应该是安全的(视SDK实现而定) var tempFrame = frame; _camera.IMV_ReleaseFrame(ref tempFrame); if ((uint)result != 0x80000001 && result != -119 && result != -102) // 超时错误代码 { // 非超时错误 System.Diagnostics.Debug.WriteLine(SN + $"获取图像帧失败,错误码:{result}"); AsyncLogHelper.Error(SN + $"获取图像帧失败,错误码:{result}"); Thread.Sleep(10); // 出错时稍作等待 } } } catch (Exception ex) { System.Diagnostics.Debug.WriteLine(SN + $"采集线程异常:{ex.Message}"); AsyncLogHelper.Error(SN + $"采集线程异常:{ex.Message}"); Thread.Sleep(10); } Thread.Sleep(1); } } /// /// 处理图像帧 /// /// 图像帧 private void ProcessFrame(IMVDefine.IMV_Frame frame) { try { // 将图像数据转换为Bitmap Bitmap bitmap = ConvertFrameToBitmap(frame); if (bitmap != null) { // 触发图像采集事件 CameraEventArgs args = new CameraEventArgs(SN, bitmap); ImageGrabbed?.Invoke(this, args); // 更新回调图像 CallBackImg = bitmap; } } catch (Exception ex) { System.Diagnostics.Debug.WriteLine(SN + $"处理图像帧失败:{ex.Message}"); AsyncLogHelper.Error(SN + $"处理图像帧失败:{ex.Message}"); } finally { // 确保无论如何都释放帧,防止缓存占满 var tempFrame = frame; _camera.IMV_ReleaseFrame(ref tempFrame); } } /// /// 将图像帧转换为Bitmap /// /// 图像帧 /// Bitmap对象 private Bitmap ConvertFrameToBitmap(IMVDefine.IMV_Frame frame) { try { Bitmap bitmap = null; switch (frame.frameInfo.pixelFormat) { case IMVDefine.IMV_EPixelType.gvspPixelMono8: bitmap = CreateMono8Bitmap(frame); break; case IMVDefine.IMV_EPixelType.gvspPixelBGR8: bitmap = CreateBgr8Bitmap(frame); break; default: // 其他格式需要转换 bitmap = ConvertToBGR8(frame); break; } return bitmap; } catch (Exception ex) { System.Diagnostics.Debug.WriteLine(SN + $"图像格式转换失败:{ex.Message}"); AsyncLogHelper.Error(SN + $"图像格式转换失败:{ex.Message}"); return null; } } /// /// 创建Mono8格式Bitmap /// private Bitmap CreateMono8Bitmap(IMVDefine.IMV_Frame frame) { Bitmap bitmap = new Bitmap((int)frame.frameInfo.width, (int)frame.frameInfo.height, PixelFormat.Format8bppIndexed); // 设置灰度调色板 ColorPalette palette = bitmap.Palette; for (int i = 0; i < 256; i++) { palette.Entries[i] = Color.FromArgb(i, i, i); } bitmap.Palette = palette; // 复制图像数据 BitmapData bmpData = bitmap.LockBits( new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.WriteOnly, bitmap.PixelFormat); // 使用CopyMemory API进行内存复制 CopyMemory(bmpData.Scan0, frame.pData, (uint)Math.Min(frame.frameInfo.size, (uint)(bmpData.Stride * bitmap.Height))); bitmap.UnlockBits(bmpData); return bitmap; } /// /// 创建BGR8格式Bitmap /// private Bitmap CreateBgr8Bitmap(IMVDefine.IMV_Frame frame) { Bitmap bitmap = new Bitmap((int)frame.frameInfo.width, (int)frame.frameInfo.height, PixelFormat.Format24bppRgb); BitmapData bmpData = bitmap.LockBits( new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.WriteOnly, bitmap.PixelFormat); // 使用CopyMemory API进行内存复制 CopyMemory(bmpData.Scan0, frame.pData, (uint)Math.Min(frame.frameInfo.size, (uint)(bmpData.Stride * bitmap.Height))); bitmap.UnlockBits(bmpData); return bitmap; } /// /// 转换为BGR8格式 /// private Bitmap ConvertToBGR8(IMVDefine.IMV_Frame frame) { IMVDefine.IMV_PixelConvertParam convertParam = new IMVDefine.IMV_PixelConvertParam { nWidth = frame.frameInfo.width, nHeight = frame.frameInfo.height, ePixelFormat = frame.frameInfo.pixelFormat, pSrcData = frame.pData, nSrcDataLen = frame.frameInfo.size, nPaddingX = frame.frameInfo.paddingX, nPaddingY = frame.frameInfo.paddingY, eBayerDemosaic = IMVDefine.IMV_EBayerDemosaic.demosaicBilinear, eDstPixelFormat = IMVDefine.IMV_EPixelType.gvspPixelBGR8, nDstBufSize = frame.frameInfo.width * frame.frameInfo.height * 3 }; IntPtr dstBuffer = Marshal.AllocHGlobal((int)convertParam.nDstBufSize); convertParam.pDstBuf = dstBuffer; try { int result = _camera.IMV_PixelConvert(ref convertParam); if (result == IMVDefine.IMV_OK) { return CreateBgr8BitmapFromBuffer(dstBuffer, frame.frameInfo.width, frame.frameInfo.height); } return null; } finally { Marshal.FreeHGlobal(dstBuffer); } } /// /// 从缓冲区创建BGR8 Bitmap /// private Bitmap CreateBgr8BitmapFromBuffer(IntPtr buffer, uint width, uint height) { Bitmap bitmap = new Bitmap((int)width, (int)height, PixelFormat.Format24bppRgb); BitmapData bmpData = bitmap.LockBits( new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.WriteOnly, bitmap.PixelFormat); // 使用CopyMemory API进行安全的内存复制 CopyMemory(bmpData.Scan0, buffer, (uint)(width * height * 3)); bitmap.UnlockBits(bmpData); return bitmap; } /// /// 将触发源枚举转换为字符串 /// private string GetTriggerSourceString(TriggerSource source) { return source switch { TriggerSource.Software => "Software", TriggerSource.Line0 => "Line0", TriggerSource.Line1 => "Line1", TriggerSource.Line2 => "Line2", TriggerSource.Line3 => "Line3", TriggerSource.Line4 => "Line4", TriggerSource.Line5 => "Line5", _ => "Software" }; } /// /// 将字符串转换为触发源枚举 /// private TriggerSource GetTriggerSourceFromString(string source) { return source switch { "Software" => TriggerSource.Software, "Line0" => TriggerSource.Line0, "Line1" => TriggerSource.Line1, "Line2" => TriggerSource.Line2, "Line3" => TriggerSource.Line3, "Line4" => TriggerSource.Line4, "Line5" => TriggerSource.Line5, _ => TriggerSource.Software }; } #endregion #region IDisposable实现 private bool _disposed = false; protected virtual void Dispose(bool disposing) { if (!_disposed) { if (disposing) { // 释放托管资源 } // 释放非托管资源 CloseDevice(); _disposed = true; } } public override void Dispose() { Dispose(true); GC.SuppressFinalize(this); } public override bool StartWith_SoftTriggerModel() { SetTriggerMode(TriggerMode.Off, TriggerSource.Software); return StartGrabbing(); } public override bool StartWith_HardTriggerModel(TriggerSource hardtriggeritem = TriggerSource.Line0) { if (hardtriggeritem == TriggerSource.Software) hardtriggeritem = TriggerSource.Line0; SetTriggerMode(TriggerMode.On, hardtriggeritem); return StartGrabbing(); } public override bool GetImage(out Bitmap bitmap, int outtime = 3000) { bitmap = null; try { // 设置超时时间 DateTime lastTime = DateTime.Now.AddMilliseconds(outtime); // 判断是否超时 while (lastTime > DateTime.Now)// 设置超时时间为 3 秒 { if (CallBackImg != null) { lock (CallBackImg) { // 保存旧 Bitmap 并释放 bitmap = CallBackImg.Clone() as Bitmap; // 创建副本 } // 释放旧资源 CallBackImg.Dispose(); CallBackImg = null; return true; } } return false; } catch { return bitmap == null ? false : true; } } public override bool GetImageWithSoftTrigger(out Bitmap bitmap, int outtime = 3000) { if (!isGrabbing) StartGrabbing(); GetTriggerMode(out TriggerMode triggerMode, out TriggerSource triggerSource); if (triggerMode != TriggerMode.On && triggerSource != TriggerSource.Software) SetTriggerMode(TriggerMode.On, TriggerSource.Software); bitmap = null; CallBackImg = null; if (!SoftTrigger()) return false; // 开始时间 DateTime startTime = DateTime.Now; // 当前时间 // 判断是否超时 while (DateTime.Now < startTime.AddMilliseconds(outtime))// 设置超时时间为 3 秒 { GetImage(out bitmap, 50); if (bitmap != null) break; Thread.Sleep(10); } if (triggerMode != TriggerMode.On) SetTriggerMode(TriggerMode.On, triggerSource); return (bitmap != null); } public override void SetCamConfig(CameraConfig config) { if (Enum.TryParse(config.Params.Inputs["触发模式"].ToString(), out TriggerMode TriggerMode) && Enum.TryParse(config.Params.Inputs["触发方式"].ToString(), out TriggerSource TriggerSource) && Enum.TryParse(config.Params.Inputs["触发极性"].ToString(), out TriggerPolarity TriggerPolarity) ) { SetTriggerMode(TriggerMode, TriggerSource); SetTriggerPolarity(TriggerPolarity); SetTriggerFliter(Convert.ToDouble(config.Params.Inputs["触发消抖"].ToString())); SetTriggerDelay(Convert.ToDouble(config.Params.Inputs["触发延时"].ToString())); SetExpouseTime(Convert.ToDouble(config.Params.Inputs["曝光时间"].ToString())); SetGain(Convert.ToDouble(config.Params.Inputs["增益"].ToString())); } } public override void GetCamConfig(out CameraConfig config) { GetTriggerMode(out TriggerMode triggerMode, out TriggerSource triggerSource); GetTriggerPolarity(out TriggerPolarity triggerPolarity); GetTriggerFliter(out double triggerfilter); GetTriggerDelay(out double triggerdelay); GetExpouseTime(out double expouseTime); GetGain(out double gain); config = new CameraConfig(null); config.Params.Inputs.Add("触发模式", triggerMode); config.Params.Inputs.Add("触发方式", triggerSource); config.Params.Inputs.Add("触发极性", triggerPolarity); config.Params.Inputs.Add("触发消抖", triggerfilter); config.Params.Inputs.Add("触发延时", triggerdelay); config.Params.Inputs.Add("曝光时间", expouseTime); config.Params.Inputs.Add("增益", gain); } public override bool StartContinuousGrab() { return true; } #endregion } }