diff --git a/RIZO.Admin.WebApi/PLC/Service/PlcService.cs b/RIZO.Admin.WebApi/PLC/Service/PlcService.cs index c7262c2..20c4c57 100644 --- a/RIZO.Admin.WebApi/PLC/Service/PlcService.cs +++ b/RIZO.Admin.WebApi/PLC/Service/PlcService.cs @@ -12,6 +12,7 @@ using S7.Net; using SqlSugar; using SqlSugar.IOC; using System; +using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; @@ -41,11 +42,6 @@ namespace RIZO.Admin.WebApi.PLC.Service // 先在类中添加2个核心优化字段(支撑高频访问) private readonly SemaphoreSlim _concurrencySemaphore = new SemaphoreSlim(15, 50); // 限制20并发(适配50台PLC) private readonly ConcurrentDictionary _plcConnPool = new(); // 连接池 - private readonly SqlSugarClient Context; - public PlcService() - { - Context = DbScoped.SugarScope.CopyNew(); - } #region PLC地址块儿映射 //MES返回PLC请求映射 private readonly Dictionary _mesIntReturnMap = new() @@ -615,36 +611,36 @@ namespace RIZO.Admin.WebApi.PLC.Service // OP085 专属地址映射(自动上料到波峰焊工位,DB1001) private readonly Dictionary _op085StringMap = new() { - { "报警信息", ("DB1001.DBB58", 48) }, // Array[1..48] of Byte - { "产品型号", ("DB1001.DBB1000", 48) }, // String[48] - { "产品名称", ("DB1001.DBB1054", 48) }, // String[48] - { "波峰焊托盘条码", ("DB1001.DBB2070", 40) }, // String[40] - { "穴位1产品SN", ("DB1001.DBB2486", 40) }, // String[40] - { "穴位2产品SN", ("DB1001.DBB2528", 40) }, // String[40] - { "穴位3产品SN", ("DB1001.DBB2570", 40) }, // String[40] - { "穴位4产品SN", ("DB1001.DBB2612", 40) } // String[40] + { "报警信息", ("DB1030.DBB58", 48) }, // Array[1..48] of Byte + { "产品型号", ("DB1030.DBB1000", 48) }, // String[48] + { "产品名称", ("DB1030.DBB1054", 48) }, // String[48] + { "波峰焊托盘条码", ("DB1030.DBB2070", 40) }, // String[40] + { "穴位1产品SN", ("DB1030.DBB2486", 40) }, // String[40] + { "穴位2产品SN", ("DB1030.DBB2528", 40) }, // String[40] + { "穴位3产品SN", ("DB1030.DBB2570", 40) }, // String[40] + { "穴位4产品SN", ("DB1030.DBB2612", 40) } // String[40] }; private readonly Dictionary _op085IntMap = new() { // 基础状态 - { "运行状态", "DB1001.DBW0" }, // Int - 1=空闲,2=运行中,3=故障 - { "设备模式", "DB1001.DBW2" }, // Int - 1=空模式;2=手动;4=初始化;8=自动;16=CycleStop - { "设备在线状态", "DB1001.DBW4" }, // Int - 1=离线,0=在线 - { "ByPass", "DB1001.DBW6" }, // Int - 1=ByPass,0=正常模式 - { "生产模式", "DB1001.DBW8" }, // Int - 1=点检,2=返工,4=样件,5=正常 + { "运行状态", "DB1030.DBW0" }, // Int - 1=空闲,2=运行中,3=故障 + { "设备模式", "DB1030.DBW2" }, // Int - 1=空模式;2=手动;4=初始化;8=自动;16=CycleStop + { "设备在线状态", "DB1030.DBW4" }, // Int - 1=离线,0=在线 + { "ByPass", "DB1030.DBW6" }, // Int - 1=ByPass,0=正常模式 + { "生产模式", "DB1030.DBW8" }, // Int - 1=点检,2=返工,4=样件,5=正常 // 产量数据 - { "实际产量", "DB1001.DBD1104" }, // DInt - { "合格数量", "DB1001.DBD1108" }, // DInt - { "失败数量", "DB1001.DBD1112" }, // DInt + { "实际产量", "DB1030.DBD1104" }, // DInt + { "合格数量", "DB1030.DBD1108" }, // DInt + { "失败数量", "DB1030.DBD1112" }, // DInt // 操作请求 - { "保存请求", "DB1001.DBW2002" }, // Int - 1=请求开始,0=无请求 - { "线体托盘号", "DB1001.DBW2070" }, // Int + { "保存请求", "DB1030.DBW2002" }, // Int - 1=请求开始,0=无请求 + { "线体托盘号", "DB1030.DBW2070" }, // Int // 工艺参数 - { "节拍时间", "DB1001.DBD2242" } // Real + { "节拍时间", "DB1030.DBD2242" } // Real }; // OP100 专属地址映射(手动上料壳体&盖板工位,DB1001) @@ -1344,7 +1340,7 @@ namespace RIZO.Admin.WebApi.PLC.Service ? $"{plcName}生产数据读取成功(复用连接)" : $"{plcName}生产数据读取成功(新建连接)"; WritePlcSaveRequestResult(plc, ip, plcName, prodData, "1"); - _ = Task.Run(() => RecordPlcOperationResult(plcName, prodData)).ConfigureAwait(false); + _ = Task.Run(() => RecordPlcOperationResult(plcName, prodData)).ConfigureAwait(false); return (true, prodData, successMsg); } @@ -1403,11 +1399,17 @@ namespace RIZO.Admin.WebApi.PLC.Service var taskTrayNo = ReadPlcIntAsync(plc, _op020_2IntMap["托盘号"]); var taskTotalResult = ReadPlcIntAsync(plc, _op020_2IntMap["总结果"]); + // DInt任务(单独提取,避免字典开销) + var taskActualOutput = ReadPlcIntAsync(plc, _op050IntMap["实际产量"]); + var taskQualifiedQty = ReadPlcIntAsync(plc, _op050IntMap["合格数量"]); + var taskFailedQty = ReadPlcIntAsync(plc, _op050IntMap["失败数量"]); + + // 1.3 等待所有并行任务完成(关键:耗时=最慢的单个读取任务,而非总和) await Task.WhenAll( taskProductModel, taskProductName, taskSN, taskRunStatus, taskMachineModel, taskOnlineStatus, - taskProduceModel, taskTrayNo, taskTotalResult); + taskProduceModel, taskTrayNo, taskTotalResult, taskActualOutput, taskQualifiedQty, taskFailedQty); // 2. 获取读取结果(带空值兜底,避免后续空引用) string productModel = await taskProductModel ?? string.Empty; @@ -1419,6 +1421,9 @@ namespace RIZO.Admin.WebApi.PLC.Service int produceModel = await taskProduceModel; int trayNo = await taskTrayNo; int totalResult = await taskTotalResult; + int actualOutput = await taskActualOutput; + int qualifiedQty = await taskQualifiedQty; + int failedQty = await taskFailedQty; // 3. 异步复位上传请求(非阻塞,不影响数据读取效率) _ = ResetUploadRequestAsync(plc, ip, _op020_2IntMap["上传请求"]); @@ -1438,9 +1443,6 @@ namespace RIZO.Admin.WebApi.PLC.Service string onlineStatusDesc = onlineStatus == 1 ? "离线" : "在线"; string qualificationFlag = totalResult switch { 1 => "1", 2 => "0", _ => totalResult.ToString() }; - // 调试日志(优化:使用StringBuilder减少拼接开销,高频场景更友好) - Console.WriteLine($"OP020-2({ip})读取结果:产品型号={productModel},运行状态={runStatusDesc},总结果={qualificationFlag}"); - // 5. 构建数据实体(优化:减少Trim/空值判断,提前兜底) return new PlcProductionData { @@ -1460,7 +1462,10 @@ namespace RIZO.Admin.WebApi.PLC.Service Runstatus = runStatus, OnlineStatus = onlineStatusDesc, ProduceModel = produceModelDesc, - TrayNo = trayNo.ToString(), // int转string开销可忽略,无需优化 + ActualOutQty = actualOutput.ToString(), + QualifiedQty = qualifiedQty.ToString(), + FailedQty = failedQty.ToString(), + TrayNo = trayNo.ToString(), CreatedBy = "PLC", CreatedTime = DateTime.Now }; @@ -1494,8 +1499,13 @@ namespace RIZO.Admin.WebApi.PLC.Service var taskOnlineStatus = ReadPlcIntAsync(plc, _op020_3IntMap["设备在线状态"]); var taskProduceModel = ReadPlcIntAsync(plc, _op020_3IntMap["生产模式"]); + // DInt任务(单独提取,避免字典开销) + var taskActualOutput = ReadPlcIntAsync(plc, _op050IntMap["实际产量"]); + var taskQualifiedQty = ReadPlcIntAsync(plc, _op050IntMap["合格数量"]); + var taskFailedQty = ReadPlcIntAsync(plc, _op050IntMap["失败数量"]); + // 步骤2:等待所有任务并行完成(耗时=最慢的单个读取任务,而非总和) - await Task.WhenAll(taskRunStatus, taskMachineModel, taskOnlineStatus, taskProduceModel); + await Task.WhenAll(taskRunStatus, taskMachineModel, taskOnlineStatus, taskProduceModel, taskActualOutput, taskQualifiedQty, taskFailedQty); // 步骤3:获取结果(带await确保值已返回,无需额外判空) int runStatus = await taskRunStatus; @@ -1503,6 +1513,10 @@ namespace RIZO.Admin.WebApi.PLC.Service int onlineStatus = await taskOnlineStatus; int produceModel = await taskProduceModel; + int actualOutput = await taskActualOutput; + int qualifiedQty = await taskQualifiedQty; + int failedQty = await taskFailedQty; + // 2. 业务逻辑计算(优化:常量复用+减少冗余计算) var reworkFlag = produceModel == 4 ? "1" : "0"; @@ -1539,7 +1553,9 @@ namespace RIZO.Admin.WebApi.PLC.Service LineCode = "line2", WorkstationCode = workstationCode, WorkstationName = "热铆", // 补充工站名称 - // 状态字段:直接赋值,无冗余转换 + ActualOutQty = actualOutput.ToString(), + QualifiedQty = qualifiedQty.ToString(), + FailedQty = failedQty.ToString(), ReworkFlag = reworkFlag, Automanual = machineModel, Runstatus = runStatus, @@ -1598,10 +1614,19 @@ namespace RIZO.Admin.WebApi.PLC.Service { "产品4结果", ReadPlcIntAsync(plc, _op020_4IntMap["产品4结果"]) } }; + // DInt(双整数)读取任务(复用ReadPlcIntAsync,PLC底层兼容DInt转int) + var dIntReadTasks = new Dictionary> + { + { "实际产量", ReadPlcIntAsync(plc, _op050IntMap["实际产量"]) }, + { "合格数量", ReadPlcIntAsync(plc, _op050IntMap["合格数量"]) }, + { "失败数量", ReadPlcIntAsync(plc, _op050IntMap["失败数量"]) } + }; + // 2. 并行等待所有读取任务完成(合并任务列表,单次WaitAll提升效率) var allTasks = new List(); allTasks.AddRange(stringReadTasks.Values); allTasks.AddRange(intReadTasks.Values); + allTasks.AddRange(dIntReadTasks.Values); await Task.WhenAll(allTasks).ConfigureAwait(false); // 3. 提取读取结果(直接取值+空值兜底,减少await重复调用) @@ -1624,6 +1649,9 @@ namespace RIZO.Admin.WebApi.PLC.Service int product3Result = intReadTasks["产品3结果"].Result; int product4Result = intReadTasks["产品4结果"].Result; + int actualOutput = dIntReadTasks["实际产量"].Result; + int qualifiedQty = dIntReadTasks["合格数量"].Result; + int failedQty = dIntReadTasks["失败数量"].Result; // 4. 业务逻辑转换(极简逻辑,仅保留必要转换) string reworkFlag = produceModel == 4 ? "1" : "0"; @@ -1668,6 +1696,9 @@ namespace RIZO.Admin.WebApi.PLC.Service Product4Result = product1Result.ToString(), SN1 = product1SN, SN2 = product2SN, + ActualOutQty = actualOutput.ToString(), + QualifiedQty = qualifiedQty.ToString(), + FailedQty = failedQty.ToString(), QualificationFlag = qualificationFlag }; } @@ -1755,6 +1786,7 @@ namespace RIZO.Admin.WebApi.PLC.Service int byPass = intReadTasks["ByPass"].Result; int produceModel = intReadTasks["生产模式"].Result; int trayNo = intReadTasks["托盘号"].Result; + int actualOutput = dIntReadTasks["实际产量"].Result; int qualifiedQty = dIntReadTasks["合格数量"].Result; int failedQty = dIntReadTasks["失败数量"].Result; @@ -1927,7 +1959,9 @@ namespace RIZO.Admin.WebApi.PLC.Service TrayNo = string.Empty, CreatedBy = "PLC", CreatedTime = DateTime.Now, - // 产量/节拍核心字段 + ActualOutQty = actualOutput.ToString(), + QualifiedQty = qualifiedQty.ToString(), + FailedQty = failedQty.ToString(), ProductionCycle = (int)Math.Round(cycleTime), // 节拍时间转整数秒 QualificationFlag = qualificationFlag, // 备注记录产量信息(便于排查) @@ -1978,9 +2012,18 @@ namespace RIZO.Admin.WebApi.PLC.Service { "托盘号", ReadPlcIntAsync(plc, _op051ScanIntMap["托盘号"]) } }; + // DInt(双整数)读取任务(复用ReadPlcIntAsync,PLC底层兼容DInt转int) + var dIntReadTasks = new Dictionary> + { + { "实际产量", ReadPlcIntAsync(plc, _op050IntMap["实际产量"]) }, + { "合格数量", ReadPlcIntAsync(plc, _op050IntMap["合格数量"]) }, + { "失败数量", ReadPlcIntAsync(plc, _op050IntMap["失败数量"]) } + }; + // 2. 并行等待所有任务完成(单次WaitAll,最小化等待时间) var allTasks = new List(); allTasks.AddRange(stringTasks.Values); + allTasks.AddRange(dIntReadTasks.Values); allTasks.AddRange(intTasks.Values); await Task.WhenAll(allTasks).ConfigureAwait(false); @@ -2000,6 +2043,10 @@ namespace RIZO.Admin.WebApi.PLC.Service var uploadRequest = intTasks["上传结果请求"].Result; var trayNo = intTasks["托盘号"].Result.ToString(); // 转换为字符串,适配实体字段 + int actualOutput = dIntReadTasks["实际产量"].Result; + int qualifiedQty = dIntReadTasks["合格数量"].Result; + int failedQty = dIntReadTasks["失败数量"].Result; + // 4. 核心逻辑转换(极简表达式,适配扫码工位业务) var reworkFlag = produceModel == 4 ? "1" : "0"; // 4=返工模式→1,其他→0 var produceModelDesc = produceModel switch @@ -2040,6 +2087,9 @@ namespace RIZO.Admin.WebApi.PLC.Service Product2SN = sn2, Product3SN = sn3, QualificationFlag = qualificationFlag, + ActualOutQty = actualOutput.ToString(), + QualifiedQty = qualifiedQty.ToString(), + FailedQty = failedQty.ToString(), CreatedBy = "PLC", CreatedTime = now, }; @@ -2178,6 +2228,9 @@ namespace RIZO.Admin.WebApi.PLC.Service Product1Result = pin1Result.ToString(), Product2Result = pin2Result.ToString(), QualificationFlag = qualificationFlag, + ActualOutQty = actualOutput.ToString(), + QualifiedQty = qualifiedQty.ToString(), + FailedQty = failedQty.ToString(), ProductionCycle = (int)Math.Round(cycleTime), }; @@ -2268,6 +2321,9 @@ namespace RIZO.Admin.WebApi.PLC.Service PinPressure1 = pressure.ToString(), PinStroke1 = stroke.ToString(), QualificationFlag = qualificationFlag, + ActualOutQty = actualOutput.ToString(), + QualifiedQty = qualifiedQty.ToString(), + FailedQty = failedQty.ToString(), ProductionCycle = (int)Math.Round(cycleTime), CreatedBy = "PLC_System", CreatedTime = now, @@ -2353,6 +2409,8 @@ namespace RIZO.Admin.WebApi.PLC.Service var onlineStatus = intTasks["设备在线状态"].Result; var produceModel = intTasks["生产模式"].Result; var actualOutput = intTasks["实际产量"].Result; + var qualifiedQty = intTasks["合格数量"].Result; + var failedQty = intTasks["失败数量"].Result; var productTotalResult = intTasks["产品总结果"].Result; var screw1Result = intTasks["1#螺丝结果"].Result; var screw2Result = intTasks["2#螺丝结果"].Result; @@ -2408,6 +2466,9 @@ namespace RIZO.Admin.WebApi.PLC.Service OnlineStatus = onlineStatusDesc, ProduceModel = produceModelDesc, QualificationFlag = qualificationFlag, + ActualOutQty = actualOutput.ToString(), + QualifiedQty = qualifiedQty.ToString(), + FailedQty = failedQty.ToString(), ProductionCycle = (int)Math.Round(cycleTime), CreatedBy = "PLC", CreatedTime = now, @@ -2482,7 +2543,10 @@ namespace RIZO.Admin.WebApi.PLC.Service var taskTrayNo = ReadPlcIntAsync(plc, _op070_1IntMap["托盘号"]); var taskCameraResult = ReadPlcIntAsync(plc, _op070_1IntMap["相机结果"]); var taskStationResult = ReadPlcIntAsync(plc, _op070_1IntMap["站位结果"]); - + // DInt任务(单独提取,避免字典开销) + var taskActualOutput = ReadPlcIntAsync(plc, _op050IntMap["实际产量"]); + var taskQualifiedQty = ReadPlcIntAsync(plc, _op050IntMap["合格数量"]); + var taskFailedQty = ReadPlcIntAsync(plc, _op050IntMap["失败数量"]); // ========== Real字段并行任务 ========== var taskCycleTime = ReadPlcRealAsync(plc, _op070_1IntMap["节拍时间"]); @@ -2494,7 +2558,9 @@ namespace RIZO.Admin.WebApi.PLC.Service taskRunStatus, taskMachineModel, taskOnlineStatus, taskByPass, taskProduceModel, taskTrayNo, taskCameraResult, taskStationResult, // Real任务 - taskCycleTime); + taskCycleTime, + // DInt任务 + taskActualOutput, taskQualifiedQty, taskFailedQty); // 3. 获取并行读取结果(带空值兜底,避免后续空引用) // 字符串字段 @@ -2513,10 +2579,14 @@ namespace RIZO.Admin.WebApi.PLC.Service int trayNo = await taskTrayNo; int cameraResult = await taskCameraResult; int stationResult = await taskStationResult; + int actualOutput = await taskActualOutput; + int qualifiedQty = await taskQualifiedQty; + int failedQty = await taskFailedQty; // Real字段 float cycleTime = await taskCycleTime; + // 4. 异步复位保存请求(非阻塞,不影响读取效率) //_ = ResetSaveRequestAsync(plc, ip, _op070_1IntMap["保存请求"]); @@ -2553,6 +2623,9 @@ namespace RIZO.Admin.WebApi.PLC.Service SN1 = sn1, SN2 = sn2, QualificationFlag = qualificationFlag, + ActualOutQty = actualOutput.ToString(), + QualifiedQty = qualifiedQty.ToString(), + FailedQty = failedQty.ToString(), ReworkFlag = reworkFlag, Automanual = machineModel, Runstatus = runStatus, @@ -2609,6 +2682,11 @@ namespace RIZO.Admin.WebApi.PLC.Service // ========== Real字段并行任务 ========== var taskCycleTime = ReadPlcRealAsync(plc, _op075IntMap["节拍时间"]); + // DInt任务(单独提取,避免字典开销) + var taskActualOutput = ReadPlcIntAsync(plc, _op050IntMap["实际产量"]); + var taskQualifiedQty = ReadPlcIntAsync(plc, _op050IntMap["合格数量"]); + var taskFailedQty = ReadPlcIntAsync(plc, _op050IntMap["失败数量"]); + // 2. 等待所有并行任务完成(核心:耗时=最慢的单个读取任务,而非总和) await Task.WhenAll( // 字符串任务 @@ -2617,7 +2695,10 @@ namespace RIZO.Admin.WebApi.PLC.Service taskRunStatus, taskMachineModel, taskOnlineStatus, taskByPass, taskProduceModel, taskTrayNo, taskStationResult, // Real任务 - taskCycleTime); + taskCycleTime + // DInt任务 + , taskActualOutput, taskQualifiedQty, taskFailedQty + ); // 3. 获取并行读取结果(带空值兜底,避免后续空引用) // 字符串字段 @@ -2636,7 +2717,9 @@ namespace RIZO.Admin.WebApi.PLC.Service int produceModel = await taskProduceModel; int trayNo = await taskTrayNo; int stationResult = await taskStationResult; - + int actualOutput = await taskActualOutput; + int qualifiedQty = await taskQualifiedQty; + int failedQty = await taskFailedQty; // Real字段 float cycleTime = await taskCycleTime; @@ -2676,6 +2759,9 @@ namespace RIZO.Admin.WebApi.PLC.Service SN2 = sn2, ChipSN = chipsn, // 保留芯片SN独有字段 QualificationFlag = qualificationFlag, + ActualOutQty = actualOutput.ToString(), + QualifiedQty = qualifiedQty.ToString(), + FailedQty = failedQty.ToString(), ReworkFlag = reworkFlag, Automanual = machineModel, Runstatus = runStatus, @@ -2690,16 +2776,9 @@ namespace RIZO.Admin.WebApi.PLC.Service catch (Exception ex) { // 增强异常日志:包含堆栈,便于定位问题 - Console.WriteLine($"OP075({ip})数据读取异常:{ex.Message}\n{ex.StackTrace}"); + Console.WriteLine($"OP075({ip})数据读取异常:{ex.Message}"); // 异常时返回基础实体,避免业务空引用 - return new PlcProductionData - { - PlcIp = ip, - WorkstationCode = workstationCode, - CreatedBy = "PLC", - CreatedTime = DateTime.Now, - OnlineStatus = "读取异常" - }; + return null; } } @@ -5479,17 +5558,22 @@ namespace RIZO.Admin.WebApi.PLC.Service try { - var routingCode = await Context.Queryable() + // 在方法内创建上下文,并使用using确保自动释放 + using var context = DbScoped.SugarScope.CopyNew(); + if (context == null) + throw new InvalidOperationException("无法创建数据库上下文,请检查SqlSugar配置"); + var routingCode = string.Empty; + try + { + routingCode = await context.Queryable() .LeftJoin((r, o) => r.RoutingCode == o.FkRoutingCode) .Where((r, o) => r.FkProductMaterialCode == strSN && r.Status == 1) - .Select((r, o) => o.FkRoutingCode) + .Select((r, o) => o.FkRoutingCode) .FirstAsync(); // 直接取第一条,避免ToList+First的双重开销 - - // 4.1 工艺路线校验(合并判断,减少分支) - if (string.IsNullOrWhiteSpace(routingCode)) - { - Console.WriteLine($"[{now:HH:mm:ss}] {plcName} - {strSN} 工艺路线无效,跳过记录"); - return; + } + catch + { + } // 5. 返工状态判断(无分配判断,直接赋值) @@ -5510,7 +5594,7 @@ namespace RIZO.Admin.WebApi.PLC.Service }; // 7. 插入数据库(批量插入优化,减少IO交互) - var insertCount = await Context.Insertable(askOutStation).ExecuteCommandAsync(); + var insertCount = await context.Insertable(askOutStation).ExecuteCommandAsync(); } catch (ArgumentNullException ex) diff --git a/RIZO.Service/PLCBackground/Stations/Into/PlcIntoStationService_OP80_01.cs b/RIZO.Service/PLCBackground/Stations/Into/PlcIntoStationService_OP80_01.cs index 0066c1f..ca45f26 100644 --- a/RIZO.Service/PLCBackground/Stations/Into/PlcIntoStationService_OP80_01.cs +++ b/RIZO.Service/PLCBackground/Stations/Into/PlcIntoStationService_OP80_01.cs @@ -23,7 +23,7 @@ namespace RIZO.Service.PLCBackground.Stations.Into private static Logger _logger = LogManager.GetCurrentClassLogger(); private PlcConntectHepler _plcService; private readonly TimeSpan _pollingInterval = TimeSpan.FromSeconds(5); - private readonly string _ipAddress = "192.168.11.56"; + private readonly string _ipAddress = "192.168.1.111"; private readonly CpuType _cpuType = CpuType.S71500; private string WorkstationCode; private readonly SqlSugarClient Context;