Переглянути джерело

优化多相机切换,图像结果显示

刘彬 6 місяців тому
батько
коміт
a3a0cd84af

+ 20 - 1
App.xaml.cs

@@ -7,6 +7,7 @@ using Prism.Ioc;
 using Prism.Unity;
 using System.ComponentModel;
 using System.IO;
+using System.Threading;
 using System.Windows;
 
 namespace LampInspectionMachine
@@ -38,7 +39,25 @@ namespace LampInspectionMachine
             containerRegistry.RegisterSingleton<Management>();
             containerRegistry.RegisterForNavigation<CameraView>("CameraView"); 
         }
+        private static System.Threading.Mutex mutex;
+        protected override void OnStartup(StartupEventArgs e)
+        {
+            //var view = new LoginView();
+            //if (!(bool)view.ShowDialog())
+            //{
+            //    this.Shutdown();
+            //}
 
-
+            mutex = new System.Threading.Mutex(true, "OnlyRun_CRNS");
+            if (mutex.WaitOne(0, false))
+            {
+                base.OnStartup(e);
+            }
+            else
+            {
+                MessageBox.Show("程序已经在运行!", "提示");
+                this.Shutdown();
+            }
+        }
     }
 }

+ 7 - 0
Cameralibs/CameraBrand.cs

@@ -19,6 +19,13 @@ namespace LampInspectionMachine.Cameralibs
         /// </summary>
         [Description("巴斯勒相机(Pylon)")]
         Basler_Pylon = 1,
+
+        /// <summary>
+        /// 巴斯勒相机
+        /// </summary>
+        [Description("欧普特(Sci)")]
+        SciCamera_Opt = 2,
+
     }
 
     public enum TriggerSource

+ 17 - 1
Cameralibs/CameraService.cs

@@ -1,4 +1,5 @@
 using LampInspectionMachine.Cameralibs.HKCamera;
+using LampInspectionMachine.Cameralibs.OPTCamera;
 using LampInspectionMachine.Interfaces;
 using LampInspectionMachine.Model;
 using MvCamCtrl.NET;
