ソースを参照

新增OPT(奥普特)相机全流程支持

本次提交实现了对OPT相机的完整支持,包括:
- CameraBrand枚举及多语言本地化增加OPT选项
- 新增OptCamera类,支持设备发现、采集、参数设置等
- CameraService支持OPT相机实例化
- 集成SciCamera.Net.dll依赖
- 相关界面与属性适配OPT相机
用户现可在系统中选择、配置和采集OPT相机。
孝锋 徐 9 ヶ月 前
親
コミット
de31b536c4

+ 887 - 0
TeamAAS-VM/Core/Cameras/OptCamera.cs

@@ -0,0 +1,887 @@
+using Cognex.VisionPro;
+using Microsoft.Win32;
+using NPOI.SS.Formula.Eval;
+using SciCamera.Net;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Diagnostics;
+using System.Drawing;
+using System.IO;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using TeamAAS_VP.Enums;
+using TeamAAS_VP.Interfaces;
+using TeamAAS_VP.Models;
+
+namespace TeamAAS_VP.Core.Cameras
+{
+    public class OptCamera : ICamera, INotifyPropertyChanged
+    {
+        #region 字段
+        private Thread m_hReceiveThread;
+        // 用于序列化对同一相机实例的操作,防止同一实例被并发操作
+        private readonly SemaphoreSlim _operationSemaphore = new SemaphoreSlim(1, 1);
+        // 用于保护状态变量(例如 IsGrabbing)的并发访问
+        private readonly object _stateLock = new object();
+        #endregion
+
+        #region 属性
+
+        public CameraBrand CameraBrand { get => CameraBrand.Opt; }
+
+        public string Name { get; private set; }
+        public Guid ID { get; private set; }
+        public int Index { get; set; }
+        public string ManufacturerName { get; private set; }
+
+        public bool IsFindByIp { get; private set; }
+
+        public string ModelName { get; private set; }
+
+        public string SerialNumber { get; private set; }
+
+        public string CameraIp { get; private set; }
+
+        public CameraType CameraType { get; private set; }
+
+        SciCam mvCameraAcq;
+
+        public SciCam.SCI_DEVICE_INFO CameraInfo { get; private set; }
+
+        public UInt32 ImageWidth { get; private set; }
+
+        public UInt32 ImageHeight { get; private set; }
+
+        public SciCam.SciCamPixelType PixelType { get; private set; }
+
+        private bool _IsGrabbing;
+
+        /// <summary>
+        /// 正在采集
+        /// </summary>
+        public bool IsGrabbing
+        {
+            get { return _IsGrabbing; }
+            private set { SetProperty(ref _IsGrabbing, value); }
+        }
+
+        private ICogImage _Image;
+
+        public ICogImage Image
+        {
+            get { return _Image; }
+            private set { SetProperty(ref _Image, value); }
+        }
+
+        public bool IsConnected { get; private set; }
+
+        private bool IsHaveCamera = false;
+
+        /// <summary>
+        /// 采集用时
+        /// </summary>
+        public TimeSpan TotalTime { get; private set; }
+
+        /// <summary>
+        /// 错误信息
+        /// </summary>
+        public string ErrorMessage { get; private set; }
+        #endregion
+
+        #region 事件
+        public event Action<ICogImage, TimeSpan, string> ImageCallbackEvent;
+        public event Action<Guid, bool> CameraConnectChangedEvent;
+        #endregion
+
+        public OptCamera(Guid id, int index, string name, string serialNumber, string cameraip, bool isFindByIp)
+        {
+            ID = id;
+            Name = serialNumber;
+            Index = index;
+            CameraIp = cameraip;
+            SerialNumber = serialNumber;
+            IsFindByIp = 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_CL));
+            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 方法
+
+        /// <summary>
+        /// 获取设备
+        /// </summary>
+        /// <returns></returns>
+        public static CameraInfo[] GetDevices()
+        {
+            List<CameraInfo> cameras = new List<CameraInfo>();
+            try
+            {
+                if (!CheckSoftwareInstalled().isInstalled)
+                {
+                    LogHelper.WriteLogInfo("未安装MVS");
+                    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_CL));
+                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 = CameraBrand.Opt,
+                            CameraType = CameraType.GIGE,
+                            ManufacturerName = gigeDevInfo.manufactureName,
+                            Model = gigeDevInfo.modelName,
+                            SerialNumber = gigeDevInfo.serialNumber,
+                            CameraIp = ip,
+                            IsFindByIp = true,
+                        });
+                    }
+                }
+            }
+            catch (Exception ex)
+            {
+                LogHelper.WriteLogError("搜索OPT相机列表时出错!", ex);
+            }
+
+            return cameras.ToArray();
+        }
+
+        /// <summary>
+        /// 检查相机软件是否安装
+        /// </summary>
+        /// <returns></returns>
+        public static (bool isInstalled, string version) CheckSoftwareInstalled()
+        {
+            //string softwareName = "MVS";
+            //string softwareVersion = "4.4.0";
+            //string[] registryPaths = new[]
+            //{
+            //    @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall",
+            //    @"SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall"
+            //};
+            //foreach (var path in registryPaths)
+            //{
+            //    using (RegistryKey key = Registry.LocalMachine.OpenSubKey(path))
+            //    {
+            //        if (key == null) continue;
+
+            //        foreach (string subKeyName in key.GetSubKeyNames())
+            //        {
+            //            using (RegistryKey subKey = key.OpenSubKey(subKeyName))
+            //            {
+            //                string displayName = subKey.GetValue("DisplayName")?.ToString();
+            //                if (displayName != null && displayName.ToUpper().Trim() == softwareName.ToUpper())
+            //                {
+            //                    string version = subKey.GetValue("DisplayVersion")?.ToString();
+            //                    Version v1 = new Version(version);
+            //                    Version v2 = new Version(softwareVersion);
+            //                    if (v1 >= v2)
+            //                    {
+            //                        return (true, version);
+            //                    }
+            //                    else
+            //                    {
+            //                        return (false, version);
+            //                    }
+            //                }
+            //            }
+            //        }
+            //    }
+            //}
+            //return (false, "");
+            return (true, "4.0.0.3"); // TODO: Opt相机不检查软件安装
+        }
+
+        /// <summary>
+        /// 打开相机
+        /// </summary>
+        /// <returns></returns>
+        /// <exception cref="Exception"></exception>
+        public bool OpenDevice()
+        {
+            _operationSemaphore.Wait();
+            uint nReVal = SciCam.SCI_CAMERA_OK;
+            try
+            {
+                if (!IsHaveCamera)
+                {
+                    mvCameraAcq = new SciCam();
+                    SciCam.SCI_DEVICE_INFO_LIST stDeviceList = new SciCam.SCI_DEVICE_INFO_LIST();    //设备列表
+                    nReVal = SciCam.DiscoveryDevices(ref stDeviceList, (uint)(SciCam.SciCamTLType.SciCam_TLType_CL));
+                    if (nReVal != SciCam.SCI_CAMERA_OK)
+                    {
+                        return false;
+                    }
+
+                    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;
+                                }
+                            }
+                        }
+                    }
+                    if (!IsHaveCamera)
+                    {
+                        throw new Exception("没有发现相机");
+                    }
+                }
+                SciCam.SCI_DEVICE_INFO sCI_DEVICE_INFO= CameraInfo;
+                nReVal = mvCameraAcq.CreateDevice(ref sCI_DEVICE_INFO);
+                if (nReVal != SciCam.SCI_CAMERA_OK)
+                {
+                    Console.WriteLine("Creat Device Fail! nRet:{0}", nReVal);
+                    throw new Exception("Creat Device Fail!");
+                }
+                // ch:打开设备 | en:Open device
+                nReVal = mvCameraAcq.OpenDevice();
+                // ch:设置触发模式为off | en:Set trigger mode as off
+                nReVal = mvCameraAcq.SetEnumValueByStringEx(SciCam.SciCamDeviceXmlType.SciCam_DeviceXml_Camera, "TriggerMode", "Off");
+                if (!IsConnected)
+                {
+                    CameraConnectChangedEvent?.Invoke(ID, true);
+                }
+                IsConnected = mvCameraAcq.IsDeviceOpen();
+                return IsConnected;
+            }
+            finally
+            {
+                _operationSemaphore.Release();
+            }
+        }
+
+        /// <summary>
+        /// 打开相机异步
+        /// </summary>
+        /// <returns></returns>
+        public Task<bool> OpenDeviceAsync()
+        {
+            return Task.Run(() =>
+            {
+                return OpenDevice();
+            });
+        }
+
+        /// <summary>
+        /// 关闭相机
+        /// </summary>
+        public void CloseDevice()
+        {
+            // ch:关闭设备 | en:Close Device
+            _operationSemaphore.Wait();
+            try
+            {
+                mvCameraAcq.CloseDevice();
+            }
+            finally
+            {
+                _operationSemaphore.Release();
+            }
+        }
+
+        /// <summary>
+        /// 采集图像
+        /// </summary>
+        /// <returns></returns>
+        public ICogImage Grab()
+        {
+            _operationSemaphore.Wait();
+            uint nReVal = SciCam.SCI_CAMERA_OK;
+            IntPtr payload = IntPtr.Zero;
+            try
+            {
+                Stopwatch sw = Stopwatch.StartNew();
+                for (int i = 0; i < 5; i++)
+                {
+                    try
+                    {
+                        if (!mvCameraAcq.IsDeviceOpen())
+                        {
+                            OpenDevice();
+                        }
+                        nReVal = mvCameraAcq.Grab(ref payload);
+                        if (nReVal == SciCam.SCI_CAMERA_OK)
+                        {
+                            Image = GetConvertedInfo(payload);
+                            mvCameraAcq.FreePayload(payload);
+                            TotalTime = sw.Elapsed;
+                            return Image;
+                        }
+                        else if (nReVal == SciCam.SCI_ERR_CAMERA_OFFLINE)
+                        {
+                            nReVal = mvCameraAcq.FreePayload(payload);
+                            CloseDevice();
+                            OpenDevice();
+                            ErrorMessage = "图像采集失败!";
+                        }
+                    }
+                    catch (Exception ex)
+                    {
+                        CloseDevice();
+                        OpenDevice();
+                        ErrorMessage = ex.Message;
+                    }
+                }
+                if (IsConnected)
+                {
+                    IsConnected = false;
+                    CameraConnectChangedEvent?.Invoke(ID, false);
+                }
+                return null;
+            }
+            finally
+            {
+                _operationSemaphore.Release();
+            }
+        }
+
+        /// <summary>
+        /// 开始采集图像
+        /// </summary>
+        public void StartGrabbing()
+        {
+            lock (_stateLock)
+            {
+                if (IsGrabbing)
+                    return;
+                IsGrabbing = true;
+            }
+            m_hReceiveThread = new Thread(GetStreamThreadProc) { IsBackground = true };
+            m_hReceiveThread.Start();
+        }
+
+        /// <summary>
+        /// 停止采集图像
+        /// </summary>
+        public void StopGrabbing()
+        {
+            try
+            {
+                lock (_stateLock)
+                {
+                    IsGrabbing = false;
+                }
+                Thread.Sleep(1000);
+                if (m_hReceiveThread != null)
+                {
+                    m_hReceiveThread.Abort();
+                    m_hReceiveThread = null;
+                }
+            }
+            catch (Exception)
+            {
+
+            }
+        }
+
+        /// <summary>
+        /// 设置曝光时间
+        /// </summary>
+        /// <param name="ExposureTime"></param>
+        /// <returns></returns>
+        public bool SetExposureTime(float ExposureTime)
+        {
+            _operationSemaphore.Wait();
+            try
+            {
+                if (!mvCameraAcq.IsDeviceOpen())
+                    return false;
+                uint nReVal = SciCam.SCI_CAMERA_OK;
+                nReVal = mvCameraAcq.SetIntValue("ExposureTime", (int)ExposureTime);
+                if (nReVal != SciCam.SCI_CAMERA_OK)
+                {
+                    mvCameraAcq.SetFloatValue("ExposureTime", ExposureTime);
+                }
+                return true;
+            }
+            finally
+            {
+                _operationSemaphore.Release();
+            }
+        }
+
+        /// <summary>
+        /// 获取曝光时间
+        /// </summary>
+        /// <returns></returns>
+        public float GetExposureTime()
+        {
+            _operationSemaphore.Wait();
+            try
+            {
+                if (!mvCameraAcq.IsDeviceOpen())
+                    return 0;
+
+                uint nReVal = SciCam.SCI_CAMERA_OK;
+                SciCam.SCI_NODE_VAL_INT iNodeVal = new SciCam.SCI_NODE_VAL_INT();
+                nReVal = mvCameraAcq.GetIntValue("ExposureTime", ref iNodeVal);
+                if (nReVal != SciCam.SCI_CAMERA_OK)
+                {
+                    SciCam.SCI_NODE_VAL_FLOAT fNodeVal = new SciCam.SCI_NODE_VAL_FLOAT();
+                    nReVal = mvCameraAcq.GetFloatValue("ExposureTime", ref fNodeVal);
+                    if (nReVal == SciCam.SCI_CAMERA_OK)
+                    {
+                        return (float)fNodeVal.dVal;
+                    }
+                    else
+                    {
+                        return 0;
+                    }
+                }
+                else
+                {
+                    return (float)iNodeVal.nVal;
+                }
+            }
+            finally
+            {
+                _operationSemaphore.Release();
+            }
+        }
+
+        /// <summary>
+        /// 设置增益
+        /// </summary>
+        /// <param name="Gain"></param>
+        /// <returns></returns>
+        public bool SetGain(float Gain)
+        {
+            _operationSemaphore.Wait();
+            try
+            {
+                if (!mvCameraAcq.IsDeviceOpen())
+                    return false;
+                uint nReVal = SciCam.SCI_CAMERA_OK;
+                nReVal = mvCameraAcq.SetIntValue("Gain", (int)Gain);
+                if (nReVal != SciCam.SCI_CAMERA_OK)
+                {
+                    mvCameraAcq.SetFloatValue("Gain", Gain);
+                }
+                return true;
+            }
+            finally
+            {
+                _operationSemaphore.Release();
+            }
+        }
+
+        /// <summary>
+        /// 获取增益
+        /// </summary>
+        /// <returns></returns>
+        public float GetGain()
+        {
+            _operationSemaphore.Wait();
+            try
+            {
+                if (!mvCameraAcq.IsDeviceOpen())
+                    return 0;
+                uint nReVal = SciCam.SCI_CAMERA_OK;
+                SciCam.SCI_NODE_VAL_INT iNodeVal = new SciCam.SCI_NODE_VAL_INT();
+                nReVal = mvCameraAcq.GetIntValue("Gain", ref iNodeVal);
+                if (nReVal != SciCam.SCI_CAMERA_OK)
+                {
+                    SciCam.SCI_NODE_VAL_FLOAT fNodeVal = new SciCam.SCI_NODE_VAL_FLOAT();
+                    nReVal = mvCameraAcq.GetFloatValue("Gain", ref fNodeVal);
+                    if (nReVal == SciCam.SCI_CAMERA_OK)
+                    {
+                        return (float)fNodeVal.dVal;
+                    }
+                    else
+                    {
+                        return 0;
+                    }
+                }
+                else
+                {
+                    return (float)iNodeVal.nVal;
+                }
+            }
+            finally
+            {
+                _operationSemaphore.Release();
+            }
+        }
+
+        private void GetStreamThreadProc()
+        {
+            while (IsGrabbing)
+            {
+                try
+                {
+                    Grab();
+                    ImageCallbackEvent?.Invoke(Image, TotalTime, ErrorMessage);
+                }
+                catch (Exception)
+                {
+
+                }
+                Thread.Sleep(10);
+            }
+            IsGrabbing = false;
+        }
+
+        private ICogImage 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)
+                        {
+                            byte[] bBitmap = new byte[destImgSize];
+                            Marshal.Copy(destImg, bBitmap, 0, (int)destImgSize);
+                            Bitmap bitMap = new Bitmap((int)imgWidth, (int)imgHeight, System.Drawing.Imaging.PixelFormat.Format8bppIndexed);
+                            System.Drawing.Imaging.BitmapData bitmapData = bitMap.LockBits(new Rectangle(0, 0, (int)imgWidth, (int)imgHeight), System.Drawing.Imaging.ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format8bppIndexed);
+                            Marshal.Copy(bBitmap, 0, bitmapData.Scan0, (int)destImgSize);
+                            bitMap.UnlockBits(bitmapData);
+
+                            //设置调色板
+                            System.Drawing.Imaging.ColorPalette palette = bitMap.Palette;
+                            for (int i = 0; i < 256; i++)
+                            {
+                                palette.Entries[i] = Color.FromArgb(i, i, i);
+                            }
+                            bitMap.Palette = palette;
+                            //显示图片
+                            return TransformICogImage((uint)imgHeight, (uint)imgWidth, destImg, SciCam.SciCamPixelType.Mono8);
+                        }
+                    }
+                    catch (Exception ex)
+                    {
+
+                    }
+                    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)
+                        {
+                            IntPtr pTemp = IntPtr.Zero;
+                            Byte[] byteArrImageData = new Byte[imgWidth * imgHeight * 3];
+                            unsafe
+                            {
+                                byte* pBufForSaveImage = (byte*)destImg;
+                                UInt32 nSupWidth = ((UInt32)imgWidth + (UInt32)3) & 0xfffffffc;
+                                for (int nRow = 0; nRow < (int)imgHeight; nRow++)
+                                {
+                                    for (int col = 0; col < (int)imgWidth; col++)
+                                    {
+                                        byteArrImageData[nRow * nSupWidth + col] = pBufForSaveImage[nRow * (int)imgWidth * 3 + (3 * col)];
+                                        byteArrImageData[(int)imgWidth * (int)imgHeight + nRow * nSupWidth + col] = pBufForSaveImage[nRow * (int)imgWidth * 3 + (3 * col + 1)];
+                                        byteArrImageData[(int)imgWidth * (int)imgHeight * 2 + nRow * nSupWidth + col] = pBufForSaveImage[nRow * (int)imgWidth * 3 + (3 * col + 2)];
+                                    }
+                                }
+
+                                pTemp = Marshal.UnsafeAddrOfPinnedArrayElement(byteArrImageData, 0);
+                            }
+                            //显示图片
+                            return TransformICogImage((uint)imgHeight, (uint)imgWidth, pTemp, SciCam.SciCamPixelType.RGB8);
+                        }
+                    }
+                    catch (Exception ex)
+                    {
+
+                    }
+                    finally
+                    {
+                        Marshal.FreeHGlobal(destImg);
+                    }
+                }
+            }
+            return null;
+        }
+
+        private ICogImage TransformICogImage(UInt32 nHeight, UInt32 nWidth, IntPtr pImageBuf, SciCam.SciCamPixelType enPixelType)
+        {
+            try
+            {
+                ICogImage image;
+                if (enPixelType == SciCam.SciCamPixelType.Mono8)
+                {
+                    CogImage8Root cogImage8Root = new CogImage8Root();
+                    cogImage8Root.Initialize((Int32)nWidth, (Int32)nHeight, pImageBuf, (Int32)nWidth, null);
+
+                    CogImage8Grey cogImage8Grey = new CogImage8Grey();
+                    cogImage8Grey.SetRoot(cogImage8Root);
+                    image = cogImage8Grey.ScaleImage((int)nWidth, (int)nHeight);
+                    System.GC.Collect();
+                    return image;
+                }
+                else
+                {
+                    CogImage8Root image0 = new CogImage8Root();
+                    IntPtr ptr0 = new IntPtr(pImageBuf.ToInt64());
+                    image0.Initialize((int)nWidth, (int)nHeight, ptr0, (int)nWidth, null);
+
+                    CogImage8Root image1 = new CogImage8Root();
+                    IntPtr ptr1 = new IntPtr(pImageBuf.ToInt64() + nWidth * nHeight);
+                    image1.Initialize((int)nWidth, (int)nHeight, ptr1, (int)nWidth, null);
+
+                    CogImage8Root image2 = new CogImage8Root();
+                    IntPtr ptr2 = new IntPtr(pImageBuf.ToInt64() + nWidth * nHeight * 2);
+                    image2.Initialize((int)nWidth, (int)nHeight, ptr2, (int)nWidth, null);
+
+                    CogImage24PlanarColor colorImage = new CogImage24PlanarColor();
+                    //colorImage.SetRoots(image0, image1, image2);
+                    colorImage.SetRoots(image2, image1, image0);
+
+                    image = colorImage.ScaleImage((int)nWidth, (int)nHeight);
+                    System.GC.Collect();
+                    return image;
+                }
+            }
+            catch (System.Exception)
+            {
+                return null;
+            }
+        }
+        #endregion
+
+        #region 属性通知
+        /// <summary>
+        /// Occurs when a property value changes.
+        /// </summary>
+        public event PropertyChangedEventHandler PropertyChanged;
+
+        /// <summary>
+        /// Checks if a property already matches a desired value. Sets the property and
+        /// notifies listeners only when necessary.
+        /// </summary>
+        /// <typeparam name="T">Type of the property.</typeparam>
+        /// <param name="storage">Reference to a property with both getter and setter.</param>
+        /// <param name="value">Desired value for the property.</param>
+        /// <param name="propertyName">Name of the property used to notify listeners. This
+        /// value is optional and can be provided automatically when invoked from compilers that
+        /// support CallerMemberName.</param>
+        /// <returns>True if the value was changed, false if the existing value matched the
+        /// desired value.</returns>
+        protected virtual bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
+        {
+            if (EqualityComparer<T>.Default.Equals(storage, value)) return false;
+
+            storage = value;
+            RaisePropertyChanged(propertyName);
+
+            return true;
+        }
+
+        /// <summary>
+        /// Checks if a property already matches a desired value. Sets the property and
+        /// notifies listeners only when necessary.
+        /// </summary>
+        /// <typeparam name="T">Type of the property.</typeparam>
+        /// <param name="storage">Reference to a property with both getter and setter.</param>
+        /// <param name="value">Desired value for the property.</param>
+        /// <param name="propertyName">Name of the property used to notify listeners. This
+        /// value is optional and can be provided automatically when invoked from compilers that
+        /// support CallerMemberName.</param>
+        /// <param name="onChanged">Action that is called after the property value has been changed.</param>
+        /// <returns>True if the value was changed, false if the existing value matched the
+        /// desired value.</returns>
+        protected virtual bool SetProperty<T>(ref T storage, T value, Action onChanged, [CallerMemberName] string propertyName = null)
+        {
+            if (EqualityComparer<T>.Default.Equals(storage, value)) return false;
+
+            storage = value;
+            onChanged?.Invoke();
+            RaisePropertyChanged(propertyName);
+
+            return true;
+        }
+
+        /// <summary>
+        /// Raises this object's PropertyChanged event.
+        /// </summary>
+        /// <param name="propertyName">Name of the property used to notify listeners. This
+        /// value is optional and can be provided automatically when invoked from compilers
+        /// that support <see cref="CallerMemberNameAttribute"/>.</param>
+        protected void RaisePropertyChanged([CallerMemberName] string propertyName = null)
+        {
+            OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
+        }
+
+        /// <summary>
+        /// Raises this object's PropertyChanged event.
+        /// </summary>
+        /// <param name="args">The PropertyChangedEventArgs</param>
+        protected virtual void OnPropertyChanged(PropertyChangedEventArgs args)
+        {
+            PropertyChanged?.Invoke(this, args);
+        }
+        #endregion
+    }
+}

