瀏覽代碼

20251215001 优化因频繁创建线程导致可能会出现的突然的识别耗时增加的问题,更改使用高性能的锁,增加了一些异常报警,为凸包的两点间的长度检测方法增加一个带XY系数的构造方法。

向羽 孟 4 月之前
父節點
當前提交
dc9730cbd6

+ 4 - 0
CCDCountWpf/CCDCountWpf.csproj

@@ -59,6 +59,9 @@
     <ErrorReport>prompt</ErrorReport>
     <Prefer32Bit>true</Prefer32Bit>
   </PropertyGroup>
+  <PropertyGroup>
+    <ApplicationManifest>app.manifest</ApplicationManifest>
+  </PropertyGroup>
   <ItemGroup>
     <Reference Include="MvCameraControl.Net, Version=4.3.2.2, Culture=neutral, PublicKeyToken=a3c7c5e3a730cd12, processorArchitecture=MSIL">
       <SpecificVersion>False</SpecificVersion>
@@ -208,6 +211,7 @@
       <Generator>ResXFileCodeGenerator</Generator>
       <LastGenOutput>Resources.Designer.cs</LastGenOutput>
     </EmbeddedResource>
+    <None Include="app.manifest" />
     <None Include="Properties\Settings.settings">
       <Generator>SettingsSingleFileGenerator</Generator>
       <LastGenOutput>Settings.Designer.cs</LastGenOutput>

+ 37 - 0
CCDCountWpf/ShowBindingClass.cs

@@ -875,6 +875,43 @@ namespace CCDCountWpf
             }
         }
 
+        /// <summary>
+        /// 合格粒最大面积
+        /// </summary>
+        public string XCoefficient
+        {
+            get
+            {
+                if (MessageBus.NowSettingLoadMainThread != null)
+                {
+                    return MessageBus.NowSettingLoadMainThread.XCoefficient.ToString();
+                }
+                else
+                {
+                    return "";
+                }
+            }
+            set
+            {
+                if (MessageBus.NowSettingLoadMainThread != null)
+                {
+                    try
+                    {
+                        double oldValue = MessageBus.NowSettingLoadMainThread.XCoefficient;
+                        MessageBus.NowSettingLoadMainThread.XCoefficient = double.Parse(value);
+                        if (oldValue != double.Parse(value))
+                            FaultLog.RecordValueChangeMessage($"数粒识别{MessageBus.NowSettingLoadMainThread.XCoefficient}的X轴系数从{oldValue}修改为{value}");
+                        OnPropertyChanged("XCoefficient");
+                    }
+                    catch (Exception)
+                    {
+                        Console.WriteLine("XCoefficient-SetError");
+                    }
+
+                }
+            }
+        }
+
         /// <summary>
         /// 异常粒识别码
         /// </summary>

+ 8 - 8
CCDCountWpf/WpfFrom/MainWindow.xaml.cs

@@ -205,8 +205,8 @@ namespace CCDCountWpf
 
                 MessageBus.MainThreadS[i].FormulationName = Configs[i].ConfigName;
                 MessageBus.MainThreadS[i].IsLoadFormulation = Configs[i].IsFormulation;
-                MessageBus.MainThreadS[i].ConnectModbus("127.0.0.1");
-                //MessageBus.MainThreadS[i].ConnectModbus("192.168.1.88");
+                //MessageBus.MainThreadS[i].ConnectModbus("127.0.0.1");
+                MessageBus.MainThreadS[i].ConnectModbus("192.168.1.88");
                 MessageBus.MainThreadS[i].LoadCamera();
             }
             MessageBus.SeetingNowLoadInMaThreadsIndex = 0;