@@ -22,7 +23,22 @@ namespace LampInspectionMachine.Cameralibs
 
         public bool CreateCamera(Guid id ,CameraInfo cameraInfo)
         {
-            ICamera  camera = new MvCamera(id, cameraInfo.CameraName, cameraInfo.SerialNumber);
+            cameraInfo.CameraBrand = CameraBrand.SciCamera_Opt;
+            ICamera camera = null;
+            switch (cameraInfo.CameraBrand)
+            {
+                case CameraBrand.HikRobot_MVS:
+                     camera = new MvCamera(id, cameraInfo.CameraName, cameraInfo.SerialNumber);
+                    break;
+                case CameraBrand.Basler_Pylon:
+                    break;
+                case CameraBrand.SciCamera_Opt:
+                    camera = new MVOptCamera(id, cameraInfo.CameraName, cameraInfo.SerialNumber);
+                    break;
+                default:
+                    break;
+            }
+            
             
 
             if ( _CameraCollection.ContainsKey(id) )

+ 6 - 1
Cameralibs/HKCamera/MvCamera.cs

@@ -21,6 +21,7 @@ using System.Text;
 using System.Threading;
 using System.Threading.Tasks;
 using System.Windows.Media.Media3D;
+using System.Configuration;
 
 namespace LampInspectionMachine.Cameralibs.HKCamera
 {
@@ -114,6 +115,7 @@ namespace LampInspectionMachine.Cameralibs.HKCamera
             {
                 throw new Exception($"Enumerate devices fail: {nRet:x8}");
             }
+            
             foreach (var devInfo in devInfoList)
             {
                 if (devInfo.TLayerType == DeviceTLayerType.MvGigEDevice || devInfo.TLayerType == DeviceTLayerType.MvVirGigEDevice || devInfo.TLayerType == DeviceTLayerType.MvGenTLGigEDevice)
@@ -524,8 +526,9 @@ namespace LampInspectionMachine.Cameralibs.HKCamera
             if( frameOut !=null)
             Image = AnalyticImage(frameOut);
             TotalTime = sw.Elapsed;
-
+            sw.Stop();
             ImageCallbackEvent?.Invoke(Image, TotalTime, ErrorMessage);
+            GrabImageCallbackEvent?.Invoke(Image);
             return Image;
         }
 
@@ -545,6 +548,7 @@ namespace LampInspectionMachine.Cameralibs.HKCamera
         /// </summary>
         public void StartTriggerGrabbing()
         {
+            IsGrabbing = true;
             m_MyCamera.StreamGrabber.StartGrabbing();
         }
         /// <summary>
@@ -761,6 +765,7 @@ namespace LampInspectionMachine.Cameralibs.HKCamera
             if (mode)
             {
                 // ch:注册回调函数 | en:Register image callback
+                m_MyCamera.StreamGrabber.FrameGrabedEvent -= FrameGrabedEventHandler;
                 m_MyCamera.StreamGrabber.FrameGrabedEvent += FrameGrabedEventHandler;
                 return SetTriggerSource(triggerSource);
             }

+ 1100 - 0
Cameralibs/OPTCamera/MVOptCamera.cs

@@ -0,0 +1,1100 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Diagnostics;
+using System.Drawing;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using Cognex.VisionPro;
+using LampInspectionMachine.Interfaces;
+using LampInspectionMachine.Log4xml;
+using GdiPlus = System.Drawing.Imaging;
+using LampInspectionMachine.Model;
+using Microsoft.Win32;
+using MvCameraControl;
+using SciCamera.Net;
+using static System.Windows.Forms.AxHost;
+using static SciCamera.Net.SciCam;
+using MvCamCtrl.NET;
+
+namespace LampInspectionMachine.Cameralibs.OPTCamera
+{
+
+
+    public class MVOptCamera : ICamera, INotifyPropertyChanged
+    {
+        #region 字段
+        private Thread m_hReceiveThread;
+        #endregion
+
+        #region 属性
+
+        public CameraBrand CameraBrand { get => CameraBrand.HikRobot_MVS; }
+
+        public string Name { get; private set; }
+        public Guid ID { get; private set; }
+        public string ManufacturerName { get; private set; }
+
+        public string ModelName { get; private set; }
+
+        public string SerialNumber { get; private set; }
+
+        public CameraType CameraType { get; private set; }
+
+
+        public SCI_DEVICE_INFO CameraInfo;
+
+        public SciCam.SCI_DEVICE_INFO_LIST m_stDevList = new SciCam.SCI_DEVICE_INFO_LIST();    //ch:设备列表 | en:Device List
+
+
+        public SciCam m_currentDev = new SciCam();
+        public UInt32 ImageWidth { get; private set; }
+
+        public UInt32 ImageHeight { get; private set; }
+
+        public MvGvspPixelType 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 事件
+        /// <summary>
+        /// 手动采集图像回调事件
+        /// </summary>
+        public event Action<ICogImage, TimeSpan, string> ImageCallbackEvent;
+        /// <summary>
+        /// 触发取图回调事件
+        /// </summary>
+        public event Action<ICogImage> GrabImageCallbackEvent;
+        public event Action<Guid, bool> CameraConnectChangedEvent;
+        #endregion
+
+        public MVOptCamera(Guid _ID, string _Name, string _SerialNumber)
+        {
+            ID = _ID;
+            Name = _Name;
+            SerialNumber = _SerialNumber;
+            uint nReVal = SciCam.DiscoveryDevices(ref m_stDevList, (uint)(SciCam.SciCamTLType.SciCam_TLType_Gige) | (uint)(SciCam.SciCamTLType.SciCam_TLType_Usb3));
+            if (nReVal != SciCam.SCI_CAMERA_OK)
+            {
+                throw new Exception("Discovery devices failed!");
+
+
+            }
+            if (m_stDevList.count == 0)
+            {
+                throw new Exception("Discovery devices Success, but found 0 device.");
+
+            }
+            string chDeviceName;
+            IsHaveCamera = false;
+            foreach (var devInfo in m_stDevList.pDevInfo)
+            {
+                SciCam.SCI_DEVICE_INFO device = devInfo;
+                SciCam.SciCamTLType devTlType = device.tlType;
+                SciCam.SciCamDeviceType devType = device.devType;
+                if (devTlType == SciCam.SciCamTLType.SciCam_TLType_Usb3)
+                {
+                    SciCam.SCI_DEVICE_USB3_INFO usbinfo = (SciCam.SCI_DEVICE_USB3_INFO)SciCam.ByteToStruct(device.info.usb3Info, typeof(SciCam.SCI_DEVICE_USB3_INFO));
+                    if (!string.IsNullOrEmpty(usbinfo.userDefineName))
+                    {
+                        chDeviceName = string.Format("{0} [{1}]", usbinfo.modelName, usbinfo.userDefineName);
+
+                    }
+                    else
+                    {
+                        chDeviceName = string.Format("{0} [{1}]", usbinfo.modelName, usbinfo.serialNumber);
+
+                    }
+                    if (string.Equals(usbinfo.serialNumber, _SerialNumber))
+                    {
+                        ManufacturerName = usbinfo.manufactureName;
+                        ModelName = usbinfo.modelName;
+                        SerialNumber = usbinfo.serialNumber;
+                        CameraType = CameraType.USB;
+                        CameraInfo = device;
+                        IsHaveCamera = true;
+                        break;
+                    }
+                }
+
+
+
+            }
+
+        }
+
+        #region 方法
+
+        /// <summary>
+        /// 获取设备
+        /// </summary>
+        /// <returns></returns>
+        public static CameraInfo[] GetDevices()
+        {
+            List<CameraInfo> cameras = new List<CameraInfo>();
+            try
+            {
+
+                // ch: 初始化 SDK | en: Initialize SDK
+                SDKSystem.Initialize();
+
+                List<IDeviceInfo> devInfoList = new List<IDeviceInfo>();
+                // ch:枚举设备 | en:Enum device
+                int nRet = DeviceEnumerator.EnumDevices(DeviceTLayerType.MvGigEDevice | DeviceTLayerType.MvVirGigEDevice | DeviceTLayerType.MvGenTLGigEDevice | DeviceTLayerType.MvUsbDevice | DeviceTLayerType.MvVirUsbDevice, out devInfoList);
+                if (nRet != MvError.MV_OK)
+                {
+                    throw new Exception($"Enumerate devices fail: {nRet:x8}");
+                }
+
+                foreach (var devInfo in devInfoList)
+                {
+                    if (devInfo.TLayerType == DeviceTLayerType.MvGigEDevice || devInfo.TLayerType == DeviceTLayerType.MvVirGigEDevice || devInfo.TLayerType == DeviceTLayerType.MvGenTLGigEDevice)
+                    {
+                        IGigEDeviceInfo gigeDevInfo = devInfo as IGigEDeviceInfo;
+                        uint nIp1 = ((gigeDevInfo.CurrentIp & 0xff000000) >> 24);
+                        uint nIp2 = ((gigeDevInfo.CurrentIp & 0x00ff0000) >> 16);
+                        uint nIp3 = ((gigeDevInfo.CurrentIp & 0x0000ff00) >> 8);
+                        uint nIp4 = (gigeDevInfo.CurrentIp & 0x000000ff);
+                        Console.WriteLine("DevIP: {0}.{1}.{2}.{3}", nIp1, nIp2, nIp3, nIp4);
+                        cameras.Add(new CameraInfo()
+                        {
+                            CameraName = "",
+                            CameraBrand = CameraBrand.HikRobot_MVS,
+                            CameraType = CameraType.GIGE,
+                            Id = Guid.NewGuid(),
+                            ManufacturerName = devInfo.ManufacturerName,
+                            Model = devInfo.ModelName,
+                            SerialNumber = devInfo.SerialNumber,
+                            CameraIp = $"{nIp1}.{nIp2}.{nIp3}.{nIp4}",
+                        });
+                    }
+                    else if (devInfo.TLayerType == DeviceTLayerType.MvUsbDevice || devInfo.TLayerType == DeviceTLayerType.MvVirUsbDevice)
+                    {
+                        cameras.Add(new CameraInfo()
+                        {
+                            CameraName = "",
+                            CameraBrand = CameraBrand.HikRobot_MVS,
+                            CameraType = CameraType.USB,
+                            Id = Guid.NewGuid(),
+                            ManufacturerName = devInfo.ManufacturerName,
+                            Model = devInfo.ModelName,
+                            SerialNumber = devInfo.SerialNumber,
+                            CameraIp = "",
+                        });
+                    }
+                }
+            }
+            catch (Exception ex)
+            {
+                // LogHelper.WriteLogError("搜索海康相机列表时出错!", ex);
+            }
+
+            return cameras.ToArray();
+        }
+
+
+
+        /// <summary>
+        /// 打开相机
+        /// </summary>
+        /// <returns></returns>
+        /// <exception cref="Exception"></exception>
+        public bool OpenDevice()
+        {
+            if (!IsHaveCamera)
+            {
+
+                SCI_DEVICE_INFO_LIST devInfoList = new SCI_DEVICE_INFO_LIST();
+                // ch:枚举设备 | en:Enum device
+                uint nReVal = SciCam.DiscoveryDevices(ref devInfoList, (uint)(SciCam.SciCamTLType.SciCam_TLType_Gige) | (uint)(SciCam.SciCamTLType.SciCam_TLType_Usb3));
+                if (nReVal != SciCam.SCI_CAMERA_OK)
+                {
+                    throw new Exception("Discovery devices failed!");
+                }
+
+                string chDeviceName;
+                foreach (var devInfo in devInfoList.pDevInfo)
+                {
+                    SciCam.SCI_DEVICE_INFO device = devInfo;
+                    SciCam.SciCamTLType devTlType = device.tlType;
+                    SciCam.SciCamDeviceType devType = device.devType;
+                    if (devTlType == SciCam.SciCamTLType.SciCam_TLType_Usb3)
+                    {
+                        SciCam.SCI_DEVICE_USB3_INFO usbinfo = (SciCam.SCI_DEVICE_USB3_INFO)SciCam.ByteToStruct(device.info.usb3Info, typeof(SciCam.SCI_DEVICE_USB3_INFO));
+                        if (!string.IsNullOrEmpty(usbinfo.userDefineName))
+                        {
+                            chDeviceName = string.Format("{0} [{1}]", usbinfo.modelName, usbinfo.userDefineName);
+
+                        }
+                        else
+                        {
+                            chDeviceName = string.Format("{0} [{1}]", usbinfo.modelName, usbinfo.serialNumber);
+
+                        }
+                        if (string.Equals(usbinfo.serialNumber, SerialNumber))
+                        {
+                            ManufacturerName = usbinfo.manufactureName;
+                            ModelName = usbinfo.modelName;
+                            SerialNumber = usbinfo.serialNumber;
+                            CameraType = CameraType.GIGE;
+                            CameraInfo = device;
+                            IsHaveCamera = true;
+                            break;
+                        }
+                    }
+
+
+
+                }
+                IsConnected = false;
+
+                if (!IsHaveCamera)
+                {
+                    throw new Exception("没有发现相机");
+                }
+            }
+
+
+            if (!IsConnected)
+            {
+                uint nReVal = SciCam.SCI_CAMERA_OK;
+                for (int i = 0; i < m_stDevList.count; i++)
+                {
+                    SciCam.SCI_DEVICE_USB3_INFO usbinfo = (SciCam.SCI_DEVICE_USB3_INFO)SciCam.ByteToStruct(m_stDevList.pDevInfo[i].info.usb3Info, typeof(SciCam.SCI_DEVICE_USB3_INFO));
+                    if (SerialNumber == usbinfo.serialNumber)
+                    {
+                        nReVal = m_currentDev.CreateDevice(ref m_stDevList.pDevInfo[i]);
+                        if (nReVal == SciCam.SCI_CAMERA_OK)
+                        {
+                            nReVal = m_currentDev.OpenDevice();
+                            if (nReVal != SciCam.SCI_CAMERA_OK)
+                            {
+                                IsConnected = false;
+                                m_currentDev.DeleteDevice();
+                                LogHelper.Info("Open device failed"+ nReVal);
+                                return false;
+                            }
+                            IsConnected = true;
+                            break;
+                        }
+                        else
+                        {
+                            IsConnected = false;
+                        }
+                    }
+                }
+
+            }
+
+            return IsConnected;
+        }
+
+        /// <summary>
+        /// 关闭相机
+        /// </summary>
+        public void CloseDevice()
+        {
+
+            if (IsGrabbing)
+            {
+                StopGrabbing();
+            }
+            if (!IsConnected) return;
+            uint nReVal = m_currentDev.CloseDevice();
+            if (nReVal != SciCam.SCI_CAMERA_OK)
+            {
+                LogHelper.Info("Close device failed");
+                return;
+            }
+            nReVal = m_currentDev.DeleteDevice();
+            if (nReVal != SciCam.SCI_CAMERA_OK)
+            {
+                LogHelper.Info("Delete device failed" + nReVal);
+
+            }
+
+
+            IsGrabbing = false;
+
+            if (IsConnected)
+            {
+                IsConnected = false;
+                CameraConnectChangedEvent?.Invoke(ID, IsConnected);
+            }
+        }
+
+        /// <summary>
+        /// 取图前的必要操作步骤
+        /// </summary>
+        /// <returns></returns>
+        //private Int32 NecessaryOperBeforeGrab()
+        //{
+        //    // ch:取图像宽 | en:Get Iamge Width
+        //    IIntValue pcWidth = null;
+        //    int nRet = m_MyCamera.Parameters.GetIntValue("Width", out pcWidth);
+        //    if (nRet != MvError.MV_OK)
+        //    {
+        //        return nRet;
+        //    }
+        //    ImageWidth = (UInt32)pcWidth.CurValue;
+
+
+        //    // ch:取图像高 | en:Get Iamge Height
+        //    IIntValue pcHeight = null;
+        //    nRet = m_MyCamera.Parameters.GetIntValue("Height", out pcHeight);
+        //    if (nRet != MvError.MV_OK)
+        //    {
+        //        return nRet;
+        //    }
+        //    ImageHeight = (UInt32)pcHeight.CurValue;
+
+        //    // ch:取像素格式 | en:Get Pixel Format
+        //    IEnumValue pcPixelFormat = null;
+        //    nRet = m_MyCamera.Parameters.GetEnumValue("PixelFormat", out pcPixelFormat);
+        //    if (nRet != MvError.MV_OK)
+        //    {
+        //        return nRet;
+        //    }
+        //    PixelType = (MvCameraControl.MvGvspPixelType)pcPixelFormat.CurEnumEntry.Value;
+
+
+        //    return MvError.MV_OK;
+        //}
+
+        /// <summary>
+        /// 采集图像
+        /// </summary>
+        /// <returns></returns>
+        public ICogImage Grab()
+        {
+
+            m_currentDev.StartGrabbing();
+            uint nReVal = SciCam.SCI_CAMERA_OK;
+            IntPtr payload = IntPtr.Zero;
+            nReVal = m_currentDev.Grab(ref payload);
+            if (nReVal == SciCam.SCI_CAMERA_OK)
+            {
+                int reVal = GetConvertedInfo(payload);
+            }
+            m_currentDev.StopGrabbing();
+            return Image;
+        }
+
+        /// <summary>
+        /// 开始采集图像
+        /// </summary>
+        public void StartGrabbing()
+        {
+            if (IsGrabbing)
+                return;
+            IsGrabbing = true;
+            m_hReceiveThread = new Thread(GetStreamThreadProc) { IsBackground = true };
+            m_hReceiveThread.Start();
+        }
+        /// <summary>
+        /// 开始触发模式采集图像
+        /// </summary>
+        public void StartTriggerGrabbing()
+        {
+            StartGrabbing();
+        }
+        /// <summary>
+        /// 停止采集图像
+        /// </summary>
+        public void StopGrabbing()
+        {
+            try
+            {
+                if (IsGrabbing)
+                {
+                    IsGrabbing = false;
+                    Thread.Sleep(1000);
+                }
+                if (m_hReceiveThread != null)
+                {
+                    m_hReceiveThread.Abort();
+                    m_hReceiveThread = null;
+                }
+            }
+            catch (Exception)
+            {
+
+            }
+        }
+
+        //private void FrameGrabedEventHandler(object sender, FrameGrabbedEventArgs e)
+        //{
+        //    GrabImageCallbackEvent?.Invoke(AnalyticImage(e.FrameOut));
+        //    //Console.WriteLine("Get one frame: Width[{0}] , Height[{1}] , ImageSize[{2}], FrameNum[{3}]", e.FrameOut.Image.Width, e.FrameOut.Image.Height, e.FrameOut.Image.ImageSize, e.FrameOut.FrameNum);
+        //}
+
+        /// <summary>
+        /// Converts the image data from the specified <see cref="IFrameOut"/> object into an <see cref="ICogImage"/>
+        /// format.
+        /// </summary>
+        /// <remarks>This method processes both color and monochrome images, converting them to a
+        /// compatible format for further analysis. Unsupported pixel formats are not processed, and the method will
+        /// return <see langword="null"/> in such cases. The caller is responsible for ensuring that the <paramref
+        /// name="frameOut"/> parameter is valid and properly initialized.</remarks>
+        /// <param name="frameOut">The frame output containing the image data to be analyzed and converted.</param>
+        /// <returns>An <see cref="ICogImage"/> object representing the converted image.  Returns <see langword="null"/> if the
+        /// image format is unsupported or if an error occurs during conversion.</returns>
+
+
+
+        /// <summary>
+        /// 设置曝光时间
+        /// </summary>
+        /// <param name="ExposureTime"></param>
+        /// <returns></returns>
+        public bool SetExposureTime(float ExposureTime)
+        {
+            string[] nodeName = new string[]
+            {
+                "ExposureTime",
+                "ExposureTimeAbs",
+                "ExposureTimeRaw"
+            };
+            uint nReVal = SciCam.SCI_CAMERA_OK;
+            int iExposure = (int)ExposureTime;
+            for (int i = 0; i < nodeName.Count(); i++)
+            {
+                nReVal = m_currentDev.SetIntValue(nodeName[i], iExposure);
+                if (nReVal != SciCam.SCI_CAMERA_OK)
+                {
+                    double dExposure = ExposureTime;
+                    nReVal = m_currentDev.SetFloatValue(nodeName[i], dExposure);
+                    if (nReVal == SciCam.SCI_CAMERA_OK)
+                    {
+                       return true;
+                    }
+
+                }
+
+            }
+
+        
+            return false;
+        }
+
+        /// <summary>
+        /// 获取曝光时间
+        /// </summary>
+        /// <returns></returns>
+        public float GetExposureTime()
+        {
+
+            string[] nodeName = new string[]
+            {
+                "ExposureTime",
+                "ExposureTimeAbs",
+                "ExposureTimeRaw"
+            };
+
+            uint nReVal = SciCam.SCI_CAMERA_OK;
+            for (int i = 0; i < nodeName.Count(); i++)
+            {
+                SciCam.SCI_NODE_VAL_INT iNodeVal = new SciCam.SCI_NODE_VAL_INT();
+                nReVal = m_currentDev.GetIntValueEx(SciCam.SciCamDeviceXmlType.SciCam_DeviceXml_Camera, nodeName[i], ref iNodeVal);
+                if (nReVal != SciCam.SCI_CAMERA_OK)
+                {
+                    SciCam.SCI_NODE_VAL_FLOAT fNodeVal = new SciCam.SCI_NODE_VAL_FLOAT();
+                    nReVal = m_currentDev.GetFloatValueEx(SciCam.SciCamDeviceXmlType.SciCam_DeviceXml_Camera, nodeName[i], ref fNodeVal);
+                    if (nReVal == SciCam.SCI_CAMERA_OK)
+                    {
+                        return (float)fNodeVal.dVal;
+                        
+                    }
+                }
+                else
+                {
+                    return iNodeVal.nVal;
+                    
+                }
+            }
+
+            return 0;
+        }
+
+        /// <summary>
+        /// 设置增益
+        /// </summary>
+        /// <param name="Gain"></param>
+        /// <returns></returns>
+        public bool SetGain(float Gain)
+        {
+          
+
+           uint nReVal = m_currentDev.SetFloatValue("Gain", Gain);
+
+            if (nReVal != SciCam.SCI_CAMERA_OK)
+            {
+                return false;
+            }
+            else
+            {
+                return true;
+            }
+        }
+
+        /// <summary>
+        /// 获取增益
+        /// </summary>
+        /// <returns></returns>
+        public float GetGain()
+        {
+            SciCam.SCI_NODE_VAL_FLOAT fNodeVal = new SciCam.SCI_NODE_VAL_FLOAT();
+            uint nReVal = m_currentDev.GetFloatValueEx(SciCam.SciCamDeviceXmlType.SciCam_DeviceXml_Camera, "Gain", ref fNodeVal);
+            if (nReVal == SciCam.SCI_CAMERA_OK)
+            {
+                return (float)fNodeVal.dVal;
+
+            }
+            return 0;
+        }
+
+        /// <summary>
+        /// ch:获取触发模式 | en:Get Trigger Mode
+        /// </summary>
+        /// <returns>On/Off</returns>
+        public bool GetTriggerMode()
+        {
+            SciCam.SCI_NODE_VAL_ENUM eNodeVal = new SciCam.SCI_NODE_VAL_ENUM();
+            uint nReVal = m_currentDev.GetEnumValue("TriggerMode", ref eNodeVal);
+            if (nReVal != SciCam.SCI_CAMERA_OK)
+            {
+            return false ;
+            }
+            return true;
+        }
+
+        /// <summary>
+        /// 设置触发模式
+        /// </summary>
+        /// <param name="mode">触发模式</param>
+        /// <param name="triggerSource">触发源0 - Line0;1 - Line1;2 - Line2;3 - Line3;4 - Counter;7 - Software;</param>
+        /// <returns></returns>
+        public bool SetTriggerMode(bool mode, int triggerSource)
+        {
+            string strmode = mode ? "On" : "Off";
+            if (m_currentDev.IsDeviceOpen())
+            {
+                uint nRet = m_currentDev.SetEnumValueByStringEx(SciCam.SciCamDeviceXmlType.SciCam_DeviceXml_Camera, "TriggerMode", strmode);
+                if (nRet != MvError.MV_OK)
+                {
+                    return false;
+                }
+                if (mode)
+                {
+
+                    return SetTriggerSource(triggerSource);
+                }
+                else
+                {
+
+
+                }
+                return true;
+            }
+            return false;
+        }
+
+        /// <summary>
+        /// Sets the trigger source for the camera.
+        /// </summary>
+        /// <param name="source">0 - Line0;1 - Line1;2 - Line2;3 - Line3;4 - Counter;7 - Software;</param>
+        /// <returns></returns>
+        /// <exception cref="ArgumentOutOfRangeException"></exception>
+        public bool SetTriggerSource(int source)
+        {
+            // ch:触发源选择:0 - Line0; | en:Trigger source select:0 - Line0;
+            //           1 - Line1;
+            //           2 - Line2;
+            //           3 - Line3;
+            //           4 - Counter;
+            //           7 - Software;
+            string sourceStr;
+            switch (source)
+            {
+                case 0: sourceStr = "Line1"; break;
+                case 1: sourceStr = "Line1"; break;
+                case 2: sourceStr = "Line2"; break;
+                case 3: sourceStr = "Line3"; break;
+                case 4: sourceStr = "Counter"; break;
+                case 7: sourceStr = "Software"; break;
+                default: throw new ArgumentOutOfRangeException(nameof(source), "Invalid trigger source value");
+            }
+
+
+            uint nReVal = m_currentDev.SetEnumValueByStringEx(SciCam.SciCamDeviceXmlType.SciCam_DeviceXml_Camera, "TriggerSource", "Line1");
+            if (SciCam.SCI_CAMERA_OK != nReVal)
+            {
+                LogHelper.Info("Set TriggerSource to Line1 fail! ");
+                return false;
+            }
+         
+           
+            return true;
+        }
+
+        /// <summary>
+        /// Retrieves the current trigger source setting of the camera.
+        /// </summary>
+        /// <remarks>This method queries the camera's parameters to determine the current trigger source. 
+        /// If the retrieval is unsuccessful or the trigger source is not recognized, the method returns -1.</remarks>
+        /// <returns>An integer representing the trigger source: <list type="bullet"> <item><description>0 for
+        /// "Line0".</description></item> <item><description>1 for "Line1".</description></item> <item><description>2
+        /// for "Line2".</description></item> <item><description>3 for "Line3".</description></item>
+        /// <item><description>4 for "Counter".</description></item> <item><description>7 for
+        /// "Software".</description></item> <item><description>-1 if the trigger source is unknown or if the retrieval
+        /// fails.</description></item> </list></returns>
+        public int GetTriggerSource()
+        {
+            SciCam.SCI_NODE_VAL_ENUM enumValue = new SciCam.SCI_NODE_VAL_ENUM();
+            uint result = m_currentDev.GetEnumValueEx(SciCam.SciCamDeviceXmlType.SciCam_DeviceXml_Card, "TriggerSource", ref enumValue);
+            if (result == MvError.MV_OK)
+            {
+                switch (enumValue.items[0].desc)
+                {
+                    case "Line0": return 0;
+                    case "Line1": return 1;
+                    case "Line2": return 2;
+                    case "Line3": return 3;
+                    case "Counter": return 4;
+                    case "Software": return 7;
+                    default: return -1; // 未知触发源
+                }
+            }
+            return -1; // 获取失败
+        }
+
+        /// <summary>
+        /// Sends a software trigger command to the camera.
+        /// </summary>
+        /// <remarks>This method triggers the camera to capture an image or perform an action  based on
+        /// its current configuration. Ensure the camera is properly  initialized and configured to respond to software
+        /// triggers before calling this method.</remarks>
+        public void TriggerSoftware()
+        {
+            // ch:触发软件 | en:Trigger Software
+            // 执行
+            uint nReVal = m_currentDev.SetCommandValue("TriggerSoftware");
+            if (nReVal != SciCam.SCI_CAMERA_OK)
+            {
+                LogHelper.Info("TriggerSoftware写入失败");
+            }
+
+          
+
+        }
+
+        private void GetStreamThreadProc()
+        {
+
+            m_currentDev.StartGrabbing();
+            uint nReVal = SciCam.SCI_CAMERA_OK;
+            IntPtr payload = IntPtr.Zero;
+            if (m_currentDev.IsDeviceOpen())
+            {
+
+                while (IsGrabbing)
+                {
+
+                    nReVal = m_currentDev.Grab(ref payload);
+                    if (nReVal == SciCam.SCI_CAMERA_OK)
+                    {
+                        int reVal = GetConvertedInfo(payload);
+                    }
+
+                    nReVal = m_currentDev.FreePayload(payload);
+                }
+            }
+
+            IsGrabbing = false;
+        }
+
+
+        private int GetConvertedInfo(IntPtr payload)
+        {
+            if (payload == IntPtr.Zero)
+            {
+                return -1;
+            }
+
+            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 -1;
+            }
+
+            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 -1;
+            }
+
+            IntPtr imgData = IntPtr.Zero;
+            nReVal = SciCam.PayloadGetImage(payload, ref imgData);
+            if (nReVal != SciCam.SCI_CAMERA_OK)
+            {
+                return -1;
+            }
+
+            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.PayloadConvertImageEx(ref payloadAttribute.imgAttr, imgData, SciCam.SciCamPixelType.Mono8, IntPtr.Zero, ref destImgSize, true, 0);
+                if (nReVal == SciCam.SCI_CAMERA_OK)
+                {
+                    IntPtr destImg = Marshal.AllocHGlobal((int)destImgSize);
+                    try
+                    {
+                        nReVal = SciCam.PayloadConvertImageEx(ref payloadAttribute.imgAttr, imgData, SciCam.SciCamPixelType.Mono8, destImg, ref destImgSize, true, 0);
+                        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, GdiPlus.PixelFormat.Format8bppIndexed);
+                            GdiPlus.BitmapData bitmapData = bitMap.LockBits(new Rectangle(0, 0, (int)imgWidth, (int)imgHeight), GdiPlus.ImageLockMode.WriteOnly, GdiPlus.PixelFormat.Format8bppIndexed);
+                            Marshal.Copy(bBitmap, 0, bitmapData.Scan0, (int)destImgSize);
+                            bitMap.UnlockBits(bitmapData);
+
+                            //设置调色板
+                            GdiPlus.ColorPalette palette = bitMap.Palette;
+                            for (int i = 0; i < 256; i++)
+                            {
+                                palette.Entries[i] = Color.FromArgb(i, i, i);
+                            }
+                            bitMap.Palette = palette;
+                            //显示图片
+                            Image = new CogImage8Grey(bitMap);
+                            ImageCallbackEvent?.Invoke(Image, TotalTime, ErrorMessage);
+                            GrabImageCallbackEvent?.Invoke(Image);
+                        }
+                    }
+                    catch (Exception ex)
+                    {
+
+                    }
+                    finally
+                    {
+                        Marshal.FreeHGlobal(destImg);
+                    }
+                }
+            }
+            else
+            {
+                nReVal = SciCam.PayloadConvertImageEx(ref payloadAttribute.imgAttr, imgData, SciCam.SciCamPixelType.RGB8, IntPtr.Zero, ref destImgSize, true, 0);
+                if (nReVal == SciCam.SCI_CAMERA_OK)
+                {
+                    IntPtr destImg = Marshal.AllocHGlobal((int)destImgSize);
+                    try
+                    {
+                        nReVal = SciCam.PayloadConvertImageEx(ref payloadAttribute.imgAttr, imgData, SciCam.SciCamPixelType.RGB8, destImg, ref destImgSize, true, 0);
+                        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, GdiPlus.PixelFormat.Format24bppRgb);
+                            GdiPlus.BitmapData bitmapData = bitMap.LockBits(new Rectangle(0, 0, (int)imgWidth, (int)imgHeight), GdiPlus.ImageLockMode.WriteOnly, GdiPlus.PixelFormat.Format24bppRgb);
+                            Marshal.Copy(bBitmap, 0, bitmapData.Scan0, (int)destImgSize);
+                            bitMap.UnlockBits(bitmapData);
+
+                            //显示图片
+                            Image = new CogImage8Grey(bitMap);
+                            ImageCallbackEvent?.Invoke(Image, TotalTime, ErrorMessage);
+                            GrabImageCallbackEvent?.Invoke(Image);
+                        }
+                    }
+                    catch (Exception ex)
+                    {
+
+                    }
+                    finally
+                    {
+                        Marshal.FreeHGlobal(destImg);
+                    }
+                }
+            }
+            return 0;
+        }
+        /// <summary>
+        /// Converts raw image data into an <see cref="ICogImage"/> object, supporting both monochrome and color pixel
+        /// formats.
+        /// </summary>
+        /// <remarks>This method supports both monochrome (PixelType_Gvsp_Mono8) and color pixel formats. 
+        /// For color images, the method processes the image data as a planar color format.</remarks>
+        /// <param name="nHeight">The height of the image in pixels.</param>
+        /// <param name="nWidth">The width of the image in pixels.</param>
+        /// <param name="pImageBuf">A pointer to the buffer containing the raw image data.</param>
+        /// <param name="enPixelType">The pixel format of the image, specified as a <see cref="MvCameraControl.MvGvspPixelType"/> value.</param>
+        /// <returns>An <see cref="ICogImage"/> object representing the converted image.  Returns <see langword="null"/> if the
+        /// conversion fails.</returns>
+        private ICogImage ConvertToICogImage(UInt32 nHeight, UInt32 nWidth, IntPtr pImageBuf, MvCameraControl.MvGvspPixelType enPixelType)
+        {
+            ICogImage cogImage = null;
+            // ch:获取步长 || en: Get nRowStep
+            uint m_nRowStep = nWidth * nHeight;
+            // ch: 显示 || display
+            try
+            {
+                if (enPixelType == MvCameraControl.MvGvspPixelType.PixelType_Gvsp_Mono8)
+                {
+                    CogImage8Root cogImage8Root = new CogImage8Root();
+                    cogImage8Root.Initialize((Int32)nWidth, (Int32)nHeight, pImageBuf, (Int32)nWidth, null);
+
+                    CogImage8Grey cogImage8Grey = new CogImage8Grey();
+                    cogImage8Grey.SetRoot(cogImage8Root);
+                    cogImage = cogImage8Grey.ScaleImage((int)nWidth, (int)nHeight);
+                    System.GC.Collect();
+                }
+                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() + m_nRowStep);
+                    image1.Initialize((int)nWidth, (int)nHeight, ptr1, (int)nWidth, null);
+
+                    CogImage8Root image2 = new CogImage8Root();
+                    IntPtr ptr2 = new IntPtr(pImageBuf.ToInt64() + m_nRowStep * 2);
+                    image2.Initialize((int)nWidth, (int)nHeight, ptr2, (int)nWidth, null);
+
+                    CogImage24PlanarColor colorImage = new CogImage24PlanarColor();
+                    colorImage.SetRoots(image0, image1, image2);
+
+                    cogImage = colorImage.ScaleImage((int)nWidth, (int)nHeight);
+                    System.GC.Collect();
+                }
+            }
+            catch (System.Exception ex)
+            {
+                ErrorMessage = $"转换ICogImage出错: {ex.Message}";
+                return null;
+            }
+            return cogImage;
+        }
+
+        /// <summary>
+        /// 图像是否为Mono格式
+        /// </summary>
+        /// <param name="enType"></param>
+        /// <returns></returns>
+        private bool IsMonoPixelFormat(MvCameraControl.MvGvspPixelType enType)
+        {
+            switch (enType)
+            {
+                case MvCameraControl.MvGvspPixelType.PixelType_Gvsp_Mono8:
+                case MvCameraControl.MvGvspPixelType.PixelType_Gvsp_Mono10:
+                case MvCameraControl.MvGvspPixelType.PixelType_Gvsp_Mono10_Packed:
+                case MvCameraControl.MvGvspPixelType.PixelType_Gvsp_Mono12:
+                case MvCameraControl.MvGvspPixelType.PixelType_Gvsp_Mono12_Packed:
+                case MvCameraControl.MvGvspPixelType.PixelType_Gvsp_Mono16:
+                    return true;
+                default:
+                    return false;
+            }
+        }
+
+        /// <summary>
+        /// 图像是否为彩色
+        /// </summary>
+        /// <param name="enType"></param>
+        /// <returns></returns>
+        private bool IsColorPixelFormat(MvCameraControl.MvGvspPixelType enType)
+        {
+            switch (enType)
+            {
+                case MvCameraControl.MvGvspPixelType.PixelType_Gvsp_RGB8_Packed:
+                case MvCameraControl.MvGvspPixelType.PixelType_Gvsp_BGR8_Packed:
+                case MvCameraControl.MvGvspPixelType.PixelType_Gvsp_RGBA8_Packed:
+                case MvCameraControl.MvGvspPixelType.PixelType_Gvsp_BGRA8_Packed:
+                case MvCameraControl.MvGvspPixelType.PixelType_Gvsp_YUV422_Packed:
+                case MvCameraControl.MvGvspPixelType.PixelType_Gvsp_YUV422_YUYV_Packed:
+                case MvCameraControl.MvGvspPixelType.PixelType_Gvsp_BayerGR8:
+                case MvCameraControl.MvGvspPixelType.PixelType_Gvsp_BayerRG8:
+                case MvCameraControl.MvGvspPixelType.PixelType_Gvsp_BayerGB8:
+                case MvCameraControl.MvGvspPixelType.PixelType_Gvsp_BayerBG8:
+                case MvCameraControl.MvGvspPixelType.PixelType_Gvsp_BayerRBGG8:
+                case MvCameraControl.MvGvspPixelType.PixelType_Gvsp_BayerGB10:
+                case MvCameraControl.MvGvspPixelType.PixelType_Gvsp_BayerGB10_Packed:
+                case MvCameraControl.MvGvspPixelType.PixelType_Gvsp_BayerBG10:
+                case MvCameraControl.MvGvspPixelType.PixelType_Gvsp_BayerBG10_Packed:
+                case MvCameraControl.MvGvspPixelType.PixelType_Gvsp_BayerRG10:
+                case MvCameraControl.MvGvspPixelType.PixelType_Gvsp_BayerRG10_Packed:
+                case MvCameraControl.MvGvspPixelType.PixelType_Gvsp_BayerGR10:
+                case MvCameraControl.MvGvspPixelType.PixelType_Gvsp_BayerGR10_Packed:
+                case MvCameraControl.MvGvspPixelType.PixelType_Gvsp_BayerGB12:
+                case MvCameraControl.MvGvspPixelType.PixelType_Gvsp_BayerGB12_Packed:
+                case MvCameraControl.MvGvspPixelType.PixelType_Gvsp_BayerBG12:
+                case MvCameraControl.MvGvspPixelType.PixelType_Gvsp_BayerBG12_Packed:
+                case MvCameraControl.MvGvspPixelType.PixelType_Gvsp_BayerRG12:
+                case MvCameraControl.MvGvspPixelType.PixelType_Gvsp_BayerRG12_Packed:
+                case MvCameraControl.MvGvspPixelType.PixelType_Gvsp_BayerGR12:
+                case MvCameraControl.MvGvspPixelType.PixelType_Gvsp_BayerGR12_Packed:
+                case MvCameraControl.MvGvspPixelType.PixelType_Gvsp_BayerGR16:
+                case MvCameraControl.MvGvspPixelType.PixelType_Gvsp_BayerRG16:
+                case MvCameraControl.MvGvspPixelType.PixelType_Gvsp_BayerGB16:
+                case MvCameraControl.MvGvspPixelType.PixelType_Gvsp_BayerBG16:
+                    return true;
+                default:
+                    return false;
+            }
+        }
+
+        #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);
+        }
+
+        public bool CheckGrabImageCallbackEventIsHas(Action<ICogImage> action)
+        {
+            if (GrabImageCallbackEvent == null) return false;
+            return GrabImageCallbackEvent.GetInvocationList().Contains(action);
+        }
+
+        public bool CheckImageCallbackEventIsHas(Action<ICogImage, TimeSpan, string> action)
+        {
+            if (ImageCallbackEvent == null) return false;
+            return ImageCallbackEvent.GetInvocationList().Contains(action);
+        }
+
+        public bool CheckCameraConnectChangedEventIsHas(Action<Guid, bool> action)
+        {
+            if (CameraConnectChangedEvent == null) return false;
+            return CameraConnectChangedEvent.GetInvocationList().Contains(action);
+        }
+
+
+        #endregion
+    }
+}
+