+ 9 - 1
TeamAAS-VM/Enums/CameraBrand.cs

@@ -2,6 +2,7 @@
 using System.Collections.Generic;
 using System.ComponentModel;
 using System.Linq;
+using System.Security.AccessControl;
 using System.Text;
 using System.Threading.Tasks;
 using TeamAAS_VP.Attributes;
@@ -49,6 +50,13 @@ namespace TeamAAS_VP.Enums
         /// </summary>
         [Description("大恒相机(Galaxy)")]
         [Localization(ResourceName = "CameraBrand_Galaxy", ResourceType = typeof(Lang))]
-        Galaxy = 4
+        Galaxy = 4,
+
+        /// <summary>
+        /// OPT相机
+        /// </summary>
+        [Description("奥普特(OPT Camera Viewer)")]
+        [Localization(ResourceName = "CameraBrand_opt", ResourceType = typeof(Lang))]
+        Opt =5
     }
 }

+ 9 - 0
TeamAAS-VM/Resources/Languages/Lang.Designer.cs

@@ -114,6 +114,15 @@ namespace TeamAAS_VP.Resources.Languages {
             }
         }
         
+        /// <summary>
+        ///   查找类似 奥普特(OPT Camera Viewer) 的本地化字符串。
+        /// </summary>
+        public static string CameraBrand_opt {
+            get {
+                return ResourceManager.GetString("CameraBrand_opt", resourceCulture);
+            }
+        }
+        
         /// <summary>
         ///   查找类似 巴斯勒相机(Pylon7) 的本地化字符串。
         /// </summary>