@@ -244,7 +244,7 @@ namespace CCDCountWpf
                         }
                         if(MessageBus.MainThreadS.Count>=1)
                         {
-                            MessageBus.MainThreadS[0].GetShowImage(2000, out Bitmap image1);
+                            MessageBus.MainThreadS[0].GetShowImage(1000, out Bitmap image1);
                             if (image1 == null)
                             {
                                 LOG.log(string.Format("{0}:相机一获取图片为空", "IdentifyCameraForm-StartShowImageThread"));
@@ -433,8 +433,8 @@ namespace CCDCountWpf
         /// </summary>
         public void InitPlcManger()
         {
-            PlcSettingMessageBus.pLCManagement = new PLCManagementClass("127.0.0.1");
-            //PlcSettingMessageBus.pLCManagement = new PLCManagementClass("192.168.1.88");
+            //PlcSettingMessageBus.pLCManagement = new PLCManagementClass("127.0.0.1");
+            PlcSettingMessageBus.pLCManagement = new PLCManagementClass("192.168.1.88");
             if (PlcSettingMessageBus.pLCManagement.IsConnect)
             {
                 IsPLCShow = true;
@@ -448,9 +448,9 @@ namespace CCDCountWpf
                         {
                             UpdatePlcState();
                         });
-                        await Application.Current.Dispatcher.InvokeAsync(() =>
-                        {
-                        });
+                        //await Application.Current.Dispatcher.InvokeAsync(() =>
+                        //{
+                        //});
                         //进行精准20帧控制
                         int elapsed = (int)renderSW.ElapsedMilliseconds;
                         //Console.WriteLine(elapsed);

+ 1 - 1
CCDCountWpf/WpfPage/MainPage.xaml.cs

@@ -197,7 +197,7 @@ namespace CCDCountWpf.WpfPage
 
         private void StartIdentifyBtn_Click(object sender, RoutedEventArgs e)
         {
-            if(ShowMessageBus.ShowBinding.BatchNumber == "")
+            if(String.IsNullOrWhiteSpace(ShowMessageBus.ShowBinding.BatchNumber))
             {
                 MessageBox.Show("请输入批次号!");
                 return;

+ 79 - 0
CCDCountWpf/app.manifest

@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="utf-8"?>
+<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
+  <assemblyIdentity version="1.0.0.0" name="MyApplication.app"/>
+  <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
+    <security>
+      <requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
+        <!-- UAC 清单选项
+             如果想要更改 Windows 用户帐户控制级别,请使用
+             以下节点之一替换 requestedExecutionLevel 节点。
+
+        <requestedExecutionLevel  level="asInvoker" uiAccess="false" />
+        <requestedExecutionLevel  level="requireAdministrator" uiAccess="false" />
+        <requestedExecutionLevel  level="highestAvailable" uiAccess="false" />
+
+            指定 requestedExecutionLevel 元素将禁用文件和注册表虚拟化。
+            如果你的应用程序需要此虚拟化来实现向后兼容性,则移除此
+            元素。
+        -->
+        <requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
+      </requestedPrivileges>
+    </security>
+  </trustInfo>
+
+  <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
+    <application>
+      <!-- 设计此应用程序与其一起工作且已针对此应用程序进行测试的
+           Windows 版本的列表。取消评论适当的元素,
+           Windows 将自动选择最兼容的环境。 -->
+
+      <!-- Windows Vista -->
+      <!--<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}" />-->
+
+      <!-- Windows 7 -->
+      <!--<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />-->
+
+      <!-- Windows 8 -->
+      <!--<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />-->
+
+      <!-- Windows 8.1 -->
+      <!--<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" />-->
+
+      <!-- Windows 10 -->
+      <!--<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />-->
+
+    </application>
+  </compatibility>
+
+  <!-- 指示该应用程序可感知 DPI 且 Windows 在 DPI 较高时将不会对其进行
+       自动缩放。Windows Presentation Foundation (WPF)应用程序自动感知 DPI,无需
+       选择加入。选择加入此设置的 Windows 窗体应用程序(面向 .NET Framework 4.6)还应
+       在其 app.config 中将 "EnableWindowsFormsHighDpiAutoResizing" 设置设置为 "true"。
+       
+       将应用程序设为感知长路径。请参阅 https://docs.microsoft.com/windows/win32/fileio/maximum-file-path-limitation -->
+  <!--
+  <application xmlns="urn:schemas-microsoft-com:asm.v3">
+    <windowsSettings>
+      <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>
+      <longPathAware xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">true</longPathAware>
+    </windowsSettings>
+  </application>
+  -->
+
+  <!-- 启用 Windows 公共控件和对话框的主题(Windows XP 和更高版本) -->
+  <!--
+  <dependency>
+    <dependentAssembly>
+      <assemblyIdentity
+          type="win32"
+          name="Microsoft.Windows.Common-Controls"
+          version="6.0.0.0"
+          processorArchitecture="*"
+          publicKeyToken="6595b64144ccf1df"
+          language="*"
+        />
+    </dependentAssembly>
+  </dependency>
+  -->
+
+</assembly>

+ 1 - 1
TestWork.DLL/CameraClass.cs

@@ -396,7 +396,7 @@ namespace CCDCount.DLL
             try
             {
                 if(device == null) return result;
-                device.StreamGrabber.SetImageNodeNum(30);
+                device.StreamGrabber.SetImageNodeNum(100);
                 device.StreamGrabber.StartGrabbing();
                 result = true;
                 SystemAlarm.AlarmCancel(AlarmMessageList.相机采集开始失败);

+ 131 - 93
TestWork.DLL/MainThreadClass.cs

@@ -31,6 +31,28 @@ namespace CCDCount.DLL
         public bool CameraConfigIsChange = false;
         public string BatchNumber = "";
         public int ThisCamerNo = -1;
+        public double XCoefficient {
+            get
+            {
+                shuLiClass.GetXYCoefficient(out double XCoefficient, out double YCoefficient);
+                return XCoefficient;
+            }
+            set 
+            {
+                shuLiClass.SetXCoefficient(value);
+            }
+        }
+        public double YCoefficient {
+            get
+            {
+                shuLiClass.GetXYCoefficient(out double XCoefficient, out double YCoefficient);
+                return YCoefficient;
+            }
+            set
+            {
+                shuLiClass.SetYCoefficient(value);
+            }
+        }
 
         public bool CameraStatic { get { return _CameraStatic; } }
         private bool _CameraStatic = false;
@@ -53,7 +75,8 @@ namespace CCDCount.DLL
         public ModbusTcpClient modbusTcpClient = new ModbusTcpClient();
 
         // 颗粒结果待发送队列
-        ConcurrentQueue<ushort> SendQueue = new ConcurrentQueue<ushort>();
+        //ConcurrentQueue<ushort> SendQueue = new ConcurrentQueue<ushort>();
+        ConcurrentQueue<TestSenMessage> SendQueue = new ConcurrentQueue<TestSenMessage>();
 
         /// <summary>
         /// 数粒状态计时器
@@ -65,7 +88,7 @@ namespace CCDCount.DLL
         private bool _ShuLiState = true;
         public bool ShuLiState { get { return _ShuLiState; } }
 
-        private bool _IsDebug = true;
+        private bool _IsDebug = false;
         public bool IsDebug { get { return _IsDebug; } }
         private bool IsConnectModbus = false;
 
@@ -85,14 +108,26 @@ namespace CCDCount.DLL
         /// <param name="modbusTcpClient"></param>
         public void ConnectModbus(string ipAddress)
         {
-            if (!modbusTcpClient.Connect(ipAddress))
+            Task.Run(() =>
             {
-                //FaultLog.RecordErrorMessage($"MianThread{cameraConfig.CamerNo}-Modbus通讯连接失败,目标IP:{ipAddress}");
-                SystemAlarm.AlarmAlert(AlarmMessageList.PLC通讯连接失败, $"Modbus通讯连接失败,目标IP:{ipAddress}", "DLL:MainThreadClass-ConnectModbus");
-                return;
-            }
-            IsConnectModbus = true;
-            SystemAlarm.AlarmCancel(AlarmMessageList.PLC通讯连接失败);
+                while(!modbusTcpClient.Connect(ipAddress))
+                {
+                    IsConnectModbus = false;
+                    SystemAlarm.AlarmAlert(AlarmMessageList.PLC通讯连接失败, $"Modbus通讯连接失败,目标IP:{ipAddress}", "DLL:MainThreadClass-ConnectModbus");
+                    Task.Delay(1000);
+                    SystemAlarm.AlarmCancel(AlarmMessageList.PLC通讯连接失败);
+                }
+                FaultLog.RecordLogMessage($"Modbus通讯连接成功,目标IP:{ipAddress}",6);
+                IsConnectModbus = true;
+            });
+            //if (!modbusTcpClient.Connect(ipAddress))
+            //{
+            //    //FaultLog.RecordErrorMessage($"MianThread{cameraConfig.CamerNo}-Modbus通讯连接失败,目标IP:{ipAddress}");
+            //    SystemAlarm.AlarmAlert(AlarmMessageList.PLC通讯连接失败, $"Modbus通讯连接失败,目标IP:{ipAddress}", "DLL:MainThreadClass-ConnectModbus");
+            //    return;
+            //}
+            //IsConnectModbus = true;
+            //SystemAlarm.AlarmCancel(AlarmMessageList.PLC通讯连接失败);
         }
 
         /// <summary>
@@ -190,6 +225,11 @@ namespace CCDCount.DLL
                     FaultLog.RecordErrorMessage($"{cameraConfig.CamerNo}号相机没有批次号,启动失败", "Dll:MainThreadClass-StartMianThread");
                     return result;
                 }
+                if (!IsConnectModbus)
+                {
+                    FaultLog.RecordErrorMessage($"{cameraConfig.CamerNo}号相机没有PLC连接,启动失败", "Dll:MainThreadClass-StartMianThread");
+                    return result;
+                }
                 actionMesSqliteDataClass = new ActionMesSqliteDataClass($"{AppDomain.CurrentDomain.BaseDirectory}DATA\\ActiveObjectData\\Cam{cameraConfig.CamerNo}\\ActiveObjectData_{BatchNumber}.db");
                 //actionMesSqliteDataClass = new ActionMesSqliteDataClass($"{AppDomain.CurrentDomain.BaseDirectory}DATA\\ActiveObjectData\\Cam{cameraConfig.CamerNo}\\ActiveObjectData20250827.db");
                 actionMesSqliteDataClass.GetAllActionMinStartMaxEndLine(out int num, out int StartLine, out int EndLine);
@@ -281,7 +321,7 @@ namespace CCDCount.DLL
             if (NewActive == null)
             {
                 LOG.log(string.Format("{0}:没有获取到数粒数据", "MainThreadClass-GetShowImage"));
-                ImageData = GetNullShowImage(4096, 2000);
+                ImageData = GetNullShowImage(4096, ImageHeight);
                 return;
             }
             List<ActiveObjectClass> Data = shuLiClass.GetHistoryActive().Where(o => o.LastSeenLine > NewActive.LastSeenLine - ImageHeight).ToList();
@@ -453,36 +493,6 @@ namespace CCDCount.DLL
             return result;
         }
 
-        /// <summary>
-        /// 获取相机原图拼接图-50张拼一张
-        /// </summary>
-        /// <returns></returns>
-        public Bitmap GetSpliceCamImageOnce()
-        {
-            Bitmap result = null;
-
-            if (_images.Count >= 50)
-                {
-                    CameraImageSizeClass cameraImageSize = cameraClass.GetCamereImageSize();
-                    if (cameraImageSize.Width == 0 || cameraImageSize.Height == 0)
-                        return null;
-                    result = new Bitmap(cameraImageSize.Width, cameraImageSize.Height * 50);
-                    using (Graphics g = Graphics.FromImage(result))
-                    {
-                        g.Clear(Color.White); // 设置背景色
-                        int currentY = 0;
-                        for (int i = 0; i < 50; i++)
-                        {
-                            Bitmap OutImage = GetCamImageOnce();
-                            // 将每个图像绘制到新Bitmap上
-                            g.DrawImage(OutImage, new Point(0, currentY));
-                            currentY += OutImage.Height;
-                        }
-                    }
-                }
-            return result;
-        }
-
         public void ParameterTrain(List<ActiveObjectClass> ListValues)
         { 
             if(ListValues.Count() > 0)
@@ -544,7 +554,7 @@ namespace CCDCount.DLL
                 }
                 else
                 {
-                    Result.NoiseFilterThreshold = 200;
+                    Result.NoiseFilterThreshold = 5;
                 }
                 if (Result.NoiseFilterThreshold != -1)
                 {
@@ -571,8 +581,8 @@ namespace CCDCount.DLL
         /// <param name="e"></param>
         private void Worker_OneGrainCompleted(object sender, ActiveObjectEventArgsClass e)
         {
-            LOG.log("有活跃物体转换为了历史物体,回调事件被触发!", 6);
-            LOG.log(string.Format("图像处理实例中的待识别图像缓存队列长度{0}", shuLiClass.ImageNum), 6);
+            //LOG.log("有活跃物体转换为了历史物体,回调事件被触发!", 6);
+            //LOG.log(string.Format("图像处理实例中的待识别图像缓存队列长度{0}", shuLiClass.ImageNum), 6);
 
             if (e.Actives.Where(o => o.StateCode == 7).Count() > 0)
             {
@@ -584,15 +594,19 @@ namespace CCDCount.DLL
                 stopwatch.Stop();
                 _ShuLiState = true;
             }
-            //往数组中计数
-            ushort result = new ushort();
+
+            //Int16 result = new Int16();
+            ushort[] result = new ushort[2];
             // 事件处理逻辑
             foreach (ActiveObjectClass oneActive in e.Actives)
             {
-                if(BatchNumber!="") oneActive.BatchNumber = BatchNumber;
-                LOG.log(string.Format("输出当前颗粒信息,颗粒编号:{0},开始时间:{1}结束时间:{2},颗粒状态:{3},通道数:{4}",
-                    oneActive.Num, oneActive.StartCheckTime.ToString("O"), oneActive.EndCheckTime.ToString("O"), oneActive.StateCode, oneActive.ChannelNO), 6);
-                Task.Run(() =>
+                //往数组中计数
+                var UseTime = DateTime.Now - oneActive.StartCheckTime;
+                if (BatchNumber!="") oneActive.BatchNumber = BatchNumber;
+                LOG.log(string.Format("输出当前颗粒信息,颗粒编号:{0},开始时间:{1},结束时间:{2},识别耗时:{3},颗粒状态:{4},通道数:{5}",
+                    oneActive.Num, oneActive.StartCheckTime.ToString("O"), oneActive.EndCheckTime.ToString("O"),UseTime.ToString(), oneActive.StateCode, oneActive.ChannelNO), 6);
+                
+                ThreadPool.QueueUserWorkItem(_ =>
                 {
                     if (actionMesSqliteDataClass != null)
                     {
@@ -602,48 +616,59 @@ namespace CCDCount.DLL
                         }
                         catch (Exception ex)
                         {
-                            LOG.error ("MianThread-Worker_OneGrainCompleted-Task:" + ex.Message);
+                            LOG.error("MianThread-Worker_OneGrainCompleted-Task:" + ex.Message);
                         }
                     }
                 });
 
-                if (oneActive.StateCode != 7 && oneActive.ChannelNO != -1)
-                {
-                    //单通道合格计数
-                    result |= (ushort)(1 << oneActive.ChannelNO);
-                    if(oneActive.StateCode != 0)
-                    {
-                        result |= (ushort)(1 << (8 + oneActive.ChannelNO));
-                    }
-                }
-                else
-                {
-                    //单通道不合格计数
-                    Console.WriteLine($"通道{oneActive.ChannelNO}的激活数据有误!");
-                }
-                //新的数据发送格式,需要与新的PLC程序配合
-                //if (oneActive.StateCode == 0 && oneActive.ChannelNO != -1)
+                //if (oneActive.StateCode != 7 && oneActive.ChannelNO != -1)
                 //{
                 //    //单通道合格计数
                 //    result |= (ushort)(1 << oneActive.ChannelNO);
-                //}
-                //else
-                //{
-                //    if(oneActive.StateCode != 7 && oneActive.ChannelNO != -1)
+                //    if (oneActive.StateCode != 0)
                 //    {
-                //        //单通道不合格计数
                 //        result |= (ushort)(1 << (8 + oneActive.ChannelNO));
                 //    }
-                //    else if(oneActive.StateCode == 7)
-                //    {
-                //        FaultLog.RecordErrorMessage("视野存在遮挡,请检查相机");
-                //    }
                 //}
+                //else
+                //{
+                //    //单通道不合格计数
+                //    Console.WriteLine($"通道{oneActive.ChannelNO}的激活数据有误!");
+                //}
+                //新的数据发送格式,需要与新的PLC程序配合
+                if(oneActive.ChannelNO == -1)
+                {
+                    FaultLog.RecordErrorMessage("颗粒通道判定异常");
+                    return ;
+                }
+                if (oneActive.StateCode == 0)
+                {
+                    //单通道合格计数
+                    result[0] |= (ushort)(1 << oneActive.ChannelNO);
+                }
+                else
+                {
+                    if (oneActive.StateCode != 7)
+                    {
+                        //单通道不合格计数
+                        result[1] |= (ushort)(1 << (oneActive.ChannelNO));
+                    }
+                    else if (oneActive.StateCode == 7)
+                    {
+                        FaultLog.RecordErrorMessage("视野存在遮挡,请检查相机");
+                    }
+                }
             }
-            LOG.log("当前待发送队列数量:" + SendQueue.Count, 6);
-            if(IsSend)
+            //LOG.log("当前待发送队列数量:" + SendQueue.Count, 6);
+            if (IsSend)
             {
-                SendQueue.Enqueue(result);
+                SendQueue.Enqueue(new TestSenMessage()
+                {
+                    Message = result,
+                    //NumMessage = new ushort[] { (ushort)oneActive.Num },
+                    NumMessage = e.Actives.Select(o => (ushort)o.Num).ToArray(),
+                    count = (ushort)e.Actives.Count
+                });
             }
         }
 
@@ -689,18 +714,18 @@ namespace CCDCount.DLL
         /// </summary>
         private void SwitchIdentifyImageProcess()
         {
-            Stopwatch stopwatch = Stopwatch.StartNew();
-            stopwatch.Start();
+            //Stopwatch stopwatch = Stopwatch.StartNew();
+            //stopwatch.Start();
             while (IsSwitch)
             {
-                if (stopwatch.ElapsedMilliseconds > 1000)
-                {
+
+                //if (stopwatch.ElapsedMilliseconds > 1000)
+                //{
                     //Console.WriteLine("交换线程-图像处理实例中的待识别图像缓存队列长度{0}", shuLiClass.ImageNum);
                     //LOG.log(string.Format("交换线程-图像处理实例中的待识别图像缓存队列长度{0}", shuLiClass.ImageNum),6);
-                    GC.Collect();
-                    stopwatch.Restart();
-                }
-                //Thread.Sleep(5);
+                    //GC.Collect();
+                    //stopwatch.Restart();
+                //}
                 bool result = cameraClass.GetOnceImage(out IFrameOut IFramedata);
                 if (result)
                 {
@@ -721,8 +746,8 @@ namespace CCDCount.DLL
                 else
                     continue;
                 IFramedata.Dispose();
+                Thread.Sleep(1);
             }
-            stopwatch.Stop();
         }
 
         /// <summary>
@@ -733,7 +758,6 @@ namespace CCDCount.DLL
             try
             {
                 if(!IsConnectModbus) return;
-                //zmcauxClass.OpenZmcauxCard();
 
                 timeBeginPeriod(1); // 设置为1ms精度
                 IsSend = true;
@@ -777,7 +801,7 @@ namespace CCDCount.DLL
         private void SendBottLogicMessageProcess()
         {
             //获取数据
-            ushort sendMessage;
+            TestSenMessage sendMessage;
             bool AllowTransfer;
             bool TransferDone;
             Stopwatch sw = Stopwatch.StartNew();
@@ -785,7 +809,7 @@ namespace CCDCount.DLL
             {
                 //LOG.log("进入线程", 6);
                 sw.Restart();
-                sendMessage = new ushort();
+                sendMessage = new TestSenMessage();
                 //读取装瓶状态
                 AllowTransfer = false;
                 TransferDone = false;
@@ -808,13 +832,19 @@ namespace CCDCount.DLL
                             //FaultLog.RecordErrorMessage("MainThreadClass-SendBottLogicMessageProcess-SendQueue.TryDequeue failed!");
                         }
                     }
-                    if (sendMessage != 0)
+                    if (sendMessage.Message == null)
+                    {
+                        continue;
+                    }
+                    if (sendMessage.Message[0] != 0|| sendMessage.Message[1] != 0)
                     {
                         //写入数据
-                        modbusTcpClient.WriteSingleRegister(slaveId: 1, registerAddress: 100, value: sendMessage);
+                        modbusTcpClient.WriteMultipleRegisters(slaveId: 1, startAddress: 100, values: sendMessage.Message);
+                        modbusTcpClient.WriteMultipleRegisters(slaveId: 1, startAddress: 150, values: sendMessage.NumMessage);
+                        modbusTcpClient.WriteSingleRegister(slaveId: 1, registerAddress: 170, value: sendMessage.count);
                         modbusTcpClient.WriteCoilsRegister(slaveId: 1, CoilsAddress: 11, values: true);
                         sw.Stop();
-                        LOG.log(string.Format("sendMessage[1]:{0},此次写值耗时:{1}", sendMessage, sw.Elapsed), 6);
+                        LOG.log(string.Format("SendMessageOk:{0},SendMessageNg:{1},此次写值耗时:{2},此次写入数据量{3}", sendMessage.Message[0],sendMessage.Message[1], sw.Elapsed,sendMessage.count), 6);
                     }
 
                     //FaultLog.RecordLogMessage(string.Format("sendMessage[1]:{0},此次写值耗时:{1}", sendMessage, sw.Elapsed),1);
@@ -829,4 +859,12 @@ namespace CCDCount.DLL
         static extern uint timeBeginPeriod(uint period);
         #endregion
     }
+
+    public class TestSenMessage
+    {
+        public ushort[] Message { get; set; }
+        //public Int16 Message { get; set; }
+        public ushort[] NumMessage { get; set; }
+        public ushort count { get; set; }
+    }
 }

+ 2 - 3
TestWork.DLL/PLCManagementClass.cs

@@ -22,9 +22,7 @@ namespace CCDCount.DLL
         public ModbusTcpClient modbusTcpClient = new ModbusTcpClient();
         public PLCManagementClass(string ipAddress)
         {
-            //ConnectModbus("127.0.0.1");
             ConnectModbus(ipAddress);
-            //ConnectModbus("192.168.1.88");
         }
 
         public void ConnectModbus(string ipAddress)
@@ -841,7 +839,7 @@ namespace CCDCount.DLL
         {
             PlcParaModelClass result = null;
             var results = modbusTcpClient.ReadHoldingRegisters(slaveId: 1, startAddress: 1110, numRegisters: 124,out ushort[] ReturnValue);
-            if (ReturnValue == null) return null;
+            if (results || ReturnValue == null) return null;
             result = new PlcParaModelClass()
             {
                 SpeedModeRunningSpeed = GetFloatFromRegisters(ReturnValue.Take(2).ToArray()),
@@ -875,6 +873,7 @@ namespace CCDCount.DLL
                 BottlingShutdownValue = ReturnValue[120],
             };
             results = modbusTcpClient.ReadHoldingRegisters(slaveId: 1, startAddress: 1234, numRegisters: 36,out ReturnValue);
+            if (results || ReturnValue == null) return null;
             result.BottlingShutdownTime = GetInt32FromRegisters(ReturnValue.Take(2).ToArray());
             result.BottleFeedingWheelRunningSpeed = GetFloatFromRegisters(ReturnValue.Skip(8).Take(2).ToArray());
             result.BottleFeedingWheelJogRunningSpeed = GetFloatFromRegisters(ReturnValue.Skip(12).Take(2).ToArray());

+ 265 - 149
TestWork.DLL/ShuLiClass.cs

@@ -34,9 +34,12 @@ namespace CCDCount.DLL
         private List<int> _ChannelsRoi = new List<int>();
         private int ChannelWidth = 0;//每个区域的宽度
         private int IdentifyImageWidth = -1;
-        private static readonly object _lockObj = new object(); // 专用锁对象
+        //private static readonly object _lockObj = new object(); // 专用锁对象
         private int ObjectNum = 0;
         public int ImageNum { get { return IFrameDatas.Count; } }
+        private ReaderWriterLockSlim _rwLock = new ReaderWriterLockSlim();
+        private double XCoefficient = 1;
+        private double YCoefficient = 1;
         #endregion
 
         #region 公共方法
@@ -83,6 +86,7 @@ namespace CCDCount.DLL
             return ObjectNum;
         }
 
+        List<ActiveObjectClass> lostObjects = new List<ActiveObjectClass>();
         /// <summary>
         /// 处理图像序列的主入口
         /// </summary>
@@ -99,42 +103,53 @@ namespace CCDCount.DLL
                 currentLine += 1;
             }
 
+
+            Stopwatch stopwatch = Stopwatch.StartNew();
             //识别到结果并输出
-            lock (_lockObj)
-            {
-                // 清理超时未更新的物体
-                var lostObjects = activeObjects
+            //lock (_lockObj)
+            //{
+            _rwLock.EnterReadLock();
+            // 清理超时未更新的物体
+            lostObjects = activeObjects
                 .Where(o => (currentLine - o.LastSeenLine) > shuLiConfig.MAX_GAP || (o.LastSeenLine - o.StartLine) > shuLiConfig.MAX_Idetify_Height)
                 .ToList();
+            _rwLock.ExitReadLock();
+            //}
+            if (stopwatch.ElapsedMilliseconds > 1)
+            {
+                FaultLog.RecordErrorMessage($"ShuLiClass-ProcessImageSequence:图像结果分析-结果筛选,此次识别耗时:{stopwatch.Elapsed}");
+            }
 
-                List<ActiveObjectClass> OneActive = new List<ActiveObjectClass>();
+            List<ActiveObjectClass> OneActive = new List<ActiveObjectClass>();
 
-                // 有物体转变为活跃物体,返回值转为true
-                if (lostObjects.Count() > 0)
+            // 有物体转变为活跃物体,返回值转为true
+            if (lostObjects.Count > 0)
+            {
+                result = true;
+                stopwatch.Restart();
+                foreach (var item in lostObjects)
                 {
-                    result = true;
-                    foreach (var item in lostObjects)
+                    //噪点判定
+                    if (item.Area < shuLiConfig.NoiseFilter_Threshold)
                     {
-                        //噪点判定
-                        if (item.Area < shuLiConfig.NoiseFilter_Threshold)
-                        {
-                            item.StateCode = 9;
-                            //LOG.log(string.Format("噪点过滤,噪点面积:{0}", item.Area), 6);
-                            continue;
-                        }
-                        //转为历史物体,添加缺少的参数
-                        item.Num = ObjectNum += 1;
-                        item.ChannelNO = ActiveChannel(item);
-                        item.EndCheckTime = DateTime.Now;
-                        item.MaxLength = GetActionMaxLength(item.RowsData);
-                        if ((item.LastSeenLine - item.StartLine) > shuLiConfig.MAX_Idetify_Height)
-                        {
-                            item.StateCode = 7;
-                            FaultLog.RecordLogMessage("ShuLiClass-ProcessLine:非颗粒,视野异常", 3);
-                            LOG.log(string.Format("ShuLiClass-ProcessLine:非颗粒,视野异常"), 6);
-                            Console.WriteLine("ShuLiClass-ProcessLine:非颗粒,视野异常");
-                        }
-                        else if (shuLiConfig.PandingCode != -1)
+                        item.StateCode = 9;
+                        //LOG.log(string.Format("噪点过滤,噪点面积:{0}", item.Area), 6);
+                        continue;
+                    }
+                    //转为历史物体,添加缺少的参数
+                    item.Num = Interlocked.Increment(ref ObjectNum) + 1;
+                    item.ChannelNO = ActiveChannel(item);
+                    item.EndCheckTime = DateTime.Now;
+                    item.MaxLength = GetActionMaxLength(item.RowsData);
+                    //item.MaxLength = 100;
+                    if ((item.LastSeenLine - item.StartLine) > shuLiConfig.MAX_Idetify_Height)
+                    {
+                        item.StateCode = 7;
+                        //FaultLog.RecordLogMessage("ShuLiClass-ProcessLine:非颗粒,视野异常", 3);
+                        //LOG.log(string.Format("ShuLiClass-ProcessLine:非颗粒,视野异常"), 6);
+                        //Console.WriteLine("ShuLiClass-ProcessLine:非颗粒,视野异常");
+                    }
+                    else if (shuLiConfig.PandingCode != -1)
                         {
                             if (item.StateCode != -1)
                             {
@@ -178,28 +193,44 @@ namespace CCDCount.DLL
                                 Console.WriteLine("颗粒编号{0}:正常粒", item.Num);
                             }
                         }
-                        OneActive.Add(item);
-                    }
-
-                    if (OneActive.Count > 0)
-                    {
-                        LOG.log(string.Format("识别完成,首个颗粒编号:{0},颗粒数量:{1}", OneActive[0].Num, OneActive.Count), 6);
-                        //触发回调事件
-                        Task.Run(() =>
-                        {
-                            OnWorkCompleted(OneActive);
-                        });
-                    }
+                    OneActive.Add(item);
                 }
-                else
+                stopwatch.Stop();
+                //if (stopwatch.ElapsedMilliseconds > 1)
+                //{
+                //    FaultLog.RecordErrorMessage($"ShuLiClass-ProcessImageSequence:图像结果分析-结果参数补全,此次识别耗时:{stopwatch.Elapsed}");
+                //}
+                //stopwatch.Restart();
+                if (OneActive.Count > 0)
                 {
-                    OneActive = null;
+                    //LOG.log(string.Format("识别完成,首个颗粒编号:{0},颗粒数量:{1}", OneActive[0].Num, OneActive.Count), 6);
+                    //触发回调事件
+                    //Task.Run(() =>
+                    //{
+                    //    OnWorkCompleted(OneActive);
+                    //});
+                    ThreadPool.QueueUserWorkItem(_ =>OnWorkCompleted(OneActive));
                 }
-
-                // 累加到总数并从活跃物体转移到历史物体
-                lostObjects.Where(o => o.Area >= shuLiConfig.NoiseFilter_Threshold && o.StateCode != 7 && o.StateCode != 9).ToList().ForEach(o => TryAdd(historyActiveObjects, o, 2500));
-                lostObjects.ForEach(o => activeObjects.Remove(o));
+                stopwatch.Stop();
+                //if (stopwatch.ElapsedMilliseconds > 1)
+                //{
+                //    FaultLog.RecordErrorMessage($"ShuLiClass-ProcessImageSequence:图像结果分析-结果完成回调函数,此次识别耗时:{stopwatch.Elapsed}");
+                //}
             }
+            else
+            {
+                OneActive.Clear();
+            }
+            //lock (_lockObj)
+            //{
+            _rwLock.EnterWriteLock();
+            // 累加到总数并从活跃物体转移到历史物体
+            OneActive.ForEach(o => TryAdd(historyActiveObjects, o, 2500));
+            //lostObjects.Where(o => o.StateCode != 7 && o.StateCode != 9).ToList().ForEach(o => TryAdd(historyActiveObjects, o, 2500));
+            lostObjects.ForEach(o => activeObjects.Remove(o));
+            _rwLock.ExitWriteLock();
+            //}
+
             return result;
         }
 
@@ -220,10 +251,13 @@ namespace CCDCount.DLL
         /// <returns></returns>
         public List<ActiveObjectClass> GetHistoryActive()
         {
-            lock (_lockObj) // 加锁
-            {
-                return historyActiveObjects.ToList();
-            }
+            //lock (_lockObj) // 加锁
+            //{
+            _rwLock.EnterReadLock();
+            var result = historyActiveObjects.ToList();
+            _rwLock.ExitReadLock();
+            return result;
+            //}
         }
 
         /// <summary>
@@ -232,8 +266,11 @@ namespace CCDCount.DLL
         /// <returns></returns>
         public int GetHistoryActiveNum()
         {
-            lock (_lockObj) // 加锁
-                return historyActiveObjects.Count();
+            //lock (_lockObj) // 加锁
+            _rwLock.EnterReadLock();
+            var result = historyActiveObjects.Count;
+            _rwLock.ExitReadLock();
+            return result;
         }
 
         /// <summary>
@@ -242,8 +279,11 @@ namespace CCDCount.DLL
         /// <returns></returns>
         public int GetOkHistoryNum()
         {
-            lock (_lockObj)
-                return historyActiveObjects.Where(o => o.StateCode == 0).Count();
+            //lock (_lockObj)
+            _rwLock.EnterReadLock();
+            var result = historyActiveObjects.Where(o => o.StateCode == 0).ToList().Count;
+            _rwLock.ExitReadLock();
+            return result;
         }
 
         /// <summary>
@@ -252,8 +292,11 @@ namespace CCDCount.DLL
         /// <returns></returns>
         public int GetNgHistoryNum()
         {
-            lock (_lockObj)
-                return historyActiveObjects.Where(o => o.StateCode != 0).Count();
+            //lock (_lockObj)
+            _rwLock.EnterReadLock();
+            var result = historyActiveObjects.Where(o => o.StateCode != 0).ToList().Count;
+            _rwLock.ExitReadLock();
+            return result;
         }
 
         /// <summary>
@@ -262,11 +305,13 @@ namespace CCDCount.DLL
         /// <returns></returns>
         public bool ClearHistoryActive()
         {
-            lock (_lockObj)
-            {
-                historyActiveObjects.Clear();
-                return true;
-            }
+            //lock (_lockObj)
+            //{
+            _rwLock.EnterWriteLock();
+            historyActiveObjects.Clear();
+            _rwLock.ExitWriteLock();
+            return true;
+            //}
         }
 
         /// <summary>
@@ -387,6 +432,34 @@ namespace CCDCount.DLL
                 result = shuLiConfig.ImageWidth;
             return result;
         }