+ 78 - 16
Core/Management.cs

@@ -42,6 +42,7 @@ using OpenCvSharp.Dnn;
 using LampInspectionMachine.Interfaces;
 using System.IO;
 using LampInspectionMachine.KHLplc;
+using LampInspectionMachine.Cameralibs.OPTCamera;
 
 namespace LampInspectionMachine.Core
 {
@@ -140,12 +141,28 @@ namespace LampInspectionMachine.Core
 
         }
 
+        Thread PlcConnChangThread ;
         public async void ConnectPlc() 
         {
             KHLCommunicate = new KHLCommunicate();
             await KHLCommunicate.ConnPlc();
+            if (KHLCommunicate.IsConnected)
+            {
+                PlcConnChangThread = new Thread(RefPlcConnect);
+                PlcConnChangThread.IsBackground = true;
+                PlcConnChangThread.Start();
+            }
         }
 
+        public async void RefPlcConnect() 
+        {
+            while (true)
+            {
+                if(!KHLCommunicate.IsConnected)
+                await  KHLCommunicate.ConnPlc();
+                Thread.Sleep(1000);
+            }
+        }
       
 
         /// <summary>
@@ -154,8 +171,9 @@ namespace LampInspectionMachine.Core
         public void LoadCameraDevice()
         {
             CameraService = new CameraService();
+            CamConfigs = FileHelper.ReadApplicationConfiguration();
             CameraList = FindLoadCamera();
-            CamConfigs=  FileHelper.ReadApplicationConfiguration();
+           
             if (CamConfigs.Count == 0)
             {
                 CamConfigs.Add(new CameraInfo() { CameraName="相机1", Id=new Guid(), SerialNumber="", VppIndex=0, VppFileName = "Cam1.vpp" });
@@ -191,12 +209,15 @@ namespace LampInspectionMachine.Core
                 myCamera = CreateCamera(CamConfigs[i]);
                 if (myCamera!=null&& myCamera.IsConnected) 
                 {
-                    if(myCamera.CheckImageCallbackEventIsHas(vision.CamCallBack))
+
+                    if (myCamera.CheckImageCallbackEventIsHas(vision.CamCallBack))
                     myCamera.ImageCallbackEvent -= vision.CamCallBack;
                     if ( !myCamera.CheckGrabImageCallbackEventIsHas(vision.ImageCallback) )
                         myCamera.GrabImageCallbackEvent += vision.ImageCallback;
                     myCamera.CameraConnectChangedEvent += vision.CameraConnectChangedEvent;
                     vision.IsConnected = true;
+                    myCamera.SetTriggerMode(true,0);
+                    myCamera.StartTriggerGrabbing();
                 }
                 vision.Camera= myCamera;
                 vision.PlcData.PlcToPcCodeIndex = (uint)(100 + i);
@@ -211,6 +232,8 @@ namespace LampInspectionMachine.Core
             }
             LogHelper.Info("模板加载完成");
             tipService.Tcolse();
+
+        
         }
 
         /// <summary>
