Sfoglia il codice sorgente

20260206001 相机读取和历史数据保存优化,识别模式改为内部触发降低CPU消耗。

向羽 孟 2 mesi fa
parent
commit
30ff8f3448

+ 61 - 1
TestWork.DLL/CameraClass.cs

@@ -296,6 +296,61 @@ namespace CCDCount.DLL
             }
             }
         }
         }
 
 
+        public DateTime FromUnixTimestamp(long timestamp, bool isMilliseconds = false)
+        {
+            try
+            {
+                // 如果未指定单位,尝试自动检测
+                if (!isMilliseconds)
+                {
+                    // 如果时间戳看起来像毫秒级(13位数字)
+                    if (timestamp.ToString().Length > 10)
+                    {
+                        isMilliseconds = true;
+                    }
+                }
+
+                if (isMilliseconds)
+                {
+                    // 验证毫秒级时间戳范围
+                    const long minMilliTimestamp = -62135596800000; // 0001-01-01 00:00:00 UTC
+                    const long maxMilliTimestamp = 253402300799999; // 9999-12-31 23:59:59 UTC
+
+                    if (timestamp < minMilliTimestamp || timestamp > maxMilliTimestamp)
+                    {
+                        throw new ArgumentOutOfRangeException(nameof(timestamp),
+                            "毫秒级时间戳超出有效范围");
+                    }
+
+                    DateTime origin = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc).AddMilliseconds(timestamp);
+                    return TimeZoneInfo.ConvertTimeFromUtc(origin, TimeZoneInfo.Local);
+                }
+                else
+                {
+                    // 验证秒级时间戳范围
+                    const long minTimestamp = -62135596800;
+                    const long maxTimestamp = 253402300799;
+
+                    if (timestamp < minTimestamp || timestamp > maxTimestamp)
+                    {
+                        throw new ArgumentOutOfRangeException(nameof(timestamp),
+                            "秒级时间戳超出有效范围");
+                    }
+
+                    DateTime origin = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc).AddSeconds(timestamp);
+                    return TimeZoneInfo.ConvertTimeFromUtc(origin, TimeZoneInfo.Local);
+                }
+            }
+            catch (ArgumentOutOfRangeException)
+            {
+                throw;
+            }
+            catch (Exception ex)
+            {
+                throw new ArgumentException($"无法转换时间戳 {timestamp}: {ex.Message}", ex);
+            }
+        }
+
         /// <summary>
         /// <summary>
         /// 获取一次图片
         /// 获取一次图片
         /// </summary>
         /// </summary>
@@ -307,6 +362,11 @@ namespace CCDCount.DLL
             int BRet = device.StreamGrabber.GetImageBuffer(1000, out IFrameData);
             int BRet = device.StreamGrabber.GetImageBuffer(1000, out IFrameData);
             if (BRet == 0 && IFrameData != null) result = true;
             if (BRet == 0 && IFrameData != null) result = true;
             else return result;
             else return result;
