2026-08-04 18:36:40 +08:00
|
|
|
|
|
2026-08-31 13:54:07 +08:00
|
|
|
|
using JinYuan.DAL.Models;
|
|
|
|
|
|
using JinYuan.DAL.Service;
|
2026-08-04 18:36:40 +08:00
|
|
|
|
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)
|
|
|
|
|
|
{
|
2026-08-31 13:54:07 +08:00
|
|
|
|
MaterialFrameRuleService service = new MaterialFrameRuleService(CommonMethods.db.Db);
|
|
|
|
|
|
MaterialFrameRule rule = service.GetRuleByCode(trayInfo.trayNo);
|
|
|
|
|
|
|
2026-08-04 18:36:40 +08:00
|
|
|
|
short model = (short)CommonMethods.mesConfig.GetModel(trayInfo?.productCode ?? CommonMethods.mesConfig.ModelName);
|
|
|
|
|
|
if (model == 0) model = 1;
|
|
|
|
|
|
|
2026-08-31 13:54:07 +08:00
|
|
|
|
// TODO 注释电池型号自动选择逻辑 by clm 20260831
|
|
|
|
|
|
//string modelName = trayInfo?.productCode;
|
|
|
|
|
|
//short model = CommonMethods.mesConfig.GetModelNum(modelName, rule?.Workshop_Code ?? string.Empty);
|
|
|
|
|
|
|
2026-08-04 18:36:40 +08:00
|
|
|
|
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
|
|
|
|
|
|
{
|
2026-08-31 13:54:07 +08:00
|
|
|
|
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接口地址");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-08-04 18:36:40 +08:00
|
|
|
|
var (success, mesMessage, mesResType, trayinfo) = await CommonMethods.hbgMes.QueryMesTrayInfosAsync(
|
2026-08-31 13:54:07 +08:00
|
|
|
|
url,
|
2026-08-04 18:36:40 +08:00
|
|
|
|
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 : 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<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 : 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");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <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
|
|
|
|
|
|
{
|
2026-08-31 13:54:07 +08:00
|
|
|
|
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接口地址");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-08-04 18:36:40 +08:00
|
|
|
|
var (success, mesMessage) = await CommonMethods.hbgMes.UnbindTrayInfosAsync(
|
2026-08-31 13:54:07 +08:00
|
|
|
|
url,
|
2026-08-04 18:36:40 +08:00
|
|
|
|
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\":\"电芯顶盖码\"}]}]}"
|
|
|
|
|
|
//}
|