PLC采集
This commit is contained in:
parent
f6e826c2a7
commit
9d60bb5fd1
@ -420,7 +420,7 @@ namespace RIZO.Admin.WebApi.PLC.Model
|
||||
/// <summary>
|
||||
/// 芯片 SN(PWM 条码)
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "Screw7TightenTime")]
|
||||
[SugarColumn(ColumnName = "ChipSN")]
|
||||
public string ChipSN { get; set; }
|
||||
|
||||
/// <summary>
|
||||
|
||||
@ -123,7 +123,6 @@ namespace RIZO.Admin.WebApi.PLC.Service
|
||||
Rack = (short)it.Rack, // 空值兜底
|
||||
Slot = (short)it.Slot // 空值兜底
|
||||
})
|
||||
.Where(c => !string.IsNullOrWhiteSpace(c.Ip))
|
||||
.ToList();
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
||||
@ -32,6 +32,7 @@ namespace RIZO.Admin.WebApi.PLC.Service
|
||||
|
||||
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)
|
||||
@ -41,15 +42,15 @@ namespace RIZO.Admin.WebApi.PLC.Service
|
||||
//MES返回PLC请求映射
|
||||
private readonly Dictionary<string, string> _mesIntReturnMap = new()
|
||||
{
|
||||
{ "设备使能", "DB1050.DBW0" }, // Int
|
||||
{ "工位开始查询结果", "DB1050.DBW2000" }, // Int
|
||||
{ "保存结果", "DB1050.DBW2002" }, // Int
|
||||
{ "设备使能", "DB1101.DBW0" }, // Int
|
||||
{ "工位开始查询结果", "DB1101.DBW2000" }, // Int
|
||||
{ "保存结果", "DB1101.DBW2002" }, // Int
|
||||
};
|
||||
|
||||
private readonly Dictionary<string, (string Addr, int Len)> _mesStringReturnMap = new()
|
||||
{
|
||||
{ "产品型号", ("DB1001.DBB1016", 14) }, // String[14]
|
||||
{ "订单下发", ("DB1001.DBB1032", 50) }, // String[50]
|
||||
{ "产品型号", ("DB1101.DBB1016", 14) }, // String[14]
|
||||
{ "订单下发", ("DB1101.DBB1032", 50) }, // String[50]
|
||||
};
|
||||
|
||||
// OP020-2 专属地址映射(合盖工位,DB1001)
|
||||
@ -403,17 +404,9 @@ namespace RIZO.Admin.WebApi.PLC.Service
|
||||
else
|
||||
{
|
||||
plc = CreatePlcClient(cpuType, ip, rack, slot);
|
||||
try
|
||||
{
|
||||
await OpenPlcConnectionAsync(plc);
|
||||
if (!plc.IsConnected) return (false, null, $"{plcName}连接失败(含2次重试)");
|
||||
_plcConnPool.TryAdd(poolKey, (plc, DateTime.Now));
|
||||
}
|
||||
catch
|
||||
{
|
||||
ReleasePlcConnection(plc);
|
||||
return (false, null, $"{plcName}连接失败(含2次重试)");
|
||||
}
|
||||
await OpenPlcConnectionAsync(plc);
|
||||
if (!plc.IsConnected) return (false, null, $"{plcName}连接失败");
|
||||
_plcConnPool.TryAdd(poolKey, (plc, DateTime.Now));
|
||||
}
|
||||
|
||||
// 4. 多工位请求状态读取(精简分支,保留原逻辑,移除冗余注释)
|
||||
@ -508,13 +501,12 @@ namespace RIZO.Admin.WebApi.PLC.Service
|
||||
? $"{plcName}生产数据读取成功(复用连接)"
|
||||
: $"{plcName}生产数据读取成功(新建连接)";
|
||||
WritePlcSaveRequestResult(plc, ip, plcName, prodData, "1");
|
||||
|
||||
//保存成功,到PLC过站表过站状态
|
||||
RecordPlcOperationResult(plcName, prodData);
|
||||
return (true, prodData, successMsg);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// 精细化异常日志,保留堆栈
|
||||
Console.WriteLine($"{plcName}({ip})生产数据读取异常:{ex.Message}\n{ex.StackTrace}");
|
||||
return (false, null, $"{plcName}生产数据读取失败:{ex.Message}");
|
||||
}
|
||||
finally
|
||||
@ -529,7 +521,6 @@ namespace RIZO.Admin.WebApi.PLC.Service
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"{plcName}({ip})释放PLC连接失败:{ex.Message}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
@ -539,7 +530,6 @@ namespace RIZO.Admin.WebApi.PLC.Service
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#region 工位专属读取方法
|
||||
/// <summary>
|
||||
/// 读取OP020-2数据(合盖工位
|
||||
@ -752,8 +742,8 @@ namespace RIZO.Admin.WebApi.PLC.Service
|
||||
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 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["站位结果"]);
|
||||
@ -767,7 +757,7 @@ namespace RIZO.Admin.WebApi.PLC.Service
|
||||
taskProductName, taskOrderName, taskProductModel, taskSN1, taskSN2,
|
||||
// Int任务
|
||||
taskRunStatus, taskMachineModel, taskOnlineStatus, taskByPass, taskProduceModel,
|
||||
taskQueryReq, taskSaveReq, taskTrayNo, taskCameraResult, taskStationResult,
|
||||
taskTrayNo, taskCameraResult, taskStationResult,
|
||||
// Real任务
|
||||
taskCycleTime);
|
||||
|
||||
@ -785,8 +775,6 @@ namespace RIZO.Admin.WebApi.PLC.Service
|
||||
int onlineStatus = await taskOnlineStatus;
|
||||
int byPass = await taskByPass;
|
||||
int produceModel = await taskProduceModel;
|
||||
int queryReq = await taskQueryReq;
|
||||
int saveReq = await taskSaveReq;
|
||||
int trayNo = await taskTrayNo;
|
||||
int cameraResult = await taskCameraResult;
|
||||
int stationResult = await taskStationResult;
|
||||
@ -879,8 +867,6 @@ namespace RIZO.Admin.WebApi.PLC.Service
|
||||
var taskOnlineStatus = ReadPlcIntAsync(plc, _op075IntMap["设备在线状态"]);
|
||||
var taskByPass = ReadPlcIntAsync(plc, _op075IntMap["ByPass"]);
|
||||
var taskProduceModel = ReadPlcIntAsync(plc, _op075IntMap["生产模式"]);
|
||||
var taskQueryReq = ReadPlcIntAsync(plc, _op075IntMap["查询请求"]);
|
||||
var taskSaveReq = ReadPlcIntAsync(plc, _op075IntMap["保存请求"]);
|
||||
var taskTrayNo = ReadPlcIntAsync(plc, _op075IntMap["托盘号"]);
|
||||
var taskStationResult = ReadPlcIntAsync(plc, _op075IntMap["站位结果"]);
|
||||
|
||||
@ -893,7 +879,7 @@ namespace RIZO.Admin.WebApi.PLC.Service
|
||||
taskProductName, taskOrderName, taskProductModel, taskSN1, taskSN2, taskChipSN,
|
||||
// Int任务
|
||||
taskRunStatus, taskMachineModel, taskOnlineStatus, taskByPass, taskProduceModel,
|
||||
taskQueryReq, taskSaveReq, taskTrayNo, taskStationResult,
|
||||
taskTrayNo, taskStationResult,
|
||||
// Real任务
|
||||
taskCycleTime);
|
||||
|
||||
@ -912,8 +898,6 @@ namespace RIZO.Admin.WebApi.PLC.Service
|
||||
int onlineStatus = await taskOnlineStatus;
|
||||
int byPass = await taskByPass;
|
||||
int produceModel = await taskProduceModel;
|
||||
int queryReq = await taskQueryReq;
|
||||
int saveReq = await taskSaveReq;
|
||||
int trayNo = await taskTrayNo;
|
||||
int stationResult = await taskStationResult;
|
||||
|
||||
@ -2134,21 +2118,85 @@ namespace RIZO.Admin.WebApi.PLC.Service
|
||||
}
|
||||
}
|
||||
|
||||
// 提取写PLC返回值的通用方法,统一处理逻辑和日志
|
||||
// 提取写PLC返回值的通用方法,统一处理逻辑和日志
|
||||
private void WritePlcSaveRequestResult(Plc plc, string ip, string plcName, PlcProductionData prodData, string saveResult)
|
||||
{
|
||||
try
|
||||
{
|
||||
WritePlcValue(plc, _mesIntReturnMap["设备使能"], "1");
|
||||
//WritePlcValue(plc, _mesIntReturnMap["工位开始查询结果"], "1");
|
||||
WritePlcValue(plc, _mesIntReturnMap["工位开始查询结果"], "1");
|
||||
WritePlcValue(plc, _mesIntReturnMap["保存结果"], saveResult);
|
||||
WritePlcValue(plc, "产品型号", prodData.ProductModel);
|
||||
WritePlcValue(plc, "订单下发", "");//??
|
||||
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)
|
||||
{;
|
||||
}
|
||||
}
|
||||
|
||||
//记录PLC过站状态
|
||||
private void RecordPlcOperationResult(string plcName, PlcProductionData prodData)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(plcName))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(plcName), "PLC/工位名称不能为空");
|
||||
}
|
||||
if (prodData == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(prodData), "PLC生产数据实体不能为空");
|
||||
}
|
||||
string strSN = prodData.SN2?.Trim();
|
||||
if (string.IsNullOrWhiteSpace(strSN))
|
||||
{
|
||||
return;
|
||||
}
|
||||
int result = string.Equals(prodData.QualificationFlag, "1", StringComparison.OrdinalIgnoreCase) ? 1 : 0;
|
||||
|
||||
try
|
||||
{
|
||||
var existingRecord = _plcOperationResultService.Queryable()
|
||||
.Where(it => it.Sn == strSN && it.Workstationcode == plcName).ToList().FirstOrDefault();
|
||||
|
||||
if (existingRecord != null)
|
||||
{
|
||||
// 情况1:记录存在
|
||||
if (existingRecord.Result == result)
|
||||
{
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 情况2:记录存在但结果不一致,执行修改操作
|
||||
existingRecord.Result = result;
|
||||
existingRecord.UpdatedBy = "PLC";
|
||||
existingRecord.UpdatedTime = DateTime.Now;
|
||||
_plcOperationResultService.Update(existingRecord);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 情况3:记录不存在,执行新增操作
|
||||
PlcOperationResult porNew = new PlcOperationResult();
|
||||
porNew.Sn = strSN;
|
||||
porNew.Workstationcode = plcName;
|
||||
porNew.Result = result;
|
||||
porNew.CreatedBy = "PLC";
|
||||
porNew.CreatedTime = DateTime.Now;
|
||||
_plcOperationResultService.Insert(porNew);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异步复位上传请求(独立方法,非阻塞)
|
||||
/// </summary>
|
||||
@ -2180,5 +2228,68 @@ namespace RIZO.Admin.WebApi.PLC.Service
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -97,8 +97,8 @@
|
||||
"uniappPath": "D:\\Work" //h5前端代码存储路径
|
||||
},
|
||||
"GlobalConfig": {
|
||||
"ConnectTimeout": 5000, // 连接超时(毫秒)
|
||||
"ReadWriteTimeout": 5000 // 读写超时(毫秒)
|
||||
"ConnectTimeout": 1000, // 连接超时(毫秒)
|
||||
"ReadWriteTimeout": 1000 // 读写超时(毫秒)
|
||||
},
|
||||
"PlcPollingSettings": {
|
||||
"MaxConcurrentPerPlc": 1,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user