PLC数采

This commit is contained in:
quowingwang 2026-02-03 17:28:39 +08:00
parent e648d92e75
commit a696db26d4
10 changed files with 461 additions and 298 deletions

View File

@ -209,6 +209,9 @@ namespace Infrastructure.Model
public string ProductModel { get; set; } public string ProductModel { get; set; }
public string ProductSN { get; set; } public string ProductSN { get; set; }
public string IntoStationResp { get; set; } public string IntoStationResp { get; set; }
public string IntoStationResp2 { get; set; }
public string IntoStationResp3 { get; set; }
public string IntoStationResp4 { get; set; }
} }
} }

View File

@ -60,18 +60,24 @@ namespace RIZO.Admin.WebApi.PLC.Service
{ {
{ "设备使能", "DB1000.DBW0" }, // Int { "设备使能", "DB1000.DBW0" }, // Int
{ "工位开始查询结果", "DB1000.DBW2000" }, // Int { "工位开始查询结果", "DB1000.DBW2000" }, // Int
{ "1#产品结束保存结果", "DB1000.DBW2002" }, // Int { "保存结果1", "DB1000.DBW2002" }, // Int
{ "2#产品结束保存结果", "DB1000.DBW2004" }, // Int { "保存结果2", "DB1000.DBW2004" }, // Int
{ "3#产品结束保存结果", "DB1000.DBW2006" }, // Int { "保存结果3", "DB1000.DBW2006" }, // Int
{ "4#产品结束保存结果", "DB1000.DBW2008" }, // Int { "保存结果4", "DB1000.DBW2008" }, // Int
}; };
private readonly Dictionary<string, string> _mesop50IntReturnMap = new() private readonly Dictionary<string, string> _mesop050IntReturnMap = new()
{ {
{ "设备使能", "DB300.DBW0" }, // Int { "设备使能", "DB300.DBW0" }, // Int
{ "1#产品结束保存结果", "DB300.DBW48" }, // Int { "保存结果1", "DB300.DBW48" }, // Int
{ "2#产品结束保存结果", "DB300.DBW204" }, // Int { "保存结果2", "DB300.DBW204" }, // Int
{ "3#产品结束保存结果", "DB300.DBW360" }, // Int { "保存结果3", "DB300.DBW360" }, // Int
{ "4#产品结束保存结果", "DB300.DBW516" }, // Int { "保存结果4", "DB300.DBW516" }, // Int
};
private readonly Dictionary<string, string> _mesop51ScanIntReturnMap = new()
{
{ "设备使能", "DB1000.DBW0" }, // Int
{ "工位开始查询结果", "DB1000.DBW2" }, // Int
{ "保存结果", "DB1000.DBW4" }, // Int
}; };
private readonly Dictionary<string, string> _mesop058IntReturnMap = new() private readonly Dictionary<string, string> _mesop058IntReturnMap = new()
{ {
@ -112,13 +118,14 @@ namespace RIZO.Admin.WebApi.PLC.Service
{ "运行状态", "DB1001.DBW0" }, // Int - 1=空闲2=运行中3=故障 { "运行状态", "DB1001.DBW0" }, // Int - 1=空闲2=运行中3=故障
{ "设备模式", "DB1001.DBW2" }, // Int - 1=空模式2=手动4=初始化8=自动16=CycleStop { "设备模式", "DB1001.DBW2" }, // Int - 1=空模式2=手动4=初始化8=自动16=CycleStop
{ "设备在线状态", "DB1001.DBW4" }, // Int - 1=离线,0=在线 { "设备在线状态", "DB1001.DBW4" }, // Int - 1=离线,0=在线
//{ "ByPass", "DB1001.DBW6" }, // Int - 1=ByPass,0=正常模式 { "ByPass", "DB1001.DBW6" }, // Int - 1=ByPass,0=正常模式
{ "生产模式", "DB1001.DBW8" }, // Int - 1=正常模式;2=清线模式;4=返工模式;8=换型模式;16=预热模式 { "生产模式", "DB1001.DBW8" }, // Int - 1=正常模式;2=清线模式;4=返工模式;8=换型模式;16=预热模式
//{ "实际产量", "DB1001.DBD1104" }, // DInt { "实际产量", "DB1001.DBD1104" }, // DInt
//{ "合格数量", "DB1001.DBD1108" }, // DInt { "合格数量", "DB1001.DBD1108" }, // DInt
//{ "失败数量", "DB1001.DBD1112" }, // DInt { "失败数量", "DB1001.DBD1112" }, // DInt
{ "查询请求", "DB1001.DBW2000" }, // Int - 1=请求开始0=无请求
{ "上传请求", "DB1001.DBW2002" }, // Int - 1=请求开始0=无请求 { "上传请求", "DB1001.DBW2002" }, // Int - 1=请求开始0=无请求
{ "托盘号", "DB1001.DBW2054" }, // Int { "托盘号", "DB1001.DBW2054" }, // Int
{ "总结果", "DB1001.DBW2158" }, // Int - 一个托盘上传四次结果 { "总结果", "DB1001.DBW2158" }, // Int - 一个托盘上传四次结果
@ -150,7 +157,7 @@ namespace RIZO.Admin.WebApi.PLC.Service
{ "上传请求", "DB1001.DBW2002" }, // Int - 1:请求开始0:无请求 { "上传请求", "DB1001.DBW2002" }, // Int - 1:请求开始0:无请求
// 节拍时间 // 节拍时间
{ "节拍时间", "DB1001.DBD2988" }, // Real { "节拍时间", "DB1001.DBD3030" }, // Real
// 产品结果与产量 // 产品结果与产量
{ "合格数量", "DB1001.DBD4110" }, // DInt { "合格数量", "DB1001.DBD4110" }, // DInt
@ -158,12 +165,10 @@ namespace RIZO.Admin.WebApi.PLC.Service
{ "实际数量", "DB1001.DBD4118" } // DInt { "实际数量", "DB1001.DBD4118" } // DInt
}; };
// OP020-4 专属地址映射pin压合&视觉检查工位DB1001
private readonly Dictionary<string, (string Addr, int Len)> _op020_4StringMap = new() private readonly Dictionary<string, (string Addr, int Len)> _op020_4StringMap = new()
{ {
{ "报警信息", ("DB1001.DBB58", 48) }, // Array[1..48] of Byte { "报警信息", ("DB1001.DBB58", 48) }, // Array[1..48] of Byte
{ "产品型号", ("DB1001.DBB1000", 48) }, // String[48] { "产品型号", ("DB1001.DBB1000", 48) }, // String[48]
{ "产品名称", ("DB1001.DBB1054", 48) }, // String[48]
{ "产品1SN", ("DB1001.DBB2230", 40) }, // String[40] { "产品1SN", ("DB1001.DBB2230", 40) }, // String[40]
{ "产品2SN", ("DB1001.DBB2360", 40) }, // String[40] { "产品2SN", ("DB1001.DBB2360", 40) }, // String[40]
{ "产品3SN", ("DB1001.DBB2490", 40) }, // String[40] { "产品3SN", ("DB1001.DBB2490", 40) }, // String[40]
@ -178,29 +183,34 @@ namespace RIZO.Admin.WebApi.PLC.Service
{ "设备在线状态", "DB1001.DBW4" }, // Int - 1=离线,0=在线 { "设备在线状态", "DB1001.DBW4" }, // Int - 1=离线,0=在线
{ "ByPass", "DB1001.DBW6" }, // Int - 1=ByPass,0=正常模式 { "ByPass", "DB1001.DBW6" }, // Int - 1=ByPass,0=正常模式
{ "生产模式", "DB1001.DBW8" }, // Int - 1=正常模式;2=清线模式;4=返工模式;8=换型模式;16=预热模式 { "生产模式", "DB1001.DBW8" }, // Int - 1=正常模式;2=清线模式;4=返工模式;8=换型模式;16=预热模式
// 上传请求 // 上传请求
{ "查询请求", "DB1001.DBW2000" }, // Int - 1:请求开始0:无请求
{ "产品1上传请求", "DB1001.DBW2002" }, // Int { "产品1上传请求", "DB1001.DBW2002" }, // Int
{ "产品2上传请求", "DB1001.DBW2004" }, // Int { "产品2上传请求", "DB1001.DBW2004" }, // Int
{ "产品3上传请求", "DB1001.DBW2006" }, // Int { "产品3上传请求", "DB1001.DBW2006" }, // Int
{ "产品4上传请求", "DB1001.DBW2008" }, // Int { "产品4上传请求", "DB1001.DBW2008" }, // Int
// 托盘号 // 托盘号
{ "托盘号", "DB1001.DBW2070" }, // Int { "托盘号", "DB1001.DBW2070" }, // Int
// 产品结果 // 产品结果
{ "产品1结果", "DB1001.DBW2274" }, // Int - 1:OK 2:NG { "产品1结果", "DB1001.DBW2274" }, // Int - 1:OK 2:NG
{ "产品2结果", "DB1001.DBW2404" }, // Int - 1:OK 2:NG { "产品2结果", "DB1001.DBW2404" }, // Int - 1:OK 2:NG
{ "产品3结果", "DB1001.DBW2534" }, // Int - 1:OK 2:NG { "产品3结果", "DB1001.DBW2534" }, // Int - 1:OK 2:NG
{ "产品4结果", "DB1001.DBW2664" }, // Int - 1:OK 2:NG { "产品4结果", "DB1001.DBW2664" }, // Int - 1:OK 2:NG
// 产量统计
{ "实际产量", "DB1001.DBD1104" }, // DInt
{ "合格数量", "DB1001.DBD1108" }, // DInt
{ "失败数量", "DB1001.DBD1112" } // DInt
}; };
// OP050 专属地址映射点白胶DC744工位DB300 // OP050 专属地址映射点白胶DC744工位DB300
private readonly Dictionary<string, (string Addr, int Len)> _op050StringMap = new() private readonly Dictionary<string, (string Addr, int Len)> _op050StringMap = new()
{ {
{ "报警信息", ("DB300.DBB680", 48) }, // Array[1..48] of Byte { "报警信息", ("DB300.DBB730", 48) }, // Array[1..48] of Byte
{ "产品型号", ("DB300.DBB728", 48) }, // String[48] { "产品型号", ("DB300.DBB680", 48) }, // String[48]
{ "产品名称", ("DB300.DBB778", 48) }, // String[48] { "产品名称", ("DB300.DBB778", 48) }, // String[48]
{ "产品1SN", ("DB300.DBB50", 40) }, // String[40] { "产品1SN", ("DB300.DBB50", 40) }, // String[40]
{ "产品2SN", ("DB300.DBB206", 40) }, // String[40] { "产品2SN", ("DB300.DBB206", 40) }, // String[40]
@ -268,30 +278,31 @@ namespace RIZO.Admin.WebApi.PLC.Service
{ "节拍时间", "DB1001.DBD5086" }, // Real { "节拍时间", "DB1001.DBD5086" }, // Real
}; };
// OP051扫码 专属地址映射Coating扫码工位假设使用 DB1002//待PLC给出 // OP051扫码 专属地址映射Coating扫码工位DB1001
private readonly Dictionary<string, (string Addr, int Len)> _op051ScanStringMap = new() private readonly Dictionary<string, (string Addr, int Len)> _op051ScanStringMap = new()
{ {
{ "产品型号", ("DB1002.DBB1000", 48) }, // String[48] { "报警信息", ("DB1001.DBB60", 48) }, // Array[0..48] of Byte
{ "产品名称", ("DB1002.DBB1054", 48) }, // String[48] { "SN", ("DB1001.DBB110", 40) }, // String[40] - 查询条码
{ "SN_1", ("DB1002.DBB50", 40) }, // String[40] { "SN_1", ("DB1001.DBB152", 40) }, // String[40] - 保存条码1
{ "SN_2", ("DB1002.DBB206", 40) }, // String[40] { "SN_2", ("DB1001.DBB194", 40) }, // String[40] - 保存条码2
{ "SN_3", ("DB1002.DBB362", 40) } // String[40] { "SN_3", ("DB1001.DBB236", 40) }, // String[40] - 保存条码3
{ "托盘号", ("DB1001.DBB278", 40) } // String[40]
}; };
private readonly Dictionary<string, string> _op051ScanIntMap = new() private readonly Dictionary<string, string> _op051ScanIntMap = new()
{ {
// 基础状态 // 基础状态
{ "运行状态", "DB1002.DBW0" }, // Int - 1=空闲2=运行中3=故障 { "运行状态", "DB1001.DBW0" }, // Int - 1=空闲2=运行中3=故障
{ "设备模式", "DB1002.DBW2" }, // Int - 1=空模式2=手动4=初始化8=自动16=CycleStop { "设备模式", "DB1001.DBW2" }, // Int - 1=空模式2=手动4=初始化8=自动16=CycleStop
{ "设备在线状态", "DB1002.DBW4" }, // Int - 1=离线,0=在线 { "设备在线状态", "DB1001.DBW4" }, // Int - 1=离线,0=在线
{ "生产模式", "DB1002.DBW8" }, // Int - 1=正常模式;2=清线模式;4=返工模式;8=换型模式;16=预热模式 { "生产模式", "DB1001.DBW6" }, // Int - 1=正常模式;2=清线模式;4=返工模式;8=换型模式;16=预热模式
// 操作请求 // 操作请求
{ "查询请求", "DB1002.DBW44" }, // Int - 非0为查询 { "查询请求", "DB1001.DBW320" }, // Int - 1:请求开始0:无请求
{ "上传结果请求", "DB1002.DBW46" }, // Int - 非0为上传 { "上传结果请求", "DB1001.DBW322" }, // Int - 1:请求开始0:无请求
// 托盘号 // 节拍时间
{ "托盘号", "DB1002.DBW844" } // Int { "节拍时间", "DB1001.DBD324" } // Real
}; };
// OP057 专属地址映射(压装定位销&激光打标工位DB1001 // OP057 专属地址映射(压装定位销&激光打标工位DB1001
@ -1076,7 +1087,9 @@ namespace RIZO.Admin.WebApi.PLC.Service
{ {
{ "报警信息", ("DB1001.DBB58", 48) }, // Array[1..48] of Byte { "报警信息", ("DB1001.DBB58", 48) }, // Array[1..48] of Byte
{ "产品型号", ("DB1001.DBB1000", 48) }, // String[48] { "产品型号", ("DB1001.DBB1000", 48) }, // String[48]
{ "产品名称", ("DB1001.DBB1054", 48) } // String[48] { "产品名称", ("DB1001.DBB1054", 48) }, // String[48]
{ "查询SN", ("DB1001.DBB2100", 40) }, // String[40]
{ "保存SN", ("DB1001.DBB2230", 40) } // String[40]
}; };
private readonly Dictionary<string, string> _op115IntMap = new() private readonly Dictionary<string, string> _op115IntMap = new()
@ -1086,7 +1099,10 @@ namespace RIZO.Admin.WebApi.PLC.Service
{ "设备模式", "DB1001.DBW2" }, // Int - 1=空模式2=手动4=初始化8=自动16=CycleStop { "设备模式", "DB1001.DBW2" }, // Int - 1=空模式2=手动4=初始化8=自动16=CycleStop
{ "设备在线状态", "DB1001.DBW4" }, // Int - 1=离线,0=在线 { "设备在线状态", "DB1001.DBW4" }, // Int - 1=离线,0=在线
{ "ByPass", "DB1001.DBW6" }, // Int - 1=ByPass,0=正常模式 { "ByPass", "DB1001.DBW6" }, // Int - 1=ByPass,0=正常模式
{ "生产模式", "DB1001.DBW8" } // Int - 1=点检2=返工4=样件5=正常 { "生产模式", "DB1001.DBW8" }, // Int - 1=点检2=返工4=样件5=正常
// 操作请求
{ "工位查询请求", "DB1001.DBW2000" }, // Int - 1=请求开始0=无请求
{ "结果保存请求", "DB1001.DBW2002" }, // Int - 1=请求开始0=无请求
}; };
// OP140 专属地址映射气密性测试工位DB1001 // OP140 专属地址映射气密性测试工位DB1001
@ -1313,6 +1329,7 @@ namespace RIZO.Admin.WebApi.PLC.Service
break; break;
case "OP050": case "OP050":
iSaveRequest = await ReadPlcIntAsync(plc, _op050IntMap["上传请求"]).ConfigureAwait(false); iSaveRequest = await ReadPlcIntAsync(plc, _op050IntMap["上传请求"]).ConfigureAwait(false);
if (iSaveRequest > 0) iSaveRequest = 1;
break; break;
case "OP050-1": case "OP050-1":
var task5_1 = ReadPlcIntAsync(plc, _op050_1IntMap["产品1保存请求"]); var task5_1 = ReadPlcIntAsync(plc, _op050_1IntMap["产品1保存请求"]);
@ -1387,8 +1404,7 @@ namespace RIZO.Admin.WebApi.PLC.Service
iSaveRequest = await ReadPlcIntAsync(plc, _op110_3IntMap["产品保存请求"]).ConfigureAwait(false); iSaveRequest = await ReadPlcIntAsync(plc, _op110_3IntMap["产品保存请求"]).ConfigureAwait(false);
break; break;
case "OP115": case "OP115":
//iSaveRequest = await ReadPlcIntAsync(plc, _op115IntMap["结果保存请求"]).ConfigureAwait(false); iSaveRequest = await ReadPlcIntAsync(plc, _op115IntMap["结果保存请求"]).ConfigureAwait(false);
iSaveRequest = 0;
break; break;
case "OP140": case "OP140":
iSaveRequest = await ReadPlcIntAsync(plc, _op140IntMap["结果保存请求"]).ConfigureAwait(false); iSaveRequest = await ReadPlcIntAsync(plc, _op140IntMap["结果保存请求"]).ConfigureAwait(false);
@ -1630,8 +1646,7 @@ namespace RIZO.Admin.WebApi.PLC.Service
{ "ByPass", ReadPlcIntAsync(plc, _op020_3IntMap["ByPass"]) }, { "ByPass", ReadPlcIntAsync(plc, _op020_3IntMap["ByPass"]) },
{ "生产模式", ReadPlcIntAsync(plc, _op020_3IntMap["生产模式"]) }, { "生产模式", ReadPlcIntAsync(plc, _op020_3IntMap["生产模式"]) },
{ "查询请求", ReadPlcIntAsync(plc, _op020_3IntMap["查询请求"]) }, { "查询请求", ReadPlcIntAsync(plc, _op020_3IntMap["查询请求"]) },
{ "上传请求", ReadPlcIntAsync(plc, _op020_3IntMap["上传请求"]) }, { "上传请求", ReadPlcIntAsync(plc, _op020_3IntMap["上传请求"]) }
{ "托盘号", ReadPlcIntAsync(plc, _op020_3IntMap["托盘号"]) }
}; };
// DInt/Real读取任务产量+节拍时间复用ReadPlcIntAsync兼容转换与OP020-4风格一致 // DInt/Real读取任务产量+节拍时间复用ReadPlcIntAsync兼容转换与OP020-4风格一致
@ -1664,7 +1679,6 @@ namespace RIZO.Admin.WebApi.PLC.Service
int onlineStatus = intReadTasks["设备在线状态"].Result; int onlineStatus = intReadTasks["设备在线状态"].Result;
int byPass = intReadTasks["ByPass"].Result; int byPass = intReadTasks["ByPass"].Result;
int produceModel = intReadTasks["生产模式"].Result; int produceModel = intReadTasks["生产模式"].Result;
int trayNo = intReadTasks["托盘号"].Result;
// DInt/Real转换结果 // DInt/Real转换结果
int cycleTime = dIntRealReadTasks["节拍时间"].Result; int cycleTime = dIntRealReadTasks["节拍时间"].Result;
@ -1703,7 +1717,6 @@ namespace RIZO.Admin.WebApi.PLC.Service
Runstatus = runStatus, Runstatus = runStatus,
OnlineStatus = onlineStatusDesc, OnlineStatus = onlineStatusDesc,
ProduceModel = produceModelDesc, ProduceModel = produceModelDesc,
TrayNo = trayNo.ToString(),
CreatedBy = "PLC", CreatedBy = "PLC",
CreatedTime = DateTime.Now, CreatedTime = DateTime.Now,
Product1SN = product1SN, Product1SN = product1SN,
@ -1748,7 +1761,7 @@ namespace RIZO.Admin.WebApi.PLC.Service
{ {
{ "报警信息", ReadPlcStringAsync(plc, _op020_4StringMap["报警信息"].Addr, _op020_4StringMap["报警信息"].Len) }, { "报警信息", ReadPlcStringAsync(plc, _op020_4StringMap["报警信息"].Addr, _op020_4StringMap["报警信息"].Len) },
{ "产品型号", ReadPlcStringAsync(plc, _op020_4StringMap["产品型号"].Addr, _op020_4StringMap["产品型号"].Len) }, { "产品型号", ReadPlcStringAsync(plc, _op020_4StringMap["产品型号"].Addr, _op020_4StringMap["产品型号"].Len) },
{ "产品名称", ReadPlcStringAsync(plc, _op020_4StringMap["产品名称"].Addr, _op020_4StringMap["产品名称"].Len) },
{ "产品1SN", ReadPlcStringAsync(plc, _op020_4StringMap["产品1SN"].Addr, _op020_4StringMap["产品1SN"].Len) }, { "产品1SN", ReadPlcStringAsync(plc, _op020_4StringMap["产品1SN"].Addr, _op020_4StringMap["产品1SN"].Len) },
{ "产品2SN", ReadPlcStringAsync(plc, _op020_4StringMap["产品2SN"].Addr, _op020_4StringMap["产品2SN"].Len) }, { "产品2SN", ReadPlcStringAsync(plc, _op020_4StringMap["产品2SN"].Addr, _op020_4StringMap["产品2SN"].Len) },
{ "产品3SN", ReadPlcStringAsync(plc, _op020_4StringMap["产品3SN"].Addr, _op020_4StringMap["产品3SN"].Len) }, { "产品3SN", ReadPlcStringAsync(plc, _op020_4StringMap["产品3SN"].Addr, _op020_4StringMap["产品3SN"].Len) },
@ -1787,7 +1800,6 @@ namespace RIZO.Admin.WebApi.PLC.Service
// 3. 提取读取结果(直接取值+空值兜底减少await重复调用 // 3. 提取读取结果(直接取值+空值兜底减少await重复调用
// 字符串字段 // 字符串字段
string productModel = stringReadTasks["产品型号"].Result ?? string.Empty; string productModel = stringReadTasks["产品型号"].Result ?? string.Empty;
string productName = stringReadTasks["产品名称"].Result ?? string.Empty;
string product1SN = stringReadTasks["产品1SN"].Result ?? string.Empty; string product1SN = stringReadTasks["产品1SN"].Result ?? string.Empty;
string product2SN = stringReadTasks["产品2SN"].Result ?? string.Empty; string product2SN = stringReadTasks["产品2SN"].Result ?? string.Empty;
string product3SN = stringReadTasks["产品3SN"].Result ?? string.Empty; string product3SN = stringReadTasks["产品3SN"].Result ?? string.Empty;
@ -1831,7 +1843,6 @@ namespace RIZO.Admin.WebApi.PLC.Service
WorkstationCode = workstationCode, WorkstationCode = workstationCode,
WorkstationName = "pin压合&视觉检查", // 补充工站名称 WorkstationName = "pin压合&视觉检查", // 补充工站名称
ProductModel = productModel, ProductModel = productModel,
ProductName = productName,
ProductCode = product1SN, // 主产品编码取产品1SN可根据业务调整 ProductCode = product1SN, // 主产品编码取产品1SN可根据业务调整
ReworkFlag = reworkFlag, ReworkFlag = reworkFlag,
Automanual = machineModel, Automanual = machineModel,
@ -2140,20 +2151,26 @@ namespace RIZO.Admin.WebApi.PLC.Service
/// <returns>PLC生产数据实体</returns> /// <returns>PLC生产数据实体</returns>
public async Task<PlcProductionData> ReadOP051ScanDataAsync(Plc plc, string ip, string workstationCode) public async Task<PlcProductionData> ReadOP051ScanDataAsync(Plc plc, string ip, string workstationCode)
{ {
if (plc == null || !plc.IsConnected) return null; // 1. 入参校验(强化兜底,更严谨)
if (plc == null || !plc.IsConnected)
{
Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] PLC未连接或实例为空无法读取OP051扫码工位数据");
return null;
}
if (string.IsNullOrWhiteSpace(ip)) throw new ArgumentNullException(nameof(ip), "PLC IP地址不能为空"); if (string.IsNullOrWhiteSpace(ip)) throw new ArgumentNullException(nameof(ip), "PLC IP地址不能为空");
if (string.IsNullOrWhiteSpace(workstationCode)) throw new ArgumentNullException(nameof(workstationCode), "工位编码不能为空"); if (string.IsNullOrWhiteSpace(workstationCode)) throw new ArgumentNullException(nameof(workstationCode), "工位编码不能为空");
try try
{ {
// 1. 批量创建并行读取任务(按类型分类,最大化并行效率 // 2. 批量创建并行读取任务(完全匹配点位表,无多余读取,添加空值兜底
var stringTasks = new Dictionary<string, Task<string>> var stringTasks = new Dictionary<string, Task<string>>
{ {
{ "产品型号", ReadPlcStringAsync(plc, _op051ScanStringMap["产品型号"].Addr, _op051ScanStringMap["产品型号"].Len) }, { "报警信息", ReadPlcStringAsync(plc, _op051ScanStringMap["报警信息"].Addr, _op051ScanStringMap["报警信息"].Len) },
{ "产品名称", ReadPlcStringAsync(plc, _op051ScanStringMap["产品名称"].Addr, _op051ScanStringMap["产品名称"].Len) }, { "查询条码", ReadPlcStringAsync(plc, _op051ScanStringMap["SN"].Addr, _op051ScanStringMap["SN"].Len) },
{ "SN_1", ReadPlcStringAsync(plc, _op051ScanStringMap["SN_1"].Addr, _op051ScanStringMap["SN_1"].Len) }, { "SN_1", ReadPlcStringAsync(plc, _op051ScanStringMap["SN_1"].Addr, _op051ScanStringMap["SN_1"].Len) },
{ "SN_2", ReadPlcStringAsync(plc, _op051ScanStringMap["SN_2"].Addr, _op051ScanStringMap["SN_2"].Len) }, { "SN_2", ReadPlcStringAsync(plc, _op051ScanStringMap["SN_2"].Addr, _op051ScanStringMap["SN_2"].Len) },
{ "SN_3", ReadPlcStringAsync(plc, _op051ScanStringMap["SN_3"].Addr, _op051ScanStringMap["SN_3"].Len) } { "SN_3", ReadPlcStringAsync(plc, _op051ScanStringMap["SN_3"].Addr, _op051ScanStringMap["SN_3"].Len) },
{ "托盘号", ReadPlcStringAsync(plc, _op051ScanStringMap["托盘号"].Addr, _op051ScanStringMap["托盘号"].Len) }
}; };
var intTasks = new Dictionary<string, Task<int>> var intTasks = new Dictionary<string, Task<int>>
@ -2163,32 +2180,24 @@ namespace RIZO.Admin.WebApi.PLC.Service
{ "设备在线状态", ReadPlcIntAsync(plc, _op051ScanIntMap["设备在线状态"]) }, { "设备在线状态", ReadPlcIntAsync(plc, _op051ScanIntMap["设备在线状态"]) },
{ "生产模式", ReadPlcIntAsync(plc, _op051ScanIntMap["生产模式"]) }, { "生产模式", ReadPlcIntAsync(plc, _op051ScanIntMap["生产模式"]) },
{ "查询请求", ReadPlcIntAsync(plc, _op051ScanIntMap["查询请求"]) }, { "查询请求", ReadPlcIntAsync(plc, _op051ScanIntMap["查询请求"]) },
{ "上传结果请求", ReadPlcIntAsync(plc, _op051ScanIntMap["上传结果请求"]) }, { "上传结果请求", ReadPlcIntAsync(plc, _op051ScanIntMap["上传结果请求"]) }
{ "托盘号", ReadPlcIntAsync(plc, _op051ScanIntMap["托盘号"]) }
}; };
// DInt双整数读取任务复用ReadPlcIntAsyncPLC底层兼容DInt转int // 3. 并行等待所有任务完成最小化等待时间ConfigureAwait(false)提升异步效率)
var dIntReadTasks = new Dictionary<string, Task<int>> var allTasks = new List<Task>()
{ .Concat(stringTasks.Values)
{ "实际产量", ReadPlcIntAsync(plc, _op050IntMap["实际产量"]) }, .Concat(intTasks.Values)
{ "合格数量", ReadPlcIntAsync(plc, _op050IntMap["合格数量"]) }, .ToList();
{ "失败数量", ReadPlcIntAsync(plc, _op050IntMap["失败数量"]) }
};
// 2. 并行等待所有任务完成单次WaitAll最小化等待时间
var allTasks = new List<Task>();
allTasks.AddRange(stringTasks.Values);
allTasks.AddRange(dIntReadTasks.Values);
allTasks.AddRange(intTasks.Values);
await Task.WhenAll(allTasks).ConfigureAwait(false); await Task.WhenAll(allTasks).ConfigureAwait(false);
// 3. 提取结果(直接取值,空值兜底,减少内存分配) // 4. 提取结果空值兜底避免NullReferenceException简化取值逻辑
var now = DateTime.Now; // 单次获取时间,减少系统调用 var now = DateTime.Now;
var productModel = stringTasks["产品型号"].Result; var alarmInfo = stringTasks["报警信息"].Result ?? string.Empty;
var productName = stringTasks["产品名称"].Result; var querySn = stringTasks["查询条码"].Result ?? string.Empty;
var sn1 = stringTasks["SN_1"].Result; var sn1 = stringTasks["SN_1"].Result ?? string.Empty;
var sn2 = stringTasks["SN_2"].Result; var sn2 = stringTasks["SN_2"].Result ?? string.Empty;
var sn3 = stringTasks["SN_3"].Result; var sn3 = stringTasks["SN_3"].Result ?? string.Empty;
var trayNo = stringTasks["托盘号"].Result ?? string.Empty;
var runStatus = intTasks["运行状态"].Result; var runStatus = intTasks["运行状态"].Result;
var machineModel = intTasks["设备模式"].Result; var machineModel = intTasks["设备模式"].Result;
@ -2196,13 +2205,8 @@ namespace RIZO.Admin.WebApi.PLC.Service
var produceModel = intTasks["生产模式"].Result; var produceModel = intTasks["生产模式"].Result;
var queryRequest = intTasks["查询请求"].Result; var queryRequest = intTasks["查询请求"].Result;
var uploadRequest = intTasks["上传结果请求"].Result; var uploadRequest = intTasks["上传结果请求"].Result;
var trayNo = intTasks["托盘号"].Result.ToString(); // 转换为字符串,适配实体字段
int actualOutput = dIntReadTasks["实际产量"].Result; // 5. 核心逻辑转换(极简表达式,适配扫码工位业务,注释清晰)
int qualifiedQty = dIntReadTasks["合格数量"].Result;
int failedQty = dIntReadTasks["失败数量"].Result;
// 4. 核心逻辑转换(极简表达式,适配扫码工位业务)
var reworkFlag = produceModel == 4 ? "1" : "0"; // 4=返工模式→1其他→0 var reworkFlag = produceModel == 4 ? "1" : "0"; // 4=返工模式→1其他→0
var produceModelDesc = produceModel switch var produceModelDesc = produceModel switch
{ {
@ -2218,18 +2222,15 @@ namespace RIZO.Admin.WebApi.PLC.Service
var uploadRequestDesc = uploadRequest != 0 ? "有上传请求" : "无上传请求"; var uploadRequestDesc = uploadRequest != 0 ? "有上传请求" : "无上传请求";
var qualificationFlag = "1"; // 扫码工位默认合格(无检测结果,可根据业务调整) var qualificationFlag = "1"; // 扫码工位默认合格(无检测结果,可根据业务调整)
// 6. 构建并返回实体(完全匹配点位表数据,精简无冗余字段,修正注释错误)
// 6. 构建并返回实体(适配扫码工位特性,精简字段)
return new PlcProductionData return new PlcProductionData
{ {
PlcIp = ip, PlcIp = ip,
OccurTime = now, OccurTime = now,
LineCode = "line2", // 可改为参数传入 LineCode = "line2", // 建议后续改为参数传入,提升方法通用性
WorkstationCode = workstationCode, WorkstationCode = workstationCode,
WorkstationName = "Coating扫码", // 补充工站名称 WorkstationName = "Coating扫码",
ProductModel = productModel, ProductCode = sn1, // 修正注释与代码不一致主SN使用SN_1
ProductName = productName,
ProductCode = sn2, // 主SN用SN_1
ReworkFlag = reworkFlag, ReworkFlag = reworkFlag,
Automanual = machineModel, Automanual = machineModel,
Runstatus = runStatus, Runstatus = runStatus,
@ -2242,16 +2243,20 @@ namespace RIZO.Admin.WebApi.PLC.Service
Product2SN = sn2, Product2SN = sn2,
Product3SN = sn3, Product3SN = sn3,
QualificationFlag = qualificationFlag, QualificationFlag = qualificationFlag,
ActualOutQty = actualOutput.ToString(),
QualifiedQty = qualifiedQty.ToString(),
FailedQty = failedQty.ToString(),
CreatedBy = "PLC", CreatedBy = "PLC",
CreatedTime = now, CreatedTime = now
}; };
} }
catch (KeyNotFoundException ex)
{
// 精准捕获键不存在异常(点位表与读取逻辑不匹配时快速定位)
Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] OP051扫码工位读取异常点位表中不存在指定键 -> {ex.Message}");
return null;
}
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] OP051扫码工位读取异常{ex.Message}"); // 通用异常捕获,保留完整异常堆栈便于排查
Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] OP051扫码工位读取异常{ex.ToString()}");
return null; return null;
} }
} }
@ -4750,8 +4755,10 @@ namespace RIZO.Admin.WebApi.PLC.Service
var stringTasks = new Dictionary<string, Task<string>> var stringTasks = new Dictionary<string, Task<string>>
{ {
{ "产品型号", ReadPlcStringAsync(plc, _op115StringMap["产品型号"].Addr, _op115StringMap["产品型号"].Len) }, { "产品型号", ReadPlcStringAsync(plc, _op115StringMap["产品型号"].Addr, _op115StringMap["产品型号"].Len) },
{ "产品名称", ReadPlcStringAsync(plc, _op115StringMap["产品名称"].Addr, _op115StringMap["产品名称"].Len) } { "产品名称", ReadPlcStringAsync(plc, _op115StringMap["产品名称"].Addr, _op115StringMap["产品名称"].Len) },
}; {"查询SN", ReadPlcStringAsync(plc, _op115StringMap["查询SN"].Addr, _op110_3StringMap["查询SN"].Len) },
{ "保存SN", ReadPlcStringAsync(plc, _op115StringMap["保存SN"].Addr, _op110_3StringMap["保存SN"].Len) }
};
var intTasks = new Dictionary<string, Task<int>> var intTasks = new Dictionary<string, Task<int>>
{ {
@ -4776,6 +4783,8 @@ namespace RIZO.Admin.WebApi.PLC.Service
var machineModel = intTasks["设备模式"].Result; var machineModel = intTasks["设备模式"].Result;
var onlineStatus = intTasks["设备在线状态"].Result; var onlineStatus = intTasks["设备在线状态"].Result;
var produceModel = intTasks["生产模式"].Result; var produceModel = intTasks["生产模式"].Result;
var sn1 = stringTasks["查询SN"].Result ?? string.Empty;
var sn2 = stringTasks["保存SN"].Result ?? string.Empty;
// 4. 业务逻辑转换 // 4. 业务逻辑转换
var reworkFlag = produceModel == 2 ? "1" : "0"; var reworkFlag = produceModel == 2 ? "1" : "0";
@ -4801,6 +4810,9 @@ namespace RIZO.Admin.WebApi.PLC.Service
WorkstationName = "自动上下料到固化炉工位", WorkstationName = "自动上下料到固化炉工位",
ProductModel = productModel, ProductModel = productModel,
ProductName = productName, ProductName = productName,
ProductCode = sn1,
SN1 = sn1,
SN2 = sn2,
ReworkFlag = reworkFlag, ReworkFlag = reworkFlag,
Automanual = machineModel, Automanual = machineModel,
Runstatus = runStatus, Runstatus = runStatus,
@ -5651,12 +5663,29 @@ namespace RIZO.Admin.WebApi.PLC.Service
{ {
try try
{ {
if (plcName == "OP020-4" || plcName == "OP050-1")
{
WritePlcValue(plc, _mesop20_4IntReturnMap["保存结果1"], saveResult);
WritePlcValue(plc, _mesop20_4IntReturnMap["保存结果2"], saveResult);
WritePlcValue(plc, _mesop20_4IntReturnMap["保存结果3"], saveResult);
WritePlcValue(plc, _mesop20_4IntReturnMap["保存结果4"], saveResult);
return;
}
if (plcName == "OP050")
{
WritePlcValue(plc, _mesop050IntReturnMap["保存结果1"], saveResult);
WritePlcValue(plc, _mesop050IntReturnMap["保存结果2"], saveResult);
WritePlcValue(plc, _mesop050IntReturnMap["保存结果3"], saveResult);
WritePlcValue(plc, _mesop050IntReturnMap["保存结果4"], saveResult);
return;
}
var targetMap = plcName switch var targetMap = plcName switch
{ {
"OP20-3" => _mesop20_3IntReturnMap, "OP020-3" => _mesop20_3IntReturnMap,
"OP20-4" => _mesop20_4IntReturnMap, //"OP020-4" => _mesop20_4IntReturnMap,
"OP50-1" => _mesop20_4IntReturnMap, //"OP050-1" => _mesop20_4IntReturnMap,
//"OP050" => _mesop050IntReturnMap,//四个保存结果 //"OP050" => _mesop050IntReturnMap,//四个保存结果
"OP051扫码" => _mesop51ScanIntReturnMap,
"OP058" => _mesop058IntReturnMap, "OP058" => _mesop058IntReturnMap,
"OP075" => _mesop075IntReturnMap, "OP075" => _mesop075IntReturnMap,
"OP080-1" => _mesop080_1IntReturnMap, "OP080-1" => _mesop080_1IntReturnMap,

View File

@ -112,6 +112,12 @@ builder.Services.AddLocalization(options => options.ResourcesPath = "");
//PLC进站后台服务注册 //PLC进站后台服务注册
//builder.Services.AddHostedService<PlcOutStationService_OP07_01>(); //builder.Services.AddHostedService<PlcOutStationService_OP07_01>();
builder.Services.AddHostedService< PlcIntoStationService_OP020_2>();
builder.Services.AddHostedService<PlcIntoStationService_OP020_3>();
builder.Services.AddHostedService<PlcIntoStationService_OP020_4>();
builder.Services.AddHostedService<PlcIntoStationService_OP050>();
builder.Services.AddHostedService<PlcIntoStationService_OP050_1>();
builder.Services.AddHostedService<PlcIntoStationService_OP051Scan>();
builder.Services.AddHostedService<PlcIntoStationService_OP057>(); builder.Services.AddHostedService<PlcIntoStationService_OP057>();
builder.Services.AddHostedService<PlcIntoStationService_OP058>(); builder.Services.AddHostedService<PlcIntoStationService_OP058>();
builder.Services.AddHostedService<PlcIntoStationService_OP060>(); builder.Services.AddHostedService<PlcIntoStationService_OP060>();
@ -120,6 +126,8 @@ builder.Services.AddHostedService<PlcIntoStationService_OP102>();
builder.Services.AddHostedService<PlcIntoStationService_OP110_1>(); builder.Services.AddHostedService<PlcIntoStationService_OP110_1>();
builder.Services.AddHostedService<PlcIntoStationService_OP110_2>(); builder.Services.AddHostedService<PlcIntoStationService_OP110_2>();
builder.Services.AddHostedService<PlcIntoStationService_OP110_3>(); builder.Services.AddHostedService<PlcIntoStationService_OP110_3>();
builder.Services.AddHostedService<PlcIntoStationService_OP115>();
//螺丝枪服务注册 //螺丝枪服务注册
//builder.Services.AddHostedService<PF6ScrewGunService>(); //builder.Services.AddHostedService<PF6ScrewGunService>();

View File

@ -114,7 +114,7 @@
"PlcSettings": [ "PlcSettings": [
{ {
"Id": 1, "Id": 1,
"isEnble": false, "isEnble": true,
"WorkStationCode": "OP020-2", "WorkStationCode": "OP020-2",
"WorkStationName": "OP020-2 合盖", "WorkStationName": "OP020-2 合盖",
"PlcType": "S71500", "PlcType": "S71500",
@ -123,13 +123,13 @@
"Heartbeat": "DB1000.DBW0", // "Heartbeat": "DB1000.DBW0", //
"IntoStationAsk": "DB1001.DBW2000", // "IntoStationAsk": "DB1001.DBW2000", //
"ProductModel": "DB1001.DBB1000", "ProductModel": "DB1001.DBB1000",
"ProductSN": "DB1001.DBB1054", "ProductSN": "DB1001.DBB2106",
"IntoStationResp": "DB1000.DBW2002" // "IntoStationResp": "DB1000.DBW2000" //
} }
}, },
{ {
"Id": 2, "Id": 2,
"isEnble": false, "isEnble": true,
"WorkStationCode": "OP020-3", "WorkStationCode": "OP020-3",
"WorkStationName": "OP020-3 热铆", "WorkStationName": "OP020-3 热铆",
@ -145,7 +145,7 @@
}, },
{ {
"Id": 3, "Id": 3,
"isEnble": false, "isEnble": true,
"WorkStationCode": "OP020-4", "WorkStationCode": "OP020-4",
"WorkStationName": "OP020-4 pin压合&视觉检查", "WorkStationName": "OP020-4 pin压合&视觉检查",
"PlcType": "S71500", "PlcType": "S71500",
@ -160,22 +160,25 @@
}, },
{ {
"Id": 4, "Id": 4,
"isEnble": false, "isEnble": true,
"WorkStationCode": "OP050", "WorkStationCode": "OP050",
"WorkStationName": "OP050 点白胶DC744", "WorkStationName": "OP050 点白胶DC744",
"PlcType": "S71500", "PlcType": "S71500",
"IpAddress": "192.168.10.1", "IpAddress": "192.168.10.1",
"IntoStation": { "IntoStation": {
"Heartbeat": "DB300.DBW0", // "Heartbeat": "DB300.DBW0", //
"IntoStationAsk": "DB300.DBW42", // "IntoStationAsk": "DB300.DBW44", //
"ProductModel": "DB300.DBB728", "ProductModel": "DB300.DBB680",
"ProductSN": "DB300.DBB778", "ProductSN": "DB300.DBB50",
"IntoStationResp": "DB300.DBW46" // "IntoStationResp": "DB300.DBW46", //
"IntoStationResp2": "DB300.DBW202",
"IntoStationResp3": "DB300.DBW358",
"IntoStationResp4": "DB300.DBW514"
} }
}, },
{ {
"Id": 5, "Id": 5,
"isEnble": false, "isEnble": true,
"WorkStationCode": "OP050-1", "WorkStationCode": "OP050-1",
"WorkStationName": "OP050-1 自动下料", "WorkStationName": "OP050-1 自动下料",
"PlcType": "S71500", "PlcType": "S71500",
@ -190,17 +193,17 @@
}, },
{ {
"Id": 41, "Id": 41,
"isEnble": false, "isEnble": true,
"WorkStationCode": "OP51扫码", "WorkStationCode": "OP051扫码",
"WorkStationName": "OP51扫码 Coating扫码", "WorkStationName": "OP051扫码 Coating扫码",
"PlcType": "S71500", "PlcType": "S71500",
"IpAddress": "待定", "IpAddress": "192.168.0.1",
"IntoStation": { "IntoStation": {
"Heartbeat": "待定", // "Heartbeat": "DB1000.DBW0", //
"IntoStationAsk": "待定", // "IntoStationAsk": "DB1001.DBW320", //
"ProductModel": "待定", "ProductModel": "待定",
"ProductSN": "待定", "ProductSN": "DB1001.DBW110",
"IntoStationResp": "待定" // "IntoStationResp": "DB1000.DBW2" //
} }
}, },
@ -536,16 +539,16 @@
}, },
{ {
"Id": 23, "Id": 23,
"isEnble": false, "isEnble": true,
"WorkStationCode": "OP115", "WorkStationCode": "OP115",
"WorkStationName": "OP115 自动上下料到固化炉", "WorkStationName": "OP115 自动上下料到固化炉",
"PlcType": "S71500", "PlcType": "S71500",
"IpAddress": "192.168.11.231", "IpAddress": "192.168.12.11",
"IntoStation": { "IntoStation": {
"Heartbeat": "DB1000.DBW0", // "Heartbeat": "DB1000.DBW0", //
"IntoStationAsk": "DB1001.DBW2000", // "IntoStationAsk": "DB1001.DBW2000", //
"ProductModel": "DB1001.DBB1000", "ProductModel": "DB1001.DBB1000",
"ProductSN": "DB1001.DBB1054", "ProductSN": "DB1001.DBB2100",
"IntoStationResp": "DB1000.DBW2000" // "IntoStationResp": "DB1000.DBW2000" //
} }
}, },

View File

@ -44,96 +44,142 @@ namespace RIZO.Service.PLCBackground.Stations.Into
_optionsSetting= options.Value; _optionsSetting= options.Value;
} }
protected override async Task ExecuteAsync(CancellationToken stoppingToken) protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{ {
_logger.Info("PLC Polling Service started"); _logger.Info("PLC Polling Service started");
// 使用工厂方法创建服务实例 try
// 获取当前的工序/工站
//WorkstationCode = await Context.Queryable<PlantWorkstation>().Where(it => it.PlcIP == plcSetting.IpAddress)
// .Select(it => it.WorkstationCode).FirstAsync();
plcSetting = _optionsSetting.PlcSettings.Where(it => it.WorkStationCode == WorkstationCode).First();
Enum.TryParse<CpuType>(plcSetting.PlcType, out CpuType cpuType);
if (!plcSetting.isEnble)
{ {
return; // 1. 优化:配置查询增加空值判断,避免未找到配置抛出异常
} plcSetting = _optionsSetting.PlcSettings
.FirstOrDefault(it => it.WorkStationCode == WorkstationCode);
using (_plcService = new PlcConntectHepler(plcSetting.IpAddress, cpuType)) if (plcSetting == null)
{
while (!stoppingToken.IsCancellationRequested)
{ {
try _logger.Error($"未找到工站 {WorkstationCode} 对应的 PLC 配置,服务退出");
return;
}
// 2. 优化:判断 Enum 解析结果,避免无效 PLC 连接
if (!Enum.TryParse<CpuType>(plcSetting.PlcType, out CpuType cpuType))
{
_logger.Error($"工站 {WorkstationCode} 的 PLC 类型 {plcSetting.PlcType} 解析失败,服务退出");
return;
}
// 配置未启用,直接退出
if (!plcSetting.isEnble)
{
_logger.Info($"工站 {WorkstationCode} 的 PLC 配置未启用,服务退出");
return;
}
// 保持原有 using 语法,确保 PLC 连接资源自动释放
using (_plcService = new PlcConntectHepler(plcSetting.IpAddress, cpuType))
{
while (!stoppingToken.IsCancellationRequested)
{ {
//心跳检测 "DB1010.DBW0" try
// await _plcService.WriteAsync(plcSetting.intoStation.Heartbeat, (short)1);
await _plcService.WriteAsync2(plcSetting.intoStation.Heartbeat, 1);
// 轮询进站请求信号/工位开始查询请求 "DB1001.DBW2000"
short intoStationAsk = await _plcService.ReadAsync<short>(plcSetting.intoStation.IntoStationAsk);
if (intoStationAsk == 1)
{ {
// 处理进站请求 // 心跳检测
_logger.Info("Processing into station request..."); await _plcService.WriteAsync2(plcSetting.intoStation.Heartbeat, 1);
//获取产品SN码 "DB1001.DBB1000" // 轮询进站请求信号
// string productModel = await _plcService.ReadStringAsync(plcSetting.intoStation.ProductModel); short intoStationAsk = await _plcService.ReadAsync<short>(plcSetting.intoStation.IntoStationAsk);
string productModel = ReadPlcStringAsync(_plcService._plc, plcSetting.intoStation.ProductModel, 48).Result;
//"DB1001.DBB1054"
//string productSN = await _plcService.ReadStringAsync(plcSetting.intoStation.ProductSN);
string productSN = ReadPlcStringAsync(_plcService._plc, plcSetting.intoStation.ProductSN, 48).Result;
// 获取工单 if (intoStationAsk > 0)
//获取工艺路线工序
List<ProcessOperation> processOperations = await Context.Queryable<ProcessRouting>().LeftJoin<ProcessOperation>((r, o) => r.RoutingCode == o.FkRoutingCode)
.Where((r, o) => r.FkProductMaterialCode == productModel && r.Status == 1)
.Select((r, o) => o).ToListAsync();
//判断改产品是正常件还是返工件
string Routingcode = processOperations?.First()?.FkRoutingCode ?? "";
//插入入站请求ASK过站记录
ProductPassStationRecord ASKintoStation = new ProductPassStationRecord
{ {
ProductSN = productSN, _logger.Info("Processing into station request...");
WorkstationCode = WorkstationCode,
Routingcode = Routingcode,
OperationCode = WorkstationCode,
OperationName= plcSetting.WorkStationName,
ProductionLifeStage = 1, // 1表示生产中
PasstationType = 0, // 0表示入站请求
PasstationDescription = "入站请求ASK",
EffectTime = DateTime.Now,
CreatedTime = DateTime.Now,
};
await Context.Insertable(ASKintoStation).ExecuteCommandAsync();
//判断该产品是否允许进站
EntryPermissionResult result = await checkEntryPermission(productModel, productSN, WorkstationCode, processOperations);
// "DB1010.DBW2000"
// await _plcService.WriteAsync(plcSetting.intoStation.IntoStationResp, (short)result);
await _plcService.WriteAsync2(plcSetting.intoStation.IntoStationResp,(int) result);
}
await Task.Delay(_pollingInterval, stoppingToken);
} // 3. 优化:消除 Task.Result 阻塞,改为异步 await且并行执行两个 PLC 读操作
catch (OperationCanceledException ex) Task<string> productModelTask = ReadPlcStringAsync(_plcService._plc, plcSetting.intoStation.ProductModel, 48);
{ Task<string> productSNTask = ReadPlcStringAsync(_plcService._plc, plcSetting.intoStation.ProductSN, 48);
_logger.Info("PLC Polling Service is stopping"); await Task.WhenAll(productModelTask, productSNTask);
break;
} string productModel = productModelTask.Result;
catch (Exception ex) string productSN = productSNTask.Result;
{
_logger.Error(ex, "PLC polling error"); string Routingcode = "";
await Task.Delay(TimeSpan.FromSeconds(10), stoppingToken); List<ProcessOperation> processOperations = new List<ProcessOperation>();
bool isSkipPermissionCheck = WorkstationCode == "OP051扫码" || WorkstationCode == "OP115";
if (!isSkipPermissionCheck)
{
// 5. 优化:数据库查询传入取消令牌,支持服务快速停止
processOperations = await Context.Queryable<ProcessRouting>()
.LeftJoin<ProcessOperation>((r, o) => r.RoutingCode == o.FkRoutingCode)
.Where((r, o) => r.FkProductMaterialCode == productModel && r.Status == 1)
.Select((r, o) => o)
.ToListAsync(stoppingToken);
Routingcode = processOperations?.FirstOrDefault()?.FkRoutingCode ?? "";
}
// 插入入站请求ASK过站记录保留原有业务逻辑
ProductPassStationRecord ASKintoStation = new ProductPassStationRecord
{
ProductSN = productSN,
WorkstationCode = WorkstationCode,
Routingcode = Routingcode,
OperationCode = WorkstationCode,
OperationName = plcSetting.WorkStationName,
ProductionLifeStage = 1, // 1表示生产中
PasstationType = 0, // 0表示入站请求
PasstationDescription = "入站请求ASK",
EffectTime = DateTime.Now,
CreatedTime = DateTime.Now,
};
await Context.Insertable(ASKintoStation).ExecuteCommandAsync(stoppingToken);
if (!isSkipPermissionCheck)
{
// 判断该产品是否允许进站
EntryPermissionResult result = await checkEntryPermission(productModel, productSN, WorkstationCode, processOperations);
// 6. 优化OP050 的多个写操作并行执行,减少 PLC 通信耗时
var plcWriteTasks = new List<Task>();
plcWriteTasks.Add(_plcService.WriteAsync2(plcSetting.intoStation.IntoStationResp, (int)result));
if (WorkstationCode == "OP050")
{
plcWriteTasks.Add(_plcService.WriteAsync2(plcSetting.intoStation.IntoStationResp2, (int)result));
plcWriteTasks.Add(_plcService.WriteAsync2(plcSetting.intoStation.IntoStationResp3, (int)result));
plcWriteTasks.Add(_plcService.WriteAsync2(plcSetting.intoStation.IntoStationResp4, (int)result));
}
// 并行执行所有写操作,提升 PLC 反馈效率
await Task.WhenAll(plcWriteTasks);
}
else
{
// 直接允许进站
await _plcService.WriteAsync2(plcSetting.intoStation.IntoStationResp, 1);
}
}
// 轮询延迟,保留取消令牌,服务停止时无需等待延迟完成
await Task.Delay(_pollingInterval, stoppingToken);
}
catch (OperationCanceledException)
{
_logger.Info("PLC Polling Service is stopping");
break;
}
catch (Exception ex)
{
_logger.Error(ex, "PLC polling error");
await Task.Delay(10, stoppingToken);
}
} }
} }
} }
catch (Exception ex)
_logger.Info("PLC Polling Service stopped"); {
_logger.Error(ex, "PLC Polling Service encountered fatal error during initialization");
}
finally
{
_logger.Info("PLC Polling Service stopped");
}
} }
/// <summary> /// <summary>
@ -196,9 +242,9 @@ namespace RIZO.Service.PLCBackground.Stations.Into
// 2. 清理 OperationCode去除首尾空格 + 移除 \r 等不可见控制字符(兼容你之前的问题) // 2. 清理 OperationCode去除首尾空格 + 移除 \r 等不可见控制字符(兼容你之前的问题)
string cleanOperationCode = LastOperation.OperationCode string cleanOperationCode = LastOperation.OperationCode
.Replace("\r", "") // 移除回车符 .Replace("\r", "")
.Replace("\n", "") // 可选:移除换行符 .Replace("\n", "")
.Trim(); // 移除首尾空格 .Trim();
productSN = productSN.Replace("\r", "").Replace("\n", "").Trim(); productSN = productSN.Replace("\r", "").Replace("\n", "").Trim();
// 3. 构建并执行真正的异步查询(补充执行方法,使 await 生效) // 3. 构建并执行真正的异步查询(补充执行方法,使 await 生效)
var ExistLastOperationRecord = await DbScoped.SugarScope.CopyNew() var ExistLastOperationRecord = await DbScoped.SugarScope.CopyNew()

View File

@ -29,7 +29,7 @@ namespace RIZO.Service.PLCBackground.Stations.Into
{ {
public PlcIntoStationService_OP020_2(IOptions<OptionsSetting> options) : base(options) public PlcIntoStationService_OP020_2(IOptions<OptionsSetting> options) : base(options)
{ {
WorkstationCode = "OP020_2"; WorkstationCode = "OP020-2";
} }
/// <summary> /// <summary>
@ -38,122 +38,122 @@ namespace RIZO.Service.PLCBackground.Stations.Into
/// <param name="productModel">产品型号</param> /// <param name="productModel">产品型号</param>
/// <param name="productSN">产品SN</param> /// <param name="productSN">产品SN</param>
/// <returns></returns> /// <returns></returns>
protected override async Task<EntryPermissionResult> checkEntryPermission(string productModel, string productSN, string workstationCode, List<ProcessOperation> processOperations) //protected override async Task<EntryPermissionResult> checkEntryPermission(string productModel, string productSN, string workstationCode, List<ProcessOperation> processOperations)
{ //{
EntryPermissionResult result = EntryPermissionResult.UnkownException; // EntryPermissionResult result = EntryPermissionResult.UnkownException;
//2上工位无记录 // //2上工位无记录
//ProcessOperation LastOperation = processOperations.Where(operation => operation.OperationSeq < processOperations.Where(it => it.OperationCode == workstationCode).Select(it => it.OperationSeq).First()).OrderByDescending(operation => operation.OperationSeq).First(); // //ProcessOperation LastOperation = processOperations.Where(operation => operation.OperationSeq < processOperations.Where(it => it.OperationCode == workstationCode).Select(it => it.OperationSeq).First()).OrderByDescending(operation => operation.OperationSeq).First();
int LastOperationSeq = processOperations.Where(operation => operation.OperationCode == workstationCode).Select(operation => operation.LastOperationSeq ?? -1).First(); // int LastOperationSeq = processOperations.Where(operation => operation.OperationCode == workstationCode).Select(operation => operation.LastOperationSeq ?? -1).First();
ProcessOperation LastOperation = processOperations.Where(it => it.OperationSeq == LastOperationSeq).First(); // ProcessOperation LastOperation = processOperations.Where(it => it.OperationSeq == LastOperationSeq).First();
bool isExistLastOperationRecord = await DbScoped.SugarScope.CopyNew().Queryable<ProductPassStationRecord>() // bool isExistLastOperationRecord = await DbScoped.SugarScope.CopyNew().Queryable<ProductPassStationRecord>()
.Where(it => it.ProductSN == productSN) // .Where(it => it.ProductSN == productSN)
.Where(it => it.OperationCode == LastOperation.OperationCode) // .Where(it => it.OperationCode == LastOperation.OperationCode)
.AnyAsync(); // .AnyAsync();
if (!isExistLastOperationRecord) // if (!isExistLastOperationRecord)
{ // {
return EntryPermissionResult.NoRecordAtPreviousStation; // return EntryPermissionResult.NoRecordAtPreviousStation;
} // }
// 3 上工位NG 入站或者出站结果 NG // // 3 上工位NG 入站或者出站结果 NG
bool isExistLastOperationNG = await Context.Queryable<ProductPassStationRecord>() // bool isExistLastOperationNG = await Context.Queryable<ProductPassStationRecord>()
.Where(it => it.ProductSN == productSN) // .Where(it => it.ProductSN == productSN)
.Where(it => it.OperationCode == LastOperation.OperationCode) // .Where(it => it.OperationCode == LastOperation.OperationCode)
.Where(it => it.PasstationType == 2 || it.PasstationType == 4) // .Where(it => it.PasstationType == 2 || it.PasstationType == 4)
.Where(it => it.ResultCode != 1) // .Where(it => it.ResultCode != 1)
.AnyAsync(); // .AnyAsync();
if (!isExistLastOperationNG) // if (!isExistLastOperationNG)
{ // {
result = EntryPermissionResult.PreviousStationNG; // result = EntryPermissionResult.PreviousStationNG;
goto InsertPassrecord; // goto InsertPassrecord;
} // }
// 4 扫码产品型号错误 // // 4 扫码产品型号错误
bool isExistproductSN = await Context.Queryable<ProductLifecycle>() // bool isExistproductSN = await Context.Queryable<ProductLifecycle>()
.Where(it => it.ProductSN == productSN).AnyAsync(); // .Where(it => it.ProductSN == productSN).AnyAsync();
bool isExistproductSNProducting = await Context.Queryable<ProductLifecycle>() // bool isExistproductSNProducting = await Context.Queryable<ProductLifecycle>()
.Where(it => it.ProductSN == productSN && (it.ProductCurrentStatus != 1 && it.ProductCurrentStatus != 3)).AnyAsync(); // .Where(it => it.ProductSN == productSN && (it.ProductCurrentStatus != 1 && it.ProductCurrentStatus != 3)).AnyAsync();
if (!isExistproductSN || !isExistproductSNProducting) // if (!isExistproductSN || !isExistproductSNProducting)
{ // {
result = EntryPermissionResult.ProductModelError; // result = EntryPermissionResult.ProductModelError;
goto InsertPassrecord; // goto InsertPassrecord;
} // }
//6时间超出规定无法生产 // //6时间超出规定无法生产
//7固化时间未到规定时间 // //7固化时间未到规定时间
int LastOperationStandardTime = processOperations.Where(operation => operation.OperationCode == LastOperation.OperationCode) // int LastOperationStandardTime = processOperations.Where(operation => operation.OperationCode == LastOperation.OperationCode)
.Select(operation => operation.StandardTime ?? 0).First(); // .Select(operation => operation.StandardTime ?? 0).First();
if (LastOperationStandardTime > 0) // if (LastOperationStandardTime > 0)
{ // {
// 上一站的入站时间 和本站的请求时间差值 // // 上一站的入站时间 和本站的请求时间差值
DateTime LastInStationTime = await Context.Queryable<ProductPassStationRecord>() // DateTime LastInStationTime = await Context.Queryable<ProductPassStationRecord>()
.Where(it => it.ProductSN == productSN) // .Where(it => it.ProductSN == productSN)
.Where(it => it.OperationCode == LastOperation.OperationCode) // .Where(it => it.OperationCode == LastOperation.OperationCode)
.Where(it => it.PasstationType == 1) // .Where(it => it.PasstationType == 1)
.MaxAsync(it => it.InStationTime ?? DateTime.MinValue); // .MaxAsync(it => it.InStationTime ?? DateTime.MinValue);
TimeSpan timeDiff = DateTime.Now - LastInStationTime; // TimeSpan timeDiff = DateTime.Now - LastInStationTime;
double totalSeconds = timeDiff.TotalSeconds; // double totalSeconds = timeDiff.TotalSeconds;
if (totalSeconds < LastOperationStandardTime) // if (totalSeconds < LastOperationStandardTime)
{ // {
result = EntryPermissionResult.CuringTimeNotReached; // result = EntryPermissionResult.CuringTimeNotReached;
goto InsertPassrecord; // goto InsertPassrecord;
} // }
} // }
//12 最大重复入站次数 // //12 最大重复入站次数
int MaxRepeatEntries = processOperations.Where(operation => operation.OperationCode == workstationCode).Select(operation => operation.MaxStationCount ?? 1).First(); // int MaxRepeatEntries = processOperations.Where(operation => operation.OperationCode == workstationCode).Select(operation => operation.MaxStationCount ?? 1).First();
if (MaxRepeatEntries > 1) // if (MaxRepeatEntries > 1)
{ // {
int currentStationEntryCount = await Context.Queryable<ProductPassStationRecord>() // int currentStationEntryCount = await Context.Queryable<ProductPassStationRecord>()
.Where(it => it.ProductSN == productSN) // .Where(it => it.ProductSN == productSN)
.Where(it => it.OperationCode == workstationCode) // .Where(it => it.OperationCode == workstationCode)
.Where(it => it.PasstationType == 1) // .Where(it => it.PasstationType == 1)
.CountAsync(); // .CountAsync();
if (currentStationEntryCount >= MaxRepeatEntries) // if (currentStationEntryCount >= MaxRepeatEntries)
{ // {
result = EntryPermissionResult.MaximumRepeatedEntries; // result = EntryPermissionResult.MaximumRepeatedEntries;
goto InsertPassrecord; // goto InsertPassrecord;
} // }
} // }
//OK 准许进站 // //OK 准许进站
result = EntryPermissionResult.AllowIntoStation; // result = EntryPermissionResult.AllowIntoStation;
goto InsertPassrecord; // goto InsertPassrecord;
InsertPassrecord: //InsertPassrecord:
//插入入站请求Resp过站记录 // //插入入站请求Resp过站记录
ProductPassStationRecord RespintoStation = new ProductPassStationRecord // ProductPassStationRecord RespintoStation = new ProductPassStationRecord
{ // {
ProductSN = productSN, // ProductSN = productSN,
WorkstationCode = WorkstationCode, // WorkstationCode = WorkstationCode,
Routingcode = processOperations?.First()?.FkRoutingCode ?? "", // Routingcode = processOperations?.First()?.FkRoutingCode ?? "",
OperationCode = WorkstationCode, // OperationCode = WorkstationCode,
OperationName = plcSetting.WorkStationName, // OperationName = plcSetting.WorkStationName,
ProductionLifeStage = 1, // 1表示生产中 // ProductionLifeStage = 1, // 1表示生产中
PasstationType = 1, // 入站完毕 // PasstationType = 1, // 入站完毕
PasstationDescription = "入站完毕Resp", // PasstationDescription = "入站完毕Resp",
EffectTime = DateTime.Now, // EffectTime = DateTime.Now,
InStationTime = DateTime.Now, // InStationTime = DateTime.Now,
ResultCode = (int)result, // ResultCode = (int)result,
ResultDescription = result.ToString(), // ResultDescription = result.ToString(),
CreatedTime = DateTime.Now, // CreatedTime = DateTime.Now,
}; // };
await Context.Insertable(RespintoStation).ExecuteCommandAsync(); // await Context.Insertable(RespintoStation).ExecuteCommandAsync();
return result; // return result;
} //}
} }

View File

@ -29,7 +29,7 @@ namespace RIZO.Service.PLCBackground.Stations.Into
{ {
public PlcIntoStationService_OP020_3(IOptions<OptionsSetting> options) : base(options) public PlcIntoStationService_OP020_3(IOptions<OptionsSetting> options) : base(options)
{ {
WorkstationCode = "OP020_3"; WorkstationCode = "OP020-3";
} }
} }

View File

@ -29,7 +29,7 @@ namespace RIZO.Service.PLCBackground.Stations.Into
{ {
public PlcIntoStationService_OP020_4(IOptions<OptionsSetting> options) : base(options) public PlcIntoStationService_OP020_4(IOptions<OptionsSetting> options) : base(options)
{ {
WorkstationCode = "OP020_4"; WorkstationCode = "OP020-4";
} }
} }

View File

@ -0,0 +1,37 @@
using Infrastructure.Attribute;
using Infrastructure.Extensions;
using Infrastructure.Model;
using MDM.Model.Plant;
using MDM.Model.Process;
using Microsoft.AspNetCore.JsonPatch.Operations;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using NLog;
using RIZO.Model.Mes;
using RIZO.Model.MES.product_trace;
using RIZO.Repository;
using RIZO.Service.MES.product.IService;
using RIZO.Service.PLC;
using S7.Net;
using SqlSugar.IOC;
using System;
using System.Threading;
using System.Threading.Tasks;
namespace RIZO.Service.PLCBackground.Stations.Into
{
/// <summary>
/// OP点散热胶GF1500
/// </summary>
[AppService(ServiceType = typeof(PlcIntoStationService_OP050_1), ServiceLifetime = LifeTime.Singleton)]
public class PlcIntoStationService_OP050_1 : PlcIntoStationService_Common
{
public PlcIntoStationService_OP050_1(IOptions<OptionsSetting> options) : base(options)
{
WorkstationCode = "OP050-1";
}
}
}

View File

@ -0,0 +1,37 @@
using Infrastructure.Attribute;
using Infrastructure.Extensions;
using Infrastructure.Model;
using MDM.Model.Plant;
using MDM.Model.Process;
using Microsoft.AspNetCore.JsonPatch.Operations;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using NLog;
using RIZO.Model.Mes;
using RIZO.Model.MES.product_trace;
using RIZO.Repository;
using RIZO.Service.MES.product.IService;
using RIZO.Service.PLC;
using S7.Net;
using SqlSugar.IOC;
using System;
using System.Threading;
using System.Threading.Tasks;
namespace RIZO.Service.PLCBackground.Stations.Into
{
/// <summary>
/// OP点散热胶GF1500
/// </summary>
[AppService(ServiceType = typeof(PlcIntoStationService_OP051Scan), ServiceLifetime = LifeTime.Singleton)]
public class PlcIntoStationService_OP051Scan : PlcIntoStationService_Common
{
public PlcIntoStationService_OP051Scan(IOptions<OptionsSetting> options) : base(options)
{
WorkstationCode = "OP051扫码";
}
}
}