+            var QutuYanshiTime = (DateTime.Now - FromUnixTimestamp((long)IFrameData.HostTimeStamp)).TotalMilliseconds;
+            if (QutuYanshiTime > 12)
+            {
+                Console.WriteLine($"GetOnceImage-识别延时超过了12ms,耗时{QutuYanshiTime}");
+            }
             if (lastframeNum == -1)
             if (lastframeNum == -1)
             {
             {
                 lastframeNum = IFrameData.FrameNum;
                 lastframeNum = IFrameData.FrameNum;
@@ -408,7 +468,7 @@ namespace CCDCount.DLL
             try
             try
             {
             {
                 if(device == null) return result;
                 if(device == null) return result;
-                device.StreamGrabber.SetImageNodeNum(100);
+                device.StreamGrabber.SetImageNodeNum(300);
                 device.StreamGrabber.StartGrabbing();
                 device.StreamGrabber.StartGrabbing();
                 result = true;
                 result = true;
                 SystemAlarm.AlarmCancel(AlarmMessageList.相机采集开始失败);
                 SystemAlarm.AlarmCancel(AlarmMessageList.相机采集开始失败);

+ 150 - 85
TestWork.DLL/MainThreadClass.cs

@@ -90,7 +90,7 @@ namespace CCDCount.DLL
         private bool _ShuLiState = true;
         private bool _ShuLiState = true;
         public bool ShuLiState { get { return _ShuLiState; } }
         public bool ShuLiState { get { return _ShuLiState; } }
 
 
-        private bool _IsDebug = true;
+        private bool _IsDebug = false;
         public bool IsDebug { get { return _IsDebug; } }
         public bool IsDebug { get { return _IsDebug; } }
         private bool IsConnectModbus = false;
         private bool IsConnectModbus = false;
 
 
@@ -112,7 +112,8 @@ namespace CCDCount.DLL
         {
         {
             Task.Run(() =>
             Task.Run(() =>
             {
             {
-                while(!modbusTcpClient.Connect(ipAddress))
+                int i = 10;
+                while(!modbusTcpClient.Connect(ipAddress)&&i>0)
                 {
                 {
                     IsConnectModbus = false;
                     IsConnectModbus = false;
                     //SystemAlarm.AlarmAlert(AlarmMessageList.数粒通讯连接失败, $"Modbus通讯连接失败,目标IP:{ipAddress}", "DLL:MainThreadClass-ConnectModbus");
                     //SystemAlarm.AlarmAlert(AlarmMessageList.数粒通讯连接失败, $"Modbus通讯连接失败,目标IP:{ipAddress}", "DLL:MainThreadClass-ConnectModbus");
@@ -120,6 +121,7 @@ namespace CCDCount.DLL
                         $"Number counting communication connection failed, target IP:{ipAddress}",
                         $"Number counting communication connection failed, target IP:{ipAddress}",
                         $"Modbus通讯连接失败,目标IP:{ipAddress}",
                         $"Modbus通讯连接失败,目标IP:{ipAddress}",
                         "DLL:MainThreadClass-ConnectModbus");
                         "DLL:MainThreadClass-ConnectModbus");
+                    i--;
                     Task.Delay(1000);
                     Task.Delay(1000);
                 }
                 }
                 SystemAlarm.AlarmCancel(AlarmMessageList.数粒通讯连接失败);
                 SystemAlarm.AlarmCancel(AlarmMessageList.数粒通讯连接失败);
@@ -233,16 +235,17 @@ namespace CCDCount.DLL
             bool result = false;
             bool result = false;
             try
             try
             {
             {
-                if(BatchNumber=="")
+                if (BatchNumber == "")
                 {
                 {
                     FaultLog.RecordErrorMessage($"Camera {cameraConfig.CamerNo} does not have a batch number, and it failed to start", "Dll:MainThreadClass-StartMianThread");
                     FaultLog.RecordErrorMessage($"Camera {cameraConfig.CamerNo} does not have a batch number, and it failed to start", "Dll:MainThreadClass-StartMianThread");
                     return result;
                     return result;
                 }
                 }
-                if (!IsConnectModbus)
-                {
-                    FaultLog.RecordErrorMessage($"Camera {cameraConfig.CamerNo} does not have a PLC connection, resulting in startup failure", "Dll:MainThreadClass-StartMianThread");
-                    return result;
-                }
+                //if (!IsConnectModbus)
+                //{
+                //    FaultLog.RecordErrorMessage($"Camera {cameraConfig.CamerNo} does not have a PLC connection, resulting in startup failure", "Dll:MainThreadClass-StartMianThread");
+                //    return result;
+                //}
+                timeBeginPeriod(1); // 设置为1ms精度
                 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}\\ActiveObjectData_{BatchNumber}.db");
                 //actionMesSqliteDataClass = new ActionMesSqliteDataClass($"{AppDomain.CurrentDomain.BaseDirectory}DATA\\ActiveObjectData\\Cam{cameraConfig.CamerNo}\\ActiveObjectData20250827.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);
                 actionMesSqliteDataClass.GetAllActionMinStartMaxEndLine(out int num, out int StartLine, out int EndLine);
@@ -258,9 +261,9 @@ namespace CCDCount.DLL
                 // 数据交换线程开启
                 // 数据交换线程开启
                 StartSwitchThread();
                 StartSwitchThread();
                 // 为数粒算法的识别成功一粒回调函数添加方法
                 // 为数粒算法的识别成功一粒回调函数添加方法
-                shuLiClass.WorkCompleted += Worker_OneGrainCompleted;
+                //shuLiClass.WorkCompleted += Worker_OneGrainCompleted;
                 // 开启识别线程
                 // 开启识别线程
-                shuLiClass.StartIdentifyFuntion(cameraClass.GetCamereImageSize().Width);
+                shuLiClass.StartIdentifyFuntion2(cameraClass.GetCamereImageSize().Width);
                 result = true;
                 result = true;
             }
             }
             catch(Exception ex)
             catch(Exception ex)
@@ -288,6 +291,7 @@ namespace CCDCount.DLL
                 shuLiClass.StopIdentifyFuntion();
                 shuLiClass.StopIdentifyFuntion();
                 StopSendBottLogicMessageThread();
                 StopSendBottLogicMessageThread();
                 result = true;
                 result = true;
+                actionMesSqliteDataClass.Dispose();
             }
             }
             catch(Exception ex)
             catch(Exception ex)
             {
             {
@@ -684,7 +688,7 @@ namespace CCDCount.DLL
                     LOG.log("参数训练-计算标定后的长度", 6);
                     LOG.log("参数训练-计算标定后的长度", 6);
                     foreach (var item in ListValues)
                     foreach (var item in ListValues)
                     {
                     {
-                        item.MaxLength = shuLiClass.SizeCalculation2(item.RowsData).MaxLength;
+                        item.MaxLength = shuLiClass.SizeCalculation(item.RowsData).Height;
                     }
                     }
                     LOG.log("参数训练-长度数据筛选", 6);
                     LOG.log("参数训练-长度数据筛选", 6);
                     if (ListValues.Count >= 1000)
                     if (ListValues.Count >= 1000)
@@ -721,7 +725,60 @@ namespace CCDCount.DLL
         #endregion
         #endregion
 
 
         #region 私有方法
         #region 私有方法
+        public DateTime FromUnixTimestamp(long timestamp, bool isMilliseconds = false)
+        {
+            try
+            {
+                // 如果未指定单位,尝试自动检测
+                if (!isMilliseconds)
+                {
+                    // 如果时间戳看起来像毫秒级(13位数字)
+                    if (timestamp.ToString().Length > 10)
+                    {
+                        isMilliseconds = true;
+                    }
+                }
 
 
+                if (isMilliseconds)
+                {
+                    // 验证毫秒级时间戳范围
+                    const long minMilliTimestamp = -62135596800000; // 0001-01-01 00:00:00 UTC
+                    const long maxMilliTimestamp = 253402300799999; // 9999-12-31 23:59:59 UTC
+
+                    if (timestamp < minMilliTimestamp || timestamp > maxMilliTimestamp)
+                    {
+                        throw new ArgumentOutOfRangeException(nameof(timestamp),
+                            "毫秒级时间戳超出有效范围");
+                    }
+
+                    DateTime origin = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc).AddMilliseconds(timestamp);
+                    return TimeZoneInfo.ConvertTimeFromUtc(origin, TimeZoneInfo.Local);
+                }
+                else
+                {
+                    // 验证秒级时间戳范围
+                    const long minTimestamp = -62135596800;
+                    const long maxTimestamp = 253402300799;
+
+                    if (timestamp < minTimestamp || timestamp > maxTimestamp)
+                    {
+                        throw new ArgumentOutOfRangeException(nameof(timestamp),
+                            "秒级时间戳超出有效范围");
+                    }
+
+                    DateTime origin = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc).AddSeconds(timestamp);
+                    return TimeZoneInfo.ConvertTimeFromUtc(origin, TimeZoneInfo.Local);
+                }
+            }
+            catch (ArgumentOutOfRangeException)
+            {
+                throw;
+            }
+            catch (Exception ex)
+            {
+                throw new ArgumentException($"无法转换时间戳 {timestamp}: {ex.Message}", ex);
+            }
+        }
 
 
         //bool IsFill = false;
         //bool IsFill = false;
         //bool IsXuanZhuanCloseFaMen = false;
         //bool IsXuanZhuanCloseFaMen = false;
@@ -754,71 +811,85 @@ namespace CCDCount.DLL
             {
             {
                 //往数组中计数
                 //往数组中计数
                 var UseTime = (oneActive.EndCheckTime - oneActive.StartCheckTime).TotalMilliseconds;
                 var UseTime = (oneActive.EndCheckTime - oneActive.StartCheckTime).TotalMilliseconds;
-                var AllPictureUseTime = (oneActive.PictureEndReadTime - oneActive.PictureStartReadTime).TotalMilliseconds;
-                var PictureUsetime = (oneActive.EndCheckTime - oneActive.PictureStartReadTime).TotalMilliseconds;
+                var QutuYanshiTime = (DateTime.Now - oneActive.PictureEndReadTime).TotalMilliseconds;
                 if (BatchNumber!="") oneActive.BatchNumber = BatchNumber;
                 if (BatchNumber!="") oneActive.BatchNumber = BatchNumber;
                 LOG.log(string.Format("输出当前颗粒信息,颗粒编号:{0},开始时间:{1},第一张图像读取时间:{2},\n" +
                 LOG.log(string.Format("输出当前颗粒信息,颗粒编号:{0},开始时间:{1},第一张图像读取时间:{2},\n" +
-                    "识别耗时:{3},首尾取图时差:{4},颗粒状态:{5},通道数:{6}",
+                    "识别耗时:{3},尾图到输出的时间:{4},颗粒状态:{5},通道数:{6}",
                     oneActive.Num, oneActive.StartCheckTime.ToString("O"), oneActive.PictureStartReadTime.ToString("O"),
                     oneActive.Num, oneActive.StartCheckTime.ToString("O"), oneActive.PictureStartReadTime.ToString("O"),
-                    UseTime.ToString(), AllPictureUseTime.ToString(), oneActive.StateCode, oneActive.ChannelNO), 6);
-                if (UseTime- AllPictureUseTime > 20)
-                    LOG.error("算法耗时超过了20ms");
-                switch(oneActive.StateCode)
+                    UseTime.ToString(), QutuYanshiTime.ToString(), oneActive.StateCode, oneActive.ChannelNO), 6);
+                if (QutuYanshiTime > 20)
                 {
                 {
-                    case 1:
-                        SystemAlarm.AlarmAlert(AlarmMessageList.数粒超长粒,
-                            "Counting extra-long particles",
-                            "数粒超长粒",
-                            "MianThreadClass-Worker_OneGrainCompleted");
-                        break;
-                    case 2:
-                        SystemAlarm.AlarmAlert(AlarmMessageList.数粒超短粒,
-                            "Counting ultra-short particles",
-                            "数粒超短粒",
-                            "MianThreadClass-Worker_OneGrainCompleted");
-                        break;
-                    case 5:
-                        SystemAlarm.AlarmAlert(AlarmMessageList.数粒超大粒,
-                            "Counting extra-large particles",
-                            "数粒超大粒",
-                            "MianThreadClass-Worker_OneGrainCompleted");
-                        break;
-                    case 6:
-                        SystemAlarm.AlarmAlert(AlarmMessageList.数粒超小粒,
-                            "Counting ultra-small particles",
-                            "数粒超小粒",
-                            "MianThreadClass-Worker_OneGrainCompleted");
-                        break;
-                    case 8:
-                        SystemAlarm.AlarmAlert(AlarmMessageList.疑似叠粒,
-                            "Count overlapping particles",
-                            "疑似叠粒",
-                            "MianThreadClass-Worker_OneGrainCompleted");
-                        break;
-                    case 10:
-                        SystemAlarm.AlarmAlert(AlarmMessageList.丢帧颗粒,
-                            "Counting frame loss particles",
-                            "丢帧颗粒",
-                            "MianThreadClass-Worker_OneGrainCompleted");
-                        break;
+                    LOG.error($"识别延时超过了20ms,耗时{QutuYanshiTime}");
                 }
                 }
-
+                //switch (oneActive.StateCode)
+                //{
+                //    case 1:
+                //        SystemAlarm.AlarmAlert(AlarmMessageList.数粒超长粒,
+                //            "Counting extra-long particles",
+                //            "数粒超长粒",
+                //            "MianThreadClass-Worker_OneGrainCompleted");
+                //        break;
+                //    case 2:
+                //        SystemAlarm.AlarmAlert(AlarmMessageList.数粒超短粒,
+                //            "Counting ultra-short particles",
+                //            "数粒超短粒",
+                //            "MianThreadClass-Worker_OneGrainCompleted");
+                //        break;
+                //    case 5:
+                //        SystemAlarm.AlarmAlert(AlarmMessageList.数粒超大粒,
+                //            "Counting extra-large particles",
+                //            "数粒超大粒",
+                //            "MianThreadClass-Worker_OneGrainCompleted");
+                //        break;
+                //    case 6:
+                //        SystemAlarm.AlarmAlert(AlarmMessageList.数粒超小粒,
+                //            "Counting ultra-small particles",
+                //            "数粒超小粒",
+                //            "MianThreadClass-Worker_OneGrainCompleted");
+                //        break;
+                //    case 8:
+                //        SystemAlarm.AlarmAlert(AlarmMessageList.疑似叠粒,
+                //            "Count overlapping particles",
+                //            "疑似叠粒",
+                //            "MianThreadClass-Worker_OneGrainCompleted");
+                //        break;
+                //    case 10:
+                //        SystemAlarm.AlarmAlert(AlarmMessageList.丢帧颗粒,
+                //            "Counting frame loss particles",
+                //            "丢帧颗粒",
+                //            "MianThreadClass-Worker_OneGrainCompleted");
+                //        break;
+                //}
+                //ThreadPool.QueueUserWorkItem(_ =>
+                //{
+                //    if (actionMesSqliteDataClass != null)
+                //    {
+                //        try
+                //        {
+                //            actionMesSqliteDataClass.AddData(oneActive);
+                //        }
+                //        catch (Exception ex)
+                //        {
+                //            LOG.error("MianThread-Worker_OneGrainCompleted-Task:" + ex.Message);
+                //        }
+                //    }
+                //});
 
 
                 //记录到数据库
                 //记录到数据库
-                ThreadPool.QueueUserWorkItem(_ =>
-                {
-                    if (actionMesSqliteDataClass != null)
-                    {
-                        try
-                        {
-                            actionMesSqliteDataClass.InsertActiveObject(oneActive);
-                        }
-                        catch (Exception ex)
-                        {
-                            LOG.error("MianThread-Worker_OneGrainCompleted-Task:" + ex.Message);
-                        }
-                    }
-                });
+                //ThreadPool.QueueUserWorkItem(_ =>
+                //{
+                //    if (actionMesSqliteDataClass != null)
+                //    {
+                //        try
+                //        {
+                //            actionMesSqliteDataClass.InsertActiveObject(oneActive);
+                //        }
+                //        catch (Exception ex)
+                //        {
+                //            LOG.error("MianThread-Worker_OneGrainCompleted-Task:" + ex.Message);
+                //        }
+                //    }
+                //});
 
 
                 if (!Hset.Add(oneActive.ChannelNO))
                 if (!Hset.Add(oneActive.ChannelNO))
                 {
                 {
@@ -911,8 +982,11 @@ namespace CCDCount.DLL
         private void StartSwitchThread()
         private void StartSwitchThread()
         {
         {
             IsSwitch = true;
             IsSwitch = true;
-
-            SwitchIdentifyImageThread = new Thread(SwitchIdentifyImageProcess);
+            shuLiClass.QueueSemaphore = new SemaphoreSlim(0);
+            SwitchIdentifyImageThread = new Thread(SwitchIdentifyImageProcess)
+            {
+                Priority = ThreadPriority.AboveNormal
+            };
             SwitchIdentifyImageThread.Start();
             SwitchIdentifyImageThread.Start();
         }
         }
 
 
@@ -946,18 +1020,8 @@ namespace CCDCount.DLL
         /// </summary>
         /// </summary>
         private void SwitchIdentifyImageProcess()
         private void SwitchIdentifyImageProcess()
         {
         {
-            //Stopwatch stopwatch = Stopwatch.StartNew();
-            //stopwatch.Start();
             while (IsSwitch)
             while (IsSwitch)
             {
             {
-
-                //if (stopwatch.ElapsedMilliseconds > 1000)
-                //{
-                    //Console.WriteLine("交换线程-图像处理实例中的待识别图像缓存队列长度{0}", shuLiClass.ImageNum);
-                    //LOG.log(string.Format("交换线程-图像处理实例中的待识别图像缓存队列长度{0}", shuLiClass.ImageNum),6);
-                    //GC.Collect();
-                    //stopwatch.Restart();
-                //}
                 bool result = cameraClass.GetOnceImage(out IFrameOut IFramedata);
                 bool result = cameraClass.GetOnceImage(out IFrameOut IFramedata);
                 if (result)
                 if (result)
                 {
                 {
@@ -974,6 +1038,7 @@ namespace CCDCount.DLL
                     if (IFramedata == null)
                     if (IFramedata == null)
                         continue;
                         continue;
                     shuLiClass.SetOnceIdentifyImageData(IFramedata);
                     shuLiClass.SetOnceIdentifyImageData(IFramedata);
+                    shuLiClass.QueueSemaphore.Release(); // 通知处理线程有新数据
                 }
                 }
                 else
                 else
                     continue;
                     continue;
@@ -991,8 +1056,6 @@ namespace CCDCount.DLL
             try
             try
             {
             {
                 if(!IsConnectModbus) return result;
                 if(!IsConnectModbus) return result;
-
-                timeBeginPeriod(1); // 设置为1ms精度
                 IsSend = true;
                 IsSend = true;
                 SendBottLogicMessageThread = new Thread(SendBottLogicMessageProcess);
                 SendBottLogicMessageThread = new Thread(SendBottLogicMessageProcess);
                 SendBottLogicMessageThread.Start();
                 SendBottLogicMessageThread.Start();
@@ -1025,7 +1088,9 @@ namespace CCDCount.DLL
                 {
                 {
                     try
                     try
                     {
                     {
-                        SendBottLogicMessageThread.Interrupt();
+                        IsSend = false;
+                        SendBottLogicMessageThread.Join();
+                        //SendBottLogicMessageThread.Interrupt();
                     }
                     }
                     catch (ThreadStateException)
                     catch (ThreadStateException)
                     {
                     {
@@ -1125,7 +1190,7 @@ namespace CCDCount.DLL
                 if ((ReturnValue[0] & 1) == 0)
                 if ((ReturnValue[0] & 1) == 0)
                 {
                 {
                     WriteDoneTime.Restart();
                     WriteDoneTime.Restart();
-                    while (SendQueue.Count==0)
+                    while (SendQueue.Count==0&& IsSend)
                     {
                     {
                         //Thread.Sleep(1);
                         //Thread.Sleep(1);
                     }
                     }

+ 3 - 1
TestWork.DLL/PLCManagementClass.cs

@@ -34,7 +34,8 @@ namespace CCDCount.DLL
         {
         {
             Task.Run(() =>
             Task.Run(() =>
             {
             {
-                while (!modbusTcpClient.Connect(ipAddress))
+                int i = 10;
+                while (!modbusTcpClient.Connect(ipAddress)&&i>0)
                 {
                 {
                     isConnect = false;
                     isConnect = false;
                     //SystemAlarm.AlarmAlert(AlarmMessageList.PLC通讯连接失败, $"Modbus通讯连接失败,目标IP:{ipAddress}", "DLL:MainThreadClass-ConnectModbus");
                     //SystemAlarm.AlarmAlert(AlarmMessageList.PLC通讯连接失败, $"Modbus通讯连接失败,目标IP:{ipAddress}", "DLL:MainThreadClass-ConnectModbus");
@@ -42,6 +43,7 @@ namespace CCDCount.DLL
                         $"PLC communication connection failed, target IP:{ipAddress}",
                         $"PLC communication connection failed, target IP:{ipAddress}",
                         $"PLC通讯连接失败, IP地址:{ipAddress}",
                         $"PLC通讯连接失败, IP地址:{ipAddress}",
                         "DLL:MainThreadClass-ConnectModbus");
                         "DLL:MainThreadClass-ConnectModbus");
+                    i--;
                     Task.Delay(1000);
                     Task.Delay(1000);
                 }
                 }
                 SystemAlarm.AlarmCancel(AlarmMessageList.PLC通讯连接失败);
                 SystemAlarm.AlarmCancel(AlarmMessageList.PLC通讯连接失败);

+ 413 - 39
TestWork.DLL/ShuLiClass.cs

@@ -4,9 +4,11 @@ using CCDCount.MODEL.ResultModel;
 using CCDCount.MODEL.ShuLiModel;
 using CCDCount.MODEL.ShuLiModel;
 using LogClass;
 using LogClass;
 using MvCameraControl;
 using MvCameraControl;
+using Org.BouncyCastle.Utilities;
 using System;
 using System;
 using System.Collections.Concurrent;
 using System.Collections.Concurrent;
 using System.Collections.Generic;
 using System.Collections.Generic;
+using System.Data.Entity.Core.Common.CommandTrees.ExpressionBuilder;
 using System.Diagnostics;
 using System.Diagnostics;
 using System.Drawing;
 using System.Drawing;
 using System.IO;
 using System.IO;
@@ -14,6 +16,8 @@ using System.Linq;
 using System.Runtime.InteropServices.WindowsRuntime;
 using System.Runtime.InteropServices.WindowsRuntime;
 using System.Threading;
 using System.Threading;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
+using System.Windows.Media.Media3D;
+using static iTextSharp.text.pdf.AcroFields;
 
 
 namespace CCDCount.DLL
 namespace CCDCount.DLL
 {
 {
@@ -109,39 +113,40 @@ namespace CCDCount.DLL
         public bool ProcessImageSequence(IFrameOut image)
         public bool ProcessImageSequence(IFrameOut image)
         {
         {
             bool result = false;
             bool result = false;
+            ReadOnlySpan<byte> spanFromArr = image.Image.PixelData.AsSpan();
             for (int i = 0; i < image.Image.Height; i++)
             for (int i = 0; i < image.Image.Height; i++)
             {
             {
-                result = ProcessLine(image, i);
+                result = ProcessLine2(spanFromArr,image.HostTimeStamp,image.Image.Width, i);
                 currentLine += 1;
                 currentLine += 1;
             }
             }
 
 
-
-            //Stopwatch stopwatch = Stopwatch.StartNew();
             //识别到结果并输出
             //识别到结果并输出
-            //lock (_lockObj)
-            //{
+
             _rwLock.EnterReadLock();
             _rwLock.EnterReadLock();
             // 清理超时未更新的物体
             // 清理超时未更新的物体
             lostObjects = activeObjects
             lostObjects = activeObjects
                 .Where(o => (currentLine - o.LastSeenLine) > shuLiConfig.MAX_GAP || (o.LastSeenLine - o.StartLine) > shuLiConfig.MAX_Idetify_Height)
                 .Where(o => (currentLine - o.LastSeenLine) > shuLiConfig.MAX_GAP || (o.LastSeenLine - o.StartLine) > shuLiConfig.MAX_Idetify_Height)
                 .ToList();
                 .ToList();
             _rwLock.ExitReadLock();
             _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
             // 有物体转变为活跃物体,返回值转为true
             if (lostObjects.Count > 0)
             if (lostObjects.Count > 0)
             {
             {
+                Stopwatch stopwatch = Stopwatch.StartNew();
                 result = true;
                 result = true;
                 //stopwatch.Restart();
                 //stopwatch.Restart();
                 foreach (var item in lostObjects)
                 foreach (var item in lostObjects)
                 {
                 {
+
                     item.PictureEndReadTime = FromUnixTimestamp((long)image.HostTimeStamp);
                     item.PictureEndReadTime = FromUnixTimestamp((long)image.HostTimeStamp);
+                    DateTime NowTime = DateTime.Now;
+                    var AllPictureUseTime = (item.PictureEndReadTime - item.PictureStartReadTime).TotalMilliseconds;
+                    var PictureUsetime = (NowTime - item.PictureStartReadTime).TotalMilliseconds;
+                    if (PictureUsetime - AllPictureUseTime > 20)
+                        LOG.error($"算法耗时超过了20ms,相机取图总耗时:{AllPictureUseTime},从取第一张图到现在的耗时{PictureUsetime}");
                     //噪点判定
                     //噪点判定
                     if (item.Area < shuLiConfig.NoiseFilter_Threshold)
                     if (item.Area < shuLiConfig.NoiseFilter_Threshold)
                     {
                     {
@@ -159,8 +164,11 @@ namespace CCDCount.DLL
                     {
                     {
                         if((item.LastSeenLine - item.StartLine) < shuLiConfig.MAX_Idetify_Height)
                         if((item.LastSeenLine - item.StartLine) < shuLiConfig.MAX_Idetify_Height)
                         {
                         {
-                            var CalculationResult = SizeCalculation2(item.RowsData);
-                            item.MaxLength = CalculationResult.MaxLength;
+                            var CalculationResult = SizeCalculation(item.RowsData);
+                            if(CalculationResult!=null)
+                            {
+                                item.MaxLength = CalculationResult.Height;
+                            }
                             //item.hasSignificantConcavity = CalculationResult.hasSignificantConcavity;
                             //item.hasSignificantConcavity = CalculationResult.hasSignificantConcavity;
                             //item.concavityRatio = CalculationResult.concavityRatio;
                             //item.concavityRatio = CalculationResult.concavityRatio;
                             //if(CalculationResult.hasSignificantConcavity)
                             //if(CalculationResult.hasSignificantConcavity)
@@ -182,46 +190,46 @@ namespace CCDCount.DLL
                         {
                         {
                             if (item.StateCode == 8)
                             if (item.StateCode == 8)
                             {
                             {
-                                LOG.log(string.Format("颗粒编号{0}:疑似叠片或缺损", item.Num));
+                                //LOG.log(string.Format("颗粒编号{0}:疑似叠片或缺损", item.Num));
                             }
                             }
                             else if (item.StateCode == 7)
                             else if (item.StateCode == 7)
                             {
                             {
-                                LOG.log(string.Format("颗粒编号{0}:视野被遮挡", item.Num));
+                                //LOG.log(string.Format("颗粒编号{0}:视野被遮挡", item.Num));
                             }
                             }
                         }
                         }
                         else if (item.Area < shuLiConfig.MinArea
                         else if (item.Area < shuLiConfig.MinArea
                             && (shuLiConfig.PandingCode == 2 || shuLiConfig.PandingCode == 1))
                             && (shuLiConfig.PandingCode == 2 || shuLiConfig.PandingCode == 1))
                         {
                         {
                             item.StateCode = 6;
                             item.StateCode = 6;
-                            LOG.log(string.Format("颗粒编号{0}:面积过小", item.Num));
-                            Console.WriteLine("颗粒编号{0}:面积过小", item.Num);
+                            //LOG.log(string.Format("颗粒编号{0}:面积过小", item.Num));
+                            //Console.WriteLine("颗粒编号{0}:面积过小", item.Num);
                         }
                         }
                         else if (item.Area > shuLiConfig.MaxArea
                         else if (item.Area > shuLiConfig.MaxArea
                             && (shuLiConfig.PandingCode == 2 || shuLiConfig.PandingCode == 1))
                             && (shuLiConfig.PandingCode == 2 || shuLiConfig.PandingCode == 1))
                         {
                         {
                             item.StateCode = 5;
                             item.StateCode = 5;
-                            LOG.log(string.Format("颗粒编号{0}:面积过大", item.Num));
-                            Console.WriteLine("颗粒编号{0}:面积过大", item.Num);
+                            //LOG.log(string.Format("颗粒编号{0}:面积过大", item.Num));
+                            //Console.WriteLine("颗粒编号{0}:面积过大", item.Num);
                         }
                         }
                         else if (item.MaxLength < shuLiConfig.MIN_Object_LENGTH
                         else if (item.MaxLength < shuLiConfig.MIN_Object_LENGTH
                             && (shuLiConfig.PandingCode == 2 || shuLiConfig.PandingCode == 0))
                             && (shuLiConfig.PandingCode == 2 || shuLiConfig.PandingCode == 0))
                         {
                         {
                             item.StateCode = 2;
                             item.StateCode = 2;
-                            LOG.log(string.Format("颗粒编号{0}:超短粒", item.Num));
-                            Console.WriteLine("颗粒编号{0}:超短粒", item.Num);
+                            //LOG.log(string.Format("颗粒编号{0}:超短粒", item.Num));
+                            //Console.WriteLine("颗粒编号{0}:超短粒", item.Num);
                         }
                         }
                         else if (item.MaxLength > shuLiConfig.MAX_Object_LENGTH
                         else if (item.MaxLength > shuLiConfig.MAX_Object_LENGTH
                             && (shuLiConfig.PandingCode == 2 || shuLiConfig.PandingCode == 0))
                             && (shuLiConfig.PandingCode == 2 || shuLiConfig.PandingCode == 0))
                         {
                         {
                             item.StateCode = 1;
                             item.StateCode = 1;
-                            LOG.log(string.Format("颗粒编号{0}:超长粒", item.Num));
-                            Console.WriteLine("颗粒编号{0}:超长粒", item.Num);
+                            //LOG.log(string.Format("颗粒编号{0}:超长粒", item.Num));
+                            //Console.WriteLine("颗粒编号{0}:超长粒", item.Num);
                         }
                         }
                         else
                         else
                         {
                         {
                             item.StateCode = 0;
                             item.StateCode = 0;
-                            LOG.log(string.Format("颗粒编号{0}:正常粒", item.Num));
-                            Console.WriteLine("颗粒编号{0}:正常粒", item.Num);
+                            //LOG.log(string.Format("颗粒编号{0}:正常粒", item.Num));
+                            //Console.WriteLine("颗粒编号{0}:正常粒", item.Num);
                         }
                         }
                     }
                     }
                     if(item.StateCode != 7&&item.StateCode!=9)
                     if(item.StateCode != 7&&item.StateCode!=9)
@@ -234,11 +242,12 @@ namespace CCDCount.DLL
                     if(item.StateCode != 9)
                     if(item.StateCode != 9)
                         OneActive.Add(item);
                         OneActive.Add(item);
                 }
                 }
-                //stopwatch.Stop();
-                //if (stopwatch.ElapsedMilliseconds > 1)
-                //{
-                //    FaultLog.RecordErrorMessage($"ShuLiClass-ProcessImageSequence:图像结果分析-结果参数补全,此次识别耗时:{stopwatch.Elapsed}");
-                //}
+                stopwatch.Stop();
+                if (stopwatch.ElapsedMilliseconds > 1)
+                {
+                    Console.WriteLine($"ShuLiClass-ProcessImageSequence:图像结果分析-结果参数补全,此次识别耗时:{stopwatch.Elapsed}");
+                    //FaultLog.RecordErrorMessage($"ShuLiClass-ProcessImageSequence:图像结果分析-结果参数补全,此次识别耗时:{stopwatch.Elapsed}");
+                }
                 //stopwatch.Restart();
                 //stopwatch.Restart();
                 if (OneActive.Count > 0)
                 if (OneActive.Count > 0)
                 {
                 {
@@ -266,6 +275,199 @@ namespace CCDCount.DLL
             return result;
             return result;
         }
         }
 
 
+        /// <summary>
+        /// 处理图像序列的主入口
+        /// </summary>
+        /// <param name="image">图像像素数据</param>
+        /// <returns>检测到的物体总数</returns>
+        public bool ProcessImageSequence2(IFrameOut image)
+        {
+            bool result = false;
+            ReadOnlySpan<byte> spanFromArr = image.Image.PixelData.AsSpan();
+            for (int i = 0; i < image.Image.Height; i++)
+            {
+                result = ProcessLine2(spanFromArr, image.HostTimeStamp, image.Image.Width, i);
+                currentLine += 1;
+            }
+
+            // 优化: 减少锁的使用频率,批量处理逻辑
+            List<ActiveObjectClass> objectsToProcess = null;
+            _rwLock.EnterReadLock();
+            try
+            {
+                // 使用ToList()避免在锁内进行复杂查询
+                objectsToProcess = activeObjects
+                    .Where(o => (currentLine - o.LastSeenLine) > shuLiConfig.MAX_GAP ||
+                               (o.LastSeenLine - o.StartLine) > shuLiConfig.MAX_Idetify_Height)
+                    .ToList();
+            }
+            finally
+            {
+                _rwLock.ExitReadLock();
+            }
+
+            // 优化: 提前返回,避免不必要的处理
+            if (objectsToProcess.Count == 0)
+            {
+                return result;
+            }
+
+            // 优化: 预分配容量,减少内存重新分配
+            var processedObjects = new List<ActiveObjectClass>(objectsToProcess.Count);
+
+            // 优化: 将耗时的计算移到循环外
+            var endTime = DateTime.Now;
+            var pictureEndTime = FromUnixTimestamp((long)image.HostTimeStamp);
+
+            foreach (var item in objectsToProcess)
+            {
+                // 优化: 将耗时的计算移到循环外
+                ProcessSingleObject(item, pictureEndTime, endTime, processedObjects);
+            }
+
+            result = true;
+
+            // 优化: 批量操作减少锁的持有时间
+            if (processedObjects.Count > 0)
+            {
+                _rwLock.EnterWriteLock();
+                try
+                {
+                    // 批量转移对象
+                    foreach (var item in processedObjects)
+                    {
+                        TryAdd(historyActiveObjects, item, 2500);
+                    }
+
+                    // 批量移除
+                    foreach (var item in objectsToProcess)
+                    {
+                        activeObjects.Remove(item);
+                    }
+                }
+                finally
+                {
+                    _rwLock.ExitWriteLock();
+                }
+
+                // 优化: 异步触发事件,避免阻塞主线程
+                ThreadPool.QueueUserWorkItem(_ =>
+                {
+                    OnWorkCompleted(processedObjects);
+                });
+            }
+
+            return result;
+        }
+
+        // 抽取的单个对象处理方法
+        private void ProcessSingleObject(ActiveObjectClass item, DateTime pictureEndTime,
+                                       DateTime endTime,  List<ActiveObjectClass> processedObjects)
+        {
+            item.PictureEndReadTime = pictureEndTime;
+            var nowTime = DateTime.Now;
+            var ShiBieLuoHouTime = (nowTime - item.PictureEndReadTime).TotalMilliseconds;
+            // 优化: 将日志记录改为条件执行
+            if (ShiBieLuoHouTime > 30)
+            {
+                LOG.error($"算法落后了超过30ms,相机取图总耗时:{ShiBieLuoHouTime}");
+            }
+
+            // 快速滤除噪点
+            if (item.Area < shuLiConfig.NoiseFilter_Threshold)
+            {
+                item.StateCode = 9;
+                return;
+            }
+
+            // 计算长度
+            if (item.StartLine == item.LastSeenLine)
+            {
+                item.MaxLength = item.MaxEndCol - item.MinStartCol;
+                item.StateCode = 10;
+                LOG.log(string.Format("颗粒{0}的有效高度仅一行", item.Num));
+            }
+            else
+            {
+                if ((item.LastSeenLine - item.StartLine) < shuLiConfig.MAX_Idetify_Height)
+                {
+                    var calculationResult = SizeCalculation(item.RowsData);
+                    if(calculationResult!=null)
+                    {
+                        item.MaxLength = calculationResult.Height;
+                    }
+                }
+                else
+                {
+                    item.StateCode = 7;
+                }
+            }
+
+            // 分类处理
+            ApplyClassificationRules(item);
+
+            // 添加到历史记录
+            if (item.StateCode != 7 && item.StateCode != 9)
+            {
+                item.Num = Interlocked.Increment(ref ObjectNum);
+                item.ChannelNO = ActiveChannel(item);
+            }
+
+            item.EndCheckTime = endTime;
+
+            if (item.StateCode != 9)
+            {
+                processedObjects.Add(item);
+            }
+        }
+
+        // 抽取分类规则,便于维护
+        private void ApplyClassificationRules(ActiveObjectClass item)
+        {
+            if (shuLiConfig.PandingCode == -1) return;
+
+            if (item.StateCode != -1)
+            {
+                if (item.StateCode == 8)
+                {
+                    LOG.log(string.Format("颗粒编号{0}:疑似叠片或缺损", item.Num));
+                }
+                else if (item.StateCode == 7)
+                {
+                    LOG.log(string.Format("颗粒编号{0}:视野被遮挡", item.Num));
+                }
+            }
+            else if (item.Area < shuLiConfig.MinArea &&
+                     (shuLiConfig.PandingCode == 2 || shuLiConfig.PandingCode == 1))
+            {
+                item.StateCode = 6;
+                LOG.log(string.Format("颗粒编号{0}:面积过小", item.Num));
+            }
+            else if (item.Area > shuLiConfig.MaxArea &&
+                     (shuLiConfig.PandingCode == 2 || shuLiConfig.PandingCode == 1))
+            {
+                item.StateCode = 5;
+                LOG.log(string.Format("颗粒编号{0}:面积过大", item.Num));
+            }
+            else if (item.MaxLength < shuLiConfig.MIN_Object_LENGTH &&
+                     (shuLiConfig.PandingCode == 2 || shuLiConfig.PandingCode == 0))
+            {
+                item.StateCode = 2;
+                LOG.log(string.Format("颗粒编号{0}:超短粒", item.Num));
+            }
+            else if (item.MaxLength > shuLiConfig.MAX_Object_LENGTH &&
+                     (shuLiConfig.PandingCode == 2 || shuLiConfig.PandingCode == 0))
+            {
+                item.StateCode = 1;
+                LOG.log(string.Format("颗粒编号{0}:超长粒", item.Num));
+            }
+            else
+            {
+                item.StateCode = 0;
+                LOG.log(string.Format("颗粒编号{0}:正常粒", item.Num));
+            }
+        }
+
         /// <summary>
         /// <summary>
         /// 返回最后一个历史物品
         /// 返回最后一个历史物品
         /// </summary>
         /// </summary>
@@ -360,7 +562,7 @@ namespace CCDCount.DLL
                 // 打开识别线程
                 // 打开识别线程
                 IdentifyImageProcessThread = new Thread(IdentifyImageProcess)
                 IdentifyImageProcessThread = new Thread(IdentifyImageProcess)
                 {
                 {
-                    Priority = ThreadPriority.Highest
+                    Priority = ThreadPriority.AboveNormal
                 };
                 };
                 IdentifyImageProcessThread.Start();
                 IdentifyImageProcessThread.Start();
                 SystemAlarm.AlarmCancel(AlarmMessageList.识别线程启动失败);
                 SystemAlarm.AlarmCancel(AlarmMessageList.识别线程启动失败);
@@ -376,6 +578,31 @@ namespace CCDCount.DLL
             }
             }
         }
         }
 
 
+        public void StartIdentifyFuntion2(int ImaageWidth)
+        {
+            UpdateIdentifyImageWidth(ImaageWidth);
+            InitChannel();
+            try
+            {
+                CancellationTokenSource = new CancellationTokenSource();
+                var token = CancellationTokenSource.Token;
+
+                // 启动图像处理线程
+                ProcessingTask = Task.Factory.StartNew(() => IdentifyImageProcessTask(token),
+                    token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
+                SystemAlarm.AlarmCancel(AlarmMessageList.识别线程启动失败);
+            }
+            catch (Exception ex)
+            {
+                SystemAlarm.AlarmAlert(AlarmMessageList.识别线程启动失败,
+                    "Start thread failed!, " + ex.Message,
+                    "识别线程启动失败, " + ex.Message,
+                    "DLL:ShuLiClass-StartIdentifyFuntion");
+                //FaultLog.RecordErrorMessage("Start thread failed!, " + ex.Message);
+                throw;
+            }
+        }
+
         /// <summary>
         /// <summary>
         /// 关闭识别
         /// 关闭识别
         /// </summary>
         /// </summary>
@@ -399,6 +626,35 @@ namespace CCDCount.DLL
                 throw;
                 throw;
             }
             }
         }
         }
+        public async void StopIdentifyFuntion2()
+        {
+            try
+            {
+                // 发送取消信号
+                CancellationTokenSource.Cancel();
+
+                // 等待线程完成
+                try
+                {
+                    if (ProcessingTask != null)
+                        await ProcessingTask;
+                }
+                catch (OperationCanceledException)
+                {
+                    // 正常取消,忽略异常
+                }
+                SystemAlarm.AlarmCancel(AlarmMessageList.识别线程停止失败);
+            }
+            catch (Exception ex)
+            {
+                //FaultLog.RecordErrorMessage("Stop thread failed!, " + ex.Message);
+                SystemAlarm.AlarmAlert(AlarmMessageList.识别线程停止失败,
+                    "Stop thread failed!, " + ex.Message,
+                    "识别线程停止失败, " + ex.Message,
+                    "DLL:ShuLiClass-StopIdentifyFuntion");
+                throw;
+            }
+        }
 
 
         /// <summary>
         /// <summary>
         /// 向识别队列添加一个数据
         /// 向识别队列添加一个数据
@@ -407,6 +663,7 @@ namespace CCDCount.DLL
         public void SetOnceIdentifyImageData(IFrameOut items)
         public void SetOnceIdentifyImageData(IFrameOut items)
         {
         {
             IFrameDatas.Enqueue(items.Clone() as IFrameOut);
             IFrameDatas.Enqueue(items.Clone() as IFrameOut);
+            //IFrameDatas.Enqueue(items );
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -679,7 +936,7 @@ namespace CCDCount.DLL
         }
         }
 
 
         private bool IsPrintLightOnError = false;
         private bool IsPrintLightOnError = false;
-
+        List<ValidRegionModelClass> currentRegions = null;
         /// <summary>
         /// <summary>
         /// 处理单行像素数据
         /// 处理单行像素数据
         /// 返回值为false的时候无活跃物体转变为历史物体
         /// 返回值为false的时候无活跃物体转变为历史物体
@@ -807,19 +1064,20 @@ namespace CCDCount.DLL
             //}
             //}
             return result;
             return result;
         }
         }
-        private bool ProcessLine2(IFrameOut imagedata, int RowNo)
+        private bool ProcessLine2(ReadOnlySpan<byte> image, ulong ImageHostTime, uint imageWidth, int RowNo)
         {
         {
             // 缓存配置值
             // 缓存配置值
             int maxGap = shuLiConfig.MAX_GAP;
             int maxGap = shuLiConfig.MAX_GAP;
             long currentTimeLine = currentLine;
             long currentTimeLine = currentLine;
 
 
             // 步骤1:检测当前行的有效区域
             // 步骤1:检测当前行的有效区域
-            var currentRegions = FindValidRegions(imagedata.Image, RowNo);
-            if (currentRegions.Count == 0) return false;
+            currentRegions = FindValidRegions(image, (int)imageWidth, RowNo);
+
+            if (currentRegions.Count == 0|| currentRegions == null) return false;
 
 
             if (currentRegions.Count == 1)
             if (currentRegions.Count == 1)
             {
             {
-                if (currentRegions[0].End - (currentRegions[0]).Start + 1 == imagedata.Image.Width)
+                if (currentRegions[0].End - (currentRegions[0]).Start + 1 == imageWidth)
                 {
                 {
                     if (!IsPrintLightOnError)
                     if (!IsPrintLightOnError)
                     {
                     {
@@ -869,6 +1127,7 @@ namespace CCDCount.DLL
                         LastSeenLineEndCol = aggregateResult.maxLastSeenEndCol,
                         LastSeenLineEndCol = aggregateResult.maxLastSeenEndCol,
                         StartCheckTime = aggregateResult.minStartTime,
                         StartCheckTime = aggregateResult.minStartTime,
                         EndCheckTime = aggregateResult.maxEndTime,
                         EndCheckTime = aggregateResult.maxEndTime,
+                        PictureStartReadTime = aggregateResult.pictureMinReadTime,
                         Area = aggregateResult.totalArea,
                         Area = aggregateResult.totalArea,
                         RowsData = aggregateResult.mergedRowsData,
                         RowsData = aggregateResult.mergedRowsData,
                         ImageWidth = aggregateResult.imageWidth,
                         ImageWidth = aggregateResult.imageWidth,
@@ -913,7 +1172,7 @@ namespace CCDCount.DLL
                             LastSeenLineStartCol = region.Start,
                             LastSeenLineStartCol = region.Start,
                             LastSeenLineEndCol = region.End,
                             LastSeenLineEndCol = region.End,
                             StartCheckTime = DateTime.Now,
                             StartCheckTime = DateTime.Now,
-                            PictureStartReadTime = FromUnixTimestamp((long)imagedata.HostTimeStamp),
+                            PictureStartReadTime = FromUnixTimestamp((long)ImageHostTime),
                             Area = region.End - region.Start + 1,
                             Area = region.End - region.Start + 1,
                             ImageWidth = IdentifyImageWidth,
                             ImageWidth = IdentifyImageWidth,
                             RowsData = new List<RowStartEndCol> {
                             RowsData = new List<RowStartEndCol> {
@@ -933,6 +1192,7 @@ namespace CCDCount.DLL
                 }
                 }
             }
             }
 
 
+            currentRegions.Clear();
             // 执行所有操作(最小化锁持有时间)
             // 执行所有操作(最小化锁持有时间)
             bool hasChanges = false;
             bool hasChanges = false;
             if (operations.Count > 0)
             if (operations.Count > 0)
@@ -1008,7 +1268,7 @@ namespace CCDCount.DLL
         // 计算聚合值的方法
         // 计算聚合值的方法
         private (int minStartCol, int maxEndCol, long minStartLine, long maxLastSeenLine,
         private (int minStartCol, int maxEndCol, long minStartLine, long maxLastSeenLine,
                  int minLastSeenStartCol, int maxLastSeenEndCol, DateTime minStartTime,
                  int minLastSeenStartCol, int maxLastSeenEndCol, DateTime minStartTime,
-                 DateTime maxEndTime, int totalArea, List<RowStartEndCol> mergedRowsData, int imageWidth)
+                 DateTime maxEndTime,DateTime pictureMinReadTime,int totalArea, List<RowStartEndCol> mergedRowsData, int imageWidth)
         CalculateAggregates(List<ActiveObjectClass> objects)
         CalculateAggregates(List<ActiveObjectClass> objects)
         {
         {
             int minStartCol = int.MaxValue;
             int minStartCol = int.MaxValue;
@@ -1019,6 +1279,7 @@ namespace CCDCount.DLL
             int maxLastSeenEndCol = int.MinValue;
             int maxLastSeenEndCol = int.MinValue;
             DateTime minStartTime = DateTime.MaxValue;
             DateTime minStartTime = DateTime.MaxValue;
             DateTime maxEndTime = DateTime.MinValue;
             DateTime maxEndTime = DateTime.MinValue;
+            DateTime pictureMinReadTime = DateTime.MaxValue;
             int totalArea = 0;
             int totalArea = 0;
             int imageWidth = objects.FirstOrDefault()?.ImageWidth ?? IdentifyImageWidth;
             int imageWidth = objects.FirstOrDefault()?.ImageWidth ?? IdentifyImageWidth;
 
 
@@ -1035,6 +1296,7 @@ namespace CCDCount.DLL
 
 
                 if (obj.StartCheckTime < minStartTime) minStartTime = obj.StartCheckTime;
                 if (obj.StartCheckTime < minStartTime) minStartTime = obj.StartCheckTime;
                 if (obj.EndCheckTime > maxEndTime) maxEndTime = obj.EndCheckTime;
                 if (obj.EndCheckTime > maxEndTime) maxEndTime = obj.EndCheckTime;
+                if (obj.PictureStartReadTime<pictureMinReadTime) pictureMinReadTime = obj.PictureStartReadTime;
 
 
                 totalArea += obj.Area;
                 totalArea += obj.Area;
                 mergedRowsData.AddRange(obj.RowsData);
                 mergedRowsData.AddRange(obj.RowsData);
@@ -1042,7 +1304,7 @@ namespace CCDCount.DLL
 
 
             return (minStartCol, maxEndCol, minStartLine, maxLastSeenLine,
             return (minStartCol, maxEndCol, minStartLine, maxLastSeenLine,
                     minLastSeenStartCol, maxLastSeenEndCol, minStartTime,
                     minLastSeenStartCol, maxLastSeenEndCol, minStartTime,
-                    maxEndTime, totalArea, mergedRowsData, imageWidth);
+                    maxEndTime,pictureMinReadTime,totalArea, mergedRowsData, imageWidth);
         }
         }
 
 
         List<ValidRegionModelClass> regions = new List<ValidRegionModelClass>();
         List<ValidRegionModelClass> regions = new List<ValidRegionModelClass>();
@@ -1116,6 +1378,48 @@ namespace CCDCount.DLL
             return new List<ValidRegionModelClass>(regions);
             return new List<ValidRegionModelClass>(regions);
         }
         }
 
 
+        private List<ValidRegionModelClass> FindValidRegions(ReadOnlySpan<byte> image,int imageWidth, int RowNo)
+        {
+            regions.Clear();
+            regions.Capacity = 10;
+
+            ReadOnlySpan<byte> rowSpan;
+            if (shuLiConfig.IsIdentifyRoiOpen)
+            {
+                int offset = RowNo * imageWidth + shuLiConfig.IdentifyStartX;
+                int length = Math.Min(shuLiConfig.IdentifyStopX - shuLiConfig.IdentifyStartX,
+                                     imageWidth - shuLiConfig.IdentifyStartX);
+                rowSpan = image.Slice(offset, length);
+            }
+            else
+            {
+                rowSpan = image.Slice(RowNo * imageWidth, imageWidth);
+            }
+
+            int start = -1;
+            byte threshold = (byte)shuLiConfig.RegionThreshold;
+
+            for (int i = 0; i < rowSpan.Length; i++)
+            {
+                if (rowSpan[i] < threshold)
+                {
+                    if (start == -1) start = i;
+                }
+                else if (start != -1)
+                {
+                    regions.Add(new ValidRegionModelClass { Start = start, End = i - 1 });
+                    start = -1;
+                }
+            }
+
+            if (start != -1)
+            {
+                regions.Add(new ValidRegionModelClass { Start = start, End = rowSpan.Length - 1 });
+            }
+
+            return new List<ValidRegionModelClass>(regions);
+        }
+
         /// <summary>
         /// <summary>
         /// 判断区域重叠(与活跃物体的横向坐标重叠检测)
         /// 判断区域重叠(与活跃物体的横向坐标重叠检测)
         /// </summary>
         /// </summary>
@@ -1605,6 +1909,12 @@ namespace CCDCount.DLL
         #endregion
         #endregion
 
 
         #region 线程方法
         #region 线程方法
+        //信号量
+        public SemaphoreSlim QueueSemaphore { get; set; }
+        //取消令牌
+        public CancellationTokenSource CancellationTokenSource { get; set; }
+        //图像处理线程
+        public Task ProcessingTask { get; set; }
         /// <summary>
         /// <summary>
         /// 识别图像线程
         /// 识别图像线程
         /// </summary>
         /// </summary>
@@ -1635,6 +1945,12 @@ namespace CCDCount.DLL
                     }
                     }
                     IFrameDatas.TryDequeue(out IFrameOut IframeData);
                     IFrameDatas.TryDequeue(out IFrameOut IframeData);
                     stopwatch.Stop();
                     stopwatch.Stop();
+                    var ShiBieLuoHouTime = (DateTime.Now - FromUnixTimestamp((long)IframeData.HostTimeStamp)).TotalMilliseconds;
+                    // 优化: 将日志记录改为条件执行
+                    if (ShiBieLuoHouTime > 30)
+                    {
+                        LOG.error($"算法落后了超过30ms,相机取图总耗时:{ShiBieLuoHouTime}");
+                    }
                     if (stopwatch.ElapsedMilliseconds > 5)
                     if (stopwatch.ElapsedMilliseconds > 5)
                     {
                     {
                         FaultLog.RecordErrorMessage($"ShuLiClass-IdentifyImageProcess:Image reading timed out, this recognition took time:{stopwatch.Elapsed}");
                         FaultLog.RecordErrorMessage($"ShuLiClass-IdentifyImageProcess:Image reading timed out, this recognition took time:{stopwatch.Elapsed}");
@@ -1642,13 +1958,71 @@ namespace CCDCount.DLL
                     if (IframeData != null)
                     if (IframeData != null)
                     {
                     {
                         //识别
                         //识别
-                        ProcessImageSequence(IframeData);
+                        ProcessImageSequence2(IframeData);
+                        IframeData.Dispose();
+                    }
+                    else
+                    {
+                        continue;
+                    }
+                }
+                else
+                {
+                    Thread.Sleep(1);
+                }
+            }
+        }
+
+        private async void IdentifyImageProcessTask(CancellationToken token)
+        {
+            Stopwatch stopwatch = Stopwatch.StartNew();
+            const int batchSize = 5; // 每批处理的最大图像数量
+            var batch = new List<IFrameOut>(batchSize); // 存储当前批次的图像
+            while (!token.IsCancellationRequested)
+            {
+                // 等待图像数据
+                await QueueSemaphore.WaitAsync(token);
+                //判断队列中是否有数据
+                if (IFrameDatas.Count() > 5)
+                {
+                    //FaultLog.RecordErrorMessage($"图像数据队列中数据过多,请及时处理!当前数据数量为:{IFrameDatas.Count()}");
+                    FaultLog.RecordErrorMessage($"There is too much data in the image data queue. Please handle it in a timely manner! The current data quantity is:{IFrameDatas.Count()}");
+                    if (IFrameDatas.Count() > 100)
+                    {
+                        SystemAlarm.AlarmAlert(AlarmMessageList.待识别队列数据堆积,
+                            $"The image data queue is blocked. Please check the configuration and images",
+                            "待识别队列数据堆积,请检查设置和图像",
+                            "DLL:ShuLIClass-IdentifyImageProcess");
+                    }
+                    else
+                    {
+                        SystemAlarm.AlarmCancel(AlarmMessageList.待识别队列数据堆积);
+                    }
+                }
+                // 收集一批图像(严格按队列顺序)
+                while (batch.Count < batchSize && IFrameDatas.TryDequeue(out IFrameOut imageData))
+                {
+                    batch.Add(imageData);
+                    if (IFrameDatas.Count > 0)
+                    {
+                        QueueSemaphore.Release(); // 提前释放信号量,唤醒其他等待线程
+                    }
+                }
+                // 顺序处理当前批次的所有图像
+                foreach (var image in batch)
+                {
+                    if (image != null)
+                    {
+                        //识别
+                        ProcessImageSequence2(image);
+                        image.Dispose();
                     }
                     }
                     else
                     else
                     {
                     {
                         continue;
                         continue;
                     }
                     }
                 }
                 }
+                batch.Clear();
             }
             }
         }
         }
         #endregion
         #endregion

+ 111 - 2
TestWork.DLL/SqlDataClass/ActionMesSqliteDataClass.cs

@@ -1,8 +1,11 @@
-using System;
+using CCDCount.MODEL.ShuLiModel; // 确保引用ActiveObjectClass所在命名空间
+using System;
+using System.Collections.Concurrent;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Data.SQLite;
 using System.Data.SQLite;
 using System.IO;
 using System.IO;
-using CCDCount.MODEL.ShuLiModel; // 确保引用ActiveObjectClass所在命名空间
+using System.Text;
+using System.Threading;
 
 
 namespace CCDCount.DLL.SqlDataClass
 namespace CCDCount.DLL.SqlDataClass
 {
 {
@@ -12,6 +15,11 @@ namespace CCDCount.DLL.SqlDataClass
         private readonly string _connectionString;
         private readonly string _connectionString;
         private object locker = new object();
         private object locker = new object();
 
 
+        private readonly ConcurrentQueue<ActiveObjectClass> _dataQueue = new ConcurrentQueue<ActiveObjectClass>();
+        private readonly Timer _timer;
+        private const int MaxBatchSize = 1000; // 单次最大插入条数(受 SQLite 参数上限限制)
+        private const int FlushIntervalMs = 5000; // 最大等待时间(毫秒)
+
         public ActionMesSqliteDataClass(string dbPath)
         public ActionMesSqliteDataClass(string dbPath)
         {
         {
             DatabaseFile = dbPath;
             DatabaseFile = dbPath;
@@ -28,6 +36,7 @@ namespace CCDCount.DLL.SqlDataClass
                 Directory.CreateDirectory(directoryPath);
                 Directory.CreateDirectory(directoryPath);
             }
             }
             InitializeDatabase();
             InitializeDatabase();
+            _timer = new Timer(FlushData, null, FlushIntervalMs, FlushIntervalMs);
         }
         }
 
 
         private void InitializeDatabase()
         private void InitializeDatabase()
@@ -88,6 +97,99 @@ namespace CCDCount.DLL.SqlDataClass
         }
         }
 
 
         // 插入ActiveObjectClass对象及其关联数据
         // 插入ActiveObjectClass对象及其关联数据
+        public void AddData(ActiveObjectClass data)
+        {
+            _dataQueue.Enqueue(data);
+
+            // 如果缓存数量达到上限,则立即触发写入
+            if (_dataQueue.Count >= MaxBatchSize)
+            {
+                FlushData(null);
+            }
+        }
+
+        // 强制刷新缓存数据到数据库
+        private void FlushData(object state)
+        {
+            lock (locker)
+            {
+                if (_dataQueue.IsEmpty) return;
+
+                var batch = new List<ActiveObjectClass>();
+                while (_dataQueue.TryDequeue(out var item))
+                {
+                    batch.Add(item);
+                }
+
+                WriteBatchToDatabase(batch);
+            }
+        }
+        private void WriteBatchToDatabase(List<ActiveObjectClass> batch)
+        {
+            using (var conn = new SQLiteConnection(_connectionString))
+            {
+                conn.Open();
+                using (var transaction = conn.BeginTransaction())
+                {
+                    try
+                    {
+                        // 构建批量插入语句
+                        var sqlBuilder = new StringBuilder();
+                        sqlBuilder.Append(@"
+                        INSERT INTO ActiveObject (
+                            Num, MinStartCol, MaxEndCol, LastSeenLineStartCol, 
+                            LastSeenLineEndCol, StartLine, LastSeenLine, 
+                            StartCheckTime, EndCheckTime, Area, MaxLength,
+                            ChannelNO, ImageWidth, StateCode, BatchNumber,
+                            hasSignificantConcavity, concavityRatio
+                        ) VALUES ");
+
+                        var parameters = new List<object>();
+                        for (int i = 0; i < batch.Count; i++)
+                        {
+                            var obj = batch[i];
+                            sqlBuilder.Append($"(@p{i * 17}, @p{i * 17 + 1}, @p{i * 17 + 2}, @p{i * 17 + 3}, " +
+                                              $"@p{i * 17 + 4}, @p{i * 17 + 5}, @p{i * 17 + 6}, " +
+                                              $"@p{i * 17 + 7}, @p{i * 17 + 8}, @p{i * 17 + 9}, " +
+                                              $"@p{i * 17 + 10}, @p{i * 17 + 11}, @p{i * 17 + 12}, " +
+                                              $"@p{i * 17 + 13}, @p{i * 17 + 14}, @p{i * 17 + 15}, @p{i * 17 + 16})");
+
+                            if (i < batch.Count - 1)
+                                sqlBuilder.Append(", ");
+
+                            // 绑定参数值
+                            parameters.AddRange(new object[]
+                            {
+                            obj.Num, obj.MinStartCol, obj.MaxEndCol, obj.LastSeenLineStartCol,
+                            obj.LastSeenLineEndCol, obj.StartLine, obj.LastSeenLine,
+                            obj.StartCheckTime.ToString("o"), obj.EndCheckTime.ToString("o"),
+                            obj.Area, obj.MaxLength, obj.ChannelNO, obj.ImageWidth,
+                            obj.StateCode, obj.BatchNumber, obj.hasSignificantConcavity, obj.concavityRatio
+                            });
+                        }
+
+                        // 执行批量插入
+                        using (var cmd = new SQLiteCommand(sqlBuilder.ToString(), conn))
+                        {
+                            for (int i = 0; i < parameters.Count; i++)
+                            {
+                                cmd.Parameters.AddWithValue($"@p{i}", parameters[i]);
+                            }
+
+                            cmd.ExecuteNonQuery();
+                        }
+
+                        transaction.Commit();
+                    }
+                    catch
+                    {
+                        transaction.Rollback();
+                        throw;
+                    }
+                }
+            }
+        }
+
         public void InsertActiveObject(ActiveObjectClass activeObject)
         public void InsertActiveObject(ActiveObjectClass activeObject)
         {
         {
             lock (locker)
             lock (locker)
@@ -470,5 +572,12 @@ namespace CCDCount.DLL.SqlDataClass
             }
             }
             return result;
             return result;
         }
         }
+
+        // 关闭定时器并释放资源
+        public void Dispose()
+        {
+            _timer?.Dispose();
+            FlushData(null); // 确保最后一批数据被写入
+        }
     }
     }
 }
 }

+ 1 - 1
TestWork.DLL/Tools/LogClass.cs

@@ -19,7 +19,7 @@ namespace LogClass
         }
         }
         static public void error(object str)
         static public void error(object str)
         {
         {
-            log("Erorr:" + str, 10);
+            log("Error:" + str, 10);
         }
         }
         //static object syn = new object();
         //static object syn = new object();
         /// <summary>
         /// <summary>

+ 16 - 10
WpfSwitchLanguage/WpfPage/MainPage.xaml.cs

@@ -148,8 +148,14 @@ namespace CCDCountWpf.WpfPage
                         DateTime RecordTime = DateTime.Now;
                         DateTime RecordTime = DateTime.Now;
                         var OutTimeBatch = batchRecordModels.Where(x => x.RecordTime.AddMinutes(1) < RecordTime).ToList();
                         var OutTimeBatch = batchRecordModels.Where(x => x.RecordTime.AddMinutes(1) < RecordTime).ToList();
                         OutTimeBatch.ForEach(x => batchRecordModels.Remove(x));
                         OutTimeBatch.ForEach(x => batchRecordModels.Remove(x));
-                        PlcSettingMessageBus.pLCManagement.ReadBottingVibrationTableHighSpeedValue(out ushort BottingVibrationTableHighSpeedValue, out ushort FilterVibrationTableHighSpeedValue, out ushort CountVibrationTableHighSpeedValue);
-                        PlcSettingMessageBus.pLCManagement.ReadBottingCount(out ushort BottingCount);
+                        if(!PlcSettingMessageBus.pLCManagement.ReadBottingVibrationTableHighSpeedValue(out ushort BottingVibrationTableHighSpeedValue, out ushort FilterVibrationTableHighSpeedValue, out ushort CountVibrationTableHighSpeedValue))
+                        {
+                            continue ;
+                        }
+                        if(!PlcSettingMessageBus.pLCManagement.ReadBottingCount(out ushort BottingCount))
+                        {
+                            continue ;
+                        }
                         ushort CountSPeed = (ushort)(batchRecordModels.Count() > 0 ? (BottingCount - batchRecordModels.Min(o => o.BottingCount)) > 0 ? BottingCount - batchRecordModels.Min(o => o.BottingCount) : 0 : 0);
                         ushort CountSPeed = (ushort)(batchRecordModels.Count() > 0 ? (BottingCount - batchRecordModels.Min(o => o.BottingCount)) > 0 ? BottingCount - batchRecordModels.Min(o => o.BottingCount) : 0 : 0);
                         ShowMessageBus.ShowBinding.BottingSpeed = CountSPeed;
                         ShowMessageBus.ShowBinding.BottingSpeed = CountSPeed;
                         BatchRecordModel batchRecordModel = new BatchRecordModel
                         BatchRecordModel batchRecordModel = new BatchRecordModel
@@ -186,13 +192,13 @@ namespace CCDCountWpf.WpfPage
 
 
         private void StartIdentifyBtn_Click(object sender, RoutedEventArgs e)
         private void StartIdentifyBtn_Click(object sender, RoutedEventArgs e)
         {
         {
-            if(String.IsNullOrWhiteSpace(ShowMessageBus.ShowBinding.BatchNumber))
+            if (String.IsNullOrWhiteSpace(ShowMessageBus.ShowBinding.BatchNumber))
             {
             {
                 MessageBox.Show("请输入批次号!");
                 MessageBox.Show("请输入批次号!");
                 return;
                 return;
             }
             }
             RunBatchRecord(ShowMessageBus.ShowBinding.BatchNumber);
             RunBatchRecord(ShowMessageBus.ShowBinding.BatchNumber);
-            if(!RunCameraIdentify())
+            if (!RunCameraIdentify())
             {
             {
                 return;
                 return;
             }
             }
@@ -200,7 +206,7 @@ namespace CCDCountWpf.WpfPage
             StartIdentifyBtn.Opacity = 0.5;
             StartIdentifyBtn.Opacity = 0.5;
             StopIdentifyBtn.IsEnabled = true;
             StopIdentifyBtn.IsEnabled = true;
             StopIdentifyBtn.Opacity = 1;
             StopIdentifyBtn.Opacity = 1;
-            if(PlcSettingMessageBus.pLCManagement.IsConnect)
+            if (PlcSettingMessageBus.pLCManagement.IsConnect)
             {
             {
                 PlcSettingMessageBus.pLCManagement.MachineRunToTrue();
                 PlcSettingMessageBus.pLCManagement.MachineRunToTrue();
             }
             }
@@ -210,15 +216,15 @@ namespace CCDCountWpf.WpfPage
         {
         {
             StopCameraIdentify();
             StopCameraIdentify();
             LOG.log("停止相机成功");
             LOG.log("停止相机成功");
-            StopBatchRecord();
+            //StopBatchRecord();
             StartIdentifyBtn.IsEnabled = true;
             StartIdentifyBtn.IsEnabled = true;
             StartIdentifyBtn.Opacity = 1;
             StartIdentifyBtn.Opacity = 1;
             StopIdentifyBtn.IsEnabled = false;
             StopIdentifyBtn.IsEnabled = false;
             StopIdentifyBtn.Opacity = 0.5;
             StopIdentifyBtn.Opacity = 0.5;
-            if (PlcSettingMessageBus.pLCManagement.IsConnect)
-            {
-                PlcSettingMessageBus.pLCManagement.MachineStopToTrue();
-            }
+            //if (PlcSettingMessageBus.pLCManagement.IsConnect)
+            //{
+            //    PlcSettingMessageBus.pLCManagement.MachineStopToTrue();
+            //}
         }
         }
 
 
         private void BottingMaterialCylinderVibrationTableHighSpeedValueUpUpBtn_Click(object sender, RoutedEventArgs e)
         private void BottingMaterialCylinderVibrationTableHighSpeedValueUpUpBtn_Click(object sender, RoutedEventArgs e)