5801 lines
303 KiB
C#
5801 lines
303 KiB
C#
// 统一引入所有必要命名空间
|
||
using JinianNet.JNTemplate;
|
||
using MDM.Model.Process;
|
||
using MDM.Services.Plant;
|
||
using Microsoft.Extensions.Options;
|
||
using NPOI.SS.Formula.Functions;
|
||
using Quartz;
|
||
using RIZO.Admin.WebApi.PLC.Model;
|
||
using RIZO.Common;
|
||
using RIZO.Model.MES.product_trace;
|
||
using S7.Net;
|
||
using SqlSugar;
|
||
using SqlSugar.IOC;
|
||
using System;
|
||
using System.Collections;
|
||
using System.Collections.Concurrent;
|
||
using System.Collections.Generic;
|
||
using System.Linq;
|
||
using System.Text;
|
||
using System.Threading;
|
||
using System.Threading.Tasks;
|
||
|
||
namespace RIZO.Admin.WebApi.PLC.Service
|
||
{
|
||
/// <summary>
|
||
/// PLC通信服务(封装连接、读写、生产数据采集等功能)
|
||
/// </summary>
|
||
public class PlcService : IDisposable
|
||
{
|
||
#region 字段与构造函数
|
||
// 标记是否已释放资源,避免重复释放
|
||
private bool _disposed = false;
|
||
|
||
// PLC配置参数(从配置文件注入)
|
||
private readonly List<PlcConfig> _plcConfigs;
|
||
private readonly GlobalPlcConfig _globalConfig;
|
||
|
||
private PlcProductionDataService _plcProductionDataService = new PlcProductionDataService();
|
||
private PlantWorkstationService _plantWorkstationService = new PlantWorkstationService();
|
||
private PlcOperationResultService _plcOperationResultService = new PlcOperationResultService();
|
||
|
||
// 先在类中添加2个核心优化字段(支撑高频访问)
|
||
private readonly SemaphoreSlim _concurrencySemaphore = new SemaphoreSlim(15, 50); // 限制20并发(适配50台PLC)
|
||
private readonly ConcurrentDictionary<string, (Plc Client, DateTime LastUsedTime)> _plcConnPool = new(); // 连接池
|
||
#region PLC地址块儿映射
|
||
//MES返回PLC请求映射
|
||
private readonly Dictionary<string, string> _mesIntReturnMap = new()
|
||
{
|
||
{ "设备使能", "DB1000.DBW0" }, // Int
|
||
{ "工位开始查询结果", "DB1000.DBW2000" }, // Int
|
||
{ "保存结果", "DB1000.DBW2002" }, // Int
|
||
};
|
||
|
||
private readonly Dictionary<string, (string Addr, int Len)> _mesStringReturnMap = new()
|
||
{
|
||
{ "产品型号", ("DB1000.DBB1016", 14) }, // String[14]
|
||
{ "订单下发", ("DB1000.DBB1032", 50) }, // String[50]
|
||
};
|
||
|
||
private readonly Dictionary<string, string> _mesop058IntReturnMap = new()
|
||
{
|
||
{ "设备使能", "DB300.DBW0" }, // Int
|
||
{ "工位开始查询结果", "DB300.DBW2000" }, // Int
|
||
{ "保存结果", "DB300.DBW2002" }, // Int
|
||
};
|
||
|
||
private readonly Dictionary<string, (string Addr, int Len)> _mesop058StringReturnMap = new()
|
||
{
|
||
{ "产品型号", ("DB300.DBB1016", 14) }, // String[14]
|
||
{ "订单下发", ("DB300.DBB1032", 50) }, // String[50]
|
||
};
|
||
|
||
// OP020-2 专属地址映射(合盖工位,DB1001)
|
||
private readonly Dictionary<string, (string Addr, int Len)> _op020_2StringMap = new()
|
||
{
|
||
{ "报警信息", ("DB1001.DBB58", 48) }, // Array[1..48] of Byte
|
||
{ "产品型号", ("DB1001.DBB1000", 48) }, // String[48]
|
||
{ "产品名称", ("DB1001.DBB1054", 48) }, // String[48]
|
||
{ "SN", ("DB1001.DBB2106", 48) } // String[48]
|
||
};
|
||
|
||
private readonly Dictionary<string, string> _op020_2IntMap = 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=返工模式;8=换型模式;16=预热模式
|
||
|
||
//{ "实际产量", "DB1001.DBD1104" }, // DInt
|
||
//{ "合格数量", "DB1001.DBD1108" }, // DInt
|
||
//{ "失败数量", "DB1001.DBD1112" }, // DInt
|
||
|
||
{ "上传请求", "DB1001.DBW2002" }, // Int - 1=请求开始,0=无请求
|
||
{ "托盘号", "DB1001.DBW2054" }, // Int
|
||
{ "总结果", "DB1001.DBW2158" }, // Int - 一个托盘上传四次结果
|
||
{ "节拍时间", "DB1001.DBD2988" } // Real
|
||
};
|
||
|
||
// OP020-3 专属地址映射(热铆工位,DB1001)
|
||
private readonly Dictionary<string, (string Addr, int Len)> _op020_3StringMap = new()
|
||
{
|
||
{ "报警信息", ("DB1001.DBB88", 48) }, // Array[1..48] of Byte
|
||
{ "产品型号", ("DB1001.DBB4060", 48) }, // String[48]
|
||
{ "产品1SN", ("DB1001.DBB2300", 48) }, // String[48]
|
||
{ "产品2SN", ("DB1001.DBB2350", 48) }, // String[48]
|
||
{ "产品3SN", ("DB1001.DBB2400", 48) }, // String[48]
|
||
{ "产品4SN", ("DB1001.DBB2450", 48) } // String[48]
|
||
};
|
||
|
||
private readonly Dictionary<string, string> _op020_3IntMap = 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=返工模式;8=换型模式;16=预热模式
|
||
|
||
// 上传请求
|
||
{ "查询请求", "DB1001.DBW2000" }, // Int - 1:请求开始,0:无请求
|
||
{ "上传请求", "DB1001.DBW2002" }, // Int - 1:请求开始,0:无请求
|
||
|
||
// 节拍时间
|
||
{ "节拍时间", "DB1001.DBD2988" }, // Real
|
||
|
||
// 产品结果与产量
|
||
{ "合格数量", "DB1001.DBD4110" }, // DInt
|
||
{ "失败数量", "DB1001.DBD4114" }, // DInt
|
||
{ "实际数量", "DB1001.DBD4118" } // DInt
|
||
};
|
||
|
||
// OP020-4 专属地址映射(pin压合&视觉检查工位,DB1001)
|
||
private readonly Dictionary<string, (string Addr, int Len)> _op020_4StringMap = new()
|
||
{
|
||
{ "报警信息", ("DB1001.DBB58", 48) }, // Array[1..48] of Byte
|
||
{ "产品型号", ("DB1001.DBB1000", 48) }, // String[48]
|
||
{ "产品名称", ("DB1001.DBB1054", 48) }, // String[48]
|
||
{ "产品1SN", ("DB1001.DBB2230", 40) }, // String[40]
|
||
{ "产品2SN", ("DB1001.DBB2360", 40) }, // String[40]
|
||
{ "产品3SN", ("DB1001.DBB2490", 40) }, // String[40]
|
||
{ "产品4SN", ("DB1001.DBB2620", 40) } // String[40]
|
||
};
|
||
|
||
private readonly Dictionary<string, string> _op020_4IntMap = 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=返工模式;8=换型模式;16=预热模式
|
||
|
||
// 上传请求
|
||
{ "产品1上传请求", "DB1001.DBW2002" }, // Int
|
||
{ "产品2上传请求", "DB1001.DBW2004" }, // Int
|
||
{ "产品3上传请求", "DB1001.DBW2006" }, // Int
|
||
{ "产品4上传请求", "DB1001.DBW2008" }, // Int
|
||
|
||
// 托盘号
|
||
{ "托盘号", "DB1001.DBW2070" }, // Int
|
||
|
||
// 产品结果
|
||
{ "产品1结果", "DB1001.DBW2274" }, // Int - 1:OK 2:NG
|
||
{ "产品2结果", "DB1001.DBW2404" }, // Int - 1:OK 2:NG
|
||
{ "产品3结果", "DB1001.DBW2534" }, // Int - 1:OK 2:NG
|
||
{ "产品4结果", "DB1001.DBW2664" }, // Int - 1:OK 2:NG
|
||
|
||
};
|
||
|
||
// OP050 专属地址映射(点白胶DC744工位,DB300)
|
||
private readonly Dictionary<string, (string Addr, int Len)> _op050StringMap = new()
|
||
{
|
||
{ "报警信息", ("DB300.DBB680", 48) }, // Array[1..48] of Byte
|
||
{ "产品型号", ("DB300.DBB728", 48) }, // String[48]
|
||
{ "产品名称", ("DB300.DBB778", 48) }, // String[48]
|
||
{ "产品1SN", ("DB300.DBB50", 40) }, // String[40]
|
||
{ "产品2SN", ("DB300.DBB206", 40) }, // String[40]
|
||
{ "产品3SN", ("DB300.DBB362", 40) }, // String[40]
|
||
{ "产品4SN", ("DB300.DBB518", 40) } // String[40]
|
||
};
|
||
|
||
private readonly Dictionary<string, string> _op050IntMap = new()
|
||
{
|
||
// 基础状态
|
||
{ "运行状态", "DB300.DBW670" }, // Int - 1=空闲,2=运行中,3=故障
|
||
{ "设备模式", "DB300.DBW672" }, // Int - 1=空模式;2=手动;4=初始化;8=自动;16=CycleStop
|
||
{ "设备在线状态", "DB300.DBW674" }, // Int - 1=离线,0=在线
|
||
{ "ByPass", "DB300.DBW676" }, // Int - 1=ByPass,0=正常模式
|
||
{ "生产模式", "DB300.DBW678" }, // Int - 1=正常模式;2=清线模式;4=返工模式;8=换型模式;16=预热模式
|
||
|
||
// 上传与查询请求
|
||
{ "上传请求", "DB300.DBW42" }, // Int
|
||
{ "查询请求", "DB300.DBW44" }, // Int - 非0是查询(%X1满足条码1查询……%X2满足条码2查询)
|
||
|
||
// 产量数据
|
||
{ "实际产量", "DB300.DBD828" }, // DInt
|
||
{ "合格数量", "DB300.DBD832" }, // DInt
|
||
{ "失败数量", "DB300.DBD836" }, // DInt
|
||
|
||
// 节拍时间
|
||
{ "节拍时间", "DB300.DBD840" }, // Real
|
||
|
||
// 托盘号
|
||
{ "托盘号", "DB300.DBW844" }, // Int
|
||
};
|
||
|
||
// OP050-1 专属地址映射(自动下料工位,DB1001)
|
||
private readonly Dictionary<string, (string Addr, int Len)> _op050_1StringMap = new()
|
||
{
|
||
{ "报警信息", ("DB1001.DBB58", 48) }, // Array[1..48] of Byte
|
||
{ "产品型号", ("DB1001.DBB1000", 48) }, // String[48]
|
||
{ "产品名称", ("DB1001.DBB1054", 48) } // String[48]
|
||
};
|
||
|
||
private readonly Dictionary<string, string> _op050_1IntMap = 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=返工模式;8=换型模式;16=预热模式
|
||
|
||
// 产量数据
|
||
{ "实际产量", "DB1001.DBD1104" }, // DInt
|
||
{ "合格数量", "DB1001.DBD1108" }, // DInt
|
||
{ "失败数量", "DB1001.DBD1112" }, // DInt
|
||
|
||
// 节拍时间
|
||
{ "节拍时间", "DB1001.DBD5086" }, // Real
|
||
};
|
||
|
||
// OP051扫码 专属地址映射(Coating扫码工位,假设使用 DB1002)//待PLC给出
|
||
private readonly Dictionary<string, (string Addr, int Len)> _op051ScanStringMap = new()
|
||
{
|
||
{ "产品型号", ("DB1002.DBB1000", 48) }, // String[48]
|
||
{ "产品名称", ("DB1002.DBB1054", 48) }, // String[48]
|
||
{ "SN_1", ("DB1002.DBB50", 40) }, // String[40]
|
||
{ "SN_2", ("DB1002.DBB206", 40) }, // String[40]
|
||
{ "SN_3", ("DB1002.DBB362", 40) } // String[40]
|
||
};
|
||
|
||
private readonly Dictionary<string, string> _op051ScanIntMap = new()
|
||
{
|
||
// 基础状态
|
||
{ "运行状态", "DB1002.DBW0" }, // Int - 1=空闲,2=运行中,3=故障
|
||
{ "设备模式", "DB1002.DBW2" }, // Int - 1=空模式;2=手动;4=初始化;8=自动;16=CycleStop
|
||
{ "设备在线状态", "DB1002.DBW4" }, // Int - 1=离线,0=在线
|
||
{ "生产模式", "DB1002.DBW8" }, // Int - 1=正常模式;2=清线模式;4=返工模式;8=换型模式;16=预热模式
|
||
|
||
// 操作请求
|
||
{ "查询请求", "DB1002.DBW44" }, // Int - 非0为查询
|
||
{ "上传结果请求", "DB1002.DBW46" }, // Int - 非0为上传
|
||
|
||
// 托盘号
|
||
{ "托盘号", "DB1002.DBW844" } // Int
|
||
};
|
||
|
||
// OP057 专属地址映射(压装定位销&激光打标工位,DB1001)
|
||
private readonly Dictionary<string, (string Addr, int Len)> _op057StringMap = new()
|
||
{
|
||
{ "报警信息", ("DB1001.DBB58", 48) }, // Array[1..48] of Byte
|
||
{ "产品型号", ("DB1001.DBB1000", 48) }, // String[48]
|
||
{ "产品名称", ("DB1001.DBB1054", 48) }, // String[48]
|
||
{ "SN_1", ("DB1001.DBB2100", 40) }, // String[40]
|
||
{ "SN_2", ("DB1001.DBB2904", 40) } // String[40]
|
||
};
|
||
|
||
private readonly Dictionary<string, string> _op057IntMap = 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=正常
|
||
|
||
// 产量数据
|
||
{ "实际产量", "DB1001.DBD1104" }, // DInt
|
||
{ "合格数量", "DB1001.DBD1108" }, // DInt
|
||
{ "失败数量", "DB1001.DBD1112" }, // DInt
|
||
|
||
// 操作请求
|
||
{ "工位开始查询请求", "DB1001.DBW2000" }, // Int - 1=请求开始,0=无请求
|
||
{ "1#产品结果保存请求", "DB1001.DBW2002" }, // Int - 1=请求,0=无请求
|
||
|
||
// 产品结果
|
||
{ "产品总结果", "DB1001.DBW2948" }, // Int - 1=OK,2=NG
|
||
{ "1#销结果", "DB1001.DBW2950" }, // Int - 1=OK
|
||
{ "2#销结果", "DB1001.DBW2964" }, // Int - 1=OK
|
||
|
||
// 工艺参数(Real)
|
||
{ "1#销压力", "DB1001.DBD2952" }, // Real
|
||
{ "1#销行程", "DB1001.DBD2956" }, // Real
|
||
{ "2#销压力", "DB1001.DBD2966" }, // Real
|
||
{ "2#销行程", "DB1001.DBD2970" }, // Real
|
||
{ "节拍时间", "DB1001.DBD4662" }, // Real
|
||
|
||
};
|
||
|
||
// OP058 专属地址映射(压装定位销&激光打标工位,DB1004)
|
||
private readonly Dictionary<string, (string Addr, int Len)> _op058StringMap = new()
|
||
{
|
||
{ "产品型号", ("DB1004.DBB1000", 48) }, // String[48]
|
||
{ "产品名称", ("DB1004.DBB1054", 48) }, // String[48]
|
||
{ "SN_1", ("DB1004.DBB2234", 40) } // String[40] - 条码(查询、上传)
|
||
};
|
||
|
||
private readonly Dictionary<string, string> _op058IntMap = new()
|
||
{
|
||
// 产量数据
|
||
{ "实际产量", "DB1004.DBD1104" }, // DInt
|
||
{ "合格数量", "DB1004.DBD1108" }, // DInt
|
||
{ "失败数量", "DB1004.DBD1112" }, // DInt
|
||
|
||
// 产品结果
|
||
{ "产品总结果", "DB1004.DBW2278" }, // Int - 1=OK,2=NG
|
||
{ "结果", "DB1004.DBW2280" }, // Int - 1=OK
|
||
|
||
// 工艺参数(Real)
|
||
{ "压力", "DB1004.DBD2282" }, // Real
|
||
{ "行程", "DB1004.DBD2286" }, // Real
|
||
{ "节拍时间", "DB1004.DBD2310" }, // Real
|
||
};
|
||
|
||
// OP060 专属地址映射(高低压接头拧紧工位,DB1001)
|
||
private readonly Dictionary<string, (string Addr, int Len)> _op060StringMap = new()
|
||
{
|
||
{ "报警信息", ("DB1001.DBB58", 48) }, // Array[1..48] of Byte
|
||
{ "产品型号", ("DB1001.DBB1000", 48) }, // String[48]
|
||
{ "产品名称", ("DB1001.DBB1054", 48) }, // String[48]
|
||
{ "SN_1", ("DB1001.DBB2100", 40) }, // String[40] - 条码查询
|
||
{ "SN_2", ("DB1001.DBB2274", 40) } // String[40] - 结果上传条码
|
||
};
|
||
|
||
private readonly Dictionary<string, string> _op060IntMap = 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=正常
|
||
|
||
// 产量数据
|
||
{ "实际产量", "DB1001.DBD1104" }, // DInt
|
||
{ "合格数量", "DB1001.DBD1108" }, // DInt
|
||
{ "失败数量", "DB1001.DBD1112" }, // DInt
|
||
|
||
// 操作请求
|
||
{ "工位开始查询请求", "DB1001.DBW2000" }, // Int - 1=请求开始,0=无请求
|
||
{ "1#产品结果保存请求", "DB1001.DBW2002" }, // Int - 1=请求,0=无请求
|
||
|
||
// 产品结果
|
||
{ "产品总结果", "DB1001.DBW2318" }, // Int - 1=OK,2=NG
|
||
{ "1#螺丝结果", "DB1001.DBW2320" }, // Int
|
||
{ "2#螺丝结果", "DB1001.DBW2338" }, // Int
|
||
{ "3#螺丝结果", "DB1001.DBW2356" }, // Int
|
||
{ "4#螺丝结果", "DB1001.DBW2374" }, // Int
|
||
|
||
// 工艺参数(Real)
|
||
{ "1#螺丝扭矩", "DB1001.DBD2322" }, // Real
|
||
{ "1#螺丝深度", "DB1001.DBD2326" }, // Real
|
||
{ "1#螺丝角度", "DB1001.DBD2330" }, // Real
|
||
{ "1#螺丝时间", "DB1001.DBD2334" }, // Real
|
||
{ "2#螺丝扭矩", "DB1001.DBD2340" }, // Real
|
||
{ "2#螺丝深度", "DB1001.DBD2344" }, // Real
|
||
{ "2#螺丝角度", "DB1001.DBD2348" }, // Real
|
||
{ "2#螺丝时间", "DB1001.DBD2352" }, // Real
|
||
{ "3#螺丝扭矩", "DB1001.DBD2358" }, // Real
|
||
{ "3#螺丝深度", "DB1001.DBD2362" }, // Real
|
||
{ "3#螺丝角度", "DB1001.DBD2366" }, // Real
|
||
{ "3#螺丝时间", "DB1001.DBD2370" }, // Real
|
||
{ "4#螺丝扭矩", "DB1001.DBD2376" }, // Real
|
||
{ "4#螺丝深度", "DB1001.DBD2380" }, // Real
|
||
{ "4#螺丝角度", "DB1001.DBD2384" }, // Real
|
||
{ "4#螺丝时间", "DB1001.DBD2388" }, // Real
|
||
{ "节拍时间", "DB1001.DBD3030" }, // Real
|
||
};
|
||
|
||
// OP070-1 专属地址映射 (点散热胶GF1500工位) OP070-2 点散热胶TC4060 OP070-3 点散热胶GF3500
|
||
private readonly Dictionary<string, (string Addr, int Len)> _op070_1StringMap = new()
|
||
{
|
||
//{ "报警信息", ("DB1001.DBB58", 48) }, // Array[1..48] of Byte
|
||
{ "订单名称", ("DB1001.DBB1000", 48) }, // String[48]
|
||
{ "产品名称", ("DB1001.DBB1054", 48) }, // String[48]
|
||
{ "产品型号", ("DB1001.DBB2006", 28) }, // String[28]
|
||
{ "SN_1", ("DB1001.DBB2100", 28) }, // String[28]
|
||
{ "SN_2", ("DB1001.DBB2134", 28) } // String[28]
|
||
};
|
||
|
||
private readonly Dictionary<string, string> _op070_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.DBW2002" }, // Int
|
||
{ "托盘号", "DB1001.DBW2004" }, // Int
|
||
{ "相机结果", "DB1001.DBW2164" }, // Int
|
||
{ "站位结果", "DB1001.DBW2166" }, // Int
|
||
{ "节拍时间", "DB1001.DBD2168" }, // Real
|
||
};
|
||
|
||
// OP075 专属地址映射 PWM折弯&装配
|
||
private readonly Dictionary<string, (string Addr, int Len)> _op075StringMap = new()
|
||
{
|
||
//{ "报警信息", ("DB1001.DBB58", 48) }, // Array[1..48] of Byte
|
||
{ "订单名称", ("DB1001.DBB1000", 48) }, // String[48]
|
||
{ "产品名称", ("DB1001.DBB1054", 48) }, // String[48]
|
||
{ "产品型号", ("DB1001.DBB2006", 28) }, // String[28]
|
||
{ "SN_1", ("DB1001.DBB2100", 28) }, // String[28]
|
||
{ "SN_2", ("DB1001.DBB2192", 28) }, // String[28]
|
||
{ "芯片SN", ("DB1001.DBB2222", 28) } // String[28]
|
||
};
|
||
|
||
private readonly Dictionary<string, string> _op075IntMap = 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.DBW2002" }, // Int
|
||
{ "托盘号", "DB1001.DBW2004" }, // Int
|
||
{ "站位结果", "DB1001.DBW2252" }, // Int
|
||
{ "节拍时间", "DB1001.DBD2284" } // Real
|
||
};
|
||
|
||
// OP080-1 专属地址映射 PCBA组装&拧紧
|
||
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.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
|
||
};
|
||
|
||
// OP080-2 专属地址映射(PCBA拧紧工位,DB1001)
|
||
private readonly Dictionary<string, (string Addr, int Len)> _op080_2StringMap = new()
|
||
{
|
||
// { "报警信息", ("DB1001.DBB58", 48) }, // Array[1..48] of Byte
|
||
{ "产品型号", ("DB1001.DBB1000", 48) }, // String[48]
|
||
{ "产品名称", ("DB1001.DBB1054", 48) }, // String[48]
|
||
{ "SN1", ("DB1001.DBB2100", 28) }, // String[28] - 条码查询
|
||
{ "SN2", ("DB1001.DBB2260", 28) }, // String[28] - 结果上传条码
|
||
};
|
||
|
||
private readonly Dictionary<string, string> _op080_2IntMap = 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=正常
|
||
|
||
{ "实际产量", "DB1001.DBD1104" }, // DInt
|
||
{ "合格数量", "DB1001.DBD1108" }, // DInt
|
||
{ "失败数量", "DB1001.DBD1112" }, // DInt
|
||
|
||
// 请求信号
|
||
{ "查询请求", "DB1001.DBW2000" }, // Int - 1=请求开始,0=无请求
|
||
{ "保存请求", "DB1001.DBW2002" }, // Int - 1=请求,0=无请求
|
||
|
||
// 托盘号
|
||
{ "托盘号", "DB1001.DBW2290" }, // Int
|
||
|
||
// 产品总结果
|
||
{ "产品总结果", "DB1001.DBW2292" }, // Int
|
||
|
||
// 1号螺钉
|
||
{ "1号螺钉_结果", "DB1001.DBW2294" }, // Int
|
||
{ "1号螺钉_扭矩", "DB1001.DBD2296" }, // Real
|
||
{ "1号螺钉_深度", "DB1001.DBD2300" }, // Real
|
||
{ "1号螺钉_角度", "DB1001.DBD2304" }, // Real
|
||
{ "1号螺钉_拧紧时间", "DB1001.DBD2308" }, // Real
|
||
|
||
// 2号螺钉
|
||
{ "2号螺钉_结果", "DB1001.DBW2312" }, // Int
|
||
{ "2号螺钉_扭矩", "DB1001.DBD2314" }, // Real
|
||
{ "2号螺钉_深度", "DB1001.DBD2318" }, // Real
|
||
{ "2号螺钉_角度", "DB1001.DBD2322" }, // Real
|
||
{ "2号螺钉_拧紧时间", "DB1001.DBD2326" }, // Real
|
||
|
||
// 3号螺钉
|
||
{ "3号螺钉_结果", "DB1001.DBW2330" }, // Int
|
||
{ "3号螺钉_扭矩", "DB1001.DBD2332" }, // Real
|
||
{ "3号螺钉_深度", "DB1001.DBD2336" }, // Real
|
||
{ "3号螺钉_角度", "DB1001.DBD2340" }, // Real
|
||
{ "3号螺钉_拧紧时间", "DB1001.DBD2344" }, // Real
|
||
|
||
// 4号螺钉
|
||
{ "4号螺钉_结果", "DB1001.DBW2348" }, // Int
|
||
{ "4号螺钉_扭矩", "DB1001.DBD2350" }, // Real
|
||
{ "4号螺钉_深度", "DB1001.DBD2354" }, // Real
|
||
{ "4号螺钉_角度", "DB1001.DBD2358" }, // Real
|
||
{ "4号螺钉_拧紧时间", "DB1001.DBD2362" }, // Real
|
||
|
||
// 5号螺钉
|
||
{ "5号螺钉_结果", "DB1001.DBW2366" }, // Int
|
||
{ "5号螺钉_扭矩", "DB1001.DBD2368" }, // Real
|
||
{ "5号螺钉_深度", "DB1001.DBD2372" }, // Real
|
||
{ "5号螺钉_角度", "DB1001.DBD2376" }, // Real
|
||
{ "5号螺钉_拧紧时间", "DB1001.DBD2380" }, // Real
|
||
|
||
// 6号螺钉
|
||
{ "6号螺钉_结果", "DB1001.DBW2384" }, // Int
|
||
{ "6号螺钉_扭矩", "DB1001.DBD2386" }, // Real
|
||
{ "6号螺钉_深度", "DB1001.DBD2390" }, // Real
|
||
{ "6号螺钉_角度", "DB1001.DBD2394" }, // Real
|
||
{ "6号螺钉_拧紧时间", "DB1001.DBD2398" }, // Real
|
||
|
||
// 7号螺钉
|
||
{ "7号螺钉_结果", "DB1001.DBW2402" }, // Int
|
||
{ "7号螺钉_扭矩", "DB1001.DBD2404" }, // Real
|
||
{ "7号螺钉_深度", "DB1001.DBD2408" }, // Real
|
||
{ "7号螺钉_角度", "DB1001.DBD2412" }, // Real
|
||
{ "7号螺钉_拧紧时间", "DB1001.DBD2416" }, // Real
|
||
|
||
{ "节拍时间", "DB1001.DBD3004" }, // Real
|
||
};
|
||
|
||
// OP080-3 专属地址映射(PWM压装&CUP上料&拧紧工位,DB1001)
|
||
private readonly Dictionary<string, (string Addr, int Len)> _op080_3StringMap = new()
|
||
{
|
||
{ "报警信息", ("DB1001.DBB58", 48) }, // Array[1..48] of Byte
|
||
{ "产品型号", ("DB1001.DBB1000", 48) }, // String[48]
|
||
{ "产品名称", ("DB1001.DBB1054", 48) }, // String[48]
|
||
{ "SN_1", ("DB1001.DBB2100", 40) }, // String[40] - 条码查询
|
||
{ "SN_2", ("DB1001.DBB2200", 40) } // String[40] - 结果上传条码
|
||
};
|
||
|
||
private readonly Dictionary<string, string> _op080_3IntMap = 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=正常
|
||
|
||
// 产量数据
|
||
{ "实际产量", "DB1001.DBD1104" }, // DInt
|
||
{ "合格数量", "DB1001.DBD1108" }, // DInt
|
||
{ "失败数量", "DB1001.DBD1112" }, // DInt
|
||
|
||
// 操作请求
|
||
{ "查询请求", "DB1001.DBW2000" }, // Int - 1=请求开始,0=无请求
|
||
{ "保存请求", "DB1001.DBW2002" }, // Int - 1=请求,0=无请求
|
||
|
||
// 产品结果
|
||
{ "产品总结果", "DB1001.DBW2244" }, // Int
|
||
{ "压机结果", "DB1001.DBW2246" }, // Int
|
||
{ "左螺丝枪结果", "DB1001.DBW2264" }, // Int
|
||
{ "右螺丝枪结果", "DB1001.DBW2282" }, // Int
|
||
|
||
// 工艺参数(Real)
|
||
{ "压机左压力值", "DB1001.DBD2248" }, // Real
|
||
{ "压机右压力值", "DB1001.DBD2252" }, // Real
|
||
{ "压机行程值", "DB1001.DBD2256" }, // Real
|
||
{ "保压时间", "DB1001.DBD2260" }, // Real
|
||
{ "左螺丝枪扭矩", "DB1001.DBD2266" }, // Real
|
||
{ "左螺丝枪角度", "DB1001.DBD2270" }, // Real
|
||
{ "左螺丝枪深度", "DB1001.DBD2274" }, // Real
|
||
{ "左螺丝枪时间", "DB1001.DBD2278" }, // Real
|
||
{ "右螺丝枪扭矩", "DB1001.DBD2284" }, // Real
|
||
{ "右螺丝枪角度", "DB1001.DBD2288" }, // Real
|
||
{ "右螺丝枪深度", "DB1001.DBD2292" }, // Real
|
||
{ "右螺丝枪时间", "DB1001.DBD2296" }, // Real
|
||
{ "节拍时间", "DB1001.DBD2450" }, // Real
|
||
|
||
// 托盘号
|
||
{ "托盘号", "DB1001.DBW2142" } // Int
|
||
};
|
||
|
||
// OP085 专属地址映射(自动上料到波峰焊工位,DB1001)
|
||
private readonly Dictionary<string, (string Addr, int Len)> _op085StringMap = new()
|
||
{
|
||
{ "报警信息", ("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<string, string> _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=正常
|
||
|
||
// 产量数据
|
||
{ "实际产量", "DB1001.DBD1104" }, // DInt
|
||
{ "合格数量", "DB1001.DBD1108" }, // DInt
|
||
{ "失败数量", "DB1001.DBD1112" }, // DInt
|
||
|
||
// 操作请求
|
||
{ "保存请求", "DB1001.DBW2002" }, // Int - 1=请求开始,0=无请求
|
||
{ "线体托盘号", "DB1001.DBW2070" }, // Int
|
||
|
||
// 工艺参数
|
||
{ "节拍时间", "DB1001.DBD2242" } // Real
|
||
};
|
||
|
||
// OP100 专属地址映射(手动上料壳体&盖板工位,DB1001)
|
||
private readonly Dictionary<string, (string Addr, int Len)> _op100StringMap = 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]
|
||
};
|
||
|
||
private readonly Dictionary<string, string> _op100IntMap = 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=正常
|
||
|
||
// 产量数据
|
||
{ "实际产量", "DB1001.DBD1104" }, // DInt
|
||
{ "合格数量", "DB1001.DBD1108" }, // DInt
|
||
{ "失败数量", "DB1001.DBD1112" }, // DInt
|
||
|
||
// 操作请求
|
||
{ "工位开始查询请求", "DB1001.DBW2000" }, // Int - 1=请求开始,0=无请求
|
||
{ "保存请求", "DB1001.DBW2002" }, // Int - 1=请求开始,0=无请求
|
||
|
||
// 托盘号
|
||
{ "托盘号", "DB1001.DBW2070" }, // Int
|
||
|
||
// 工艺参数
|
||
{ "节拍时间", "DB1001.DBD2958" } // Real
|
||
};
|
||
// OP1011 专属地址映射(HV连接器装配工位,DB1001)
|
||
private readonly Dictionary<string, (string Addr, int Len)> _op1011StringMap = new()
|
||
{
|
||
{ "报警信息", ("DB1001.DBB58", 48) }, // Array[1..48] of Byte
|
||
{ "产品型号", ("DB1001.DBB1000", 48) }, // String[48]
|
||
{ "产品名称", ("DB1001.DBB1054", 48) }, // String[48]
|
||
{ "查询SN", ("DB1001.DBB2100", 40) }, // String[40]
|
||
{ "上传结果SN", ("DB1001.DBB2364", 40) } // String[40]
|
||
};
|
||
|
||
private readonly Dictionary<string, string> _op1011IntMap = 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=正常
|
||
{ "螺钉枪程序号", "DB1001.DBW998" }, // Int - 给MES提供螺钉枪程序号控制切换
|
||
|
||
// 产量数据
|
||
{ "实际产量", "DB1001.DBD1104" }, // DInt
|
||
{ "合格数量", "DB1001.DBD1108" }, // DInt
|
||
{ "失败数量", "DB1001.DBD1112" }, // DInt
|
||
|
||
// 操作请求
|
||
{ "开始查询请求", "DB1001.DBW2000" }, // Int - 1=请求开始,0=无请求
|
||
{ "结果保存请求", "DB1001.DBW2002" }, // Int - 1=请求开始,0=无请求
|
||
{ "请求设备MES传送螺丝", "DB1001.DBW2004" }, // Int
|
||
|
||
// 产品结果
|
||
{ "产品总结果", "DB1001.DBW2364" }, // Int
|
||
{ "1#结果", "DB1001.DBW2464" }, // Int - 0=无结果,1=OK,2=NG
|
||
{ "2#结果", "DB1001.DBW2466" }, // Int - 0=无结果,1=OK,2=NG
|
||
{ "3#结果", "DB1001.DBW2468" }, // Int - 0=无结果,1=OK,2=NG
|
||
{ "4#结果", "DB1001.DBW2470" }, // Int - 0=无结果,1=OK,2=NG
|
||
|
||
// 工艺参数(Real)
|
||
{ "1#扭矩", "DB1001.DBD2384" }, // Real
|
||
{ "2#扭矩", "DB1001.DBD2388" }, // Real
|
||
{ "3#扭矩", "DB1001.DBD2392" }, // Real
|
||
{ "4#扭矩", "DB1001.DBD2396" }, // Real
|
||
{ "1#转角", "DB1001.DBD2424" }, // Real
|
||
{ "2#转角", "DB1001.DBD2428" }, // Real
|
||
{ "3#转角", "DB1001.DBD2432" }, // Real
|
||
{ "4#转角", "DB1001.DBD2436" }, // Real
|
||
{ "1#深度", "DB1001.DBD2484" }, // Real
|
||
{ "2#深度", "DB1001.DBD2488" }, // Real
|
||
{ "3#深度", "DB1001.DBD2492" }, // Real
|
||
{ "4#深度", "DB1001.DBD2496" }, // Real
|
||
{ "节拍时间", "DB1001.DBD2690" } // Real
|
||
};
|
||
// OP1012 专属地址映射(Ring terminal装配工位,DB1001)
|
||
private readonly Dictionary<string, (string Addr, int Len)> _op1012StringMap = new()
|
||
{
|
||
{ "报警信息", ("DB1001.DBB58", 48) }, // Array[1..48] of Byte
|
||
{ "产品型号", ("DB1001.DBB1000", 48) }, // String[48]
|
||
{ "产品名称", ("DB1001.DBB1054", 48) }, // String[48]
|
||
{ "查询SN", ("DB1001.DBB2100", 40) }, // String[40]
|
||
{ "上传SN", ("DB1001.DBB2320", 40) } // String[40]
|
||
};
|
||
|
||
private readonly Dictionary<string, string> _op1012IntMap = 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=正常
|
||
{ "螺钉枪程序号", "DB1001.DBW998" }, // Int
|
||
|
||
// 产量数据
|
||
{ "实际产量", "DB1001.DBD1104" }, // DInt
|
||
{ "合格数量", "DB1001.DBD1108" }, // DInt
|
||
{ "失败数量", "DB1001.DBD1112" }, // DInt
|
||
|
||
// 操作请求
|
||
{ "查询请求", "DB1001.DBW2000" }, // Int - 1=请求开始,0=无请求
|
||
{ "结果保存请求", "DB1001.DBW2002" }, // Int - 1=请求开始,0=无请求
|
||
{ "请求传送打螺丝结果", "DB1001.DBW2001" }, // Int
|
||
|
||
// 产品结果
|
||
{ "产品总结果", "DB1001.DBW2364" }, // Int - 1=OK,2=NG
|
||
{ "1#结果", "DB1001.DBW2464" }, // Int - 1=OK,2=NG
|
||
{ "2#结果", "DB1001.DBW2466" }, // Int - 1=OK,2=NG
|
||
|
||
// 工艺参数(Real)
|
||
{ "1#扭矩", "DB1001.DBD2384" }, // Real
|
||
{ "2#扭矩", "DB1001.DBD2388" }, // Real
|
||
{ "1#转角", "DB1001.DBD2424" }, // Real
|
||
{ "2#转角", "DB1001.DBD2428" }, // Real
|
||
{ "1#深度", "DB1001.DBD2484" }, // Real
|
||
{ "2#深度", "DB1001.DBD2488" }, // Real
|
||
{ "节拍时间", "DB1001.DBD2690" } // Real
|
||
};
|
||
|
||
// OP102 专属地址映射(等离子处理工位,DB1001)
|
||
private readonly Dictionary<string, (string Addr, int Len)> _op102StringMap = 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]
|
||
{ "上传结果SN", ("DB1001.DBB2134", 28) } // String[28]
|
||
};
|
||
|
||
private readonly Dictionary<string, string> _op102IntMap = 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=正常
|
||
|
||
// 产量数据
|
||
{ "实际产量", "DB1001.DBD1104" }, // DInt
|
||
{ "合格数量", "DB1001.DBD1108" }, // DInt
|
||
{ "失败数量", "DB1001.DBD1112" }, // DInt
|
||
|
||
// 操作请求
|
||
{ "工位开始查询请求", "DB1001.DBW2000" }, // Int - 1=请求开始,0=无请求
|
||
{ "产品保存请求", "DB1001.DBW2002" }, // Int - 1=请求开始,0=无请求
|
||
|
||
// 托盘与站位
|
||
{ "托盘号", "DB1001.DBW2104" }, // Int
|
||
{ "站位结果", "DB1001.DBW2166" }, // Int
|
||
|
||
// 工艺参数
|
||
{ "节拍时间", "DB1001.DBD2168" } // Real
|
||
};
|
||
|
||
// OP110-1 专属地址映射(点胶封胶Q3-3636工位,DB1001)
|
||
private readonly Dictionary<string, (string Addr, int Len)> _op110_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]
|
||
{ "上传结果SN", ("DB1001.DBB2134", 28) } // String[28]
|
||
};
|
||
|
||
private readonly Dictionary<string, string> _op110_1IntMap = 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=正常
|
||
|
||
// 产量数据
|
||
{ "实际产量", "DB1001.DBD1104" }, // DInt
|
||
{ "合格数量", "DB1001.DBD1108" }, // DInt
|
||
{ "失败数量", "DB1001.DBD1112" }, // DInt
|
||
|
||
// 操作请求
|
||
{ "工位开始查询请求", "DB1001.DBW2000" }, // Int - 1=请求开始,0=无请求
|
||
{ "产品保存请求", "DB1001.DBW2002" }, // Int - 1=请求开始,0=无请求
|
||
|
||
// 结果与托盘
|
||
{ "托盘号", "DB1001.DBW2004" }, // Int
|
||
{ "相机结果", "DB1001.DBW2164" }, // Int
|
||
{ "站位结果", "DB1001.DBW2166" }, // Int
|
||
|
||
// 工艺参数
|
||
{ "节拍时间", "DB1001.DBD2168" } // Real
|
||
};
|
||
|
||
// OP110-2 专属地址映射(盖板组装&拧紧工位,DB1001)
|
||
private readonly Dictionary<string, (string Addr, int Len)> _op110_2StringMap = 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]
|
||
{ "拧紧工位上传结果SN", ("DB1001.DBB2616", 28) } // String[28]
|
||
};
|
||
|
||
private readonly Dictionary<string, string> _op110_2IntMap = 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=正常
|
||
|
||
// 产量数据
|
||
{ "实际产量", "DB1001.DBD1104" }, // DInt
|
||
{ "合格数量", "DB1001.DBD1108" }, // DInt
|
||
{ "失败数量", "DB1001.DBD1112" }, // DInt
|
||
|
||
// 操作请求
|
||
{ "工位开始查询请求", "DB1001.DBW2000" }, // Int - 1=请求开始,0=无请求
|
||
{ "产品保存请求", "DB1001.DBW2006" }, // Int - 1=请求开始,0=无请求
|
||
|
||
// 合装与托盘
|
||
{ "合装工位托盘号", "DB1001.DBW2130" }, // Int
|
||
{ "托盘号", "DB1001.DBW2646" }, // Int
|
||
{ "产品总结果", "DB1001.DBW2648" }, // Int
|
||
|
||
// 螺丝结果(3#-7#)
|
||
{ "3#螺丝结果", "DB1001.DBW2662" }, // Int
|
||
{ "5#螺丝结果", "DB1001.DBW2680" }, // Int
|
||
{ "6#螺丝结果", "DB1001.DBW2698" }, // Int
|
||
{ "1#螺丝结果", "DB1001.DBW2716" }, // Int
|
||
{ "2#螺丝结果", "DB1001.DBW2734" }, // Int
|
||
{ "4#螺丝结果", "DB1001.DBW2752" }, // Int
|
||
{ "7#螺丝结果", "DB1001.DBW2770" }, // Int
|
||
|
||
// 螺丝工艺参数(Real)
|
||
{ "3#扭矩", "DB1001.DBD2664" }, // Real
|
||
{ "3#深度", "DB1001.DBD2668" }, // Real
|
||
{ "3#角度", "DB1001.DBD2672" }, // Real
|
||
{ "3#拧紧时间", "DB1001.DBD2676" }, // Real
|
||
{ "5#扭矩", "DB1001.DBD2682" }, // Real
|
||
{ "5#深度", "DB1001.DBD2686" }, // Real
|
||
{ "5#角度", "DB1001.DBD2690" }, // Real
|
||
{ "5#拧紧时间", "DB1001.DBD2694" }, // Real
|
||
{ "6#扭矩", "DB1001.DBD2700" }, // Real
|
||
{ "6#深度", "DB1001.DBD2704" }, // Real
|
||
{ "6#角度", "DB1001.DBD2708" }, // Real
|
||
{ "6#拧紧时间", "DB1001.DBD2712" }, // Real
|
||
{ "1#扭矩", "DB1001.DBD2718" }, // Real
|
||
{ "1#深度", "DB1001.DBD2722" }, // Real
|
||
{ "1#角度", "DB1001.DBD2726" }, // Real
|
||
{ "1#拧紧时间", "DB1001.DBD2730" }, // Real
|
||
{ "2#扭矩", "DB1001.DBD2736" }, // Real
|
||
{ "2#深度", "DB1001.DBD2740" }, // Real
|
||
{ "2#角度", "DB1001.DBD2744" }, // Real
|
||
{ "2#拧紧时间", "DB1001.DBD2748" }, // Real
|
||
{ "4#扭矩", "DB1001.DBD2754" }, // Real
|
||
{ "4#深度", "DB1001.DBD2758" }, // Real
|
||
{ "4#角度", "DB1001.DBD2762" }, // Real
|
||
{ "4#拧紧时间", "DB1001.DBD2766" }, // Real
|
||
{ "7#扭矩", "DB1001.DBD2772" }, // Real
|
||
{ "7#深度", "DB1001.DBD2776" }, // Real
|
||
{ "7#角度", "DB1001.DBD2780" }, // Real
|
||
{ "7#拧紧时间", "DB1001.DBD2784" }, // Real
|
||
{ "节拍时间", "DB1001.DBD3518" } // Real
|
||
};
|
||
|
||
// OP110-3 专属地址映射(盖板拧紧工位,DB1001)
|
||
private readonly Dictionary<string, (string Addr, int Len)> _op110_3StringMap = 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]
|
||
{ "保存SN", ("DB1001.DBB2258", 28) } // String[28]
|
||
};
|
||
|
||
private readonly Dictionary<string, string> _op110_3IntMap = 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=正常
|
||
|
||
// 产量数据
|
||
{ "实际产量", "DB1001.DBD1104" }, // DInt
|
||
{ "合格数量", "DB1001.DBD1108" }, // DInt
|
||
{ "失败数量", "DB1001.DBD1112" }, // DInt
|
||
|
||
// 操作请求
|
||
{ "工位开始查询请求", "DB1001.DBW2000" }, // Int - 1=请求开始,0=无请求
|
||
{ "产品保存请求", "DB1001.DBW2002" }, // Int - 1=请求开始,0=无请求
|
||
|
||
// 产品结果
|
||
{ "产品总结果", "DB1001.DBW2288" }, // Int
|
||
{ "1#螺丝结果", "DB1001.DBW2290" }, // Int
|
||
{ "2#螺丝结果", "DB1001.DBW2308" }, // Int
|
||
{ "3#螺丝结果", "DB1001.DBW2326" }, // Int
|
||
{ "4#螺丝结果", "DB1001.DBW2344" }, // Int
|
||
{ "5#螺丝结果", "DB1001.DBW2362" }, // Int
|
||
{ "6#螺丝结果", "DB1001.DBW2380" }, // Int
|
||
{ "7#螺丝结果", "DB1001.DBW2398" }, // Int
|
||
|
||
// 螺丝工艺参数(Real)
|
||
{ "1#扭矩", "DB1001.DBD2292" }, // Real
|
||
{ "1#深度", "DB1001.DBD2296" }, // Real
|
||
{ "1#角度", "DB1001.DBD2300" }, // Real
|
||
{ "1#拧紧时间", "DB1001.DBD2304" }, // Real
|
||
{ "2#扭矩", "DB1001.DBD2310" }, // Real
|
||
{ "2#深度", "DB1001.DBD2314" }, // Real
|
||
{ "2#角度", "DB1001.DBD2318" }, // Real
|
||
{ "2#拧紧时间", "DB1001.DBD2322" }, // Real
|
||
{ "3#扭矩", "DB1001.DBD2328" }, // Real
|
||
{ "3#深度", "DB1001.DBD2332" }, // Real
|
||
{ "3#角度", "DB1001.DBD2336" }, // Real
|
||
{ "3#拧紧时间", "DB1001.DBD2340" }, // Real
|
||
{ "4#扭矩", "DB1001.DBD2346" }, // Real
|
||
{ "4#深度", "DB1001.DBD2350" }, // Real
|
||
{ "4#角度", "DB1001.DBD2354" }, // Real
|
||
{ "4#拧紧时间", "DB1001.DBD2358" }, // Real
|
||
{ "5#扭矩", "DB1001.DBD2364" }, // Real
|
||
{ "5#深度", "DB1001.DBD2368" }, // Real
|
||
{ "5#角度", "DB1001.DBD2372" }, // Real
|
||
{ "5#拧紧时间", "DB1001.DBD2376" }, // Real
|
||
{ "6#扭矩", "DB1001.DBD2382" }, // Real
|
||
{ "6#深度", "DB1001.DBD2386" }, // Real
|
||
{ "6#角度", "DB1001.DBD2390" }, // Real
|
||
{ "6#拧紧时间", "DB1001.DBD2394" }, // Real
|
||
{ "7#扭矩", "DB1001.DBD2400" }, // Real
|
||
{ "7#深度", "DB1001.DBD2404" }, // Real
|
||
{ "7#角度", "DB1001.DBD2408" }, // Real
|
||
{ "7#拧紧时间", "DB1001.DBD2412" }, // Real
|
||
{ "节拍时间", "DB1001.DBD3036" }, // Real
|
||
|
||
// 托盘号
|
||
{ "托盘号", "DB1001.DBW2072" } // Int
|
||
};
|
||
|
||
// OP115 专属地址映射(自动上下料到固化炉工位,DB1001)
|
||
private readonly Dictionary<string, (string Addr, int Len)> _op115StringMap = new()
|
||
{
|
||
{ "报警信息", ("DB1001.DBB58", 48) }, // Array[1..48] of Byte
|
||
{ "产品型号", ("DB1001.DBB1000", 48) }, // String[48]
|
||
{ "产品名称", ("DB1001.DBB1054", 48) } // String[48]
|
||
};
|
||
|
||
private readonly Dictionary<string, string> _op115IntMap = 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=正常
|
||
};
|
||
|
||
// OP140 专属地址映射(气密性测试工位,DB1001)
|
||
private readonly Dictionary<string, (string Addr, int Len)> _op140StringMap = new()
|
||
{
|
||
{ "报警信息", ("DB1001.DBB58", 48) }, // Array[1..48] of Byte
|
||
{ "产品型号", ("DB1001.DBB1000", 48) }, // String[48]
|
||
{ "产品名称", ("DB1001.DBB1054", 48) }, // String[48]
|
||
{ "查询SN", ("DB1001.DBB2146", 40) } // String[40]
|
||
};
|
||
|
||
private readonly Dictionary<string, string> _op140IntMap = 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=正常
|
||
|
||
// 产量数据
|
||
{ "实际产量", "DB1001.DBD1104" }, // DInt
|
||
{ "合格数量", "DB1001.DBD1108" }, // DInt
|
||
{ "失败数量", "DB1001.DBD1112" }, // DInt
|
||
|
||
// 操作请求
|
||
{ "工位查询请求", "DB1001.DBW2000" }, // Int - 1=请求开始,0=无请求
|
||
{ "结果保存请求", "DB1001.DBW2002" }, // Int - 1=请求开始,0=无请求
|
||
|
||
// 测试结果
|
||
{ "结果SN", "DB1001.DBW2260" }, // Int
|
||
{ "Socket", "DB1001.DBW2302" }, // Int - 1:工位1;2:工位2
|
||
{ "产品结果", "DB1001.DBW2304" }, // Int
|
||
{ "托盘号", "DB1001.DBW2318" }, // Int
|
||
|
||
// 工艺参数(Real)
|
||
{ "压力值", "DB1001.DBD2306" }, // Real
|
||
{ "泄露值", "DB1001.DBD2310" }, // Real
|
||
{ "节拍时间", "DB1001.DBD2314" } // Real
|
||
};
|
||
|
||
// OP165 专属地址映射(自动贴标工位,DB1001)
|
||
private readonly Dictionary<string, (string Addr, int Len)> _op165StringMap = new()
|
||
{
|
||
{ "报警信息", ("DB1001.DBB58", 48) }, // Array[1..48] of Byte
|
||
{ "产品型号", ("DB1001.DBB1000", 48) }, // String[48]
|
||
{ "产品名称", ("DB1001.DBB1054", 48) }, // String[48]
|
||
{ "查询SN", ("DB1001.DBB2060", 40) }, // String[40]
|
||
{ "结果SN", ("DB1001.DBB2170", 40) }, // String[40]
|
||
{ "二维码内容", ("DB1001.DBB2226", 28) }, // String[28]
|
||
{ "下料工位条码", ("DB1001.DBB2876", 40) } // String[40]
|
||
};
|
||
|
||
private readonly Dictionary<string, string> _op165IntMap = 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=正常
|
||
|
||
// 产量数据
|
||
{ "实际产量", "DB1001.DBD1104" }, // DInt
|
||
{ "合格数量", "DB1001.DBD1108" }, // DInt
|
||
{ "失败数量", "DB1001.DBD1112" }, // DInt
|
||
|
||
// 操作请求
|
||
{ "工位查询请求", "DB1001.DBW2000" }, // Int - 1=请求开始,0=无请求
|
||
{ "结果保存请求", "DB1001.DBW2002" }, // Int - 1=请求开始,0=无请求
|
||
{ "下料工位条码上传", "DB1001.DBW2874" }, // Int
|
||
|
||
// 检测结果
|
||
{ "产品总结果", "DB1001.DBW2214" }, // Int
|
||
{ "轴承检测结果", "DB1001.DBW2216" }, // Int
|
||
{ "贴标拍照结果", "DB1001.DBW2218" }, // Int
|
||
{ "托盘号", "DB1001.DBW2220" }, // Int
|
||
|
||
// 工艺参数
|
||
{ "节拍时间", "DB1001.DBD2222" } // Real
|
||
};
|
||
#endregion
|
||
/// <summary>
|
||
/// 构造函数(依赖注入获取PLC配置)
|
||
/// </summary>
|
||
/// <param name="plcConfigs">所有PLC的连接配置</param>
|
||
/// <param name="globalConfig">PLC全局超时配置</param>
|
||
public PlcService(IOptions<GlobalPlcConfig> globalConfig)
|
||
{
|
||
_globalConfig = globalConfig?.Value ?? throw new ArgumentNullException(nameof(globalConfig), "PLC全局配置不能为空");
|
||
//初始化plcConfigs
|
||
_plcConfigs = initPlcConfigs(_plcConfigs);
|
||
}
|
||
#endregion
|
||
|
||
#region 核心业务方法
|
||
|
||
/// <summary>
|
||
/// 读取PLC生产数据(优化版:连接池+并发控制+OP070-1/OP080专属适配)
|
||
/// </summary>
|
||
/// <param name="ip">PLC IP地址</param>
|
||
/// <param name="plcName">PLC名称(支持OP070-1/OP080)</param>
|
||
/// <param name="rack">机架号</param>
|
||
/// <param name="slot">槽位号</param>
|
||
/// <param name="cpuType">PLC型号(默认S7-1500)</param>
|
||
/// <returns>读取结果(状态+数据+消息)</returns>
|
||
/// <summary>
|
||
/// PLC生产数据读取核心方法(优化版)
|
||
/// </summary>
|
||
/// <returns>成功状态/生产数据/提示消息</returns>
|
||
public async Task<(bool Success, PlcProductionData Data, string Message)> ReadProductionDataAsync(
|
||
string ip,
|
||
string plcName,
|
||
short rack,
|
||
short slot,
|
||
CpuType cpuType = CpuType.S71500)
|
||
{
|
||
if (string.IsNullOrWhiteSpace(ip)) return (false, null, "PLC IP地址不能为空");
|
||
if (string.IsNullOrWhiteSpace(plcName)) return (false, null, "PLC名称不能为空");
|
||
if (rack is < 0 or > 10) return (false, null, $"PLC机架号{rack}无效(有效值0-10)");
|
||
if (slot is < 0 or > 4) return (false, null, $"PLC槽位号{slot}无效(有效值0-4)");
|
||
|
||
Plc plc = null;
|
||
bool isConnReused = false;
|
||
var poolKey = $"{ip}_{rack}_{slot}_{cpuType}";
|
||
PlcProductionData prodData = null;
|
||
int iQueryRequest = 0;
|
||
int iSaveRequest = 0;
|
||
string strSaveRequest = string.Empty;
|
||
|
||
// 2. 并发控制,ConfigureAwait(false)减少线程上下文切换开销、提升异步执行效率
|
||
await _concurrencySemaphore.WaitAsync().ConfigureAwait(false);
|
||
|
||
try
|
||
{
|
||
|
||
// 3. 连接池复用+有效性校验(精简逻辑+轻量提效)
|
||
if (_plcConnPool.TryGetValue(poolKey, out var poolItem) && poolItem.Client is { IsConnected: true })
|
||
{
|
||
plc = poolItem.Client;
|
||
_plcConnPool[poolKey] = (plc, DateTime.Now);
|
||
isConnReused = true;
|
||
}
|
||
else
|
||
{
|
||
plc = CreatePlcClient(cpuType, ip, rack, slot);
|
||
await OpenPlcConnectionAsync(plc).ConfigureAwait(false);
|
||
if (!plc.IsConnected) return (false, null, $"{plcName}连接失败");
|
||
_plcConnPool.TryAdd(poolKey, (plc, DateTime.Now));
|
||
}
|
||
#region 多工位请求状态读取
|
||
//// 4. 多工位请求状态读取
|
||
//switch (plcName)
|
||
//{
|
||
// case "OP020-2":
|
||
// // 优化:异步读取取消上下文切换
|
||
// iSaveRequest = await ReadPlcIntAsync(plc, _op020_2IntMap["上传请求"]).ConfigureAwait(false);
|
||
// break;
|
||
// case "OP020-3":
|
||
// iSaveRequest = 1;
|
||
// break;
|
||
// case "OP020-4":
|
||
// // 核心优化:4个地址并行读取(耗时从累加和→最长单地址耗时)
|
||
// var task4_1 = ReadPlcIntAsync(plc, _op020_4IntMap["产品1上传请求"]);
|
||
// var task4_2 = ReadPlcIntAsync(plc, _op020_4IntMap["产品2上传请求"]);
|
||
// var task4_3 = ReadPlcIntAsync(plc, _op020_4IntMap["产品3上传请求"]);
|
||
// var task4_4 = ReadPlcIntAsync(plc, _op020_4IntMap["产品4上传请求"]);
|
||
// // 并行等待所有任务完成(无编译错误)
|
||
// await Task.WhenAll(task4_1, task4_2, task4_3, task4_4).ConfigureAwait(false);
|
||
// iSaveRequest = (task4_1.Result == 1 || task4_2.Result == 1 || task4_3.Result == 1 || task4_4.Result == 1) ? 1 : 0;
|
||
// break;
|
||
// case "OP070-1":
|
||
// case "OP070-2":
|
||
// case "OP070-3":
|
||
// // 优化:2个地址并行读取
|
||
// var taskQuery70 = ReadPlcIntAsync(plc, _op070_1IntMap["查询请求"]);
|
||
// var taskSave70 = ReadPlcIntAsync(plc, _op070_1IntMap["保存请求"]);
|
||
// await Task.WhenAll(taskQuery70, taskSave70).ConfigureAwait(false);
|
||
// iQueryRequest = taskQuery70.Result;
|
||
// iSaveRequest = taskSave70.Result;
|
||
// break;
|
||
// case "OP075":
|
||
// // 优化:2个地址并行读取
|
||
// var taskQuery75 = ReadPlcIntAsync(plc, _op075IntMap["查询请求"]);
|
||
// var taskSave75 = ReadPlcIntAsync(plc, _op075IntMap["保存请求"]);
|
||
// await Task.WhenAll(taskQuery75, taskSave75).ConfigureAwait(false);
|
||
// iQueryRequest = taskQuery75.Result;
|
||
// iSaveRequest = taskSave75.Result;
|
||
// break;
|
||
// case "OP080-1":
|
||
// // 优化:3个地址并行读取
|
||
// var taskQuery80 = ReadPlcIntAsync(plc, _op080_1IntMap["合装工位查询请求"]);
|
||
// var task8_1 = ReadPlcIntAsync(plc, _op080_1IntMap["合装结果保存请求"]);
|
||
// var task8_2 = ReadPlcIntAsync(plc, _op080_1IntMap["拧紧结果保存请求"]);
|
||
// await Task.WhenAll(taskQuery80, task8_1, task8_2).ConfigureAwait(false);
|
||
// iQueryRequest = taskQuery80.Result;
|
||
// if (task8_1.Result == 1) { iSaveRequest = 1; strSaveRequest = "合装结果保存请求"; }
|
||
// if (task8_2.Result == 1)
|
||
// {
|
||
// iSaveRequest = 1;
|
||
// strSaveRequest = string.IsNullOrEmpty(strSaveRequest)
|
||
// ? "拧紧结果保存请求"
|
||
// : $"{strSaveRequest},拧紧结果保存请求";
|
||
// }
|
||
// break;
|
||
//}
|
||
#endregion
|
||
// 4. 多工位请求状态读取
|
||
switch (plcName)
|
||
{
|
||
case "OP020-2":
|
||
iSaveRequest = await ReadPlcIntAsync(plc, _op020_2IntMap["上传请求"]).ConfigureAwait(false);
|
||
break;
|
||
case "OP020-3":
|
||
iSaveRequest = 1;
|
||
break;
|
||
case "OP020-4":
|
||
var task4_1 = ReadPlcIntAsync(plc, _op020_4IntMap["产品1上传请求"]);
|
||
var task4_2 = ReadPlcIntAsync(plc, _op020_4IntMap["产品2上传请求"]);
|
||
var task4_3 = ReadPlcIntAsync(plc, _op020_4IntMap["产品3上传请求"]);
|
||
var task4_4 = ReadPlcIntAsync(plc, _op020_4IntMap["产品4上传请求"]);
|
||
await Task.WhenAll(task4_1, task4_2, task4_3, task4_4).ConfigureAwait(false);
|
||
iSaveRequest = (task4_1.Result == 1 || task4_2.Result == 1 || task4_3.Result == 1 || task4_4.Result == 1) ? 1 : 0;
|
||
break;
|
||
case "OP050":
|
||
iSaveRequest = await ReadPlcIntAsync(plc, _op050IntMap["上传请求"]).ConfigureAwait(false);
|
||
break;
|
||
case "OP050-1":
|
||
iSaveRequest = 1;
|
||
break;
|
||
case "OP051扫码":
|
||
iSaveRequest = await ReadPlcIntAsync(plc, _op051ScanIntMap["上传结果请求"]).ConfigureAwait(false);
|
||
break;
|
||
case "OP057":
|
||
iSaveRequest = await ReadPlcIntAsync(plc, _op057IntMap["1#产品结果保存请求"]).ConfigureAwait(false);
|
||
break;
|
||
case "OP058":
|
||
iSaveRequest = 1;
|
||
break;
|
||
case "OP060":
|
||
iSaveRequest = await ReadPlcIntAsync(plc, _op060IntMap["1#产品结果保存请求"]).ConfigureAwait(false);
|
||
break;
|
||
case "OP070-1":
|
||
case "OP070-2":
|
||
case "OP070-3":
|
||
iSaveRequest = await ReadPlcIntAsync(plc, _op070_1IntMap["保存请求"]).ConfigureAwait(false);
|
||
break;
|
||
case "OP075":
|
||
iSaveRequest = await ReadPlcIntAsync(plc, _op075IntMap["保存请求"]).ConfigureAwait(false);
|
||
break;
|
||
case "OP080-1":
|
||
var task8_1 = ReadPlcIntAsync(plc, _op080_1IntMap["合装结果保存请求"]);
|
||
var task8_2 = ReadPlcIntAsync(plc, _op080_1IntMap["拧紧结果保存请求"]);
|
||
await Task.WhenAll(task8_1, task8_2).ConfigureAwait(false);
|
||
if (task8_1.Result == 1) { iSaveRequest = 1; strSaveRequest = "合装结果保存请求"; }
|
||
if (task8_2.Result == 1)
|
||
{
|
||
iSaveRequest = 1;
|
||
strSaveRequest = string.IsNullOrEmpty(strSaveRequest)
|
||
? "拧紧结果保存请求"
|
||
: $"{strSaveRequest},拧紧结果保存请求";
|
||
}
|
||
break;
|
||
case "OP080-2":
|
||
iSaveRequest = await ReadPlcIntAsync(plc, _op080_2IntMap["保存请求"]).ConfigureAwait(false);
|
||
break;
|
||
case "OP080-3":
|
||
iSaveRequest = await ReadPlcIntAsync(plc, _op080_3IntMap["保存请求"]).ConfigureAwait(false);
|
||
break;
|
||
case "OP085":
|
||
iSaveRequest = await ReadPlcIntAsync(plc, _op085IntMap["保存请求"]).ConfigureAwait(false);
|
||
break;
|
||
case "OP100":
|
||
iSaveRequest = await ReadPlcIntAsync(plc, _op100IntMap["保存请求"]).ConfigureAwait(false);
|
||
break;
|
||
case "OP1011":
|
||
iSaveRequest = await ReadPlcIntAsync(plc, _op1011IntMap["结果保存请求"]).ConfigureAwait(false);
|
||
break;
|
||
case "OP1012":
|
||
iSaveRequest = await ReadPlcIntAsync(plc, _op1012IntMap["结果保存请求"]).ConfigureAwait(false);
|
||
break;
|
||
case "OP102":
|
||
iSaveRequest = await ReadPlcIntAsync(plc, _op102IntMap["产品保存请求"]).ConfigureAwait(false);
|
||
break;
|
||
case "OP110-1":
|
||
iSaveRequest = await ReadPlcIntAsync(plc, _op110_1IntMap["产品保存请求"]).ConfigureAwait(false);
|
||
break;
|
||
case "OP110-2":
|
||
iSaveRequest = await ReadPlcIntAsync(plc, _op110_2IntMap["产品保存请求"]).ConfigureAwait(false);
|
||
break;
|
||
case "OP110-3":
|
||
iSaveRequest = await ReadPlcIntAsync(plc, _op110_3IntMap["产品保存请求"]).ConfigureAwait(false);
|
||
break;
|
||
case "OP115":
|
||
//iSaveRequest = await ReadPlcIntAsync(plc, _op115IntMap["结果保存请求"]).ConfigureAwait(false);
|
||
iSaveRequest = 1;
|
||
break;
|
||
case "OP140":
|
||
iSaveRequest = await ReadPlcIntAsync(plc, _op140IntMap["结果保存请求"]).ConfigureAwait(false);
|
||
break;
|
||
case "OP165":
|
||
iSaveRequest = await ReadPlcIntAsync(plc, _op165IntMap["结果保存请求"]).ConfigureAwait(false);
|
||
break;
|
||
}
|
||
if (iSaveRequest != 1) return (false, null, string.Empty);
|
||
if (iSaveRequest == 1)
|
||
{
|
||
// 优化:所有工位数据读取取消上下文切换
|
||
prodData = plcName switch
|
||
{
|
||
"OP020-2" => await ReadOP020_2DataAsync(plc, ip, plcName).ConfigureAwait(false),
|
||
"OP020-3" => await ReadOP020_3DataAsync(plc, ip, plcName).ConfigureAwait(false),
|
||
"OP020-4" => await ReadOP020_4DataAsync(plc, ip, plcName).ConfigureAwait(false),
|
||
"OP050" => await ReadOP050DataAsync(plc, ip, plcName).ConfigureAwait(false),
|
||
"OP050-1" => await ReadOP050_1DataAsync(plc, ip, plcName).ConfigureAwait(false),
|
||
"OP051扫码" => await ReadOP051ScanDataAsync(plc, ip, plcName).ConfigureAwait(false),
|
||
"OP057" => await ReadOP057DataAsync(plc, ip, plcName).ConfigureAwait(false),
|
||
"OP058" => await ReadOP058DataAsync(plc, ip, plcName).ConfigureAwait(false),
|
||
"OP060" => await ReadOP060DataAsync(plc, ip, plcName).ConfigureAwait(false),
|
||
"OP070-1" or "OP070-2" or "OP070-3" => await ReadOP070_1DataAsync(plc, ip, plcName).ConfigureAwait(false),
|
||
"OP075" => await ReadOP075DataAsync(plc, ip, plcName).ConfigureAwait(false),
|
||
"OP080-1" => await ReadOP080_1DataAsync(plc, ip, plcName, strSaveRequest).ConfigureAwait(false),
|
||
"OP080-2" => await ReadOP080_2DataAsync(plc, ip, plcName).ConfigureAwait(false),
|
||
"OP080-3" => await ReadOP080_3DataAsync(plc, ip, plcName).ConfigureAwait(false),
|
||
"OP085" => await ReadOP085DataAsync(plc, ip, plcName).ConfigureAwait(false),
|
||
"OP100" => await ReadOP100DataAsync(plc, ip, plcName).ConfigureAwait(false),
|
||
"OP1011" => await ReadOP1011DataAsync(plc, ip, plcName).ConfigureAwait(false),
|
||
"OP1012" => await ReadOP1012DataAsync(plc, ip, plcName).ConfigureAwait(false),
|
||
"OP102" => await ReadOP102DataAsync(plc, ip, plcName).ConfigureAwait(false),
|
||
"OP110-1" => await ReadOP110_1DataAsync(plc, ip, plcName).ConfigureAwait(false),
|
||
"OP110-2" => await ReadOP110_2DataAsync(plc, ip, plcName).ConfigureAwait(false),
|
||
"OP110-3" => await ReadOP110_3DataAsync(plc, ip, plcName).ConfigureAwait(false),
|
||
"OP115" => await ReadOP115DataAsync(plc, ip, plcName).ConfigureAwait(false),
|
||
"OP140" => await ReadOP140DataAsync(plc, ip, plcName).ConfigureAwait(false),
|
||
"OP165" => await ReadOP165DataAsync(plc, ip, plcName).ConfigureAwait(false),
|
||
_ => prodData
|
||
};
|
||
|
||
// 统一空值兜底(保留原逻辑)
|
||
if (prodData != null)
|
||
{
|
||
prodData.QualificationFlag ??= "0";
|
||
prodData.ReworkFlag ??= "0";
|
||
prodData.ProductionCycle ??= 0;
|
||
|
||
_ = Task.Run(() => _plcProductionDataService.AddPlcProductionData(prodData))
|
||
.ContinueWith(t =>
|
||
{
|
||
if (t.IsFaulted && plc is { IsConnected: true })
|
||
{
|
||
WritePlcSaveRequestResult(plc, ip, plcName, prodData, "6");
|
||
Console.WriteLine($"{plcName}({ip})数据保存失败:{t.Exception?.InnerException?.Message ?? t.Exception?.Message ?? "未知错误"}");
|
||
}
|
||
}, TaskContinuationOptions.OnlyOnFaulted)
|
||
.ConfigureAwait(false); // 新增:取消上下文切换
|
||
}
|
||
}
|
||
|
||
// 个性化返回消息+写入成功返回值
|
||
var successMsg = isConnReused
|
||
? $"{plcName}生产数据读取成功(复用连接)"
|
||
: $"{plcName}生产数据读取成功(新建连接)";
|
||
WritePlcSaveRequestResult(plc, ip, plcName, prodData, "1");
|
||
_ = Task.Run(() => RecordPlcOperationResult(plcName, prodData)).ConfigureAwait(false);
|
||
return (true, prodData, successMsg);
|
||
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
return (false, null, $"{plcName}生产数据读取失败:{ex.Message}");
|
||
}
|
||
finally
|
||
{
|
||
// 资源绝对释放:防止死锁+连接泄漏,增加多重校验
|
||
try
|
||
{
|
||
if (!isConnReused && plc != null)
|
||
{
|
||
ReleasePlcConnection(plc);
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] PLC连接释放前置校验异常(工位:{plcName},IP:{ip}):{ex.Message}");
|
||
}
|
||
finally
|
||
{
|
||
// 确保信号量释放,无论任何情况
|
||
_concurrencySemaphore.Release();
|
||
}
|
||
}
|
||
}
|
||
|
||
#region 工位专属读取方法
|
||
/// <summary>
|
||
/// 读取OP020-2数据(合盖工位
|
||
/// </summary>
|
||
private async Task<PlcProductionData?> ReadOP020_2DataAsync(Plc plc, string ip, string workstationCode)
|
||
{
|
||
// 前置校验:PLC连接无效直接返回,避免无效操作
|
||
if (plc == null || !plc.IsConnected)
|
||
{
|
||
Console.WriteLine($"OP020-2({ip})PLC连接无效,跳过数据读取");
|
||
return null;
|
||
}
|
||
|
||
try
|
||
{
|
||
// 1. 并行读取所有PLC字段
|
||
// 1.1 准备字符串字段读取任务(并行)
|
||
var taskProductModel = ReadPlcStringAsync(plc, _op020_2StringMap["产品型号"].Addr, _op020_2StringMap["产品型号"].Len);
|
||
var taskProductName = ReadPlcStringAsync(plc, _op020_2StringMap["产品名称"].Addr, _op020_2StringMap["产品名称"].Len);
|
||
var taskSN = ReadPlcStringAsync(plc, _op020_2StringMap["SN"].Addr, _op020_2StringMap["SN"].Len);
|
||
|
||
// 1.2 准备整数字段读取任务(并行)
|
||
var taskRunStatus = ReadPlcIntAsync(plc, _op020_2IntMap["运行状态"]);
|
||
var taskMachineModel = ReadPlcIntAsync(plc, _op020_2IntMap["设备模式"]);
|
||
var taskOnlineStatus = ReadPlcIntAsync(plc, _op020_2IntMap["设备在线状态"]);
|
||
var taskProduceModel = ReadPlcIntAsync(plc, _op020_2IntMap["生产模式"]);
|
||
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, taskActualOutput, taskQualifiedQty, taskFailedQty);
|
||
|
||
// 2. 获取读取结果(带空值兜底,避免后续空引用)
|
||
string productModel = await taskProductModel ?? string.Empty;
|
||
string productName = await taskProductName ?? string.Empty;
|
||
string sn = await taskSN ?? string.Empty;
|
||
int runStatus = await taskRunStatus;
|
||
int machineModel = await taskMachineModel;
|
||
int onlineStatus = await taskOnlineStatus;
|
||
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["上传请求"]);
|
||
|
||
// 4. 业务逻辑计算(优化:提前定义常量,减少重复计算)
|
||
string reworkFlag = produceModel == 4 ? "1" : "0";
|
||
string produceModelDesc = produceModel switch
|
||
{
|
||
1 => "正常模式",
|
||
2 => "清线模式",
|
||
4 => "返工模式",
|
||
8 => "换型模式",
|
||
16 => "预热模式",
|
||
_ => $"未知({produceModel})"
|
||
};
|
||
string runStatusDesc = runStatus switch { 1 => "空闲", 2 => "运行中", 3 => "故障", _ => $"未知({runStatus})" };
|
||
string onlineStatusDesc = onlineStatus == 1 ? "离线" : "在线";
|
||
string qualificationFlag = totalResult switch { 1 => "1", 2 => "0", _ => totalResult.ToString() };
|
||
|
||
// 5. 构建数据实体(优化:减少Trim/空值判断,提前兜底)
|
||
return new PlcProductionData
|
||
{
|
||
PlcIp = ip, // 提前确保ip非空,无需Trim(外层已校验)
|
||
OccurTime = DateTime.Now,
|
||
LineCode = "line2",
|
||
WorkstationCode = workstationCode,
|
||
WorkstationName = "合盖", // 补充工站名称
|
||
ProductModel = productModel,
|
||
ProductName = productName,
|
||
ProductCode = sn,
|
||
SN1 = sn,
|
||
SN2 = sn,
|
||
QualificationFlag = qualificationFlag,
|
||
ReworkFlag = reworkFlag,
|
||
Automanual = machineModel,
|
||
Runstatus = runStatus,
|
||
OnlineStatus = onlineStatusDesc,
|
||
ProduceModel = produceModelDesc,
|
||
ActualOutQty = actualOutput.ToString(),
|
||
QualifiedQty = qualifiedQty.ToString(),
|
||
FailedQty = failedQty.ToString(),
|
||
TrayNo = trayNo.ToString(),
|
||
CreatedBy = "PLC",
|
||
CreatedTime = DateTime.Now
|
||
};
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
// 增强异常日志:包含堆栈,便于定位问题
|
||
Console.WriteLine($"OP020-2({ip})数据读取异常:{ex.Message}\n{ex.StackTrace}");
|
||
return null;
|
||
}
|
||
}
|
||
/// <summary>
|
||
/// OP020-3 数据读取(热铆工位,DB1001)- 按OP020-4风格优化
|
||
/// </summary>
|
||
private async Task<PlcProductionData> ReadOP020_3DataAsync(Plc plc, string ip, string workstationCode)
|
||
{
|
||
// 前置校验:PLC连接无效直接返回,与OP020-4风格保持一致
|
||
if (plc == null || !plc.IsConnected)
|
||
{
|
||
return null;
|
||
}
|
||
|
||
try
|
||
{
|
||
// 1. 批量创建并行读取任务(字典存储,最大化并行效率,减少临时变量开销)
|
||
// 字符串读取任务(匹配_op020_3StringMap所有字段)
|
||
var stringReadTasks = new Dictionary<string, Task<string>>
|
||
{
|
||
{ "产品型号", ReadPlcStringAsync(plc, _op020_3StringMap["产品型号"].Addr, _op020_3StringMap["产品型号"].Len) },
|
||
{ "产品1SN", ReadPlcStringAsync(plc, _op020_3StringMap["产品1SN"].Addr, _op020_3StringMap["产品1SN"].Len) },
|
||
{ "产品2SN", ReadPlcStringAsync(plc, _op020_3StringMap["产品2SN"].Addr, _op020_3StringMap["产品2SN"].Len) },
|
||
{ "产品3SN", ReadPlcStringAsync(plc, _op020_3StringMap["产品3SN"].Addr, _op020_3StringMap["产品3SN"].Len) },
|
||
{ "产品4SN", ReadPlcStringAsync(plc, _op020_3StringMap["产品4SN"].Addr, _op020_3StringMap["产品4SN"].Len) }
|
||
};
|
||
|
||
// Int读取任务(匹配_op020_3IntMap基础状态/请求/托盘号字段)
|
||
var intReadTasks = new Dictionary<string, Task<int>>
|
||
{
|
||
{ "运行状态", ReadPlcIntAsync(plc, _op020_3IntMap["运行状态"]) },
|
||
{ "设备模式", ReadPlcIntAsync(plc, _op020_3IntMap["设备模式"]) },
|
||
{ "设备在线状态", ReadPlcIntAsync(plc, _op020_3IntMap["设备在线状态"]) },
|
||
{ "ByPass", ReadPlcIntAsync(plc, _op020_3IntMap["ByPass"]) },
|
||
{ "生产模式", ReadPlcIntAsync(plc, _op020_3IntMap["生产模式"]) },
|
||
{ "查询请求", ReadPlcIntAsync(plc, _op020_3IntMap["查询请求"]) },
|
||
{ "上传请求", ReadPlcIntAsync(plc, _op020_3IntMap["上传请求"]) },
|
||
{ "托盘号", ReadPlcIntAsync(plc, _op020_3IntMap["托盘号"]) }
|
||
};
|
||
|
||
// DInt/Real读取任务(产量+节拍时间,复用ReadPlcIntAsync兼容转换,与OP020-4风格一致)
|
||
var dIntRealReadTasks = new Dictionary<string, Task<int>>
|
||
{
|
||
{ "节拍时间", ReadPlcIntAsync(plc, _op020_3IntMap["节拍时间"]) },
|
||
{ "合格数量", ReadPlcIntAsync(plc, _op020_3IntMap["合格数量"]) },
|
||
{ "失败数量", ReadPlcIntAsync(plc, _op020_3IntMap["失败数量"]) },
|
||
{ "实际数量", ReadPlcIntAsync(plc, _op020_3IntMap["实际数量"]) }
|
||
};
|
||
|
||
// 2. 并行等待所有读取任务完成(合并任务列表,单次WaitAll提升效率,添加ConfigureAwait(false)优化异步)
|
||
var allTasks = new List<Task>();
|
||
allTasks.AddRange(stringReadTasks.Values);
|
||
allTasks.AddRange(intReadTasks.Values);
|
||
allTasks.AddRange(dIntRealReadTasks.Values);
|
||
await Task.WhenAll(allTasks).ConfigureAwait(false);
|
||
|
||
// 3. 提取读取结果(直接取值+空值兜底,减少await重复调用,与OP020-4风格一致)
|
||
// 字符串字段
|
||
string productModel = stringReadTasks["产品型号"].Result ?? string.Empty;
|
||
string product1SN = stringReadTasks["产品1SN"].Result ?? string.Empty;
|
||
string product2SN = stringReadTasks["产品2SN"].Result ?? string.Empty;
|
||
string product3SN = stringReadTasks["产品3SN"].Result ?? string.Empty;
|
||
string product4SN = stringReadTasks["产品4SN"].Result ?? string.Empty;
|
||
|
||
// 整数字段
|
||
int runStatus = intReadTasks["运行状态"].Result;
|
||
int machineModel = intReadTasks["设备模式"].Result;
|
||
int onlineStatus = intReadTasks["设备在线状态"].Result;
|
||
int byPass = intReadTasks["ByPass"].Result;
|
||
int produceModel = intReadTasks["生产模式"].Result;
|
||
int trayNo = intReadTasks["托盘号"].Result;
|
||
|
||
// DInt/Real转换结果
|
||
int cycleTime = dIntRealReadTasks["节拍时间"].Result;
|
||
int qualifiedQty = dIntRealReadTasks["合格数量"].Result;
|
||
int failedQty = dIntRealReadTasks["失败数量"].Result;
|
||
int actualOutput = dIntRealReadTasks["实际数量"].Result;
|
||
|
||
// 4. 业务逻辑转换(极简逻辑,仅保留必要转换,与OP020-4风格对齐)
|
||
string reworkFlag = produceModel == 4 ? "1" : "0";
|
||
string produceModelDesc = produceModel switch
|
||
{
|
||
1 => "正常模式",
|
||
2 => "清线模式",
|
||
4 => "返工模式",
|
||
8 => "换型模式",
|
||
16 => "预热模式",
|
||
_ => produceModel.ToString()
|
||
};
|
||
string onlineStatusDesc = onlineStatus == 1 ? "离线" : "在线";
|
||
// OP020-3无产品结果字段,默认合格(与原有逻辑保持一致)
|
||
string qualificationFlag = "1";
|
||
|
||
// 5. 构建实体(精准匹配PlcProductionData字段,无冗余,补充OP020-3独有信息)
|
||
return new PlcProductionData
|
||
{
|
||
PlcIp = ip,
|
||
OccurTime = DateTime.Now,
|
||
LineCode = "line2",
|
||
WorkstationCode = workstationCode,
|
||
WorkstationName = "热铆工位", // 补充工站名称,与OP020-4对齐
|
||
ProductModel = productModel,
|
||
ProductName = string.Empty, // OP020-3无产品名字段,置空
|
||
ProductCode = product1SN, // 主产品编码取产品1SN,与OP020-4对齐
|
||
ReworkFlag = reworkFlag,
|
||
Automanual = machineModel,
|
||
Runstatus = runStatus,
|
||
OnlineStatus = onlineStatusDesc,
|
||
ProduceModel = produceModelDesc,
|
||
TrayNo = trayNo.ToString(),
|
||
CreatedBy = "PLC",
|
||
CreatedTime = DateTime.Now,
|
||
Product1SN = product1SN,
|
||
Product2SN = product2SN,
|
||
Product3SN = product3SN,
|
||
Product4SN = product4SN,
|
||
SN1 = product1SN,
|
||
SN2 = product2SN,
|
||
ActualOutQty = actualOutput.ToString(),
|
||
QualifiedQty = qualifiedQty.ToString(),
|
||
FailedQty = failedQty.ToString(),
|
||
// 补充节拍时间和报警信息
|
||
ProductionCycle = cycleTime,
|
||
// 合格标识
|
||
QualificationFlag = qualificationFlag
|
||
};
|
||
}
|
||
catch
|
||
{
|
||
// 异常直接返回null,与OP020-4风格保持一致
|
||
return null;
|
||
}
|
||
}
|
||
|
||
|
||
|
||
/// <summary>
|
||
/// 读取OP020-4数据(pin压合&视觉检查工位)
|
||
/// </summary>
|
||
private async Task<PlcProductionData> ReadOP020_4DataAsync(Plc plc, string ip, string workstationCode)
|
||
{
|
||
// 前置校验:PLC连接无效直接返回,避免无效操作
|
||
if (plc == null || !plc.IsConnected)
|
||
{
|
||
return null;
|
||
}
|
||
|
||
try
|
||
{
|
||
// 1. 批量创建并行读取任务(最大化并行效率,减少临时变量开销)
|
||
var stringReadTasks = new Dictionary<string, Task<string>>
|
||
{
|
||
{ "报警信息", 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) },
|
||
{ "产品2SN", ReadPlcStringAsync(plc, _op020_4StringMap["产品2SN"].Addr, _op020_4StringMap["产品2SN"].Len) },
|
||
{ "产品3SN", ReadPlcStringAsync(plc, _op020_4StringMap["产品3SN"].Addr, _op020_4StringMap["产品3SN"].Len) },
|
||
{ "产品4SN", ReadPlcStringAsync(plc, _op020_4StringMap["产品4SN"].Addr, _op020_4StringMap["产品4SN"].Len) }
|
||
};
|
||
|
||
var intReadTasks = new Dictionary<string, Task<int>>
|
||
{
|
||
{ "运行状态", ReadPlcIntAsync(plc, _op020_4IntMap["运行状态"]) },
|
||
{ "设备模式", ReadPlcIntAsync(plc, _op020_4IntMap["设备模式"]) },
|
||
{ "设备在线状态", ReadPlcIntAsync(plc, _op020_4IntMap["设备在线状态"]) },
|
||
{ "ByPass", ReadPlcIntAsync(plc, _op020_4IntMap["ByPass"]) },
|
||
{ "生产模式", ReadPlcIntAsync(plc, _op020_4IntMap["生产模式"]) },
|
||
{ "托盘号", ReadPlcIntAsync(plc, _op020_4IntMap["托盘号"]) },
|
||
{ "产品1结果", ReadPlcIntAsync(plc, _op020_4IntMap["产品1结果"]) },
|
||
{ "产品2结果", ReadPlcIntAsync(plc, _op020_4IntMap["产品2结果"]) },
|
||
{ "产品3结果", ReadPlcIntAsync(plc, _op020_4IntMap["产品3结果"]) },
|
||
{ "产品4结果", ReadPlcIntAsync(plc, _op020_4IntMap["产品4结果"]) }
|
||
};
|
||
|
||
// DInt(双整数)读取任务(复用ReadPlcIntAsync,PLC底层兼容DInt转int)
|
||
var dIntReadTasks = new Dictionary<string, Task<int>>
|
||
{
|
||
{ "实际产量", ReadPlcIntAsync(plc, _op050IntMap["实际产量"]) },
|
||
{ "合格数量", ReadPlcIntAsync(plc, _op050IntMap["合格数量"]) },
|
||
{ "失败数量", ReadPlcIntAsync(plc, _op050IntMap["失败数量"]) }
|
||
};
|
||
|
||
// 2. 并行等待所有读取任务完成(合并任务列表,单次WaitAll提升效率)
|
||
var allTasks = new List<Task>();
|
||
allTasks.AddRange(stringReadTasks.Values);
|
||
allTasks.AddRange(intReadTasks.Values);
|
||
allTasks.AddRange(dIntReadTasks.Values);
|
||
await Task.WhenAll(allTasks).ConfigureAwait(false);
|
||
|
||
// 3. 提取读取结果(直接取值+空值兜底,减少await重复调用)
|
||
// 字符串字段
|
||
string productModel = stringReadTasks["产品型号"].Result ?? string.Empty;
|
||
string productName = stringReadTasks["产品名称"].Result ?? string.Empty;
|
||
string product1SN = stringReadTasks["产品1SN"].Result ?? string.Empty;
|
||
string product2SN = stringReadTasks["产品2SN"].Result ?? string.Empty;
|
||
string product3SN = stringReadTasks["产品3SN"].Result ?? string.Empty;
|
||
string product4SN = stringReadTasks["产品4SN"].Result ?? string.Empty;
|
||
|
||
// 整数字段
|
||
int runStatus = intReadTasks["运行状态"].Result;
|
||
int machineModel = intReadTasks["设备模式"].Result;
|
||
int onlineStatus = intReadTasks["设备在线状态"].Result;
|
||
int produceModel = intReadTasks["生产模式"].Result;
|
||
int trayNo = intReadTasks["托盘号"].Result;
|
||
int product1Result = intReadTasks["产品1结果"].Result;
|
||
int product2Result = intReadTasks["产品2结果"].Result;
|
||
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";
|
||
string produceModelDesc = produceModel switch
|
||
{
|
||
1 => "正常模式",
|
||
2 => "清线模式",
|
||
4 => "返工模式",
|
||
8 => "换型模式",
|
||
16 => "预热模式",
|
||
_ => produceModel.ToString()
|
||
};
|
||
string onlineStatusDesc = onlineStatus == 1 ? "离线" : "在线";
|
||
bool allProductsOk = product1Result == 1 && product2Result == 1 && product3Result == 1 && product4Result == 1;
|
||
string qualificationFlag = allProductsOk ? "1" : "2";
|
||
// 6. 构建实体(精准匹配PlcProductionData字段,无冗余)
|
||
return new PlcProductionData
|
||
{
|
||
PlcIp = ip,
|
||
OccurTime = DateTime.Now,
|
||
LineCode = "line2",
|
||
WorkstationCode = workstationCode,
|
||
WorkstationName = "pin压合&视觉检查", // 补充工站名称
|
||
ProductModel = productModel,
|
||
ProductName = productName,
|
||
ProductCode = product1SN, // 主产品编码取产品1SN(可根据业务调整)
|
||
ReworkFlag = reworkFlag,
|
||
Automanual = machineModel,
|
||
Runstatus = runStatus,
|
||
OnlineStatus = onlineStatusDesc,
|
||
ProduceModel = produceModelDesc,
|
||
TrayNo = trayNo.ToString(),
|
||
CreatedBy = "PLC",
|
||
CreatedTime = DateTime.Now,
|
||
Product1SN = product1SN,
|
||
Product2SN = product2SN,
|
||
Product3SN = product3SN,
|
||
Product4SN = product4SN,
|
||
Product1Result = product1Result.ToString(),
|
||
Product2Result = product2Result.ToString(),
|
||
Product3Result = product1Result.ToString(),
|
||
Product4Result = product1Result.ToString(),
|
||
SN1 = product1SN,
|
||
SN2 = product2SN,
|
||
ActualOutQty = actualOutput.ToString(),
|
||
QualifiedQty = qualifiedQty.ToString(),
|
||
FailedQty = failedQty.ToString(),
|
||
QualificationFlag = qualificationFlag
|
||
};
|
||
}
|
||
catch
|
||
{
|
||
return null;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 读取OP050数据(点白胶DC744工位)
|
||
/// </summary>
|
||
/// <param name="plc">PLC连接实例</param>
|
||
/// <param name="ip">PLC的IP地址</param>
|
||
/// <param name="workstationCode">工位编码</param>
|
||
/// <returns>PLC生产数据实体</returns>
|
||
private async Task<PlcProductionData> ReadOP050DataAsync(Plc plc, string ip, string workstationCode)
|
||
{
|
||
// 前置校验:PLC连接无效直接返回,避免无效操作
|
||
if (plc == null || !plc.IsConnected)
|
||
{
|
||
return null;
|
||
}
|
||
|
||
try
|
||
{
|
||
// 1. 批量创建并行读取任务(最大化并行效率,按字段类型分类)
|
||
var stringReadTasks = new Dictionary<string, Task<string>>
|
||
{
|
||
{ "报警信息", ReadPlcStringAsync(plc, _op050StringMap["报警信息"].Addr, _op050StringMap["报警信息"].Len) },
|
||
{ "产品型号", ReadPlcStringAsync(plc, _op050StringMap["产品型号"].Addr, _op050StringMap["产品型号"].Len) },
|
||
{ "产品名称", ReadPlcStringAsync(plc, _op050StringMap["产品名称"].Addr, _op050StringMap["产品名称"].Len) },
|
||
{ "产品1SN", ReadPlcStringAsync(plc, _op050StringMap["产品1SN"].Addr, _op050StringMap["产品1SN"].Len) },
|
||
{ "产品2SN", ReadPlcStringAsync(plc, _op050StringMap["产品2SN"].Addr, _op050StringMap["产品2SN"].Len) },
|
||
{ "产品3SN", ReadPlcStringAsync(plc, _op050StringMap["产品3SN"].Addr, _op050StringMap["产品3SN"].Len) },
|
||
{ "产品4SN", ReadPlcStringAsync(plc, _op050StringMap["产品4SN"].Addr, _op050StringMap["产品4SN"].Len) }
|
||
};
|
||
|
||
// 注意:OP050包含DInt/Real类型,需适配读取方法(假设已有对应异步方法)
|
||
var intReadTasks = new Dictionary<string, Task<int>>
|
||
{
|
||
{ "运行状态", ReadPlcIntAsync(plc, _op050IntMap["运行状态"]) },
|
||
{ "设备模式", ReadPlcIntAsync(plc, _op050IntMap["设备模式"]) },
|
||
{ "设备在线状态", ReadPlcIntAsync(plc, _op050IntMap["设备在线状态"]) },
|
||
{ "ByPass", ReadPlcIntAsync(plc, _op050IntMap["ByPass"]) },
|
||
{ "生产模式", ReadPlcIntAsync(plc, _op050IntMap["生产模式"]) },
|
||
{ "托盘号", ReadPlcIntAsync(plc, _op050IntMap["托盘号"]) }
|
||
};
|
||
|
||
// DInt(双整数)读取任务(复用ReadPlcIntAsync,PLC底层兼容DInt转int)
|
||
var dIntReadTasks = new Dictionary<string, Task<int>>
|
||
{
|
||
{ "实际产量", ReadPlcIntAsync(plc, _op050IntMap["实际产量"]) },
|
||
{ "合格数量", ReadPlcIntAsync(plc, _op050IntMap["合格数量"]) },
|
||
{ "失败数量", ReadPlcIntAsync(plc, _op050IntMap["失败数量"]) }
|
||
};
|
||
|
||
// Real(浮点型)读取任务(新增ReadPlcRealAsync方法,返回float)
|
||
var realReadTasks = new Dictionary<string, Task<float>>
|
||
{
|
||
{ "节拍时间", ReadPlcRealAsync(plc, _op050IntMap["节拍时间"]) }
|
||
};
|
||
|
||
// 2. 并行等待所有读取任务完成(合并所有任务列表,单次WaitAll提升效率)
|
||
var allTasks = new List<Task>();
|
||
allTasks.AddRange(stringReadTasks.Values);
|
||
allTasks.AddRange(intReadTasks.Values);
|
||
allTasks.AddRange(dIntReadTasks.Values);
|
||
allTasks.AddRange(realReadTasks.Values);
|
||
await Task.WhenAll(allTasks).ConfigureAwait(false);
|
||
|
||
// 3. 提取读取结果(直接取值+空值兜底,任务已完成无阻塞风险)
|
||
// 字符串字段
|
||
string productModel = stringReadTasks["产品型号"].Result ?? string.Empty;
|
||
string productName = stringReadTasks["产品名称"].Result ?? string.Empty;
|
||
string product1SN = stringReadTasks["产品1SN"].Result ?? string.Empty;
|
||
string product2SN = stringReadTasks["产品2SN"].Result ?? string.Empty;
|
||
string product3SN = stringReadTasks["产品3SN"].Result ?? string.Empty;
|
||
string product4SN = stringReadTasks["产品4SN"].Result ?? string.Empty;
|
||
|
||
// 整数/双整数字段
|
||
int runStatus = intReadTasks["运行状态"].Result;
|
||
int machineModel = intReadTasks["设备模式"].Result;
|
||
int onlineStatus = intReadTasks["设备在线状态"].Result;
|
||
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;
|
||
|
||
// 浮点数字段
|
||
float cycleTime = realReadTasks["节拍时间"].Result;
|
||
|
||
// 5. 业务逻辑转换(极简+严谨)
|
||
string reworkFlag = produceModel == 4 ? "1" : "0";
|
||
string produceModelDesc = produceModel switch
|
||
{
|
||
1 => "正常模式",
|
||
2 => "清线模式",
|
||
4 => "返工模式",
|
||
8 => "换型模式",
|
||
16 => "预热模式",
|
||
_ => produceModel.ToString()
|
||
};
|
||
string onlineStatusDesc = onlineStatus == 1 ? "离线" : "在线";
|
||
// OP050无单个产品结果,合格标识按总产量逻辑:有失败则NG(2),否则OK(1)
|
||
bool allQualified = failedQty == 0 && actualOutput >= 0;
|
||
string qualificationFlag = "1";
|
||
|
||
// 6. 构建实体(精准匹配PlcProductionData字段,适配OP050特性)
|
||
return new PlcProductionData
|
||
{
|
||
PlcIp = ip,
|
||
OccurTime = DateTime.Now,
|
||
LineCode = "line2", // 按实际产线调整,建议后续改为参数
|
||
WorkstationCode = workstationCode,
|
||
WorkstationName = "点白胶DC744", // 补充工站名称
|
||
ProductModel = productModel,
|
||
ProductName = productName,
|
||
ProductCode = product1SN, // 主产品编码取产品1SN
|
||
ReworkFlag = reworkFlag,
|
||
Automanual = machineModel,
|
||
Runstatus = runStatus,
|
||
OnlineStatus = onlineStatusDesc,
|
||
ProduceModel = produceModelDesc,
|
||
TrayNo = trayNo.ToString(),
|
||
CreatedBy = "PLC",
|
||
CreatedTime = DateTime.Now,
|
||
// 4个产品SN(匹配实体字段)
|
||
Product1SN = product1SN,
|
||
Product2SN = product2SN,
|
||
Product3SN = product3SN,
|
||
Product4SN = product4SN,
|
||
|
||
ActualOutQty = actualOutput.ToString(),
|
||
QualifiedQty = qualifiedQty.ToString(),
|
||
FailedQty = failedQty.ToString(),
|
||
// 兼容原有SN字段
|
||
SN1 = product1SN,
|
||
SN2 = product2SN,
|
||
// OP050核心产量/节拍字段
|
||
ProductionCycle = (int)Math.Round(cycleTime), // 节拍时间转整数秒
|
||
QualificationFlag = qualificationFlag,
|
||
};
|
||
}
|
||
catch
|
||
{
|
||
Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] OP050读取异常");
|
||
return null;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 读取OP050-1数据(自动下料工位,DB1001)
|
||
/// </summary>
|
||
private async Task<PlcProductionData> ReadOP050_1DataAsync(Plc plc, string ip, string workstationCode)
|
||
{
|
||
// 前置校验:PLC连接无效直接返回,避免无效操作
|
||
if (plc == null || !plc.IsConnected)
|
||
{
|
||
return null;
|
||
}
|
||
|
||
try
|
||
{
|
||
// 1. 批量创建并行读取任务(按字段类型分类,最大化并行效率)
|
||
var stringReadTasks = new Dictionary<string, Task<string>>
|
||
{
|
||
{ "报警信息", ReadPlcStringAsync(plc, _op050_1StringMap["报警信息"].Addr, _op050_1StringMap["报警信息"].Len) },
|
||
{ "产品型号", ReadPlcStringAsync(plc, _op050_1StringMap["产品型号"].Addr, _op050_1StringMap["产品型号"].Len) },
|
||
{ "产品名称", ReadPlcStringAsync(plc, _op050_1StringMap["产品名称"].Addr, _op050_1StringMap["产品名称"].Len) }
|
||
};
|
||
|
||
var intReadTasks = new Dictionary<string, Task<int>>
|
||
{
|
||
{ "运行状态", ReadPlcIntAsync(plc, _op050_1IntMap["运行状态"]) },
|
||
{ "设备模式", ReadPlcIntAsync(plc, _op050_1IntMap["设备模式"]) },
|
||
{ "设备在线状态", ReadPlcIntAsync(plc, _op050_1IntMap["设备在线状态"]) },
|
||
{ "ByPass", ReadPlcIntAsync(plc, _op050_1IntMap["ByPass"]) },
|
||
{ "生产模式", ReadPlcIntAsync(plc, _op050_1IntMap["生产模式"]) }
|
||
};
|
||
|
||
// DInt(双整数)读取任务(产量相关)
|
||
var dIntReadTasks = new Dictionary<string, Task<int>>
|
||
{
|
||
{ "实际产量", ReadPlcIntAsync(plc, _op050_1IntMap["实际产量"]) },
|
||
{ "合格数量", ReadPlcIntAsync(plc, _op050_1IntMap["合格数量"]) },
|
||
{ "失败数量", ReadPlcIntAsync(plc, _op050_1IntMap["失败数量"]) }
|
||
};
|
||
|
||
// Real(浮点型)读取任务(节拍时间)
|
||
var realReadTasks = new Dictionary<string, Task<float>>
|
||
{
|
||
{ "节拍时间", ReadPlcRealAsync(plc, _op050_1IntMap["节拍时间"]) }
|
||
};
|
||
|
||
// 2. 并行等待所有读取任务完成(单次WaitAll,最小化等待时间)
|
||
var allTasks = new List<Task>();
|
||
allTasks.AddRange(stringReadTasks.Values);
|
||
allTasks.AddRange(intReadTasks.Values);
|
||
allTasks.AddRange(dIntReadTasks.Values);
|
||
allTasks.AddRange(realReadTasks.Values);
|
||
await Task.WhenAll(allTasks).ConfigureAwait(false);
|
||
|
||
// 3. 提取读取结果(直接取值+空值兜底,无阻塞风险)
|
||
// 字符串字段
|
||
string productModel = stringReadTasks["产品型号"].Result ?? string.Empty;
|
||
string productName = stringReadTasks["产品名称"].Result ?? string.Empty;
|
||
|
||
// 整数基础状态字段
|
||
int runStatus = intReadTasks["运行状态"].Result;
|
||
int machineModel = intReadTasks["设备模式"].Result;
|
||
int onlineStatus = intReadTasks["设备在线状态"].Result;
|
||
int produceModel = intReadTasks["生产模式"].Result;
|
||
|
||
// 产量相关字段(DInt)
|
||
int actualOutput = dIntReadTasks["实际产量"].Result;
|
||
int qualifiedQty = dIntReadTasks["合格数量"].Result;
|
||
int failedQty = dIntReadTasks["失败数量"].Result;
|
||
|
||
// 节拍时间(Real)
|
||
float cycleTime = realReadTasks["节拍时间"].Result;
|
||
|
||
// 4. 业务逻辑转换(极简+严谨)
|
||
string reworkFlag = produceModel == 4 ? "1" : "0"; // 返工模式标识
|
||
string produceModelDesc = produceModel switch
|
||
{
|
||
1 => "正常模式",
|
||
2 => "清线模式",
|
||
4 => "返工模式",
|
||
8 => "换型模式",
|
||
16 => "预热模式",
|
||
_ => produceModel.ToString()
|
||
};
|
||
string onlineStatusDesc = onlineStatus == 1 ? "离线" : "在线";
|
||
// 合格标识:失败数量为0且实际产量≥0则OK(1),否则NG(2)
|
||
bool allQualified = failedQty == 0 && actualOutput >= 0;
|
||
string qualificationFlag = allQualified ? "1" : "2";
|
||
|
||
// 5. 构建实体(精准匹配PlcProductionData字段,无冗余)
|
||
return new PlcProductionData
|
||
{
|
||
PlcIp = ip,
|
||
OccurTime = DateTime.Now,
|
||
LineCode = "line2", // 按实际产线调整,建议改为参数
|
||
WorkstationName = "自动下料", // 补充工站名称
|
||
WorkstationCode = workstationCode,
|
||
ProductModel = productModel,
|
||
ProductName = productName,
|
||
ProductCode = string.Empty,
|
||
ReworkFlag = reworkFlag,
|
||
Automanual = machineModel,
|
||
Runstatus = runStatus,
|
||
OnlineStatus = onlineStatusDesc,
|
||
ProduceModel = produceModelDesc,
|
||
TrayNo = string.Empty,
|
||
CreatedBy = "PLC",
|
||
CreatedTime = DateTime.Now,
|
||
ActualOutQty = actualOutput.ToString(),
|
||
QualifiedQty = qualifiedQty.ToString(),
|
||
FailedQty = failedQty.ToString(),
|
||
ProductionCycle = (int)Math.Round(cycleTime), // 节拍时间转整数秒
|
||
QualificationFlag = qualificationFlag,
|
||
// 备注记录产量信息(便于排查)
|
||
Remark = $"实际产量:{actualOutput},合格数量:{qualifiedQty},失败数量:{failedQty},节拍时间:{cycleTime:F2}秒"
|
||
};
|
||
}
|
||
catch(Exception ex)
|
||
{
|
||
// 异常静默处理,返回null避免上层崩溃
|
||
Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] OP050_1读取异常:{ex.Message}");
|
||
return null;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 读取OP051扫码工位数据(适配Coating扫码业务,无冗余逻辑)
|
||
/// </summary>
|
||
/// <param name="plc">PLC连接实例</param>
|
||
/// <param name="ip">PLC IP地址</param>
|
||
/// <param name="workstationCode">工位编码(如OP051)</param>
|
||
/// <returns>PLC生产数据实体</returns>
|
||
public async Task<PlcProductionData> ReadOP051ScanDataAsync(Plc plc, string ip, string workstationCode)
|
||
{
|
||
if (plc == null || !plc.IsConnected) return null;
|
||
if (string.IsNullOrWhiteSpace(ip)) throw new ArgumentNullException(nameof(ip), "PLC IP地址不能为空");
|
||
if (string.IsNullOrWhiteSpace(workstationCode)) throw new ArgumentNullException(nameof(workstationCode), "工位编码不能为空");
|
||
|
||
try
|
||
{
|
||
// 1. 批量创建并行读取任务(按类型分类,最大化并行效率)
|
||
var stringTasks = new Dictionary<string, Task<string>>
|
||
{
|
||
{ "产品型号", ReadPlcStringAsync(plc, _op051ScanStringMap["产品型号"].Addr, _op051ScanStringMap["产品型号"].Len) },
|
||
{ "产品名称", ReadPlcStringAsync(plc, _op051ScanStringMap["产品名称"].Addr, _op051ScanStringMap["产品名称"].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_3", ReadPlcStringAsync(plc, _op051ScanStringMap["SN_3"].Addr, _op051ScanStringMap["SN_3"].Len) }
|
||
};
|
||
|
||
var intTasks = new Dictionary<string, Task<int>>
|
||
{
|
||
{ "运行状态", ReadPlcIntAsync(plc, _op051ScanIntMap["运行状态"]) },
|
||
{ "设备模式", ReadPlcIntAsync(plc, _op051ScanIntMap["设备模式"]) },
|
||
{ "设备在线状态", ReadPlcIntAsync(plc, _op051ScanIntMap["设备在线状态"]) },
|
||
{ "生产模式", ReadPlcIntAsync(plc, _op051ScanIntMap["生产模式"]) },
|
||
{ "查询请求", ReadPlcIntAsync(plc, _op051ScanIntMap["查询请求"]) },
|
||
{ "上传结果请求", ReadPlcIntAsync(plc, _op051ScanIntMap["上传结果请求"]) },
|
||
{ "托盘号", ReadPlcIntAsync(plc, _op051ScanIntMap["托盘号"]) }
|
||
};
|
||
|
||
// DInt(双整数)读取任务(复用ReadPlcIntAsync,PLC底层兼容DInt转int)
|
||
var dIntReadTasks = new Dictionary<string, Task<int>>
|
||
{
|
||
{ "实际产量", ReadPlcIntAsync(plc, _op050IntMap["实际产量"]) },
|
||
{ "合格数量", ReadPlcIntAsync(plc, _op050IntMap["合格数量"]) },
|
||
{ "失败数量", 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);
|
||
|
||
// 3. 提取结果(直接取值,空值兜底,减少内存分配)
|
||
var now = DateTime.Now; // 单次获取时间,减少系统调用
|
||
var productModel = stringTasks["产品型号"].Result;
|
||
var productName = stringTasks["产品名称"].Result;
|
||
var sn1 = stringTasks["SN_1"].Result;
|
||
var sn2 = stringTasks["SN_2"].Result;
|
||
var sn3 = stringTasks["SN_3"].Result;
|
||
|
||
var runStatus = intTasks["运行状态"].Result;
|
||
var machineModel = intTasks["设备模式"].Result;
|
||
var onlineStatus = intTasks["设备在线状态"].Result;
|
||
var produceModel = intTasks["生产模式"].Result;
|
||
var queryRequest = intTasks["查询请求"].Result;
|
||
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
|
||
{
|
||
1 => "正常模式",
|
||
2 => "清线模式",
|
||
4 => "返工模式",
|
||
8 => "换型模式",
|
||
16 => "预热模式",
|
||
_ => $"未知模式({produceModel})"
|
||
};
|
||
var onlineStatusDesc = onlineStatus == 1 ? "离线" : "在线";
|
||
var queryRequestDesc = queryRequest != 0 ? "有查询请求" : "无查询请求";
|
||
var uploadRequestDesc = uploadRequest != 0 ? "有上传请求" : "无上传请求";
|
||
var qualificationFlag = "1"; // 扫码工位默认合格(无检测结果,可根据业务调整)
|
||
|
||
|
||
// 6. 构建并返回实体(适配扫码工位特性,精简字段)
|
||
return new PlcProductionData
|
||
{
|
||
PlcIp = ip,
|
||
OccurTime = now,
|
||
LineCode = "line2", // 可改为参数传入
|
||
WorkstationCode = workstationCode,
|
||
WorkstationName = "Coating扫码", // 补充工站名称
|
||
ProductModel = productModel,
|
||
ProductName = productName,
|
||
ProductCode = sn2, // 主SN用SN_1
|
||
ReworkFlag = reworkFlag,
|
||
Automanual = machineModel,
|
||
Runstatus = runStatus,
|
||
OnlineStatus = onlineStatusDesc,
|
||
ProduceModel = produceModelDesc,
|
||
TrayNo = trayNo,
|
||
SN1 = sn1,
|
||
SN2 = sn2,
|
||
Product1SN = sn1,
|
||
Product2SN = sn2,
|
||
Product3SN = sn3,
|
||
QualificationFlag = qualificationFlag,
|
||
ActualOutQty = actualOutput.ToString(),
|
||
QualifiedQty = qualifiedQty.ToString(),
|
||
FailedQty = failedQty.ToString(),
|
||
CreatedBy = "PLC",
|
||
CreatedTime = now,
|
||
};
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] OP051扫码工位读取异常:{ex.Message}");
|
||
return null;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 读取OP057数据(压装定位销&激光打标工位,DB1001)
|
||
/// </summary>
|
||
private async Task<PlcProductionData> ReadOP057DataAsync(Plc plc, string ip, string workstationCode)
|
||
{
|
||
// 前置校验:PLC连接无效直接返回
|
||
if (plc == null || !plc.IsConnected)
|
||
{
|
||
return null;
|
||
}
|
||
|
||
try
|
||
{
|
||
// 1. 批量创建并行读取任务
|
||
var stringReadTasks = new Dictionary<string, Task<string>>
|
||
{
|
||
{ "报警信息", ReadPlcStringAsync(plc, _op057StringMap["报警信息"].Addr, _op057StringMap["报警信息"].Len) },
|
||
{ "产品型号", ReadPlcStringAsync(plc, _op057StringMap["产品型号"].Addr, _op057StringMap["产品型号"].Len) },
|
||
{ "产品名称", ReadPlcStringAsync(plc, _op057StringMap["产品名称"].Addr, _op057StringMap["产品名称"].Len) },
|
||
{ "SN_1", ReadPlcStringAsync(plc, _op057StringMap["SN_1"].Addr, _op057StringMap["SN_1"].Len) },
|
||
{ "SN_2", ReadPlcStringAsync(plc, _op057StringMap["SN_2"].Addr, _op057StringMap["SN_2"].Len) }
|
||
};
|
||
|
||
var intReadTasks = new Dictionary<string, Task<int>>
|
||
{
|
||
{ "运行状态", ReadPlcIntAsync(plc, _op057IntMap["运行状态"]) },
|
||
{ "设备模式", ReadPlcIntAsync(plc, _op057IntMap["设备模式"]) },
|
||
{ "设备在线状态", ReadPlcIntAsync(plc, _op057IntMap["设备在线状态"]) },
|
||
{ "ByPass", ReadPlcIntAsync(plc, _op057IntMap["ByPass"]) },
|
||
{ "生产模式", ReadPlcIntAsync(plc, _op057IntMap["生产模式"]) },
|
||
{ "产品总结果", ReadPlcIntAsync(plc, _op057IntMap["产品总结果"]) },
|
||
{ "1#销结果", ReadPlcIntAsync(plc, _op057IntMap["1#销结果"]) },
|
||
{ "2#销结果", ReadPlcIntAsync(plc, _op057IntMap["2#销结果"]) }
|
||
};
|
||
|
||
var dIntReadTasks = new Dictionary<string, Task<int>>
|
||
{
|
||
{ "实际产量", ReadPlcIntAsync(plc, _op057IntMap["实际产量"]) },
|
||
{ "合格数量", ReadPlcIntAsync(plc, _op057IntMap["合格数量"]) },
|
||
{ "失败数量", ReadPlcIntAsync(plc, _op057IntMap["失败数量"]) }
|
||
};
|
||
|
||
var realReadTasks = new Dictionary<string, Task<float>>
|
||
{
|
||
{ "1#销压力", ReadPlcRealAsync(plc, _op057IntMap["1#销压力"]) },
|
||
{ "1#销行程", ReadPlcRealAsync(plc, _op057IntMap["1#销行程"]) },
|
||
{ "2#销压力", ReadPlcRealAsync(plc, _op057IntMap["2#销压力"]) },
|
||
{ "2#销行程", ReadPlcRealAsync(plc, _op057IntMap["2#销行程"]) },
|
||
{ "节拍时间", ReadPlcRealAsync(plc, _op057IntMap["节拍时间"]) }
|
||
};
|
||
|
||
// 2. 并行等待所有读取任务完成
|
||
var allTasks = new List<Task>();
|
||
allTasks.AddRange(stringReadTasks.Values);
|
||
allTasks.AddRange(intReadTasks.Values);
|
||
allTasks.AddRange(dIntReadTasks.Values);
|
||
allTasks.AddRange(realReadTasks.Values);
|
||
await Task.WhenAll(allTasks).ConfigureAwait(false);
|
||
|
||
// 3. 提取读取结果
|
||
string productModel = stringReadTasks["产品型号"].Result ?? string.Empty;
|
||
string productName = stringReadTasks["产品名称"].Result ?? string.Empty;
|
||
string sn1 = stringReadTasks["SN_1"].Result ?? string.Empty;
|
||
string sn2 = stringReadTasks["SN_2"].Result ?? string.Empty;
|
||
|
||
int runStatus = intReadTasks["运行状态"].Result;
|
||
int machineModel = intReadTasks["设备模式"].Result;
|
||
int onlineStatus = intReadTasks["设备在线状态"].Result;
|
||
int produceModel = intReadTasks["生产模式"].Result;
|
||
int productTotalResult = intReadTasks["产品总结果"].Result;
|
||
|
||
int pin1Result = intReadTasks["1#销结果"].Result;
|
||
int pin2Result = intReadTasks["2#销结果"].Result;
|
||
|
||
int actualOutput = dIntReadTasks["实际产量"].Result;
|
||
int qualifiedQty = dIntReadTasks["合格数量"].Result;
|
||
int failedQty = dIntReadTasks["失败数量"].Result;
|
||
|
||
float pin1Pressure = realReadTasks["1#销压力"].Result;
|
||
float pin1Stroke = realReadTasks["1#销行程"].Result;
|
||
float pin2Pressure = realReadTasks["2#销压力"].Result;
|
||
float pin2Stroke = realReadTasks["2#销行程"].Result;
|
||
float cycleTime = realReadTasks["节拍时间"].Result;
|
||
|
||
// 4. 业务逻辑转换
|
||
string reworkFlag = produceModel == 2 ? "1" : "0";
|
||
string produceModelDesc = produceModel switch
|
||
{
|
||
1 => "点检模式",
|
||
2 => "返工模式",
|
||
4 => "样件模式",
|
||
5 => "正常模式",
|
||
_ => produceModel.ToString()
|
||
};
|
||
string onlineStatusDesc = onlineStatus == 1 ? "离线" : "在线";
|
||
string qualificationFlag = productTotalResult == 1 ? "1" : "2";
|
||
|
||
|
||
// 5. 构建实体
|
||
return new PlcProductionData
|
||
{
|
||
PlcIp = ip,
|
||
OccurTime = DateTime.Now,
|
||
LineCode = "line2",
|
||
WorkstationCode = workstationCode,
|
||
WorkstationName = "压装定位销&激光打标", // 补充工站名称
|
||
ProductModel = productModel,
|
||
ProductName = productName,
|
||
ProductCode = sn1,
|
||
ReworkFlag = reworkFlag,
|
||
Automanual = machineModel,
|
||
Runstatus = runStatus,
|
||
OnlineStatus = onlineStatusDesc,
|
||
ProduceModel = produceModelDesc,
|
||
PinPressure1 = pin1Pressure.ToString(),
|
||
PinStroke1 = pin1Stroke.ToString(),
|
||
PinPressure2 = pin2Pressure.ToString(),
|
||
PinStroke2 = pin2Stroke.ToString(),
|
||
CreatedBy = "PLC",
|
||
CreatedTime = DateTime.Now,
|
||
SN1 = sn1,
|
||
SN2 = sn2,
|
||
Product1SN = sn1,
|
||
Product2SN = sn2,
|
||
Product1Result = pin1Result.ToString(),
|
||
Product2Result = pin2Result.ToString(),
|
||
QualificationFlag = qualificationFlag,
|
||
ActualOutQty = actualOutput.ToString(),
|
||
QualifiedQty = qualifiedQty.ToString(),
|
||
FailedQty = failedQty.ToString(),
|
||
ProductionCycle = (int)Math.Round(cycleTime),
|
||
|
||
};
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] OP057读取异常:{ex.Message}");
|
||
return null;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 读取OP058数据(压装定位销&激光打标工位)
|
||
/// </summary>
|
||
public async Task<PlcProductionData> ReadOP058DataAsync(Plc plc, string ip, string workstationCode)
|
||
{
|
||
// 前置校验:快速失败
|
||
if (plc == null || !plc.IsConnected) return null;
|
||
if (string.IsNullOrWhiteSpace(ip) || string.IsNullOrWhiteSpace(workstationCode))
|
||
throw new ArgumentNullException("IP或工位编码不能为空");
|
||
|
||
try
|
||
{
|
||
// 1. 批量创建并行读取任务
|
||
var stringTasks = new Dictionary<string, Task<string>>
|
||
{
|
||
{ "产品型号", ReadPlcStringAsync(plc, _op058StringMap["产品型号"].Addr, _op058StringMap["产品型号"].Len) },
|
||
{ "产品名称", ReadPlcStringAsync(plc, _op058StringMap["产品名称"].Addr, _op058StringMap["产品名称"].Len) },
|
||
{ "SN_1", ReadPlcStringAsync(plc, _op058StringMap["SN_1"].Addr, _op058StringMap["SN_1"].Len) }
|
||
};
|
||
|
||
var intTasks = new Dictionary<string, Task<int>>
|
||
{
|
||
{ "实际产量", ReadPlcIntAsync(plc, _op058IntMap["实际产量"]) },
|
||
{ "合格数量", ReadPlcIntAsync(plc, _op058IntMap["合格数量"]) },
|
||
{ "失败数量", ReadPlcIntAsync(plc, _op058IntMap["失败数量"]) },
|
||
{ "产品总结果", ReadPlcIntAsync(plc, _op058IntMap["产品总结果"]) },
|
||
{ "结果", ReadPlcIntAsync(plc, _op058IntMap["结果"]) }
|
||
};
|
||
|
||
var realTasks = new Dictionary<string, Task<float>>
|
||
{
|
||
{ "压力", ReadPlcRealAsync(plc, _op058IntMap["压力"]) },
|
||
{ "行程", ReadPlcRealAsync(plc, _op058IntMap["行程"]) },
|
||
{ "节拍时间", ReadPlcRealAsync(plc, _op058IntMap["节拍时间"]) }
|
||
};
|
||
|
||
// 2. 并行等待所有任务完成
|
||
var allTasks = new List<Task>();
|
||
allTasks.AddRange(stringTasks.Values);
|
||
allTasks.AddRange(intTasks.Values);
|
||
allTasks.AddRange(realTasks.Values);
|
||
await Task.WhenAll(allTasks).ConfigureAwait(false);
|
||
|
||
// 3. 提取结果
|
||
var now = DateTime.Now;
|
||
var productModel = stringTasks["产品型号"].Result ?? string.Empty;
|
||
var productName = stringTasks["产品名称"].Result ?? string.Empty;
|
||
var sn1 = stringTasks["SN_1"].Result ?? string.Empty;
|
||
|
||
var actualOutput = intTasks["实际产量"].Result;
|
||
var qualifiedQty = intTasks["合格数量"].Result;
|
||
var failedQty = intTasks["失败数量"].Result;
|
||
var productTotalResult = intTasks["产品总结果"].Result;
|
||
var result = intTasks["结果"].Result;
|
||
|
||
var pressure = realTasks["压力"].Result;
|
||
var stroke = realTasks["行程"].Result;
|
||
var cycleTime = realTasks["节拍时间"].Result;
|
||
|
||
// 4. 业务逻辑转换
|
||
var qualificationFlag = productTotalResult == 1 ? "1" : "2";
|
||
|
||
// 5. 构建实体
|
||
return new PlcProductionData
|
||
{
|
||
PlcIp = ip,
|
||
OccurTime = now,
|
||
LineCode = "line2",
|
||
WorkstationCode = workstationCode,
|
||
WorkstationName = "压装定位销&激光打标", // 补充工站名称
|
||
ProductModel = productModel,
|
||
ProductName = productName,
|
||
ProductCode = sn1,
|
||
SN1 = sn1,
|
||
Product1SN = sn1,
|
||
Product1Result = result.ToString(),
|
||
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,
|
||
};
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] OP058读取异常:{ex.Message}");
|
||
return null;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 读取OP060数据(高低压接头拧紧工位,DB1001)
|
||
/// </summary>
|
||
public async Task<PlcProductionData> ReadOP060DataAsync(Plc plc, string ip, string workstationCode)
|
||
{
|
||
// 前置校验:快速失败
|
||
if (plc == null || !plc.IsConnected) return null;
|
||
if (string.IsNullOrWhiteSpace(ip) || string.IsNullOrWhiteSpace(workstationCode))
|
||
throw new ArgumentNullException("IP或工位编码不能为空");
|
||
|
||
try
|
||
{
|
||
// 1. 批量创建并行读取任务
|
||
var stringTasks = new Dictionary<string, Task<string>>
|
||
{
|
||
{ "产品型号", ReadPlcStringAsync(plc, _op060StringMap["产品型号"].Addr, _op060StringMap["产品型号"].Len) },
|
||
{ "产品名称", ReadPlcStringAsync(plc, _op060StringMap["产品名称"].Addr, _op060StringMap["产品名称"].Len) },
|
||
{ "SN_1", ReadPlcStringAsync(plc, _op060StringMap["SN_1"].Addr, _op060StringMap["SN_1"].Len) },
|
||
{ "SN_2", ReadPlcStringAsync(plc, _op060StringMap["SN_2"].Addr, _op060StringMap["SN_2"].Len) }
|
||
};
|
||
|
||
var intTasks = new Dictionary<string, Task<int>>
|
||
{
|
||
{ "运行状态", ReadPlcIntAsync(plc, _op060IntMap["运行状态"]) },
|
||
{ "设备模式", ReadPlcIntAsync(plc, _op060IntMap["设备模式"]) },
|
||
{ "设备在线状态", ReadPlcIntAsync(plc, _op060IntMap["设备在线状态"]) },
|
||
{ "生产模式", ReadPlcIntAsync(plc, _op060IntMap["生产模式"]) },
|
||
{ "实际产量", ReadPlcIntAsync(plc, _op060IntMap["实际产量"]) },
|
||
{ "合格数量", ReadPlcIntAsync(plc, _op060IntMap["合格数量"]) },
|
||
{ "失败数量", ReadPlcIntAsync(plc, _op060IntMap["失败数量"]) },
|
||
{ "产品总结果", ReadPlcIntAsync(plc, _op060IntMap["产品总结果"]) },
|
||
{ "1#螺丝结果", ReadPlcIntAsync(plc, _op060IntMap["1#螺丝结果"]) },
|
||
{ "2#螺丝结果", ReadPlcIntAsync(plc, _op060IntMap["2#螺丝结果"]) },
|
||
{ "3#螺丝结果", ReadPlcIntAsync(plc, _op060IntMap["3#螺丝结果"]) },
|
||
{ "4#螺丝结果", ReadPlcIntAsync(plc, _op060IntMap["4#螺丝结果"]) }
|
||
};
|
||
|
||
var realTasks = new Dictionary<string, Task<float>>
|
||
{
|
||
{ "1#螺丝扭矩", ReadPlcRealAsync(plc, _op060IntMap["1#螺丝扭矩"]) },
|
||
{ "1#螺丝深度", ReadPlcRealAsync(plc, _op060IntMap["1#螺丝深度"]) },
|
||
{ "1#螺丝角度", ReadPlcRealAsync(plc, _op060IntMap["1#螺丝角度"]) },
|
||
{ "2#螺丝扭矩", ReadPlcRealAsync(plc, _op060IntMap["2#螺丝扭矩"]) },
|
||
{ "2#螺丝深度", ReadPlcRealAsync(plc, _op060IntMap["2#螺丝深度"]) },
|
||
{ "2#螺丝角度", ReadPlcRealAsync(plc, _op060IntMap["2#螺丝角度"]) },
|
||
{ "3#螺丝扭矩", ReadPlcRealAsync(plc, _op060IntMap["3#螺丝扭矩"]) },
|
||
{ "3#螺丝深度", ReadPlcRealAsync(plc, _op060IntMap["3#螺丝深度"]) },
|
||
{ "3#螺丝角度", ReadPlcRealAsync(plc, _op060IntMap["3#螺丝角度"]) },
|
||
{ "4#螺丝扭矩", ReadPlcRealAsync(plc, _op060IntMap["4#螺丝扭矩"]) },
|
||
{ "4#螺丝深度", ReadPlcRealAsync(plc, _op060IntMap["4#螺丝深度"]) },
|
||
{ "4#螺丝角度", ReadPlcRealAsync(plc, _op060IntMap["4#螺丝角度"]) },
|
||
{ "节拍时间", ReadPlcRealAsync(plc, _op060IntMap["节拍时间"]) }
|
||
};
|
||
|
||
// 2. 并行等待所有任务完成
|
||
var allTasks = new List<Task>();
|
||
allTasks.AddRange(stringTasks.Values);
|
||
allTasks.AddRange(intTasks.Values);
|
||
allTasks.AddRange(realTasks.Values);
|
||
await Task.WhenAll(allTasks).ConfigureAwait(false);
|
||
|
||
// 3. 提取结果
|
||
var now = DateTime.Now;
|
||
var productModel = stringTasks["产品型号"].Result ?? string.Empty;
|
||
var productName = stringTasks["产品名称"].Result ?? string.Empty;
|
||
var sn1 = stringTasks["SN_1"].Result ?? string.Empty;
|
||
var sn2 = stringTasks["SN_2"].Result ?? string.Empty;
|
||
|
||
var runStatus = intTasks["运行状态"].Result;
|
||
var machineModel = intTasks["设备模式"].Result;
|
||
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;
|
||
var screw3Result = intTasks["3#螺丝结果"].Result;
|
||
var screw4Result = intTasks["4#螺丝结果"].Result;
|
||
|
||
var screw1Torque = realTasks["1#螺丝扭矩"].Result;
|
||
var screw1Depth = realTasks["1#螺丝深度"].Result;
|
||
var screw1Angle = realTasks["1#螺丝角度"].Result;
|
||
var screw2Torque = realTasks["2#螺丝扭矩"].Result;
|
||
var screw2Depth = realTasks["2#螺丝深度"].Result;
|
||
var screw2Angle = realTasks["2#螺丝角度"].Result;
|
||
var screw3Torque = realTasks["3#螺丝扭矩"].Result;
|
||
var screw3Depth = realTasks["3#螺丝深度"].Result;
|
||
var screw3Angle = realTasks["3#螺丝角度"].Result;
|
||
var screw4Torque = realTasks["4#螺丝扭矩"].Result;
|
||
var screw4Depth = realTasks["4#螺丝深度"].Result;
|
||
var screw4Angle = realTasks["4#螺丝角度"].Result;
|
||
var cycleTime = realTasks["节拍时间"].Result;
|
||
|
||
// 4. 业务逻辑转换
|
||
var reworkFlag = produceModel == 2 ? "1" : "0";
|
||
var produceModelDesc = produceModel switch
|
||
{
|
||
1 => "点检模式",
|
||
2 => "返工模式",
|
||
4 => "样件模式",
|
||
5 => "正常模式",
|
||
_ => produceModel.ToString()
|
||
};
|
||
var onlineStatusDesc = onlineStatus == 1 ? "离线" : "在线";
|
||
var qualificationFlag = productTotalResult == 1 ? "1" : "2";
|
||
|
||
|
||
// 6. 构建实体
|
||
// 6. 构建完整实体(覆盖所有螺丝参数字段)
|
||
return new PlcProductionData
|
||
{
|
||
// 基础字段
|
||
PlcIp = ip,
|
||
OccurTime = now,
|
||
LineCode = "line2",
|
||
WorkstationCode = workstationCode,
|
||
WorkstationName = "高低压接头拧紧工位", // 补充工站名称
|
||
ProductModel = productModel,
|
||
ProductName = productName,
|
||
ProductCode = sn1,
|
||
SN1 = sn1,
|
||
SN2 = sn2,
|
||
ReworkFlag = reworkFlag,
|
||
Automanual = machineModel,
|
||
Runstatus = runStatus,
|
||
OnlineStatus = onlineStatusDesc,
|
||
ProduceModel = produceModelDesc,
|
||
QualificationFlag = qualificationFlag,
|
||
ActualOutQty = actualOutput.ToString(),
|
||
QualifiedQty = qualifiedQty.ToString(),
|
||
FailedQty = failedQty.ToString(),
|
||
ProductionCycle = (int)Math.Round(cycleTime),
|
||
CreatedBy = "PLC",
|
||
CreatedTime = now,
|
||
|
||
// 螺丝1参数(完整赋值)
|
||
Screw1Result = screw1Result.ToString(),
|
||
Screw1Torque = screw1Torque.ToString("F2"),
|
||
Screw1Depth = screw1Depth.ToString("F2"),
|
||
Screw1Angle = screw1Angle.ToString("F2"),
|
||
Screw1TightenTime = realTasks["1#螺丝时间"].Result.ToString("F2"),
|
||
|
||
// 螺丝2参数(完整赋值)
|
||
Screw2Result = screw2Result.ToString(),
|
||
Screw2Torque = screw2Torque.ToString("F2"),
|
||
Screw2Depth = screw2Depth.ToString("F2"),
|
||
Screw2Angle = screw2Angle.ToString("F2"),
|
||
Screw2TightenTime = realTasks["2#螺丝时间"].Result.ToString("F2"),
|
||
|
||
// 螺丝3参数(完整赋值)
|
||
Screw3Result = screw3Result.ToString(),
|
||
Screw3Torque = screw3Torque.ToString("F2"),
|
||
Screw3Depth = screw3Depth.ToString("F2"),
|
||
Screw3Angle = screw3Angle.ToString("F2"),
|
||
Screw3TightenTime = realTasks["3#螺丝时间"].Result.ToString("F2"),
|
||
|
||
// 螺丝4参数(完整赋值)
|
||
Screw4Result = screw4Result.ToString(),
|
||
Screw4Torque = screw4Torque.ToString("F2"),
|
||
Screw4Depth = screw4Depth.ToString("F2"),
|
||
Screw4Angle = screw4Angle.ToString("F2"),
|
||
Screw4TightenTime = realTasks["4#螺丝时间"].Result.ToString("F2"),
|
||
};
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] OP060读取异常:{ex.Message}");
|
||
return null;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// OP070-1数据读取 - 效率优化版
|
||
/// </summary>
|
||
private async Task<PlcProductionData?> ReadOP070_1DataAsync(Plc plc, string ip, string workstationCode)
|
||
{
|
||
// 前置核心校验:避免无效PLC操作(节省10+ms无效耗时)
|
||
if (plc == null || !plc.IsConnected)
|
||
{
|
||
Console.WriteLine($"OP070-1({ip})PLC连接无效,跳过数据读取");
|
||
// 若业务允许,可返回空实体而非抛异常,根据实际场景调整
|
||
return new PlcProductionData { PlcIp = ip, WorkstationCode = workstationCode };
|
||
}
|
||
|
||
try
|
||
{
|
||
// 1. 真正的并行读取所有字段(核心效率优化)
|
||
// ========== 字符串字段并行任务 ==========
|
||
var taskProductName = ReadPlcStringAsync(plc, _op070_1StringMap["产品名称"].Addr, _op070_1StringMap["产品名称"].Len);
|
||
var taskOrderName = ReadPlcStringAsync(plc, _op070_1StringMap["订单名称"].Addr, _op070_1StringMap["订单名称"].Len);
|
||
var taskProductModel = ReadPlcStringAsync(plc, _op070_1StringMap["产品型号"].Addr, _op070_1StringMap["产品型号"].Len);
|
||
var taskSN1 = ReadPlcStringAsync(plc, _op070_1StringMap["SN_1"].Addr, _op070_1StringMap["SN_1"].Len);
|
||
var taskSN2 = ReadPlcStringAsync(plc, _op070_1StringMap["SN_2"].Addr, _op070_1StringMap["SN_2"].Len);
|
||
|
||
// ========== Int字段并行任务 ==========
|
||
var taskRunStatus = ReadPlcIntAsync(plc, _op070_1IntMap["运行状态"]);
|
||
var taskMachineModel = ReadPlcIntAsync(plc, _op070_1IntMap["设备模式"]);
|
||
var taskOnlineStatus = ReadPlcIntAsync(plc, _op070_1IntMap["设备在线状态"]);
|
||
var taskByPass = ReadPlcIntAsync(plc, _op070_1IntMap["ByPass"]);
|
||
var taskProduceModel = ReadPlcIntAsync(plc, _op070_1IntMap["生产模式"]);
|
||
//var taskQueryReq = ReadPlcIntAsync(plc, _op070_1IntMap["查询请求"]);
|
||
//var taskSaveReq = ReadPlcIntAsync(plc, _op070_1IntMap["保存请求"]);
|
||
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["节拍时间"]);
|
||
|
||
// 2. 等待所有并行任务完成(核心:耗时=最慢的单个读取任务,而非总和)
|
||
await Task.WhenAll(
|
||
// 字符串任务
|
||
taskProductName, taskOrderName, taskProductModel, taskSN1, taskSN2,
|
||
// Int任务
|
||
taskRunStatus, taskMachineModel, taskOnlineStatus, taskByPass, taskProduceModel,
|
||
taskTrayNo, taskCameraResult, taskStationResult,
|
||
// Real任务
|
||
taskCycleTime,
|
||
// DInt任务
|
||
taskActualOutput, taskQualifiedQty, taskFailedQty);
|
||
|
||
// 3. 获取并行读取结果(带空值兜底,避免后续空引用)
|
||
// 字符串字段
|
||
string productName = await taskProductName ?? string.Empty;
|
||
string orderName = await taskOrderName ?? string.Empty;
|
||
string productModel = await taskProductModel ?? string.Empty;
|
||
string sn1 = await taskSN1 ?? string.Empty;
|
||
string sn2 = await taskSN2 ?? string.Empty;
|
||
|
||
// Int字段
|
||
int runStatus = await taskRunStatus;
|
||
int machineModel = await taskMachineModel;
|
||
int onlineStatus = await taskOnlineStatus;
|
||
int byPass = await taskByPass;
|
||
int produceModel = await taskProduceModel;
|
||
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["保存请求"]);
|
||
|
||
// 5. 业务逻辑计算(优化:减少冗余字符串创建)
|
||
var reworkFlag = produceModel == 4 ? "1" : "0";
|
||
string produceModelDesc = produceModel switch
|
||
{
|
||
1 => "正常模式",
|
||
2 => "清线模式",
|
||
4 => "返工模式",
|
||
8 => "换型模式",
|
||
16 => "预热模式",
|
||
_ => $"未知({produceModel})"
|
||
};
|
||
string runStatusDesc = runStatus switch { 1 => "空闲", 2 => "运行中", 3 => "故障", _ => $"未知({runStatus})" };
|
||
string onlineStatusDesc = onlineStatus == 1 ? "离线" : "在线";
|
||
string qualificationFlag = stationResult switch { 1 => "1", 2 => "0", _ => stationResult.ToString() };
|
||
string cameraResultDesc = cameraResult switch { 1 => "OK", 2 => "NG", _ => $"未知({cameraResult})" };
|
||
|
||
// 调试日志:优化字符串拼接,保留核心信息
|
||
Console.WriteLine($"OP070-1({ip})读取结果:产品型号={productModel},运行状态={runStatusDesc},订单名称={orderName}");
|
||
|
||
// 6. 构建数据实体(优化:减少冗余操作,提前兜底)
|
||
return new PlcProductionData
|
||
{
|
||
PlcIp = ip, // 外层已校验ip非空,无需Trim(减少字符串操作)
|
||
OccurTime = DateTime.Now,
|
||
LineCode = "line2",
|
||
WorkstationName = "点散热胶",
|
||
WorkstationCode = workstationCode,
|
||
ProductModel = productModel,
|
||
ProductName = productName,
|
||
ProductCode = sn2,
|
||
SN1 = sn1,
|
||
SN2 = sn2,
|
||
QualificationFlag = qualificationFlag,
|
||
ActualOutQty = actualOutput.ToString(),
|
||
QualifiedQty = qualifiedQty.ToString(),
|
||
FailedQty = failedQty.ToString(),
|
||
ReworkFlag = reworkFlag,
|
||
Automanual = machineModel,
|
||
Runstatus = runStatus,
|
||
OnlineStatus = onlineStatusDesc,
|
||
ProduceModel = produceModelDesc,
|
||
TrayNo = trayNo.ToString(),
|
||
CameraResult = cameraResultDesc,
|
||
ProductionCycle = (int?)cycleTime,
|
||
CreatedBy = "PLC",
|
||
CreatedTime = DateTime.Now
|
||
};
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
// 增强异常日志:包含堆栈,便于定位问题
|
||
Console.WriteLine($"OP070-1({ip})数据读取异常:{ex.Message}\n{ex.StackTrace}");
|
||
return null;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// OP075数据读取
|
||
/// </summary>
|
||
private async Task<PlcProductionData?> ReadOP075DataAsync(Plc plc, string ip, string workstationCode)
|
||
{
|
||
// 前置核心校验:避免无效PLC操作(节省10+ms无效耗时)
|
||
if (plc == null || !plc.IsConnected)
|
||
{
|
||
Console.WriteLine($"OP075({ip})PLC连接无效,跳过数据读取");
|
||
// 返回基础实体避免业务空引用,可根据实际场景调整为return null
|
||
return new PlcProductionData { PlcIp = ip, WorkstationCode = workstationCode };
|
||
}
|
||
|
||
try
|
||
{
|
||
// 1. 真正的并行读取所有字段(核心效率优化)
|
||
// ========== 字符串字段并行任务 ==========
|
||
var taskProductName = ReadPlcStringAsync(plc, _op075StringMap["产品名称"].Addr, _op075StringMap["产品名称"].Len);
|
||
var taskOrderName = ReadPlcStringAsync(plc, _op075StringMap["订单名称"].Addr, _op075StringMap["订单名称"].Len);
|
||
var taskProductModel = ReadPlcStringAsync(plc, _op075StringMap["产品型号"].Addr, _op075StringMap["产品型号"].Len);
|
||
var taskSN1 = ReadPlcStringAsync(plc, _op075StringMap["SN_1"].Addr, _op075StringMap["SN_1"].Len);
|
||
var taskSN2 = ReadPlcStringAsync(plc, _op075StringMap["SN_2"].Addr, _op075StringMap["SN_2"].Len);
|
||
var taskChipSN = ReadPlcStringAsync(plc, _op075StringMap["芯片SN"].Addr, _op075StringMap["芯片SN"].Len);
|
||
|
||
// ========== Int字段并行任务 ==========
|
||
var taskRunStatus = ReadPlcIntAsync(plc, _op075IntMap["运行状态"]);
|
||
var taskMachineModel = ReadPlcIntAsync(plc, _op075IntMap["设备模式"]);
|
||
var taskOnlineStatus = ReadPlcIntAsync(plc, _op075IntMap["设备在线状态"]);
|
||
var taskByPass = ReadPlcIntAsync(plc, _op075IntMap["ByPass"]);
|
||
var taskProduceModel = ReadPlcIntAsync(plc, _op075IntMap["生产模式"]);
|
||
var taskTrayNo = ReadPlcIntAsync(plc, _op075IntMap["托盘号"]);
|
||
var taskStationResult = ReadPlcIntAsync(plc, _op075IntMap["站位结果"]);
|
||
|
||
// ========== 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(
|
||
// 字符串任务
|
||
taskProductName, taskOrderName, taskProductModel, taskSN1, taskSN2, taskChipSN,
|
||
// Int任务
|
||
taskRunStatus, taskMachineModel, taskOnlineStatus, taskByPass, taskProduceModel,
|
||
taskTrayNo, taskStationResult,
|
||
// Real任务
|
||
taskCycleTime
|
||
// DInt任务
|
||
, taskActualOutput, taskQualifiedQty, taskFailedQty
|
||
);
|
||
|
||
// 3. 获取并行读取结果(带空值兜底,避免后续空引用)
|
||
// 字符串字段
|
||
string productName = await taskProductName ?? string.Empty;
|
||
string orderName = await taskOrderName ?? string.Empty;
|
||
string productModel = await taskProductModel ?? string.Empty;
|
||
string sn1 = await taskSN1 ?? string.Empty;
|
||
string sn2 = await taskSN2 ?? string.Empty;
|
||
string chipsn = await taskChipSN ?? string.Empty;
|
||
|
||
// Int字段
|
||
int runStatus = await taskRunStatus;
|
||
int machineModel = await taskMachineModel;
|
||
int onlineStatus = await taskOnlineStatus;
|
||
int byPass = await taskByPass;
|
||
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;
|
||
|
||
// 4. 异步复位保存请求(非阻塞,不影响读取效率)
|
||
//_ = ResetSaveRequestAsync(plc, ip, _op075IntMap["保存请求"]);
|
||
|
||
// 5. 业务逻辑计算(优化:减少冗余字符串创建)
|
||
var reworkFlag = produceModel == 4 ? "1" : "0";
|
||
string produceModelDesc = produceModel switch
|
||
{
|
||
1 => "正常模式",
|
||
2 => "清线模式",
|
||
4 => "返工模式",
|
||
8 => "换型模式",
|
||
16 => "预热模式",
|
||
_ => $"未知({produceModel})"
|
||
};
|
||
string runStatusDesc = runStatus switch { 1 => "空闲", 2 => "运行中", 3 => "故障", _ => $"未知({runStatus})" };
|
||
string onlineStatusDesc = onlineStatus == 1 ? "离线" : "在线";
|
||
string qualificationFlag = stationResult switch { 1 => "1", 2 => "0", _ => stationResult.ToString() };
|
||
|
||
// 调试日志:优化字符串拼接,保留核心信息
|
||
Console.WriteLine($"OP075({ip})读取结果:产品型号={productModel},运行状态={runStatusDesc},订单名称={orderName}");
|
||
|
||
// 6. 构建数据实体(优化:减少冗余操作,提前兜底)
|
||
return new PlcProductionData
|
||
{
|
||
PlcIp = ip, // 外层已校验ip非空,无需Trim(减少字符串操作)
|
||
OccurTime = DateTime.Now,
|
||
LineCode = "line2",
|
||
WorkstationCode = workstationCode,
|
||
WorkstationName = "PWM折弯&装配",
|
||
ProductModel = productModel,
|
||
ProductName = productName,
|
||
ProductCode = sn2,
|
||
SN1 = sn1,
|
||
SN2 = sn2,
|
||
ChipSN = chipsn, // 保留芯片SN独有字段
|
||
QualificationFlag = qualificationFlag,
|
||
ActualOutQty = actualOutput.ToString(),
|
||
QualifiedQty = qualifiedQty.ToString(),
|
||
FailedQty = failedQty.ToString(),
|
||
ReworkFlag = reworkFlag,
|
||
Automanual = machineModel,
|
||
Runstatus = runStatus,
|
||
OnlineStatus = onlineStatusDesc,
|
||
ProduceModel = produceModelDesc,
|
||
TrayNo = trayNo.ToString(),
|
||
ProductionCycle = (int?)cycleTime,
|
||
CreatedBy = "PLC",
|
||
CreatedTime = DateTime.Now
|
||
};
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
// 增强异常日志:包含堆栈,便于定位问题
|
||
Console.WriteLine($"OP075({ip})数据读取异常:{ex.Message}");
|
||
// 异常时返回基础实体,避免业务空引用
|
||
return null;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 读取OP080-1数据 - 极致优化版(批量并行+内存复用+异步优化)
|
||
/// </summary>
|
||
/// <param name="plc">PLC实例</param>
|
||
/// <param name="ip">PLC IP</param>
|
||
/// <param name="workstationCode">工位编码</param>
|
||
/// <param name="strSaveRequest">保存请求类型</param>
|
||
/// <returns>PLC生产数据</returns>
|
||
private async Task<PlcProductionData?> ReadOP080_1DataAsync(Plc plc, string ip, string workstationCode, string strSaveRequest)
|
||
{
|
||
// 前置核心校验:避免无效PLC操作(节省10+ms无效耗时)
|
||
if (plc == null || !plc.IsConnected)
|
||
{
|
||
return null;
|
||
}
|
||
|
||
try
|
||
{
|
||
// ========== 阶段1:批量创建并行任务(按类型分组,减少分散管理) ==========
|
||
// 1.1 字符串字段任务(6个)- 批量存储
|
||
var stringTasks = new List<Task<string>>
|
||
{
|
||
ReadPlcStringAsync(plc, _op080_1StringMap["订单名称"].Addr, _op080_1StringMap["订单名称"].Len),
|
||
ReadPlcStringAsync(plc, _op080_1StringMap["产品名称"].Addr, _op080_1StringMap["产品名称"].Len),
|
||
ReadPlcStringAsync(plc, _op080_1StringMap["合装位机壳_SN"].Addr, _op080_1StringMap["合装位机壳_SN"].Len),
|
||
ReadPlcStringAsync(plc, _op080_1StringMap["合装位PCB_SN"].Addr, _op080_1StringMap["合装位PCB_SN"].Len),
|
||
ReadPlcStringAsync(plc, _op080_1StringMap["拧紧位机壳_SN"].Addr, _op080_1StringMap["拧紧位机壳_SN"].Len),
|
||
ReadPlcStringAsync(plc, _op080_1StringMap["拧紧位PCB_SN"].Addr, _op080_1StringMap["拧紧位PCB_SN"].Len)
|
||
};
|
||
|
||
// 1.2 Int字段任务(17个)- 批量存储
|
||
var intTasks = new List<Task<int>>
|
||
{
|
||
// 基础状态字段
|
||
ReadPlcIntAsync(plc, _op080_1IntMap["运行状态"]),
|
||
ReadPlcIntAsync(plc, _op080_1IntMap["设备模式"]),
|
||
ReadPlcIntAsync(plc, _op080_1IntMap["设备在线状态"]),
|
||
ReadPlcIntAsync(plc, _op080_1IntMap["ByPass"]),
|
||
ReadPlcIntAsync(plc, _op080_1IntMap["生产模式"]),
|
||
// 请求/托盘字段
|
||
ReadPlcIntAsync(plc, _op080_1IntMap["合装工位查询请求"]),
|
||
ReadPlcIntAsync(plc, _op080_1IntMap["合装结果保存请求"]),
|
||
ReadPlcIntAsync(plc, _op080_1IntMap["拧紧结果保存请求"]),
|
||
ReadPlcIntAsync(plc, _op080_1IntMap["合装位托盘号"]),
|
||
ReadPlcIntAsync(plc, _op080_1IntMap["拧紧位托盘号"]),
|
||
// 螺钉结果字段(7个)
|
||
ReadPlcIntAsync(plc, _op080_1IntMap["1号螺钉_结果"]),
|
||
ReadPlcIntAsync(plc, _op080_1IntMap["2号螺钉_结果"]),
|
||
ReadPlcIntAsync(plc, _op080_1IntMap["3号螺钉_结果"]),
|
||
ReadPlcIntAsync(plc, _op080_1IntMap["4号螺钉_结果"]),
|
||
ReadPlcIntAsync(plc, _op080_1IntMap["5号螺钉_结果"]),
|
||
ReadPlcIntAsync(plc, _op080_1IntMap["6号螺钉_结果"]),
|
||
ReadPlcIntAsync(plc, _op080_1IntMap["7号螺钉_结果"])
|
||
};
|
||
|
||
// DInt(双整数)读取任务(复用ReadPlcIntAsync,PLC底层兼容DInt转int)
|
||
var dIntReadTasks = new Dictionary<string, Task<int>>
|
||
{
|
||
{ "实际产量", ReadPlcIntAsync(plc, _op080_1IntMap["实际产量"]) },
|
||
{ "合格数量", ReadPlcIntAsync(plc, _op080_1IntMap["合格数量"]) },
|
||
{ "失败数量", ReadPlcIntAsync(plc, _op080_1IntMap["失败数量"]) }
|
||
};
|
||
|
||
// 1.3 Real字段任务(28个)- 按螺钉分组,批量存储(减少硬编码)
|
||
var realTasks = new List<Task<float>>();
|
||
// 预定义螺钉字段前缀(复用数组,减少GC)
|
||
var screwPrefixes = new[] { "1号螺钉_", "2号螺钉_", "3号螺钉_", "4号螺钉_", "5号螺钉_", "6号螺钉_", "7号螺钉_" };
|
||
var screwDimensions = new[] { "扭矩", "深度", "角度", "拧紧时间" };
|
||
foreach (var prefix in screwPrefixes)
|
||
{
|
||
foreach (var dim in screwDimensions)
|
||
{
|
||
realTasks.Add(ReadPlcRealAsync(plc, _op080_1IntMap[$"{prefix}{dim}"]));
|
||
}
|
||
}
|
||
|
||
// ========== 阶段2:批量等待任务完成(核心优化:异步上下文取消) ==========
|
||
var allTasks = new List<Task>();
|
||
allTasks.AddRange(stringTasks); // 字符串任务(Task<string> 继承自 Task)
|
||
allTasks.AddRange(intTasks); // Int任务(Task<int> 继承自 Task)
|
||
allTasks.AddRange(realTasks); // Real任务(Task<float> 继承自 Task)
|
||
allTasks.AddRange(dIntReadTasks.Values);
|
||
await Task.WhenAll(allTasks).ConfigureAwait(false); // 取消上下文捕获,提升性能
|
||
|
||
// ========== 阶段3:批量获取结果(索引访问,减少临时变量) ==========
|
||
// 3.1 字符串结果(索引对应创建顺序)
|
||
string orderName = stringTasks[0].Result ?? string.Empty;
|
||
string productName = stringTasks[1].Result ?? string.Empty;
|
||
string hzMachineSN = stringTasks[2].Result ?? string.Empty;
|
||
string hzPcbsn = stringTasks[3].Result ?? string.Empty;
|
||
string njMachineSN = stringTasks[4].Result ?? string.Empty;
|
||
string njPcbsn = stringTasks[5].Result ?? string.Empty;
|
||
|
||
// 3.2 Int结果(索引对应创建顺序)
|
||
int runStatus = intTasks[0].Result;
|
||
int machineModel = intTasks[1].Result;
|
||
int onlineStatus = intTasks[2].Result;
|
||
int byPass = intTasks[3].Result;
|
||
int produceModel = intTasks[4].Result;
|
||
int hzQueryReq = intTasks[5].Result;
|
||
int hzSaveReq = intTasks[6].Result;
|
||
int njSaveReq = intTasks[7].Result;
|
||
int hzTrayNo = intTasks[8].Result;
|
||
int njTrayNo = intTasks[9].Result;
|
||
// 螺钉结果(索引10-16)
|
||
int[] screwResults = new[]
|
||
{
|
||
intTasks[10].Result, intTasks[11].Result, intTasks[12].Result,
|
||
intTasks[13].Result, intTasks[14].Result, intTasks[15].Result, intTasks[16].Result
|
||
};
|
||
|
||
int actualOutput = dIntReadTasks["实际产量"].Result;
|
||
int qualifiedQty = dIntReadTasks["合格数量"].Result;
|
||
int failedQty = dIntReadTasks["失败数量"].Result;
|
||
|
||
// 3.3 Real结果(按螺钉维度批量读取,索引0-27:7螺钉×4维度)
|
||
float[] realResults = realTasks.Select(t => t.Result).ToArray();
|
||
// 按螺钉分组提取(复用数组,减少临时变量)
|
||
float[][] screwRealData = new float[7][];
|
||
for (int i = 0; i < 7; i++)
|
||
{
|
||
screwRealData[i] = realResults.Skip(i * 4).Take(4).ToArray();
|
||
}
|
||
|
||
// ========== 阶段4:精简业务逻辑(减少GC+CPU开销) ==========
|
||
// 4.1 状态描述(复用静态字典,减少switch开销)
|
||
string produceModelDesc = produceModel switch
|
||
{
|
||
1 => "正常模式",
|
||
2 => "清线模式",
|
||
4 => "返工模式",
|
||
8 => "换型模式",
|
||
16 => "预热模式",
|
||
_ => $"未知({produceModel})"
|
||
};
|
||
string runStatusDesc = runStatus switch { 1 => "空闲", 2 => "运行中", 3 => "故障", _ => $"未知({runStatus})" };
|
||
string onlineStatusDesc = onlineStatus == 1 ? "离线" : "在线";
|
||
|
||
// 4.2 合格/返工标志(无分支优化)
|
||
string reworkFlag = produceModel == 4 ? "1" : "0";
|
||
string qualificationFlag = screwResults.All(s => s == 1) ? "1" : "0";
|
||
|
||
// ========== 阶段5:构建数据实体(内存复用+精简赋值) ==========
|
||
var plcData = new PlcProductionData
|
||
{
|
||
// 基础字段(固定值直接赋值,无冗余操作)
|
||
PlcIp = ip,
|
||
OccurTime = DateTime.Now,
|
||
LineCode = "line2",
|
||
WorkstationCode = workstationCode,
|
||
WorkstationName = "PCBA组装&拧紧",
|
||
ProductName = productName,
|
||
ProductCode = njPcbsn,
|
||
|
||
// 合格/返工标志
|
||
QualificationFlag = qualificationFlag,
|
||
ReworkFlag = reworkFlag,
|
||
|
||
// 设备状态相关
|
||
Automanual = machineModel,
|
||
Runstatus = runStatus,
|
||
OnlineStatus = onlineStatusDesc,
|
||
ProduceModel = produceModelDesc,
|
||
|
||
// 托盘号相关(字符串拼接优化)
|
||
TrayNo = $"{hzTrayNo}!{njTrayNo}",
|
||
AssemblyTrayNo = hzTrayNo.ToString(),
|
||
TightenTrayNo = njTrayNo.ToString(),
|
||
|
||
// SN相关字段(无重复判空)
|
||
SN1 = hzMachineSN,
|
||
SN2 = njMachineSN,
|
||
AssemblyHousingSN = hzMachineSN,
|
||
AssemblyPCBSN = hzPcbsn,
|
||
TightenHousingSN = njMachineSN,
|
||
TightenPCBSN = njPcbsn,
|
||
|
||
|
||
ActualOutQty = actualOutput.ToString(),
|
||
QualifiedQty = qualifiedQty.ToString(),
|
||
FailedQty = failedQty.ToString(),
|
||
|
||
// 螺钉相关字段(批量赋值,减少重复代码)
|
||
// 1号螺钉
|
||
Screw1Result = screwResults[0].ToString(),
|
||
Screw1Torque = screwRealData[0][0].ToString("0.00"),
|
||
Screw1Depth = screwRealData[0][1].ToString("0.00"),
|
||
Screw1Angle = screwRealData[0][2].ToString("0.00"),
|
||
Screw1TightenTime = screwRealData[0][3].ToString("0.00"),
|
||
// 2号螺钉
|
||
Screw2Result = screwResults[1].ToString(),
|
||
Screw2Torque = screwRealData[1][0].ToString("0.00"),
|
||
Screw2Depth = screwRealData[1][1].ToString("0.00"),
|
||
Screw2Angle = screwRealData[1][2].ToString("0.00"),
|
||
Screw2TightenTime = screwRealData[1][3].ToString("0.00"),
|
||
// 3号螺钉
|
||
Screw3Result = screwResults[2].ToString(),
|
||
Screw3Torque = screwRealData[2][0].ToString("0.00"),
|
||
Screw3Depth = screwRealData[2][1].ToString("0.00"),
|
||
Screw3Angle = screwRealData[2][2].ToString("0.00"),
|
||
Screw3TightenTime = screwRealData[2][3].ToString("0.00"),
|
||
// 4号螺钉
|
||
Screw4Result = screwResults[3].ToString(),
|
||
Screw4Torque = screwRealData[3][0].ToString("0.00"),
|
||
Screw4Depth = screwRealData[3][1].ToString("0.00"),
|
||
Screw4Angle = screwRealData[3][2].ToString("0.00"),
|
||
Screw4TightenTime = screwRealData[3][3].ToString("0.00"),
|
||
// 5号螺钉
|
||
Screw5Result = screwResults[4].ToString(),
|
||
Screw5Torque = screwRealData[4][0].ToString("0.00"),
|
||
Screw5Depth = screwRealData[4][1].ToString("0.00"),
|
||
Screw5Angle = screwRealData[4][2].ToString("0.00"),
|
||
Screw5TightenTime = screwRealData[4][3].ToString("0.00"),
|
||
// 6号螺钉
|
||
Screw6Result = screwResults[5].ToString(),
|
||
Screw6Torque = screwRealData[5][0].ToString("0.00"),
|
||
Screw6Depth = screwRealData[5][1].ToString("0.00"),
|
||
Screw6Angle = screwRealData[5][2].ToString("0.00"),
|
||
Screw6TightenTime = screwRealData[5][3].ToString("0.00"),
|
||
// 7号螺钉
|
||
Screw7Result = screwResults[6].ToString(),
|
||
Screw7Torque = screwRealData[6][0].ToString("0.00"),
|
||
Screw7Depth = screwRealData[6][1].ToString("0.00"),
|
||
Screw7Angle = screwRealData[6][2].ToString("0.00"),
|
||
Screw7TightenTime = screwRealData[6][3].ToString("0.00"),
|
||
|
||
// 系统字段(固定值,无冗余赋值)
|
||
CreatedBy = "PLC",
|
||
CreatedTime = DateTime.Now,
|
||
ParamName = string.Empty,
|
||
ParamValue = string.Empty,
|
||
ProductionCycle = null,
|
||
Remark = string.Empty,
|
||
UpdatedBy = string.Empty,
|
||
UpdatedTime = null,
|
||
ProductModel = string.Empty,
|
||
CameraResult = string.Empty
|
||
};
|
||
|
||
return plcData;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
// 增强异常日志(包含IP+时间+堆栈)
|
||
Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] OP080-1({ip})数据读取异常:{ex.Message}\n{ex.StackTrace}");
|
||
return null;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 读取OP080-2 数据
|
||
/// </summary>
|
||
/// <param name="plc"></param>
|
||
/// <param name="ip"></param>
|
||
/// <param name="workstationCode"></param>
|
||
/// <returns></returns>
|
||
/// <summary>
|
||
private async Task<PlcProductionData?> ReadOP080_2DataAsync(Plc plc, string ip, string plcName)
|
||
{
|
||
try
|
||
{
|
||
// 1. 批量并行读取所有字段
|
||
var (strFields, intFields, realFields) = await Task.Run(async () => (
|
||
// 字符串字段(增强空值和异常处理)
|
||
(
|
||
await ReadPlcStringAsync(plc, _op080_2StringMap["产品型号"].Addr, _op080_2StringMap["产品型号"].Len),
|
||
await ReadPlcStringAsync(plc, _op080_2StringMap["产品名称"].Addr, _op080_2StringMap["产品名称"].Len),
|
||
await ReadPlcStringAsync(plc, _op080_2StringMap["SN1"].Addr, _op080_2StringMap["SN1"].Len),
|
||
await ReadPlcStringAsync(plc, _op080_2StringMap["SN2"].Addr, _op080_2StringMap["SN2"].Len)
|
||
),
|
||
// Int字段(使用增强版ReadPlcIntAsync)
|
||
(
|
||
// 基础状态字段
|
||
await ReadPlcIntAsync(plc, _op080_2IntMap["运行状态"]),
|
||
await ReadPlcIntAsync(plc, _op080_2IntMap["设备模式"]),
|
||
await ReadPlcIntAsync(plc, _op080_2IntMap["设备在线状态"]),
|
||
//await ReadPlcIntAsync(plc, _op080_2IntMap["ByPass"]),
|
||
await ReadPlcIntAsync(plc, _op080_2IntMap["生产模式"]),
|
||
// 托盘号+产品总结果
|
||
await ReadPlcIntAsync(plc, _op080_2IntMap["托盘号"]),
|
||
await ReadPlcIntAsync(plc, _op080_2IntMap["产品总结果"]),
|
||
// 螺钉结果字段
|
||
await ReadPlcIntAsync(plc, _op080_2IntMap["1号螺钉_结果"]),
|
||
await ReadPlcIntAsync(plc, _op080_2IntMap["2号螺钉_结果"]),
|
||
await ReadPlcIntAsync(plc, _op080_2IntMap["3号螺钉_结果"]),
|
||
await ReadPlcIntAsync(plc, _op080_2IntMap["4号螺钉_结果"]),
|
||
await ReadPlcIntAsync(plc, _op080_2IntMap["5号螺钉_结果"]),
|
||
await ReadPlcIntAsync(plc, _op080_2IntMap["6号螺钉_结果"]),
|
||
await ReadPlcIntAsync(plc, _op080_2IntMap["7号螺钉_结果"])
|
||
),
|
||
// Real字段(螺钉扭矩/深度/角度/时间)
|
||
(
|
||
// 1号螺钉Real字段
|
||
await ReadPlcRealAsync(plc, _op080_2IntMap["1号螺钉_扭矩"]),
|
||
await ReadPlcRealAsync(plc, _op080_2IntMap["1号螺钉_深度"]),
|
||
await ReadPlcRealAsync(plc, _op080_2IntMap["1号螺钉_角度"]),
|
||
await ReadPlcRealAsync(plc, _op080_2IntMap["1号螺钉_拧紧时间"]),
|
||
// 2号螺钉Real字段
|
||
await ReadPlcRealAsync(plc, _op080_2IntMap["2号螺钉_扭矩"]),
|
||
await ReadPlcRealAsync(plc, _op080_2IntMap["2号螺钉_深度"]),
|
||
await ReadPlcRealAsync(plc, _op080_2IntMap["2号螺钉_角度"]),
|
||
await ReadPlcRealAsync(plc, _op080_2IntMap["2号螺钉_拧紧时间"]),
|
||
// 3号螺钉Real字段
|
||
await ReadPlcRealAsync(plc, _op080_2IntMap["3号螺钉_扭矩"]),
|
||
await ReadPlcRealAsync(plc, _op080_2IntMap["3号螺钉_深度"]),
|
||
await ReadPlcRealAsync(plc, _op080_2IntMap["3号螺钉_角度"]),
|
||
await ReadPlcRealAsync(plc, _op080_2IntMap["3号螺钉_拧紧时间"]),
|
||
// 4号螺钉Real字段
|
||
await ReadPlcRealAsync(plc, _op080_2IntMap["4号螺钉_扭矩"]),
|
||
await ReadPlcRealAsync(plc, _op080_2IntMap["4号螺钉_深度"]),
|
||
await ReadPlcRealAsync(plc, _op080_2IntMap["4号螺钉_角度"]),
|
||
await ReadPlcRealAsync(plc, _op080_2IntMap["4号螺钉_拧紧时间"]),
|
||
// 5号螺钉Real字段
|
||
await ReadPlcRealAsync(plc, _op080_2IntMap["5号螺钉_扭矩"]),
|
||
await ReadPlcRealAsync(plc, _op080_2IntMap["5号螺钉_深度"]),
|
||
await ReadPlcRealAsync(plc, _op080_2IntMap["5号螺钉_角度"]),
|
||
await ReadPlcRealAsync(plc, _op080_2IntMap["5号螺钉_拧紧时间"]),
|
||
// 6号螺钉Real字段
|
||
await ReadPlcRealAsync(plc, _op080_2IntMap["6号螺钉_扭矩"]),
|
||
await ReadPlcRealAsync(plc, _op080_2IntMap["6号螺钉_深度"]),
|
||
await ReadPlcRealAsync(plc, _op080_2IntMap["6号螺钉_角度"]),
|
||
await ReadPlcRealAsync(plc, _op080_2IntMap["6号螺钉_拧紧时间"]),
|
||
// 7号螺钉Real字段
|
||
await ReadPlcRealAsync(plc, _op080_2IntMap["7号螺钉_扭矩"]),
|
||
await ReadPlcRealAsync(plc, _op080_2IntMap["7号螺钉_深度"]),
|
||
await ReadPlcRealAsync(plc, _op080_2IntMap["7号螺钉_角度"]),
|
||
await ReadPlcRealAsync(plc, _op080_2IntMap["7号螺钉_拧紧时间"])
|
||
)
|
||
));
|
||
|
||
// 2. 解构字段
|
||
var (productModel, productName, sn1, sn2) = strFields;
|
||
|
||
var (runStatus, machineModel, onlineStatus, produceModel,
|
||
trayNo, productTotalResult,
|
||
screw1Result, screw2Result, screw3Result,
|
||
screw4Result, screw5Result, screw6Result, screw7Result) = intFields;
|
||
|
||
// 解构螺钉Real字段
|
||
var (
|
||
// 1号螺钉
|
||
screw1Torque, screw1Depth, screw1Angle, screw1Time,
|
||
// 2号螺钉
|
||
screw2Torque, screw2Depth, screw2Angle, screw2Time,
|
||
// 3号螺钉
|
||
screw3Torque, screw3Depth, screw3Angle, screw3Time,
|
||
// 4号螺钉
|
||
screw4Torque, screw4Depth, screw4Angle, screw4Time,
|
||
// 5号螺钉
|
||
screw5Torque, screw5Depth, screw5Angle, screw5Time,
|
||
// 6号螺钉
|
||
screw6Torque, screw6Depth, screw6Angle, screw6Time,
|
||
// 7号螺钉
|
||
screw7Torque, screw7Depth, screw7Angle, screw7Time
|
||
) = realFields;
|
||
|
||
// 3. 写入保存请求复位
|
||
//try
|
||
//{
|
||
// WritePlcValue(plc, _op080_2IntMap["保存请求"], "0");
|
||
//}
|
||
//catch (Exception ex)
|
||
//{
|
||
// Console.WriteLine($"OP080-2({ip})写保存请求失败:{ex.Message}");
|
||
//}
|
||
|
||
// 4. 业务逻辑计算
|
||
// 返工标志:生产模式=2时为返工
|
||
var reworkFlag = produceModel == 2 ? "1" : "0";
|
||
// 生产模式描述(OP080-2:1=点检,2=返工,4=样件,5=正常)
|
||
string produceModelDesc = produceModel switch
|
||
{
|
||
1 => "点检模式",
|
||
2 => "返工模式",
|
||
4 => "样件模式",
|
||
5 => "正常模式",
|
||
_ => $"未知({produceModel})"
|
||
};
|
||
// 运行状态描述(1=空闲,2=运行中,3=故障)
|
||
string runStatusDesc = runStatus switch { 1 => "空闲", 2 => "运行中", 3 => "故障", _ => $"未知({runStatus})" };
|
||
// 在线状态描述(1=离线,0=在线)
|
||
string onlineStatusDesc = onlineStatus == 1 ? "离线" : "在线";
|
||
// 合格标识:优先用产品总结果,无则判断所有螺钉结果
|
||
string qualificationFlag = productTotalResult switch
|
||
{
|
||
1 => "1", // 总结果OK
|
||
2 => "0", // 总结果NG
|
||
_ => new[] { screw1Result, screw2Result, screw3Result, screw4Result, screw5Result, screw6Result, screw7Result }
|
||
.All(s => s == 1) ? "1" : "0" // 总结果未知时,判断所有螺钉是否合格
|
||
};
|
||
|
||
// 调试日志:输出关键读取结果
|
||
Console.WriteLine($"OP080-2({ip})读取结果:产品型号={productModel},运行状态={runStatusDesc},产品名称={productName},合格标识={qualificationFlag}");
|
||
|
||
// 5. 构建数据实体(严格匹配PlcProductionData,适配OP080-2字段)
|
||
var plcData = new PlcProductionData
|
||
{
|
||
// 基础字段
|
||
PlcIp = ip.Trim(),
|
||
OccurTime = DateTime.Now,
|
||
LineCode = "line2", // 按实际产线调整
|
||
WorkstationCode = plcName, // 直接使用传入的plcName作为工位编码
|
||
WorkstationName = "PCBA拧紧",
|
||
ProductModel = productModel ?? string.Empty, // OP080-2有产品型号字段
|
||
ProductName = productName ?? string.Empty,
|
||
ProductCode = sn2 ?? string.Empty, // 结果上传条码作为产品编码
|
||
|
||
// 合格/返工标志
|
||
QualificationFlag = qualificationFlag,
|
||
ReworkFlag = reworkFlag,
|
||
|
||
// 设备状态相关
|
||
Automanual = machineModel, // 设备模式:1=空模式;2=手动;4=初始化;8=自动;16=CycleStop
|
||
Runstatus = runStatus, // 运行状态
|
||
OnlineStatus = onlineStatusDesc, // 在线状态
|
||
ProduceModel = produceModelDesc, // 生产模式描述
|
||
|
||
// 托盘号
|
||
TrayNo = trayNo.ToString(), // OP080-2只有单个托盘号
|
||
|
||
// SN相关字段
|
||
SN1 = sn1 ?? string.Empty, // 条码查询SN
|
||
SN2 = sn2 ?? string.Empty, // 结果上传SN
|
||
|
||
// 螺钉相关字段
|
||
Screw1Result = screw1Result.ToString(),
|
||
Screw1Torque = screw1Torque.ToString("0.00"),
|
||
Screw1Depth = screw1Depth.ToString("0.00"),
|
||
Screw1Angle = screw1Angle.ToString("0.00"),
|
||
Screw1TightenTime = screw1Time.ToString("0.00"),
|
||
|
||
Screw2Result = screw2Result.ToString(),
|
||
Screw2Torque = screw2Torque.ToString("0.00"),
|
||
Screw2Depth = screw2Depth.ToString("0.00"),
|
||
Screw2Angle = screw2Angle.ToString("0.00"),
|
||
Screw2TightenTime = screw2Time.ToString("0.00"),
|
||
|
||
Screw3Result = screw3Result.ToString(),
|
||
Screw3Torque = screw3Torque.ToString("0.00"),
|
||
Screw3Depth = screw3Depth.ToString("0.00"),
|
||
Screw3Angle = screw3Angle.ToString("0.00"),
|
||
Screw3TightenTime = screw3Time.ToString("0.00"),
|
||
|
||
Screw4Result = screw4Result.ToString(),
|
||
Screw4Torque = screw4Torque.ToString("0.00"),
|
||
Screw4Depth = screw4Depth.ToString("0.00"),
|
||
Screw4Angle = screw4Angle.ToString("0.00"),
|
||
Screw4TightenTime = screw4Time.ToString("0.00"),
|
||
|
||
Screw5Result = screw5Result.ToString(),
|
||
Screw5Torque = screw5Torque.ToString("0.00"),
|
||
Screw5Depth = screw5Depth.ToString("0.00"),
|
||
Screw5Angle = screw5Angle.ToString("0.00"),
|
||
Screw5TightenTime = screw5Time.ToString("0.00"),
|
||
|
||
Screw6Result = screw6Result.ToString(),
|
||
Screw6Torque = screw6Torque.ToString("0.00"),
|
||
Screw6Depth = screw6Depth.ToString("0.00"),
|
||
Screw6Angle = screw6Angle.ToString("0.00"),
|
||
Screw6TightenTime = screw6Time.ToString("0.00"),
|
||
|
||
Screw7Result = screw7Result.ToString(),
|
||
Screw7Torque = screw7Torque.ToString("0.00"),
|
||
Screw7Depth = screw7Depth.ToString("0.00"),
|
||
Screw7Angle = screw7Angle.ToString("0.00"),
|
||
Screw7TightenTime = screw7Time.ToString("0.00"),
|
||
|
||
// 系统字段
|
||
CreatedBy = "PLC",
|
||
CreatedTime = DateTime.Now,
|
||
};
|
||
return plcData;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine($"OP080-2({ip})数据读取异常:{ex.Message}");
|
||
return null;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 读取OP080-3数据(PWM压装&CUP上料&拧紧工位,DB1001)
|
||
/// </summary>
|
||
public async Task<PlcProductionData> ReadOP080_3DataAsync(Plc plc, string ip, string workstationCode)
|
||
{
|
||
// 前置校验:快速失败
|
||
if (plc == null || !plc.IsConnected) return null;
|
||
if (string.IsNullOrWhiteSpace(ip) || string.IsNullOrWhiteSpace(workstationCode))
|
||
throw new ArgumentNullException("IP或工位编码不能为空");
|
||
|
||
try
|
||
{
|
||
// 1. 批量创建并行读取任务
|
||
var stringTasks = new Dictionary<string, Task<string>>
|
||
{
|
||
{ "产品型号", ReadPlcStringAsync(plc, _op080_3StringMap["产品型号"].Addr, _op080_3StringMap["产品型号"].Len) },
|
||
{ "产品名称", ReadPlcStringAsync(plc, _op080_3StringMap["产品名称"].Addr, _op080_3StringMap["产品名称"].Len) },
|
||
{ "SN_1", ReadPlcStringAsync(plc, _op080_3StringMap["SN_1"].Addr, _op080_3StringMap["SN_1"].Len) },
|
||
{ "SN_2", ReadPlcStringAsync(plc, _op080_3StringMap["SN_2"].Addr, _op080_3StringMap["SN_2"].Len) }
|
||
};
|
||
|
||
var intTasks = new Dictionary<string, Task<int>>
|
||
{
|
||
{ "运行状态", ReadPlcIntAsync(plc, _op080_3IntMap["运行状态"]) },
|
||
{ "设备模式", ReadPlcIntAsync(plc, _op080_3IntMap["设备模式"]) },
|
||
{ "设备在线状态", ReadPlcIntAsync(plc, _op080_3IntMap["设备在线状态"]) },
|
||
{ "生产模式", ReadPlcIntAsync(plc, _op080_3IntMap["生产模式"]) },
|
||
{ "实际产量", ReadPlcIntAsync(plc, _op080_3IntMap["实际产量"]) },
|
||
{ "产品总结果", ReadPlcIntAsync(plc, _op080_3IntMap["产品总结果"]) },
|
||
{ "压机结果", ReadPlcIntAsync(plc, _op080_3IntMap["压机结果"]) },
|
||
{ "左螺丝枪结果", ReadPlcIntAsync(plc, _op080_3IntMap["左螺丝枪结果"]) },
|
||
{ "右螺丝枪结果", ReadPlcIntAsync(plc, _op080_3IntMap["右螺丝枪结果"]) },
|
||
{ "托盘号", ReadPlcIntAsync(plc, _op080_3IntMap["托盘号"]) }
|
||
};
|
||
|
||
var realTasks = new Dictionary<string, Task<float>>
|
||
{
|
||
{ "压机左压力值", ReadPlcRealAsync(plc, _op080_3IntMap["压机左压力值"]) },
|
||
{ "压机右压力值", ReadPlcRealAsync(plc, _op080_3IntMap["压机右压力值"]) },
|
||
{ "压机行程值", ReadPlcRealAsync(plc, _op080_3IntMap["压机行程值"]) },
|
||
{ "保压时间", ReadPlcRealAsync(plc, _op080_3IntMap["保压时间"]) },
|
||
{ "左螺丝枪扭矩", ReadPlcRealAsync(plc, _op080_3IntMap["左螺丝枪扭矩"]) },
|
||
{ "左螺丝枪角度", ReadPlcRealAsync(plc, _op080_3IntMap["左螺丝枪角度"]) },
|
||
{ "左螺丝枪深度", ReadPlcRealAsync(plc, _op080_3IntMap["左螺丝枪深度"]) },
|
||
{ "左螺丝枪时间", ReadPlcRealAsync(plc, _op080_3IntMap["左螺丝枪时间"]) },
|
||
{ "右螺丝枪扭矩", ReadPlcRealAsync(plc, _op080_3IntMap["右螺丝枪扭矩"]) },
|
||
{ "右螺丝枪角度", ReadPlcRealAsync(plc, _op080_3IntMap["右螺丝枪角度"]) },
|
||
{ "右螺丝枪深度", ReadPlcRealAsync(plc, _op080_3IntMap["右螺丝枪深度"]) },
|
||
{ "右螺丝枪时间", ReadPlcRealAsync(plc, _op080_3IntMap["右螺丝枪时间"]) },
|
||
{ "节拍时间", ReadPlcRealAsync(plc, _op080_3IntMap["节拍时间"]) }
|
||
};
|
||
|
||
// 2. 并行等待所有任务完成
|
||
var allTasks = new List<Task>();
|
||
allTasks.AddRange(stringTasks.Values);
|
||
allTasks.AddRange(intTasks.Values);
|
||
allTasks.AddRange(realTasks.Values);
|
||
await Task.WhenAll(allTasks).ConfigureAwait(false);
|
||
|
||
// 3. 提取结果
|
||
var now = DateTime.Now;
|
||
var productModel = stringTasks["产品型号"].Result ?? string.Empty;
|
||
var productName = stringTasks["产品名称"].Result ?? string.Empty;
|
||
var sn1 = stringTasks["SN_1"].Result ?? string.Empty;
|
||
var sn2 = stringTasks["SN_2"].Result ?? string.Empty;
|
||
|
||
var runStatus = intTasks["运行状态"].Result;
|
||
var machineModel = intTasks["设备模式"].Result;
|
||
var onlineStatus = intTasks["设备在线状态"].Result;
|
||
var produceModel = intTasks["生产模式"].Result;
|
||
var actualOutput = intTasks["实际产量"].Result;
|
||
var productTotalResult = intTasks["产品总结果"].Result;
|
||
var pressResult = intTasks["压机结果"].Result;
|
||
var leftScrewResult = intTasks["左螺丝枪结果"].Result;
|
||
var rightScrewResult = intTasks["右螺丝枪结果"].Result;
|
||
var trayNo = intTasks["托盘号"].Result.ToString();
|
||
|
||
var pressLeftPressure = realTasks["压机左压力值"].Result;
|
||
var pressRightPressure = realTasks["压机右压力值"].Result;
|
||
var pressStroke = realTasks["压机行程值"].Result;
|
||
var pressHoldTime = realTasks["保压时间"].Result;
|
||
var leftScrewTorque = realTasks["左螺丝枪扭矩"].Result;
|
||
var leftScrewAngle = realTasks["左螺丝枪角度"].Result;
|
||
var leftScrewDepth = realTasks["左螺丝枪深度"].Result;
|
||
var leftScrewTime = realTasks["左螺丝枪时间"].Result;
|
||
var rightScrewTorque = realTasks["右螺丝枪扭矩"].Result;
|
||
var rightScrewAngle = realTasks["右螺丝枪角度"].Result;
|
||
var rightScrewDepth = realTasks["右螺丝枪深度"].Result;
|
||
var rightScrewTime = realTasks["右螺丝枪时间"].Result;
|
||
var cycleTime = realTasks["节拍时间"].Result;
|
||
|
||
// 4. 业务逻辑转换
|
||
var reworkFlag = produceModel == 2 ? "1" : "0";
|
||
var produceModelDesc = produceModel switch
|
||
{
|
||
1 => "点检模式",
|
||
2 => "返工模式",
|
||
4 => "样件模式",
|
||
5 => "正常模式",
|
||
_ => produceModel.ToString()
|
||
};
|
||
var onlineStatusDesc = onlineStatus == 1 ? "离线" : "在线";
|
||
var qualificationFlag = productTotalResult == 1 ? "1" : "2";
|
||
|
||
|
||
// 6. 构建完整实体(覆盖所有工艺参数字段)
|
||
return new PlcProductionData
|
||
{
|
||
// 基础字段
|
||
PlcIp = ip,
|
||
OccurTime = now,
|
||
LineCode = "line2",
|
||
WorkstationCode = workstationCode,
|
||
WorkstationName = "PWM压装&CUP上料&拧紧工位",
|
||
ProductModel = productModel,
|
||
ProductName = productName,
|
||
ProductCode = sn1,
|
||
SN1 = sn1,
|
||
SN2 = sn2,
|
||
TrayNo = trayNo,
|
||
ReworkFlag = reworkFlag,
|
||
Automanual = machineModel,
|
||
Runstatus = runStatus,
|
||
OnlineStatus = onlineStatusDesc,
|
||
ProduceModel = produceModelDesc,
|
||
QualificationFlag = qualificationFlag,
|
||
ProductionCycle = (int)Math.Round(cycleTime),
|
||
CreatedBy = "PLC",
|
||
CreatedTime = now,
|
||
|
||
// 压机参数
|
||
PinPressure1 = pressLeftPressure.ToString("F2"),//压机左压力值
|
||
PinPressure2 = pressRightPressure.ToString("F2"),//压机右压力值
|
||
PinStroke1 = pressStroke.ToString("F2"),//压机行程值
|
||
PressHoldTime = pressHoldTime.ToString(),//保压时间
|
||
|
||
// 左螺丝枪参数
|
||
Screw1Result = leftScrewResult.ToString(),
|
||
Screw1Torque = leftScrewTorque.ToString("F2"),
|
||
Screw1Depth = leftScrewDepth.ToString("F2"),
|
||
Screw1Angle = leftScrewAngle.ToString("F2"),
|
||
Screw1TightenTime = leftScrewTime.ToString("F2"),
|
||
|
||
// 右螺丝枪参数
|
||
Screw2Result = rightScrewResult.ToString(),
|
||
Screw2Torque = rightScrewTorque.ToString("F2"),
|
||
Screw2Depth = rightScrewDepth.ToString("F2"),
|
||
Screw2Angle = rightScrewAngle.ToString("F2"),
|
||
Screw2TightenTime = rightScrewTime.ToString("F2"),
|
||
};
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] OP080-3读取异常:{ex.Message}");
|
||
return null;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 读取OP085数据(自动上料到波峰焊工位,DB1001)
|
||
/// </summary>
|
||
public async Task<PlcProductionData> ReadOP085DataAsync(Plc plc, string ip, string workstationCode)
|
||
{
|
||
// 前置校验:快速失败
|
||
if (plc == null || !plc.IsConnected) return null;
|
||
if (string.IsNullOrWhiteSpace(ip) || string.IsNullOrWhiteSpace(workstationCode))
|
||
throw new ArgumentNullException("IP或工位编码不能为空");
|
||
|
||
try
|
||
{
|
||
// 1. 批量创建并行读取任务
|
||
var stringTasks = new Dictionary<string, Task<string>>
|
||
{
|
||
{ "产品型号", ReadPlcStringAsync(plc, _op085StringMap["产品型号"].Addr, _op085StringMap["产品型号"].Len) },
|
||
{ "产品名称", ReadPlcStringAsync(plc, _op085StringMap["产品名称"].Addr, _op085StringMap["产品名称"].Len) },
|
||
{ "波峰焊托盘条码", ReadPlcStringAsync(plc, _op085StringMap["波峰焊托盘条码"].Addr, _op085StringMap["波峰焊托盘条码"].Len) },
|
||
{ "穴位1产品SN", ReadPlcStringAsync(plc, _op085StringMap["穴位1产品SN"].Addr, _op085StringMap["穴位1产品SN"].Len) },
|
||
{ "穴位2产品SN", ReadPlcStringAsync(plc, _op085StringMap["穴位2产品SN"].Addr, _op085StringMap["穴位2产品SN"].Len) },
|
||
{ "穴位3产品SN", ReadPlcStringAsync(plc, _op085StringMap["穴位3产品SN"].Addr, _op085StringMap["穴位3产品SN"].Len) },
|
||
{ "穴位4产品SN", ReadPlcStringAsync(plc, _op085StringMap["穴位4产品SN"].Addr, _op085StringMap["穴位4产品SN"].Len) }
|
||
};
|
||
|
||
var intTasks = new Dictionary<string, Task<int>>
|
||
{
|
||
{ "运行状态", ReadPlcIntAsync(plc, _op085IntMap["运行状态"]) },
|
||
{ "设备模式", ReadPlcIntAsync(plc, _op085IntMap["设备模式"]) },
|
||
{ "设备在线状态", ReadPlcIntAsync(plc, _op085IntMap["设备在线状态"]) },
|
||
{ "生产模式", ReadPlcIntAsync(plc, _op085IntMap["生产模式"]) },
|
||
{ "实际产量", ReadPlcIntAsync(plc, _op085IntMap["实际产量"]) },
|
||
{ "线体托盘号", ReadPlcIntAsync(plc, _op085IntMap["线体托盘号"]) }
|
||
};
|
||
|
||
var realTasks = new Dictionary<string, Task<float>>
|
||
{
|
||
{ "节拍时间", ReadPlcRealAsync(plc, _op085IntMap["节拍时间"]) }
|
||
};
|
||
|
||
// 2. 并行等待所有任务完成
|
||
var allTasks = new List<Task>();
|
||
allTasks.AddRange(stringTasks.Values);
|
||
allTasks.AddRange(intTasks.Values);
|
||
allTasks.AddRange(realTasks.Values);
|
||
await Task.WhenAll(allTasks).ConfigureAwait(false);
|
||
|
||
// 3. 提取结果
|
||
var now = DateTime.Now;
|
||
var productModel = stringTasks["产品型号"].Result ?? string.Empty;
|
||
var productName = stringTasks["产品名称"].Result ?? string.Empty;
|
||
var trayBarcode = stringTasks["波峰焊托盘条码"].Result ?? string.Empty;
|
||
var sn1 = stringTasks["穴位1产品SN"].Result ?? string.Empty;
|
||
var sn2 = stringTasks["穴位2产品SN"].Result ?? string.Empty;
|
||
var sn3 = stringTasks["穴位3产品SN"].Result ?? string.Empty;
|
||
var sn4 = stringTasks["穴位4产品SN"].Result ?? string.Empty;
|
||
|
||
var runStatus = intTasks["运行状态"].Result;
|
||
var machineModel = intTasks["设备模式"].Result;
|
||
var onlineStatus = intTasks["设备在线状态"].Result;
|
||
var produceModel = intTasks["生产模式"].Result;
|
||
var actualOutput = intTasks["实际产量"].Result;
|
||
var lineTrayNo = intTasks["线体托盘号"].Result.ToString();
|
||
|
||
var cycleTime = realTasks["节拍时间"].Result;
|
||
|
||
// 4. 业务逻辑转换
|
||
var reworkFlag = produceModel == 2 ? "1" : "0";
|
||
var produceModelDesc = produceModel switch
|
||
{
|
||
1 => "点检模式",
|
||
2 => "返工模式",
|
||
4 => "样件模式",
|
||
5 => "正常模式",
|
||
_ => produceModel.ToString()
|
||
};
|
||
var onlineStatusDesc = onlineStatus == 1 ? "离线" : "在线";
|
||
var qualificationFlag = "1"; // 上料工位默认合格
|
||
|
||
|
||
// 5. 构建完整实体
|
||
return new PlcProductionData
|
||
{
|
||
// 基础字段
|
||
PlcIp = ip,
|
||
OccurTime = now,
|
||
LineCode = "line2",
|
||
WorkstationCode = workstationCode,
|
||
WorkstationName = "自动上料到波峰焊工位",
|
||
ProductModel = productModel,
|
||
ProductName = productName,
|
||
ProductCode = sn1,
|
||
SN1 = sn1,
|
||
SN2 = sn2,
|
||
Product1SN = sn1,
|
||
Product2SN = sn2,
|
||
Product3SN = sn3,
|
||
Product4SN = sn4,
|
||
TrayNo = lineTrayNo,
|
||
TrayBarcode = trayBarcode,
|
||
ReworkFlag = reworkFlag,
|
||
Automanual = machineModel,
|
||
Runstatus = runStatus,
|
||
OnlineStatus = onlineStatusDesc,
|
||
ProduceModel = produceModelDesc,
|
||
QualificationFlag = qualificationFlag,
|
||
ProductionCycle = (int)Math.Round(cycleTime),
|
||
CreatedBy = "PLC",
|
||
CreatedTime = now,
|
||
};
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] OP085读取异常:{ex.Message}");
|
||
return null;
|
||
}
|
||
}
|
||
/// <summary>
|
||
/// 读取OP100数据(手动上料壳体&盖板工位,DB1001)
|
||
/// </summary>
|
||
public async Task<PlcProductionData> ReadOP100DataAsync(Plc plc, string ip, string workstationCode)
|
||
{
|
||
// 前置校验:快速失败
|
||
if (plc == null || !plc.IsConnected) return null;
|
||
if (string.IsNullOrWhiteSpace(ip) || string.IsNullOrWhiteSpace(workstationCode))
|
||
throw new ArgumentNullException("IP或工位编码不能为空");
|
||
|
||
try
|
||
{
|
||
// 1. 批量创建并行读取任务
|
||
var stringTasks = new Dictionary<string, Task<string>>
|
||
{
|
||
{ "产品型号", ReadPlcStringAsync(plc, _op100StringMap["产品型号"].Addr, _op100StringMap["产品型号"].Len) },
|
||
{ "产品名称", ReadPlcStringAsync(plc, _op100StringMap["产品名称"].Addr, _op100StringMap["产品名称"].Len) },
|
||
{ "产品SN", ReadPlcStringAsync(plc, _op100StringMap["产品SN"].Addr, _op100StringMap["产品SN"].Len) }
|
||
};
|
||
|
||
var intTasks = new Dictionary<string, Task<int>>
|
||
{
|
||
{ "运行状态", ReadPlcIntAsync(plc, _op100IntMap["运行状态"]) },
|
||
{ "设备模式", ReadPlcIntAsync(plc, _op100IntMap["设备模式"]) },
|
||
{ "设备在线状态", ReadPlcIntAsync(plc, _op100IntMap["设备在线状态"]) },
|
||
{ "生产模式", ReadPlcIntAsync(plc, _op100IntMap["生产模式"]) },
|
||
{ "实际产量", ReadPlcIntAsync(plc, _op100IntMap["实际产量"]) },
|
||
{ "托盘号", ReadPlcIntAsync(plc, _op100IntMap["托盘号"]) }
|
||
};
|
||
|
||
var realTasks = new Dictionary<string, Task<float>>
|
||
{
|
||
{ "节拍时间", ReadPlcRealAsync(plc, _op100IntMap["节拍时间"]) }
|
||
};
|
||
|
||
// 2. 并行等待所有任务完成
|
||
var allTasks = new List<Task>();
|
||
allTasks.AddRange(stringTasks.Values);
|
||
allTasks.AddRange(intTasks.Values);
|
||
allTasks.AddRange(realTasks.Values);
|
||
await Task.WhenAll(allTasks).ConfigureAwait(false);
|
||
|
||
// 3. 提取结果
|
||
var now = DateTime.Now;
|
||
var productModel = stringTasks["产品型号"].Result ?? string.Empty;
|
||
var productName = stringTasks["产品名称"].Result ?? string.Empty;
|
||
var sn1 = stringTasks["产品SN"].Result ?? string.Empty;
|
||
|
||
var runStatus = intTasks["运行状态"].Result;
|
||
var machineModel = intTasks["设备模式"].Result;
|
||
var onlineStatus = intTasks["设备在线状态"].Result;
|
||
var produceModel = intTasks["生产模式"].Result;
|
||
var actualOutput = intTasks["实际产量"].Result;
|
||
var trayNo = intTasks["托盘号"].Result.ToString();
|
||
|
||
var cycleTime = realTasks["节拍时间"].Result;
|
||
|
||
// 4. 业务逻辑转换
|
||
var reworkFlag = produceModel == 2 ? "1" : "0";
|
||
var produceModelDesc = produceModel switch
|
||
{
|
||
1 => "点检模式",
|
||
2 => "返工模式",
|
||
4 => "样件模式",
|
||
5 => "正常模式",
|
||
_ => produceModel.ToString()
|
||
};
|
||
var onlineStatusDesc = onlineStatus == 1 ? "离线" : "在线";
|
||
var qualificationFlag = "1"; // 手动上料工位默认合格
|
||
|
||
|
||
// 6. 构建完整实体
|
||
return new PlcProductionData
|
||
{
|
||
// 基础字段
|
||
PlcIp = ip,
|
||
OccurTime = now,
|
||
LineCode = "line2",
|
||
WorkstationCode = workstationCode,
|
||
WorkstationName = "手动上料壳体&盖板工位",
|
||
ProductModel = productModel,
|
||
ProductName = productName,
|
||
ProductCode = sn1,
|
||
SN1 = sn1,
|
||
TrayNo = trayNo,
|
||
ReworkFlag = reworkFlag,
|
||
Automanual = machineModel,
|
||
Runstatus = runStatus,
|
||
OnlineStatus = onlineStatusDesc,
|
||
ProduceModel = produceModelDesc,
|
||
QualificationFlag = qualificationFlag,
|
||
ProductionCycle = (int)Math.Round(cycleTime),
|
||
CreatedBy = "PLC_System",
|
||
CreatedTime = now,
|
||
|
||
};
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] OP100读取异常:{ex.Message}");
|
||
return null;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 读取OP1011数据(HV连接器装配工位,DB1001)
|
||
/// </summary>
|
||
public async Task<PlcProductionData> ReadOP1011DataAsync(Plc plc, string ip, string workstationCode)
|
||
{
|
||
// 前置校验:快速失败
|
||
if (plc == null || !plc.IsConnected) return null;
|
||
if (string.IsNullOrWhiteSpace(ip) || string.IsNullOrWhiteSpace(workstationCode))
|
||
throw new ArgumentNullException("IP或工位编码不能为空");
|
||
|
||
try
|
||
{
|
||
// 1. 批量创建并行读取任务
|
||
var stringTasks = new Dictionary<string, Task<string>>
|
||
{
|
||
{ "产品型号", ReadPlcStringAsync(plc, _op1011StringMap["产品型号"].Addr, _op1011StringMap["产品型号"].Len) },
|
||
{ "产品名称", ReadPlcStringAsync(plc, _op1011StringMap["产品名称"].Addr, _op1011StringMap["产品名称"].Len) },
|
||
{ "查询SN", ReadPlcStringAsync(plc, _op1011StringMap["查询SN"].Addr, _op1011StringMap["查询SN"].Len) },
|
||
{ "上传结果SN", ReadPlcStringAsync(plc, _op1011StringMap["上传结果SN"].Addr, _op1011StringMap["上传结果SN"].Len) }
|
||
};
|
||
|
||
var intTasks = new Dictionary<string, Task<int>>
|
||
{
|
||
{ "运行状态", ReadPlcIntAsync(plc, _op1011IntMap["运行状态"]) },
|
||
{ "设备模式", ReadPlcIntAsync(plc, _op1011IntMap["设备模式"]) },
|
||
{ "设备在线状态", ReadPlcIntAsync(plc, _op1011IntMap["设备在线状态"]) },
|
||
{ "生产模式", ReadPlcIntAsync(plc, _op1011IntMap["生产模式"]) },
|
||
{ "螺钉枪程序号", ReadPlcIntAsync(plc, _op1011IntMap["螺钉枪程序号"]) },
|
||
{ "实际产量", ReadPlcIntAsync(plc, _op1011IntMap["实际产量"]) },
|
||
{ "产品总结果", ReadPlcIntAsync(plc, _op1011IntMap["产品总结果"]) },
|
||
{ "1#结果", ReadPlcIntAsync(plc, _op1011IntMap["1#结果"]) },
|
||
{ "2#结果", ReadPlcIntAsync(plc, _op1011IntMap["2#结果"]) },
|
||
{ "3#结果", ReadPlcIntAsync(plc, _op1011IntMap["3#结果"]) },
|
||
{ "4#结果", ReadPlcIntAsync(plc, _op1011IntMap["4#结果"]) }
|
||
};
|
||
|
||
var realTasks = new Dictionary<string, Task<float>>
|
||
{
|
||
{ "1#扭矩", ReadPlcRealAsync(plc, _op1011IntMap["1#扭矩"]) },
|
||
{ "2#扭矩", ReadPlcRealAsync(plc, _op1011IntMap["2#扭矩"]) },
|
||
{ "3#扭矩", ReadPlcRealAsync(plc, _op1011IntMap["3#扭矩"]) },
|
||
{ "4#扭矩", ReadPlcRealAsync(plc, _op1011IntMap["4#扭矩"]) },
|
||
{ "1#转角", ReadPlcRealAsync(plc, _op1011IntMap["1#转角"]) },
|
||
{ "2#转角", ReadPlcRealAsync(plc, _op1011IntMap["2#转角"]) },
|
||
{ "3#转角", ReadPlcRealAsync(plc, _op1011IntMap["3#转角"]) },
|
||
{ "4#转角", ReadPlcRealAsync(plc, _op1011IntMap["4#转角"]) },
|
||
{ "1#深度", ReadPlcRealAsync(plc, _op1011IntMap["1#深度"]) },
|
||
{ "2#深度", ReadPlcRealAsync(plc, _op1011IntMap["2#深度"]) },
|
||
{ "3#深度", ReadPlcRealAsync(plc, _op1011IntMap["3#深度"]) },
|
||
{ "4#深度", ReadPlcRealAsync(plc, _op1011IntMap["4#深度"]) },
|
||
{ "节拍时间", ReadPlcRealAsync(plc, _op1011IntMap["节拍时间"]) }
|
||
};
|
||
|
||
// 2. 并行等待所有任务完成
|
||
var allTasks = new List<Task>();
|
||
allTasks.AddRange(stringTasks.Values);
|
||
allTasks.AddRange(intTasks.Values);
|
||
allTasks.AddRange(realTasks.Values);
|
||
await Task.WhenAll(allTasks).ConfigureAwait(false);
|
||
|
||
// 3. 提取结果
|
||
var now = DateTime.Now;
|
||
var productModel = stringTasks["产品型号"].Result ?? string.Empty;
|
||
var productName = stringTasks["产品名称"].Result ?? string.Empty;
|
||
var sn1 = stringTasks["查询SN"].Result ?? string.Empty;
|
||
var sn2 = stringTasks["上传结果SN"].Result ?? string.Empty;
|
||
|
||
var runStatus = intTasks["运行状态"].Result;
|
||
var machineModel = intTasks["设备模式"].Result;
|
||
var onlineStatus = intTasks["设备在线状态"].Result;
|
||
var produceModel = intTasks["生产模式"].Result;
|
||
var screwGunProgram = intTasks["螺钉枪程序号"].Result;
|
||
var actualOutput = intTasks["实际产量"].Result;
|
||
var productTotalResult = intTasks["产品总结果"].Result;
|
||
var screw1Result = intTasks["1#结果"].Result;
|
||
var screw2Result = intTasks["2#结果"].Result;
|
||
var screw3Result = intTasks["3#结果"].Result;
|
||
var screw4Result = intTasks["4#结果"].Result;
|
||
|
||
var screw1Torque = realTasks["1#扭矩"].Result;
|
||
var screw2Torque = realTasks["2#扭矩"].Result;
|
||
var screw3Torque = realTasks["3#扭矩"].Result;
|
||
var screw4Torque = realTasks["4#扭矩"].Result;
|
||
var screw1Angle = realTasks["1#转角"].Result;
|
||
var screw2Angle = realTasks["2#转角"].Result;
|
||
var screw3Angle = realTasks["3#转角"].Result;
|
||
var screw4Angle = realTasks["4#转角"].Result;
|
||
var screw1Depth = realTasks["1#深度"].Result;
|
||
var screw2Depth = realTasks["2#深度"].Result;
|
||
var screw3Depth = realTasks["3#深度"].Result;
|
||
var screw4Depth = realTasks["4#深度"].Result;
|
||
var cycleTime = realTasks["节拍时间"].Result;
|
||
|
||
// 4. 业务逻辑转换
|
||
var reworkFlag = produceModel == 2 ? "1" : "0";
|
||
var produceModelDesc = produceModel switch
|
||
{
|
||
1 => "点检模式",
|
||
2 => "返工模式",
|
||
4 => "样件模式",
|
||
5 => "正常模式",
|
||
_ => produceModel.ToString()
|
||
};
|
||
var onlineStatusDesc = onlineStatus == 1 ? "离线" : "在线";
|
||
var qualificationFlag = productTotalResult == 1 ? "1" : "2";
|
||
|
||
// 5. 构建完整实体
|
||
return new PlcProductionData
|
||
{
|
||
// 基础字段
|
||
PlcIp = ip,
|
||
OccurTime = now,
|
||
LineCode = "line2",
|
||
WorkstationCode = workstationCode,
|
||
WorkstationName = "HV连接器装配工位",
|
||
ProductModel = productModel,
|
||
ProductName = productName,
|
||
ProductCode = sn1,
|
||
SN1 = sn1,
|
||
SN2 = sn2,
|
||
ReworkFlag = reworkFlag,
|
||
Automanual = machineModel,
|
||
Runstatus = runStatus,
|
||
OnlineStatus = onlineStatusDesc,
|
||
ProduceModel = produceModelDesc,
|
||
QualificationFlag = qualificationFlag,
|
||
ProductionCycle = (int)Math.Round(cycleTime),
|
||
CreatedBy = "PLC",
|
||
CreatedTime = now,
|
||
|
||
// 螺丝1-4参数完整赋值
|
||
Screw1Result = screw1Result.ToString(),
|
||
Screw1Torque = screw1Torque.ToString("F2"),
|
||
Screw1Depth = screw1Depth.ToString("F2"),
|
||
Screw1Angle = screw1Angle.ToString("F2"),
|
||
Screw2Result = screw2Result.ToString(),
|
||
Screw2Torque = screw2Torque.ToString("F2"),
|
||
Screw2Depth = screw2Depth.ToString("F2"),
|
||
Screw2Angle = screw2Angle.ToString("F2"),
|
||
Screw3Result = screw3Result.ToString(),
|
||
Screw3Torque = screw3Torque.ToString("F2"),
|
||
Screw3Depth = screw3Depth.ToString("F2"),
|
||
Screw3Angle = screw3Angle.ToString("F2"),
|
||
Screw4Result = screw4Result.ToString(),
|
||
Screw4Torque = screw4Torque.ToString("F2"),
|
||
Screw4Depth = screw4Depth.ToString("F2"),
|
||
Screw4Angle = screw4Angle.ToString("F2"),
|
||
};
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] OP1011读取异常:{ex.Message}");
|
||
return null;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 读取OP1012数据(Ring terminal装配工位,DB1001)
|
||
/// </summary>
|
||
public async Task<PlcProductionData> ReadOP1012DataAsync(Plc plc, string ip, string workstationCode)
|
||
{
|
||
// 前置校验:快速失败
|
||
if (plc == null || !plc.IsConnected) return null;
|
||
if (string.IsNullOrWhiteSpace(ip) || string.IsNullOrWhiteSpace(workstationCode))
|
||
throw new ArgumentNullException("IP或工位编码不能为空");
|
||
|
||
try
|
||
{
|
||
// 1. 批量创建并行读取任务
|
||
var stringTasks = new Dictionary<string, Task<string>>
|
||
{
|
||
{ "产品型号", ReadPlcStringAsync(plc, _op1012StringMap["产品型号"].Addr, _op1012StringMap["产品型号"].Len) },
|
||
{ "产品名称", ReadPlcStringAsync(plc, _op1012StringMap["产品名称"].Addr, _op1012StringMap["产品名称"].Len) },
|
||
{ "查询SN", ReadPlcStringAsync(plc, _op1012StringMap["查询SN"].Addr, _op1012StringMap["查询SN"].Len) },
|
||
{ "上传SN", ReadPlcStringAsync(plc, _op1012StringMap["上传SN"].Addr, _op1012StringMap["上传SN"].Len) }
|
||
};
|
||
|
||
var intTasks = new Dictionary<string, Task<int>>
|
||
{
|
||
{ "运行状态", ReadPlcIntAsync(plc, _op1012IntMap["运行状态"]) },
|
||
{ "设备模式", ReadPlcIntAsync(plc, _op1012IntMap["设备模式"]) },
|
||
{ "设备在线状态", ReadPlcIntAsync(plc, _op1012IntMap["设备在线状态"]) },
|
||
{ "生产模式", ReadPlcIntAsync(plc, _op1012IntMap["生产模式"]) },
|
||
{ "螺钉枪程序号", ReadPlcIntAsync(plc, _op1012IntMap["螺钉枪程序号"]) },
|
||
{ "实际产量", ReadPlcIntAsync(plc, _op1012IntMap["实际产量"]) },
|
||
{ "产品总结果", ReadPlcIntAsync(plc, _op1012IntMap["产品总结果"]) },
|
||
{ "1#结果", ReadPlcIntAsync(plc, _op1012IntMap["1#结果"]) },
|
||
{ "2#结果", ReadPlcIntAsync(plc, _op1012IntMap["2#结果"]) }
|
||
};
|
||
|
||
var realTasks = new Dictionary<string, Task<float>>
|
||
{
|
||
{ "1#扭矩", ReadPlcRealAsync(plc, _op1012IntMap["1#扭矩"]) },
|
||
{ "2#扭矩", ReadPlcRealAsync(plc, _op1012IntMap["2#扭矩"]) },
|
||
{ "1#转角", ReadPlcRealAsync(plc, _op1012IntMap["1#转角"]) },
|
||
{ "2#转角", ReadPlcRealAsync(plc, _op1012IntMap["2#转角"]) },
|
||
{ "1#深度", ReadPlcRealAsync(plc, _op1012IntMap["1#深度"]) },
|
||
{ "2#深度", ReadPlcRealAsync(plc, _op1012IntMap["2#深度"]) },
|
||
{ "节拍时间", ReadPlcRealAsync(plc, _op1012IntMap["节拍时间"]) }
|
||
};
|
||
|
||
// 2. 并行等待所有任务完成
|
||
var allTasks = new List<Task>();
|
||
allTasks.AddRange(stringTasks.Values);
|
||
allTasks.AddRange(intTasks.Values);
|
||
allTasks.AddRange(realTasks.Values);
|
||
await Task.WhenAll(allTasks).ConfigureAwait(false);
|
||
|
||
// 3. 提取结果
|
||
var now = DateTime.Now;
|
||
var productModel = stringTasks["产品型号"].Result ?? string.Empty;
|
||
var productName = stringTasks["产品名称"].Result ?? string.Empty;
|
||
var sn1 = stringTasks["查询SN"].Result ?? string.Empty;
|
||
var sn2 = stringTasks["上传SN"].Result ?? string.Empty;
|
||
|
||
var runStatus = intTasks["运行状态"].Result;
|
||
var machineModel = intTasks["设备模式"].Result;
|
||
var onlineStatus = intTasks["设备在线状态"].Result;
|
||
var produceModel = intTasks["生产模式"].Result;
|
||
var screwGunProgram = intTasks["螺钉枪程序号"].Result;
|
||
var actualOutput = intTasks["实际产量"].Result;
|
||
var productTotalResult = intTasks["产品总结果"].Result;
|
||
var screw1Result = intTasks["1#结果"].Result;
|
||
var screw2Result = intTasks["2#结果"].Result;
|
||
|
||
var screw1Torque = realTasks["1#扭矩"].Result;
|
||
var screw2Torque = realTasks["2#扭矩"].Result;
|
||
var screw1Angle = realTasks["1#转角"].Result;
|
||
var screw2Angle = realTasks["2#转角"].Result;
|
||
var screw1Depth = realTasks["1#深度"].Result;
|
||
var screw2Depth = realTasks["2#深度"].Result;
|
||
var cycleTime = realTasks["节拍时间"].Result;
|
||
|
||
// 4. 业务逻辑转换
|
||
var reworkFlag = produceModel == 2 ? "1" : "0";
|
||
var produceModelDesc = produceModel switch
|
||
{
|
||
1 => "点检模式",
|
||
2 => "返工模式",
|
||
4 => "样件模式",
|
||
5 => "正常模式",
|
||
_ => produceModel.ToString()
|
||
};
|
||
var onlineStatusDesc = onlineStatus == 1 ? "离线" : "在线";
|
||
var qualificationFlag = productTotalResult == 1 ? "1" : "2";
|
||
|
||
// 5. 构建完整实体
|
||
return new PlcProductionData
|
||
{
|
||
// 基础字段
|
||
PlcIp = ip,
|
||
OccurTime = now,
|
||
LineCode = "line2",
|
||
WorkstationCode = workstationCode,
|
||
WorkstationName = "Ring terminal装配工位",
|
||
ProductModel = productModel,
|
||
ProductName = productName,
|
||
ProductCode = sn1,
|
||
SN1 = sn1,
|
||
SN2 = sn2,
|
||
ReworkFlag = reworkFlag,
|
||
Automanual = machineModel,
|
||
Runstatus = runStatus,
|
||
OnlineStatus = onlineStatusDesc,
|
||
ProduceModel = produceModelDesc,
|
||
QualificationFlag = qualificationFlag,
|
||
ProductionCycle = (int)Math.Round(cycleTime),
|
||
CreatedBy = "PLC",
|
||
CreatedTime = now,
|
||
|
||
// 螺丝1-2参数完整赋值
|
||
Screw1Result = screw1Result.ToString(),
|
||
Screw1Torque = screw1Torque.ToString("F2"),
|
||
Screw1Depth = screw1Depth.ToString("F2"),
|
||
Screw1Angle = screw1Angle.ToString("F2"),
|
||
Screw2Result = screw2Result.ToString(),
|
||
Screw2Torque = screw2Torque.ToString("F2"),
|
||
Screw2Depth = screw2Depth.ToString("F2"),
|
||
Screw2Angle = screw2Angle.ToString("F2"),
|
||
};
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] OP1012读取异常:{ex.Message}");
|
||
return null;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 读取OP102数据(等离子处理工位,DB1001)
|
||
/// </summary>
|
||
public async Task<PlcProductionData> ReadOP102DataAsync(Plc plc, string ip, string workstationCode)
|
||
{
|
||
// 前置校验:快速失败
|
||
if (plc == null || !plc.IsConnected) return null;
|
||
if (string.IsNullOrWhiteSpace(ip) || string.IsNullOrWhiteSpace(workstationCode))
|
||
throw new ArgumentNullException("IP或工位编码不能为空");
|
||
|
||
try
|
||
{
|
||
// 1. 批量创建并行读取任务
|
||
var stringTasks = new Dictionary<string, Task<string>>
|
||
{
|
||
{ "产品型号", ReadPlcStringAsync(plc, _op102StringMap["产品型号"].Addr, _op102StringMap["产品型号"].Len) },
|
||
{ "产品名称", ReadPlcStringAsync(plc, _op102StringMap["产品名称"].Addr, _op102StringMap["产品名称"].Len) },
|
||
{ "查询SN", ReadPlcStringAsync(plc, _op102StringMap["查询SN"].Addr, _op102StringMap["查询SN"].Len) },
|
||
{ "上传结果SN", ReadPlcStringAsync(plc, _op102StringMap["上传结果SN"].Addr, _op102StringMap["上传结果SN"].Len) }
|
||
};
|
||
|
||
var intTasks = new Dictionary<string, Task<int>>
|
||
{
|
||
{ "运行状态", ReadPlcIntAsync(plc, _op102IntMap["运行状态"]) },
|
||
{ "设备模式", ReadPlcIntAsync(plc, _op102IntMap["设备模式"]) },
|
||
{ "设备在线状态", ReadPlcIntAsync(plc, _op102IntMap["设备在线状态"]) },
|
||
{ "生产模式", ReadPlcIntAsync(plc, _op102IntMap["生产模式"]) },
|
||
{ "实际产量", ReadPlcIntAsync(plc, _op102IntMap["实际产量"]) },
|
||
{ "站位结果", ReadPlcIntAsync(plc, _op102IntMap["站位结果"]) },
|
||
{ "托盘号", ReadPlcIntAsync(plc, _op102IntMap["托盘号"]) }
|
||
};
|
||
|
||
var realTasks = new Dictionary<string, Task<float>>
|
||
{
|
||
{ "节拍时间", ReadPlcRealAsync(plc, _op102IntMap["节拍时间"]) }
|
||
};
|
||
|
||
// 2. 并行等待所有任务完成
|
||
var allTasks = new List<Task>();
|
||
allTasks.AddRange(stringTasks.Values);
|
||
allTasks.AddRange(intTasks.Values);
|
||
allTasks.AddRange(realTasks.Values);
|
||
await Task.WhenAll(allTasks).ConfigureAwait(false);
|
||
|
||
// 3. 提取结果
|
||
var now = DateTime.Now;
|
||
var productModel = stringTasks["产品型号"].Result ?? string.Empty;
|
||
var productName = stringTasks["产品名称"].Result ?? string.Empty;
|
||
var sn1 = stringTasks["查询SN"].Result ?? string.Empty;
|
||
var sn2 = stringTasks["上传结果SN"].Result ?? string.Empty;
|
||
|
||
var runStatus = intTasks["运行状态"].Result;
|
||
var machineModel = intTasks["设备模式"].Result;
|
||
var onlineStatus = intTasks["设备在线状态"].Result;
|
||
var produceModel = intTasks["生产模式"].Result;
|
||
var actualOutput = intTasks["实际产量"].Result;
|
||
var stationResult = intTasks["站位结果"].Result;
|
||
var trayNo = intTasks["托盘号"].Result.ToString();
|
||
|
||
var cycleTime = realTasks["节拍时间"].Result;
|
||
|
||
// 4. 业务逻辑转换
|
||
var reworkFlag = produceModel == 2 ? "1" : "0";
|
||
var produceModelDesc = produceModel switch
|
||
{
|
||
1 => "点检模式",
|
||
2 => "返工模式",
|
||
4 => "样件模式",
|
||
5 => "正常模式",
|
||
_ => produceModel.ToString()
|
||
};
|
||
var onlineStatusDesc = onlineStatus == 1 ? "离线" : "在线";
|
||
var qualificationFlag = stationResult == 1 ? "1" : "2";
|
||
|
||
// 6. 构建完整实体
|
||
return new PlcProductionData
|
||
{
|
||
// 基础字段
|
||
PlcIp = ip,
|
||
OccurTime = now,
|
||
LineCode = "line2",
|
||
WorkstationCode = workstationCode,
|
||
WorkstationName = "等离子处理工位",
|
||
ProductModel = productModel,
|
||
ProductName = productName,
|
||
ProductCode = sn1,
|
||
SN1 = sn1,
|
||
SN2 = sn2,
|
||
TrayNo = trayNo,
|
||
ReworkFlag = reworkFlag,
|
||
Automanual = machineModel,
|
||
Runstatus = runStatus,
|
||
OnlineStatus = onlineStatusDesc,
|
||
ProduceModel = produceModelDesc,
|
||
QualificationFlag = qualificationFlag,
|
||
ProductionCycle = (int)Math.Round(cycleTime),
|
||
CreatedBy = "PLC",
|
||
CreatedTime = now,
|
||
};
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] OP102读取异常:{ex.Message}");
|
||
return null;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 读取OP110-1数据(点胶封胶Q3-3636工位,DB1001)
|
||
/// </summary>
|
||
public async Task<PlcProductionData> ReadOP110_1DataAsync(Plc plc, string ip, string workstationCode)
|
||
{
|
||
// 前置校验:快速失败
|
||
if (plc == null || !plc.IsConnected) return null;
|
||
if (string.IsNullOrWhiteSpace(ip) || string.IsNullOrWhiteSpace(workstationCode))
|
||
throw new ArgumentNullException("IP或工位编码不能为空");
|
||
|
||
try
|
||
{
|
||
// 1. 批量创建并行读取任务
|
||
var stringTasks = new Dictionary<string, Task<string>>
|
||
{
|
||
{ "产品型号", ReadPlcStringAsync(plc, _op110_1StringMap["产品型号"].Addr, _op110_1StringMap["产品型号"].Len) },
|
||
{ "产品名称", ReadPlcStringAsync(plc, _op110_1StringMap["产品名称"].Addr, _op110_1StringMap["产品名称"].Len) },
|
||
{ "查询SN", ReadPlcStringAsync(plc, _op110_1StringMap["查询SN"].Addr, _op110_1StringMap["查询SN"].Len) },
|
||
{ "上传结果SN", ReadPlcStringAsync(plc, _op110_1StringMap["上传结果SN"].Addr, _op110_1StringMap["上传结果SN"].Len) }
|
||
};
|
||
|
||
var intTasks = new Dictionary<string, Task<int>>
|
||
{
|
||
{ "运行状态", ReadPlcIntAsync(plc, _op110_1IntMap["运行状态"]) },
|
||
{ "设备模式", ReadPlcIntAsync(plc, _op110_1IntMap["设备模式"]) },
|
||
{ "设备在线状态", ReadPlcIntAsync(plc, _op110_1IntMap["设备在线状态"]) },
|
||
{ "生产模式", ReadPlcIntAsync(plc, _op110_1IntMap["生产模式"]) },
|
||
{ "实际产量", ReadPlcIntAsync(plc, _op110_1IntMap["实际产量"]) },
|
||
{ "相机结果", ReadPlcIntAsync(plc, _op110_1IntMap["相机结果"]) },
|
||
{ "站位结果", ReadPlcIntAsync(plc, _op110_1IntMap["站位结果"]) },
|
||
{ "托盘号", ReadPlcIntAsync(plc, _op110_1IntMap["托盘号"]) }
|
||
};
|
||
|
||
var realTasks = new Dictionary<string, Task<float>>
|
||
{
|
||
{ "节拍时间", ReadPlcRealAsync(plc, _op110_1IntMap["节拍时间"]) }
|
||
};
|
||
|
||
// 2. 并行等待所有任务完成
|
||
var allTasks = new List<Task>();
|
||
allTasks.AddRange(stringTasks.Values);
|
||
allTasks.AddRange(intTasks.Values);
|
||
allTasks.AddRange(realTasks.Values);
|
||
await Task.WhenAll(allTasks).ConfigureAwait(false);
|
||
|
||
// 3. 提取结果
|
||
var now = DateTime.Now;
|
||
var productModel = stringTasks["产品型号"].Result ?? string.Empty;
|
||
var productName = stringTasks["产品名称"].Result ?? string.Empty;
|
||
var sn1 = stringTasks["查询SN"].Result ?? string.Empty;
|
||
var sn2 = stringTasks["上传结果SN"].Result ?? string.Empty;
|
||
|
||
var runStatus = intTasks["运行状态"].Result;
|
||
var machineModel = intTasks["设备模式"].Result;
|
||
var onlineStatus = intTasks["设备在线状态"].Result;
|
||
var produceModel = intTasks["生产模式"].Result;
|
||
var actualOutput = intTasks["实际产量"].Result;
|
||
var cameraResult = intTasks["相机结果"].Result;
|
||
var stationResult = intTasks["站位结果"].Result;
|
||
var trayNo = intTasks["托盘号"].Result.ToString();
|
||
|
||
var cycleTime = realTasks["节拍时间"].Result;
|
||
|
||
// 4. 业务逻辑转换
|
||
var reworkFlag = produceModel == 2 ? "1" : "0";
|
||
var produceModelDesc = produceModel switch
|
||
{
|
||
1 => "点检模式",
|
||
2 => "返工模式",
|
||
4 => "样件模式",
|
||
5 => "正常模式",
|
||
_ => produceModel.ToString()
|
||
};
|
||
var onlineStatusDesc = onlineStatus == 1 ? "离线" : "在线";
|
||
var qualificationFlag = stationResult == 1 ? "1" : "2";
|
||
|
||
// 6. 构建完整实体
|
||
return new PlcProductionData
|
||
{
|
||
// 基础字段
|
||
PlcIp = ip,
|
||
OccurTime = now,
|
||
LineCode = "line2",
|
||
WorkstationCode = workstationCode,
|
||
WorkstationName = "点胶封胶Q3-3636工位",
|
||
ProductModel = productModel,
|
||
ProductName = productName,
|
||
ProductCode = sn1,
|
||
SN1 = sn1,
|
||
SN2 = sn2,
|
||
TrayNo = trayNo,
|
||
ReworkFlag = reworkFlag,
|
||
Automanual = machineModel,
|
||
Runstatus = runStatus,
|
||
OnlineStatus = onlineStatusDesc,
|
||
ProduceModel = produceModelDesc,
|
||
QualificationFlag = qualificationFlag,
|
||
ProductionCycle = (int)Math.Round(cycleTime),
|
||
CreatedBy = "PLC",
|
||
CreatedTime = now,
|
||
CameraResult = cameraResult == 1 ? "1" : "2", // 相机结果映射
|
||
};
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] OP110-1读取异常:{ex.Message}");
|
||
return null;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 读取OP110-2数据(盖板组装&拧紧工位,DB1001)
|
||
/// </summary>
|
||
public async Task<PlcProductionData> ReadOP110_2DataAsync(Plc plc, string ip, string workstationCode)
|
||
{
|
||
// 前置校验:快速失败
|
||
if (plc == null || !plc.IsConnected) return null;
|
||
if (string.IsNullOrWhiteSpace(ip) || string.IsNullOrWhiteSpace(workstationCode))
|
||
throw new ArgumentNullException("IP或工位编码不能为空");
|
||
|
||
try
|
||
{
|
||
// 1. 批量创建并行读取任务
|
||
var stringTasks = new Dictionary<string, Task<string>>
|
||
{
|
||
{ "产品型号", ReadPlcStringAsync(plc, _op110_2StringMap["产品型号"].Addr, _op110_2StringMap["产品型号"].Len) },
|
||
{ "产品名称", ReadPlcStringAsync(plc, _op110_2StringMap["产品名称"].Addr, _op110_2StringMap["产品名称"].Len) },
|
||
{ "合装工位查询SN", ReadPlcStringAsync(plc, _op110_2StringMap["合装工位查询SN"].Addr, _op110_2StringMap["合装工位查询SN"].Len) },
|
||
{ "拧紧工位上传结果SN", ReadPlcStringAsync(plc, _op110_2StringMap["拧紧工位上传结果SN"].Addr, _op110_2StringMap["拧紧工位上传结果SN"].Len) }
|
||
};
|
||
|
||
var intTasks = new Dictionary<string, Task<int>>
|
||
{
|
||
{ "运行状态", ReadPlcIntAsync(plc, _op110_2IntMap["运行状态"]) },
|
||
{ "设备模式", ReadPlcIntAsync(plc, _op110_2IntMap["设备模式"]) },
|
||
{ "设备在线状态", ReadPlcIntAsync(plc, _op110_2IntMap["设备在线状态"]) },
|
||
{ "生产模式", ReadPlcIntAsync(plc, _op110_2IntMap["生产模式"]) },
|
||
{ "实际产量", ReadPlcIntAsync(plc, _op110_2IntMap["实际产量"]) },
|
||
{ "产品总结果", ReadPlcIntAsync(plc, _op110_2IntMap["产品总结果"]) },
|
||
{ "3#螺丝结果", ReadPlcIntAsync(plc, _op110_2IntMap["3#螺丝结果"]) },
|
||
{ "5#螺丝结果", ReadPlcIntAsync(plc, _op110_2IntMap["5#螺丝结果"]) },
|
||
{ "6#螺丝结果", ReadPlcIntAsync(plc, _op110_2IntMap["6#螺丝结果"]) },
|
||
{ "1#螺丝结果", ReadPlcIntAsync(plc, _op110_2IntMap["1#螺丝结果"]) },
|
||
{ "2#螺丝结果", ReadPlcIntAsync(plc, _op110_2IntMap["2#螺丝结果"]) },
|
||
{ "4#螺丝结果", ReadPlcIntAsync(plc, _op110_2IntMap["4#螺丝结果"]) },
|
||
{ "7#螺丝结果", ReadPlcIntAsync(plc, _op110_2IntMap["7#螺丝结果"]) },
|
||
{ "托盘号", ReadPlcIntAsync(plc, _op110_2IntMap["托盘号"]) }
|
||
};
|
||
|
||
var realTasks = new Dictionary<string, Task<float>>
|
||
{
|
||
{ "3#扭矩", ReadPlcRealAsync(plc, _op110_2IntMap["3#扭矩"]) },
|
||
{ "3#深度", ReadPlcRealAsync(plc, _op110_2IntMap["3#深度"]) },
|
||
{ "3#角度", ReadPlcRealAsync(plc, _op110_2IntMap["3#角度"]) },
|
||
{ "3#拧紧时间", ReadPlcRealAsync(plc, _op110_2IntMap["3#拧紧时间"]) },
|
||
{ "5#扭矩", ReadPlcRealAsync(plc, _op110_2IntMap["5#扭矩"]) },
|
||
{ "5#深度", ReadPlcRealAsync(plc, _op110_2IntMap["5#深度"]) },
|
||
{ "5#角度", ReadPlcRealAsync(plc, _op110_2IntMap["5#角度"]) },
|
||
{ "5#拧紧时间", ReadPlcRealAsync(plc, _op110_2IntMap["5#拧紧时间"]) },
|
||
{ "6#扭矩", ReadPlcRealAsync(plc, _op110_2IntMap["6#扭矩"]) },
|
||
{ "6#深度", ReadPlcRealAsync(plc, _op110_2IntMap["6#深度"]) },
|
||
{ "6#角度", ReadPlcRealAsync(plc, _op110_2IntMap["6#角度"]) },
|
||
{ "6#拧紧时间", ReadPlcRealAsync(plc, _op110_2IntMap["6#拧紧时间"]) },
|
||
{ "1#扭矩", ReadPlcRealAsync(plc, _op110_2IntMap["1#扭矩"]) },
|
||
{ "1#深度", ReadPlcRealAsync(plc, _op110_2IntMap["1#深度"]) },
|
||
{ "1#角度", ReadPlcRealAsync(plc, _op110_2IntMap["1#角度"]) },
|
||
{ "1#拧紧时间", ReadPlcRealAsync(plc, _op110_2IntMap["1#拧紧时间"]) },
|
||
{ "2#扭矩", ReadPlcRealAsync(plc, _op110_2IntMap["2#扭矩"]) },
|
||
{ "2#深度", ReadPlcRealAsync(plc, _op110_2IntMap["2#深度"]) },
|
||
{ "2#角度", ReadPlcRealAsync(plc, _op110_2IntMap["2#角度"]) },
|
||
{ "2#拧紧时间", ReadPlcRealAsync(plc, _op110_2IntMap["2#拧紧时间"]) },
|
||
{ "4#扭矩", ReadPlcRealAsync(plc, _op110_2IntMap["4#扭矩"]) },
|
||
{ "4#深度", ReadPlcRealAsync(plc, _op110_2IntMap["4#深度"]) },
|
||
{ "4#角度", ReadPlcRealAsync(plc, _op110_2IntMap["4#角度"]) },
|
||
{ "4#拧紧时间", ReadPlcRealAsync(plc, _op110_2IntMap["4#拧紧时间"]) },
|
||
{ "7#扭矩", ReadPlcRealAsync(plc, _op110_2IntMap["7#扭矩"]) },
|
||
{ "7#深度", ReadPlcRealAsync(plc, _op110_2IntMap["7#深度"]) },
|
||
{ "7#角度", ReadPlcRealAsync(plc, _op110_2IntMap["7#角度"]) },
|
||
{ "7#拧紧时间", ReadPlcRealAsync(plc, _op110_2IntMap["7#拧紧时间"]) },
|
||
{ "节拍时间", ReadPlcRealAsync(plc, _op110_2IntMap["节拍时间"]) }
|
||
};
|
||
|
||
// 2. 并行等待所有任务完成
|
||
var allTasks = new List<Task>();
|
||
allTasks.AddRange(stringTasks.Values);
|
||
allTasks.AddRange(intTasks.Values);
|
||
allTasks.AddRange(realTasks.Values);
|
||
await Task.WhenAll(allTasks).ConfigureAwait(false);
|
||
|
||
// 3. 提取结果
|
||
var now = DateTime.Now;
|
||
var productModel = stringTasks["产品型号"].Result ?? string.Empty;
|
||
var productName = stringTasks["产品名称"].Result ?? string.Empty;
|
||
var sn1 = stringTasks["合装工位查询SN"].Result ?? string.Empty;
|
||
var sn2 = stringTasks["拧紧工位上传结果SN"].Result ?? string.Empty;
|
||
|
||
var runStatus = intTasks["运行状态"].Result;
|
||
var machineModel = intTasks["设备模式"].Result;
|
||
var onlineStatus = intTasks["设备在线状态"].Result;
|
||
var produceModel = intTasks["生产模式"].Result;
|
||
var actualOutput = intTasks["实际产量"].Result;
|
||
var productTotalResult = intTasks["产品总结果"].Result;
|
||
var screw1Result = intTasks["1#螺丝结果"].Result;
|
||
var screw2Result = intTasks["2#螺丝结果"].Result;
|
||
var screw3Result = intTasks["3#螺丝结果"].Result;
|
||
var screw4Result = intTasks["4#螺丝结果"].Result;
|
||
var screw5Result = intTasks["5#螺丝结果"].Result;
|
||
var screw6Result = intTasks["6#螺丝结果"].Result;
|
||
var screw7Result = intTasks["7#螺丝结果"].Result;
|
||
var trayNo = intTasks["托盘号"].Result.ToString();
|
||
|
||
var screw1Torque = realTasks["1#扭矩"].Result;
|
||
var screw1Depth = realTasks["1#深度"].Result;
|
||
var screw1Angle = realTasks["1#角度"].Result;
|
||
var screw1TightenTime = realTasks["1#拧紧时间"].Result;
|
||
var screw2Torque = realTasks["2#扭矩"].Result;
|
||
var screw2Depth = realTasks["2#深度"].Result;
|
||
var screw2Angle = realTasks["2#角度"].Result;
|
||
var screw2TightenTime = realTasks["2#拧紧时间"].Result;
|
||
var screw3Torque = realTasks["3#扭矩"].Result;
|
||
var screw3Depth = realTasks["3#深度"].Result;
|
||
var screw3Angle = realTasks["3#角度"].Result;
|
||
var screw3TightenTime = realTasks["3#拧紧时间"].Result;
|
||
var screw4Torque = realTasks["4#扭矩"].Result;
|
||
var screw4Depth = realTasks["4#深度"].Result;
|
||
var screw4Angle = realTasks["4#角度"].Result;
|
||
var screw4TightenTime = realTasks["4#拧紧时间"].Result;
|
||
var screw5Torque = realTasks["5#扭矩"].Result;
|
||
var screw5Depth = realTasks["5#深度"].Result;
|
||
var screw5Angle = realTasks["5#角度"].Result;
|
||
var screw5TightenTime = realTasks["5#拧紧时间"].Result;
|
||
var screw6Torque = realTasks["6#扭矩"].Result;
|
||
var screw6Depth = realTasks["6#深度"].Result;
|
||
var screw6Angle = realTasks["6#角度"].Result;
|
||
var screw6TightenTime = realTasks["6#拧紧时间"].Result;
|
||
var screw7Torque = realTasks["7#扭矩"].Result;
|
||
var screw7Depth = realTasks["7#深度"].Result;
|
||
var screw7Angle = realTasks["7#角度"].Result;
|
||
var screw7TightenTime = realTasks["7#拧紧时间"].Result;
|
||
var cycleTime = realTasks["节拍时间"].Result;
|
||
|
||
// 4. 业务逻辑转换
|
||
var reworkFlag = produceModel == 2 ? "1" : "0";
|
||
var produceModelDesc = produceModel switch
|
||
{
|
||
1 => "点检模式",
|
||
2 => "返工模式",
|
||
4 => "样件模式",
|
||
5 => "正常模式",
|
||
_ => produceModel.ToString()
|
||
};
|
||
var onlineStatusDesc = onlineStatus == 1 ? "离线" : "在线";
|
||
var qualificationFlag = productTotalResult == 1 ? "1" : "2";
|
||
|
||
// 6. 构建完整实体
|
||
return new PlcProductionData
|
||
{
|
||
// 基础字段
|
||
PlcIp = ip,
|
||
OccurTime = now,
|
||
LineCode = "line2",
|
||
WorkstationCode = workstationCode,
|
||
WorkstationName = "盖板组装&拧紧工位",
|
||
ProductModel = productModel,
|
||
ProductName = productName,
|
||
ProductCode = sn1,
|
||
SN1 = sn1,
|
||
SN2 = sn2,
|
||
TrayNo = trayNo,
|
||
ReworkFlag = reworkFlag,
|
||
Automanual = machineModel,
|
||
Runstatus = runStatus,
|
||
OnlineStatus = onlineStatusDesc,
|
||
ProduceModel = produceModelDesc,
|
||
QualificationFlag = qualificationFlag,
|
||
ProductionCycle = (int)Math.Round(cycleTime),
|
||
CreatedBy = "PLC",
|
||
CreatedTime = now,
|
||
|
||
// 螺丝1-7参数完整赋值
|
||
Screw1Result = screw1Result.ToString(),
|
||
Screw1Torque = screw1Torque.ToString("F2"),
|
||
Screw1Depth = screw1Depth.ToString("F2"),
|
||
Screw1Angle = screw1Angle.ToString("F2"),
|
||
Screw1TightenTime = screw1TightenTime.ToString("F2"),
|
||
Screw2Result = screw2Result.ToString(),
|
||
Screw2Torque = screw2Torque.ToString("F2"),
|
||
Screw2Depth = screw2Depth.ToString("F2"),
|
||
Screw2Angle = screw2Angle.ToString("F2"),
|
||
Screw2TightenTime = screw2TightenTime.ToString("F2"),
|
||
Screw3Result = screw3Result.ToString(),
|
||
Screw3Torque = screw3Torque.ToString("F2"),
|
||
Screw3Depth = screw3Depth.ToString("F2"),
|
||
Screw3Angle = screw3Angle.ToString("F2"),
|
||
Screw3TightenTime = screw3TightenTime.ToString("F2"),
|
||
Screw4Result = screw4Result.ToString(),
|
||
Screw4Torque = screw4Torque.ToString("F2"),
|
||
Screw4Depth = screw4Depth.ToString("F2"),
|
||
Screw4Angle = screw4Angle.ToString("F2"),
|
||
Screw4TightenTime = screw4TightenTime.ToString("F2"),
|
||
Screw5Result = screw5Result.ToString(),
|
||
Screw5Torque = screw5Torque.ToString("F2"),
|
||
Screw5Depth = screw5Depth.ToString("F2"),
|
||
Screw5Angle = screw5Angle.ToString("F2"),
|
||
Screw5TightenTime = screw5TightenTime.ToString("F2"),
|
||
Screw6Result = screw6Result.ToString(),
|
||
Screw6Torque = screw6Torque.ToString("F2"),
|
||
Screw6Depth = screw6Depth.ToString("F2"),
|
||
Screw6Angle = screw6Angle.ToString("F2"),
|
||
Screw6TightenTime = screw6TightenTime.ToString("F2"),
|
||
Screw7Result = screw7Result.ToString(),
|
||
Screw7Torque = screw7Torque.ToString("F2"),
|
||
Screw7Depth = screw7Depth.ToString("F2"),
|
||
Screw7Angle = screw7Angle.ToString("F2"),
|
||
Screw7TightenTime = screw7TightenTime.ToString("F2"),
|
||
|
||
};
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] OP110-2读取异常:{ex.Message}");
|
||
return null;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 读取OP110-3数据(盖板拧紧工位,DB1001)
|
||
/// </summary>
|
||
public async Task<PlcProductionData> ReadOP110_3DataAsync(Plc plc, string ip, string workstationCode)
|
||
{
|
||
// 前置校验:快速失败
|
||
if (plc == null || !plc.IsConnected) return null;
|
||
if (string.IsNullOrWhiteSpace(ip) || string.IsNullOrWhiteSpace(workstationCode))
|
||
throw new ArgumentNullException("IP或工位编码不能为空");
|
||
|
||
try
|
||
{
|
||
// 1. 批量创建并行读取任务
|
||
var stringTasks = new Dictionary<string, Task<string>>
|
||
{
|
||
{ "产品型号", ReadPlcStringAsync(plc, _op110_3StringMap["产品型号"].Addr, _op110_3StringMap["产品型号"].Len) },
|
||
{ "产品名称", ReadPlcStringAsync(plc, _op110_3StringMap["产品名称"].Addr, _op110_3StringMap["产品名称"].Len) },
|
||
{ "查询SN", ReadPlcStringAsync(plc, _op110_3StringMap["查询SN"].Addr, _op110_3StringMap["查询SN"].Len) },
|
||
{ "保存SN", ReadPlcStringAsync(plc, _op110_3StringMap["保存SN"].Addr, _op110_3StringMap["保存SN"].Len) }
|
||
};
|
||
|
||
var intTasks = new Dictionary<string, Task<int>>
|
||
{
|
||
{ "运行状态", ReadPlcIntAsync(plc, _op110_3IntMap["运行状态"]) },
|
||
{ "设备模式", ReadPlcIntAsync(plc, _op110_3IntMap["设备模式"]) },
|
||
{ "设备在线状态", ReadPlcIntAsync(plc, _op110_3IntMap["设备在线状态"]) },
|
||
{ "生产模式", ReadPlcIntAsync(plc, _op110_3IntMap["生产模式"]) },
|
||
{ "实际产量", ReadPlcIntAsync(plc, _op110_3IntMap["实际产量"]) },
|
||
{ "产品总结果", ReadPlcIntAsync(plc, _op110_3IntMap["产品总结果"]) },
|
||
{ "1#螺丝结果", ReadPlcIntAsync(plc, _op110_3IntMap["1#螺丝结果"]) },
|
||
{ "2#螺丝结果", ReadPlcIntAsync(plc, _op110_3IntMap["2#螺丝结果"]) },
|
||
{ "3#螺丝结果", ReadPlcIntAsync(plc, _op110_3IntMap["3#螺丝结果"]) },
|
||
{ "4#螺丝结果", ReadPlcIntAsync(plc, _op110_3IntMap["4#螺丝结果"]) },
|
||
{ "5#螺丝结果", ReadPlcIntAsync(plc, _op110_3IntMap["5#螺丝结果"]) },
|
||
{ "6#螺丝结果", ReadPlcIntAsync(plc, _op110_3IntMap["6#螺丝结果"]) },
|
||
{ "7#螺丝结果", ReadPlcIntAsync(plc, _op110_3IntMap["7#螺丝结果"]) },
|
||
{ "托盘号", ReadPlcIntAsync(plc, _op110_3IntMap["托盘号"]) }
|
||
};
|
||
|
||
var realTasks = new Dictionary<string, Task<float>>
|
||
{
|
||
{ "1#扭矩", ReadPlcRealAsync(plc, _op110_3IntMap["1#扭矩"]) },
|
||
{ "1#深度", ReadPlcRealAsync(plc, _op110_3IntMap["1#深度"]) },
|
||
{ "1#角度", ReadPlcRealAsync(plc, _op110_3IntMap["1#角度"]) },
|
||
{ "1#拧紧时间", ReadPlcRealAsync(plc, _op110_3IntMap["1#拧紧时间"]) },
|
||
{ "2#扭矩", ReadPlcRealAsync(plc, _op110_3IntMap["2#扭矩"]) },
|
||
{ "2#深度", ReadPlcRealAsync(plc, _op110_3IntMap["2#深度"]) },
|
||
{ "2#角度", ReadPlcRealAsync(plc, _op110_3IntMap["2#角度"]) },
|
||
{ "2#拧紧时间", ReadPlcRealAsync(plc, _op110_3IntMap["2#拧紧时间"]) },
|
||
{ "3#扭矩", ReadPlcRealAsync(plc, _op110_3IntMap["3#扭矩"]) },
|
||
{ "3#深度", ReadPlcRealAsync(plc, _op110_3IntMap["3#深度"]) },
|
||
{ "3#角度", ReadPlcRealAsync(plc, _op110_3IntMap["3#角度"]) },
|
||
{ "3#拧紧时间", ReadPlcRealAsync(plc, _op110_3IntMap["3#拧紧时间"]) },
|
||
{ "4#扭矩", ReadPlcRealAsync(plc, _op110_3IntMap["4#扭矩"]) },
|
||
{ "4#深度", ReadPlcRealAsync(plc, _op110_3IntMap["4#深度"]) },
|
||
{ "4#角度", ReadPlcRealAsync(plc, _op110_3IntMap["4#角度"]) },
|
||
{ "4#拧紧时间", ReadPlcRealAsync(plc, _op110_3IntMap["4#拧紧时间"]) },
|
||
{ "5#扭矩", ReadPlcRealAsync(plc, _op110_3IntMap["5#扭矩"]) },
|
||
{ "5#深度", ReadPlcRealAsync(plc, _op110_3IntMap["5#深度"]) },
|
||
{ "5#角度", ReadPlcRealAsync(plc, _op110_3IntMap["5#角度"]) },
|
||
{ "5#拧紧时间", ReadPlcRealAsync(plc, _op110_3IntMap["5#拧紧时间"]) },
|
||
{ "6#扭矩", ReadPlcRealAsync(plc, _op110_3IntMap["6#扭矩"]) },
|
||
{ "6#深度", ReadPlcRealAsync(plc, _op110_3IntMap["6#深度"]) },
|
||
{ "6#角度", ReadPlcRealAsync(plc, _op110_3IntMap["6#角度"]) },
|
||
{ "6#拧紧时间", ReadPlcRealAsync(plc, _op110_3IntMap["6#拧紧时间"]) },
|
||
{ "7#扭矩", ReadPlcRealAsync(plc, _op110_3IntMap["7#扭矩"]) },
|
||
{ "7#深度", ReadPlcRealAsync(plc, _op110_3IntMap["7#深度"]) },
|
||
{ "7#角度", ReadPlcRealAsync(plc, _op110_3IntMap["7#角度"]) },
|
||
{ "7#拧紧时间", ReadPlcRealAsync(plc, _op110_3IntMap["7#拧紧时间"]) },
|
||
{ "节拍时间", ReadPlcRealAsync(plc, _op110_3IntMap["节拍时间"]) }
|
||
};
|
||
|
||
// 2. 并行等待所有任务完成
|
||
var allTasks = new List<Task>();
|
||
allTasks.AddRange(stringTasks.Values);
|
||
allTasks.AddRange(intTasks.Values);
|
||
allTasks.AddRange(realTasks.Values);
|
||
await Task.WhenAll(allTasks).ConfigureAwait(false);
|
||
|
||
// 3. 提取结果
|
||
var now = DateTime.Now;
|
||
var productModel = stringTasks["产品型号"].Result ?? string.Empty;
|
||
var productName = stringTasks["产品名称"].Result ?? string.Empty;
|
||
var sn1 = stringTasks["查询SN"].Result ?? string.Empty;
|
||
var sn2 = stringTasks["保存SN"].Result ?? string.Empty;
|
||
|
||
var runStatus = intTasks["运行状态"].Result;
|
||
var machineModel = intTasks["设备模式"].Result;
|
||
var onlineStatus = intTasks["设备在线状态"].Result;
|
||
var produceModel = intTasks["生产模式"].Result;
|
||
var actualOutput = intTasks["实际产量"].Result;
|
||
var productTotalResult = intTasks["产品总结果"].Result;
|
||
var screw1Result = intTasks["1#螺丝结果"].Result;
|
||
var screw2Result = intTasks["2#螺丝结果"].Result;
|
||
var screw3Result = intTasks["3#螺丝结果"].Result;
|
||
var screw4Result = intTasks["4#螺丝结果"].Result;
|
||
var screw5Result = intTasks["5#螺丝结果"].Result;
|
||
var screw6Result = intTasks["6#螺丝结果"].Result;
|
||
var screw7Result = intTasks["7#螺丝结果"].Result;
|
||
var trayNo = intTasks["托盘号"].Result.ToString();
|
||
|
||
var screw1Torque = realTasks["1#扭矩"].Result;
|
||
var screw1Depth = realTasks["1#深度"].Result;
|
||
var screw1Angle = realTasks["1#角度"].Result;
|
||
var screw1TightenTime = realTasks["1#拧紧时间"].Result;
|
||
var screw2Torque = realTasks["2#扭矩"].Result;
|
||
var screw2Depth = realTasks["2#深度"].Result;
|
||
var screw2Angle = realTasks["2#角度"].Result;
|
||
var screw2TightenTime = realTasks["2#拧紧时间"].Result;
|
||
var screw3Torque = realTasks["3#扭矩"].Result;
|
||
var screw3Depth = realTasks["3#深度"].Result;
|
||
var screw3Angle = realTasks["3#角度"].Result;
|
||
var screw3TightenTime = realTasks["3#拧紧时间"].Result;
|
||
var screw4Torque = realTasks["4#扭矩"].Result;
|
||
var screw4Depth = realTasks["4#深度"].Result;
|
||
var screw4Angle = realTasks["4#角度"].Result;
|
||
var screw4TightenTime = realTasks["4#拧紧时间"].Result;
|
||
var screw5Torque = realTasks["5#扭矩"].Result;
|
||
var screw5Depth = realTasks["5#深度"].Result;
|
||
var screw5Angle = realTasks["5#角度"].Result;
|
||
var screw5TightenTime = realTasks["5#拧紧时间"].Result;
|
||
var screw6Torque = realTasks["6#扭矩"].Result;
|
||
var screw6Depth = realTasks["6#深度"].Result;
|
||
var screw6Angle = realTasks["6#角度"].Result;
|
||
var screw6TightenTime = realTasks["6#拧紧时间"].Result;
|
||
var screw7Torque = realTasks["7#扭矩"].Result;
|
||
var screw7Depth = realTasks["7#深度"].Result;
|
||
var screw7Angle = realTasks["7#角度"].Result;
|
||
var screw7TightenTime = realTasks["7#拧紧时间"].Result;
|
||
var cycleTime = realTasks["节拍时间"].Result;
|
||
|
||
// 4. 业务逻辑转换
|
||
var reworkFlag = produceModel == 2 ? "1" : "0";
|
||
var produceModelDesc = produceModel switch
|
||
{
|
||
1 => "点检模式",
|
||
2 => "返工模式",
|
||
4 => "样件模式",
|
||
5 => "正常模式",
|
||
_ => produceModel.ToString()
|
||
};
|
||
var onlineStatusDesc = onlineStatus == 1 ? "离线" : "在线";
|
||
var qualificationFlag = productTotalResult == 1 ? "1" : "2";
|
||
|
||
// 5. 构建完整实体
|
||
return new PlcProductionData
|
||
{
|
||
// 基础字段
|
||
PlcIp = ip,
|
||
OccurTime = now,
|
||
LineCode = "line2",
|
||
WorkstationCode = workstationCode,
|
||
WorkstationName = "盖板拧紧工位",
|
||
ProductModel = productModel,
|
||
ProductName = productName,
|
||
ProductCode = sn1,
|
||
SN1 = sn1,
|
||
SN2 = sn2,
|
||
TrayNo = trayNo,
|
||
ReworkFlag = reworkFlag,
|
||
Automanual = machineModel,
|
||
Runstatus = runStatus,
|
||
OnlineStatus = onlineStatusDesc,
|
||
ProduceModel = produceModelDesc,
|
||
QualificationFlag = qualificationFlag,
|
||
ProductionCycle = (int)Math.Round(cycleTime),
|
||
CreatedBy = "PLC",
|
||
CreatedTime = now,
|
||
|
||
// 螺丝1-7参数完整赋值
|
||
Screw1Result = screw1Result.ToString(),
|
||
Screw1Torque = screw1Torque.ToString("F2"),
|
||
Screw1Depth = screw1Depth.ToString("F2"),
|
||
Screw1Angle = screw1Angle.ToString("F2"),
|
||
Screw1TightenTime = screw1TightenTime.ToString("F2"),
|
||
Screw2Result = screw2Result.ToString(),
|
||
Screw2Torque = screw2Torque.ToString("F2"),
|
||
Screw2Depth = screw2Depth.ToString("F2"),
|
||
Screw2Angle = screw2Angle.ToString("F2"),
|
||
Screw2TightenTime = screw2TightenTime.ToString("F2"),
|
||
Screw3Result = screw3Result.ToString(),
|
||
Screw3Torque = screw3Torque.ToString("F2"),
|
||
Screw3Depth = screw3Depth.ToString("F2"),
|
||
Screw3Angle = screw3Angle.ToString("F2"),
|
||
Screw3TightenTime = screw3TightenTime.ToString("F2"),
|
||
Screw4Result = screw4Result.ToString(),
|
||
Screw4Torque = screw4Torque.ToString("F2"),
|
||
Screw4Depth = screw4Depth.ToString("F2"),
|
||
Screw4Angle = screw4Angle.ToString("F2"),
|
||
Screw4TightenTime = screw4TightenTime.ToString("F2"),
|
||
Screw5Result = screw5Result.ToString(),
|
||
Screw5Torque = screw5Torque.ToString("F2"),
|
||
Screw5Depth = screw5Depth.ToString("F2"),
|
||
Screw5Angle = screw5Angle.ToString("F2"),
|
||
Screw5TightenTime = screw5TightenTime.ToString("F2"),
|
||
Screw6Result = screw6Result.ToString(),
|
||
Screw6Torque = screw6Torque.ToString("F2"),
|
||
Screw6Depth = screw6Depth.ToString("F2"),
|
||
Screw6Angle = screw6Angle.ToString("F2"),
|
||
Screw6TightenTime = screw6TightenTime.ToString("F2"),
|
||
Screw7Result = screw7Result.ToString(),
|
||
Screw7Torque = screw7Torque.ToString("F2"),
|
||
Screw7Depth = screw7Depth.ToString("F2"),
|
||
Screw7Angle = screw7Angle.ToString("F2"),
|
||
Screw7TightenTime = screw7TightenTime.ToString("F2"),
|
||
|
||
};
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] OP110-3读取异常:{ex.Message}");
|
||
return null;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 读取OP115数据(自动上下料到固化炉工位,DB1001)
|
||
/// </summary>
|
||
public async Task<PlcProductionData> ReadOP115DataAsync(Plc plc, string ip, string workstationCode)
|
||
{
|
||
// 前置校验:快速失败
|
||
if (plc == null || !plc.IsConnected) return null;
|
||
if (string.IsNullOrWhiteSpace(ip) || string.IsNullOrWhiteSpace(workstationCode))
|
||
throw new ArgumentNullException("IP或工位编码不能为空");
|
||
|
||
try
|
||
{
|
||
// 1. 批量创建并行读取任务
|
||
var stringTasks = new Dictionary<string, Task<string>>
|
||
{
|
||
{ "产品型号", ReadPlcStringAsync(plc, _op115StringMap["产品型号"].Addr, _op115StringMap["产品型号"].Len) },
|
||
{ "产品名称", ReadPlcStringAsync(plc, _op115StringMap["产品名称"].Addr, _op115StringMap["产品名称"].Len) }
|
||
};
|
||
|
||
var intTasks = new Dictionary<string, Task<int>>
|
||
{
|
||
{ "运行状态", ReadPlcIntAsync(plc, _op115IntMap["运行状态"]) },
|
||
{ "设备模式", ReadPlcIntAsync(plc, _op115IntMap["设备模式"]) },
|
||
{ "设备在线状态", ReadPlcIntAsync(plc, _op115IntMap["设备在线状态"]) },
|
||
{ "生产模式", ReadPlcIntAsync(plc, _op115IntMap["生产模式"]) }
|
||
};
|
||
|
||
// 2. 并行等待所有任务完成
|
||
var allTasks = new List<Task>();
|
||
allTasks.AddRange(stringTasks.Values);
|
||
allTasks.AddRange(intTasks.Values);
|
||
await Task.WhenAll(allTasks).ConfigureAwait(false);
|
||
|
||
// 3. 提取结果
|
||
var now = DateTime.Now;
|
||
var productModel = stringTasks["产品型号"].Result ?? string.Empty;
|
||
var productName = stringTasks["产品名称"].Result ?? string.Empty;
|
||
|
||
var runStatus = intTasks["运行状态"].Result;
|
||
var machineModel = intTasks["设备模式"].Result;
|
||
var onlineStatus = intTasks["设备在线状态"].Result;
|
||
var produceModel = intTasks["生产模式"].Result;
|
||
|
||
// 4. 业务逻辑转换
|
||
var reworkFlag = produceModel == 2 ? "1" : "0";
|
||
var produceModelDesc = produceModel switch
|
||
{
|
||
1 => "点检模式",
|
||
2 => "返工模式",
|
||
4 => "样件模式",
|
||
5 => "正常模式",
|
||
_ => produceModel.ToString()
|
||
};
|
||
var onlineStatusDesc = onlineStatus == 1 ? "离线" : "在线";
|
||
var qualificationFlag = "1"; // 上下料工位默认合格
|
||
|
||
// 5. 构建完整实体
|
||
return new PlcProductionData
|
||
{
|
||
// 基础字段
|
||
PlcIp = ip,
|
||
OccurTime = now,
|
||
LineCode = "line2",
|
||
WorkstationCode = workstationCode,
|
||
WorkstationName = "自动上下料到固化炉工位",
|
||
ProductModel = productModel,
|
||
ProductName = productName,
|
||
ReworkFlag = reworkFlag,
|
||
Automanual = machineModel,
|
||
Runstatus = runStatus,
|
||
OnlineStatus = onlineStatusDesc,
|
||
ProduceModel = produceModelDesc,
|
||
QualificationFlag = qualificationFlag,
|
||
CreatedBy = "PLC",
|
||
CreatedTime = now,
|
||
};
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] OP115读取异常:{ex.Message}");
|
||
return null;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 读取OP140数据(气密性测试工位,DB1001)
|
||
/// </summary>
|
||
public async Task<PlcProductionData> ReadOP140DataAsync(Plc plc, string ip, string workstationCode)
|
||
{
|
||
// 前置校验:快速失败
|
||
if (plc == null || !plc.IsConnected) return null;
|
||
if (string.IsNullOrWhiteSpace(ip) || string.IsNullOrWhiteSpace(workstationCode))
|
||
throw new ArgumentNullException("IP或工位编码不能为空");
|
||
|
||
try
|
||
{
|
||
// 1. 批量创建并行读取任务
|
||
var stringTasks = new Dictionary<string, Task<string>>
|
||
{
|
||
{ "产品型号", ReadPlcStringAsync(plc, _op140StringMap["产品型号"].Addr, _op140StringMap["产品型号"].Len) },
|
||
{ "产品名称", ReadPlcStringAsync(plc, _op140StringMap["产品名称"].Addr, _op140StringMap["产品名称"].Len) },
|
||
{ "查询SN", ReadPlcStringAsync(plc, _op140StringMap["查询SN"].Addr, _op140StringMap["查询SN"].Len) }
|
||
};
|
||
|
||
var intTasks = new Dictionary<string, Task<int>>
|
||
{
|
||
{ "运行状态", ReadPlcIntAsync(plc, _op140IntMap["运行状态"]) },
|
||
{ "设备模式", ReadPlcIntAsync(plc, _op140IntMap["设备模式"]) },
|
||
{ "设备在线状态", ReadPlcIntAsync(plc, _op140IntMap["设备在线状态"]) },
|
||
{ "生产模式", ReadPlcIntAsync(plc, _op140IntMap["生产模式"]) },
|
||
{ "实际产量", ReadPlcIntAsync(plc, _op140IntMap["实际产量"]) },
|
||
{ "产品结果", ReadPlcIntAsync(plc, _op140IntMap["产品结果"]) },
|
||
{ "Socket", ReadPlcIntAsync(plc, _op140IntMap["Socket"]) },
|
||
{ "托盘号", ReadPlcIntAsync(plc, _op140IntMap["托盘号"]) }
|
||
};
|
||
|
||
var realTasks = new Dictionary<string, Task<float>>
|
||
{
|
||
{ "压力值", ReadPlcRealAsync(plc, _op140IntMap["压力值"]) },
|
||
{ "泄露值", ReadPlcRealAsync(plc, _op140IntMap["泄露值"]) },
|
||
{ "节拍时间", ReadPlcRealAsync(plc, _op140IntMap["节拍时间"]) }
|
||
};
|
||
|
||
// 2. 并行等待所有任务完成
|
||
var allTasks = new List<Task>();
|
||
allTasks.AddRange(stringTasks.Values);
|
||
allTasks.AddRange(intTasks.Values);
|
||
allTasks.AddRange(realTasks.Values);
|
||
await Task.WhenAll(allTasks).ConfigureAwait(false);
|
||
|
||
// 3. 提取结果
|
||
var now = DateTime.Now;
|
||
var productModel = stringTasks["产品型号"].Result ?? string.Empty;
|
||
var productName = stringTasks["产品名称"].Result ?? string.Empty;
|
||
var sn1 = stringTasks["查询SN"].Result ?? string.Empty;
|
||
|
||
var runStatus = intTasks["运行状态"].Result;
|
||
var machineModel = intTasks["设备模式"].Result;
|
||
var onlineStatus = intTasks["设备在线状态"].Result;
|
||
var produceModel = intTasks["生产模式"].Result;
|
||
var actualOutput = intTasks["实际产量"].Result;
|
||
var productResult = intTasks["产品结果"].Result;
|
||
var socket = intTasks["Socket"].Result;
|
||
var trayNo = intTasks["托盘号"].Result.ToString();
|
||
|
||
var pressure = realTasks["压力值"].Result;
|
||
var leak = realTasks["泄露值"].Result;
|
||
var cycleTime = realTasks["节拍时间"].Result;
|
||
|
||
// 4. 业务逻辑转换
|
||
var reworkFlag = produceModel == 2 ? "1" : "0";
|
||
var produceModelDesc = produceModel switch
|
||
{
|
||
1 => "点检模式",
|
||
2 => "返工模式",
|
||
4 => "样件模式",
|
||
5 => "正常模式",
|
||
_ => produceModel.ToString()
|
||
};
|
||
var onlineStatusDesc = onlineStatus == 1 ? "离线" : "在线";
|
||
var qualificationFlag = productResult == 1 ? "1" : "2";
|
||
|
||
// 5. 构建完整实体
|
||
return new PlcProductionData
|
||
{
|
||
// 基础字段
|
||
PlcIp = ip,
|
||
OccurTime = now,
|
||
LineCode = "line2",
|
||
WorkstationCode = workstationCode,
|
||
WorkstationName = "气密性测试工位",
|
||
ProductModel = productModel,
|
||
ProductName = productName,
|
||
ProductCode = sn1,
|
||
SN1 = sn1,
|
||
TrayNo = trayNo,
|
||
ReworkFlag = reworkFlag,
|
||
Automanual = machineModel,
|
||
Runstatus = runStatus,
|
||
OnlineStatus = onlineStatusDesc,
|
||
ProduceModel = produceModelDesc,
|
||
QualificationFlag = qualificationFlag,
|
||
ProductionCycle = (int)Math.Round(cycleTime),
|
||
CreatedBy = "PLC",
|
||
CreatedTime = now,
|
||
|
||
// 测试参数
|
||
PinPressure1 = pressure.ToString("F2"),
|
||
PinPressure2 = leak.ToString("F2"),
|
||
};
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] OP140读取异常:{ex.Message}");
|
||
return null;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 读取OP165数据(自动贴标工位,DB1001)
|
||
/// </summary>
|
||
public async Task<PlcProductionData> ReadOP165DataAsync(Plc plc, string ip, string workstationCode)
|
||
{
|
||
// 前置校验:快速失败
|
||
if (plc == null || !plc.IsConnected) return null;
|
||
if (string.IsNullOrWhiteSpace(ip) || string.IsNullOrWhiteSpace(workstationCode))
|
||
throw new ArgumentNullException("IP或工位编码不能为空");
|
||
|
||
try
|
||
{
|
||
// 1. 批量创建并行读取任务
|
||
var stringTasks = new Dictionary<string, Task<string>>
|
||
{
|
||
{ "产品型号", ReadPlcStringAsync(plc, _op165StringMap["产品型号"].Addr, _op165StringMap["产品型号"].Len) },
|
||
{ "产品名称", ReadPlcStringAsync(plc, _op165StringMap["产品名称"].Addr, _op165StringMap["产品名称"].Len) },
|
||
{ "查询SN", ReadPlcStringAsync(plc, _op165StringMap["查询SN"].Addr, _op165StringMap["查询SN"].Len) },
|
||
{ "结果SN", ReadPlcStringAsync(plc, _op165StringMap["结果SN"].Addr, _op165StringMap["结果SN"].Len) },
|
||
{ "二维码内容", ReadPlcStringAsync(plc, _op165StringMap["二维码内容"].Addr, _op165StringMap["二维码内容"].Len) },
|
||
{ "下料工位条码", ReadPlcStringAsync(plc, _op165StringMap["下料工位条码"].Addr, _op165StringMap["下料工位条码"].Len) }
|
||
};
|
||
|
||
var intTasks = new Dictionary<string, Task<int>>
|
||
{
|
||
{ "运行状态", ReadPlcIntAsync(plc, _op165IntMap["运行状态"]) },
|
||
{ "设备模式", ReadPlcIntAsync(plc, _op165IntMap["设备模式"]) },
|
||
{ "设备在线状态", ReadPlcIntAsync(plc, _op165IntMap["设备在线状态"]) },
|
||
{ "生产模式", ReadPlcIntAsync(plc, _op165IntMap["生产模式"]) },
|
||
{ "实际产量", ReadPlcIntAsync(plc, _op165IntMap["实际产量"]) },
|
||
{ "产品总结果", ReadPlcIntAsync(plc, _op165IntMap["产品总结果"]) },
|
||
{ "轴承检测结果", ReadPlcIntAsync(plc, _op165IntMap["轴承检测结果"]) },
|
||
{ "贴标拍照结果", ReadPlcIntAsync(plc, _op165IntMap["贴标拍照结果"]) },
|
||
{ "托盘号", ReadPlcIntAsync(plc, _op165IntMap["托盘号"]) }
|
||
};
|
||
|
||
var realTasks = new Dictionary<string, Task<float>>
|
||
{
|
||
{ "节拍时间", ReadPlcRealAsync(plc, _op165IntMap["节拍时间"]) }
|
||
};
|
||
|
||
// 2. 并行等待所有任务完成
|
||
var allTasks = new List<Task>();
|
||
allTasks.AddRange(stringTasks.Values);
|
||
allTasks.AddRange(intTasks.Values);
|
||
allTasks.AddRange(realTasks.Values);
|
||
await Task.WhenAll(allTasks).ConfigureAwait(false);
|
||
|
||
// 3. 提取结果
|
||
var now = DateTime.Now;
|
||
var productModel = stringTasks["产品型号"].Result ?? string.Empty;
|
||
var productName = stringTasks["产品名称"].Result ?? string.Empty;
|
||
var sn1 = stringTasks["查询SN"].Result ?? string.Empty;
|
||
var sn2 = stringTasks["结果SN"].Result ?? string.Empty;
|
||
var qrCode = stringTasks["二维码内容"].Result ?? string.Empty;
|
||
var outFeedBarcode = stringTasks["下料工位条码"].Result ?? string.Empty;
|
||
|
||
var runStatus = intTasks["运行状态"].Result;
|
||
var machineModel = intTasks["设备模式"].Result;
|
||
var onlineStatus = intTasks["设备在线状态"].Result;
|
||
var produceModel = intTasks["生产模式"].Result;
|
||
var actualOutput = intTasks["实际产量"].Result;
|
||
var productTotalResult = intTasks["产品总结果"].Result;
|
||
var bearingResult = intTasks["轴承检测结果"].Result;
|
||
var labelPhotoResult = intTasks["贴标拍照结果"].Result;
|
||
var trayNo = intTasks["托盘号"].Result.ToString();
|
||
|
||
var cycleTime = realTasks["节拍时间"].Result;
|
||
|
||
// 4. 业务逻辑转换
|
||
var reworkFlag = produceModel == 2 ? "1" : "0";
|
||
var produceModelDesc = produceModel switch
|
||
{
|
||
1 => "点检模式",
|
||
2 => "返工模式",
|
||
4 => "样件模式",
|
||
5 => "正常模式",
|
||
_ => produceModel.ToString()
|
||
};
|
||
var onlineStatusDesc = onlineStatus == 1 ? "离线" : "在线";
|
||
var qualificationFlag = productTotalResult == 1 ? "1" : "2";
|
||
|
||
// 6. 构建完整实体
|
||
return new PlcProductionData
|
||
{
|
||
// 基础字段
|
||
PlcIp = ip,
|
||
OccurTime = now,
|
||
LineCode = "line2",
|
||
WorkstationCode = workstationCode,
|
||
WorkstationName = "自动贴标工位",
|
||
ProductModel = productModel,
|
||
ProductName = productName,
|
||
ProductCode = sn1,
|
||
SN1 = sn1,
|
||
SN2 = sn2,
|
||
TrayNo = trayNo,
|
||
ReworkFlag = reworkFlag,
|
||
Automanual = machineModel,
|
||
Runstatus = runStatus,
|
||
OnlineStatus = onlineStatusDesc,
|
||
ProduceModel = produceModelDesc,
|
||
QualificationFlag = qualificationFlag,
|
||
ProductionCycle = (int)Math.Round(cycleTime),
|
||
CreatedBy = "PLC",
|
||
CreatedTime = now,
|
||
CameraResult = labelPhotoResult == 1 ? "1" : "2", // 贴标拍照结果映射
|
||
};
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] OP165读取异常:{ex.Message}");
|
||
return null;
|
||
}
|
||
}
|
||
#endregion
|
||
|
||
// 可选:添加连接池清理方法(防止长期运行导致连接泄露)
|
||
private void CleanupExpiredConnections()
|
||
{
|
||
var expiredTime = DateTime.Now.AddMinutes(-5); // 5分钟未使用的连接清理
|
||
// 先复制所有Key到列表,避免遍历中修改原集合
|
||
var allKeys = _plcConnPool.Keys.ToList();
|
||
|
||
foreach (var key in allKeys)
|
||
{
|
||
if (!_plcConnPool.TryGetValue(key, out var poolItem))
|
||
continue;
|
||
|
||
if (poolItem.LastUsedTime < expiredTime)
|
||
{
|
||
if (_plcConnPool.TryRemove(key, out var removedItem))
|
||
{
|
||
try
|
||
{
|
||
if (removedItem.Client != null && removedItem.Client.IsConnected)
|
||
removedItem.Client.Close();
|
||
if (removedItem.Client is IDisposable disposable)
|
||
disposable.Dispose();
|
||
// 可选:置空引用,帮助GC回收
|
||
removedItem.Client = null;
|
||
}
|
||
catch
|
||
{
|
||
// 静默释放,避免清理失败影响主流程
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 在类的构造函数中启动定时清理(可选)
|
||
// public PlcService(IOptions<GlobalPlcConfig> globalConfig)
|
||
// {
|
||
// _globalConfig = globalConfig?.Value ?? throw new ArgumentNullException(nameof(globalConfig));
|
||
// _plcConfigs = initPlcConfigs(_plcConfigs);
|
||
// // 每30秒清理一次过期连接
|
||
// new Timer(_ => CleanupExpiredConnections(), null, TimeSpan.Zero, TimeSpan.FromSeconds(30));
|
||
// }
|
||
|
||
/// <summary>
|
||
/// 测试单个PLC的连接、读、写功能
|
||
/// </summary>
|
||
/// <param name="config">单个PLC的配置参数</param>
|
||
/// <param name="cancellationToken">取消令牌</param>
|
||
/// <returns>测试结果</returns>
|
||
public async Task<PlcTestResult> TestSinglePlcAsync(PlcConfig config, CancellationToken cancellationToken = default)
|
||
{
|
||
// 1. 极简参数校验(提前返回,减少嵌套)
|
||
if (config == null)
|
||
throw new ArgumentNullException(nameof(config), "PLC测试配置不能为空");
|
||
if (string.IsNullOrWhiteSpace(config.Ip))
|
||
return new PlcTestResult { PlcName = config.PlcName, Ip = config.Ip, ConnectSuccess = false, ConnectMessage = "IP地址为空" };
|
||
|
||
var result = new PlcTestResult { PlcName = config.PlcName, Ip = config.Ip };
|
||
Plc plc = null;
|
||
|
||
try
|
||
{
|
||
// 2. 初始化PLC客户端
|
||
plc = CreatePlcClient(CpuType.S71500, config.Ip, config.Rack, config.Slot);
|
||
|
||
// 3. 带超时的连接测试(精简逻辑)
|
||
var connectTask = OpenPlcConnectionAsync(plc);
|
||
if (await Task.WhenAny(connectTask, Task.Delay(_globalConfig.ReadWriteTimeout, cancellationToken)) != connectTask)
|
||
{
|
||
result.ConnectSuccess = false;
|
||
result.ConnectMessage = $"连接超时({_globalConfig.ReadWriteTimeout}ms)";
|
||
return result;
|
||
}
|
||
await connectTask;
|
||
|
||
// 4. 连接状态校验(精简分支)
|
||
if (!plc.IsConnected)
|
||
{
|
||
result.ConnectSuccess = false;
|
||
result.ConnectMessage = "连接失败(PLC未返回连接状态)";
|
||
return result;
|
||
}
|
||
result.ConnectSuccess = true;
|
||
result.ConnectMessage = "连接成功";
|
||
|
||
const string TestAddress = "DB1010.DBB238";
|
||
if (!string.IsNullOrWhiteSpace(TestAddress))
|
||
{
|
||
// 读取测试
|
||
try
|
||
{
|
||
var readValue = await Task.Run(() => plc.Read(TestAddress), cancellationToken);
|
||
result.ReadSuccess = true;
|
||
result.ReadValue = FormatPlcValue(readValue);
|
||
result.ReadMessage = "通用地址读取成功";
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
result.ReadSuccess = false;
|
||
result.ReadMessage = $"通用地址读取失败:{ex.Message}";
|
||
}
|
||
|
||
// 写入测试
|
||
try
|
||
{
|
||
bool writeOk = await Task.Run(() => WritePlcValue(plc, TestAddress, "1"), cancellationToken);
|
||
result.WriteSuccess = writeOk;
|
||
result.WriteMessage = writeOk ? "写入成功" : "写入失败(值类型与地址不匹配)";
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
result.WriteSuccess = false;
|
||
result.WriteMessage = $"写入失败:{ex.Message}";
|
||
}
|
||
}
|
||
|
||
|
||
}
|
||
catch (OperationCanceledException)
|
||
{
|
||
result.ConnectSuccess = false;
|
||
result.ConnectMessage = "测试被取消";
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
result.ConnectSuccess = false;
|
||
result.ConnectMessage = $"连接异常:{ex.Message}";
|
||
}
|
||
finally
|
||
{
|
||
ReleasePlcConnection(plc);
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 批量测试配置文件中所有PLC的读写功能
|
||
/// </summary>
|
||
/// <param name="cancellationToken">取消令牌</param>
|
||
/// <returns>所有PLC的测试结果列表</returns>
|
||
public async Task<List<PlcTestResult>> BatchTestAllPlcAsync(CancellationToken cancellationToken = default)
|
||
{
|
||
if (_plcConfigs == null || _plcConfigs.Count == 0)
|
||
throw new InvalidOperationException("未从配置文件加载到任何PLC参数,请检查PlcConfigs配置");
|
||
|
||
// 并行测试所有PLC(带最大并行度限制,避免资源耗尽)
|
||
var testTasks = _plcConfigs
|
||
.Select(config => TestSinglePlcAsync(config, cancellationToken))
|
||
.ToList();
|
||
|
||
var results = await Task.WhenAll(testTasks);
|
||
return results.ToList();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 单独读取指定PLC的某个地址数据
|
||
/// </summary>
|
||
/// <param name="ip">PLC的IP地址</param>
|
||
/// <param name="rack">机架号</param>
|
||
/// <param name="slot">槽位号</param>
|
||
/// <param name="address">读取地址(如DB1.DBD0)</param>
|
||
/// <param name="cpuType">PLC型号</param>
|
||
/// <returns>读取结果(成功状态、值、消息)</returns>
|
||
public async Task<(bool Success, string Value, string Message)> ReadPlcDataAsync(
|
||
string ip,
|
||
short rack,
|
||
short slot,
|
||
string address,
|
||
CpuType cpuType = CpuType.S71500)
|
||
{
|
||
// 参数校验
|
||
if (string.IsNullOrWhiteSpace(ip))
|
||
return (false, "", "PLC IP地址不能为空");
|
||
if (string.IsNullOrWhiteSpace(address))
|
||
return (false, "", "读取地址不能为空");
|
||
|
||
Plc plc = null;
|
||
try
|
||
{
|
||
plc = CreatePlcClient(cpuType, ip, rack, slot);
|
||
await OpenPlcConnectionAsync(plc);
|
||
|
||
if (!plc.IsConnected)
|
||
return (false, "", "PLC连接失败,请检查IP/机架号/槽位号是否正确");
|
||
|
||
var value = await Task.Run(() => plc.Read(address));
|
||
return (true, FormatPlcValue(value), "读取成功");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
return (false, "", $"读取失败:{ex.Message}");
|
||
}
|
||
finally
|
||
{
|
||
ReleasePlcConnection(plc);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 单独写入数据到指定PLC的某个地址
|
||
/// </summary>
|
||
/// <param name="ip">PLC的IP地址</param>
|
||
/// <param name="rack">机架号</param>
|
||
/// <param name="slot">槽位号</param>
|
||
/// <param name="address">写入地址(如DB1.DBD0)</param>
|
||
/// <param name="valueStr">写入的值(字符串格式)</param>
|
||
/// <param name="cpuType">PLC型号</param>
|
||
/// <returns>写入结果(成功状态、消息)</returns>
|
||
public async Task<(bool Success, string Message)> WritePlcDataAsync(
|
||
string ip,
|
||
short rack,
|
||
short slot,
|
||
string address,
|
||
string valueStr,
|
||
CpuType cpuType = CpuType.S71500)
|
||
{
|
||
// 参数校验
|
||
if (string.IsNullOrWhiteSpace(ip))
|
||
return (false, "PLC IP地址不能为空");
|
||
if (string.IsNullOrWhiteSpace(address))
|
||
return (false, "写入地址不能为空");
|
||
if (valueStr == null) // 允许空字符串(如清空值)
|
||
return (false, "写入值不能为null");
|
||
|
||
Plc plc = null;
|
||
try
|
||
{
|
||
plc = CreatePlcClient(cpuType, ip, rack, slot);
|
||
await OpenPlcConnectionAsync(plc);
|
||
|
||
if (!plc.IsConnected)
|
||
return (false, "PLC连接失败,请检查IP/机架号/槽位号是否正确");
|
||
|
||
bool writeOk = await Task.Run(() => WritePlcValue(plc, address, valueStr));
|
||
return (writeOk, writeOk ? "写入成功" : "写入失败(值类型与地址不匹配)");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
return (false, $"写入失败:{ex.Message}");
|
||
}
|
||
finally
|
||
{
|
||
ReleasePlcConnection(plc);
|
||
}
|
||
}
|
||
#endregion
|
||
|
||
#region 私有辅助方法
|
||
/// <summary>
|
||
/// 创建PLC客户端实例(统一配置超时)
|
||
/// </summary>
|
||
private Plc CreatePlcClient(CpuType cpuType, string ip, short rack, short slot)
|
||
{
|
||
var plc = new Plc(cpuType, ip, rack, slot)
|
||
{
|
||
ReadTimeout = _globalConfig.ReadWriteTimeout,
|
||
WriteTimeout = _globalConfig.ReadWriteTimeout
|
||
};
|
||
return plc;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 异步打开PLC连接(封装同步方法为异步)
|
||
/// </summary>
|
||
// 优化后的OpenPlcConnectionAsync
|
||
private async Task<bool> OpenPlcConnectionAsync(Plc plc)
|
||
{
|
||
if (plc == null) return false;
|
||
try
|
||
{
|
||
// 带超时的连接操作
|
||
var openTask = Task.Run(() => plc.Open());
|
||
if (await Task.WhenAny(openTask, Task.Delay(_globalConfig.ReadWriteTimeout)) != openTask)
|
||
{
|
||
return false;
|
||
}
|
||
await openTask;
|
||
return plc.IsConnected;
|
||
}
|
||
catch
|
||
{
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 释放PLC连接(安全关闭+资源释放)
|
||
/// </summary>
|
||
private void ReleasePlcConnection(Plc plc)
|
||
{
|
||
if (plc == null) return;
|
||
|
||
try
|
||
{
|
||
if (plc.IsConnected)
|
||
plc.Close();
|
||
// 正确释放PLC实例(适配S7.Net显式实现IDisposable)
|
||
if (plc is IDisposable disposable)
|
||
disposable.Dispose();
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine($"PLC连接关闭失败:{ex.Message}");
|
||
}
|
||
// 移除finally中的Dispose(),避免误释放服务
|
||
}
|
||
|
||
/// <summary>
|
||
/// 按业务规则解析PLC字符串(支持中文GBK编码)
|
||
/// </summary>
|
||
private async Task<string> ReadPlcStringAsync(Plc plc, string addr, int maxLen)
|
||
{
|
||
try
|
||
{
|
||
// 解析地址:DB1010.DBB50 → DB编号1010 + 起始字节50
|
||
var parts = addr.Split('.', StringSplitOptions.RemoveEmptyEntries);
|
||
if (parts.Length != 2)
|
||
return string.Empty;
|
||
|
||
if (!int.TryParse(parts[0].Replace("DB", ""), out int dbNum) || dbNum <= 0)
|
||
return string.Empty;
|
||
|
||
if (!int.TryParse(parts[1].Replace("DBB", ""), out int startByte) || startByte < 0)
|
||
return string.Empty;
|
||
|
||
// 读取字节数组
|
||
var bytes = await Task.Run(() => plc.ReadBytes(DataType.DataBlock, dbNum, startByte, maxLen));
|
||
if (bytes == null || bytes.Length < 3)
|
||
return string.Empty;
|
||
|
||
// 严格按规则解析:bytes[0]=总长度(备用)、bytes[1]=有效长度、bytes[2+]=内容
|
||
int validLen = Math.Clamp(bytes[1], 0, bytes.Length - 2);
|
||
if (validLen <= 0)
|
||
return string.Empty;
|
||
|
||
// 提取有效内容并转字符串(GBK编码支持中文)
|
||
var contentBytes = new byte[validLen];
|
||
Array.Copy(bytes, 2, contentBytes, 0, validLen);
|
||
return Encoding.GetEncoding("GBK").GetString(contentBytes).Trim('\0', ' ', '\t');
|
||
}
|
||
catch
|
||
{
|
||
return string.Empty;
|
||
}
|
||
}
|
||
|
||
/// </summary>
|
||
/// <param name="plc">PLC客户端实例</param>
|
||
/// <param name="addr">读取地址(如DB1010.DBW226)</param>
|
||
/// <returns>解析后的整数值,读取失败返回0</returns>
|
||
private async Task<int> ReadPlcIntAsync(Plc plc, string addr)
|
||
{
|
||
if (plc == null)
|
||
{
|
||
return 0;
|
||
}
|
||
if (string.IsNullOrWhiteSpace(addr))
|
||
{
|
||
return 0;
|
||
}
|
||
try
|
||
{
|
||
var val = await plc.ReadAsync(addr).ConfigureAwait(false);
|
||
return val switch
|
||
{
|
||
short s => s, // 16位短整型(DBW默认类型)
|
||
int i => i, // 32位整型(兼容返回)
|
||
uint ui => (int)ui, // 无符号32位整型
|
||
byte b => b, // 8位字节型
|
||
ushort us => us, // 无符号16位整型
|
||
long l => (int)l, // 64位整型(截断为32位)
|
||
ulong ul => (int)ul, // 无符号64位整型(截断为32位)
|
||
float f => (int)f, // 浮点数转整型(兼容PLC数值存储)
|
||
double d => (int)d, // 双精度浮点数转整型
|
||
_ => 0 // 未知类型返回0
|
||
};
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
return 0;
|
||
}
|
||
}
|
||
|
||
private async Task<int> ReadPlcDIntAsync(Plc plc, string addr)
|
||
{
|
||
if (plc == null || string.IsNullOrWhiteSpace(addr))
|
||
{
|
||
Console.WriteLine($"DInt读取失败:参数无效(地址:{addr})");
|
||
return 0;
|
||
}
|
||
try
|
||
{
|
||
var val = await Task.Run(() => plc.Read(addr));
|
||
Console.WriteLine($"DInt地址[{addr}] 原始值:{val},类型:{val?.GetType().Name}");
|
||
return val switch
|
||
{
|
||
int i => i,
|
||
long l => (int)l,
|
||
uint ui => (int)ui,
|
||
ulong ul => (int)ul,
|
||
_ => 0
|
||
};
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine($"DInt读取失败(地址:{addr}):{ex.Message}");
|
||
return 0;
|
||
}
|
||
}
|
||
private async Task<float> ReadPlcRealAsync(Plc plc, string addr)
|
||
{
|
||
// 前置校验:快速过滤无效入参
|
||
if (plc is null || !plc.IsConnected || string.IsNullOrWhiteSpace(addr))
|
||
return 0.0f;
|
||
|
||
try
|
||
{
|
||
// 地址解析:仅支持DBx.DBDy格式,增加容错
|
||
var parts = addr.Split('.');
|
||
if (parts.Length != 2 || !parts[0].StartsWith("DB") || !parts[1].StartsWith("DBD"))
|
||
return 0.0f;
|
||
|
||
if (!int.TryParse(parts[0].Substring(2), out int dbNumber) ||
|
||
!int.TryParse(parts[1].Substring(3), out int startByte))
|
||
return 0.0f;
|
||
|
||
// 核心读取:调用PLC非泛型读取方法
|
||
var result = await plc.ReadAsync(
|
||
DataType.DataBlock,
|
||
dbNumber,
|
||
startByte,
|
||
VarType.Real,
|
||
1).ConfigureAwait(false);
|
||
|
||
// 关键修复:直接处理单个数值(非数组)
|
||
if (result != null)
|
||
{
|
||
// 不管result是int/double/float,都强制转为float
|
||
return Convert.ToSingle(result);
|
||
}
|
||
|
||
// 无有效结果返回0
|
||
return 0.0f;
|
||
}
|
||
catch
|
||
{
|
||
// 异常兜底返回0
|
||
return 0.0f;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 格式化PLC读取的值,转为友好的字符串
|
||
/// </summary>
|
||
private string FormatPlcValue(object value)
|
||
{
|
||
if (value == null) return "空值";
|
||
|
||
return value switch
|
||
{
|
||
float f => f.ToString("0.000"), // 浮点数保留3位小数
|
||
double d => d.ToString("0.000"), // 补充双精度浮点数支持
|
||
short s => s.ToString(), // 16位整数
|
||
byte b => b.ToString(), // 8位整数
|
||
bool b => b.ToString(), // 布尔值
|
||
int i => i.ToString(), // 32位整数
|
||
uint ui => ui.ToString(), // 无符号32位整数
|
||
long l => l.ToString(), // 64位整数
|
||
ulong ul => ul.ToString(), // 无符号64位整数
|
||
_ => value.ToString() // 其他类型直接转字符串
|
||
};
|
||
}
|
||
|
||
/// <summary>
|
||
/// 根据地址类型转换值并写入PLC
|
||
/// </summary>
|
||
private bool WritePlcValue(Plc plc, string address, string valueStr)
|
||
{
|
||
// 双字(DBD/DD):浮点数或32位整数
|
||
if (address.Contains("DBD") || address.Contains("DD"))
|
||
{
|
||
if (float.TryParse(valueStr, out float f))
|
||
{
|
||
plc.Write(address, f);
|
||
return true;
|
||
}
|
||
else if (int.TryParse(valueStr, out int i))
|
||
{
|
||
plc.Write(address, i);
|
||
return true;
|
||
}
|
||
}
|
||
// 字(DBW/DW):16位整数
|
||
else if (address.Contains("DBW") || address.Contains("DW"))
|
||
{
|
||
if (short.TryParse(valueStr, out short s))
|
||
{
|
||
plc.Write(address, s);
|
||
return true;
|
||
}
|
||
}
|
||
// 字节(DBB/DB):8位整数
|
||
else if (address.Contains("DBB") || address.Contains("DB"))
|
||
{
|
||
if (byte.TryParse(valueStr, out byte b))
|
||
{
|
||
plc.Write(address, b);
|
||
return true;
|
||
}
|
||
}
|
||
// 位(DBX):布尔值(兼容0/1输入)
|
||
else if (address.Contains("DBX"))
|
||
{
|
||
if (bool.TryParse(valueStr, out bool bl))
|
||
{
|
||
plc.Write(address, bl);
|
||
return true;
|
||
}
|
||
else if (valueStr == "0")
|
||
{
|
||
plc.Write(address, false);
|
||
return true;
|
||
}
|
||
else if (valueStr == "1")
|
||
{
|
||
plc.Write(address, true);
|
||
return true;
|
||
}
|
||
}
|
||
|
||
// 不匹配的类型
|
||
return false;
|
||
}
|
||
#endregion
|
||
|
||
#region 资源释放
|
||
/// <summary>
|
||
/// 实现IDisposable接口,释放资源
|
||
/// </summary>
|
||
public void Dispose()
|
||
{
|
||
Dispose(true);
|
||
GC.SuppressFinalize(this); // 通知GC无需调用终结器
|
||
}
|
||
|
||
/// <summary>
|
||
/// 实际释放资源的逻辑(区分托管/非托管资源)
|
||
/// </summary>
|
||
/// <param name="disposing">是否释放托管资源</param>
|
||
protected virtual void Dispose(bool disposing)
|
||
{
|
||
if (_disposed) return; // 避免重复释放
|
||
|
||
// 释放托管资源
|
||
if (disposing)
|
||
{
|
||
// 此处无长期持有的PLC连接,若有可在此统一释放
|
||
}
|
||
|
||
// 标记为已释放
|
||
_disposed = true;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 终结器(防忘记手动Dispose)
|
||
/// </summary>
|
||
~PlcService()
|
||
{
|
||
Dispose(false);
|
||
}
|
||
#endregion
|
||
|
||
private List<PlcConfig> initPlcConfigs(List<PlcConfig> result)
|
||
{
|
||
var defaultResult = result ?? new List<PlcConfig>();
|
||
try
|
||
{
|
||
List<PlcConfig> query = _plantWorkstationService.Queryable()
|
||
.Where(it => it.Status == 1)
|
||
.Select(it => new PlcConfig
|
||
{
|
||
PlcName = it.WorkstationCode,
|
||
Ip = it.PlcIP,
|
||
Rack = (short)it.Rack, // 直接强制转换(it.Rack是int,非空)
|
||
Slot = (short)it.Slot // 同理,it.Slot是int,非空
|
||
})
|
||
.ToList();
|
||
return query.Count > 0 ? query : defaultResult;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine($"初始化PLC配置异常:{ex.Message}");
|
||
return defaultResult;
|
||
}
|
||
}
|
||
|
||
// 提取写PLC返回值的通用方法,统一处理逻辑和日志
|
||
private void WritePlcSaveRequestResult(Plc plc, string ip, string plcName, PlcProductionData prodData, string saveResult)
|
||
{
|
||
try
|
||
{
|
||
if (plcName == "OP058")
|
||
{
|
||
WritePlcValue(plc, _mesop058IntReturnMap["设备使能"], "1");
|
||
//WritePlcValue(plc, _mesIntReturnMap["工位开始查询结果"], "1");
|
||
WritePlcValue(plc, _mesop058IntReturnMap["保存结果"], saveResult);
|
||
if (prodData.ProductModel != null && prodData.ProductModel.Length > 0)
|
||
{
|
||
WritePlcString(plc, _mesop058StringReturnMap["产品型号"].Addr, _mesop058StringReturnMap["产品型号"].Len, prodData.ProductModel);
|
||
}
|
||
else
|
||
{
|
||
WritePlcString(plc, _mesop058StringReturnMap["产品型号"].Addr, _mesop058StringReturnMap["产品型号"].Len, prodData.ProductModel);
|
||
}
|
||
WritePlcString(plc, _mesop058StringReturnMap["订单下发"].Addr, _mesop058StringReturnMap["订单下发"].Len, "cpxhtest");
|
||
}
|
||
else
|
||
{
|
||
WritePlcValue(plc, _mesIntReturnMap["设备使能"], "1");
|
||
//WritePlcValue(plc, _mesIntReturnMap["工位开始查询结果"], "1");
|
||
WritePlcValue(plc, _mesIntReturnMap["保存结果"], saveResult);
|
||
if (prodData.ProductModel != null && prodData.ProductModel.Length > 0)
|
||
{
|
||
WritePlcString(plc, _mesStringReturnMap["产品型号"].Addr, _mesStringReturnMap["产品型号"].Len, prodData.ProductModel);
|
||
}
|
||
else
|
||
{
|
||
WritePlcString(plc, _mesStringReturnMap["产品型号"].Addr, _mesStringReturnMap["产品型号"].Len, prodData.ProductModel);
|
||
}
|
||
WritePlcString(plc, _mesStringReturnMap["订单下发"].Addr, _mesStringReturnMap["订单下发"].Len, "cpxhtest");
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 记录PLC过站状态
|
||
/// </summary>
|
||
private async Task RecordPlcOperationResult(string plcName, PlcProductionData prodData)
|
||
{
|
||
// 1. 入参校验(极致精简,减少分支和字符串操作)
|
||
if (string.IsNullOrWhiteSpace(plcName))
|
||
throw new ArgumentNullException(nameof(plcName), "PLC/工位名称不能为空");
|
||
if (prodData is null)
|
||
throw new ArgumentNullException(nameof(prodData), "PLC生产数据实体不能为空");
|
||
|
||
// 2. SN校验(直接判断,减少临时变量赋值)
|
||
if (string.IsNullOrWhiteSpace(prodData.SN2?.Trim()))
|
||
return;
|
||
var strSN = prodData.SN2.Trim(); // 仅在非空时赋值,减少内存分配
|
||
|
||
// 3. 合格状态转换(提前计算,减少后续引用开销)
|
||
var result = string.Equals(prodData.QualificationFlag, "1", StringComparison.OrdinalIgnoreCase) ? 1 : 0;
|
||
var now = DateTime.Now;
|
||
|
||
try
|
||
{
|
||
// 在方法内创建上下文,并使用using确保自动释放
|
||
using var context = DbScoped.SugarScope.CopyNew();
|
||
if (context == null)
|
||
throw new InvalidOperationException("无法创建数据库上下文,请检查SqlSugar配置");
|
||
var routingCode = string.Empty;
|
||
try
|
||
{
|
||
routingCode = await context.Queryable<ProcessRouting>()
|
||
.LeftJoin<ProcessOperation>((r, o) => r.RoutingCode == o.FkRoutingCode)
|
||
.Where((r, o) => r.FkProductMaterialCode == strSN && r.Status == 1)
|
||
.Select((r, o) => o.FkRoutingCode)
|
||
.FirstAsync(); // 直接取第一条,避免ToList+First的双重开销
|
||
}
|
||
catch
|
||
{
|
||
|
||
}
|
||
|
||
// 5. 返工状态判断(无分配判断,直接赋值)
|
||
var productionLifeStage = string.Equals(prodData.ReworkFlag, "1", StringComparison.OrdinalIgnoreCase) ? 2 : 1;
|
||
|
||
// 6. 构建记录(结构体思维,减少冗余字段)
|
||
var askOutStation = new ProductPassStationRecord
|
||
{
|
||
ProductSN = strSN,
|
||
WorkstationCode = plcName,
|
||
Routingcode = routingCode,
|
||
OperationCode = plcName,
|
||
ProductionLifeStage = productionLifeStage,
|
||
PasstationType = 1,
|
||
ResultCode = result,
|
||
EffectTime = now,
|
||
CreatedTime = now
|
||
};
|
||
|
||
// 7. 插入数据库(批量插入优化,减少IO交互)
|
||
var insertCount = await context.Insertable(askOutStation).ExecuteCommandAsync();
|
||
|
||
}
|
||
catch (ArgumentNullException ex)
|
||
{
|
||
Console.WriteLine($"[{now:HH:mm:ss}] {plcName} - 入参异常:{ex.Message}");
|
||
throw;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine($"[{now:HH:mm:ss}] {plcName} - {strSN} 记录异常:{ex.Message}");
|
||
}
|
||
}
|
||
/// <summary>
|
||
/// 异步复位上传请求(独立方法,非阻塞)
|
||
/// </summary>
|
||
private async Task ResetUploadRequestAsync(Plc plc, string ip, string uploadRequestAddr)
|
||
{
|
||
try
|
||
{
|
||
// 若WritePlcValue无异步版本,用Task.Run包装避免阻塞
|
||
await Task.Run(() => WritePlcValue(plc, uploadRequestAddr, "0"));
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine($"({ip})写上传请求复位失败:{ex.Message}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 异步复位保存请求(独立方法,非阻塞)
|
||
/// </summary>
|
||
private async Task ResetSaveRequestAsync(Plc plc, string ip, string saveRequestAddr)
|
||
{
|
||
try
|
||
{
|
||
// 若WritePlcValue无异步版本,用Task.Run包装避免阻塞
|
||
await Task.Run(() => WritePlcValue(plc, saveRequestAddr, "0"));
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine($"({ip})写保存请求复位失败:{ex.Message}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 向PLC写入字符串(字节数组形式)
|
||
/// </summary>
|
||
/// <param name="plc">PLC客户端</param>
|
||
/// <param name="startAddress">字符串起始地址(如DB1101.DBB1016)</param>
|
||
/// <param name="strLength">PLC定义的字符串长度(如14)</param>
|
||
/// <param name="value">要写入的字符串</param>
|
||
private void WritePlcString(Plc plc, string startAddress, int strLength, string value)
|
||
{
|
||
// 1. 解析起始地址(提取DB块、起始字节)
|
||
if (!ParsePlcAddress(startAddress, out var dataType, out int dbNumber, out int startByte))
|
||
{
|
||
throw new ArgumentException($"无效的PLC字符串起始地址:{startAddress}");
|
||
}
|
||
|
||
// 2. 处理字符串:转字节数组,长度不足补0,超长截断
|
||
byte[] strBytes = Encoding.ASCII.GetBytes(value ?? "");
|
||
byte[] plcBytes = new byte[strLength]; // PLC字符串固定长度
|
||
|
||
// 复制字符串字节,超长则截断
|
||
int copyLength = Math.Min(strBytes.Length, strLength);
|
||
Array.Copy(strBytes, 0, plcBytes, 0, copyLength);
|
||
// 剩余字节填充0(清空原有内容)
|
||
if (copyLength < strLength)
|
||
{
|
||
Array.Clear(plcBytes, copyLength, strLength - copyLength);
|
||
}
|
||
|
||
// 3. 写入PLC(批量写入字节数组)
|
||
plc.WriteBytes(dataType, dbNumber, startByte, plcBytes);
|
||
}
|
||
|
||
/// </summary>
|
||
/// <param name="address">PLC地址(如DB1101.DBB1016)</param>
|
||
/// <param name="dataType">数据类型(DB/Input等)</param>
|
||
/// <param name="dbNumber">DB块编号(如1101)</param>
|
||
/// <param name="startByte">起始字节(如1016)</param>
|
||
/// <returns>是否解析成功</returns>
|
||
private bool ParsePlcAddress(string address, out DataType dataType, out int dbNumber, out int startByte)
|
||
{
|
||
dataType = DataType.DataBlock; // 默认DB块
|
||
dbNumber = 0;
|
||
startByte = 0;
|
||
|
||
try
|
||
{
|
||
// 匹配格式:DB1101.DBB1016
|
||
var match = System.Text.RegularExpressions.Regex.Match(address, @"DB(\d+)\.DBB(\d+)");
|
||
if (match.Success)
|
||
{
|
||
dbNumber = int.Parse(match.Groups[1].Value);
|
||
startByte = int.Parse(match.Groups[2].Value);
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
return false;
|
||
}
|
||
}
|
||
}
|
||
} |