+ 3 - 0
TeamAAS-VM/Resources/Languages/Lang.resx

@@ -2114,4 +2114,7 @@
   <data name="是否控制光源通道亮灭" xml:space="preserve">
     <value>是否控制光源通道亮灭</value>
   </data>
+  <data name="CameraBrand_opt" xml:space="preserve">
+    <value>奥普特(OPT Camera Viewer)</value>
+  </data>
 </root>

BIN
TeamAAS-VM/Resources/SciCamera.Net.dll


+ 4 - 0
TeamAAS-VM/Services/CameraService.cs

@@ -450,6 +450,10 @@ namespace TeamAAS_VP.Services
             {
                 camera = new GalaxyCamera(id, cameraInfo.CameraNo, cameraInfo.CameraName, cameraInfo.SerialNumber, cameraInfo.CameraIp, cameraInfo.IsFindByIp);
             }
+            else if (cameraInfo.CameraBrand == Enums.CameraBrand.Opt)
+            {
+                camera = new OptCamera(id, cameraInfo.CameraNo, cameraInfo.CameraName, cameraInfo.SerialNumber, cameraInfo.CameraIp, cameraInfo.IsFindByIp);
+            }
             else
             {
                 camera = new MvCamera(id, cameraInfo.CameraNo, cameraInfo.CameraName, cameraInfo.SerialNumber, cameraInfo.CameraIp, cameraInfo.IsFindByIp);

+ 6 - 0
TeamAAS-VM/TeamAAS-VP.csproj

@@ -311,6 +311,10 @@
     <Reference Include="OxyPlot.Wpf.Shared, Version=2.2.0.0, Culture=neutral, PublicKeyToken=75e952ba404cdbb0, processorArchitecture=MSIL">
       <HintPath>..\packages\OxyPlot.Wpf.Shared.2.2.0\lib\net462\OxyPlot.Wpf.Shared.dll</HintPath>
     </Reference>
+    <Reference Include="SciCamera.Net, Version=1.7.0.0, Culture=neutral, PublicKeyToken=befd096655b94148, processorArchitecture=AMD64">
+      <SpecificVersion>False</SpecificVersion>
+      <HintPath>Resources\SciCamera.Net.dll</HintPath>
+    </Reference>
     <Reference Include="Sdcb.OpenVINO, Version=0.7.0.0, Culture=neutral, processorArchitecture=MSIL">
       <HintPath>..\packages\Sdcb.OpenVINO.0.7.0\lib\net462\Sdcb.OpenVINO.dll</HintPath>
     </Reference>
@@ -564,6 +568,7 @@
     <Compile Include="Core\BgModbusTcpCommunicate.cs" />
     <Compile Include="Core\BraceFoldingStrategy.cs" />
     <Compile Include="Core\Cameras\CameraBase.cs" />
+    <Compile Include="Core\Cameras\OptCamera.cs" />
     <Compile Include="Core\Lights\ICommunicationProtocol.cs" />
     <Compile Include="Core\Lights\ILightChannel.cs" />
     <Compile Include="Core\Lights\ILightController.cs" />
@@ -1517,6 +1522,7 @@
   </ItemGroup>
   <ItemGroup>
     <Content Include="Resources\ddraw.dll" />
+    <Content Include="Resources\SciCamera.Net.dll" />
     <Resource Include="Resources\Images\MobileDown.png" />
     <Content Include="Resources\GxIAPINET.dll" />
     <Resource Include="Resources\Images\Feeder-反向.png" />