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
|
{
|
/// <summary>
|
/// 华睿2D工业相机实现类
|
/// 基于MVSDK_Net SDK封装
|
/// </summary>
|
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<IMVDefine.IMV_Frame> _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 构造函数和析构函数
|
|
/// <summary>
|
/// 构造函数
|
/// </summary>
|
public HRCamera()
|
{
|
_camera = new MyCamera();
|
_frameList = new List<IMVDefine.IMV_Frame>();
|
Brand = CameraBrand.HRCamera;
|
_isGrabbing = false;
|
_threadRunning = false;
|
}
|
|
/// <summary>
|
/// 析构函数
|
/// </summary>
|
~HRCamera()
|
{
|
Dispose(false);
|
}
|
|
#endregion
|
|
#region 设备管理操作
|
|
/// <summary>
|
/// 获取相机SN枚举列表
|
/// </summary>
|
/// <returns>相机SN列表</returns>
|
public override List<string> GetListEnum()
|
{
|
List<string> cameraList = new List<string>();
|
|
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}]";
|
if (!cameraInfo.Contains("L"))
|
{
|
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;
|
}
|
|
/// <summary>
|
/// 初始化相机设备
|
/// </summary>
|
/// <param name="SN">相机序列号</param>
|
/// <param name="Handle">窗口句柄(可选)</param>
|
/// <returns>初始化是否成功</returns>
|
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<string> 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<Bitmap>());
|
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);
|
}
|
|
/// <summary>
|
/// 启动队列(保证单线程)
|
/// </summary>
|
private void StartConsumeQueue()
|
{
|
// 使用轻量级判断,避免重复启动消费任务
|
if (CollectedImages.TryGetValue(SN, out var queue) && !queue.IsEmpty)
|
{
|
Task.Factory.StartNew(ProcessImageQueue, TaskCreationOptions.LongRunning);
|
}
|
}
|
|
/// <summary>
|
/// 处理CollectedImages中的缓存图像
|
/// 核心逻辑:遍历取第一个图像 -> 赋值给CallBackImg -> 触发事件 -> 释放并移除
|
/// </summary>
|
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);
|
}
|
}
|
|
/// <summary>
|
/// 图像是否为Mono格式
|
/// </summary>
|
/// <param name="enType"></param>
|
/// <returns></returns>
|
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;
|
}
|
}
|
|
/// <summary>
|
/// 图像是否为彩色
|
/// </summary>
|
/// <param name="enType"></param>
|
/// <returns></returns>
|
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;
|
}
|
}
|
|
|
/// <summary>
|
/// 关闭相机设备
|
/// </summary>
|
/// <returns>关闭是否成功</returns>
|
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 图像采集操作
|
|
/// <summary>
|
/// 开始图像采集
|
/// </summary>
|
/// <returns>采集启动是否成功</returns>
|
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;
|
}
|
}
|
|
/// <summary>
|
/// 停止图像采集
|
/// </summary>
|
/// <returns>停止是否成功</returns>
|
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;
|
}
|
}
|
|
/// <summary>
|
/// 执行软触发
|
/// </summary>
|
/// <returns>触发是否成功</returns>
|
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 参数设置和获取
|
|
/// <summary>
|
/// 设置触发模式
|
/// </summary>
|
/// <param name="mode">触发模式</param>
|
/// <param name="source">触发源</param>
|
/// <returns>设置是否成功</returns>
|
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;
|
}
|
}
|
|
/// <summary>
|
/// 获取触发模式
|
/// </summary>
|
/// <param name="mode">触发模式</param>
|
/// <param name="source">触发源</param>
|
/// <returns>获取是否成功</returns>
|
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;
|
}
|
}
|
|
/// <summary>
|
/// 设置曝光时间
|
/// </summary>
|
/// <param name="value">曝光时间(微秒)</param>
|
/// <returns>设置是否成功</returns>
|
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;
|
}
|
}
|
|
/// <summary>
|
/// 获取曝光时间
|
/// </summary>
|
/// <param name="value">曝光时间</param>
|
/// <returns>获取是否成功</returns>
|
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;
|
}
|
}
|
|
/// <summary>
|
/// 设置增益
|
/// </summary>
|
/// <param name="gain">增益值</param>
|
/// <returns>设置是否成功</returns>
|
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;
|
}
|
}
|
|
/// <summary>
|
/// 获取增益值
|
/// </summary>
|
/// <param name="gain">增益值</param>
|
/// <returns>获取是否成功</returns>
|
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;
|
}
|
}
|
|
/// <summary>
|
/// 设置触发极性
|
/// </summary>
|
/// <param name="polarity">触发极性</param>
|
/// <returns>是否成功</returns>
|
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;
|
}
|
}
|
|
/// <summary>
|
/// 获取触发极性
|
/// </summary>
|
/// <param name="polarity">触发极性</param>
|
/// <returns>是否成功</returns>
|
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;
|
}
|
}
|
|
/// <summary>
|
/// 设置触发滤波时间 (us)
|
/// </summary>
|
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;
|
}
|
}
|
|
/// <summary>
|
/// 获取触发滤波时间 (us)
|
/// </summary>
|
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;
|
}
|
}
|
|
/// <summary>
|
/// 设置触发延时 (us)
|
/// </summary>
|
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;
|
}
|
}
|
|
/// <summary>
|
/// 获取触发延时 (us)
|
/// </summary>
|
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;
|
}
|
}
|
|
/// <summary>
|
/// 设置信号线模式
|
/// </summary>
|
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;
|
}
|
}
|
|
/// <summary>
|
/// 设置信号线电平状态
|
/// </summary>
|
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;
|
}
|
}
|
|
/// <summary>
|
/// 获取信号线电平状态
|
/// </summary>
|
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;
|
}
|
}
|
|
/// <summary>
|
/// 自动白平衡
|
/// </summary>
|
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 采集和转换辅助方法
|
|
/// <summary>
|
/// 图像采集线程处理函数
|
/// </summary>
|
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);
|
}
|
}
|
|
/// <summary>
|
/// 处理图像帧
|
/// </summary>
|
/// <param name="frame">图像帧</param>
|
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);
|
}
|
}
|
|
/// <summary>
|
/// 将图像帧转换为Bitmap
|
/// </summary>
|
/// <param name="frame">图像帧</param>
|
/// <returns>Bitmap对象</returns>
|
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;
|
}
|
}
|
|
/// <summary>
|
/// 创建Mono8格式Bitmap
|
/// </summary>
|
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;
|
}
|
|
/// <summary>
|
/// 创建BGR8格式Bitmap
|
/// </summary>
|
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;
|
}
|
|
/// <summary>
|
/// 转换为BGR8格式
|
/// </summary>
|
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);
|
}
|
}
|
|
/// <summary>
|
/// 从缓冲区创建BGR8 Bitmap
|
/// </summary>
|
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;
|
}
|
|
/// <summary>
|
/// 将触发源枚举转换为字符串
|
/// </summary>
|
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"
|
};
|
}
|
|
/// <summary>
|
/// 将字符串转换为触发源枚举
|
/// </summary>
|
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
|
}
|
}
|