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设备安全访问
///
/// 安全获取PLC设备
///
/// PLC编号(从1开始)
/// PLC设备实例,若不存在或未连接则返回null
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 物流线料盘来料
///
/// 物流线料盘来料
///
private async Task TrayFeedingStation(JYResult 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 { 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); //反馈处理完成
}
}
}
///
/// 带超时的MES料盘信息获取
///
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);
}
///
/// 等级查询结果
///
private class GradeQueryResult
{
public string Grade { get; set; }
public bool IsConsistent { get; set; }
public string ErrorMessage { get; set; }
}
///
/// 带超时的电池等级查询
///
private async Task 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;
}
///
/// 构建MaterialLot列表
///
private List BuildMaterialLotList(TrayInfo trayInfo)
{
var listCode = new List();
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;
}
///
/// 构建TrayEntity实体
///
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
};
}
///
/// 反馈结果到PLC
///
private async Task FeedBackToPLC(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[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");
}
///
/// 处理错误并反馈PLC
///
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 { 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
{
var (success, mesMessage, mesResType, trayinfo) = await CommonMethods.hbgMes.QueryMesTrayInfosAsync(
CommonMethods.mesConfig.queryTrayUrl,
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 batteryInfo)> QueryGrades(List 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 物流线料盘进站
///
/// 物流线料盘进站
///
private async Task CheckTrayStation(JYResult 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 : CommonMethods.curFdConfig.Grade1;
string add = (gradeResult.Grade == null || string.IsNullOrEmpty(gradeResult.Grade)) ? ",临时使用本机架1垛位的挡位" : "";
string gradeRemark = gradeResult.IsConsistent ? "" : gradeResult.ErrorMessage + add;
// 反馈电池条码到PLC
//await WriteBatteryBarcodesToPLC(pf, mesResult.TrayInfo);
// 构建并保存实体
var mesTrayEntity = BuildTrayEntity(mesResult.TrayInfo, finalGrade, gradeRemark);
await SaveTrayDataBatchAsync(new List { 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); //反馈处理完成
}
}
}
///
/// 进站反馈结果到PLC
///
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");
}
///
/// 处理进站错误
///
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 { 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);
}
///
/// 保存料盘数据
///
///
///
private async Task SaveTrayDataBatchAsync(List 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 电池进站
///
/// 电池进站
///
private async Task FeedingStation(JYResult 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 : CommonMethods.curFdConfig.Grade1;
string add = (gradeResult.Grade == null || string.IsNullOrEmpty(gradeResult.Grade) )? ",临时使用本机架1垛位的挡位" : "";
string gradeRemark = gradeResult.IsConsistent ? "" : gradeResult.ErrorMessage + add;
// 反馈电池条码到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");
}
}
///
/// 清空电池条码区域
///
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);
}
}
///
/// 写入电池条码到PLC
///
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);
}
///
/// 构建上料实体列表
///
private List 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();
}
///
/// 反馈上料结果到PLC
///
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");
}
///
/// 处理上料错误
///
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 { entity }), null);
await WritePLCInt16Sync(pf, pf.VarList[5].VarAddress, 2);
}
#region 保存上料数据
///
/// 保存上料数据
///
///
private async Task SaveFeedingDataBatchAsync(List list)
{
if (list == null || list.Count == 0)
return;
try
{
var barcodes = list.Select(x => x.BarCode).ToList();
var existing = CommonMethods.db.QueryWhereList()
.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(toUpdate);
if (toInsert.Any())
await CommonMethods.db.AddReturnBoolAsync(toInsert);
// UI更新(主线程)
_uiSyncContext.Post(_ => CommonMethods.ShowFeedingDelegate?.Invoke(list), null);
}
catch (Exception ex)
{
LogHelper.Instance.WriteError($"组盘工位保存处理异常:{ex}", "SysErrorLog");
throw; // 重新抛出异常,让上层知道保存失败
}
finally
{
//_dbSemaphore.Release();
}
}
#endregion
#endregion
#region 满箱上传
///
/// 组盘
///
///
///
///
private async Task PackStation(JYResult 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 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 保存叠盘数据
///
/// 保存叠盘工位数据
///
///
private async Task SaveBlankingDataBatchAsync(List 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(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 ParseAndValidateData(JYResult 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 kbArrays = plcDevice.ReadValue>(
$"{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 kbArrays = plcDevice.ReadValue>(
$"{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 ParseBatteryBarcodes(JYResult bArrays, short layerNum)
{
const int BARCODE_SIZE = 40;
int maxBatteryNum = CommonMethods.mesConfig.rowsPerLayer * CommonMethods.mesConfig.colsPerLayer;
var batteryInfos = new List(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 GetBatteryGrade(string barcode)
{
try
{
await _dbSemaphore.WaitAsync();
var entities = await Task.Run(() => CommonMethods.db.QueryWhereList(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 };
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();
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(
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 CallMESPackLoadAsync(ParsedData data, List 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();
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 };
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 CallMESUpResultParamAsync(string containerCode, string palletId, List 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();
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 };
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();
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 };
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(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 };
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 batteryEntities)
{
if (batteryEntities == null || !batteryEntities.Any()) return;
try
{
var containerCodes = batteryEntities.Select(m => m.containerCode).Distinct().ToList();
bool b = await CommonMethods.db.AddReturnBoolAsync(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();
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 };
//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 };
_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 };
_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 };
//CommonMethods.ShowBlankingDelegate?.Invoke(bGearlist);
//UpdatePLCStatus(pf, data.LayerNum, 1, 1, 2, data.LayerNum);
var bGearlist = new List { 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 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 MaterialPackInfos { get; set; }
}
private enum ErrorType
{
None,
ContainerCodeEmpty,
InvalidStation,
InvalidLayer,
NoValidBarcodes,
NotFullPack
}
#endregion
#region 强制出站
private Task ForceOutStation(JYResult 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;
}
}
///
/// 解析位置编码
///
/// 位置编码(0-99999)
/// 包含层号和位置信息的元组
public string ParseLocation(int layerNum, int number)
{
if (CommonMethods.mesConfig.colsPerLayer <= 0)
throw new InvalidOperationException("列数配置无效");
Dictionary dictRow = new Dictionary() { { 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 料框码解绑
///
/// 料框码解绑
///
///
///
///
private async Task UnBindTrayStation(JYResult 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
{
var (success, mesMessage) = await CommonMethods.hbgMes.UnbindTrayInfosAsync(
CommonMethods.mesConfig.unbindTrayUrl,
CommonMethods.mesConfig.equipNum,
trayID
).ConfigureAwait(false);
return (success, mesMessage);
}
catch (Exception ex)
{
return (false, $"MES解绑失败{ex.Message}");
}
}
#endregion
#region 创建空的数组List
private List GetList(int count, T defaultValue)
{
if (count <= 0)
{
return new List();
}
// Enumerable.Repeat比循环Add更高效,尤其是大数据量
return Enumerable.Repeat(defaultValue, count).ToList();
}
public List GetMesType(int Num)
{
List mesResTypes = new List();
for (int i = 0; i < Num; i++)
{
mesResTypes.Add(MesResType.UnKnow);
}
return mesResTypes;
}
///
/// PLC内批量写\0
///
///
///
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 辅助方法
///
/// 批量清空PLC寄存器
///
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 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);
}
}
///
/// 批量写入Int16到PLC
///
private async Task WritePLCInt16Batch(PlcGroup pf, Dictionary 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();
}
}
///
/// 写入Int16到PLC
///
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");
}
}
///
/// 写入字节数组到PLC(带超时)
///
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();
}
}
///
/// 检测数据是否只包含字母加数字
///
///
///
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);
}
///
/// 格式化条码
///
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\":\"电芯顶盖码\"}]}]}"
//}