+
+        /// <summary>
+        /// 获取坐标转换系数
+        /// </summary>
+        /// <param name="XCoefficient"></param>
+        /// <param name="YCoefficient"></param>
+        public void GetXYCoefficient(out double XCoefficient, out double YCoefficient)
+        {
+            XCoefficient = this.XCoefficient;
+            YCoefficient = this.YCoefficient;
+        }
+
+        /// <summary>
+        /// 设置坐标转换X系数
+        /// </summary>
+        /// <param name="XCoefficient"></param>
+        public void SetXCoefficient(double XCoefficient)
+        {
+            this.XCoefficient = XCoefficient;
+        }
+
+        /// <summary>
+        /// 设置坐标转换Y系数
+        /// </summary>
+        public void SetYCoefficient(double YCoefficient)
+        {
+            this.YCoefficient = YCoefficient;
+        }
         #endregion
 
         #region 私有方法
@@ -396,6 +469,7 @@ namespace CCDCount.DLL
         /// </summary>
         private void OnWorkCompleted(List<ActiveObjectClass> activeObject)
         {
+            if(activeObject == null) return;
             ActiveObjectEventArgsClass activeObjectEventArgs = new ActiveObjectEventArgsClass(activeObject);
             // 触发事件
             WorkCompleted?.Invoke(this, activeObjectEventArgs);
@@ -411,6 +485,7 @@ namespace CCDCount.DLL
         /// <param name="image">当前行像素数组</param>
         private bool ProcessLine(IImage imagedata, int RowNo)
         {
+            Stopwatch stopwatch = Stopwatch.StartNew();
             bool result = false;
             // 步骤1:检测当前行的有效区域
             var currentRegions = FindValidRegions(imagedata, RowNo);
@@ -428,93 +503,112 @@ namespace CCDCount.DLL
                 }
             }
             IsPrintLightOnError = false;
