using CCDCount.DLL.AlarmTools; using CCDCount.DLL.CanBus; using CCDCount.DLL.SqlDataClass; using CCDCount.DLL.Tools; using CCDCount.MODEL.CameraClass; using CCDCount.MODEL.ConfigModel; using CCDCount.MODEL.ResultModel; using CCDCount.MODEL.ShuLiModel; using LogClass; using MvCameraControl; using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Data.Entity.Core.Common.CommandTrees.ExpressionBuilder; using System.Diagnostics; using System.Drawing; using System.Linq; using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; namespace CCDCount.DLL { public class MainThreadClass { #region 变量与实例 private bool IsSwitch = false; public ShuLiClass shuLiClass = null; CameraClass cameraClass = new CameraClass(); public ShuLiConfigClass shuLiConfig { get { return ConfigManager.Instance.SaveConfigModel.ShuLiConfigClass; } set { ConfigManager.Instance.SaveConfigModel.ShuLiConfigClass = value; } } public CameraConfig cameraConfig { get { return ConfigManager.Instance.SaveConfigModel.CameraConfig; } set { ConfigManager.Instance.SaveConfigModel.CameraConfig = value; } } 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; // 返回交换线程状态 public bool CameraRunStatic { get { return IsSwitch; } } public bool CameraSendMessageStatic { get { return IsSend; } } public bool IsOpenLoadThread { get { return _IsOpenLoadThread; }} private bool _IsOpenLoadThread = false; public int HistoryActiveNum { get { return shuLiClass.HistoryActiveNum; } } public int OkHistoryNum { get { return shuLiClass.OkHistoryNum; } } public int NgHistoryNum { get { return shuLiClass.NgHistoryNum; } } // 数粒信息发送线程 Thread SendBottLogicMessageThread = null; bool IsSend = false; // Modbus客户端实例 public ModbusTcpClient modbusTcpClient = new ModbusTcpClient(); // 颗粒结果待发送队列 //ConcurrentQueue SendQueue = new ConcurrentQueue(); ConcurrentQueue SendQueue = new ConcurrentQueue(); ConcurrentQueue CanSendQueue = new ConcurrentQueue(); /// /// 数粒状态计时器 /// Stopwatch stopwatch = Stopwatch.StartNew(); /// /// 数粒状态 /// private bool _ShuLiState = true; public bool ShuLiState { get { return _ShuLiState; } } private bool _IsDebug = true; public bool IsDebug { get { return _IsDebug; } } private bool IsConnectModbus = false; public static ActionMesSqliteDataClass actionMesSqliteDataClass = null; ConcurrentQueue _images = new ConcurrentQueue(); public string FormulationName { get; set; } = string.Empty; public bool IsLoadFormulation = false; private long lastframeNum = -1; #endregion #region 公共方法 /// /// 设置ModbusTcpClient /// /// public void ConnectModbus(string ipAddress) { Task.Run(() => { int i = 10; while(!modbusTcpClient.Connect(ipAddress)&&i>0) { IsConnectModbus = false; //SystemAlarm.AlarmAlert(AlarmMessageList.数粒通讯连接失败, $"Modbus通讯连接失败,目标IP:{ipAddress}", "DLL:MainThreadClass-ConnectModbus"); SystemAlarm.AlarmAlert(AlarmMessageList.数粒通讯连接失败, $"Number counting communication connection failed, target IP:{ipAddress}", $"Modbus通讯连接失败,目标IP:{ipAddress}", "DLL:MainThreadClass-ConnectModbus"); i--; Task.Delay(1000); } SystemAlarm.AlarmCancel(AlarmMessageList.数粒通讯连接失败); LOG.log("数粒通讯成功", 6); IsConnectModbus = true; }); } /// /// 初始化构造方法 /// /// /// public MainThreadClass(ShuLiConfigClass configClass,CameraConfig CameraConfig) { shuLiConfig = configClass; cameraConfig = CameraConfig; // 数粒配置文件地址 if (configClass!=null) { // 创建数粒对象(配置文件) shuLiClass = new ShuLiClass(shuLiConfig); } else { // 创建数粒对象(默认) shuLiClass = new ShuLiClass(); } _IsOpenLoadThread = cameraConfig.IsOpenLoad; ThisCamerNo = cameraConfig.CamerNo; } /// /// 加载相机 /// /// public bool LoadCamera() { bool result = false; // 相机列表 try { // 获取相机列表 cameraClass.GetCameraList(out List list); if (list.Count == 0) { //FaultLog.RecordErrorMessage(string.Format("{0}:没有相机", "MainThreadClass-StartMianThread")); SystemAlarm.AlarmAlert(AlarmMessageList.未检测到相机, "No Camera", "没有相机", "DLL:MainThreadClass-LoadCamera"); // 如果没有相机,则退出 return result; } SystemAlarm.AlarmCancel(AlarmMessageList.未检测到相机); // 加载相机 // cameraClass.LoadCamereDevice(list.First().DeviceSN); if (!cameraClass.LoadCamereDevice(cameraConfig)) { //FaultLog.RecordErrorMessage(string.Format("{0}:相机加载失败", "MainThreadClass-StartMianThread")); SystemAlarm.AlarmAlert(AlarmMessageList.相机加载失败, "No LoadCamera", "相机加载失败", "DLL:MainThreadClass-LoadCamera"); return result; } SystemAlarm.AlarmCancel(AlarmMessageList.相机加载失败); CameraConfigIsChange = false; } catch(Exception ex) { Console.WriteLine($"MainThreadClass-LoadCamera:加载相机失败: {ex.Message}"); } return result; } /// /// 重新加载相机配置 /// public void ReLoadCameraConfig() { if(CameraConfigIsChange) { CameraConfig OldcameraConfig = cameraClass.GetConfigValue(); if(OldcameraConfig.CameraSNNum != cameraConfig.CameraSNNum) { cameraClass.LoadCamereDevice(cameraConfig); } else { cameraClass.ReLoadCameraConfig(cameraConfig); } } } /// /// 开始主线程 /// public bool StartMianThread() { bool result = false; try { if (BatchNumber == "") { FaultLog.RecordErrorMessage($"Camera {cameraConfig.CamerNo} does not have a batch number, and it failed to start", "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}\\ActiveObjectData20250827.db"); actionMesSqliteDataClass.GetAllActionMinStartMaxEndLine(out int num, out int StartLine, out int EndLine); shuLiClass.InitCurrentLine(EndLine); shuLiClass.InitNum(num); shuLiClass.QueueSemaphore = new SemaphoreSlim(0); cameraClass.FrameGrabbedEvent += SwitchIdentifyImageEvent; IsSwitch = true; // 取图开启 if (!cameraClass.StartCamera()) { return result; } _CameraStatic = true; StartCanSendBottLogicMessageThread(); // 为数粒算法的识别成功一粒回调函数添加方法 //shuLiClass.WorkCompleted += Worker_OneGrainCompleted; shuLiClass.WorkCompleted += Worker_OneGrainCompletedUseCan; // 开启识别线程 shuLiClass.StartIdentifyFuntion(cameraClass.GetCamereImageSize().Width); result = true; } catch(Exception ex) { Console.WriteLine($"MainThreadClass-StartMianThread:启动失败: {ex.Message}"); } return result; } /// /// 停止主线程 /// public bool StopMianThread() { bool result = false; try { // 数粒识别线程关闭 shuLiClass.StopIdentifyFuntion(); LOG.log("执行数粒识别线程关闭成功",6); _CameraStatic = false; //shuLiClass.WorkCompleted -= Worker_OneGrainCompleted; shuLiClass.WorkCompleted -= Worker_OneGrainCompletedUseCan; LOG.log("识别回调取消完成",6); // 相机取图关闭 cameraClass.StopCamera(); cameraClass.FrameGrabbedEvent -= SwitchIdentifyImageEvent; LOG.log("相机回调取消完成", 6); IsSwitch = false; StopSendBottLogicMessageThread(); LOG.log("发送线程关闭完成", 6); result = true; actionMesSqliteDataClass.Dispose(); LOG.log("批处理实例释放完成", 6); } catch(Exception ex) { LOG.error($"MainThreadClass-StopMianThread:停止失败: {ex.Message}"); } return result; } /// /// 释放相机资源 /// public void DisposeCamera() { cameraClass.StopCamera(); cameraClass.DisPoseCamera(); } /// /// 重加载相机 /// /// /// public bool ReLoadCamera(string CameraSN) { bool result; result = cameraClass.ReLoadCameraDevice(CameraSN); if (result) { cameraConfig = cameraClass.GetConfigValue(); } return result; } /// /// 获取显示用的图片数据 /// /// /// public void GetShowImage(int ImageHeight,out Bitmap ImageData) { List RowsShowList = new List(); ActiveObjectClass NewActive = shuLiClass.GetLastActive(); if (NewActive == null) { LOG.log(string.Format("{0}:没有获取到数粒数据", "MainThreadClass-GetShowImage")); ImageData = GetNullShowImage(4096, ImageHeight); return; } List Data = shuLiClass.GetHistoryActive().Where(o => o.LastSeenLine > NewActive.LastSeenLine - ImageHeight).ToList(); Bitmap BitmapImage = new Bitmap(NewActive.ImageWidth, ImageHeight); if (Data.Count > 0) { int ThisImageStartLine = (int)Data.Max(o => o.LastSeenLine) - ImageHeight; using (Graphics g = Graphics.FromImage(BitmapImage)) { g.Clear(Color.White); var redPen = new Pen(Color.Red, 1); var bluePen = new Pen(Color.FromArgb(0,146,255), 1); var GreenPen = new Pen(Color.SeaGreen, 5); using (Pen RedPan = new Pen(Color.Black, 5)) { for (int i = 0; i < shuLiClass.ChannelsRoi.Count - 1; i++) { g.DrawLine(RedPan, new Point(shuLiClass.ChannelsRoi[i], 0), new Point(shuLiClass.ChannelsRoi[i], BitmapImage.Height)); } } foreach (var item in Data) { int roix = item.MinStartCol - 5; int roiy = (int)(item.StartLine - ThisImageStartLine) - 5 < 0 ? 0 : (int)(item.StartLine - ThisImageStartLine) - 5; int roiheight = (int)(item.LastSeenLine - ThisImageStartLine - roiy) + 10; int roiwidth = item.MaxEndCol - item.MinStartCol + 10; g.DrawRectangle(GreenPen, new Rectangle(roix, roiy, roiwidth, roiheight)); Font font = new Font("Arial", 20); g.DrawString(item.Num.ToString(), font, Brushes.Black, new Point(roix - 20*item.Num.ToString().Length, roiy - 20)); foreach (var item1 in item.RowsData) { if((int)(item1.RowsCol - ThisImageStartLine)<0) continue; int yPos = (int)(item1.RowsCol - ThisImageStartLine); g.DrawLine(item.StateCode == 0 ? bluePen : redPen, new Point(item1.StartCol, yPos), new Point(item1.EndCol, yPos)); } } } } ImageData = BitmapImage.Clone() as Bitmap; BitmapImage.Dispose(); //GC.Collect(); } public void GetShowImage(int ImageHeight, int ImageWidth , out Bitmap ImageData) { List RowsShowList = new List(); ActiveObjectClass NewActive = shuLiClass.GetLastActive(); if (NewActive == null) { LOG.log(string.Format("{0}:没有获取到数粒数据", "MainThreadClass-GetShowImage")); ImageData = GetNullShowImage(ImageWidth, ImageHeight); return; } List Data = shuLiClass.GetHistoryActive().Where(o => o.StartLine > NewActive.StartLine - ImageHeight).ToList(); Bitmap BitmapImage = new Bitmap(NewActive.ImageWidth, ImageHeight); if (Data.Count > 0) { int ThisImageStartLine = (int)Data.Max(o => o.LastSeenLine) - ImageHeight; using (Graphics g = Graphics.FromImage(BitmapImage)) { g.Clear(Color.White); var redPen = new Pen(Color.Red, 1); var bluePen = new Pen(Color.FromArgb(0, 146, 255), 1); var GreenPen = new Pen(Color.SeaGreen, 5); using (Pen RedPan = new Pen(Color.Black, 5)) { for (int i = 0; i < shuLiClass.ChannelsRoi.Count - 1; i++) { g.DrawLine(RedPan, new Point(shuLiClass.ChannelsRoi[i], 0), new Point(shuLiClass.ChannelsRoi[i], BitmapImage.Height)); } } foreach (var item in Data) { int roix = item.MinStartCol - 5; int roiy = ImageHeight - (int)(item.LastSeenLine - ThisImageStartLine) - 5 < 0 ? 0 : ImageHeight - (int)(item.LastSeenLine - ThisImageStartLine) - 5; int roiheight = ImageHeight - (int)(item.StartLine - ThisImageStartLine) - roiy + 10; int roiwidth = item.MaxEndCol - item.MinStartCol + 10; g.DrawRectangle(GreenPen, new Rectangle(roix, roiy, roiwidth, roiheight)); Font font = new Font("Arial", 20); g.DrawString(item.Num.ToString(), font, Brushes.Black, new Point(roix - 20 * item.Num.ToString().Length, roiy - 20)); foreach (var item1 in item.RowsData) { if ((int)(item1.RowsCol - ThisImageStartLine) < 0) continue; int yPos = ImageHeight - (int)(item1.RowsCol - ThisImageStartLine); g.DrawLine(item.StateCode == 0 ? bluePen : redPen, new Point(item1.StartCol, yPos), new Point(item1.EndCol, yPos)); } } } } ImageData = BitmapImage.Clone() as Bitmap; BitmapImage.Dispose(); //GC.Collect(); } /// /// 获取无数据的图片 /// /// /// /// public Bitmap GetNullShowImage(int ImageWidth, int ImageHeight) { if(ImageWidth==0) { return null; } Bitmap BitmapImage = new Bitmap(ImageWidth, ImageHeight); using (Graphics g = Graphics.FromImage(BitmapImage)) { g.Clear(Color.White); using (Pen RedPan = new Pen(Color.Black, 8)) { for (int i = 0; i < shuLiClass.ChannelsRoi.Count - 1; i++) { g.DrawLine(RedPan, new Point(shuLiClass.ChannelsRoi[i], 0), new Point(shuLiClass.ChannelsRoi[i], BitmapImage.Height)); } } } return BitmapImage; } /// /// 获取此刻的Config数据 /// /// public void GetAllConfigValue(out CameraConfig Camconfig,out ShuLiConfigClass shuLiConfig) { //判断是否加载了相机 if(cameraClass.IsLoadCamera()) { //获取已经加载的相机的配置 cameraConfig = cameraClass.GetConfigValue(); Camconfig = cameraConfig; } else { cameraConfig = new CameraConfig(); Camconfig = cameraConfig; } //读取视觉配置 shuLiConfig = shuLiClass.GetConfigValue(); this.shuLiConfig = shuLiConfig; } /// /// 获取相机此刻的Config数据 /// /// public CameraConfig GetCameraConfig() { CameraConfig result = null; //判断是否加载了相机 if (cameraClass.IsLoadCamera()) { //获取已经加载的相机的配置 result = cameraClass.GetConfigValue(); cameraConfig = result; } return result; } /// /// 保存所有Config /// public void SaveAllConfig() { shuLiClass.SaveConfig(); } /// /// 获取相机连接状态 /// /// public bool GetCameraConnectStatic() { bool result = false; if(cameraClass != null) result = cameraClass.IsConnectCamera(); return result; } /// /// 清除历史数据 /// /// public bool ClearHistoryActive() { bool result = false; if(shuLiClass != null) result = shuLiClass.ClearHistoryActive(); return result; } public bool ClearSendQueue() { bool result = false; while(SendQueue.Count>0) { SendQueue.TryDequeue(out TestSenMessage testSen); } result = true; return result; } /// /// 获取过去一秒内颗粒数量 /// /// public int GetOneSecondActiveNum() { int result = 0; if(shuLiClass != null) result = shuLiClass.GetHistoryActive().Where(o=>o.EndCheckTime>DateTime.Now-TimeSpan.FromSeconds(1)).Count(); return result; } /// /// 获取相机原图一张 /// /// public Bitmap GetCamImageOnce() { Bitmap result = null; if(_images.Count>0) { _images.TryDequeue(out IFrameOut image); if(image != null) result = image.Image.ToBitmap(); } return result; } public void ParameterTrain(List ListValues) { if(ListValues.Count() > 0) { if(ListValues.Count()>500) { ListValues = ListValues.Take(500).OrderByDescending(o=>o.Area).ToList(); } int MaxArea = ListValues.Max(x => x.Area); int MinArea = ListValues.Min(x => x.Area); double MaxLength = ListValues.Max(x => x.MaxLength); double MinLength = ListValues.Min(x => x.MaxLength); if(ListValues.Count<2) return; int ChazhiId = -1; int MaxChazhi = 0; for (int i = 1; i < ListValues.Count; i++) { int Chazhi = (ListValues[i - 1].Area - ListValues[i].Area); if (Chazhi > MaxChazhi*10) { MaxChazhi = Chazhi; ChazhiId = i; } } if(ChazhiId == -1) return; int yuzhi = (int)Math.Sqrt(2) + 2; } } /// /// 运行参数训练 /// /// public ParaTrainResultClass ParameterTrain() { var ListValues = shuLiClass.GetHistoryActive(); ParaTrainResultClass Result = new ParaTrainResultClass(); if (ListValues.Count() > 0) { if (ListValues.Count() > 500) { ListValues = ListValues.Skip(ListValues.Count - 500).OrderByDescending(o => o.Area).ToList(); } if (ListValues.Count < 2) { Result = null; return Result; } Result.NoiseFilterThreshold = 5; var LengthListValues = ListValues.OrderBy(o => o.MaxLength).Skip(100).Take(300).ToList(); Result.MaxLength = Convert.ToInt32(ListValues.Max(x => x.MaxLength) * 1.1); Result.MinLength = Convert.ToInt32(ListValues.Min(x => x.MaxLength) * 0.9); var AreaListValues = ListValues.OrderBy(o => o.Area).Skip(100).Take(300).ToList(); Result.MaxArea = Convert.ToInt32(ListValues.Max(x => x.Area) * 1.1); Result.MinArea = Convert.ToInt32(ListValues.Min(x => x.Area) * 0.9); } return Result; } public ParaTrainResultClass ParameterTrain(int KeliW,int KeliL) { var ListValues = shuLiClass.GetHistoryActive(); ParaTrainResultClass Result = new ParaTrainResultClass(); List LengthListValues = null; List AreaListValues = null; if (ListValues.Count() > 0) { try { LOG.log("参数训练-数据筛选", 6); if (ListValues.Count() > 1000) { ListValues = ListValues.Skip(ListValues.Count - 1000).OrderByDescending(o => o.Num).ToList(); } if (ListValues.Count < 2) { Result = null; return Result; } List maxLengthModels = new List(); LOG.log("参数训练-凸包计算-未标定长度检测", 6); foreach (var item in ListValues) { List points = shuLiClass.ConvexHull(item.RowsData); var itemLenValue = shuLiClass.CoefficientCalculateMinimumBoundingRectangle(points); var UseValue = new MaxLengthModel() { MaxLength = itemLenValue.Height, Point1 = itemLenValue.HeightPoints[0], Point2 = itemLenValue.HeightPoints[1] }; maxLengthModels.Add(UseValue); } LOG.log("参数训练-标定计算", 6); var ScaleResult = LineScanCameraCalibrator.CalculateLineScanPixelScale(maxLengthModels, KeliL); shuLiClass.SetXCoefficient(ScaleResult.XScale); shuLiClass.SetYCoefficient(ScaleResult.YScale); shuLiConfig.ScaleX = ScaleResult.XScale; shuLiConfig.ScaleY = ScaleResult.YScale; Result.ScaleX = ScaleResult.XScale; Result.ScaleY = ScaleResult.YScale; Result.NoiseFilterThreshold = 5; LOG.log("参数训练-计算标定后的长度", 6); foreach (var item in ListValues) { item.MaxLength = shuLiClass.SizeCalculation(item.RowsData).Height; } LOG.log("参数训练-长度数据筛选", 6); if (ListValues.Count >= 1000) { LengthListValues = ListValues.OrderBy(o => o.MaxLength).Skip(10).Take(980).ToList(); } else { LengthListValues = ListValues.OrderBy(o => o.MaxLength).Skip(10).Take(ListValues.Count - 20).ToList(); } LOG.log("参数训练-长度数据计算", 6); Result.MaxLength = Convert.ToInt32(LengthListValues.Max(x => x.MaxLength)) + 2; Result.MinLength = Convert.ToInt32(LengthListValues.Min(x => x.MaxLength) - 2); LOG.log("参数训练-面积数据筛选", 6); if (ListValues.Count >= 1000) { AreaListValues = ListValues.OrderBy(o => o.Area).Skip(10).Take(980).ToList(); } else { AreaListValues = ListValues.OrderBy(o => o.Area).Skip(10).Take(ListValues.Count - 20).ToList(); } LOG.log("参数训练-面积数据计算", 6); Result.MaxArea = Convert.ToInt32(AreaListValues.Max(x => x.Area)) + 50; Result.MinArea = Convert.ToInt32(AreaListValues.Min(x => x.Area)) - 50; } catch(Exception ex) { LOG.error(ex.Message); } } return Result; } #endregion #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 IsXuanZhuanCloseFaMen = false; /// /// 每数完一粒识别线程执行的事件 /// /// /// private void Worker_OneGrainCompleted(object sender, ActiveObjectEventArgsClass e) { //LOG.log("有活跃物体转换为了历史物体,回调事件被触发!", 6); //LOG.log(string.Format("图像处理实例中的待识别图像缓存队列长度{0}", shuLiClass.ImageNum), 6); if (e.Actives.Where(o => o.StateCode == 7).Count() > 0) { stopwatch.Restart(); _ShuLiState = false; } else if (stopwatch.ElapsedMilliseconds > 1000) { stopwatch.Stop(); _ShuLiState = true; } //Int16 result = new Int16(); ushort[] result = new ushort[2]; var Hset = new HashSet(); // 事件处理逻辑 foreach (ActiveObjectClass oneActive in e.Actives) { //往数组中计数 var UseTime = (oneActive.EndCheckTime - oneActive.StartCheckTime).TotalMilliseconds; var QutuYanshiTime = (DateTime.Now - oneActive.PictureEndReadTime).TotalMilliseconds; if (BatchNumber!="") oneActive.BatchNumber = BatchNumber; LOG.log(string.Format("输出当前颗粒信息,颗粒编号:{0},开始时间:{1},第一张图像读取时间:{2},\n" + "识别耗时:{3},尾图到输出的时间:{4},颗粒状态:{5},通道数:{6}", oneActive.Num, oneActive.StartCheckTime.ToString("O"), oneActive.PictureStartReadTime.ToString("O"), UseTime.ToString(), QutuYanshiTime.ToString(), oneActive.StateCode, oneActive.ChannelNO), 6); if (QutuYanshiTime > 30) { //LOG.error($"结果输出延时超过了30ms,耗时{QutuYanshiTime}"); Console.WriteLine($"结果输出延时超过了30ms,耗时{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); } } }); if (!Hset.Add(oneActive.ChannelNO)) { //有重复的通道的异常记录流程 var EResult = new ushort[2]; //正常运行流程 if (oneActive.ChannelNO == -1) { //FaultLog.RecordErrorMessage("颗粒通道判定异常"); FaultLog.RecordErrorMessage("Abnormal particle channel determination"); continue; } if (oneActive.StateCode == 0) { //单通道合格计数 EResult[0] |= (ushort)(1 << oneActive.ChannelNO); } else { if (oneActive.StateCode != 7 && oneActive.StateCode != 9) { //单通道不合格计数 EResult[1] |= (ushort)(1 << (oneActive.ChannelNO)); } else if (oneActive.StateCode == 7) { //FaultLog.RecordErrorMessage("视野存在遮挡,请检查相机"); FaultLog.RecordErrorMessage("The field of view is obstructed. Please check the camera"); } } //SendQueue.Enqueue(new TestSenMessage() //{ // Message = EResult, // NumMessage = new ushort[] { (ushort)oneActive.Num }, // count = 1 //}); continue; } else { //正常运行流程 if (oneActive.ChannelNO == -1) { //FaultLog.RecordErrorMessage("颗粒通道判定异常"); FaultLog.RecordErrorMessage("Abnormal particle channel determination"); continue; } if (oneActive.StateCode == 0) { //单通道合格计数 result[0] |= (ushort)(1 << oneActive.ChannelNO); } else { if (oneActive.StateCode != 7 && oneActive.StateCode != 9) { //单通道不合格计数 result[1] |= (ushort)(1 << (oneActive.ChannelNO)); } else if (oneActive.StateCode == 7) { //FaultLog.RecordErrorMessage("视野存在遮挡,请检查相机"); FaultLog.RecordErrorMessage("The field of view is obstructed. Please check the camera"); } } } } //LOG.log("当前待发送队列数量:" + SendQueue.Count, 6); if (IsSend) { 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 }); } } /// /// 每数完一粒识别线程执行的事件 /// /// /// private void Worker_OneGrainCompletedUseCan(object sender, ActiveObjectEventArgsClass e) { //LOG.log("有活跃物体转换为了历史物体,回调事件被触发!", 6); //LOG.log(string.Format("图像处理实例中的待识别图像缓存队列长度{0}", shuLiClass.ImageNum), 6); if (e.Actives.Where(o => o.StateCode == 7).Count() > 0) { stopwatch.Restart(); _ShuLiState = false; } else if (stopwatch.ElapsedMilliseconds > 1000) { stopwatch.Stop(); _ShuLiState = true; } var Hset = new HashSet(); byte result = new byte(); bool IsOK = false; // 事件处理逻辑 foreach (ActiveObjectClass oneActive in e.Actives) { //往数组中计数 var UseTime = (oneActive.EndCheckTime - oneActive.StartCheckTime).TotalMilliseconds; var QutuYanshiTime = (DateTime.Now - oneActive.PictureEndReadTime).TotalMilliseconds; if (BatchNumber != "") oneActive.BatchNumber = BatchNumber; LOG.log(string.Format("输出当前颗粒信息,颗粒编号:{0},开始时间:{1},第一张图像读取时间:{2},\n" + "识别耗时:{3},尾图到输出的时间:{4},颗粒状态:{5},通道数:{6}", oneActive.Num, oneActive.StartCheckTime.ToString("O"), oneActive.PictureStartReadTime.ToString("O"), UseTime.ToString(), QutuYanshiTime.ToString(), oneActive.StateCode, oneActive.ChannelNO), 6); if (QutuYanshiTime > 30) { //LOG.error($"结果输出延时超过了30ms,耗时{QutuYanshiTime}"); Console.WriteLine($"结果输出延时超过了30ms,耗时{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.InsertActiveObject(oneActive); } catch (Exception ex) { LOG.error("MianThread-Worker_OneGrainCompleted-Task:" + ex.Message); } } }); if (!Hset.Add(oneActive.ChannelNO)) { //有重复的通道的异常记录流程 var EResult = new byte(); bool EIsOK = false; //正常运行流程 if (oneActive.ChannelNO == -1) { //FaultLog.RecordErrorMessage("颗粒通道判定异常"); FaultLog.RecordErrorMessage("Abnormal particle channel determination"); continue; } if (oneActive.StateCode == 0) { //单通道合格计数 EResult |= (byte)(1 << oneActive.ChannelNO); EIsOK = true; } else { if (oneActive.StateCode != 7 && oneActive.StateCode != 9) { //单通道不合格计数 EResult |= (byte)(1 << (oneActive.ChannelNO)); } else if (oneActive.StateCode == 7) { //FaultLog.RecordErrorMessage("视野存在遮挡,请检查相机"); FaultLog.RecordErrorMessage("The field of view is obstructed. Please check the camera"); } } CanSendQueue.Enqueue(new CanSenMessage() { SendDate = EResult, IsOK = EIsOK, TaiHao = 0 }); continue; } else { //正常运行流程 if (oneActive.ChannelNO == -1) { //FaultLog.RecordErrorMessage("颗粒通道判定异常"); FaultLog.RecordErrorMessage("Abnormal particle channel determination"); continue; } if (oneActive.StateCode == 0) { //单通道合格计数 result |= (byte)(1 << oneActive.ChannelNO); } else { if (oneActive.StateCode != 7 && oneActive.StateCode != 9) { //单通道不合格计数 result |= (byte)(1 << (oneActive.ChannelNO)); } else if (oneActive.StateCode == 7) { //FaultLog.RecordErrorMessage("视野存在遮挡,请检查相机"); FaultLog.RecordErrorMessage("The field of view is obstructed. Please check the camera"); } } } } if (IsSend) { CanSendQueue.Enqueue(new CanSenMessage() { SendDate = result, IsOK = IsOK, TaiHao = 0 }); } } #endregion #region 线程方法 /// /// 交换数据事件-赋予给相机回调方法 /// /// /// private void SwitchIdentifyImageEvent(object sender, FrameGrabbedEventArgs e) { if (e.FrameOut!=null) { //var QutuYanshiTime = (DateTime.Now - FromUnixTimestamp((long)e.FrameOut.HostTimeStamp)).TotalMilliseconds; //if (QutuYanshiTime > 12) //{ // Console.WriteLine($"GetOnceImage-识别延时超过了12ms,耗时{QutuYanshiTime}"); //} if (lastframeNum == -1) { lastframeNum = e.FrameOut.FrameNum; } else if (lastframeNum == e.FrameOut.FrameNum - 1) { lastframeNum = e.FrameOut.FrameNum; } else { //丢帧记录 LOG.log(string.Format("lost frame: Width[{0}] , Height[{1}] , FrameNum[{2}] ,Frevous[{3}]", e.FrameOut.Image.Width, e.FrameOut.Image.Height, e.FrameOut.FrameNum, lastframeNum), 6); Console.WriteLine("lost frame: Width[{0}] , Height[{1}] , FrameNum[{2}] ,Frevous[{3}]", e.FrameOut.Image.Width, e.FrameOut.Image.Height, e.FrameOut.FrameNum, lastframeNum); lastframeNum = e.FrameOut.FrameNum; } //Debug模式,不进行图像处理 if (_IsDebug) { _images.Enqueue(e.FrameOut.Clone() as IFrameOut); if (_images.Count > 5) { _images.TryDequeue(out IFrameOut image); image.Dispose(); } } shuLiClass.SetOnceIdentifyImageData(e.FrameOut); shuLiClass.QueueSemaphore.Release(); // 通知处理线程有新数据 } e.FrameOut.Dispose(); } /// /// 启动发送消息线程 /// private bool StartSendBottLogicMessageThread() { bool result = false; try { if(!IsConnectModbus) return result; IsSend = true; SendBottLogicMessageThread = new Thread(SendBottLogicMessageProcess); SendBottLogicMessageThread.Start(); SystemAlarm.AlarmCancel(AlarmMessageList.结果发送线程启动失败); result = true; } catch (Exception ex) { //FaultLog.RecordErrorMessage("MianThread-StartSendBottLogicMessageThread:Start thread failed!, " + ex.Message); SystemAlarm.AlarmAlert(AlarmMessageList.结果发送线程启动失败, "MianThread-StartSendBottLogicMessageThread:Start thread failed!, " + ex.Message, "结果发送线程启动失败," + ex.Message, "DLL:MainThreadClass-StartSendBottLogicMessageThread"); Console.WriteLine("MianThread-StartSendBottLogicMessageThread:Start thread failed!, " + ex.Message); result = false; } return result; } /// /// 停止发送消息线程 /// private void StopSendBottLogicMessageThread() { try { // 标志位设为false if (SendBottLogicMessageThread != null && SendBottLogicMessageThread.IsAlive) { try { IsSend = false; SendBottLogicMessageThread.Join(); //SendBottLogicMessageThread.Interrupt(); } catch (ThreadStateException) { // 忽略异常 } } //if (modbusTcpClient != null) modbusTcpClient.Disconnect(); SystemAlarm.AlarmCancel(AlarmMessageList.结果发送线程停止失败); } catch (Exception ex) { //FaultLog.RecordErrorMessage("Start thread failed!, " + ex.Message); SystemAlarm.AlarmAlert(AlarmMessageList.结果发送线程停止失败, "MianThread-StopSendBottLogicMessageThread:Stop thread failed!, " + ex.Message, "结果发送线程停止失败, " + ex.Message, "DLL:MainThreadClass-StopSendBottLogicMessageThread"); throw; } } /// /// 启动发送消息线程 /// private bool StartCanSendBottLogicMessageThread() { bool result = false; try { IsSend = true; SendBottLogicMessageThread = new Thread(SendBottLogicMessageProcessUseCan); SendBottLogicMessageThread.Start(); SystemAlarm.AlarmCancel(AlarmMessageList.结果发送线程启动失败); result = true; } catch (Exception ex) { //FaultLog.RecordErrorMessage("MianThread-StartSendBottLogicMessageThread:Start thread failed!, " + ex.Message); SystemAlarm.AlarmAlert(AlarmMessageList.结果发送线程启动失败, "MianThread-StartSendBottLogicMessageThread:Start thread failed!, " + ex.Message, "结果发送线程启动失败," + ex.Message, "DLL:MainThreadClass-StartSendBottLogicMessageThread"); Console.WriteLine("MianThread-StartSendBottLogicMessageThread:Start thread failed!, " + ex.Message); result = false; } return result; } /// /// 信息发送线程 /// private void SendBottLogicMessageProcess() { //获取数据 TestSenMessage sendMessage; Stopwatch sw = Stopwatch.StartNew(); Stopwatch WriteDoneTime = Stopwatch.StartNew(); ushort writedoneMes = 0; writedoneMes |= (ushort)(1 << 0); while (IsSend) { //LOG.log("进入线程", 6); sendMessage = new TestSenMessage(); ushort[] ReturnValue = null; ushort[] result = new ushort[20]; modbusTcpClient.ReadHoldingRegisters(slaveId: 1, startAddress: 0, numRegisters: 1, out ReturnValue); if (ReturnValue == null) { continue; } if ((ReturnValue[0] & 1) == 0) { WriteDoneTime.Restart(); while (SendQueue.Count==0&& IsSend) { //Thread.Sleep(1); } sw.Restart(); int SendMessageCount = SendQueue.Count > 10 ? 10 : SendQueue.Count; for (int i = 0; i < SendMessageCount; i++) { if (!SendQueue.TryDequeue(out sendMessage)) { //FaultLog.RecordErrorMessage("MainThreadClass-SendBottLogicMessageProcess-SendQueue.TryDequeue failed!"); } if (sendMessage.Message == null) { continue; } result[i * 2] = sendMessage.Message[0]; result[i * 2 + 1] = sendMessage.Message[1]; } //写入数据 modbusTcpClient.WriteMultipleRegisters(slaveId: 1, startAddress: 100, values: result); modbusTcpClient.WriteSingleRegister(slaveId: 1, registerAddress: 0, value: writedoneMes); if (result[0] > 0 || result[1]>0) { sw.Stop(); WriteDoneTime.Stop(); LOG.log(string.Format("待发送数量{0}", SendQueue.Count), 6); LOG.log(string.Format("SendMess1Ok:{0},SendMess1Ng:{1},SendMess2Ok:{2},SendMess2Ng:{3}," + "\nSendMessage3Ok:{4},SendMessage3Ng:{5},SendMessage4Ok:{6},SendMessage4Ng:{7}," + "\nSendMessage5Ok:{8},SendMessage5Ng:{9},SendMessage6Ok:{10},SendMessage6Ng:{11}," + "\nSendMessage7Ok:{12},SendMessage7Ng:{13},SendMessage8Ok:{14},SendMessage8Ng:{15}," + "\nSendMessage9Ok:{16},SendMessage9Ng:{17},SendMessage10Ok:{18},SendMessage10Ng:{19}," + "\n此次写值耗时:{20},此次WriteDone耗时:{21},此次写入数据量{22}", result[0], result[1], result[2], result[3], result[4], result[5], result[6], result[7], result[8], result[9], result[10], result[11], result[12], result[13], result[14], result[15], result[16], result[17], result[18], result[19], sw.Elapsed,WriteDoneTime.Elapsed,sendMessage.count), 6); if (sw.ElapsedMilliseconds > 10) LOG.error("通讯超时"); } } Thread.Sleep(1); } } private void SendBottLogicMessageProcessUseCan() { //获取数据 CanSenMessage sendMessage; Stopwatch sw = Stopwatch.StartNew(); Stopwatch WriteDoneTime = Stopwatch.StartNew(); CanManagerClass canManager = new CanManagerClass(); bool isFrist = true; try { canManager.OpenDevice(); canManager.InitCan(); } catch (Exception ex) { LOG.error(string.Format("MainThreadClass-SendBottLogicMessageProcessUseCan-Open:{0}", ex.Message)); } while (IsSend) { try { //LOG.log("进入线程", 6); sendMessage = new CanSenMessage(); bool IsTrigger = canManager.GetTrigger(ref isFrist); if (!IsTrigger) { continue; } else { WriteDoneTime.Restart(); while (CanSendQueue.Count == 0 && IsSend) { Thread.Sleep(1); } sw.Restart(); if (!CanSendQueue.TryDequeue(out sendMessage)) { FaultLog.RecordErrorMessage("MainThreadClass-SendBottLogicMessageProcess-SendQueue.TryDequeue failed!"); } canManager.SenMessage(sendMessage); } } catch (Exception ex) { LOG.error(string.Format("MainThreadClass-SendBottLogicMessageProcessUseCan-Send:{0}", ex.Message)); } } try { canManager.CloseCan(); } catch (Exception ex) { LOG.error(string.Format("MainThreadClass-SendBottLogicMessageProcessUseCan-Close:{0}", ex.Message)); } LOG.log("发送线程关闭完成", 6); } #endregion #region 外部函数 [DllImport("winmm.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; } } public class CanSenMessage { public byte SendDate { get; set; } public bool IsOK { get; set; } public int TaiHao { get; set; } } }