@@ -248,6 +271,9 @@ namespace LampInspectionMachine.Core
                 CurrCamConfig.TriggerMode = false;
                 if ( !CurrCamera.CheckImageCallbackEventIsHas(Currvision.CamCallBack) )
                     CurrCamera.ImageCallbackEvent += Currvision.CamCallBack;
+
+                if (myCamera.CheckGrabImageCallbackEventIsHas(Currvision.ImageCallback))
+                    myCamera.GrabImageCallbackEvent -= Currvision.ImageCallback;
                 LogHelper.Info($"连续采集启动");
                 CurrCamera.StartGrabbing();
             }
@@ -273,12 +299,12 @@ namespace LampInspectionMachine.Core
                     ICogImage icogImage = new CogImage8Grey(bitmap);
 
                     // 设置输入参数
-                   Currvision.CogToolBlock.Inputs[0].Value = icogImage;
+                   Currvision.CogToolBlock.Inputs["Image"].Value = icogImage;
 
-                    // 执行处理
+                    //执行处理
                     Currvision.CogToolBlock.Run();
 
-                    // 获取输出结果
+                    //获取输出结果
                     if (Currvision.CogToolBlock.RunStatus.Result == CogToolResultConstants.Accept)
                     {
 
@@ -290,12 +316,12 @@ namespace LampInspectionMachine.Core
                                 record = item.Value as ICogRecord;
                         }
                         ICogImage cogImage = (ICogImage)Currvision.CogToolBlock.Inputs["Image"].Value;
-                        if (cogImage == null)
+                        if (cogImage != null)
                         {
 
                             Render = new ShowRender()
                             {
-                                Graphic = outputCollection["Graphic"].Value as CogGraphicCollection,
+                                //Graphic = outputCollection["Graphic"].Value as CogGraphicCollection,
                                 Image = icogImage,
                                 Record = record
                             };
@@ -379,16 +405,50 @@ namespace LampInspectionMachine.Core
         /// </summary>
         public ObservableCollection<string> FindLoadCamera()
         {
-            var listsn = MvCamera.GetDevices();
-            if (listsn.Length > 0)
+            ObservableCollection<string> strings = new ObservableCollection<string>();
+            if (CamConfigs.Count!=0) 
             {
-                
-                ObservableCollection<string> strings = new ObservableCollection<string>();
-                strings.Add("");
-                for (int i = 0; i < listsn.Length; i++)
+               
+                switch (CamConfigs[0].CameraBrand)
                 {
-                    strings.Add(listsn[i].SerialNumber);
+                    case CameraBrand.HikRobot_MVS:
+                        var listsn = MvCamera.GetDevices();
+                        if (listsn.Length > 0)
+                        {
+
+                           
+                            strings.Add("");
+                            for (int i = 0; i < listsn.Length; i++)
+                            {
+                                strings.Add(listsn[i].SerialNumber);
+                            }
+                            return strings;
+                        }
+                        break;
+                    case CameraBrand.Basler_Pylon:
+                        break;
+                    case CameraBrand.SciCamera_Opt:
+                        var listsn2 = MVOptCamera.GetDevices();
+                        if (listsn2.Length > 0)
+                        {
+
+
+                            strings.Add("");
+                            for (int i = 0; i < listsn2.Length; i++)
+                            {
+                                strings.Add(listsn2[i].SerialNumber);
+                            }
+                            return strings;
+                        }
+                        break;
+                    default:
+                        break;
                 }
+            }
+           
+            if (strings.Count > 0)
+            {
+               
                 return strings;
             }
             else
@@ -438,6 +498,7 @@ namespace LampInspectionMachine.Core
                     CurrCamera = null;
                     myCamera = CreateCamera(new CameraInfo() { SerialNumber = CurrCameraSn });
                     CurrCamera = myCamera;
+                    if(myCamera!=null)
                     CurrCamConfig.SerialNumber = myCamera.SerialNumber;
                     if ( myCamera != null&&  myCamera.IsConnected )
                     {
@@ -448,8 +509,9 @@ namespace LampInspectionMachine.Core
                         if ( !myCamera.CheckCameraConnectChangedEventIsHas(Currvision.CameraConnectChangedEvent) )
                             myCamera.CameraConnectChangedEvent += Currvision.CameraConnectChangedEvent;
                         Currvision.IsConnected = true;
+                        return true;
                     }
-                    return true;
+                    return false;
                 }
             }
             catch 
@@ -479,6 +541,7 @@ namespace LampInspectionMachine.Core
                     }
                     else
                     {
+                        myCamera.SetExposureTime(cameraInfo.ExpouseTime);
                         return myCamera;
                     }
                 }
@@ -620,7 +683,6 @@ namespace LampInspectionMachine.Core
             cogToolBlock1 = VisionProManagers[index].CogToolBlock;
             CurrCamera= VisionProManagers[ index ].Camera;
             CurrCamConfig = VisionProManagers[ index ].CameraInfo;
-            
             ToolBlockSwitched?.Invoke(index); // 触发事件,传递索引参数。
         }
         public void OnSaveSwitchToolBlock(int index)

+ 1 - 1
Core/TipService.cs

@@ -19,7 +19,7 @@ namespace LampInspectionMachine.Core
         }
         public void Tshow()
         {
-            
+                loadingView.Topmost = true;
                 loadingView.Show();
 
         }

+ 122 - 66
Core/VisionProManager.cs

@@ -23,36 +23,49 @@ using System.Threading.Tasks;
 using System.Windows.Forms;
 using System.Diagnostics;
 using LampInspectionMachine.KHLplc;
+using System.IO;
+using SqlSugar;
+using System.Windows.Controls;
 
 namespace LampInspectionMachine.Core
 {
-    public class VisionProManager:BindableBase
+    public class VisionProManager : BindableBase
     {
 
         private ICamera _camera;
 
-        private PlcData _plcData=new PlcData();
+        private PlcData _plcData = new PlcData();
 
         private CogToolBlock _cogToolBlock;
-        
-        private  readonly object _lock = new object();
 
-        private bool  _IsConnected;
+        private Queue<ResultsPlcInfo> values = new Queue<ResultsPlcInfo>();
+
+        private Thread PcToPlcValueTHread;
+
+        private readonly object _lockRead = new object();
+
+        private readonly object _lockWrite = new object();
 
-        private  CameraInfo _cameraInfo;
+        private int _IsNgCount = 0;
+
+        private int _IsOkCount = 0;
+
+        private bool _IsConnected;
+
+        private CameraInfo _cameraInfo;
         private Management _management;
 
-        private ShowRender render=new ShowRender();
+        private ShowRender render = new ShowRender();
         public ICamera Camera { get => _camera; set => _camera = value; }
         public CogToolBlock CogToolBlock { get => _cogToolBlock; set => _cogToolBlock = value; }
         public CameraInfo CameraInfo { get => _cameraInfo; set { SetProperty(ref _cameraInfo, value); } }
-        public Management Management { get => _management; set => _management =  value ; }
+        public Management Management { get => _management; set => _management = value; }
         public ShowRender Render
         {
-            get => render; 
+            get => render;
             set
             {
-                SetProperty(ref render, value);
+                SetProperty(ref render, value);  
             }
 
         }
@@ -66,6 +79,18 @@ namespace LampInspectionMachine.Core
         }
 
         public PlcData PlcData { get => _plcData; set => _plcData = value; }
+        public int IsNgCount { get => _IsNgCount; set{ SetProperty(ref _IsNgCount, value); } }
+        public int IsOkCount { get => _IsOkCount; set { SetProperty(ref _IsOkCount, value); } }
+
+        public VisionProManager()
+        {
+
+
+            PcToPlcValueTHread = new Thread(PcToPlcValueMethod);
+            PcToPlcValueTHread.IsBackground = true;
+            PcToPlcValueTHread.Start();
+
+        }
 
         /// <summary>
         ///调试图像显示
@@ -75,31 +100,12 @@ namespace LampInspectionMachine.Core
         {
             try
             {
-                if ( Management != null )
+                if (Management != null)
                 {
                     Management.Render = new ShowRender()
                     {
                         Image = image,
-
                     };
-                    
-
-                    if ( CogToolBlock != null )
-                    {
-                        lock ( _lock )
-                        {
-                            App.Current.Dispatcher.BeginInvoke(new Action(() =>
-                            {
-                                CogToolBlock.Inputs["Image"].Value = image;
-                            }));
-                            //CogToolBlock.Run();
-                            //CogImage = ( ICogImage ) CogToolBlock.Outputs[ 6 ].Value;
-                            //if ( CogImage==null ) 
-                            //{
-                            //    CogImage = image;
-                            //}
-                        }
-                    }
                 }
             }
             finally
@@ -111,26 +117,49 @@ namespace LampInspectionMachine.Core
         /// <summary>
         /// 运行视觉处理
         /// </summary>
-        public void ImageCallback(ICogImage image)
+        public async void ImageCallback(ICogImage image)
         {
-            ImageCallbackAsync(image).ConfigureAwait(false) ;
+            ImageCallbackAsync(image);
         }
-
-        public async Task ImageCallbackAsync(ICogImage image) 
+        private int OldCode = 0;
+        private Stopwatch stopwatch = new Stopwatch();
+        public async Task ImageCallbackAsync(ICogImage image)
         {
 
-            if ( CogToolBlock != null )
+            if (CogToolBlock != null)
             {
                 try
                 {
-                    lock (_lock)
+                    await Task.Run(() =>
                     {
+                        bool isok = true;
                         Guid guid = Guid.NewGuid();
-                        int resCode = Management.KHLCommunicate.ReadInt(PlcData.PlcToPcCodeIndex);
-                        LogHelper.CameraInfo(guid.ToString()+$"\r产品码:{resCode}");
-                        CogToolBlock.Inputs["Image"].Value = image;
-                        CogToolBlock.Run();
-                        LogHelper.CameraInfo(guid.ToString() + $"\r产品码:{resCode}[{CameraInfo.CameraName}]运行流程:{CogToolBlock.RunStatus.ProcessingTime:F1}ms");
+                        ushort resCode;
+                        lock (_lockRead)
+                        {
+                            stopwatch.Restart();
+                            while (true)
+                            {
+                                resCode = Management.KHLCommunicate.ReadUshort(PlcData.PlcToPcCodeIndex, 1)[0];
+                                if (OldCode != resCode)
+                                {
+                                    OldCode = resCode;
+                                    break;
+                                }
+                                if (stopwatch.ElapsedMilliseconds > 200)
+                                {
+                                    isok = false;
+                                    break;
+                                }
+                            }
+                            stopwatch.Stop();
+                          //  LogHelper.SwitchCameraInfo(Camera.Name, guid.ToString() + $"\r产品码:{resCode}");
+
+                            CogToolBlock.Inputs["Image"].Value = image;
+                            CogToolBlock.Run();
+                            
+                        }
+                       // LogHelper.SwitchCameraInfo(Camera.Name, guid.ToString() + $"\r产品码:{resCode}[{CameraInfo.CameraName}]运行流程:{CogToolBlock.RunStatus.ProcessingTime:F1}ms");
                         if (CogToolBlock.RunStatus.Result == CogToolResultConstants.Accept)
                         {
                             CogToolBlockTerminalCollection outputCollection = CogToolBlock.Outputs;
@@ -143,46 +172,34 @@ namespace LampInspectionMachine.Core
                             ICogImage cogImage = (ICogImage)CogToolBlock.Inputs["Image"].Value;
                             if (cogImage != null)
                             {
-
+                                Render = null;
                                 Render = new ShowRender()
                                 {
+                                    CameraName = Camera.Name,
                                     Graphic = outputCollection["Graphics"].Value as CogGraphicCollection,
-                                    Image = image,
+                                    Image = cogImage,
                                     Record = record,
                                     Result = (bool)(outputCollection["Result"].Value),
                                 };
-                              
-                               
                             }
-
-
                         }
                         else
                         {
+                            Render = null;
                             Render = new ShowRender()
                             {
-
+                                CameraName = Camera.Name,
                                 Image = image,
-                                Result=false
+                                Result = false
                             };
-                          
-                        }
-
 
-                        LogHelper.CameraInfo(guid.ToString() + $"\r产品码:{resCode}[{CameraInfo.CameraName}]流程结果:{Render.Result}");
-                        int Result = Render.Result ? 1 : 0;
-                        while (Management.KHLCommunicate.IsConnected)
-                        {
-                            if (Management.KHLCommunicate.ReadInt(PlcData.PcToPlcCompleted) == 0)
-                            {
-                                Management.KHLCommunicate.WriteInt(PlcData.PcToPlcCodeIndex, 1, new int[] { resCode });
-                                Management.KHLCommunicate.WriteInt(PlcData.PcToPlcResult, 1, new int[] { Result });
-                                Management.KHLCommunicate.WriteInt(PlcData.PcToPlcCompleted,1,new int[] {1});
-                                break;
-                            }
                         }
-                        LogHelper.CameraInfo(guid.ToString() + $"\r产品码:{resCode} 回复完成");
-                    }
+                      //  LogHelper.SwitchCameraInfo(Camera.Name, guid.ToString() + $"\r产品码:{resCode}[{CameraInfo.CameraName}]流程结果:{Render.Result}");
+                        ushort Result = (ushort)(Render.Result ? 1 : 2);
+                        if(isok)
+                        values.Enqueue(new ResultsPlcInfo() { Id=guid, Code = resCode, ProcessingTime=  CogToolBlock.RunStatus.ProcessingTime.ToString("F1"), Res = Result });
+                       
+                    });
                 }
                 catch (Exception ex)
                 {
@@ -190,10 +207,48 @@ namespace LampInspectionMachine.Core
                     LogHelper.Info("Plc交互出错");
                 }
             }
+           
             await Task.CompletedTask;
 
         }
 
+
+        public void PcToPlcValueMethod()
+        {
+            ResultsPlcInfo res;
+            while (true)
+            {
+                if (values.Count > 0)
+                {
+                    res = values.Dequeue();
+                    if (res != null)
+                    {
+                        while (Management.KHLCommunicate.IsConnected)
+                        {
+
+                            if (Management.KHLCommunicate.ReadUshort(PlcData.PcToPlcCompleted, 1)[0] != 1)
+                            {
+                                Management.KHLCommunicate.WriteUshort(PlcData.PcToPlcCodeIndex, 1, new ushort[] { res.Code });
+                                Management.KHLCommunicate.WriteUshort(PlcData.PcToPlcResult, 1, new ushort[] { res.Res });
+                                Management.KHLCommunicate.WriteUshort(PlcData.PcToPlcCompleted, 1, new ushort[] { 1 });
+                                break;
+                            }
+                            Thread.Sleep(5);
+                        }
+                        if (res.Res == 1)
+                        {
+                            IsOkCount++;
+                        }
+                        else
+                        {
+                            IsNgCount++;
+                        }
+                        LogHelper.SwitchCameraInfo(Camera.Name, Camera.Name + res.Id.ToString() + $"\r\n产品码:{res.Code},耗时:{res.ProcessingTime} 结果;{res.Res} 回复完成");
+                    }
+                }
+                Thread.Sleep(5);
+            }
+        }
         /// <summary>
         /// 相机连接或者断开时
         /// </summary>
@@ -204,8 +259,9 @@ namespace LampInspectionMachine.Core
             IsConnected = state;
         }
 