-            lock (_lockObj)
+            stopwatch.Stop();
+            if (stopwatch.ElapsedMilliseconds > 1)
             {
-                foreach (var region in currentRegions)
+                FaultLog.RecordErrorMessage($"ShuLiClass-ProcessLine:图像连通域检测超时,此次识别耗时:{stopwatch.Elapsed}");
+            }
+            stopwatch.Restart();
+            //lock (_lockObj)
+            //{
+            foreach (var region in currentRegions)
+            {
+
+                // 查找全部可合并的活跃物体(有重叠+在允许间隔内)
+                _rwLock.EnterReadLock();
+                var matcheds = activeObjects.Where(o =>
+                    IsOverlapping(o, region) &&
+                    (currentLine - o.LastSeenLine - 1) <= shuLiConfig.MAX_GAP).ToList();
+                _rwLock.ExitReadLock();
+                //当有多个可合并的活跃物体时,将多个物体合并
+                if (matcheds.Count >= 2)
                 {
-                    // 查找全部可合并的活跃物体(有重叠+在允许间隔内)
-                    var matcheds = activeObjects.Where(o =>
-                        IsOverlapping(o, region) &&
-                        (currentLine - o.LastSeenLine - 1) <= shuLiConfig.MAX_GAP).ToList();
-                    //当有多个可合并的活跃物体时,将多个物体合并
-                    if (matcheds.Count >= 2)
+                    // 合并有效区域队列
+                    var CopeRowsData = new List<RowStartEndCol>();
+                    matcheds.ForEach(o => CopeRowsData = CopeRowsData.Concat(o.RowsData).ToList());
+                    // 合并有效区域并保存在新的区域中
+                    var MergeMatched = new ActiveObjectClass
                     {
-                        // 合并有效区域队列
-                        var CopeRowsData = new List<RowStartEndCol>();
-                        matcheds.ForEach(o => CopeRowsData = CopeRowsData.Concat(o.RowsData).ToList());
-                        // 合并有效区域并保存在新的区域中
-                        var MergeMatched = new ActiveObjectClass
-                        {
-                            MinStartCol = matcheds.Min(o => o.MinStartCol),
-                            MaxEndCol = matcheds.Max(o => o.MaxEndCol),
-                            StartLine = matcheds.Min(o => o.StartLine),
-                            LastSeenLine = matcheds.Max(o => o.LastSeenLine),
-                            LastSeenLineStartCol = matcheds.Min(o => o.LastSeenLineStartCol),
-                            LastSeenLineEndCol = matcheds.Max(o => o.LastSeenLineEndCol),
-                            StartCheckTime = matcheds.Min(o => o.StartCheckTime),
-                            EndCheckTime = matcheds.Max(o => o.EndCheckTime),
-                            Area = matcheds.Sum(o => o.Area),
-                            RowsData = CopeRowsData,
-                            ImageWidth = matcheds.FirstOrDefault().ImageWidth,
-                            //StateCode = 8
-                        };
-                        // 从活跃区域中删除被合并的区域
-                        matcheds.ForEach(o => activeObjects.Remove(o));
-                        // 保存新的区域到活跃区域中
-                        activeObjects.Add(MergeMatched);
-                    }
-
-                    // 搜获可用且可合并的活跃区域
-                    var matched = activeObjects.FirstOrDefault(o =>
-                        IsOverlapping(o, region) &&
-                        (currentLine - o.LastSeenLine - 1) <= shuLiConfig.MAX_GAP);
-                    if (matched != null)
+                        MinStartCol = matcheds.Min(o => o.MinStartCol),
+                        MaxEndCol = matcheds.Max(o => o.MaxEndCol),
+                        StartLine = matcheds.Min(o => o.StartLine),
+                        LastSeenLine = matcheds.Max(o => o.LastSeenLine),
+                        LastSeenLineStartCol = matcheds.Min(o => o.LastSeenLineStartCol),
+                        LastSeenLineEndCol = matcheds.Max(o => o.LastSeenLineEndCol),
+                        StartCheckTime = matcheds.Min(o => o.StartCheckTime),
+                        EndCheckTime = matcheds.Max(o => o.EndCheckTime),
+                        Area = matcheds.Sum(o => o.Area),
+                        RowsData = CopeRowsData,
+                        ImageWidth = matcheds.FirstOrDefault().ImageWidth,
+                        //StateCode = 8
+                    };
+                    _rwLock.EnterWriteLock();
+                    // 从活跃区域中删除被合并的区域
+                    matcheds.ForEach(o => activeObjects.Remove(o));
+                    // 保存新的区域到活跃区域中
+                    activeObjects.Add(MergeMatched);
+                    _rwLock.ExitWriteLock();
+                }
+                _rwLock.EnterReadLock();
+                // 搜获可用且可合并的活跃区域
+                var matched = activeObjects.FirstOrDefault(o =>
+                    IsOverlapping(o, region) &&
+                    (currentLine - o.LastSeenLine - 1) <= shuLiConfig.MAX_GAP);
+                _rwLock.ExitReadLock();
+                if (matched != null)
+                {
+                    // 合并区域:扩展物体边界并更新状态
+                    matched.MinStartCol = Math.Min(matched.MinStartCol, region.Start);
+                    matched.MaxEndCol = Math.Max(matched.MaxEndCol, region.End);
+                    matched.Area += region.End - region.Start + 1;
+                    matched.LastSeenLine = currentLine;
+                    matched.RowsData.Add(new RowStartEndCol
                     {
-                        // 合并区域:扩展物体边界并更新状态
-                        matched.MinStartCol = Math.Min(matched.MinStartCol, region.Start);
-                        matched.MaxEndCol = Math.Max(matched.MaxEndCol, region.End);
-                        matched.Area += region.End - region.Start + 1;
-                        matched.LastSeenLine = currentLine;
-                        matched.RowsData.Add(new RowStartEndCol
-                        {
-                            StartCol = region.Start,
-                            EndCol = region.End,
-                            RowsCol = currentLine,
-                        });
-                        matched.LastSeenLineStartCol = region.Start;
-                        matched.LastSeenLineEndCol = region.End;
-                    }
-                    else
+                        StartCol = region.Start,
+                        EndCol = region.End,
+                        RowsCol = currentLine,
+                    });
+                    matched.LastSeenLineStartCol = region.Start;
+                    matched.LastSeenLineEndCol = region.End;
+                }
+                else
+                {
+                    _rwLock.EnterWriteLock();
+                    // 创建新物体(首次出现的区域)
+                    activeObjects.Add(new ActiveObjectClass
                     {
-                        // 创建新物体(首次出现的区域)
-                        activeObjects.Add(new ActiveObjectClass
-                        {
-                            MinStartCol = region.Start,
-                            MaxEndCol = region.End,
-                            StartLine = currentLine,
-                            LastSeenLine = currentLine,
-                            LastSeenLineStartCol = region.Start,
-                            LastSeenLineEndCol = region.End,
-                            StartCheckTime = DateTime.Now,
-                            Area = region.End - region.Start + 1,
-                            ImageWidth = IdentifyImageWidth,
-                            RowsData = new List<RowStartEndCol> {
-                                new RowStartEndCol {
-                                    StartCol = region.Start,
-                                    EndCol = region.End,
-                                    RowsCol = currentLine,
-                                }
+                        MinStartCol = region.Start,
+                        MaxEndCol = region.End,
+                        StartLine = currentLine,
+                        LastSeenLine = currentLine,
+                        LastSeenLineStartCol = region.Start,
+                        LastSeenLineEndCol = region.End,
+                        StartCheckTime = DateTime.Now,
+                        Area = region.End - region.Start + 1,
+                        ImageWidth = IdentifyImageWidth,
+                        RowsData = new List<RowStartEndCol> {
+                            new RowStartEndCol {
+                                StartCol = region.Start,
+                                EndCol = region.End,
+                                RowsCol = currentLine,
                             }
-                        });
-                    }
+                        }
+                    });
+                    _rwLock.ExitWriteLock();
                 }
             }
