You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

560 lines
20 KiB
C#

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.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<LogoBusiness> 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/";
/// <summary>
/// 存储海康相机识别结果
/// </summary>
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<LogoBusiness> 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;
this.baseMaterialService = baseMaterialService;
this.logoIdentifyService = logoIdentifyService;
this.logoConfigService = logoConfigService;
gunHelper.InstanceSerialPort();
plc = _plcPool.GetPlcByKey("plc");
lightHelper.InstanceSerialPort();
//定时清除日志及照片
CleanOldLogs(PicturePath);
CleanOldLogs(LogPath);
InitClearTimer();
}
4 months ago
public void test()
{
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);
});
}
private void SerialSignalPush(string ip, int flag)
{
// 光电触发拍照
if (flag == 1)
{
}
else //光电释放
{
}
}
private void ReceiveCameraResult(string result)
{
logger.LogInformation($"相机返回结果:{result}");
if (result == "OK")
{
HikCameraResult = true;
}
else
{
HikCameraResult = false;
}
}
public static LogoBusiness GetInstance(ILogger<LogoBusiness> 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;
}
/// <summary>
/// 条码触发相机拍照校验
/// </summary>
/// <param name="materialCodeStr"></param>
/// <param name="ip"></param>
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
{
4 months ago
#region 初始操作:复位海康上次结果,删除海康上次保存文件
HikCameraResult = false;
FileHelper.DeleteAllPictures(config.CameraFilePath);
#endregion
bool judge = FoamtJudge(materialCodeStr);
if (!judge)
{
WarningAndStop($"箱体码{materialCodeStr}格式不正确,停线报警!");
return;
}
logger.LogInformation($"扫描到箱体码:{materialCodeStr}");
4 months ago
// 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");
4 months ago
#region 复位海康上次结果,删除海康上次保存文件
HikCameraResult = false;
FileHelper.DeleteAllPictures(config.CameraFilePath);
#endregion
}
}
/// <summary>
/// 判断Logo校验是否成功
/// </summary>
/// <returns></returns>
public async Task<bool> JudgeIsSuccessAsync()
{
bool result = false;
// 设置计时器
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
//至多等待2.5s海康的校验结果
result = await Task.Run(() =>
{
while (true)
{
if (HikCameraResult == true)
{
return true;
}
// 检查是否超过两秒
4 months ago
if (stopwatch.ElapsedMilliseconds > 2500)
{
return false;
}
Thread.Sleep(100);
}
});
return result;
}
/// <summary>
/// 箱体码格式校验20位成品码B开头的21位箱体码为MES码
/// </summary>
/// <param name="code"></param>
/// <returns></returns>
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;
}
/// <summary>
/// 截取两个逗号之间的字符串
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
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
}
}