+        
+
 
-       
 
     }
 

+ 50 - 34
KHLPLC/KHLCommunicate.cs

@@ -8,25 +8,25 @@ using System.Threading.Tasks;
 using LampInspectionMachine.Log4xml;
 using System.Windows.Controls;
 using System.Threading;
+using LampInspectionMachine.Model;
 
 namespace LampInspectionMachine.KHLplc
 {
     public class KHLCommunicate
     {
-        byte[] readBuf = new byte[2048];
-        byte[] writeBuf = new byte[2048];
-        private bool  isConnected=false;
-        private int sock=0;
-       
+
+        private bool isConnected = false;
+        private int sock = 0;
+
 
         public bool IsConnected { get => isConnected; set => isConnected = value; }
 
 
 
-        public KHLCommunicate() 
+        public KHLCommunicate()
         {
 
-            
+
 
 
         }
@@ -53,9 +53,8 @@ namespace LampInspectionMachine.KHLplc
                     continue;
                 }
                 IsConnected = true;
-
+                ThreadPool.SetMinThreads(500,500);
                 LogHelper.Info("plc连接成功");
-
                 break;
             }
 
@@ -63,19 +62,20 @@ namespace LampInspectionMachine.KHLplc
 
 
 
-         
-        
+
+
 
         public bool[] ReadBool(uint topno, ushort offset, uint bitnum)
         {
-            if ( IsConnected )
+            if (IsConnected)
             {
 
                 bool[] rdBool = new bool[bitnum];
+                byte[] readBuf = new byte[2048];
                 int err = KHL.KHLReadDevicesAsBits(sock, KHLDevType.DEV_EM, topno, offset, bitnum, readBuf);
-                if ( err != 0 )
+                if (err != 0)
                 {
-                  
+                    IsConnected = false;
                     LogHelper.Info("plc连接读取错误:" + err);
                     throw new Exception("plc连接读取错误");
                 }
@@ -90,17 +90,19 @@ namespace LampInspectionMachine.KHLplc
 
 
 
-        public bool WriteBool(uint topno, ushort offset, uint bitnum,bool[] wrBool)
+        public bool WriteBool(uint topno, ushort offset, uint bitnum, bool[] wrBool)
         {
-            if ( IsConnected )
+            if (IsConnected)
             {
+                byte[] readBuf = new byte[2048];
+                byte[] writeBuf = new byte[2048];
 
                 bool[] rdBool = new bool[bitnum];
                 KHST.BoolToByte(ref writeBuf, wrBool, rdBool.Length, 0, 0);
                 int err = KHL.KHLWriteDevicesAsBits(sock, KHLDevType.DEV_ZF, topno, offset, bitnum, writeBuf);
-                if ( err != 0 )
+                if (err != 0)
                 {
-                   
+                    IsConnected = false;
                     LogHelper.Info("plc连接读取错误:" + err);
                     throw new Exception("plc连接读取错误");
                 }
@@ -109,28 +111,37 @@ namespace LampInspectionMachine.KHLplc
             return false;
         }
 
-        public ushort[] ReadUshort(uint topno, uint wordnum) 
+        public ushort[] ReadUshort(uint topno, uint wordnum)
         {
+            byte[] readBuf = new byte[2048];
+            byte[] writeBuf = new byte[2048];
+
             ushort[] rdUshort = new ushort[wordnum];
-            int err = KHL.KHLReadDevicesAsWords(sock, KHLDevType.DEV_EM, topno, wordnum, readBuf);
-            if ( err != 0 )
+            int err = KHL.KHLReadDevicesAsWords(sock, KHLDevType.DEV_DM, topno, wordnum, readBuf);
+            if (err != 0)
             {
-               
+                IsConnected = false;
                 LogHelper.Info("plc连接读取错误:" + err);
                 throw new Exception("plc连接读取错误");
             }
             KHST.ByteToUshort(ref rdUshort, readBuf, rdUshort.Length, 0);
-            return rdUshort;
+            if (topno == 200&& rdUshort[0]!=1)
+            {
+                int a = 0;
+            }
+                return rdUshort;
         }
 
-        public bool WriteUshort(uint topno, uint wordnum)
+        public bool WriteUshort(uint topno, uint wordnum, ushort[] rdUshort)
         {
-            ushort[] rdUshort = new ushort[wordnum];
+            byte[] readBuf = new byte[2048];
+            byte[] writeBuf = new byte[2048];
+
             KHST.UshortToByte(ref writeBuf, rdUshort, rdUshort.Length, 0);
-            int err = KHL.KHLWriteDevicesAsWords(sock, KHLDevType.DEV_ZF, topno, wordnum, writeBuf);
-            if ( err != 0 )
+            int err = KHL.KHLWriteDevicesAsWords(sock, KHLDevType.DEV_DM, topno, wordnum, writeBuf);
+            if (err != 0)
             {
-                
+                IsConnected = false;
                 LogHelper.Info("plc连接读取错误:" + err);
                 throw new Exception("plc连接读取错误");
             }
@@ -138,11 +149,13 @@ namespace LampInspectionMachine.KHLplc
         }
         public int ReadInt(uint topno)
         {
+            byte[] readBuf = new byte[2048];
+            byte[] writeBuf = new byte[2048];
             int[] rdint = new int[1];
             int err = KHL.KHLReadDevicesAsWords(sock, KHLDevType.DEV_EM, topno, 1, readBuf);
             if (err != 0)
             {
-               
+                IsConnected = false;
                 LogHelper.Info("plc连接读取错误:" + err);
                 throw new Exception("plc连接读取错误");
             }
@@ -151,11 +164,13 @@ namespace LampInspectionMachine.KHLplc
         }
         public int[] ReadInt(uint topno, uint wordnum)
         {
+            byte[] readBuf = new byte[2048];
+            byte[] writeBuf = new byte[2048];
             int[] rdint = new int[wordnum];
             int err = KHL.KHLReadDevicesAsWords(sock, KHLDevType.DEV_EM, topno, wordnum, readBuf);
-            if ( err != 0 )
+            if (err != 0)
             {
-              
+                IsConnected = false;
                 LogHelper.Info("plc连接读取错误:" + err);
                 throw new Exception("plc连接读取错误");
             }
@@ -167,12 +182,13 @@ namespace LampInspectionMachine.KHLplc
 
         public bool WriteInt(uint topno, uint wordnum, int[] rdInt)
         {
-            
+            byte[] readBuf = new byte[2048];
+            byte[] writeBuf = new byte[2048];
             KHST.IntToByte(ref writeBuf, rdInt, rdInt.Length, 0);
             int err = KHL.KHLWriteDevicesAsWords(sock, KHLDevType.DEV_ZF, topno, wordnum, writeBuf);
-            if ( err != 0 )
+            if (err != 0)
             {
-               
+                IsConnected = false;
                 LogHelper.Info("plc连接读取错误:" + err);
                 throw new Exception("plc连接读取错误");
             }

+ 36 - 0
LampInspectionMachine.csproj

@@ -18,6 +18,21 @@
     <Deterministic>true</Deterministic>
     <NuGetPackageImportStamp>
     </NuGetPackageImportStamp>
+    <PublishUrl>publish\</PublishUrl>
+    <Install>true</Install>
+    <InstallFrom>Disk</InstallFrom>
+    <UpdateEnabled>false</UpdateEnabled>
+    <UpdateMode>Foreground</UpdateMode>
+    <UpdateInterval>7</UpdateInterval>
+    <UpdateIntervalUnits>Days</UpdateIntervalUnits>
+    <UpdatePeriodically>false</UpdatePeriodically>
+    <UpdateRequired>false</UpdateRequired>
+    <MapFileExtensions>true</MapFileExtensions>
+    <ApplicationRevision>0</ApplicationRevision>
+    <ApplicationVersion>1.0.0.%2a</ApplicationVersion>
+    <IsWebBootstrapper>false</IsWebBootstrapper>
+    <UseApplicationTrust>false</UseApplicationTrust>
+    <BootstrapperEnabled>true</BootstrapperEnabled>
   </PropertyGroup>
   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
     <PlatformTarget>AnyCPU</PlatformTarget>
@@ -63,6 +78,9 @@
     <ErrorReport>prompt</ErrorReport>
     <Prefer32Bit>true</Prefer32Bit>
   </PropertyGroup>
+  <PropertyGroup>
+    <ApplicationIcon>1.ico</ApplicationIcon>
+  </PropertyGroup>
   <ItemGroup>
     <Reference Include="Cognex.VisionPro, Version=59.2.0.0, Culture=neutral, PublicKeyToken=ef0f902af9dee505, processorArchitecture=AMD64">
       <SpecificVersion>False</SpecificVersion>
@@ -140,6 +158,9 @@
     <Reference Include="Prism.Wpf, Version=8.1.97.5141, Culture=neutral, PublicKeyToken=40ee6c3a2184dc59, processorArchitecture=MSIL">
       <HintPath>packages\Prism.Wpf.8.1.97\lib\net47\Prism.Wpf.dll</HintPath>
     </Reference>
+    <Reference Include="SciCamera.Net">
+      <HintPath>C:\Program Files (x86)\OPTMV\Development\Windows\DotNet\AnyCPU\SciCamera.Net.dll</HintPath>
+    </Reference>
     <Reference Include="SqlSugar, Version=5.1.4.188, Culture=neutral, processorArchitecture=MSIL">
       <HintPath>packages\SqlSugar.5.1.4.188\lib\SqlSugar.dll</HintPath>
     </Reference>
@@ -216,6 +237,7 @@
     <Compile Include="Cameralibs\CameraService.cs" />
     <Compile Include="Cameralibs\CameraType.cs" />
     <Compile Include="Cameralibs\HKCamera\MvCamera.cs" />
+    <Compile Include="Cameralibs\OPTCamera\MVOptCamera.cs" />
     <Compile Include="Core\FileHelper.cs" />
     <Compile Include="Core\FilePath.cs" />
     <Compile Include="Interfaces\ICamera.cs" />
@@ -232,6 +254,7 @@
     <Compile Include="Model\CameraInfo.cs" />
     <Compile Include="Model\PlcData.cs" />
     <Compile Include="Model\ProjectData.cs" />
+    <Compile Include="Model\ResultsPlcInfo.cs" />
     <Compile Include="Model\ShowRender.cs" />
     <Compile Include="Model\Users.cs" />
     <Compile Include="ValueConverters\BoolToColorConverter.cs" />
@@ -344,8 +367,21 @@
     </Page>
   </ItemGroup>
   <ItemGroup>
+    <Resource Include="1.ico" />
     <Content Include="Resources\MvCameraControl.Net.dll" />
   </ItemGroup>
+  <ItemGroup>
+    <BootstrapperPackage Include=".NETFramework,Version=v4.8">
+      <Visible>False</Visible>
+      <ProductName>Microsoft .NET Framework 4.8 %28x86 和 x64%29</ProductName>
+      <Install>true</Install>
+    </BootstrapperPackage>
+    <BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
+      <Visible>False</Visible>
+      <ProductName>.NET Framework 3.5 SP1</ProductName>
+      <Install>false</Install>
+    </BootstrapperPackage>
+  </ItemGroup>
   <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
   <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
     <PropertyGroup>

+ 104 - 0
Log4xml/LogHelper.cs

@@ -1,5 +1,6 @@
 using System;
 using System.Linq;
+using System.Windows.Media.Media3D;
 
 
 namespace LampInspectionMachine.Log4xml
@@ -47,6 +48,109 @@ namespace LampInspectionMachine.Log4xml
         }
 
 
+        // #region 执行日志
+        private static readonly log4net.ILog camerainfologger2 = log4net.LogManager.GetLogger("CameraLogInfo2");
+        /// <summary>
+        /// 相机日志
+        /// </summary>
+        /// <param name="message">日志内容</param>
+        public static void CameraInfo2(string message)
+        {
+            // 可以改成type typeof(类)
+            if (camerainfologger2.IsInfoEnabled)
+            {
+                camerainfologger2.Info(message);
+                logChangHandler?.Invoke(new Log() { Time = DateTime.Now.ToString("yyyy-MM-dd:HH:mm:ss"), Msg = message });
+            }
+        }
+
+
+        // #region 执行日志
+        private static readonly log4net.ILog camerainfologger3 = log4net.LogManager.GetLogger("CameraLogInfo3");
+        /// <summary>
+        /// 相机日志
+        /// </summary>
+        /// <param name="message">日志内容</param>
+        public static void CameraInfo3(string message)
+        {
+            // 可以改成type typeof(类)
+            if (camerainfologger3.IsInfoEnabled)
+            {
+                camerainfologger3.Info(message);
+                logChangHandler?.Invoke(new Log() { Time = DateTime.Now.ToString("yyyy-MM-dd:HH:mm:ss"), Msg = message });
+            }
+        }
+
+
+
+        // #region 执行日志
+        private static readonly log4net.ILog camerainfologger4 = log4net.LogManager.GetLogger("CameraLogInfo4");
+        /// <summary>
+        /// 相机日志
+        /// </summary>
+        /// <param name="message">日志内容</param>
+        public static void CameraInfo4(string message)
+        {
+            // 可以改成type typeof(类)
+            if (camerainfologger4.IsInfoEnabled)
+            {
+                camerainfologger4.Info(message);
+                logChangHandler?.Invoke(new Log() { Time = DateTime.Now.ToString("yyyy-MM-dd:HH:mm:ss"), Msg = message });
+            }
+        }
+
+
+
+
+        // #region 执行日志
+        private static readonly log4net.ILog camerainfologger5 = log4net.LogManager.GetLogger("CameraLogInfo5");
+        /// <summary>
+        /// 相机日志
+        /// </summary>
+        /// <param name="message">日志内容</param>
+        public static void CameraInfo5(string message)
+        {
+            // 可以改成type typeof(类)
+            if (camerainfologger5.IsInfoEnabled)
+            {
+                camerainfologger5.Info(message);
+                logChangHandler?.Invoke(new Log() { Time = DateTime.Now.ToString("yyyy-MM-dd:HH:mm:ss"), Msg = message });
+            }
+        }
+
+
+        public static void SwitchCameraInfo(string cameraName,string message) 
+        {
+
+            switch (cameraName)
+            {
+                case "相机1":
+                    CameraInfo(message);
+                    break;
+                case "相机2":
+                    CameraInfo2(message);
+                    break;
+                case "相机3":
+                    CameraInfo3(message);
+                    break;
+                case "相机4":
+                    CameraInfo4(message);
+                    break;
+                case "相机5":
+                    CameraInfo5(message);
+                    break;
+                default:
+                    break;
+            }
+
+
+        }
+
+
+
+
+
+
         private static log4net.ILog alarmlog4net =log4net.LogManager.GetLogger("AlarmLogInfo");
         /// <summary>
         /// 警告日志

+ 108 - 0
Log4xml/log4net.config

@@ -51,6 +51,98 @@
 				</layout>
 			</appender>
 
+
+			<!-- Agv日志文件-->
+			<appender name="CameraInfoFileAppender2" type="log4net.Appender.RollingFileAppender">
+				<!-- RollingFileAppender以FileAppender为基础 -->
+				<File value="Log\CameraInfoLog2" />
+				<!--<file value="${TMP}\LOGS\" /> tmp为环境变量里的路径值,未配置环境变量地址就生成在temp中,log-file.txt为文件名 -->
+				<param name="AppendToFile" value="true" />
+				<!-- 指定是追加到还是覆盖掉已有的日志文件 -->
+				<!--<lockingModel type="log4net.Appender.FileAppender+InterProcessLock" />-->
+				<param name="lockingModel" type="log4net.Appender.FileAppender+MinimalLock" />
+				<!-- 启用最小锁定格式以允许多个进程可以写入同一个文件-->
+				<param name="RollingStyle" value="Composite" />
+				<!--按照何种方式产生多个日志文件(指明文件名是变动的,日期[Date],文件大小[Size],混合[Composite])-->
+				<StaticLogFileName value="false" />
+				<DatePattern value="yyyy\\MM\\dd\\yyyy-MM-dd'.txt'" />
+				<MaxSizeRollBackups value="-1" />
+				<!-- 最多个数为-1无限个-->
+				<MaximumFileSize value="2MB" />
+				<layout type="log4net.Layout.PatternLayout">
+					<conversionPattern value="时间:%date___描述:%message%newline" />
+
+				</layout>
+			</appender>
+
+			<appender name="CameraInfoFileAppender3" type="log4net.Appender.RollingFileAppender">
+				<!-- RollingFileAppender以FileAppender为基础 -->
+				<File value="Log\CameraInfoLog3" />
+				<!--<file value="${TMP}\LOGS\" /> tmp为环境变量里的路径值,未配置环境变量地址就生成在temp中,log-file.txt为文件名 -->
+				<param name="AppendToFile" value="true" />
+				<!-- 指定是追加到还是覆盖掉已有的日志文件 -->
+				<!--<lockingModel type="log4net.Appender.FileAppender+InterProcessLock" />-->
+				<param name="lockingModel" type="log4net.Appender.FileAppender+MinimalLock" />
+				<!-- 启用最小锁定格式以允许多个进程可以写入同一个文件-->
+				<param name="RollingStyle" value="Composite" />
+				<!--按照何种方式产生多个日志文件(指明文件名是变动的,日期[Date],文件大小[Size],混合[Composite])-->
+				<StaticLogFileName value="false" />
+				<DatePattern value="yyyy\\MM\\dd\\yyyy-MM-dd'.txt'" />
+				<MaxSizeRollBackups value="-1" />
+				<!-- 最多个数为-1无限个-->
+				<MaximumFileSize value="2MB" />
+				<layout type="log4net.Layout.PatternLayout">
+					<conversionPattern value="时间:%date___描述:%message%newline" />
+
+				</layout>
+			</appender>
+
+
+			<appender name="CameraInfoFileAppender4" type="log4net.Appender.RollingFileAppender">
+				<!-- RollingFileAppender以FileAppender为基础 -->
+				<File value="Log\CameraInfoLog4" />
+				<!--<file value="${TMP}\LOGS\" /> tmp为环境变量里的路径值,未配置环境变量地址就生成在temp中,log-file.txt为文件名 -->
+				<param name="AppendToFile" value="true" />
+				<!-- 指定是追加到还是覆盖掉已有的日志文件 -->
+				<!--<lockingModel type="log4net.Appender.FileAppender+InterProcessLock" />-->
+				<param name="lockingModel" type="log4net.Appender.FileAppender+MinimalLock" />
+				<!-- 启用最小锁定格式以允许多个进程可以写入同一个文件-->
+				<param name="RollingStyle" value="Composite" />
+				<!--按照何种方式产生多个日志文件(指明文件名是变动的,日期[Date],文件大小[Size],混合[Composite])-->
+				<StaticLogFileName value="false" />
+				<DatePattern value="yyyy\\MM\\dd\\yyyy-MM-dd'.txt'" />
+				<MaxSizeRollBackups value="-1" />
+				<!-- 最多个数为-1无限个-->
+				<MaximumFileSize value="2MB" />
+				<layout type="log4net.Layout.PatternLayout">
+					<conversionPattern value="时间:%date___描述:%message%newline" />
+
+				</layout>
+			</appender>
+
+
+			<appender name="CameraInfoFileAppender5" type="log4net.Appender.RollingFileAppender">
+				<!-- RollingFileAppender以FileAppender为基础 -->
+				<File value="Log\CameraInfoLog5" />
+				<!--<file value="${TMP}\LOGS\" /> tmp为环境变量里的路径值,未配置环境变量地址就生成在temp中,log-file.txt为文件名 -->
+				<param name="AppendToFile" value="true" />
+				<!-- 指定是追加到还是覆盖掉已有的日志文件 -->
+				<!--<lockingModel type="log4net.Appender.FileAppender+InterProcessLock" />-->
+				<param name="lockingModel" type="log4net.Appender.FileAppender+MinimalLock" />
+				<!-- 启用最小锁定格式以允许多个进程可以写入同一个文件-->
+				<param name="RollingStyle" value="Composite" />
+				<!--按照何种方式产生多个日志文件(指明文件名是变动的,日期[Date],文件大小[Size],混合[Composite])-->
+				<StaticLogFileName value="false" />
+				<DatePattern value="yyyy\\MM\\dd\\yyyy-MM-dd'.txt'" />
+				<MaxSizeRollBackups value="-1" />
+				<!-- 最多个数为-1无限个-->
+				<MaximumFileSize value="2MB" />
+				<layout type="log4net.Layout.PatternLayout">
+					<conversionPattern value="时间:%date___描述:%message%newline" />
+
+				</layout>
+			</appender>
+
 			<!-- 报警日志文件 -->
 			<appender name="AlarmInfoFileAppender" type="log4net.Appender.RollingFileAppender">
 				<!-- RollingFileAppender以FileAppender为基础 -->
@@ -105,6 +197,22 @@
 				<level value="INFO" />
 				<appender-ref ref="CameraInfoFileAppender" />
 			</logger>
+			<logger name="CameraLogInfo2">
+				<level value="INFO" />
+				<appender-ref ref="CameraInfoFileAppender2" />
+			</logger>
+			<logger name="CameraLogInfo3">
+				<level value="INFO" />
+				<appender-ref ref="CameraInfoFileAppender3" />
+			</logger>
+			<logger name="CameraLogInfo4">
+				<level value="INFO" />
+				<appender-ref ref="CameraInfoFileAppender4" />
+			</logger>
+			<logger name="CameraLogInfo5">
+				<level value="INFO" />
+				<appender-ref ref="CameraInfoFileAppender5" />
+			</logger>
 			
 			<logger name="AlarmLogInfo">
 				<level value="WARN" />

+ 1 - 1
Model/CameraInfo.cs

@@ -102,7 +102,7 @@ namespace LampInspectionMachine.Model
             set { SetProperty(ref _CameraType, value); }
         }
 
-        private CameraBrand _CameraBrand;
+        private CameraBrand _CameraBrand= CameraBrand.SciCamera_Opt;
         /// <summary>
         /// 相机品牌
         /// </summary>

+ 18 - 0
Model/ResultsPlcInfo.cs

@@ -0,0 +1,18 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace LampInspectionMachine.Model
+{
+    public class ResultsPlcInfo
+    {
+        public Guid Id { get; set; }
+        public ushort Code { get; set; }    
+
+        public ushort Res { get; set; }
+
+        public string ProcessingTime { get; set; }
+    }
+}

+ 8 - 5
Properties/Resources.resx

@@ -46,7 +46,7 @@
     
     mimetype: application/x-microsoft.net.object.binary.base64
     value   : The object must be serialized with 
-            : System.Serialization.Formatters.Binary.BinaryFormatter
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
             : and then encoded with base64 encoding.
     
     mimetype: application/x-microsoft.net.object.soap.base64
@@ -60,6 +60,7 @@
             : and then encoded with base64 encoding.
     -->
   <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
     <xsd:element name="root" msdata:IsDataSet="true">
       <xsd:complexType>
         <xsd:choice maxOccurs="unbounded">
@@ -68,9 +69,10 @@
               <xsd:sequence>
                 <xsd:element name="value" type="xsd:string" minOccurs="0" />
               </xsd:sequence>
-              <xsd:attribute name="name" type="xsd:string" />
+              <xsd:attribute name="name" use="required" type="xsd:string" />
               <xsd:attribute name="type" type="xsd:string" />
               <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
             </xsd:complexType>
           </xsd:element>
           <xsd:element name="assembly">
@@ -85,9 +87,10 @@
                 <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
                 <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
               </xsd:sequence>
-              <xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
               <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
               <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
             </xsd:complexType>
           </xsd:element>
           <xsd:element name="resheader">
@@ -109,9 +112,9 @@
     <value>2.0</value>
   </resheader>
   <resheader name="reader">
-    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
   </resheader>
   <resheader name="writer">
-    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
   </resheader>
 </root>

+ 81 - 18
ViewModels/CameraViewModel.cs

@@ -24,6 +24,10 @@ using System.Globalization;
 using LampInspectionMachine.Log4xml;
 using System.Collections.ObjectModel;
 using SqlSugar;
+using Cognex.VisionPro.ImageFile;
+using System.IO;
+using System.Windows.Media.Media3D;
+using OpenCvSharp.Flann;
 
 namespace LampInspectionMachine.ViewModels
 {
@@ -105,25 +109,21 @@ namespace LampInspectionMachine.ViewModels
             _CameraCloseCommand ?? ( _CameraCloseCommand = new DelegateCommand(OnCameraClose) );
 
 
+        private DelegateCommand _CameraExpouseTimeSetCommand;
+        /// <summary>
+        /// 相机曝光写入
+        /// </summary>
+        public DelegateCommand CameraExpouseTimeSetCommand =>
+            _CameraExpouseTimeSetCommand ?? (_CameraExpouseTimeSetCommand = new DelegateCommand(OnCameraSetExposureTime));
+
+
 
         private void ShowLog(Log4xml.Log log)
         {
-            System.Windows.Application.Current.Dispatcher.Invoke(new Action(() =>
+            System.Windows.Application.Current.Dispatcher.BeginInvoke(new Action(() =>
             {
                 string str = log.Msg.Trim();
-                while (true)
-                {
-                    if (str.Length > 60)
-                    {
-                        Logs.Add(str.Substring(0, 60));
-                        str = log.Time + " " + str.Substring(60);
-                    }
-                    else
-                    {
-                        Logs.Add(str);
-                        break;
-                    }
-                }
+                 Logs.Add(str);
                 if (Logs.Count > 100)
                 {
                     Logs.RemoveAt(0);
@@ -137,7 +137,11 @@ namespace LampInspectionMachine.ViewModels
         {
             try
             {
-                Management.SoftTrigger();
+                Task.Run(() =>
+                {
+                    Management.SoftTrigger();
+                }
+                );
             }
             catch
             {
@@ -214,12 +218,20 @@ namespace LampInspectionMachine.ViewModels
                         {
                             if (Management.CurrCamera!=null&&Management.CurrCamera.IsGrabbing) 
                             {
-                                Management.CurrCamera.StopGrabbing();
+                              //  Management.CurrCamera.StopGrabbing();
                             }
                             Management.CurrCamera = Management.VisionProManagers[ Index ].Camera;
                             Management.CurrCamConfig = Management.VisionProManagers[ Index ].CameraInfo.Copy();
                             Management.CurrCameraSn = Management.CurrCamConfig.SerialNumber;
                             Management.Currvision = Management.VisionProManagers[ Index ];
+                            if (Management.CurrCamera != null)
+                            {
+                                Management.CurrCamConfig.ExpouseTime = (ulong)(Management.CurrCamera.GetExposureTime());
+                            }
+                            else 
+                            {
+                                OnCameraInit();
+                            }
                         }
                     }
                 }
@@ -246,7 +258,20 @@ namespace LampInspectionMachine.ViewModels
                 MessageBox.Show("相机出错");
             }
         }
+        private void OnCameraSetExposureTime()
+        {
 
+            try
+            {
+                Management.CurrCamera.SetExposureTime(Management.CurrCamConfig.ExpouseTime);
+                Management.CamConfigs[Index].ExpouseTime = Management.CurrCamConfig.ExpouseTime;
+                Management.SaveCameraDevice();
+            }
+            catch
+            {
+                MessageBox.Show("相机写入曝光出错");
+            }
+        }
         public void ConfirmNavigationRequest(NavigationContext navigationContext, Action<bool> continuationCallback)
         {
             if ( Management.CurrCamConfig != null )
@@ -312,11 +337,19 @@ namespace LampInspectionMachine.ViewModels
                                 display.StaticGraphics.AddList(newRender.Graphic, "");
                             if (newRender.Record != null)
                                 display.Record = newRender.Record;
-                            if (newRender.IsSaveImage)
+                            if (!newRender.IsSaveImage)
                             {
-                              //  SaveImage(newRender.SaveImageModel, newRender.SaveImagePathModel, render.SavePath, render.Image, (Bitmap)((Cognex.VisionPro.CogRecordDisplay)item.Value.Child).CreateContentBitmap(Cognex.VisionPro.Display.CogDisplayContentBitmapConstants.Custom), render.Result, render.IsCompress);
+                                if (newRender.Result)
+                                {
+                                    SaveImage($"D:\\{newRender.CameraName}检测图片\\OK\\" + DateTime.Now.ToString("yyyy_MM_dd_HH_mm_ss_FFF") + ".bmp", newRender.Image);
+                                }
+                                else 
+                                {
+                                    SaveImage($"D:\\{newRender.CameraName}检测图片\\NG\\" + DateTime.Now.ToString("yyyy_MM_dd_HH_mm_ss_FFF") + ".bmp", newRender.Image);
+                                }
                             }
                             display.Fit(true);
+                           
                         }
                         catch
                         {
@@ -330,6 +363,36 @@ namespace LampInspectionMachine.ViewModels
                 }
             }
         }
+
+
+        /// <summary>
+        /// 保存图像
+        /// </summary>
+        private static void SaveImage(string path, ICogImage image)
+        {
+            Task.Run(() =>
+            {
+                try
+                {
+                    DateTime now = DateTime.Now;
+                    if (!Directory.Exists(Path.GetDirectoryName(path)))   //判断文件夹是否存在
+                    {
+                        Directory.CreateDirectory(Path.GetDirectoryName(path));    //创建文件夹
+                    }
+                    using (CogImageFileBMP _cogImageFileTool = new CogImageFileBMP())
+                    {
+                        _cogImageFileTool.Open(path, CogImageFileModeConstants.Write);
+                        _cogImageFileTool.Append(image);
+                        _cogImageFileTool.Close();
+                    }
+                }
+                catch (Exception ex)
+                {
+                    LogHelper.Error("保存流程运行结果的图片时出错!" + ex);
+                }
+
+            });
+        }
     }
 
 }

+ 5 - 3
ViewModels/MainWindowViewModel.cs

@@ -80,9 +80,11 @@ namespace LampInspectionMachine.ViewModels
 
         void MenuViewShow()
         {
-            LogHelper.Info("切换相机调试");
-            _regionManager.Regions[ "RegionContent" ].RequestNavigate("MenuView");
-
+            if (management.VisionProManagers.Count == management.CamConfigs.Count)
+            {
+                LogHelper.Info("切换相机调试");
+                _regionManager.Regions["RegionContent"].RequestNavigate("MenuView");
+            }
         }
 
         void RoleLoginViewShow()

+ 31 - 2
ViewModels/VisionProViewModel.cs

@@ -15,6 +15,7 @@ using System.Windows;
 using Cognex.VisionPro.ToolGroup;
 using OpenCvSharp.Flann;
 using Prism.Commands;
+using System.IO;
 
 namespace LampInspectionMachine.ViewModels
 {
@@ -29,6 +30,16 @@ namespace LampInspectionMachine.ViewModels
         /// </summary>
         public DelegateCommand SoftTriggerCommand =>
             _SoftTriggerCommand ?? ( _SoftTriggerCommand = new DelegateCommand(RunCurrVisionCogToolBlock) );
+
+        private DelegateCommand _SaveVPPCommand;
+        /// <summary>
+        /// 开始采集
+        /// </summary>
+        public DelegateCommand SaveVPPCommand =>
+            _SaveVPPCommand ?? (_SaveVPPCommand = new DelegateCommand(SaveCurrVisionCogToolBlock));
+
+        
+
         public VisionProViewModel(IContainerProvider container, IRegionManager regionManager, IEventAggregator eventAggregato) 
         {
             management= container.Resolve<Management>();
@@ -63,8 +74,26 @@ namespace LampInspectionMachine.ViewModels
         }
         public void RunCurrVisionCogToolBlock() 
         {
-            management.SoftTrigger();
-            management.Currvision.CogToolBlock.Run();
+            management.SoftTrigger(); 
+        }
+
+        public void SaveCurrVisionCogToolBlock()
+        {
+            try
+            {
+                FileInfo fileInfo = new FileInfo(Environment.CurrentDirectory + $"\\Vpp\\{management.CurrCamConfig.VppFileName}");
+                fileInfo.CopyTo(Environment.CurrentDirectory + $"\\Vpp备份\\{DateTime.Now.ToString("yyyy_MM_dd_HH_mm_ss")+ management.CurrCamConfig.VppFileName}", false);
+
+
+                CogSerializer.SaveObjectToFile(management.Currvision.CogToolBlock, Environment.CurrentDirectory + $"\\Vpp\\{management.CurrCamConfig.VppFileName}");
+                MessageBox.Show("保存完成");
+            }
+            catch (Exception ex)
+            {
+                MessageBox.Show("保存失败"+ex.Message);
+            }
         }
+
+       
     }
 }

+ 56 - 36
ViewModels/WorkRunViewModel.cs

@@ -25,6 +25,7 @@ using System.Windows.Controls;
 using LampInspectionMachine.Core;
 using Prism.Commands;
 using MvCamCtrl.NET;
+using System.Windows;
 
 namespace LampInspectionMachine.ViewModels
 {
@@ -42,8 +43,13 @@ namespace LampInspectionMachine.ViewModels
         private ObservableCollection<string> _Logs = new ObservableCollection<string>();
         public ObservableCollection<string> Logs { get => _Logs; set => _Logs = value; }
 
-        
-      
+
+
+        private DelegateCommand _ClearViewCommand;
+        public DelegateCommand ClearViewCommand =>
+            _ClearViewCommand ?? (_ClearViewCommand = new DelegateCommand(ClearOKAndNGMethod));
+
+
 
         public DelegateCommand LoadedCommand { get; set; }
         public Management Management { get => management; set { SetProperty(ref management, value); } }
@@ -68,6 +74,7 @@ namespace LampInspectionMachine.ViewModels
                 LogHelper.Info("程序启动");
                 management.LoadCameraDevice();
                 management.ConnectPlc();
+                
                 TipService tipService = new TipService();
                 await Task.Run(() => { Management.InitTemplates(tipService, Management); });
             }
@@ -78,31 +85,29 @@ namespace LampInspectionMachine.ViewModels
         {
             System.Windows.Application.Current.Dispatcher.Invoke(new Action(() =>
             {
-                string str = log.Msg.Trim();
-                while ( true )
-                {
-                    if ( str.Length > 60 )
-                    {
-                        Logs.Add(str.Substring(0, 60));
-                        str = log.Time + " " + str.Substring(60);
-                    }
-                    else
-                    {
-                        Logs.Add(str);
-                        break;
-                    }
-                }
-                if ( Logs.Count > 100 )
+                if (Logs.Count > 100)
                 {
-                    Logs.RemoveAt(0);
+                    Logs.Clear();
                 }
+                string str = log.Msg.Trim();
+                Logs.Add(str);    
+                
             }));
 
         }
+        private void ClearOKAndNGMethod() 
+        {
 
+            for (int i = 0; i < management.VisionProManagers.Count; i++)
+            {
+                management.VisionProManagers[i].IsOkCount = 0;
+                management.VisionProManagers[i].IsNgCount = 0;
+            }
+        
+        }
         public bool IsNavigationTarget(NavigationContext navigationContext)
         {
-            return true;
+            return IsLoad;
         }
 
         public void OnNavigatedFrom(NavigationContext navigationContext)
@@ -112,24 +117,34 @@ namespace LampInspectionMachine.ViewModels
 
         public void OnNavigatedTo(NavigationContext navigationContext)
         {
-            //继续运行
-            for ( int i = 0; i < Management.VisionProManagers.Count; i++ )
+            try
             {
-                if ( Management.VisionProManagers[ i ].Camera != null&& Management.VisionProManagers[ i ].Camera.IsConnected)
+                //继续运行
+                for (int i = 0; i < Management.VisionProManagers.Count; i++)
                 {
-                    if ( !Management.VisionProManagers[ i ].Camera.IsGrabbing )
+                    if (Management.VisionProManagers[i].Camera != null && Management.VisionProManagers[i].Camera.IsConnected)
                     {
-                        Management.VisionProManagers[ i ].Camera.SetTriggerMode(true, 0);
-                        Management.VisionProManagers[ i ].CameraInfo.TriggerMode = true;
-                        management.VisionProManagers[ i ].Camera.StartTriggerGrabbing();
-                    }
-                    if ( Management.VisionProManagers[ i ].Camera.CheckImageCallbackEventIsHas(Management.VisionProManagers[ i ].CamCallBack) )
-                    {
-                        Management.VisionProManagers[ i ].Camera.ImageCallbackEvent -= Management.VisionProManagers[ i ].CamCallBack;
+                        //Management.VisionProManagers[i].Camera.StopGrabbing();
+                        //if (!Management.VisionProManagers[i].Camera.IsGrabbing)
+                        //{
+                            Management.VisionProManagers[i].Camera.SetTriggerMode(true, 0);
+                            Management.VisionProManagers[i].CameraInfo.TriggerMode = true;
+                            if (!Management.VisionProManagers[i].Camera.CheckGrabImageCallbackEventIsHas(Management.VisionProManagers[i].ImageCallback))
+                                Management.VisionProManagers[i].Camera.GrabImageCallbackEvent += Management.VisionProManagers[i].ImageCallback;
+                        //    management.VisionProManagers[i].Camera.StartTriggerGrabbing();
+                        //}
+                        if (Management.VisionProManagers[i].Camera.CheckImageCallbackEventIsHas(Management.VisionProManagers[i].CamCallBack))
+                        {
+                            Management.VisionProManagers[i].Camera.ImageCallbackEvent -= Management.VisionProManagers[i].CamCallBack;
+                        }
                     }
                 }
             }
-
+            catch (Exception ex)
+            {
+                LogHelper.Error("切花运行界面出错" + ex.Message);
+                MessageBox.Show("切花运行界面出错"+ex.Message);
+            }
         }
 
 
@@ -154,16 +169,21 @@ namespace LampInspectionMachine.ViewModels
                 incc.CollectionChanged += OnCollectionChanged;
             }
         }
-
+        bool isend = true;
         private void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
         {
             if ( e.Action == NotifyCollectionChangedAction.Add && AssociatedObject.HasItems )
             {
-                AssociatedObject.Dispatcher.BeginInvoke(new Action(() =>
+                if (isend)
                 {
-
-                    AssociatedObject.ScrollIntoView(AssociatedObject.Items[ AssociatedObject.Items.Count - 1 ]);
-                }));
+                    isend = false;
+                    AssociatedObject.Dispatcher.BeginInvoke(new Action(() =>
+                    {
+                        AssociatedObject.UpdateLayout();
+                        AssociatedObject.ScrollIntoView(AssociatedObject.Items[AssociatedObject.Items.Count - 1]);
+                        isend = true;
+                    }));
+                }
 
             }
         }

+ 15 - 1
Views/CameraView.xaml

@@ -347,7 +347,21 @@
                                 VerticalAlignment="Center">
                             <GroupBox Header="曝光">
                                 <StackPanel Orientation="Horizontal">
-                                    <TextBox Width="190"/>
+                                    <TextBox Width="190" Text="{Binding  Management.CurrCamConfig.ExpouseTime}"/>
+                                    <Border Background="#0A85D9"
+                                                BorderBrush="Black"
+                                                BorderThickness="1"
+                                                Margin="10,5,3,5"
+                                                CornerRadius="8">
+                                        <Button Content="曝光写入"
+                                                Background="Transparent"
+                                                Foreground="White"
+                                                Margin="2" 
+                                                MinWidth="50"
+                                                BorderThickness="0"
+                                                Command="{Binding CameraExpouseTimeSetCommand}" />
+
+                                    </Border>
                                 </StackPanel>
                             </GroupBox>
                         </StackPanel>

+ 14 - 1
Views/CameraView.xaml.cs

@@ -5,6 +5,7 @@ using System;
 using System.Collections.Generic;
 using System.Linq;
 using System.Text;
+using System.Threading;
 using System.Threading.Tasks;
 using System.Windows;
 using System.Windows.Controls;
@@ -28,9 +29,21 @@ namespace LampInspectionMachine.Views
         public CameraView(IContainerProvider container)
         {
             InitializeComponent();
-            management=container.Resolve<Management>(); 
+            management=container.Resolve<Management>();
+            Loaded += CameraView_Loaded;
            
         }
+
+        private void CameraView_Loaded(object sender, RoutedEventArgs e)
+        {
+
+            System.Windows.Application.Current.Dispatcher.BeginInvoke(new Action(() =>
+            {
+                Thread.Sleep(500);
+                this.InvalidateVisual();
+                this.UpdateLayout();
+            }));
+        }
     }
 
   