+            //}
+            stopwatch.Stop();
+            if (stopwatch.ElapsedMilliseconds > 1)
+            {
+                FaultLog.RecordErrorMessage($"ShuLiClass-ProcessLine:图像区域合并超时,此次识别耗时:{stopwatch.Elapsed}");
+            }
 
             currentRegions.Clear();
             
             return result;
         }
-
+        List<ValidRegionModelClass> regions = new List<ValidRegionModelClass>();
         /// <summary>
         /// 检测有效物体区域(横向连续黑色像素段)
         /// </summary>
@@ -522,7 +616,7 @@ namespace CCDCount.DLL
         /// <returns>有效区域列表(起始/结束位置)</returns>
         private List<ValidRegionModelClass> FindValidRegions(IImage image, int RowNo)
         {
-            List<ValidRegionModelClass> regions = new List<ValidRegionModelClass>();
+            regions.Clear();
             int start = -1; // 当前区域起始标记
             int end = -1;
             // 遍历所有像素列
@@ -728,7 +822,8 @@ namespace CCDCount.DLL
                     CenterPoint = new Point() { X = (hull[1].X + hull[0].X) / 2, Y = (hull[1].Y + hull[0].Y) / 2 },
                     Width = 0,
                     Height = Distance(hull[0], hull[1]),
-                    Angle = Math.Atan2(hull[1].Y - hull[0].Y, hull[1].X - hull[0].X)
+                    Angle = Math.Atan2(hull[1].Y - hull[0].Y, hull[1].X - hull[0].X),
+                    MaxLength = Distance(hull[0], hull[1])
                 };
                 return result;
             }
