using HslCommunication.Profinet.GE; using Microsoft.Extensions.Logging; using SlnMesnac.Common; using SlnMesnac.Config; using SlnMesnac.Model.domain; 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.Threading; using System.Threading.Tasks; using System.Timers; using TouchSocket.Core; 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 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; this.baseMaterialService = baseMaterialService; this.logoIdentifyService = logoIdentifyService; this.logoConfigService = logoConfigService; gunHelper.InstanceSerialPort(); plc = _plcPool.GetPlcByKey("plc"); lightHelper.InstanceSerialPort(); //定时清除日志及照片 CleanOldLogs(PicturePath); CleanOldLogs(LogPath); InitClearTimer(); } private void ReceiveCameraResult(string result) { logger.LogInformation($"相机返回结果:{result}"); if (result == "OK") { HikCameraResult = true; } else { HikCameraResult = false; } } 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; } /// /// 条码触发相机拍照校验 /// /// /// public async void BarCodeHandler(string materialCodeStr, string ip) { //打开灯光 lightHelper.SendData("OPEN"); #region 复位报警灯 // 声光电报警复位 gunHelper.SendData("OK"); #endregion logger.LogInformation("相机流程开始:"); // 传入照片 byte[] ImageData = null; // 压缩后的图片 byte[] compressedImageData = null; try { #region 初始操作:复位海康上次结果,删除海康上次保存文件 HikCameraResult = false; FileHelper.DeleteAllPictures(config.CameraFilePath); #endregion bool judge = FoamtJudge(materialCodeStr); if (!judge) { WarningAndStop($"箱体码{materialCodeStr}格式不正确,停线报警!"); return; } logger.LogInformation($"扫描到箱体码:{materialCodeStr}"); // 1.立即触发相机拍照 tcpServer.SendCommand(config.CameraIP, "START"); //2.根据箱体码查询型号,根据型号判断是否需要校验LOGO ProductModel mode = logoConfigService.GetMaterialTypeByBarCode(materialCodeStr); if(mode == null) { // 根据成品码查询不到型号 Warning($"根据成品码{materialCodeStr}查询不到型号,判断该型号是否样机或检查网络!"); return; } LogoConfig logoConfig = logoConfigService.GetByMaterialType(mode.MaterialCode); // 海康校验结果 bool hikFlag = false; if (logoConfig.IsChecked == 0) { // 不需要校验 hikFlag = true; RefreshMessageEvent?.Invoke("LOGO+PCI无需识别,下发放行"); logger.LogInformation($"箱体码:{materialCodeStr},Logo无需识别,下发放行"); } else { // 需要检验但是还没设置配方 if(logoConfig.CheckKind == 0) { // 不需要校验 hikFlag = false; Warning($"箱体码:{materialCodeStr},LOGO+PCI需要识别,但是未设置PCI模版"); } else { #region 拍照处理流程 Thread.Sleep(int.Parse(config.SleepStr)); // 1.触发相机拍照 tcpServer.SendCommand(config.CameraIP, logoConfig.CheckKind.ToString()); // 需要校验 // 2.等待接收海康结果 hikFlag = await JudgeIsSuccessAsync(); if (hikFlag) { //校验成功放行 RefreshMessageEvent?.Invoke("LOGO+PCI识别成功,下发放行"); logger.LogInformation($"箱体码:{materialCodeStr},Logo识别成功,下发放行"); } else { //校验失败禁止放行 WarningAndStop($"LOGO+PCI识别失败,禁止放行"); } #endregion } } // 刷新界面、刷新图片,照片按照日期存储本地 RefreshBoxInfoEvent?.Invoke(materialCodeStr, DateTime.Now.ToString(), logoConfig.MaterialName, hikFlag); //需要检测并且设置过配方的条码等待照片 if(logoConfig.IsChecked == 1 && logoConfig.CheckKind != 0) { 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, materialCodeStr + ".jpg", PicturePath); RefreshPictureEvent?.Invoke(ImageData); } else { // WarningAndStop($"读取相机保存照片文件夹失败,请检查文件夹:{config.CameraFilePath}是否存在"); } }); } #region 更新数据库 LogoIdentify record = new LogoIdentify(); record.ProductCode = materialCodeStr; record.MaterialType = logoConfig.MaterialType; record.MaterialName = logoConfig.MaterialName; record.isChecked = logoConfig.IsChecked; record.Result = hikFlag ? 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($"BarCodeHandler异常,识别Logo失败,原因:{ex.Message},箱体条码:{materialCodeStr}"); } finally { lightHelper.SendData("CLOSE"); #region 复位海康上次结果,删除海康上次保存文件 HikCameraResult = false; FileHelper.DeleteAllPictures(config.CameraFilePath); #endregion } } /// /// 判断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 } }