+ 7 - 0
Views/MainWindow.xaml

@@ -13,6 +13,7 @@
         Height="450"
         FontWeight="Bold"
         FontFamily="微软雅黑"
+        WindowState="Maximized"
         MouseDoubleClick="Grid_MouseDown"
         SizeChanged="Window_SizeChanged"
         Width="800">
@@ -72,6 +73,7 @@
             <Grid.RowDefinitions>
                 <RowDefinition Height="70" />
                 <RowDefinition />
+                <RowDefinition Height="Auto"/>
             </Grid.RowDefinitions>
             <Grid Grid.Row="0"
                   VerticalAlignment="Center">
@@ -114,6 +116,7 @@
                                 BorderBrush="Transparent"
                                 FontSize="15"
                                 Command="{Binding OpenMenuViewCommand}"
+                                
                                 WindowChrome.IsHitTestVisibleInChrome="True"
                                 Background="Transparent" />
 
@@ -126,6 +129,7 @@
                                 BorderBrush="Transparent"
                                 FontSize="15"
                                 Command="{Binding OpenWorkRunViewCommand}"
+                                
                                 WindowChrome.IsHitTestVisibleInChrome="True"
                                 Background="Transparent" />
 
@@ -168,6 +172,9 @@
             <Grid Grid.Row="1">
                 <ContentControl prism:RegionManager.RegionName="RegionContent" />
             </Grid>
