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.

604 lines
21 KiB
C#

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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<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 Queue<BarCodeModel> consumeQueue = new Queue<BarCodeModel>();
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;
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<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="ip"></param>
/// <param name="flag"></param>
private void SerialSignalPush(string ip, int flag)
{
// 光电触发拍照
if (flag == 1)
{
CameraHandler();
}
else //光电释放暂不使用
{
}
}
/// <summary>
/// 接受相机识别结果
/// </summary>
/// <param name="result"></param>
private void ReceiveCameraResult(string result)
{
logger.LogInformation($"相机返回结果:{result}");
if (result == "OK")
{
HikCameraResult = true;
}
else
{
HikCameraResult = false;
}
}
/// <summary>
/// 条码触发相机拍照校验
/// </summary>
/// <param name="materialCodeStr"></param>
/// <param name="ip"></param>
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
{
//需要检测
//打开灯光
lightHelper.SendData("OPEN");
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}");
}
}
/// <summary>
/// 相机流程处理
/// </summary>
/// <returns></returns>
private async void CameraHandler()
{
try
{
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报警!");
}
/// <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;
}
// 检查是否超时
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
#region 测试用例
/// <summary>
/// 模拟光电发送byte测试接收处理
/// </summary>
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
}
}