Files
2026-09-09 09:10:43 +08:00

2265 lines
93 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using JinYuan.DAL.Models;
using JinYuan.DAL.Service;
using JinYuan.Helper;
using JinYuan.MES.Enums;
using JinYuan.MES.Models;
using JinYuan.Models;
using JinYuan.VirtualDataLibrary;
using JinYuan.VirtualDataLibrary.Utlis;
using Language;
using PLC;
using PLCCommunication;
using PLCCommunication.Common;
using PLCCommunication.Common.DataConvert;
using SqlSugar;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.Diagnostics.Eventing.Reader;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Security.Policy;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using static System.Runtime.CompilerServices.RuntimeHelpers;
using static System.Windows.Forms.VisualStyles.VisualStyleElement.StartPanel;
namespace JinYuan.ControlCenters
{
public partial class ControlCenter
{
private static readonly SemaphoreSlim _plcSemaphore = new SemaphoreSlim(5, 10); // PLC操作并发限制
private static readonly SemaphoreSlim _dbSemaphore = new SemaphoreSlim(10, 20); // 数据库操作并发限制
private static readonly object _fileLock = new object(); // 文件锁
//private static readonly TimeSpan _plcTimeout = TimeSpan.FromSeconds(1); // PLC操作超时
//private static readonly TimeSpan _mesTimeout = TimeSpan.FromSeconds(2); // MES请求超时
// UI同步上下文:避免跨线程阻塞UI
private readonly SynchronizationContext _uiSyncContext = SynchronizationContext.Current;
#region 保留2位小数
private float SaveTwoNum(float TNum)
{
return (float)Math.Round(TNum, 2, MidpointRounding.AwayFromZero);
}
#endregion
#region PLC设备安全访问
/// <summary>
/// 安全获取PLC设备
/// </summary>
/// <param name="plcNum">PLC编号(从1开始)</param>
/// <returns>PLC设备实例,若不存在或未连接则返回null</returns>
private PlcReadWriteBase GetPlcDevice(int plcNum)
{
try
{
if (CommonMethods.plcDevices != null && plcNum > 0)
{
// 根据PLC编号查找设备,而不是依赖列表索引
var plcDevice = CommonMethods.plcDevices.FirstOrDefault(p =>
p != null &&
p.Name == $"PLC_{plcNum}" &&
p.IsConnected);
if (plcDevice != null)
{
return plcDevice;
}
}
}
catch { }
return null;
}
#endregion
#region 物流线料盘来料
/// <summary>
/// 物流线料盘来料
/// </summary>
private async Task TrayFeedingStation(JYResult<byte[]> bArrays, object plcModel)
{
LogHelper.Instance.WriteLog($"Step 1: 进入 TrayFeedingStation - {DateTime.Now:HH:mm:ss.fff}", "WuLiuLog");
PlcGroup pf = plcModel as PlcGroup;
if (pf == null)
{
LogHelper.Instance.WriteError("PLC模型转换失败", "WuLiuLog");
await WritePLCInt16Sync(pf, pf.VarList[4].VarAddress, 2); //反馈结果 1OK 2NG
await WritePLCInt16Sync(pf, pf.VarList[5].VarAddress, 1); //反馈处理完成
return;
}
using (var timer = new TimedOperation("物流线料盘来料", (elapsedMs, operationName) =>
{
CommonMethods.AddDataMonitorLog(0, $"物流线料盘来料共耗时:{operationName} 执行耗时:{elapsedMs}ms");
}))
{
if (!bArrays.IsSuccess || bArrays.Content == null)
{
await HandleFeedingError(pf, "PLC读地址无料框码数据", null);
return;
}
try
{
LogHelper.Instance.WriteLog($"Step 2: ClearPLCRegistersBatch - {DateTime.Now:HH:mm:ss.fff}", "WuLiuLog");
// 1. 批量清空PLC寄存器
await ClearPLCRegistersBatch(pf, new[] { 2, 3, 4 }, 0).ConfigureAwait(false);
#region 数据解析
const int BARCODE_SIZE = 40;
byte[] bytes = bArrays.Content.ByteReverse();//高低位转换
string trayID = FormatBarcode(StringLib.GetStringFromByteArrayByEncoding(bytes, 0, BARCODE_SIZE, Encoding.ASCII));
#endregion
LogHelper.Instance.WriteLog($"Step 3: 数据解析: 料盘码【{trayID}】", "WuLiuLog");
if (!IsValidTrayID(trayID))
{
await HandleFeedingError(pf, $"料框码无效:{trayID}", trayID);
return;
}
var mesResult = await GetTrayInfoWithTimeout(pf, trayID, 500);
LogHelper.Instance.WriteLog($"Step 4: GetTrayInfoWithTimeout: 料盘码【{trayID}】", "WuLiuLog");
if (!mesResult.Success || mesResult.TrayInfo == null)
{
await HandleFeedingError(pf, mesResult.ErrorMessage, trayID);
return;
}
//获取电池型号
var modelType = mesResult.TrayInfo.productCode;
// 获取电池档位信息
var gradeResult = await GetBatteryGradesWithTimeout(mesResult.TrayInfo, 500);
LogHelper.Instance.WriteLog($"Step 5: 获取电池档位: 料盘码【{trayID}】", "WuLiuLog");
string finalGrade = gradeResult.Grade;
string gradeRemark = gradeResult.IsConsistent ? "" : gradeResult.ErrorMessage;
LogHelper.Instance.WriteLog($"Step 6:物流线料盘来料: 获取电池型号【{modelType}】,电池档位:【{gradeResult}】{gradeRemark}", "WuLiuLog");
// 构建并保存实体
var mesTrayEntity = BuildTrayEntity(mesResult.TrayInfo, finalGrade, gradeRemark);
await SaveTrayDataBatchAsync(new List<TrayEntity> { mesTrayEntity }).ConfigureAwait(false);
LogHelper.Instance.WriteLog($"Step 7: 保存数据: 料盘码【{trayID}】", "WuLiuLog");
// 反馈PLC
await FeedBackToPLC(pf, mesResult.TrayInfo, finalGrade);
LogHelper.Instance.WriteLog($"Step 8: 写PLC: 料盘码【{trayID}】", "WuLiuLog");
}
catch (Exception ex)
{
LogHelper.Instance.WriteError($"物流线料盘来料异常: {ex}", "WuLiuLog");
await WritePLCInt16Sync(pf, pf.VarList[4].VarAddress, 2); //反馈结果 1 OK 2 NG
await WritePLCInt16Sync(pf, pf.VarList[5].VarAddress, 1); //反馈处理完成
}
}
}
/// <summary>
/// 带超时的MES料盘信息获取
/// </summary>
private async Task<(bool Success, TrayInfo TrayInfo, string ErrorMessage)> GetTrayInfoWithTimeout(PlcGroup pf, string trayID, int timeoutMs)
{
var task = QueryMesTrayInfos(pf, trayID, CancellationToken.None);
if (await Task.WhenAny(task, Task.Delay(timeoutMs)) != task)
{
return (false, null, "MES请求超时");
}
var result = await task;
return (result.success, result.trayinfo, result.mesMessage);
}
/// <summary>
/// 等级查询结果
/// </summary>
private class GradeQueryResult
{
public string Grade { get; set; }
public bool IsConsistent { get; set; }
public string ErrorMessage { get; set; }
}
/// <summary>
/// 带超时的电池等级查询
/// </summary>
private async Task<GradeQueryResult> GetBatteryGradesWithTimeout(TrayInfo trayInfo, int timeoutMs)
{
var result = new GradeQueryResult { Grade = "", IsConsistent = true, ErrorMessage = "" };
// 构建电池条码列表
var listCode = BuildMaterialLotList(trayInfo);
if (listCode.Count == 0)
{
result.ErrorMessage = "料盘中无电池条码";
CommonMethods.AddDataMonitorLog(0, result.ErrorMessage);
return result;
}
var gradeTask = QueryGrades(listCode, CancellationToken.None);
if (await Task.WhenAny(gradeTask, Task.Delay(timeoutMs)) != gradeTask)
{
result.ErrorMessage = "电池等级查询超时";
result.IsConsistent = false;
CommonMethods.AddDataMonitorLog(0, result.ErrorMessage);
return result;
}
var gradeResult = await gradeTask;
if (!gradeResult.success || gradeResult.batteryInfo == null || gradeResult.batteryInfo.Count == 0)
{
result.ErrorMessage = "电池等级查询失败或无数据";
result.IsConsistent = false;
CommonMethods.AddDataMonitorLog(0, result.ErrorMessage);
return result;
}
// 检查等级一致性
var grades = gradeResult.batteryInfo.Where(g => !string.IsNullOrEmpty(g?.grade)).Select(g => g.grade).Distinct().ToList();
if (grades.Count == 0)
{
result.ErrorMessage = "未获取到有效的电池等级";
result.IsConsistent = false;
CommonMethods.AddDataMonitorLog(0, result.ErrorMessage);
return result;
}
if (grades.Count == 1)
{
result.Grade = grades[0];
result.IsConsistent = true;
CommonMethods.AddDataMonitorLog(0, $"电池等级查询成功,所有电池等级一致: {result.Grade}");
}
else
{
result.ErrorMessage = $"电池等级不一致!检测到多个等级: {string.Join(",", grades)}";
result.IsConsistent = false;
LogHelper.Instance.WriteError(result.ErrorMessage, "WuLiuLog");
CommonMethods.AddDataMonitorLog(0, result.ErrorMessage);
}
return result;
}
/// <summary>
/// 构建MaterialLot列表
/// </summary>
private List<MaterialLot> BuildMaterialLotList(TrayInfo trayInfo)
{
var listCode = new List<MaterialLot>();
if (trayInfo?.cellList == null) return listCode;
foreach (var cell in trayInfo.cellList)
{
if (!string.IsNullOrEmpty(cell.cellNo))
{
listCode.Add(new MaterialLot
{
identification = cell.cellNo,
locationBat = cell.channel.ToString()
});
}
}
return listCode;
}
/// <summary>
/// 构建TrayEntity实体
/// </summary>
private TrayEntity BuildTrayEntity(TrayInfo trayInfo, string grade, string gradeRemark)
{
return new TrayEntity
{
trayID = trayInfo?.trayNo ?? "",
model = trayInfo?.productCode ?? CommonMethods.mesConfig.ModelName,
grade = grade,
moreGrade = "",
Count = trayInfo?.cellList?.Count ?? 0,
GongWei = CommonMethods.sysConfig.GongWei,
Time = DateTime.Now,
Result = string.IsNullOrEmpty(gradeRemark) ? "OK" : "NG",
Remark = string.IsNullOrEmpty(gradeRemark)
? $"本机架电池型号{CommonMethods.mesConfig.ModelName}, 处理档位{string.Join("|", CommonMethods.GetValidGrades())}"
: gradeRemark
};
}
/// <summary>
/// 反馈结果到PLC
/// </summary>
private async Task FeedBackToPLC(PlcGroup pf, TrayInfo trayInfo, string grade)
{
MaterialFrameRuleService service = new MaterialFrameRuleService(CommonMethods.db.Db);
MaterialFrameRule rule = service.GetRuleByCode(trayInfo.trayNo);
short model = (short)CommonMethods.mesConfig.GetModel(trayInfo?.productCode ?? CommonMethods.mesConfig.ModelName);
if (model == 0) model = 1;
// TODO 注释电池型号自动选择逻辑 by clm 20260831
//string modelName = trayInfo?.productCode;
//short model = CommonMethods.mesConfig.GetModelNum(modelName, rule?.Workshop_Code ?? string.Empty);
short gradeCode = 99;
if (!string.IsNullOrEmpty(grade))
gradeCode = (short)CommonMethods.GetGradeNo(grade);
await WritePLCInt16Sync(pf, pf.VarList[2].VarAddress, model);
await WritePLCInt16Sync(pf, pf.VarList[3].VarAddress, gradeCode);
await WritePLCInt16Sync(pf, pf.VarList[4].VarAddress, 1);
await WritePLCInt16Sync(pf, pf.VarList[5].VarAddress, 1);
CommonMethods.AddDataMonitorLog(0, $"物流线料盘来料[{trayInfo.trayNo}]:反馈PLC:[{pf.VarList[2].VarAddress}]写型号{model}(mes获取的原型号:{trayInfo?.productCode}); [{pf.VarList[3].VarAddress}]写挡位{gradeCode}({grade})");
LogHelper.Instance.WriteLog($"物流线料盘来料[{trayInfo.trayNo}]:反馈PLC:[{pf.VarList[2].VarAddress}]写型号{model}(mes获取的原型号:{trayInfo?.productCode}); [{pf.VarList[3].VarAddress}]写挡位{gradeCode}({grade})", "WuLiuLog");
}
/// <summary>
/// 处理错误并反馈PLC
/// </summary>
private async Task HandleFeedingError(PlcGroup pf, string errorMessage, string trayID)
{
var mesTrayEntity = new TrayEntity
{
trayID = trayID ?? "",
model = "",
grade = "",
moreGrade = "",
Count = 0,
GongWei = CommonMethods.sysConfig.GongWei,
Time = DateTime.Now,
Result = "NG",
Remark = errorMessage
};
await SaveTrayDataBatchAsync(new List<TrayEntity> { mesTrayEntity });
await WritePLCInt16Sync(pf, pf.VarList[4].VarAddress, 2); //反馈结果 1OK 2NG
await WritePLCInt16Sync(pf, pf.VarList[5].VarAddress, 1); //反馈处理完成
CommonMethods.AddDataMonitorLog(0, $"料盘码[{trayID}]:" + errorMessage);
LogHelper.Instance.WriteLog($"料盘码[{trayID}]:" + errorMessage, "WuLiuLog");
}
// MES查询方法
private async Task<(bool success, string mesMessage, MesResType mesResType, TrayInfo trayinfo)> QueryMesTrayInfos(PlcGroup plcGroup, string trayID, CancellationToken token)
{
token.ThrowIfCancellationRequested();
try
{
MesUrlService service = new MesUrlService(CommonMethods.db.Db);
string url = service.GetMesUrl(trayID, ConstValue.INTER_QueryTray);
if (string.IsNullOrEmpty(url))
{
throw new ArgumentException($"未找到{ConstValue.INTER_QueryTray}对应的MES接口地址");
}
var (success, mesMessage, mesResType, trayinfo) = await CommonMethods.hbgMes.QueryMesTrayInfosAsync(
url,
CommonMethods.mesConfig.siteCode,
CommonMethods.mesConfig.lineCode,
CommonMethods.mesConfig.equipNum,
DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
trayID,
CommonMethods.mesConfig.MaterialCode,
CommonMethods.mesConfig.mesUserName
);
return (success, mesMessage, mesResType, trayinfo);
}
catch (OperationCanceledException)
{
// 超时, 快速返回,不等待
return (false, "MES查询超时", MesResType.B, null);
}
catch (Exception ex)
{
// 其他错误
LogHelper.Instance.WriteEX(ex);
return (false, "MES查询失败", MesResType.B, null);
}
}
private async Task<(bool success, List<MaterialGrade> batteryInfo)> QueryGrades(List<MaterialLot> listCode, CancellationToken token)
{
token.ThrowIfCancellationRequested();
if (listCode == null || listCode.Count == 0)
{
return (false, null);
}
try
{
var (success, batteryInfo) = await CommonMethods.hbgMes.QueryGradeAsync(
CommonMethods.mesConfig.queryGradeUrl,
CommonMethods.mesConfig.siteCode,
CommonMethods.mesConfig.lineCode,
CommonMethods.mesConfig.equipNum,
DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
listCode.Count.ToString(),
CommonMethods.mesConfig.MaterialCode,
CommonMethods.mesConfig.mesUserName,
listCode
);
return (success, batteryInfo);
}
catch (OperationCanceledException)
{
// 超时, 快速返回,不等待
LogHelper.Instance.WriteError($"查询电池等级超时", "WuLiuLog");
return (false, null);
}
catch (Exception ex)
{
LogHelper.Instance.WriteError($"查询电池等级异常: {ex.Message}", "WuLiuLog");
return (false, null);
}
}
#endregion
#region 物流线料盘进站
/// <summary>
/// 物流线料盘进站
/// </summary>
private async Task CheckTrayStation(JYResult<byte[]> bArrays, object plcModel)
{
LogHelper.Instance.WriteLog($"Step 1: 进入 CheckTrayStation - {DateTime.Now:HH:mm:ss.fff}", "WuLiuLog");
PlcGroup pf = plcModel as PlcGroup;
if (pf == null)
{
LogHelper.Instance.WriteError("PLC模型转换失败", "WuLiuLog");
await WritePLCInt16Sync(pf, pf.VarList[4].VarAddress, 2); //反馈结果 1OK 2NG
await WritePLCInt16Sync(pf, pf.VarList[5].VarAddress, 1); //反馈处理完成
return;
}
using (var timer = new TimedOperation("物流线料盘进站", (elapsedMs, operationName) =>
{
CommonMethods.AddDataMonitorLog(0, $"物流线料盘进站共耗时:{operationName} 执行耗时:{elapsedMs}ms");
}))
{
LogHelper.Instance.WriteLog($"Step 2: 判断bArrays CheckTrayStation - {DateTime.Now:HH:mm:ss.fff}", "WuLiuLog");
if (!bArrays.IsSuccess || bArrays.Content == null)
{
await HandleCheckError(pf, "PLC读地址无料框码数据", null);
return;
}
try
{
LogHelper.Instance.WriteLog($"Step 3: ClearPLCRegistersBatch CheckTrayStation - {DateTime.Now:HH:mm:ss.fff}", "WuLiuLog");
// 批量清空寄存器
await ClearPLCRegistersBatch(pf, new[] { 2, 3, 4 }, 0).ConfigureAwait(false);
#region 数据解析
const int BARCODE_SIZE = 40;
LogHelper.Instance.WriteLog($"Step 4: 数据解析 CheckTrayStation - {DateTime.Now:HH:mm:ss.fff}", "WuLiuLog");
byte[] bytes = bArrays.Content.ByteReverse();//高低位转换
// 读取料盘码
string trayID = FormatBarcode(StringLib.GetStringFromByteArrayByEncoding(bytes, 0, BARCODE_SIZE, Encoding.ASCII));
#endregion
LogHelper.Instance.WriteLog($"Step 5: 验证料框码ID CheckTrayStation - {DateTime.Now:HH:mm:ss.fff}", "WuLiuLog");
// 验证卡板ID
if (!IsValidTrayID(trayID))
{
await HandleCheckError(pf, $"料框码无效:{trayID}", trayID);
return;
}
// 获取MES料盘信息
var mesResult = await GetTrayInfoWithTimeout(pf, trayID, 500);
if (!mesResult.Success || mesResult.TrayInfo == null)
{
await HandleCheckError(pf, mesResult.ErrorMessage, trayID);
return;
}
LogHelper.Instance.WriteLog($"Step 8: 组合TrayEntity CheckTrayStation - {DateTime.Now:HH:mm:ss.fff}", "WuLiuLog");
// 获取电池等级信息
var gradeResult = await GetBatteryGradesWithTimeout(mesResult.TrayInfo, 500);
string finalGrade = gradeResult.IsConsistent ? gradeResult.Grade : "";
string gradeRemark = gradeResult.IsConsistent ? "" : gradeResult.ErrorMessage;
// 反馈电池条码到PLC
//await WriteBatteryBarcodesToPLC(pf, mesResult.TrayInfo);
// 构建并保存实体
var mesTrayEntity = BuildTrayEntity(mesResult.TrayInfo, finalGrade, gradeRemark);
await SaveTrayDataBatchAsync(new List<TrayEntity> { mesTrayEntity }).ConfigureAwait(false);
// 反馈PLC
await FeedBackToPLCCheck(pf, mesResult.TrayInfo, finalGrade);
LogHelper.Instance.WriteLog($"物流线料盘进站:料框码:{trayID},往PLC地址[{pf.VarList[4].VarAddress}]写1(OK)", "WuLiuLog");
LogHelper.Instance.WriteLog($"Step 11: 结束 CheckTrayStation - {DateTime.Now:HH:mm:ss.fff}", "WuLiuLog");
}
catch (Exception ex)
{
LogHelper.Instance.WriteError($"物流线料盘进站异常: {ex}", "WuLiuLog");
await WritePLCInt16Sync(pf, pf.VarList[4].VarAddress, 2); //反馈结果 1OK 2NG
await WritePLCInt16Sync(pf, pf.VarList[5].VarAddress, 1); //反馈处理完成
}
}
}
/// <summary>
/// 进站反馈结果到PLC
/// </summary>
private async Task FeedBackToPLCCheck(PlcGroup pf, TrayInfo trayInfo, string grade)
{
short model = (short)CommonMethods.mesConfig.GetModel(trayInfo?.productCode ?? CommonMethods.mesConfig.ModelName);
if (model == 0) model = 1;
short gradeCode = 99; // 默认NG档位
if (!string.IsNullOrEmpty(grade))
gradeCode = (short)CommonMethods.GetGradeNo(grade);
await WritePLCInt16Sync(pf, pf.VarList[2].VarAddress, model);
await WritePLCInt16Sync(pf, pf.VarList[3].VarAddress, gradeCode);
await WritePLCInt16Sync(pf, pf.VarList[4].VarAddress, 1);
await WritePLCInt16Sync(pf, pf.VarList[5].VarAddress, 1);
CommonMethods.AddDataMonitorLog(0, $"物流线料盘进站[{trayInfo.trayNo}]:反馈PLC:[{pf.VarList[2].VarAddress}]写型号{model}(mes获取的原型号:{trayInfo?.productCode}); [{pf.VarList[3].VarAddress}]写挡位{gradeCode}({grade})");
LogHelper.Instance.WriteError($"物流线料盘进站[{trayInfo.trayNo}]:反馈PLC:[{pf.VarList[2].VarAddress}]写型号{model}(mes获取的原型号:{trayInfo?.productCode}); [{pf.VarList[3].VarAddress}]写挡位{gradeCode}({grade})", "WuLiuLog");
LogHelper.Instance.WriteLog($"物流线料盘进站完成", "WuLiuLog");
}
/// <summary>
/// 处理进站错误
/// </summary>
private async Task HandleCheckError(PlcGroup pf, string errorMessage, string trayID)
{
var mesTrayEntity = new TrayEntity
{
trayID = trayID ?? "",
model = "",
grade = "",
moreGrade = "",
Count = 0,
GongWei = CommonMethods.sysConfig.GongWei,
Time = DateTime.Now,
Result = "NG",
Remark = errorMessage
};
await SaveTrayDataBatchAsync(new List<TrayEntity> { mesTrayEntity });
short model = (short)CommonMethods.mesConfig.GetModel(CommonMethods.mesConfig.ModelName);
if (model == 0) model = 1;
short gradeCode = 99; // 默认NG档位
await WritePLCInt16Sync(pf, pf.VarList[2].VarAddress, model);
await WritePLCInt16Sync(pf, pf.VarList[3].VarAddress, gradeCode);//档位NG 99
await WritePLCInt16Sync(pf, pf.VarList[4].VarAddress, 2);
await WritePLCInt16Sync(pf, pf.VarList[5].VarAddress, 1);
CommonMethods.AddDataMonitorLog(0, $"物流线料盘进站[{trayID}]:反馈PLC:[{pf.VarList[2].VarAddress}]写型号1(mes获取的原型号:无); [{pf.VarList[3].VarAddress}]写挡位99(NG)");
LogHelper.Instance.WriteError($"物流线料盘进站[{trayID}]:反馈PLC:[{pf.VarList[2].VarAddress}]写型号1(mes获取的原型号:无); [{pf.VarList[3].VarAddress}]写挡位99(NG)", "WuLiuLog");
CommonMethods.AddDataMonitorLog(0, errorMessage);
}
/// <summary>
/// 保存料盘数据
/// </summary>
/// <param name="list"></param>
///
private async Task SaveTrayDataBatchAsync(List<TrayEntity> list)
{
if (list == null || list.Count == 0)
return;
try
{
foreach (var m in list)
{
if (string.IsNullOrEmpty(m.trayID))
{
m.Remark += " 料盘ID为空,跳过";
continue;
}
try
{
int updateCount = await CommonMethods.db.UpdateSingleAsync(m, null, it => it.trayID == m.trayID);
if (updateCount > 0)
{
m.Remark += " 更新Yes";
}
else
{
// 新增时直接调用底层,减少封装开销
bool addSuccess = await Task.Run(() =>
CommonMethods.db.AddReturnBoolAsync(m)
);
m.Remark += addSuccess ? " 新增Yes" : " 新增No";
}
}
catch (Exception ex)
{
LogHelper.Instance.WriteError($"物流线进站保存{m.trayID}处理异常:{ex}", "WuLiuLog");
continue;
}
}
_uiSyncContext.Post(_ => CommonMethods.ShowTrayDataDelegate?.Invoke(list), null);
}
catch (Exception ex)
{
LogHelper.Instance.WriteError($"物流线进站保存处理异常:{ex}", "WuLiuLog");
throw;
}
finally
{
// _dbSemaphore.Release();
}
}
private bool IsValidTrayID(string trayID)
{
return !string.IsNullOrWhiteSpace(trayID) &&
!trayID.ToUpper().Trim().Equals("ERROR", StringComparison.OrdinalIgnoreCase) &&
trayID.Length >= 3; // 增加字母数字校验,提前过滤无效数据
}
#endregion
#region 电池进站
/// <summary>
/// 电池进站
/// </summary>
private async Task FeedingStation(JYResult<byte[]> bArrays, object plcModel)
{
LogHelper.Instance.WriteLog($"Step 1: 进入 FeedingStation - {DateTime.Now:HH:mm:ss.fff}", "ZuPanLog");
PlcGroup pf = plcModel as PlcGroup;
if (pf == null)
{
LogHelper.Instance.WriteLog("PLC模型转换失败", "ZuPanLog");
WritePLCInt16Sync(pf, pf.VarList[5].VarAddress, 2);
return;
}
Stopwatch sw = Stopwatch.StartNew();
try
{
LogHelper.Instance.WriteLog($"Step 2: 判断bArrays FeedingStation - {DateTime.Now:HH:mm:ss.fff}", "ZuPanLog");
if (!bArrays.IsSuccess || bArrays.Content == null)
{
await HandleFeedingStationError(pf, "PLC读地址无料框码数据,不存库", null, null);
return;
}
LogHelper.Instance.WriteLog($"Step 3.1: ClearPLCRegistersBatch FeedingStation - {DateTime.Now:HH:mm:ss.fff}", "ZuPanLog");
#region 清空上一次寄存器数据
// 批量清空寄存器
await ClearPLCRegistersBatch(pf, new[] { 3, 4, 5 }, 0);
LogHelper.Instance.WriteLog($"Step 3.2: Clear电池条码 FeedingStation - {DateTime.Now:HH:mm:ss.fff}", "ZuPanLog");
// 清空电池条码区域
await ClearBatteryBarcodeArea(pf);
#endregion
#region 数据解析
const int BARCODE_SIZE = 40;
byte[] bytes = bArrays.Content.ByteReverse();
string trayID = FormatBarcode(StringLib.GetStringFromByteArrayByEncoding(bytes, 0, BARCODE_SIZE, Encoding.ASCII));
#endregion
if (!IsValidTrayID(trayID))
{
await HandleFeedingStationError(pf, "料框码无效,不存库", trayID, null);
return;
}
CommonMethods.AddDataMonitorLog(0, $"PLC读地址料框码数据,PLC地址[{pf.VarList[1].VarAddress}]:料框码{trayID}");
// 获取MES料盘信息
var mesResult = await GetTrayInfoWithTimeout(pf, trayID, 500);
if (!mesResult.Success || mesResult.TrayInfo == null)
{
await HandleFeedingStationError(pf, mesResult.ErrorMessage, trayID, mesResult.TrayInfo);
return;
}
// 获取电池等级信息
var gradeResult = await GetBatteryGradesWithTimeout(mesResult.TrayInfo, 500);
string finalGrade = gradeResult.IsConsistent ? gradeResult.Grade : "";
string gradeRemark = gradeResult.IsConsistent ? "" : gradeResult.ErrorMessage;
// 反馈电池条码到PLC
await WriteBatteryBarcodesToPLC(pf, mesResult.TrayInfo);
// 构建保存数据
var feedingList = BuildFeedingEntityList(mesResult.TrayInfo, finalGrade, gradeRemark);
await SaveFeedingDataBatchAsync(feedingList);
// 反馈结果到PLC
await FeedBackToPLCLayer(pf, mesResult.TrayInfo, finalGrade);
LogHelper.Instance.WriteLog($"Step 12: 结束 FeedingStation - {DateTime.Now:HH:mm:ss.fff}", "ZuPanLog");
}
catch (Exception ex)
{
LogHelper.Instance.WriteLog($"电池上料组盘线程处理异常:{ex}", "ZuPanLog");
await WritePLCInt16Sync(pf, pf.VarList[5].VarAddress, 2);
}
finally
{
sw.Stop();
CommonMethods.AddDataMonitorLog(0, "组盘上料共耗时".Translated() + $":{sw.Elapsed.TotalMilliseconds}ms");
}
}
/// <summary>
/// 清空电池条码区域
/// </summary>
private async Task ClearBatteryBarcodeArea(PlcGroup pf)
{
const int BARCODE_SIZE = 40;
int trayCapacity = 40;
string strBatteryIDBar = "";
for (int i = 0; i < trayCapacity; i++)
{
strBatteryIDBar += GetString_0(BARCODE_SIZE);
}
byte[] codeBytes = Encoding.Default.GetBytes(strBatteryIDBar).ByteReverse();
var plcDevice = GetPlcDevice(pf.PlcNum);
if (plcDevice != null)
{
plcDevice.WriteValue(pf.VarList[2].VarAddress, codeBytes, PLC.DataType.ArrByte);
}
}
/// <summary>
/// 写入电池条码到PLC
/// </summary>
private async Task WriteBatteryBarcodesToPLC(PlcGroup pf, TrayInfo trayInfo)
{
const int BARCODE_SIZE = 40;
string batteryIDBar = string.Concat(trayInfo.cellList.Select(cell =>
cell.cellNo + GetString_0(BARCODE_SIZE - cell.cellNo.Length)));
byte[] codeBytes = Encoding.Default.GetBytes(batteryIDBar).ByteReverse();
await WritePLCByteArray(pf, pf.VarList[2].VarAddress, codeBytes);
}
/// <summary>
/// 构建上料实体列表
/// </summary>
private List<AGearEntity> BuildFeedingEntityList(TrayInfo trayInfo, string grade, string gradeRemark)
{
return trayInfo.cellList.Select((cell, i) => new AGearEntity
{
Location = cell.channel.ToString(),
Time = DateTime.Now,
BarCode = cell.cellNo,
TrayID = trayInfo.trayNo,
Result = string.IsNullOrEmpty(gradeRemark) ? "OK" : "NG",
Remark = gradeRemark,
Grade = grade,
Model = trayInfo.productCode ?? CommonMethods.mesConfig.ModelName,
GongWei = CommonMethods.sysConfig.GongWei,
}).ToList();
}
/// <summary>
/// 反馈上料结果到PLC
/// </summary>
private async Task FeedBackToPLCLayer(PlcGroup pf, TrayInfo trayInfo, string grade)
{
short model = (short)CommonMethods.mesConfig.GetModel(trayInfo?.productCode ?? CommonMethods.mesConfig.ModelName);
if (model == 0) model = 1;
short gradeCode = 99;
if (!string.IsNullOrEmpty(grade))
gradeCode = (short)CommonMethods.GetGradeNo(grade);
await WritePLCInt16Sync(pf, pf.VarList[3].VarAddress, model);
await WritePLCInt16Sync(pf, pf.VarList[4].VarAddress, gradeCode);
await WritePLCInt16Sync(pf, pf.VarList[5].VarAddress, 1);
CommonMethods.AddDataMonitorLog(0, $"电池进站[{trayInfo.trayNo}]:反馈PLC:[{pf.VarList[2].VarAddress}]写型号{model}(mes获取的原型号:{trayInfo?.productCode}); [{pf.VarList[3].VarAddress}]写挡位{gradeCode}({grade})");
LogHelper.Instance.WriteError($"电池进站[{trayInfo.trayNo}]:反馈PLC:[{pf.VarList[2].VarAddress}]写型号{model}(mes获取的原型号:{trayInfo?.productCode}); [{pf.VarList[3].VarAddress}]写挡位{gradeCode}({grade})", "ZuPanLog");
}
/// <summary>
/// 处理上料错误
/// </summary>
private async Task HandleFeedingStationError(PlcGroup pf, string errorMessage, string trayID, TrayInfo trayInfo)
{
var entity = new AGearEntity
{
Time = DateTime.Now,
BarCode = "",
TrayID = trayID ?? "",
Result = "NG",
Remark = errorMessage,
Grade = "",
Model = trayInfo?.productCode ?? "",
GongWei = CommonMethods.sysConfig.GongWei,
};
_uiSyncContext.Post(_ => CommonMethods.ShowFeedingDelegate?.Invoke(new List<AGearEntity> { entity }), null);
await WritePLCInt16Sync(pf, pf.VarList[5].VarAddress, 2);
}
#region 保存上料数据
/// <summary>
/// 保存上料数据
/// </summary>
/// <param name="list"></param>
private async Task SaveFeedingDataBatchAsync(List<AGearEntity> list)
{
if (list == null || list.Count == 0)
return;
try
{
var barcodes = list.Select(x => x.BarCode).ToList();
var existing = CommonMethods.db.QueryWhereList<AGearEntity>()
.Where(x => barcodes.Contains(x.BarCode))
.Select(x => x.BarCode)
.ToList();
var toUpdate = list.Where(x => existing.Contains(x.BarCode)).ToList();
var toInsert = list.Where(x => !existing.Contains(x.BarCode)).ToList();
// 批量更新、批量插入
if (toUpdate.Any())
await CommonMethods.db.UpdateAsync<AGearEntity>(toUpdate);
if (toInsert.Any())
await CommonMethods.db.AddReturnBoolAsync<AGearEntity>(toInsert);
// UI更新(主线程)
_uiSyncContext.Post(_ => CommonMethods.ShowFeedingDelegate?.Invoke(list), null);
}
catch (Exception ex)
{
LogHelper.Instance.WriteError($"组盘工位保存处理异常:{ex}", "SysErrorLog");
throw; // 重新抛出异常,让上层知道保存失败
}
finally
{
//_dbSemaphore.Release();
}
}
#endregion
#endregion
#region 满箱上传
/// <summary>
/// 组盘
/// </summary>
/// <param name="bArrays"></param>
/// <param name="plcModel"></param>
/// <returns></returns>
private async Task PackStation(JYResult<byte[]> bArrays, object plcModel)
{
LogHelper.Instance.WriteLog($"Step 1: 进入 PackStation - {DateTime.Now:HH:mm:ss.fff}");
PlcGroup pf = plcModel as PlcGroup;
if (pf == null)
{
LogHelper.Instance.WriteError("PLC模型转换失败", "ZuPanLog");
UpdatePLCStatus(pf, 999, 2, 2, 2, 999);
return;
}
using (var timer = new TimedOperation("电池满层绑定", (elapsedMs, operationName) =>
{
LogHelper.Instance.WriteLog($"电池满层绑定共耗时:{elapsedMs}ms", "ZuPanLog");
CommonMethods.AddDataMonitorLog(0, $"电池满层绑定共耗时:{operationName} 执行耗时:{elapsedMs}ms");
}))
{
if (bArrays.IsSuccess && bArrays.Content != null)
{
await ProcessPackStation(bArrays, pf).ConfigureAwait(false);
}
}
}
private async Task ProcessPackStation(JYResult<byte[]> bArrays, PlcGroup pf)
{
try
{
LogHelper.Instance.WriteLog($"Step 2: PackStation ParseAndValidateData - {DateTime.Now:HH:mm:ss.fff}", "ZuPanLog");
var parseTask = ParseAndValidateData(bArrays, pf);
int timeout = 200;
if (await Task.WhenAny(parseTask, Task.Delay(timeout)) == parseTask)
{
LogHelper.Instance.WriteLog($"Step 3: PackStation ParseAndValidateData完成 - {DateTime.Now:HH:mm:ss.fff}", "ZuPanLog");
// 正常返回
var parsedData = await parseTask;
if (!parsedData.IsValid)
{
LogHelper.Instance.WriteLog($"Step 4: PackStation HandleInvalidData - {DateTime.Now:HH:mm:ss.fff}", "ZuPanLog");
await HandleInvalidData(parsedData, pf);
return;
}
LogHelper.Instance.WriteLog($"Step 4: PackStation ProcessBinding - {DateTime.Now:HH:mm:ss.fff}", "ZuPanLog");
// 处理绑定逻辑
await ProcessBinding(parsedData, pf);
}
else
{
LogHelper.Instance.WriteLog($"Step 3: PackStation ParseAndValidateData超时 - {DateTime.Now:HH:mm:ss.fff}", "ZuPanLog");
// 超时,直接返回,
LogHelper.Instance.WriteError($"PLC解析超时({timeout}ms)", "ZuPanLog");
UpdatePLCStatus(pf, 999, 2, 2, 2, 999);
return;
}
}
catch (Exception ex)
{
LogHelper.Instance.WriteError($"电池满层绑定线程处理异常:{ex}", "ZuPanLog");
// 异常时反馈NG状态
UpdatePLCStatus(pf, 999, 2, 2, 2, 999);
}
}
#endregion
#region 保存叠盘数据
/// <summary>
/// 保存叠盘工位数据
/// </summary>
/// <param name="list"></param>
private async Task SaveBlankingDataBatchAsync(List<BGearEntity> list)
{
if (list == null || list.Count == 0) return;
await _dbSemaphore.WaitAsync().ConfigureAwait(false);
//数据的保存
try
{
if (list != null && list.Count > 0)
{
//数据显示
foreach (BGearEntity m in list)
{
string strRes = "";
if (m.VassoioID != "NG")
{
bool b = await CommonMethods.db.AddReturnBoolAsync<BGearEntity>(m);
}
else
{
strRes = ",不存储";
}
m.Remark = m.Remark + strRes;
}
//数据显示
//CommonMethods.ShowBlankingDelegate.Invoke(list);
// UI更新
_uiSyncContext.Post(_ => CommonMethods.ShowBlankingDelegate?.Invoke(list), null);
}
else
{
LogHelper.Instance.WriteError($"组盘工位数据List为空", "SysErrorLog");
}
//// 过滤有效数据
//var validList = list.Where(m => m.VassoioID != "NG").ToList();
//if (validList.Any())
//{
// await CommonMethods.db.Insertable(validList).ExecuteCommandAsync().ConfigureAwait(false);
//}
}
catch (Exception ex)
{
LogHelper.Instance.WriteError($"组盘数据保存异常:{ex}", "SysErrorLog");
}
finally
{
//_dbSemaphore.Release();
}
}
#endregion
#region 初始化方法
// 初始化PLC状态
private void InitializePLCStatus(PlcGroup pf)
{
////电池条码读取结果
//CommonMethods.plcDevices[pf.PlcNum - 1].WriteInt16(pf.VarList[4].VarAddress, 0);
////卡板码读取结果反馈
//CommonMethods.plcDevices[pf.PlcNum - 1].WriteInt16(pf.VarList[5].VarAddress, 0);
////绑定结果反馈
//CommonMethods.plcDevices[pf.PlcNum - 1].WriteInt16(pf.VarList[6].VarAddress, 0);
////绑定处理完成反馈
//CommonMethods.plcDevices[pf.PlcNum - 1].WriteInt16(pf.VarList[7].VarAddress, 0);
_ = ClearPLCRegistersBatch(pf, new[] { 4, 5, 6, 7 }, 0);
}
#endregion
#region 数据解析和验证
private async Task<ParsedData> ParseAndValidateData(JYResult<byte[]> bArrays, PlcGroup pf)
{
var parsedData = new ParsedData();
const int BARCODE_SIZE = 40;
string containerCode = "";
var plcDevice = GetPlcDevice(pf.PlcNum);
// 读取跺号
int stationNo = 0;
if (plcDevice != null)
{
stationNo = plcDevice.ReadInt16(pf.VarList[4].VarAddress);
}
if (stationNo != 0 )
{
JYResult<byte[]> kbArrays = plcDevice.ReadValue<JYResult<byte[]>>(
$"{pf.VarList[3].VarAddress}",
PLC.DataType.ArrByte,
Convert.ToUInt16(pf.VarList[3].OffsetOrLength));
// 从3个可能的地址读取卡板码
//for (int i = 0; i < 3; i++)
{
string code = FormatBarcode(StringLib.GetStringFromByteArrayByEncoding(
kbArrays.Content.ByteReverse(),
(stationNo - 1) * BARCODE_SIZE,
BARCODE_SIZE,
Encoding.ASCII));
if (!string.IsNullOrEmpty(code) && code.ToUpper() != "ERROR" && code.Length >= 8)
{
containerCode = code;
//stationNo = i + 1;
//break;
}
}
}
// 读取层数
short layerNum = 0;
if (plcDevice != null)
{
layerNum = plcDevice.ReadInt16(pf.VarList[2].VarAddress);
}
parsedData.ContainerCode = (string)containerCode;
parsedData.StationNo = stationNo;
parsedData.LayerNum = layerNum;
// 验证基础数据
var (ret, errType, errMessage) = ValidateBasicData(containerCode, stationNo, layerNum);
if (!ret)
{
parsedData.ErrorType = errType;
parsedData.ErrorMessage = errMessage;
parsedData.IsValid = ret;
return parsedData;
}
// 解析电池条码
var batteryInfos = ParseBatteryBarcodes(bArrays, layerNum);
var validBarcodeCount = batteryInfos.Count(b => !string.IsNullOrEmpty(b.identification) && b.identification.ToUpper() != "ERROR");
parsedData.BatteryInfos = batteryInfos;
parsedData.ValidBarcodeCount = validBarcodeCount;
// 验证电池数据
var (batteryRet, batErrType, batErrMessage) = ValidateBatteryData(validBarcodeCount);
if (!batteryRet)
{
parsedData.ErrorType = batErrType;
parsedData.ErrorMessage = batErrMessage;
parsedData.IsValid = batteryRet;
return parsedData;
}
string grade = "";
// 读取电池等级(从第一个有效条码获取)
if (validBarcodeCount > 0)
{
grade = await GetBatteryGrade(batteryInfos.First(b => !string.IsNullOrEmpty(b.identification) && b.identification.ToUpper() != "ERROR").identification).ConfigureAwait(false);
parsedData.Grade = grade;
}
if(parsedData.Grade == "")
{
parsedData.Grade = MesConfig.GetLocGrade(stationNo, CommonMethods.curFdConfig);//CommonMethods.mesConfig.GetGradeNo("A").ToString();
}
parsedData.IsValid = true;
return parsedData;
}
private async Task<(string, int)> ParseContainerCode(PlcGroup pf)
{
//await _plcSemaphore.WaitAsync().ConfigureAwait(false);
try
{
// 将PLC读取和解析放到后台线程执行
//var cts = new CancellationTokenSource(_plcTimeout);
return await Task.Run(() =>
{
const int BARCODE_SIZE = 40;
const int BARCODE_ACTUAL_LENGTH = 39;
string containerCode = "";
int stationNo = 0;
var plcDevice = GetPlcDevice(pf.PlcNum);
if (plcDevice != null)
{
stationNo = plcDevice.ReadInt16(pf.VarList[4].VarAddress);
JYResult<byte[]> kbArrays = plcDevice.ReadValue<JYResult<byte[]>>(
$"{pf.VarList[3].VarAddress}",
PLC.DataType.ArrByte,
Convert.ToUInt16(pf.VarList[3].OffsetOrLength));
// 从3个可能的地址读取卡板码
if(stationNo != 0)
{
string code = FormatBarcode(StringLib.GetStringFromByteArrayByEncoding(
kbArrays.Content.ByteReverse(),
(stationNo - 1) * BARCODE_SIZE,
BARCODE_ACTUAL_LENGTH,
Encoding.ASCII));
if (!string.IsNullOrEmpty(code) && code.ToUpper() != "ERROR" && code.Length >= 8)
{
containerCode = code;
//stationNo = i + 1;
//break;
}
}
}
return (containerCode, stationNo);
});
}
catch (TaskCanceledException)
{
LogHelper.Instance.WriteError($"PLC读取容器码超时", "SysErrorLog");
return ("", 0);
}
finally
{
_plcSemaphore.Release();
}
}
private List<BatteryInfo> ParseBatteryBarcodes(JYResult<byte[]> bArrays, short layerNum)
{
const int BARCODE_SIZE = 40;
int maxBatteryNum = CommonMethods.mesConfig.rowsPerLayer * CommonMethods.mesConfig.colsPerLayer;
var batteryInfos = new List<BatteryInfo>(maxBatteryNum);
for (int i = 0; i < maxBatteryNum; i++)
{
string location = ParseLocation(layerNum, i);
string barcode = FormatBarcode(StringLib.GetStringFromByteArrayByEncoding(
bArrays.Content.ByteReverse(),
i * BARCODE_SIZE,
BARCODE_SIZE,
Encoding.ASCII));
LogHelper.Instance.WriteLog($"PLC读取绑盘条码{i}-{barcode}-{location}", "绑盘条码");
if (string.IsNullOrEmpty(barcode) || barcode.Length < 8)
{
LogHelper.Instance.WriteLog($"PLC读取绑盘条码{i}-{barcode}-{location}异常!!!", "绑盘条码");
continue;
}
batteryInfos.Add(new BatteryInfo
{
identification = barcode,
locationRow = location
});
if (!string.IsNullOrEmpty(barcode) && barcode.ToUpper() != "ERROR")
{
LogHelper.Instance.WriteLog($"电池条码:{barcode},序号:{i + 1}, 位置:{location},层数:{layerNum}");
}
}
return batteryInfos;
}
private (bool, ErrorType, string) ValidateBasicData(string containerCode, int stationNo, int layerNum)
{
string errorMessage = "";
ErrorType errorType = ErrorType.None;
// 验证卡板码
if (string.IsNullOrEmpty(containerCode) ||
containerCode.ToUpper().Trim() == "ERROR" ||
containerCode.ToUpper().Trim() == "NG" || containerCode.Length < 8)
{
errorType = ErrorType.ContainerCodeEmpty;
errorMessage = "箱唛码NG";
return (false, errorType, errorMessage);
}
// 验证 机架号
if (stationNo < 1 || stationNo > 3)
{
errorType = ErrorType.InvalidStation;
errorMessage = $"机架号NG{stationNo}";
return (false, errorType, errorMessage);
}
// 验证层数范围
if (layerNum < 1 || layerNum > CommonMethods.mesConfig.packLayers)
{
errorType = ErrorType.InvalidLayer;
errorMessage = $"层数NG{layerNum}";
return (false, errorType, errorMessage);
}
return (true, errorType, errorMessage);
}
private (bool, ErrorType, string) ValidateBatteryData(int validBarcodeCount)
{
string errMessage = "";
ErrorType errorType = ErrorType.None;
int maxBatteryNum = CommonMethods.mesConfig.rowsPerLayer * CommonMethods.mesConfig.colsPerLayer;
// 验证是否有有效条码
if (validBarcodeCount == 0)
{
errorType = ErrorType.NoValidBarcodes;
errMessage = "电池信息列表为空";
return (false, errorType, errMessage);
}
// 验证是否满盘
if (validBarcodeCount != maxBatteryNum)
{
errorType = ErrorType.NotFullPack;
errMessage = $"电池未满盘,有效条码:{validBarcodeCount},应满盘:{maxBatteryNum}";
return (false, errorType, errMessage);
}
return (true, errorType, errMessage);
}
private async Task<string> GetBatteryGrade(string barcode)
{
try
{
await _dbSemaphore.WaitAsync();
var entities = await Task.Run(() => CommonMethods.db.QueryWhereList<AGearEntity>(it => it.BarCode == barcode)).ConfigureAwait(false);
if (entities == null || entities.Count == 0)
{
LogHelper.Instance.WriteError($"本地数据库无该电池条码{barcode}", "SysErrorLog");
return string.Empty;
}
return entities[0].Grade;
}
catch (Exception ex)
{
LogHelper.Instance.WriteError($"本地数据库无该电池条码{barcode}:{ex}", "SysErrorLog");
return string.Empty;
}
finally
{
_dbSemaphore.Release();
}
}
#endregion
#region 数据处理方法
private async Task HandleInvalidData(ParsedData data, PlcGroup pf)
{
// 创建基础实体
var bGearEntity = CreateBaseBGearEntity(data);
short batteryReadResult = 0;
short containerReadResult = 0;
short bindingResult = 0;
short processCompleteLayer = 999;
switch (data.ErrorType)
{
case ErrorType.ContainerCodeEmpty:
bGearEntity.Result = "NG";
bGearEntity.Remark = data.ErrorMessage;
batteryReadResult = 2;
containerReadResult = 2;
bindingResult = 2;
processCompleteLayer = data.LayerNum;
break;
case ErrorType.InvalidLayer:
bGearEntity.Result = "NG";
bGearEntity.Remark = data.ErrorMessage;
batteryReadResult = 2;
containerReadResult = 2;
bindingResult = 2;
processCompleteLayer = data.LayerNum;
break;
case ErrorType.NoValidBarcodes:
bGearEntity.Result = "NG";
bGearEntity.Remark = data.ErrorMessage;
batteryReadResult = 2;
containerReadResult = 1;
bindingResult = 2;
processCompleteLayer = data.LayerNum;
break;
case ErrorType.NotFullPack:
bGearEntity.Result = "NG";
bGearEntity.Remark = data.ErrorMessage ?? "电池未满盘,不绑定";
batteryReadResult = 1;
containerReadResult = 1;
bindingResult = 2;
processCompleteLayer = data.LayerNum;
break;
}
// 显示结果
var bGearlist = new List<BGearEntity> { bGearEntity };
CommonMethods.ShowBlankingDelegate?.Invoke(bGearlist);
UpdatePLCStatus(pf, (short)(data.LayerNum == 0? 999: data.LayerNum),
batteryReadResult,
containerReadResult,
bindingResult,
processCompleteLayer);
// 记录日志
CommonMethods.AddDataMonitorLog(1, data.ErrorMessage);
}
private async Task ProcessBinding(ParsedData data, PlcGroup pf)
{
// 创建基础实体
var bGearEntity = CreateBaseBGearEntity(data);
bGearEntity.Total = data.ValidBarcodeCount;
bGearEntity.Qty = data.Grade;
LogHelper.Instance.WriteLog($"Step 5: PackStation ProcessMESBinding - {DateTime.Now:HH:mm:ss.fff}");
await ProcessMESBinding(data, pf, bGearEntity);
}
private async Task ProcessMESBinding(ParsedData data, PlcGroup pf, BGearEntity bGearEntity)
{
try
{
// 合并电池信息
var totalBatteryInfos = new List<BatteryInfo>();
totalBatteryInfos.AddRange(data.BatteryInfos);
if (data.LayerNum == CommonMethods.mesConfig.packLayers)
{
LogHelper.Instance.WriteLog($"Step 6: PackStation QueryWhereList - {DateTime.Now:HH:mm:ss.fff}");
// 查询数据库中已存在的绑定记录
var existingBatteries = CommonMethods.db.QueryWhereList<BatteryEntity>(
it => it.containerCode == data.ContainerCode)
.Select(a => new BatteryInfo
{
identification = a.identification,
locationRow = a.location //(a.location).Replace("-", "")
})
.GroupBy(b => new { b.identification, b.locationRow })
.Select(g => g.First()) // 保留每组的第一条记录
.ToList();
totalBatteryInfos.AddRange(existingBatteries);
totalBatteryInfos = totalBatteryInfos
.GroupBy(b => new { b.identification, b.locationRow })
.Select(g => g.First())
.ToList();
bGearEntity.Total = totalBatteryInfos.Count;
LogHelper.Instance.WriteLog($"Step 7: PackStation CallMESPackLoadAsync - {DateTime.Now:HH:mm:ss.fff}");
// 调用MES接口
var mesResult = await CallMESPackLoadAsync(data, totalBatteryInfos);
if (!mesResult.Success)
{
bGearEntity.OutSuccTotal = 0;
LogHelper.Instance.WriteLog($"Step 8: PackStation HandleMESRequestFailed - {DateTime.Now:HH:mm:ss.fff}");
await HandleMESRequestFailed(data, pf, bGearEntity, mesResult.ErrorMessage);
return;
}
LogHelper.Instance.WriteLog($"Step 8: PackStation ProcessMESResult - {DateTime.Now:HH:mm:ss.fff}");
// 处理MES返回结果
await ProcessMESResult(data, pf, bGearEntity, mesResult).ConfigureAwait(false);
//string palletId = "";
//var upParamResult = await CallMESUpResultParamAsync(data.ContainerCode, palletId, totalBatteryInfos);
}
else
{
LogHelper.Instance.WriteLog($"Step 6: PackStation ProcessTempResult - {DateTime.Now:HH:mm:ss.fff}");
// 处理MES返回结果
await ProcessTempResult(data, pf, bGearEntity).ConfigureAwait(false);
}
}
catch (Exception ex)
{
LogHelper.Instance.WriteError($"MES绑定处理异常:{ex}", "SysErrorLog");
UpdatePLCStatus(pf, data.LayerNum, 1, 1, 2, data.LayerNum);
}
}
private async Task<MESPackLoadResult> CallMESPackLoadAsync(ParsedData data, List<BatteryInfo> totalBatteryInfos)
{
if (!CommonMethods.sysConfig.FinishSwitching)
{
var result = await CommonMethods.hbgMes.PackLoadAsync(
CommonMethods.mesConfig.packLoadUrl,
CommonMethods.mesConfig.siteCode,
CommonMethods.mesConfig.lineCode,
CommonMethods.mesConfig.equipNum,
data.ContainerCode,
totalBatteryInfos.Count,
data.Grade,
CommonMethods.mesConfig.MaterialCode,
totalBatteryInfos
);
return new MESPackLoadResult
{
Success = result.Item1,
ErrorMessage = result.Item2,
ResType = (short)result.Item3,
MaterialPackInfos = result.Item4
};
}
else
{
var result = await CommonMethods.hbgMes.PackLoadFinishAsync(
CommonMethods.mesConfig.finishedPackUrl,
CommonMethods.mesConfig.siteCode,
CommonMethods.mesConfig.lineCode,
CommonMethods.mesConfig.equipNum,
data.ContainerCode,
totalBatteryInfos.Count,
data.Grade,
CommonMethods.mesConfig.MaterialCode,
totalBatteryInfos
);
return new MESPackLoadResult
{
Success = result.Item1,
ErrorMessage = result.Item2,
ResType = (short)result.Item3,
MaterialPackInfos = result.Item4
};
}
}
private async Task ProcessMESResult(ParsedData data, PlcGroup pf, BGearEntity bGearEntity, MESPackLoadResult mesResult)
{
var batteryEntities = new List<BatteryEntity>();
bool allSuccess = true;
LogHelper.Instance.WriteLog($"Step 9: PackStation 整合 - {DateTime.Now:HH:mm:ss.fff}");
// 处理每个电池的绑定结果
var matpackDict = mesResult.MaterialPackInfos.ToDictionary(m => m.identification, StringComparer.OrdinalIgnoreCase);
foreach (var batteryInfo in data.BatteryInfos)
{
if (matpackDict.TryGetValue(batteryInfo.identification, out var matpackInfo))
{
batteryEntities.Add(new BatteryEntity
{
identification = matpackInfo.identification,
location = $"{matpackInfo.locationRow[0]}-{matpackInfo.locationRow[1]}-{matpackInfo.locationRow.Substring(2)}",
packResult = mesResult.Success,
packFalseReason = mesResult.ErrorMessage,
containerCode = data.ContainerCode,
grade = data.Grade,
outStationType = 1
});
if (!matpackInfo.packResult)
{
allSuccess = false;
}
}
}
LogHelper.Instance.WriteLog($"Step 10: PackStation SaveBatteryEntitiesBatchAsync 开始 - {DateTime.Now:HH:mm:ss.fff}");
// 保存到数据库
await SaveBatteryEntitiesBatchAsync(batteryEntities);
LogHelper.Instance.WriteLog($"Step 11: PackStation SaveBatteryEntitiesBatchAsync 结束 - {DateTime.Now:HH:mm:ss.fff}");
// 更新UI
//CommonMethods.ShowBatteryNGRsnDelegate?.Invoke(batteryEntities);
//CommonMethods.ShowBatteryStatusDelegate?.Invoke(data.StationNo, batteryEntities);
//CommonMethods.ShowBatteryQRCoderDelegate?.Invoke(batteryEntities);
// 设置结果
if (allSuccess)
{
bGearEntity.Result = "OK";
bGearEntity.Remark = "电芯绑定成功";
UpdatePLCStatus(pf, data.LayerNum, 1, 1, 1, data.LayerNum);
}
else
{
bGearEntity.Result = "绑定_NG";
bGearEntity.Remark = "部分电芯绑定失败";
UpdatePLCStatus(pf, data.LayerNum, 1, 1, 2, data.LayerNum);
}
bGearEntity.OutSuccTotal = batteryEntities.Where(m => m.packResult).Count();
LogHelper.Instance.WriteLog($"Step 12: PackStation ShowBlankingDelegate 开始 - {DateTime.Now:HH:mm:ss.fff}");
// 显示结果
var bGearlist = new List<BGearEntity> { bGearEntity };
CommonMethods.ShowBlankingDelegate?.Invoke(bGearlist);
LogHelper.Instance.WriteLog($"Step 13: PackStation ShowBlankingDelegate 结束 - {DateTime.Now:HH:mm:ss.fff}");
// 保存到本地数据库
if (bGearEntity.Result == "OK")
{
LogHelper.Instance.WriteLog($"Step 14: PackStation AddReturnBoolAsync 开始 - {DateTime.Now:HH:mm:ss.fff}");
bool saveSuccess = await CommonMethods.db.AddReturnBoolAsync(bGearlist);
CommonMethods.AddDataMonitorLog(1, $"电池满层绑定本地存储{(saveSuccess ? "成功" : "失败")}");
LogHelper.Instance.WriteLog($"Step 15: PackStation AddReturnBoolAsync 结束 - {DateTime.Now:HH:mm:ss.fff}");
}
else
{
LogHelper.Instance.WriteLog($"Step 14: PackStation ng 结果不存库 - {DateTime.Now:HH:mm:ss.fff}");
}
}
private async Task<bool> CallMESUpResultParamAsync(string containerCode, string palletId, List<BatteryInfo> totalBatteryInfos)
{
var result = await CommonMethods.hbgMes.ProducResultParamAsync(
CommonMethods.mesConfig.upResultParamUrl,
CommonMethods.mesConfig.siteCode,
CommonMethods.mesConfig.lineCode,
CommonMethods.mesConfig.equipNum,
CommonMethods.mesConfig.MaterialCode,
CommonMethods.mesConfig.mesUserName,
containerCode,
palletId,
totalBatteryInfos,
MesUploadType.DZ
);
return result;
}
private async Task ProcessTempResult(ParsedData data, PlcGroup pf, BGearEntity bGearEntity)
{
var batteryEntities = new List<BatteryEntity>();
bool allSuccess = true;
foreach (var batteryInfo in data.BatteryInfos)
{
batteryEntities.Add(new BatteryEntity
{
identification = batteryInfo.identification,
location = batteryInfo.locationRow, //$"{batteryInfo.locationRow[0]}-{batteryInfo.locationRow[1]}-{batteryInfo.locationRow.Substring(2)}",
packResult = true,
packFalseReason = "",
containerCode = data.ContainerCode,
grade = data.Grade,
outStationType = 1
});
}
LogHelper.Instance.WriteLog($"Step 7: PackStation SaveBatteryEntitiesBatchAsync 开始 - {DateTime.Now:HH:mm:ss.fff}");
// 保存到数据库
await SaveBatteryEntitiesBatchAsync(batteryEntities);
// 更新UI
//CommonMethods.ShowBatteryNGRsnDelegate?.Invoke(batteryEntities);
//CommonMethods.ShowBatteryStatusDelegate?.Invoke(data.StationNo, batteryEntities);
//CommonMethods.ShowBatteryQRCoderDelegate?.Invoke(batteryEntities);
LogHelper.Instance.WriteLog($"Step 8: PackStation SaveBatteryEntitiesBatchAsync 完成 - {DateTime.Now:HH:mm:ss.fff}");
// 设置结果
//if (allSuccess)
{
bGearEntity.Result = "OK";
bGearEntity.Remark = "待整垛电芯一起绑定";
UpdatePLCStatus(pf, data.LayerNum, 1, 1, 1, data.LayerNum);
}
//else
//{
// bGearEntity.Result = "绑定_NG";
// bGearEntity.Remark = "待整垛电芯绑定";
// UpdatePLCStatus(pf, data.LayerNum, 1, 1, 2, data.LayerNum);
//}
bGearEntity.OutSuccTotal = batteryEntities.Where(m => !m.packResult).Count();
LogHelper.Instance.WriteLog($"Step 9: PackStation ShowBlankingDelegate - {DateTime.Now:HH:mm:ss.fff}");
// 显示结果
var bGearlist = new List<BGearEntity> { bGearEntity };
CommonMethods.ShowBlankingDelegate?.Invoke(bGearlist);
// 保存到本地数据库
if (bGearEntity.Result == "OK")
{
LogHelper.Instance.WriteLog($"Step 10: PackStation AddReturnBoolAsync 开始 - {DateTime.Now:HH:mm:ss.fff}");
bool saveSuccess = await CommonMethods.db.AddReturnBoolAsync(bGearlist).ConfigureAwait(false);
CommonMethods.AddDataMonitorLog(1, $"电池满层绑定本地存储{(saveSuccess ? "成功" : "失败")}");
LogHelper.Instance.WriteLog($"Step 11: PackStation AddReturnBoolAsync 结束 - {DateTime.Now:HH:mm:ss.fff}");
}
else
{
LogHelper.Instance.WriteLog($"Step 10: PackStation ng结果不保存 - {DateTime.Now:HH:mm:ss.fff}");
}
}
private async Task ProcessDebugBinding(ParsedData data, PlcGroup pf, BGearEntity bGearEntity)
{
// 创建模拟的绑定结果
var batteryEntities = new List<BatteryEntity>();
for (int i = 0; i < data.BatteryInfos.Count; i++)
{
var batteryInfo = data.BatteryInfos[i];
bool packResult = true; //(i % 3) != 1; // 模拟部分失败
string packFalseReason = packResult ? null : "模拟失败原因";
batteryEntities.Add(new BatteryEntity
{
identification = string.IsNullOrEmpty(batteryInfo.identification) ? $"DC_{i}" : batteryInfo.identification,
location = batteryInfo.locationRow,
packResult = packResult,
packFalseReason = packFalseReason,
containerCode = data.ContainerCode,
grade = data.Grade,
outStationType = 1
});
}
// 保存到数据库
await SaveBatteryEntitiesBatchAsync(batteryEntities).ConfigureAwait(false);
// 更新UI
//CommonMethods.ShowBatteryNGRsnDelegate?.Invoke(batteryEntities);
//CommonMethods.ShowBatteryStatusDelegate?.Invoke(data.StationNo, batteryEntities);
//CommonMethods.ShowBatteryQRCoderDelegate?.Invoke(batteryEntities);
// 设置结果
bGearEntity.Result = "OK";
bGearEntity.Remark = "调试模式绑定成功";
bGearEntity.OutSuccTotal = batteryEntities.Count;
UpdatePLCStatus(pf, data.LayerNum, 0, 0, 1, data.LayerNum);
// 显示结果
var bGearlist = new List<BGearEntity> { bGearEntity };
CommonMethods.ShowBlankingDelegate?.Invoke(bGearlist);
}
private async Task ProcessDebugMode(ParsedData data, PlcGroup pf, BGearEntity bGearEntity)
{
// 调试模式模拟数据
data.StationNo = data.StationNo == 0 ? 1 : data.StationNo;
data.LayerNum = data.LayerNum == (short)0 ? (short)1 : data.LayerNum;
data.Grade = data.Grade ?? MesConfig.GetLocGrade(data.StationNo, CommonMethods.curFdConfig);
// 生成模拟电池信息
int maxBatteryNum = CommonMethods.mesConfig.rowsPerLayer * CommonMethods.mesConfig.colsPerLayer;
var batteryEntities = new List<BatteryEntity>(maxBatteryNum);
for (int i = 0; i < maxBatteryNum; i++)
{
bool packResult = (i % 5) != 1; // 模拟部分失败
string packFalseReason = packResult ? null : $"错误NG{(i % 5)}";
batteryEntities.Add(new BatteryEntity
{
identification = $"DC_{i}",
location = ParseLocation(data.LayerNum, i),
packResult = packResult,
packFalseReason = packFalseReason,
containerCode = "DEBUG_XM_CODE123",
grade = data.Grade,
outStationType = 1
});
}
// 保存到数据库
await SaveBatteryEntitiesBatchAsync(batteryEntities).ConfigureAwait(false);
// 更新UI
//CommonMethods.ShowBatteryNGRsnDelegate?.Invoke(batteryEntities);
//CommonMethods.ShowBatteryStatusDelegate?.Invoke(data.StationNo, batteryEntities);
//CommonMethods.ShowBatteryQRCoderDelegate?.Invoke(batteryEntities);
// 设置结果
bGearEntity.Result = "NG";
bGearEntity.Remark = "电芯绑定失败(调试模式)";
bGearEntity.OutSuccTotal = batteryEntities.Where(x => x.packResult == true).Count();
bGearEntity.Qty = data.Grade;
bGearEntity.Total = batteryEntities.Count;
// 显示结果
var bGearlist = new List<BGearEntity> { bGearEntity };
CommonMethods.ShowBlankingDelegate?.Invoke(bGearlist);
UpdatePLCStatus(pf, data.LayerNum, 0, 0, 2, data.LayerNum);
}
#endregion
#region 辅助方法
private BGearEntity CreateBaseBGearEntity(ParsedData data)
{
return new BGearEntity
{
TestTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
VassoioID = data.ContainerCode ?? string.Empty,
GongWei = CommonMethods.sysConfig.GongWei,
StackingLayer = data.LayerNum
};
}
private async Task SaveBatteryEntitiesBatchAsync(List<BatteryEntity> batteryEntities)
{
if (batteryEntities == null || !batteryEntities.Any()) return;
try
{
var containerCodes = batteryEntities.Select(m => m.containerCode).Distinct().ToList();
bool b = await CommonMethods.db.AddReturnBoolAsync<BatteryEntity>(batteryEntities);
CommonMethods.AddDataMonitorLog(0, $"电池绑定数据批量保存成功");
}
catch (Exception ex)
{
LogHelper.Instance.WriteError($"电池绑定保存异常:{ex}", "SysErrorLog");
}
finally
{
}
}
private void UpdatePLCStatus(PlcGroup pf, short layerNum,
short batteryReadResult, short containerReadResult,
short bindingResult, short processCompleteLayer)
{
var dict = new Dictionary<string, short>();
if (batteryReadResult != 0) dict[pf.VarList[5].VarAddress] = batteryReadResult; // 电池条码读取结果反馈
if (containerReadResult != 0) dict[pf.VarList[6].VarAddress] = containerReadResult; // 卡板码读取结果反馈
if (bindingResult != 0) dict[pf.VarList[7].VarAddress] = bindingResult; // 绑定结果反馈
if (processCompleteLayer != 0) dict[pf.VarList[8].VarAddress] = processCompleteLayer; // 绑定处理完成反馈
WritePLCInt16Sync(pf, pf.VarList[5].VarAddress, batteryReadResult);
WritePLCInt16Sync(pf, pf.VarList[6].VarAddress, containerReadResult);
WritePLCInt16Sync(pf, pf.VarList[7].VarAddress, bindingResult);
WritePLCInt16Sync(pf, pf.VarList[8].VarAddress, processCompleteLayer);
}
private async Task HandleMESDisconnected(ParsedData data, PlcGroup pf, BGearEntity bGearEntity)
{
await Task.Run(() =>
{
bGearEntity.Result = "MES_NG";
bGearEntity.Remark = "MES连接中断";
//var bGearlist = new List<BGearEntity> { bGearEntity };
//CommonMethods.ShowBlankingDelegate?.Invoke(bGearlist);
//CommonMethods.AddDataMonitorLog(1, "MES连接中断,停止上传");
//CommonMethods.plcDevices[pf.PlcNum - 1].WriteInt16("D80", 1);
//UpdatePLCStatus(pf, data.LayerNum, 1, 1, 2, data.LayerNum);
var bGearlist = new List<BGearEntity> { bGearEntity };
_uiSyncContext.Post(_ => CommonMethods.ShowBlankingDelegate?.Invoke(bGearlist), null);
CommonMethods.AddDataMonitorLog(1, "MES连接中断,停止上传");
_ = WritePLCInt16Sync(pf, "D80", 1);
UpdatePLCStatus(pf, data.LayerNum, 1, 1, 2, data.LayerNum);
});
}
private async Task HandleMESRequestFailed(ParsedData data, PlcGroup pf, BGearEntity bGearEntity, string errorMessage)
{
await Task.Run(() =>
{
bGearEntity.Result = "MES_NG";
bGearEntity.Remark = $"MES请求失败:{errorMessage}";
var bGearlist = new List<BGearEntity> { bGearEntity };
_uiSyncContext.Post(_ => CommonMethods.ShowBlankingDelegate?.Invoke(bGearlist), null);
CommonMethods.AddDataMonitorLog(1, $"MES请求失败:{errorMessage}");
_ = WritePLCInt16Sync(pf, "D80", 1);
UpdatePLCStatus(pf, data.LayerNum, 1, 1, 2, data.LayerNum);
});
}
private async Task HandleMESDataMismatch(ParsedData data, PlcGroup pf, BGearEntity bGearEntity, MESPackLoadResult mesResult)
{
await Task.Run(() =>
{
CommonMethods.AddDataMonitorLog(1,
$"返回数据数量不匹配({mesResult.MaterialPackInfos?.Count ?? 0} != {data.BatteryInfos.Count})");
bGearEntity.Result = "绑定_NG";
bGearEntity.Remark = "部分电芯没有返回绑定结果";
bGearEntity.OutSuccTotal = mesResult.MaterialPackInfos?.Where(x => x.packResult == true).Count() ?? 0;
//var bGearlist = new List<BGearEntity> { bGearEntity };
//CommonMethods.ShowBlankingDelegate?.Invoke(bGearlist);
//UpdatePLCStatus(pf, data.LayerNum, 1, 1, 2, data.LayerNum);
var bGearlist = new List<BGearEntity> { bGearEntity };
_uiSyncContext.Post(_ => CommonMethods.ShowBlankingDelegate?.Invoke(bGearlist), null);
UpdatePLCStatus(pf, data.LayerNum, 1, 1, 2, data.LayerNum);
});
}
#endregion
#region 数据模型
private class ParsedData
{
public bool IsValid { get; set; }
public string ContainerCode { get; set; }
public short LayerNum { get; set; }
public List<BatteryInfo> BatteryInfos { get; set; }
public int ValidBarcodeCount { get; set; }
public string Grade { get; set; }
public string ErrorMessage { get; set; }
public ErrorType ErrorType { get; set; }
public int StationNo { get; set; }
}
private class MESPackLoadResult
{
public bool Success { get; set; }
public string ErrorMessage { get; set; }
public short ResType { get; set; }
public List<MaterialPackInfo> MaterialPackInfos { get; set; }
}
private enum ErrorType
{
None,
ContainerCodeEmpty,
InvalidStation,
InvalidLayer,
NoValidBarcodes,
NotFullPack
}
#endregion
#region 强制出站
private Task ForceOutStation(JYResult<byte[]> bArrays, object plcModel)
{
PlcGroup pf = plcModel as PlcGroup;
if (pf == null)
{
LogHelper.Instance.WriteError("PLC模型转换失败", "SysErrorLog");
return Task.CompletedTask;
}
using (var timer = new TimedOperation("强制出站", (elapsedMs, operationName) =>
{
CommonMethods.AddDataMonitorLog(0, $"强制出站共耗时:{operationName} 执行耗时:{elapsedMs}ms");
}))
{
// 空方法直接返回已完成任务,避免异步开销
return Task.CompletedTask;
}
}
/// <summary>
/// 解析位置编码
/// </summary>
/// <param name="number">位置编码(0-99999)</param>
/// <returns>包含层号和位置信息的元组</returns>
public string ParseLocation(int layerNum, int number)
{
if (CommonMethods.mesConfig.colsPerLayer <= 0)
throw new InvalidOperationException("列数配置无效");
Dictionary<int, string> dictRow = new Dictionary<int, string>() { { 1, "A" }, { 2, "B" }, { 3, "C" }, { 4, "D" }, { 5, "E" }, };
if (CommonMethods.mesConfig.ModelName == "LF350S")
{
int row = number / CommonMethods.mesConfig.colsPerLayer + 1;
string rowStr = dictRow[row];
int col = (number % CommonMethods.mesConfig.colsPerLayer) + 1;
return $"{rowStr}{col}";
}
else
{
int row = number / CommonMethods.mesConfig.colsPerLayer + 1;
int col = (number % CommonMethods.mesConfig.colsPerLayer) + 1;
return $"{layerNum}{row}{col}";
}
}
#endregion
#region 料框码解绑
/// <summary>
/// 料框码解绑
/// </summary>
/// <param name="bArrays"></param>
/// <param name="plcModel"></param>
/// <returns></returns>
private async Task UnBindTrayStation(JYResult<byte[]> bArrays, object plcModel)
{
PlcGroup pf = plcModel as PlcGroup;
if (pf == null)
{
LogHelper.Instance.WriteError("PLC模型转换失败", "SysErrorLog");
return;
}
CommonMethods.AddDataMonitorLog(0, $"料框码解绑开始");
int _mesTimeout = 1000;
using (var cts = new CancellationTokenSource(_mesTimeout))
{
if (bArrays.IsSuccess && bArrays.Content != null)
{
byte[] bytes = bArrays.Content.ByteReverse();
//料框码
string trayCode = FormatBarcode(StringLib.GetStringFromByteArrayByEncoding(bytes, 0, 40, System.Text.Encoding.ASCII));
CommonMethods.AddDataMonitorLog(0, $"料框码解绑料况码{trayCode}");
if (string.IsNullOrEmpty(trayCode) || trayCode.ToUpper().Contains("ERROR") || trayCode.ToUpper().Contains("NG"))
{
await WritePLCInt16Sync(pf, pf.VarList[2].VarAddress, 2);
return;
}
try
{
var (success, mesMessage) = await UnbindTrayInfo(trayCode);
{
await WritePLCInt16Sync(pf, pf.VarList[2].VarAddress, success ? (short)1 : (short)2);
LogHelper.Instance.WriteError($"料框码解绑完成:{trayCode},解绑结果:{success},{mesMessage}", "ZuPanLog");
CommonMethods.AddDataMonitorLog(0, $"料框码解绑完成,料框码:{trayCode},解绑结果:{success},{mesMessage}");
}
}
catch (OperationCanceledException)
{
await WritePLCInt16Sync(pf, pf.VarList[2].VarAddress, 2);
LogHelper.Instance.WriteError($"料框码解绑被取消(超时):{trayCode}", "ZuPanLog");
CommonMethods.AddDataMonitorLog(0, $"料框码解绑被取消(超时):{trayCode}");
}
catch (Exception ex)
{
await WritePLCInt16Sync(pf, pf.VarList[2].VarAddress, 2);
LogHelper.Instance.WriteError($"料框码解绑异常:{ex.Message},料框码:{trayCode}", "ZuPanLog");
CommonMethods.AddDataMonitorLog(0, $"料框码解绑异常:{ex.Message},料框码:{trayCode}");
}
}
}
}
// MES查询方法
private async Task<(bool success, string mesMessage)> UnbindTrayInfo(string trayID)
{
//if (!CommonMethods.mesConfig.isConnected)
//{
// //CommonMethods.AddDataMonitorLog(1, "MES连接中断,停止上传");
// return (false, "MES连接中断", MesResType.B, null);
//}
try
{
MesUrlService service = new MesUrlService(CommonMethods.db.Db);
string url = service.GetMesUrl(trayID, ConstValue.INTER_UnBindTray);
if (string.IsNullOrEmpty(url))
{
throw new ArgumentException($"未找到{ConstValue.INTER_UnBindTray}对应的MES接口地址");
}
var (success, mesMessage) = await CommonMethods.hbgMes.UnbindTrayInfosAsync(
url,
CommonMethods.mesConfig.equipNum,
trayID
).ConfigureAwait(false);
return (success, mesMessage);
}
catch (Exception ex)
{
return (false, $"MES解绑失败{ex.Message}");
}
}
#endregion
#region 创建空的数组List
private List<T> GetList<T>(int count, T defaultValue)
{
if (count <= 0)
{
return new List<T>();
}
// Enumerable.Repeat比循环Add更高效,尤其是大数据量
return Enumerable.Repeat(defaultValue, count).ToList();
}
public List<MesResType> GetMesType(int Num)
{
List<MesResType> mesResTypes = new List<MesResType>();
for (int i = 0; i < Num; i++)
{
mesResTypes.Add(MesResType.UnKnow);
}
return mesResTypes;
}
/// <summary>
/// PLC内批量写\0
/// </summary>
/// <param name="Num"></param>
/// <returns></returns>
public string GetString_0(int Num)
{
string ff = "";
for (int i = 0; i < Num; i++)
{
ff = ff.Insert(ff.Length, "\0");
}
return ff;
}
#endregion
#region 辅助方法
/// <summary>
/// 批量清空PLC寄存器
/// </summary>
private async Task ClearPLCRegistersBatch(PlcGroup pf, int[] varIndexes, short value)
{
var dict = varIndexes.ToDictionary(i => pf.VarList[i].VarAddress, _ => value);
await WritePLCInt16Batch(pf, dict);
}
private async Task WritePLCInt16BatchWithTimeout(PlcGroup pf, Dictionary<string, short> addressValues)
{
try
{
// 给PLC写入加超时,避免卡慢
var writeTask = WritePLCInt16Batch(pf, addressValues);
//var timeoutTask = Task.Delay(_plcTimeout);
//var completedTask = await Task.WhenAny(writeTask, timeoutTask).ConfigureAwait(false);
//if (completedTask == timeoutTask)
//{
// //LogHelper.Instance.WriteError($"PLC批量写入超时({_plcTimeout.TotalSeconds}s)", "WuLiuLog");
// throw new TimeoutException("PLC写入超时");
//}
//await writeTask.ConfigureAwait(false);
}
catch (Exception ex)
{
LogHelper.Instance.WriteError($"PLC批量写入异常:{ex.Message}", "WuLiuLog");
// 可考虑重试1次(可选)
// await Task.Delay(100);
// await WritePLCInt16Batch(pf, addressValues).ConfigureAwait(false);
}
}
/// <summary>
/// 批量写入Int16到PLC
/// </summary>
private async Task WritePLCInt16Batch(PlcGroup pf, Dictionary<string, short> addressValueDict)
{
//if (addressValueDict.Count == 0) return;
//var tasks = addressValueDict.Select(kv => WritePLCInt16Sync(pf, kv.Key, kv.Value));
//await Task.WhenAll(tasks);
if (addressValueDict == null || addressValueDict.Count == 0) return;
var plc = GetPlcDevice(pf.PlcNum);
if (plc == null) return;
//await _plcSemaphore.WaitAsync().ConfigureAwait(false);
try
{
// 【关键优化】批量写入,一次通讯完成
foreach (var kv in addressValueDict)
{
plc.WriteInt16(kv.Key, kv.Value);
}
}
finally
{
//_plcSemaphore.Release();
}
}
/// <summary>
/// 写入Int16到PLC
/// </summary>
private async Task WritePLCInt16Sync(PlcGroup pf, string address, short value)
{
try
{
var plc = GetPlcDevice(pf.PlcNum);
if (plc == null) return;
//// 加信号量 + 超时保护
//await _plcSemaphore.WaitAsync(100);
//try
{
plc.WriteInt16(address, value);
}
//finally
//{
// _plcSemaphore.Release();
//}
}
catch (Exception ex)
{
LogHelper.Instance.WriteError($"PLC写入失败(地址:{address}):{ex.Message}", "SysErrorLog");
}
}
/// <summary>
/// 写入字节数组到PLC(带超时)
/// </summary>
private async Task WritePLCByteArray(PlcGroup pf, string address, byte[] data)
{
await _plcSemaphore.WaitAsync();
try
{
//var cts = new CancellationTokenSource(_plcTimeout);
//await Task.Run(() =>
// CommonMethods.plcDevices[pf.PlcNum - 1].WriteValue(address.ToString(), data, PLC.DataType.ArrByte), cts.Token);
await Task.Run(() => GetPlcDevice(pf.PlcNum).WriteValue(address.ToString(), data, PLC.DataType.ArrByte));
}
finally
{
_plcSemaphore.Release();
}
}
/// <summary>
/// 检测数据是否只包含字母加数字
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
private static readonly Regex _alphaNumericRegex = new Regex("^[a-zA-Z0-9]+$", RegexOptions.Compiled);
public bool IsAlphaNumeric(string input)
{
return !string.IsNullOrEmpty(input) && _alphaNumericRegex.IsMatch(input);
}
/// <summary>
/// 格式化条码
/// </summary>
private string FormatBarcode(string barcode)
{
if (string.IsNullOrEmpty(barcode)) return string.Empty;
int index = barcode.IndexOf('\0');
if (index >= 0)
barcode = barcode.Substring(0, index);
return barcode.Replace("\0", "").Replace("\r", "").Replace(" ", "").Trim();
}
#endregion
}
}
//http://10.22.160.3/core/api/public/product/process/param/new/result
//{
// "equipNum": "60PLR03",
// "type": "DZ",
// "payload": "{\"siteCode\":\"60J\",\"lineCode\":\"KW-602\",\"userName\":\"admin\",\"materialCode\":\"81044802\",\"carCode\":\"\",\"recordDate\":\"2026/06/24 12:01:00\",\"qty\":2,\"containerCode\":\"ETE\",\"identificationList\":[{\"identification\":\"ML-10085826-001\",\"qualityStatus\":\"\",\"tagDataVOList\":[{\"tagCode\":\"TRAY_NUMBER_DBJJG\",\"tagValue\":\"\",\"tagTime\":\"2026/06/24 12:01:00\",\"tagCalculateResult\":\"\",\"tagRemark\":\"托盘号\"},{\"tagCode\":\"PALLET_NUMBER_DBJJG\",\"tagValue\":\"\",\"tagTime\":\"2026/06/24 12:01:00\",\"tagCalculateResult\":\"\",\"tagRemark\":\"栈板号\"},{\"tagCode\":\"BATTERY_LOCATION_DBJJG\",\"tagValue\":\"111\",\"tagTime\":\"2026/06/24 12:01:00\",\"tagCalculateResult\":\"\",\"tagRemark\":\"电池位置\"},{\"tagCode\":\"TOP_COVER_CODE_DBJJG\",\"tagValue\":\"\",\"tagTime\":\"2026/06/24 12:01:00\",\"tagCalculateResult\":\"\",\"tagRemark\":\"电芯顶盖码\"}]},{\"identification\":\"ML-10085826-002\",\"qualityStatus\":\"\",\"tagDataVOList\":[{\"tagCode\":\"TRAY_NUMBER_DBJJG\",\"tagValue\":\"\",\"tagTime\":\"2026/06/2412:01:00\",\"tagCalculateResult\":\"\",\"tagRemark\":\"托盘号\"},{\"tagCode\":\"PALLET_NUMBER_DBJJG\",\"tagValue\":\"\",\"tagTime\":\"2026/06/24 12:01:00\",\"tagCalculateResult\":\"\",\"tagRemark\":\"栈板号\"},{\"tagCode\":\"BATTERY_LOCATION_DBJJG\",\"tagValue\":\"4512\",\"tagTime\":\"2026/06/24 12:01:00\",\"tagCalculateResult\":\"\",\"tagRemark\":\"电池位置\"},{\"tagCode\":\"TOP_COVER_CODE_DBJJG\",\"tagValue\":\"\",\"tagTime\":\"2026/06/24 12:01:00\",\"tagCalculateResult\":\"\",\"tagRemark\":\"电芯顶盖码\"}]}]}"
//}