using Microsoft.Win32;
using System.ComponentModel;
using SciCamera.Net;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using TeamAAS.Camera.Images;
using TeamAAS.Camera.Models;
using TeamAAS.Camera.Attributes;
using TeamAAS.Camera.Enums;
namespace TeamAAS.Camera.Cameras
{
///
/// 奥普特相机(SciCamera.Net SDK)
///
[Camera("OPT", "奥普特")]
public class OptCamera : CameraBase
{
#region 字段
private readonly object _stateLock = new object();
private TaskCompletionSource _tcs;
SciCam mvCameraAcq;
[Browsable(false)]
public SciCam.SCI_DEVICE_INFO CameraInfo { get; private set; }
[Browsable(false)]
public SciCam.SciCamPixelType PixelType { get; private set; }
[Browsable(false)]
public SciCam.SciCamGrabStrategy CurrentGrabStrategy { get; private set; } = SciCam.SciCamGrabStrategy.SciCam_GrabStrategy_Latest;
private bool IsHaveCamera = false;
#endregion
[Browsable(false)]
public override string CameraBrand => "奥普特";
public OptCamera(Guid id, int index, string name, string serialNumber, string cameraIp, bool isFindByIp)
: base(id, index, name, serialNumber, cameraIp, isFindByIp)
{
mvCameraAcq = new SciCam();
SciCam.SCI_DEVICE_INFO_LIST stDeviceList = new SciCam.SCI_DEVICE_INFO_LIST();
uint nReVal = SciCam.DiscoveryDevices(ref stDeviceList, (uint)(SciCam.SciCamTLType.SciCam_TLType_Gige));
if (nReVal != SciCam.SCI_CAMERA_OK)
{
return;
}
for (int i = 0; i < stDeviceList.count; i++)
{
SciCam.SCI_DEVICE_INFO device = stDeviceList.pDevInfo[i];
SciCam.SciCamTLType devTlType = device.tlType;
if (devTlType == SciCam.SciCamTLType.SciCam_TLType_Gige)
{
SciCam.SCI_DEVICE_GIGE_INFO gigeDevInfo = (SciCam.SCI_DEVICE_GIGE_INFO)SciCam.ByteToStruct(device.info.gigeInfo, typeof(SciCam.SCI_DEVICE_GIGE_INFO));
uint ip1 = gigeDevInfo.ip;
int nIp1 = (int)(ip1 & 0x000000ff);
int nIp2 = (int)((ip1 & 0x0000ff00) >> 8);
int nIp3 = (int)((ip1 & 0x00ff0000) >> 16);
int nIp4 = (int)((ip1 & 0xff000000) >> 24);
string ip = string.Format("{0}.{1}.{2}.{3}", nIp1, nIp2, nIp3, nIp4);
if (IsFindByIp)
{
if (CameraIp == ip)
{
ManufacturerName = gigeDevInfo.manufactureName;
ModelName = gigeDevInfo.modelName;
SerialNumber = gigeDevInfo.serialNumber;
CameraType = CameraType.GIGE;
CameraInfo = device;
IsHaveCamera = true;
break;
}
}
else
{
if (string.Equals(gigeDevInfo.serialNumber, serialNumber))
{
ManufacturerName = gigeDevInfo.manufactureName;
ModelName = gigeDevInfo.modelName;
SerialNumber = gigeDevInfo.serialNumber;
CameraType = CameraType.GIGE;
CameraInfo = device;
IsHaveCamera = true;
break;
}
}
}
}
IsConnected = false;
}
#region 设备发现
public static CameraInfo[] GetDevices()
{
List cameras = new List();
try
{
if (!CheckSoftwareInstalled().isInstalled)
{
return cameras.ToArray();
}
SciCam.SCI_DEVICE_INFO_LIST stDeviceList = new SciCam.SCI_DEVICE_INFO_LIST();
uint nReVal = SciCam.DiscoveryDevices(ref stDeviceList, (uint)(SciCam.SciCamTLType.SciCam_TLType_Gige));
if (nReVal != SciCam.SCI_CAMERA_OK)
{
return cameras.ToArray();
}
for (int i = 0; i < stDeviceList.count; i++)
{
SciCam.SCI_DEVICE_INFO device = stDeviceList.pDevInfo[i];
SciCam.SciCamTLType devTlType = device.tlType;
if (devTlType == SciCam.SciCamTLType.SciCam_TLType_Gige)
{
SciCam.SCI_DEVICE_GIGE_INFO gigeDevInfo = (SciCam.SCI_DEVICE_GIGE_INFO)SciCam.ByteToStruct(device.info.gigeInfo, typeof(SciCam.SCI_DEVICE_GIGE_INFO));
uint ip1 = gigeDevInfo.ip;
int nIp1 = (int)(ip1 & 0x000000ff);
int nIp2 = (int)((ip1 & 0x0000ff00) >> 8);
int nIp3 = (int)((ip1 & 0x00ff0000) >> 16);
int nIp4 = (int)((ip1 & 0xff000000) >> 24);
string ip = string.Format("{0}.{1}.{2}.{3}", nIp1, nIp2, nIp3, nIp4);
cameras.Add(new CameraInfo()
{
CameraNo = i + 1,
CameraName = gigeDevInfo.name,
CameraBrand = "奥普特",
CameraType = CameraType.GIGE,
ManufacturerName = gigeDevInfo.manufactureName,
Model = gigeDevInfo.modelName,
SerialNumber = gigeDevInfo.serialNumber,
CameraIp = ip,
IsFindByIp = true,
});
}
}
}
catch (Exception)
{
}
return cameras.ToArray();
}
public static (bool isInstalled, string version) CheckSoftwareInstalled()
{
return (true, "4.0.0.3");
}
#endregion
#region SDK 实现
public override bool OpenDevice()
{
using (WriteLockScope())
{
try
{
SciCam.SCI_DEVICE_INFO stDeviceInfo = CameraInfo;
uint nReVal = mvCameraAcq.CreateDevice(ref stDeviceInfo);
if (nReVal != SciCam.SCI_CAMERA_OK)
{
throw new Exception("Create Device Fail!");
}
nReVal = mvCameraAcq.OpenDevice();
// 按当前触发模式下发(默认软触发,与历史硬编码行为一致);AcquisitionMode 由该方法一并置 Continuous
ApplyTriggerToHardware(TriggerMode);
nReVal = mvCameraAcq.SetGrabStrategy(CurrentGrabStrategy);
if (!IsConnected)
{
OnConnectChanged(true);
}
mvCameraAcq.SetGrabTimeout(5000);
IsConnected = mvCameraAcq.IsDeviceOpen();
if (IsConnected)
{
mvCameraAcq.StartGrabbing();
}
return IsConnected;
}
catch (Exception ex)
{
ErrorMessage = ex.Message;
IsConnected = false;
return false;
}
}
}
public override void CloseDevice()
{
using (WriteLockScope())
{
try
{
if (mvCameraAcq != null)
{
if (mvCameraAcq.IsDeviceOpen())
{
mvCameraAcq.StopGrabbing();
mvCameraAcq.CloseDevice();
}
}
}
catch { }
IsConnected = false;
OnConnectChanged(false);
}
}
public override GenImage Grab()
{
using (WriteLockScope())
{
var sw = Stopwatch.StartNew();
try
{
if (mvCameraAcq == null || !mvCameraAcq.IsDeviceOpen())
{
ErrorMessage = "相机未连接";
return null;
}
if (!mvCameraAcq.IsGrabbing())
{
mvCameraAcq.StartGrabbing();
}
IntPtr payload = IntPtr.Zero;
// 仅软触发模式需主动发触发命令;连续采集自由出图,硬触发等外部信号
bool triggerOk = true;
if (IsSoftwareTrigger)
triggerOk = mvCameraAcq.SetCommandValueEx(
SciCam.SciCamDeviceXmlType.SciCam_DeviceXml_Camera, "TriggerSoftware") == SciCam.SCI_CAMERA_OK;
if (triggerOk)
{
uint nReVal = mvCameraAcq.Grab(ref payload);
if (nReVal == SciCam.SCI_CAMERA_OK)
{
Image = GetConvertedInfo(payload);
mvCameraAcq.FreePayload(payload);
if (Image != null)
{
ImageWidth = (uint)Image.Width;
ImageHeight = (uint)Image.Height;
ApplyGammaToImage(Image);
TotalTime = sw.Elapsed;
ErrorMessage = "";
return Image;
}
}
else
{
CloseDevice();
OpenDevice();
ErrorMessage = "图像采集失败!";
}
}
else
{
CloseDevice();
OpenDevice();
ErrorMessage = "软触发失败!";
}
}
catch (Exception ex)
{
CloseDevice();
OpenDevice();
ErrorMessage = ex.Message;
}
return null;
}
}
#region 触发模式
/// OPT(SciCamera)支持连续/软触发/硬触发(Line0)
public override IReadOnlyList SupportedTriggerModes => FullTriggerModes;
/// 历史行为:OpenDevice 硬编码 TriggerMode=On + TriggerSource=Software,故默认软触发
protected override CameraTriggerMode DefaultTriggerMode => CameraTriggerMode.软触发;
protected override bool ApplyTriggerMode(CameraTriggerMode mode)
{
using (WriteLockScope())
return ApplyTriggerToHardware(mode);
}
///
/// 把触发模式下发到 SciCamera SDK(GenICam 字符串接口)。
/// 调用方需已持有写锁(OpenDevice/Grab 内部已在锁内;锁可重入)。
///
private bool ApplyTriggerToHardware(CameraTriggerMode mode)
{
if (mvCameraAcq == null || !mvCameraAcq.IsDeviceOpen()) return false;
var xml = SciCam.SciCamDeviceXmlType.SciCam_DeviceXml_Camera;
mvCameraAcq.SetEnumValueByString("AcquisitionMode", "Continuous");
switch (mode)
{
case CameraTriggerMode.连续采集:
return mvCameraAcq.SetEnumValueByStringEx(xml, "TriggerMode", "Off") == SciCam.SCI_CAMERA_OK;
case CameraTriggerMode.软触发:
mvCameraAcq.SetEnumValueByString("TriggerSource", "Software");
return mvCameraAcq.SetEnumValueByStringEx(xml, "TriggerMode", "On") == SciCam.SCI_CAMERA_OK;
case CameraTriggerMode.硬触发上升沿:
case CameraTriggerMode.硬触发下降沿:
mvCameraAcq.SetEnumValueByString("TriggerSource", "Line0");
mvCameraAcq.SetEnumValueByString("TriggerActivation",
mode == CameraTriggerMode.硬触发上升沿 ? "Rising" : "Falling");
return mvCameraAcq.SetEnumValueByStringEx(xml, "TriggerMode", "On") == SciCam.SCI_CAMERA_OK;
default: // 跟随相机:不下发,沿用相机自身配置
return true;
}
}
#endregion
public override bool SetExposureTime(float exposureTime)
{
using (WriteLockScope())
{
try
{
if (mvCameraAcq != null && mvCameraAcq.IsDeviceOpen())
{
mvCameraAcq.SetFloatValue("ExposureTime", exposureTime);
}
return true;
}
catch { return false; }
}
}
public override float GetExposureTime()
{
using (ReadLockScope())
{
try
{
if (mvCameraAcq != null && mvCameraAcq.IsDeviceOpen())
{
return 0;
}
}
catch { }
return 0;
}
}
public override bool SetGain(float gain)
{
using (WriteLockScope())
{
try
{
if (mvCameraAcq != null && mvCameraAcq.IsDeviceOpen())
{
mvCameraAcq.SetFloatValue("Gain", gain);
}
return true;
}
catch { return false; }
}
}
public override float GetGain()
{
using (ReadLockScope())
{
try
{
if (mvCameraAcq != null && mvCameraAcq.IsDeviceOpen())
{
return 0;
}
}
catch { }
return 0;
}
}
public override bool SetGamma(float gamma)
{
using (WriteLockScope())
{
try
{
if (mvCameraAcq != null && mvCameraAcq.IsDeviceOpen())
{
mvCameraAcq.SetFloatValue("Gamma", gamma);
return true;
}
}
catch { }
UpdateGammaSnapshot(gamma);
return true;
}
}
public override float GetGamma()
{
using (ReadLockScope())
{
try
{
if (mvCameraAcq != null && mvCameraAcq.IsDeviceOpen())
{
return Gamma;
}
}
catch { }
return Gamma;
}
}
#endregion
#region 辅助方法
void ImageCallBack(IntPtr payload, IntPtr tag)
{
try
{
if (payload == IntPtr.Zero) return;
uint nReVal = SciCam.SCI_CAMERA_OK;
SciCam.SCI_CAM_PAYLOAD_ATTRIBUTE attribute = new SciCam.SCI_CAM_PAYLOAD_ATTRIBUTE();
nReVal = SciCam.PayloadGetAttribute(payload, ref attribute);
if (nReVal != SciCam.SCI_CAMERA_OK || !attribute.isComplete)
{
_tcs?.TrySetException(new Exception($"Failed to get image attributes or frame is incomplete! nRet: {nReVal}"));
}
else
{
Image = GetConvertedInfo(payload);
_tcs?.TrySetResult(Image);
}
}
catch (Exception ex)
{
_tcs?.TrySetException(new Exception($"相机采集错误: {ex.Message}"));
}
}
private GenImage GetConvertedInfo(IntPtr payload)
{
if (payload == IntPtr.Zero)
{
return null;
}
SciCam.SCI_CAM_PAYLOAD_ATTRIBUTE payloadAttribute = new SciCam.SCI_CAM_PAYLOAD_ATTRIBUTE();
uint nReVal = SciCam.PayloadGetAttribute(payload, ref payloadAttribute);
if (nReVal != SciCam.SCI_CAMERA_OK)
{
return null;
}
bool imgIsComplete = payloadAttribute.isComplete;
SciCam.SciCamPayloadMode payloadMode = payloadAttribute.payloadMode;
SciCam.SciCamPixelType imgPixelType = payloadAttribute.imgAttr.pixelType;
ulong imgWidth = payloadAttribute.imgAttr.width;
ulong imgHeight = payloadAttribute.imgAttr.height;
ulong framID = payloadAttribute.frameID;
if (!imgIsComplete || payloadMode != SciCam.SciCamPayloadMode.SciCam_PayloadMode_2D)
{
return null;
}
IntPtr imgData = IntPtr.Zero;
nReVal = SciCam.PayloadGetImage(payload, ref imgData);
if (nReVal != SciCam.SCI_CAMERA_OK)
{
return null;
}
long destImgSize = 0;
if (imgPixelType == SciCam.SciCamPixelType.Mono1p ||
imgPixelType == SciCam.SciCamPixelType.Mono2p ||
imgPixelType == SciCam.SciCamPixelType.Mono4p ||
imgPixelType == SciCam.SciCamPixelType.Mono8s ||
imgPixelType == SciCam.SciCamPixelType.Mono8 ||
imgPixelType == SciCam.SciCamPixelType.Mono10 ||
imgPixelType == SciCam.SciCamPixelType.Mono10p ||
imgPixelType == SciCam.SciCamPixelType.Mono12 ||
imgPixelType == SciCam.SciCamPixelType.Mono12p ||
imgPixelType == SciCam.SciCamPixelType.Mono14 ||
imgPixelType == SciCam.SciCamPixelType.Mono16 ||
imgPixelType == SciCam.SciCamPixelType.Mono10Packed ||
imgPixelType == SciCam.SciCamPixelType.Mono12Packed ||
imgPixelType == SciCam.SciCamPixelType.Mono14p)
{
nReVal = SciCam.PayloadConvertImage(ref payloadAttribute.imgAttr, imgData, SciCam.SciCamPixelType.Mono8, IntPtr.Zero, ref destImgSize, true);
if (nReVal == SciCam.SCI_CAMERA_OK)
{
IntPtr destImg = Marshal.AllocHGlobal((int)destImgSize);
try
{
nReVal = SciCam.PayloadConvertImage(ref payloadAttribute.imgAttr, imgData, SciCam.SciCamPixelType.Mono8, destImg, ref destImgSize, true);
if (nReVal == SciCam.SCI_CAMERA_OK)
{
// destImg 此时为 Mono8 交错的灰度数据;GenImage.FromIntPtr 内部会 Marshal.Copy 出独立副本,可在 finally 中安全释放 destImg
return GenImage.FromIntPtr((int)imgWidth, (int)imgHeight, Images.PixelType.Grey8, destImg, (int)imgWidth);
}
}
finally
{
Marshal.FreeHGlobal(destImg);
}
}
}
else
{
nReVal = SciCam.PayloadConvertImage(ref payloadAttribute.imgAttr, imgData, SciCam.SciCamPixelType.RGB8, IntPtr.Zero, ref destImgSize, true);
if (nReVal == SciCam.SCI_CAMERA_OK)
{
IntPtr destImg = Marshal.AllocHGlobal((int)destImgSize);
try
{
nReVal = SciCam.PayloadConvertImage(ref payloadAttribute.imgAttr, imgData, SciCam.SciCamPixelType.RGB8, destImg, ref destImgSize, true);
if (nReVal == SciCam.SCI_CAMERA_OK)
{
// destImg 此时为交错 RGB8(R-G-B)数据,可直接用于 GenImage.RGB24
return GenImage.FromIntPtr((int)imgWidth, (int)imgHeight, Images.PixelType.RGB24, destImg, (int)imgWidth * 3);
}
}
finally
{
Marshal.FreeHGlobal(destImg);
}
}
}
return null;
}
#endregion
}
}