@@ -737,6 +832,8 @@ namespace CCDCount.DLL
             int pointIndex1 = 0;
             int pointIndex2 = 0;
 
+            maxDist = Distance(hull[pointIndex1], hull[pointIndex2], XCoefficient, YCoefficient);
+
             for (int i = 0; i < n; i++)
             {
                 for (int j = i + 1; j < n; j++)
@@ -751,6 +848,7 @@ namespace CCDCount.DLL
             }
 
 
+
             double angle = Math.Atan2(hull[pointIndex2].Y - hull[pointIndex1].Y, hull[pointIndex2].X - hull[pointIndex2].X);
             var rotatedPoints = hull.Select(p => RotatePoint(p, -angle)).ToList();
             // 找到包围盒
@@ -779,7 +877,8 @@ namespace CCDCount.DLL
                 CenterPoint = RotatePoint(new Point() { X = centerX, Y = centerY }, angle),
                 Width = width,
                 Height = height,
-                Angle = angle
+                Angle = angle,
+                MaxLength = maxDist
             };
 
             return result;
@@ -790,7 +889,7 @@ namespace CCDCount.DLL
         /// </summary>
         /// <param name="convexHull">凸包顶点列表(按逆时针顺序排列)</param>
         /// <returns>最小外接矩形的四个顶点</returns>