+            <StackPanel>
+                <TextBlock />
+            </StackPanel>
         </Grid>
     </Border>
 </Window>

+ 33 - 9
Views/MainWindow.xaml.cs

@@ -1,5 +1,9 @@
-using System.Windows;
+using System;
+using System.Threading;
+using System.Windows;
 using System.Windows.Input;
+using System.Windows.Threading;
+using LampInspectionMachine.Log4xml;
 
 namespace LampInspectionMachine.Views
 {
@@ -36,10 +40,11 @@ namespace LampInspectionMachine.Views
             else
             {
                 _windowState = WindowState.Normal;
-                this.Left = rcnormal.Left;
-                this.Top = rcnormal.Top;
-                this.Width = rcnormal.Width;
-                this.Height = rcnormal.Height;
+                Rect rc = SystemParameters.WorkArea;//获取工作区大小
+                this.Width = rc.Width/2;
+                this.Height = rc.Height/2;
+                this.Left = rc.Left / 2;
+                this.Top = rc.Top / 2;
             }
         }
         /// <summary>
@@ -50,13 +55,23 @@ namespace LampInspectionMachine.Views
             this.WindowState = WindowState.Minimized;
 
         }
+         
         private void Window_SizeChanged(object sender, SizeChangedEventArgs e)
         {
-            if ( this.ActualHeight > SystemParameters.WorkArea.Height || this.ActualWidth > SystemParameters.WorkArea.Width )
+            //if (this.ActualHeight > SystemParameters.WorkArea.Height || this.ActualWidth > SystemParameters.WorkArea.Width)
+            //{
+            //    this.WindowState = System.Windows.WindowState.Normal;
+            //    btnMaximize_Click(null, null);
+            //}
+
+
+            System.Windows.Application.Current.Dispatcher.BeginInvoke(new Action(() =>
             {
-                this.WindowState = System.Windows.WindowState.Normal;
-                btnMaximize_Click(null, null);
-            }
+                Thread.Sleep(500);
+                this.InvalidateVisual();
+                this.UpdateLayout();
+            }));
+         
         }
         private void Grid_MouseDown(object sender, MouseButtonEventArgs e)
         {
@@ -72,5 +87,14 @@ namespace LampInspectionMachine.Views
                 }
             }
         }
