Compare commits

...

3 Commits

Author SHA1 Message Date
quowingwang
dbf4a54a91 PLC收集数据 2026-01-24 11:21:57 +08:00
quowingwang
35dd9ea851 PLC查询变更 2026-01-24 10:49:00 +08:00
quowingwang
ad2f0021e6 PLC获取变更 2026-01-24 10:29:47 +08:00

View File

@ -37,39 +37,14 @@ namespace RIZO.Admin.WebApi.PLC.Service
private readonly SemaphoreSlim _concurrencySemaphore = new SemaphoreSlim(15, 50); // 限制20并发适配50台PLC
private readonly ConcurrentDictionary<string, (Plc Client, DateTime LastUsedTime)> _plcConnPool = new(); // 连接池
// PLC地址映射严格匹配业务地址清单
private readonly Dictionary<string, (string Addr, int Len)> _plcStringMap = new()
{
{ "LineCode", ("DB1010.DBB50", 14) },
{ "IpStation", ("DB1010.DBB64", 16) },
{ "ProductCode", ("DB1010.DBB80", 16) },
{ "ProductName", ("DB1010.DBB94", 50) },
{ "PartCode", ("DB1010.DBB144", 16) },
{ "PartName", ("DB1010.DBB158", 28) },
{ "ProcessName", ("DB1010.DBB186", 12) },
{ "ParamName", ("DB1010.DBB198", 12) },
{ "ParamValue", ("DB1010.DBB210", 14) },
{ "Static16", ("DB1010.DBB238", 68) }
};
private readonly Dictionary<string, string> _plcIntMap = new()
{
{ "QualificationFlag", "DB1010.DBW224" },
{ "ReworkFlag", "DB1010.DBW226" },
{ "ProductionCycle", "DB1010.DBW228" },
{ "AutoManual", "DB1010.DBW230" },
{ "RunStatus", "DB1010.DBW232" },
{ "QueryRequest", "DB1001.DBW2001" }
};
#region PLC地址块儿映射
// OP070-1 专属地址映射
private readonly Dictionary<string, (string Addr, int Len)> _op070_1StringMap = new()
{
{ "报警信息", ("DB1001.DBB58", 48) }, // Array[1..48] of Byte
{ "产品型号_48", ("DB1001.DBB1000", 48) }, // String[48]
//{ "报警信息", ("DB1001.DBB58", 48) }, // Array[1..48] of Byte
{ "订单名称", ("DB1001.DBB1000", 48) }, // String[48]
{ "产品名称", ("DB1001.DBB1054", 48) }, // String[48]
{ "产品型号_28", ("DB1001.DBB2006", 28) }, // String[28]
{ "产品型号", ("DB1001.DBB2006", 28) }, // String[28]
{ "SN_1", ("DB1001.DBB2100", 28) }, // String[28]
{ "SN_2", ("DB1001.DBB2134", 28) } // String[28]
};
@ -82,9 +57,9 @@ namespace RIZO.Admin.WebApi.PLC.Service
{ "ByPass", "DB1001.DBW6" }, // Int
{ "生产模式", "DB1001.DBW8" }, // Int
{ "实际产量", "DB1001.DBD1104" }, // DInt
{ "合格数量", "DB1001.DBD1108" }, // DInt
{ "失败数量", "DB1001.DBD1112" }, // DInt
{ "查询请求", "DB1001.DBW2000" }, // Int
//{ "合格数量", "DB1001.DBD1108" }, // DInt
//{ "失败数量", "DB1001.DBD1112" }, // DInt
//{ "查询请求", "DB1001.DBW2000" }, // Int
{ "保存请求", "DB1001.DBW2002" }, // Int
{ "托盘号", "DB1001.DBW2004" }, // Int
{ "相机结果", "DB1001.DBW2164" }, // Int
@ -92,6 +67,78 @@ namespace RIZO.Admin.WebApi.PLC.Service
{ "节拍时间", "DB1001.DBD2168" } // Real
};
// OP080-1 专属地址映射
private readonly Dictionary<string, (string Addr, int Len)> _op080_1StringMap = new()
{
//{ "报警信息", ("DB1001.DBB58", 48) }, // Array[1..48] of Byte
{ "订单名称", ("DB1001.DBB1000", 48) }, // String[48]
{ "产品名称", ("DB1001.DBB1054", 48) }, // String[48]
{ "合装位机壳_SN", ("DB1001.DBB2100", 28) }, // String[28]
{ "合装位PCB_SN", ("DB1001.DBB2130", 28) }, // String[28]
{ "拧紧位机壳_SN", ("DB1001.DBB2904", 28) }, // String[28]
{ "拧紧位PCB_SN", ("DB1001.DBB3092", 28) }, // String[28]
};
private readonly Dictionary<string, string> _op080_1IntMap = new()
{
{ "运行状态", "DB1001.DBW0" }, // Int
{ "设备模式", "DB1001.DBW2" }, // Int
{ "设备在线状态", "DB1001.DBW4" }, // Int
{ "ByPass", "DB1001.DBW6" }, // Int
{ "生产模式", "DB1001.DBW8" }, // Int
{ "实际产量", "DB1001.DBD1104" }, // DInt
{ "合格数量", "DB1001.DBD1108" }, // DInt
{ "失败数量", "DB1001.DBD1112" }, // DInt
{ "合装工位查询", "DB1001.DBW2000" }, // Int
{ "合装结果保存请求", "DB1001.DBW2004" }, // Int
{ "拧紧结果保存请求", "DB1001.DBW2006" }, // Int
{ "托盘号", "DB1001.DBW2004" }, // Int
{ "合装位托盘号", "DB1001.DBW2100" }, // Int
{ "拧紧位托盘号", "DB1001.DBW3152" }, // Int
// 2号螺钉
{ "2号螺钉_结果", "DB1001.DBW3164" }, // Int
{ "2号螺钉_扭矩", "DB1001.DBD3166" }, // Real
{ "2号螺钉_深度", "DB1001.DBD3170" }, // Real
{ "2号螺钉_角度", "DB1001.DBD3174" }, // Real
{ "2号螺钉_拧紧时间", "DB1001.DBD3178" }, // Real
// 3号螺钉
{ "3号螺钉_结果", "DB1001.DBW3182" }, // Int
{ "3号螺钉_扭矩", "DB1001.DBD3184" }, // Real
{ "3号螺钉_深度", "DB1001.DBD3188" }, // Real
{ "3号螺钉_角度", "DB1001.DBD3192" }, // Real
{ "3号螺钉_拧紧时间", "DB1001.DBD3196" }, // Real
// 4号螺钉
{ "4号螺钉_结果", "DB1001.DBW3200" }, // Int
{ "4号螺钉_扭矩", "DB1001.DBD3202" }, // Real
{ "4号螺钉_深度", "DB1001.DBD3206" }, // Real
{ "4号螺钉_角度", "DB1001.DBD3210" }, // Real
{ "4号螺钉_拧紧时间", "DB1001.DBD3214" }, // Real
// 1号螺钉
{ "1号螺钉_结果", "DB1001.DBW3218" }, // Int
{ "1号螺钉_扭矩", "DB1001.DBD3220" }, // Real
{ "1号螺钉_深度", "DB1001.DBD3224" }, // Real
{ "1号螺钉_角度", "DB1001.DBD3228" }, // Real
{ "1号螺钉_拧紧时间", "DB1001.DBD3232" }, // Real
// 5号螺钉
{ "5号螺钉_结果", "DB1001.DBW3236" }, // Int
{ "5号螺钉_扭矩", "DB1001.DBD3238" }, // Real
{ "5号螺钉_深度", "DB1001.DBD3242" }, // Real
{ "5号螺钉_角度", "DB1001.DBD3246" }, // Real
{ "5号螺钉_拧紧时间", "DB1001.DBD3250" }, // Real
// 6号螺钉
{ "6号螺钉_结果", "DB1001.DBW3254" }, // Int
{ "6号螺钉_扭矩", "DB1001.DBD3256" }, // Real
{ "6号螺钉_深度", "DB1001.DBD3260" }, // Real
{ "6号螺钉_角度", "DB1001.DBD3264" }, // Real
{ "6号螺钉_拧紧时间", "DB1001.DBD3268" }, // Real
// 7号螺钉
{ "7号螺钉_结果", "DB1001.DBW3272" }, // Int
{ "7号螺钉_扭矩", "DB1001.DBD3274" }, // Real
{ "7号螺钉_深度", "DB1001.DBD3278" }, // Real
{ "7号螺钉_角度", "DB1001.DBD3282" }, // Real
{ "7号螺钉_拧紧时间", "DB1001.DBD3286" }, // Real
};
#endregion
/// <summary>
/// 构造函数依赖注入获取PLC配置
@ -300,11 +347,6 @@ namespace RIZO.Admin.WebApi.PLC.Service
if (slot < 0 || slot > 4)
return (false, null, $"PLC槽位号{slot}无效有效值0-4");
// 仅放行指定工位,防止非法请求
var supportedPlcs = new HashSet<string> { "OP070-1", "OP080" };
if (!supportedPlcs.Contains(plcName))
return (false, null, $"仅支持OP070-1/OP080当前工位{plcName}");
Plc plc = null;
bool isConnReused = false;
var poolKey = $"{ip}_{rack}_{slot}_{cpuType}";
@ -361,39 +403,40 @@ namespace RIZO.Admin.WebApi.PLC.Service
return (false, null, $"");
}
//给PLC返回数据
if (iQueryRequest == 1)
{
}
// 5. 多工位专属数据读取(预留扩展,逻辑隔离)
if (plcName == "OP070-1")
if (iSaveRequest == 1)
{
if (iSaveRequest == 1)
// 5. 多工位专属数据读取(预留扩展,逻辑隔离)
if (plcName == "OP070-1" || plcName == "OP070-2" || plcName == "OP070-3" || plcName == "OP075")
{
prodData = await ReadOP070_1DataAsync(plc, ip, plcName);
}
}
else if (plcName == "OP080")
{
//prodData = await ReadOP080DataAsync(plc, ip);
}
else if (plcName == "OP080")
{
//prodData = await ReadOP080DataAsync(plc, ip);
}
// 6. 统一空值兜底(避免空引用)
if (prodData != null)
{
prodData.QualificationFlag ??= "0";
prodData.ReworkFlag ??= "0";
prodData.ProductionCycle ??= 0;
// 6. 统一空值兜底(避免空引用)
if (prodData != null)
{
prodData.QualificationFlag ??= "0";
prodData.ReworkFlag ??= "0";
prodData.ProductionCycle ??= 0;
// 7. 异步保存数据日志补充PLC名称便于定位
_ = Task.Run(() => _plcProductionDataService.AddPlcProductionData(prodData))
.ContinueWith(t =>
{
if (t.IsFaulted)
// 7. 异步保存数据日志补充PLC名称便于定位
_ = Task.Run(() => _plcProductionDataService.AddPlcProductionData(prodData))
.ContinueWith(t =>
{
Console.WriteLine($"{plcName}({ip})数据保存失败:{t.Exception?.InnerException?.Message}");
}
});
if (t.IsFaulted)
{
Console.WriteLine($"{plcName}({ip})数据保存失败:{t.Exception?.InnerException?.Message}");
}
});
}
}
// 8. 个性化返回消息(区分工位和连接类型)
@ -419,91 +462,20 @@ namespace RIZO.Admin.WebApi.PLC.Service
}
}
#region
#region
/// <summary>
/// OP070-1专属数据读取精简版高效+易读)
/// </summary>
//private async Task<PlcProductionData> ReadOP070_1DataAsync(Plc plc, string ip)
//{
// // 1. 批量并行读取字符串字段
// // 合并并行读取
// var (strFields, intFields) = await Task.Run(async () => (
// // 字符串字段
// (
// await ReadPlcStringAsync(plc, _op070_1StringMap["报警信息"].Addr, _op070_1StringMap["报警信息"].Len),
// await ReadPlcStringAsync(plc, _op070_1StringMap["产品名称"].Addr, _op070_1StringMap["产品名称"].Len),
// await ReadPlcStringAsync(plc, _op070_1StringMap["产品型号"].Addr, _op070_1StringMap["产品型号"].Len),
// await ReadPlcStringAsync(plc, _op070_1StringMap["SN_1"].Addr, _op070_1StringMap["SN_1"].Len),
// await ReadPlcStringAsync(plc, _op070_1StringMap["SN_2"].Addr, _op070_1StringMap["SN_2"].Len)
// ),
// // 整数字段
// (
// await ReadPlcIntAsync(plc, _op070_1IntMap["运行状态"]),
// await ReadPlcIntAsync(plc, _op070_1IntMap["设备模式"]),
// await ReadPlcIntAsync(plc, _op070_1IntMap["设备在线状态"]),
// await ReadPlcIntAsync(plc, _op070_1IntMap["ByPass"]),
// await ReadPlcIntAsync(plc, _op070_1IntMap["生产模式"]),
// await ReadPlcIntAsync(plc, _op070_1IntMap["托盘号"]),
// await ReadPlcIntAsync(plc, _op070_1IntMap["站位结果"])
// )
// ));
// // 2.解构字段(保持原有逻辑)
// var (alarmMsg, productName, product_model, sn1, product_code) = strFields;
// var (runStatus, machineModel, onlineStatus, byPass, produceModel, trayNo, stationResult) = intFields;
// // 3. 写入保存请求(极简异常防护,不影响主流程)
// try { WritePlcValue(plc, _op070_1IntMap["保存请求"], "1"); }
// catch (Exception ex) { Console.WriteLine($"OP070-1({ip})写保存请求失败:{ex.Message}"); }
// // 4. 极简条件计算(一行搞定,易读高效)
// var reworkFlag = produceModel == 4 ? "1" : "0"; // 返工标志
// string produceModelDesc = produceModel switch // 生产模式映射
// {
// 1 => "正常模式",
// 2 => "清线模式",
// 4 => "返工模式",
// 8 => "换型模式",
// 16 => "预热模式",
// _ => $"未知({produceModel})"
// };
// // 5. 构建数据实体(极简空值兜底,逻辑清晰)
// return new PlcProductionData
// {
// //加一个产品型号
// PlcIp = ip.Trim(),
// OccurTime = DateTime.Now,
// LineCode = "line2",
// WorkstationCode = "OP070-1",
// ProductModel = product_model ?? "",
// ProductName = productName ?? "",
// ProductCode = product_code,
// QualificationFlag = stationResult.ToString(),
// ReworkFlag = reworkFlag,
// AutoManual = machineModel,
// RunStatus = runStatus,
// OnlineStatus = onlineStatus.ToString(),
// TrayNo = trayNo.ToString(),
// ProduceModel = produceModelDesc,
// CreatedBy = "PLC",
// CreatedTime = DateTime.Now
// };
//}
/// <summary>
/// 优化版OP070-1数据读取集成增强型PLC读写方法
/// OP070-1数据读取
/// </summary>
private async Task<PlcProductionData> ReadOP070_1DataAsync(Plc plc, string ip,string workstationCode)
{
// 1. 批量并行读取所有字段(沿用原有结构+增强型读取方法)
var (strFields, intFields, dintFields, realFields) = await Task.Run(async () => (
var (strFields, intFields, realFields) = await Task.Run(async () => (
// 字符串字段(增强空值和异常处理)
(
await ReadPlcStringAsync(plc, _op070_1StringMap["报警信息"].Addr, _op070_1StringMap["报警信息"].Len),
//await ReadPlcStringAsync(plc, _op070_1StringMap["报警信息"].Addr, _op070_1StringMap["报警信息"].Len),
await ReadPlcStringAsync(plc, _op070_1StringMap["产品名称"].Addr, _op070_1StringMap["产品名称"].Len),
await ReadPlcStringAsync(plc, _op070_1StringMap["产品型号_48"].Addr, _op070_1StringMap["产品型号_48"].Len),
await ReadPlcStringAsync(plc, _op070_1StringMap["产品型号_28"].Addr, _op070_1StringMap["产品型号_28"].Len),
await ReadPlcStringAsync(plc, _op070_1StringMap["订单名称"].Addr, _op070_1StringMap["订单名称"].Len),
await ReadPlcStringAsync(plc, _op070_1StringMap["产品型号"].Addr, _op070_1StringMap["产品型号"].Len),
await ReadPlcStringAsync(plc, _op070_1StringMap["SN_1"].Addr, _op070_1StringMap["SN_1"].Len),
await ReadPlcStringAsync(plc, _op070_1StringMap["SN_2"].Addr, _op070_1StringMap["SN_2"].Len)
),
@ -521,11 +493,11 @@ namespace RIZO.Admin.WebApi.PLC.Service
await ReadPlcIntAsync(plc, _op070_1IntMap["站位结果"])
),
// DInt字段增强版读取方法
(
await ReadPlcDIntAsync(plc, _op070_1IntMap["实际产量"]),
await ReadPlcDIntAsync(plc, _op070_1IntMap["合格数量"]),
await ReadPlcDIntAsync(plc, _op070_1IntMap["失败数量"])
),
//(
// await ReadPlcDIntAsync(plc, _op070_1IntMap["实际产量"]),
// await ReadPlcDIntAsync(plc, _op070_1IntMap["合格数量"]),
// await ReadPlcDIntAsync(plc, _op070_1IntMap["失败数量"])
//),
// Real字段增强版读取方法
(
await ReadPlcRealAsync(plc, _op070_1IntMap["节拍时间"])
@ -533,9 +505,9 @@ namespace RIZO.Admin.WebApi.PLC.Service
));
// 2. 解构字段(保持原有逻辑,空值兜底)
var (alarmMsg, productName, productModel48, productModel28, sn1, sn2) = strFields;
var (productName,orderName, productModel, sn1, sn2) = strFields;
var (runStatus, machineModel, onlineStatus, byPass, produceModel, queryReq, saveReq, trayNo, cameraResult, stationResult) = intFields;
var (actualOutput, qualifiedQty, failedQty) = dintFields;
//var (actualOutput, qualifiedQty, failedQty) = dintFields;
float cycleTime = realFields;
// 3. 写入保存请求(异步+增强异常日志)
@ -560,7 +532,7 @@ namespace RIZO.Admin.WebApi.PLC.Service
string cameraResultDesc = cameraResult switch { 1 => "OK", 2 => "NG", _ => $"未知({cameraResult})" };
// 调试日志:输出关键读取结果
Console.WriteLine($"OP070-1({ip})读取结果:产品型号={productModel28 ?? productModel48},运行状态={runStatusDesc},产量={actualOutput}");
Console.WriteLine($"OP070-1({ip})读取结果:产品型号={productModel},运行状态={runStatusDesc},订单名称={orderName}");
// 5. 构建数据实体(增强空值处理)
return new PlcProductionData
@ -569,7 +541,7 @@ namespace RIZO.Admin.WebApi.PLC.Service
OccurTime = DateTime.Now,
LineCode = "line2",
WorkstationCode = workstationCode,
ProductModel = productModel28 ?? productModel48 ?? string.Empty,
ProductModel = productModel ?? string.Empty,
ProductName = productName ?? string.Empty,
ProductCode = sn2 ?? string.Empty,
SN1 = sn1 ?? string.Empty,