-        public List<Point> CalculateMinimumBoundingRectangle(List<Point> convexHull)
+        private List<Point> CalculateMinimumBoundingRectangle(List<Point> convexHull)
         {
             if (convexHull == null || convexHull.Count < 3)
                 return null;
@@ -889,6 +988,13 @@ namespace CCDCount.DLL
             return Math.Sqrt(dx * dx + dy * dy);
         }
 
+        public double Distance(Point a,Point b,double XCoefficient,double YCoefficient)
+        {
+            double dx = a.X - b.X;
+            double dy = a.Y - b.Y;
+            return Math.Sqrt(dx * dx * XCoefficient + dy * dy * YCoefficient);
+        }
+
         private bool TryAdd(List<ActiveObjectClass> list, ActiveObjectClass item, int maxSize)
         {
             list.Add(item);
@@ -906,19 +1012,25 @@ namespace CCDCount.DLL
         /// </summary>
         private void IdentifyImageProcess()
         {
-            //Stopwatch stopwatch = Stopwatch.StartNew();
+            Stopwatch stopwatch = Stopwatch.StartNew();
             while (IsIdentify)
             {
                 //判断队列中是否有数据
                 if (IFrameDatas.Count() > 0)
                 {
-                    //stopwatch.Restart();
-                    if (IFrameDatas.Count() > 50)
-                        SystemAlarm.AlarmAlert(AlarmMessageList.待识别队列数据堆积, $"图像数据队列中数据过多,请及时处理!当前数据数量为:{IFrameDatas.Count()}", "DLL:ShuLIClass-IdentifyImageProcess");
-                        //FaultLog.RecordErrorMessage($"图像数据队列中数据过多,请及时处理!当前数据数量为:{IFrameDatas.Count()}");
-                    else
-                        SystemAlarm.AlarmCancel(AlarmMessageList.待识别队列数据堆积);
+                    stopwatch.Restart();
+                    if (IFrameDatas.Count() > 5)
+                        //SystemAlarm.AlarmAlert(AlarmMessageList.待识别队列数据堆积, $"图像数据队列中数据过多,请及时处理!当前数据数量为:{IFrameDatas.Count()}", "DLL:ShuLIClass-IdentifyImageProcess");
+                        FaultLog.RecordErrorMessage($"图像数据队列中数据过多,请及时处理!当前数据数量为:{IFrameDatas.Count()}");
+                    //else
+                        //SystemAlarm.AlarmCancel(AlarmMessageList.待识别队列数据堆积);
                     IFrameDatas.TryDequeue(out IImage IframeData);
+                    stopwatch.Stop();
+                    if (stopwatch.ElapsedMilliseconds > 5)
+                    {
+                        FaultLog.RecordErrorMessage($"ShuLiClass-IdentifyImageProcess:图像读取超时,此次识别耗时:{stopwatch.Elapsed}");
+                    }
+                    stopwatch.Restart();
                     //是否成功取得数据
                     if (IframeData != null)
                     {
@@ -931,7 +1043,11 @@ namespace CCDCount.DLL
                         continue;
                     }
                     //输出耗时
-                    //stopwatch.Stop();
+                    stopwatch.Stop();
+                    if(stopwatch.ElapsedMilliseconds > 5)
+                    {
+                        FaultLog.RecordErrorMessage($"ShuLiClass-IdentifyImageProcess:图像识别超时,此次识别耗时:{stopwatch.Elapsed}");
+                    }
                     //Console.WriteLine($"识别线程识别一张图片耗时:{stopwatch.Elapsed},待识别队列剩余数量{IFrameDatas.Count()}");
                 }
                 else

+ 23 - 0
TestWork.DLL/Tools/ModbusClass.cs

@@ -317,6 +317,29 @@ namespace CCDCount.DLL.Tools
             return result;
         }
 
+        public bool WriteSingleRegister(byte slaveId, ushort registerAddress, Int16 value)
+        {
+            bool result = false;
+            if (_modbusMaster == null)
+            {
+                //FaultLog.RecordErrorMessage($"MainThreadClass-WriteSingleRegister failed:未连接到服务器");
+                result = false;
+                return result;
+            }
+            try
+            {
+                _modbusMaster.WriteSingleRegister(slaveId, registerAddress, (ushort)value);
+                result = true;
+            }
+            catch (Exception ex)
+            {
+                //Console.WriteLine($"写入单个寄存器失败: {ex.Message}");
+                //FaultLog.RecordErrorMessage($"MainThreadClass-WriteSingleRegister-WriteSingleRegister failed:{ex.Message}");
+                result = false;
+            }
+            return result;
+        }
+
         /// <summary>
         /// 写入多个寄存器(功能码16)
         /// </summary>

+ 2 - 0
TestWork.MODEL/ShuLiModel/BoundingRectangleMdoel.cs

@@ -18,5 +18,7 @@ namespace CCDCount.MODEL.ShuLiModel
         public double Height { get; set; }
 
         public double Angle { get; set; }
+
+        public double MaxLength { get; set; }
     }
 }