+
+        private void Button_Click(object sender, RoutedEventArgs e)
+        {
+           
+        }
+
+        private void Button_Click_1(object sender, RoutedEventArgs e)
+        {
+        }
     }
 }

+ 24 - 4
Views/VisionProView.xaml

@@ -11,13 +11,17 @@
              mc:Ignorable="d" 
              d:DesignHeight="500" d:DesignWidth="800">
     <Grid>
-        <StackPanel>
-            <DockPanel>
+        <Grid.RowDefinitions>
+            <RowDefinition />
+            <RowDefinition Height="Auto"/>
+        </Grid.RowDefinitions>
+        <DockPanel Grid.Row="0">
                 <WindowsFormsHost>
                     <cognexWF1:CogToolGroupEditV2 x:Name="cogToolBlock" />
                 </WindowsFormsHost>
             </DockPanel>
-            <Border Background="#0A85D9"
+        <StackPanel Grid.Row="1" Orientation="Horizontal">
+        <Border Background="#0A85D9"
                     BorderBrush="Black"
                     BorderThickness="1"
                     Width="100"
@@ -25,13 +29,29 @@
                     HorizontalAlignment="Left"
                     Margin="10,5,10,10"
                     CornerRadius="8">
-                <Button Content="运行流程"
+            <Button Content="运行流程"
                         Background="Transparent"
                         Foreground="White"
                         Margin="2"
                         MinWidth="50"
                         BorderThickness="0"
                         Command="{Binding  SoftTriggerCommand}" />
+        </Border>
+            <Border Background="#0A85D9"
+             BorderBrush="Black"
+             BorderThickness="1"
+             Width="100"
+             Height="40"
+             HorizontalAlignment="Left"
+             Margin="10,5,10,10"
+             CornerRadius="8">
+                <Button Content="保存"
+                 Background="Transparent"
+                 Foreground="White"
+                 Margin="2"
+                 MinWidth="50"
+                 BorderThickness="0"
+                 Command="{Binding  SaveVPPCommand}" />
             </Border>
         </StackPanel>
     </Grid>

+ 63 - 11
Views/WorkRunView.xaml

@@ -46,15 +46,15 @@
 
         </DockPanel>
 
-        <DockPanel  Grid.Column="2"
-                    Grid.Row="0">
+        <DockPanel  Grid.Column="0"
+                    Grid.Row="1">
             <WindowsFormsHost Viewlocal:CogDisplayBinder.ImageSource="{Binding Management.VisionProManagers[2].Render}">
                 <cognexWF1:CogRecordDisplay />
             </WindowsFormsHost>
 
         </DockPanel>
 
-        <DockPanel  Grid.Column="0"
+        <DockPanel  Grid.Column="1"
                     Grid.Row="1">
             <WindowsFormsHost Viewlocal:CogDisplayBinder.ImageSource="{Binding Management.VisionProManagers[3].Render}">
                 <cognexWF1:CogRecordDisplay />
@@ -62,38 +62,89 @@
 
         </DockPanel>
 
-        <DockPanel  Grid.Column="1"
+        <!--<DockPanel  Grid.Column="1"
                     Grid.Row="1">
             <WindowsFormsHost Viewlocal:CogDisplayBinder.ImageSource="{Binding Management.VisionProManagers[4].Render}">
                 <cognexWF1:CogRecordDisplay />
             </WindowsFormsHost>
 
-        </DockPanel>
+        </DockPanel>-->
 
         <DockPanel  Grid.Column="2"
                     Grid.Row="1">
-            <WindowsFormsHost Viewlocal:CogDisplayBinder.ImageSource="{Binding Management.VisionProManagers[5].Render}">
+            <WindowsFormsHost Viewlocal:CogDisplayBinder.ImageSource="{Binding Management.VisionProManagers[4].Render}">
                 <cognexWF1:CogRecordDisplay />
             </WindowsFormsHost>
 
         </DockPanel>
-        <DockPanel  Grid.Column="0"
+        <!--<DockPanel  Grid.Column="0"
                     Grid.Row="2">
             <WindowsFormsHost Viewlocal:CogDisplayBinder.ImageSource="{Binding Management.VisionProManagers[6].Render}">
                 <cognexWF1:CogRecordDisplay />
             </WindowsFormsHost>
 
-        </DockPanel>
-        <DockPanel  Grid.Column="1"
+        </DockPanel>-->
+        <!--<DockPanel  Grid.Column="1"
                     Grid.Row="2">
             <WindowsFormsHost Viewlocal:CogDisplayBinder.ImageSource="{Binding Management.VisionProManagers[7].Render}">
                 <cognexWF1:CogRecordDisplay />
             </WindowsFormsHost>
 
-        </DockPanel>
-        <DockPanel  Grid.Column="3"
+        </DockPanel>-->
+        <StackPanel Grid.Column="3"
                     Grid.RowSpan="3"
                     Grid.Row="0">
+            <StackPanel Orientation="Horizontal" Margin="20,0,0,0">
+                <TextBlock Text="相机1_OK:" Margin="5" Foreground="Green" FontSize="18"/>
+                <TextBlock Text="{Binding Management.VisionProManagers[0].IsOkCount}" Margin="5" Foreground="Green" FontSize="18" MinWidth="80" MaxWidth="100"/>
+                <TextBlock Text="相机1_NG:" Margin="5" Foreground="Red" FontSize="18"/>
+                <TextBlock Text="{Binding Management.VisionProManagers[0].IsNgCount}" Margin="5" Foreground="Red" FontSize="18"/>
+            </StackPanel>
+
+            <StackPanel Orientation="Horizontal" Margin="20,0,0,0">
+                <TextBlock Text="相机2_OK:" Margin="5" Foreground="Green" FontSize="18"/>
+                <TextBlock Text="{Binding Management.VisionProManagers[1].IsOkCount}" Margin="5" Foreground="Green" FontSize="18" MinWidth="80" MaxWidth="100"/>
+                <TextBlock Text="相机2_NG:" Margin="5" Foreground="Red" FontSize="18"/>
+                <TextBlock Text="{Binding Management.VisionProManagers[1].IsNgCount}" Margin="5" Foreground="Red" FontSize="18"/>
+            </StackPanel>
+
+            <StackPanel Orientation="Horizontal" Margin="20,0,0,0">
+                <TextBlock Text="相机3_OK:" Margin="5" Foreground="Green" FontSize="18"/>
+                <TextBlock Text="{Binding Management.VisionProManagers[2].IsOkCount}" Margin="5" Foreground="Green" FontSize="18" MinWidth="80" MaxWidth="100"/>
+                <TextBlock Text="相机3_NG:" Margin="5" Foreground="Red" FontSize="18"/>
+                <TextBlock Text="{Binding Management.VisionProManagers[2].IsNgCount}" Margin="5" Foreground="Red" FontSize="18"/>
+            </StackPanel>
+
+            <StackPanel Orientation="Horizontal" Margin="20,0,0,0">
+                <TextBlock Text="相机4_OK:" Margin="5" Foreground="Green" FontSize="18"/>
+                <TextBlock Text="{Binding Management.VisionProManagers[3].IsOkCount}" Margin="5" Foreground="Green" FontSize="18" MinWidth="80" MaxWidth="100"/>
+                <TextBlock Text="相机4_NG:" Margin="5" Foreground="Red" FontSize="18"/>
+                <TextBlock Text="{Binding Management.VisionProManagers[3].IsNgCount}" Margin="5" Foreground="Red" FontSize="18"/>
+            </StackPanel>
+
+            <StackPanel Orientation="Horizontal" Margin="20,0,0,0">
+                <TextBlock Text="相机5_OK:" Margin="5" Foreground="Green" FontSize="18"/>
+                <TextBlock Text="{Binding Management.VisionProManagers[4].IsOkCount}" Margin="5" Foreground="Green" FontSize="18" MinWidth="80" MaxWidth="100"/>
+                <TextBlock Text="相机5_NG:" Margin="5" Foreground="Red" FontSize="18"/>
+                <TextBlock Text="{Binding Management.VisionProManagers[4].IsNgCount}" Margin="5" Foreground="Red" FontSize="18"/>
+            </StackPanel>
+            <Border Background="#0A85D9"
+                    BorderBrush="Black"
+                    BorderThickness="1"
+                    Width="80"
+                    Height="35"
+                    HorizontalAlignment="Right"
+                    Margin="0,0,0,0"
+                    CornerRadius="8">
+                <Button Content="清除计数"
+                  Background="Transparent"
+                  Foreground="White"
+                  Margin="2"
+                  MinWidth="50"
+                  BorderThickness="0"
+                  Command="{Binding  ClearViewCommand}" />
+            </Border>
+            <DockPanel  >
             <ListBox ItemsSource="{Binding Logs}"
                      Grid.Row="0"
                      BorderBrush="Transparent">
@@ -102,5 +153,6 @@
                 </i:Interaction.Behaviors>
             </ListBox>
         </DockPanel>
+        </StackPanel>
     </Grid>
 </UserControl>

+ 15 - 1
Views/WorkRunView.xaml.cs

@@ -1,4 +1,6 @@
-using System.Windows.Controls;
+using System;
+using System.Threading;
+using System.Windows.Controls;
 
 namespace LampInspectionMachine.Views
 {
@@ -10,6 +12,18 @@ namespace LampInspectionMachine.Views
         public WorkRunView()
         {
             InitializeComponent();
+            Loaded += WorkRunView_Loaded;
+        }
+
+        private void WorkRunView_Loaded(object sender, System.Windows.RoutedEventArgs e)
+        {
+
+            System.Windows.Application.Current.Dispatcher.BeginInvoke(new Action(() =>
+            {
+                Thread.Sleep(500);
+                this.InvalidateVisual();
+                this.UpdateLayout();
+            }));
         }
     }
 }