using HslCommunication.Profinet.GE; using Microsoft.Extensions.Logging; using SlnMesnac.Common; using SlnMesnac.Config; using SlnMesnac.Model.domain; using SlnMesnac.Model.dto; using SlnMesnac.Plc; using SlnMesnac.Repository.service; using SlnMesnac.Repository.service.Impl; using SlnMesnac.TouchSocket; using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Security.Cryptography.Xml; using System.ServiceModel.Channels; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Timers; using TouchSocket.Core; using TouchSocket.Sockets; namespace SlnMesnac.Business { public class LogoBusiness { private TcpServer tcpServer = null; private ILogger logger; private PlcAbsractFactory plc = null; private LightHelper lightHelper = LightHelper.Instance; private DebugConfig config = DebugConfig.Instance; private IBaseMaterialService baseMaterialService; private ILogoIdentifyService logoIdentifyService; private ILogoConfigService logoConfigService; public PaddleOCRSharp.PaddleOCREngine engine = null; private GunHelper gunHelper = GunHelper.Instance; private Queue consumeQueue = new Queue(); private static LogoBusiness instance; // 存放照片路径 public static string PicturePath = System.Environment.CurrentDirectory + "/picture/"; // 存放日志路径 public static string LogPath = System.Environment.CurrentDirectory + "/Logs/"; /// /// 接收海康相机识别结果 /// private static bool HikCameraResult = false; #region 委托定义 刷新界面扫描信息 public delegate void RefreshBoxInfo(string boxCode, string boxTime, string model, bool isSuccess); public static event RefreshBoxInfo? RefreshBoxInfoEvent; public delegate void RefreshPicture(byte[] imageData); public static event RefreshPicture? RefreshPictureEvent; public delegate void RefreshMessage(string message, bool isWarning = false); public static event RefreshMessage? RefreshMessageEvent; public delegate void RefreshDataGrid(); public static event RefreshDataGrid? RefreshDataGridEvent; #endregion private LogoBusiness(ILogger logger, ILogoConfigService logoConfigService, IBaseMaterialService baseMaterialService, ILogoIdentifyService logoIdentifyService, PlcPool _plcPool, TcpServer _tcpServer) { this.logger = logger; tcpServer = _tcpServer; TcpServer.RefreshMaterialCodeStrEvent += BarCodeHandler; TcpServer.CameraResultEvent += ReceiveCameraResult; TcpServer.SerialSignalPushEvent += SerialSignalPush; TcpServer.MessageNoReadEvent += MessageNoRead; this.baseMaterialService = baseMaterialService; this.logoIdentifyService = logoIdentifyService; this.logoConfigService = logoConfigService; gunHelper.InstanceSerialPort(); plc = _plcPool.GetPlcByKey("plc"); lightHelper.InstanceSerialPort(); //定时清除日志及照片 CleanOldLogs(PicturePath); CleanOldLogs(LogPath); InitClearTimer(); } public static LogoBusiness GetInstance(ILogger logger, ILogoConfigService logoConfigService, IBaseMaterialService baseMaterialService, ILogoIdentifyService ocrVerfiyService, PlcPool _plcPool, TcpServer _tcpServer) { if (instance == null) { instance = new LogoBusiness(logger, logoConfigService, baseMaterialService, ocrVerfiyService, _plcPool, _tcpServer); } return instance; } /// /// 接收光电模块信号 /// /// /// private void SerialSignalPush(string ip, int flag) { // 光电触发拍照 if (flag == 1) { CameraHandler(); } else //光电释放 { lightHelper.SendData("CLOSE"); } } /// /// 接受相机识别结果 /// /// private void ReceiveCameraResult(string result) { logger.LogInformation($"相机返回结果:{result}"); if (result == "OK") { HikCameraResult = true; } else { HikCameraResult = false; } } /// /// 条码触发相机拍照校验 /// /// /// public async void BarCodeHandler(string materialCodeStr, string ip) { try { logger.LogInformation("扫码流程开始:"); #region 初始操作:复位海康上次结果,删除海康上次保存文件 HikCameraResult = false; FileHelper.DeleteAllPictures(config.CameraFilePath); // 保证队列一条数据 consumeQueue.Clear(); // 声光电报警复位 gunHelper.SendData("OK"); #endregion bool judge = FoamtJudge(materialCodeStr); if (!judge) { WarningAndStop($"箱体码{materialCodeStr}格式不正确,停线报警!"); return; } logger.LogInformation($"扫描到箱体码:{materialCodeStr}"); //1.根据箱体码查询型号,根据型号判断是否需要校验LOGO ProductModel mode = logoConfigService.GetMaterialTypeByBarCode(materialCodeStr); if(mode == null) { // 根据成品码查询不到型号 Warning($"根据成品码{materialCodeStr}查询不到型号,判断该型号是否样机或检查网络!"); return; } LogoConfig logoConfig = logoConfigService.GetByMaterialType(mode.MaterialCode); if (logoConfig.IsChecked == 0) { // 不需要校验 RefreshMessageEvent?.Invoke("LOGO+PCI无需识别,下发放行"); logger.LogInformation($"箱体码:{materialCodeStr},Logo无需识别,下发放行"); RefreshBoxInfoEvent?.Invoke(materialCodeStr, DateTime.Now.ToString(), logoConfig.MaterialName, true); #region 更新数据库 LogoIdentify record = new LogoIdentify(); record.ProductCode = materialCodeStr; record.MaterialType = logoConfig.MaterialType; record.MaterialName = logoConfig.MaterialName; record.isChecked = logoConfig.IsChecked; record.Result = 1; record.RecordTime = DateTime.Now; record.ProductLine = config.ProductLine; logoIdentifyService.InsertRecord(record); #endregion RefreshDataGridEvent?.Invoke(); } else { // 需要检验但是还没设置配方 if(logoConfig.CheckKind == 0) { Warning($"箱体码:{materialCodeStr},LOGO+PCI需要识别,但是未设置PCI模版"); return; } else { BarCodeModel barCodeModel = new BarCodeModel(); barCodeModel.BarCode = materialCodeStr; barCodeModel.logoConfig = logoConfig; consumeQueue.Enqueue(barCodeModel); logger.LogInformation($"箱体码:{materialCodeStr},入队等待识别"); } } } catch (Exception ex) { Warning($"BarCodeHandler异常,识别Logo失败,原因:{ex.Message},箱体条码:{materialCodeStr}"); } } /// /// 相机流程处理 /// /// private async void CameraHandler() { try { //需要检测 //打开灯光 lightHelper.SendData("OPEN"); logger.LogInformation("相机流程开始:"); //照片 byte[] ImageData = null; // 压缩后的图片 byte[] compressedImageData = null; //从队列取出条码 if (consumeQueue.Count <= 0) return; bool hikFlag = false; BarCodeModel barCodeModel = consumeQueue.Dequeue(); // Thread.Sleep(int.Parse(config.SleepStr)); // 1.触发相机拍照 tcpServer.SendCommand(config.CameraIP, barCodeModel.logoConfig.CheckKind.ToString()); // 2.等待接收海康结果 hikFlag = await JudgeIsSuccessAsync(); if (hikFlag) { RefreshMessageEvent?.Invoke("LOGO+PCI识别成功,下发放行"); logger.LogInformation($"箱体码:{barCodeModel.BarCode},Logo识别成功,下发放行"); } else { WarningAndStop($"LOGO+PCI识别失败,禁止放行"); } // 刷新界面、刷新图片,照片按照日期存储本地 #region RefreshBoxInfoEvent?.Invoke(barCodeModel.BarCode, DateTime.Now.ToString(), barCodeModel.logoConfig.MaterialName, hikFlag); await Task.Run(() => { Thread.Sleep(int.Parse(config.PictureSleep)); ImageData = FileHelper.FindPhoto(config.CameraFilePath); if (ImageData != null) { // 图片压缩 compressedImageData = FileHelper.CompressImageData(ImageData, 20); FileHelper.SaveImage(compressedImageData, barCodeModel.BarCode + ".jpg", PicturePath); RefreshPictureEvent?.Invoke(ImageData); } }); #endregion #region 更新数据库 LogoIdentify record = new LogoIdentify(); record.ProductCode = barCodeModel.BarCode; record.MaterialType = barCodeModel.logoConfig.MaterialType; record.MaterialName = barCodeModel.logoConfig.MaterialName; record.isChecked = barCodeModel.logoConfig.IsChecked; record.Result = hikFlag == true ? 1 : 0; record.RecordTime = DateTime.Now; record.ProductLine = config.ProductLine; if (compressedImageData != null) { record.Picture = compressedImageData; } logoIdentifyService.InsertRecord(record); #endregion RefreshDataGridEvent?.Invoke(); }catch(Exception ex) { Warning($"相机流程异常,原因:{ex.Message}"); } finally { #region 复位海康上次结果,删除海康上次保存文件、关闭灯光 HikCameraResult = false; FileHelper.DeleteAllPictures(config.CameraFilePath); lightHelper.SendData("CLOSE"); #endregion } } public void MessageNoRead() { WarningAndStop($"扫码器NoRead报警!"); } /// /// 判断Logo校验是否成功 /// /// public async Task JudgeIsSuccessAsync() { bool result = false; // 设置计时器 Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); //至多等待2.5s海康的校验结果 result = await Task.Run(() => { while (true) { if (HikCameraResult == true) { return true; } // 检查是否超时 if (stopwatch.ElapsedMilliseconds > 2500) { return false; } Thread.Sleep(100); } }); return result; } /// /// 箱体码格式校验,20位成品码,B开头的21位箱体码为MES码 /// /// /// private bool FoamtJudge(string code) { if (!string.IsNullOrEmpty(code)) { if (code.Length == 20 || code.Length == 21&& code.Substring(0, 1) == "B") { return true; } } return false; } /// /// 截取两个逗号之间的字符串 /// /// /// static string GetSubstringBetweenCommas(string input) { if (input == null) return null; // 找到第一个逗号的位置 int firstCommaIndex = input.IndexOf(','); if (firstCommaIndex != -1) { // 找到第二个逗号的位置 int secondCommaIndex = input.IndexOf(',', firstCommaIndex + 1); if (secondCommaIndex != -1) { // 使用Substring截取第一个逗号和第二个逗号之间的字符 return input.Substring(firstCommaIndex + 1, secondCommaIndex - firstCommaIndex - 1); } else { return null; } } else { return null; } } #region PLC交互部分 //M100停止点位 public void Pass() { try { if (plc != null && plc.IsConnected) { plc.writeInt16ByAddress("DB22.DBW2", 8); Task.Run(() => { // 设置计时器 Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); while (true) { if (plc.readInt16ByAddress("DB22.DBW2") == 0) { logger.LogInformation("PLC复位成功,启动线体"); RefreshMessageEvent?.Invoke("PLC复位成功,启动线体"); break; } // 检查是否超过两秒 if (stopwatch.ElapsedMilliseconds > 3000) { logger.LogError("PLC复位超时"); RefreshMessageEvent?.Invoke("PLC复位超时", true); break; } Thread.Sleep(100); } }); } else { logger.LogError("PLC未连接,请检查连接"); RefreshMessageEvent?.Invoke("PLC未连接,请检查连接", true); } // 声光电报警复位 gunHelper.SendData("OK"); } catch (Exception ex) { logger.LogError($"下发PLC复位方法Pass()异常:{ex.Message}"); } } public void Stop() { try { if (plc != null && plc.IsConnected) { plc.writeInt16ByAddress("DB22.DBW2", 9); Task.Run(() => { // 设置计时器 Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); while (true) { if (plc.readInt16ByAddress("DB22.DBW2") == 1) { logger.LogInformation("PLC复位成功,启动线体"); RefreshMessageEvent?.Invoke("PLC复位成功,启动线体"); break; } // 检查是否超过两秒 if (stopwatch.ElapsedMilliseconds > 3000) { logger.LogError("PLC复位超时"); RefreshMessageEvent?.Invoke("PLC复位超时", true); break; } Thread.Sleep(100); } }); } // 声光电报警 gunHelper.SendData("NG"); } catch (Exception ex) { logger.LogError($"下发PLC报警方法Stop()异常:{ex.Message}"); } } #endregion #region 记录日志,刷新界面及下发PLC报警 public void WarningAndStop(string message) { logger.LogError(message); RefreshMessageEvent?.Invoke(message, true); Stop(); } public void Warning(string message) { logger.LogError(message); RefreshMessageEvent?.Invoke(message, true); } #endregion #region 定时清除照片及日志 private void InitClearTimer() { System.Timers.Timer timer = new System.Timers.Timer { Interval = TimeSpan.FromDays(1).TotalMilliseconds, // 每天执行一次 AutoReset = true }; timer.Elapsed += OnTimedEvent; timer.Start(); } private void OnTimedEvent(object source, ElapsedEventArgs e) { CleanOldLogs(PicturePath); CleanOldLogs(LogPath); } private void CleanOldLogs(string Path) { try { int saveDays = int.Parse(config.PictureSaveTime); var directoryInfo = new DirectoryInfo(Path); if (!directoryInfo.Exists) { Console.WriteLine($"日志文件夹 {Path} 不存在."); return; } var cutoffDate = DateTime.Now.AddDays(-saveDays); var directories = directoryInfo.GetDirectories(); foreach (var directory in directories) { // 解析文件夹名称 if (DateTime.TryParseExact(directory.Name, "yyyy-MM-dd", null, System.Globalization.DateTimeStyles.None, out DateTime folderDate)) { if (folderDate < cutoffDate) { Console.WriteLine($"删除过期日志文件夹: {directory.FullName}"); directory.Delete(true); // 删除文件夹及其内容 } } else { Console.WriteLine($"文件夹名称不符合日期格式: {directory.Name}"); } } } catch (Exception ex) { Console.WriteLine($"清理日志时发生错误: {ex.Message}"); } } #endregion #region 测试用例 /// /// 模拟光电发送byte,测试接收处理 /// public void testPostByte() { Task.Run(async () => { Thread.Sleep(5000); TcpClient _tcpClient = new TcpClient(); _tcpClient.Setup(new TouchSocketConfig().SetRemoteIPHost($"127.0.0.1:7024")); _tcpClient.Connect(); var waitClient = _tcpClient.CreateWaitingClient(new WaitingOptions() { FilterFunc = response => { return true; } }); byte[] release = new byte[] { (byte)0x01, (byte)0x03, (byte)0x02, (byte)0x01, (byte)0xFF, (byte)0xF9, (byte)0x94 }; var reciveBuffer = await waitClient.SendThenResponseAsync(release); }); } #endregion } }