jingzhan3校验

This commit is contained in:
gcw_MV9p2JJN 2026-01-28 19:26:37 +08:00
parent d70c0be648
commit da16fed4dc
6 changed files with 587 additions and 98 deletions

View File

@ -14,7 +14,8 @@ using RIZO.Common.Cache;
using RIZO.Common.DynamicApiSimple.Extens;
using RIZO.Infrastructure.WebExtensions;
using RIZO.Mall;
using RIZO.Service.PLCBackground.Stations;
using RIZO.Service.PLCBackground.Stations.Into;
using RIZO.Service.PLCBackground.Stations.Out;
using RIZO.ServiceCore.Signalr;
using RIZO.ServiceCore.SqlSugar;
using S7.Net;
@ -110,7 +111,7 @@ builder.Services.AddLogo();
builder.Services.AddLocalization(options => options.ResourcesPath = "");
builder.Services.AddHostedService<PlcOutStationService_OP07_01>();
builder.Services.AddHostedService<PlcIntoStationService_OP07_01>();
builder.Services.AddHostedService<PlcIntoStationService_OP70_01>();
// 在应用程序启动的最开始处调用

View File

@ -13,9 +13,12 @@ using System;
using System.Threading;
using System.Threading.Tasks;
namespace RIZO.Service.PLCBackground.Stations
namespace RIZO.Service.PLCBackground.Stations.Into
{
public class PlcIntoStationService_OP07_01 : BackgroundService
/// <summary>
/// OP点散热胶GF1500
/// </summary>
public class PlcIntoStationService_OP70_01 : BackgroundService
{
private static Logger _logger = LogManager.GetCurrentClassLogger();
private PlcConntectHepler _plcService;
@ -24,7 +27,7 @@ namespace RIZO.Service.PLCBackground.Stations
private readonly CpuType _cpuType = CpuType.S71500;
private string WorkstationCode;
private readonly SqlSugarClient Context;
public PlcIntoStationService_OP07_01()
public PlcIntoStationService_OP70_01()
{
Context = DbScoped.SugarScope.CopyNew();
}
@ -81,23 +84,12 @@ namespace RIZO.Service.PLCBackground.Stations
PasstationDescription = "入站请求ASK",
EffectTime = DateTime.Now,
CreatedTime = DateTime.Now,
};
await Context.Insertable(ASKintoStation).ExecuteCommandAsync();
//判断该产品是否允许进站
EntryPermissionResult result = await checkEntryPermission(productModel, productSN, WorkstationCode, processOperations);
await _plcService.WriteAsync("DB1010.DBW2000", (int)result);
}
await Task.Delay(_pollingInterval, stoppingToken);
}
@ -243,35 +235,6 @@ namespace RIZO.Service.PLCBackground.Stations
}
/// <summary>
/// 入站校验结果
/// </summary>
enum EntryPermissionResult
{
//1之前工位数据正常
AllowIntoStation = 1,
// 2上工位无记录
NoRecordAtPreviousStation = 2,
// 3上工位NG
PreviousStationNG = 3,
// 4扫码产品型号错误
ProductModelError = 4,
//5本工位NG
CurrentStationNG = 5,
//6时间超出规定无法生产
TimeExceeded = 6,
//7固化时间未到规定时间
CuringTimeNotReached = 7,
// 8未找到时间属性
TimeAttributeNotFound = 8,
// 10条码为空
BarcodeEmpty = 10,
//0之前工位数据异常
PreviousStationDataAbnormal = 0,
//11 未知异常
UnkownException = 11,
//12 最大重复入站次数
MaximumRepeatedEntries = 12
}
}

View File

@ -0,0 +1,243 @@
using MDM.Model.Plant;
using MDM.Model.Process;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using NLog;
using RIZO.Model.Mes;
using RIZO.Model.MES.product_trace;
using RIZO.Repository;
using RIZO.Service.PLC;
using S7.Net;
using SqlSugar.IOC;
using System;
using System.Threading;
using System.Threading.Tasks;
namespace RIZO.Service.PLCBackground.Stations.Into
{
/// <summary>
/// OP80_01 PCBA组装&拧紧
/// </summary>
public class PlcIntoStationService_OP80_01 : BackgroundService
{
private static Logger _logger = LogManager.GetCurrentClassLogger();
private PlcConntectHepler _plcService;
private readonly TimeSpan _pollingInterval = TimeSpan.FromSeconds(5);
private readonly string _ipAddress = "192.168.11.56";
private readonly CpuType _cpuType = CpuType.S71500;
private string WorkstationCode;
private readonly SqlSugarClient Context;
public PlcIntoStationService_OP80_01()
{
Context = DbScoped.SugarScope.CopyNew();
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
_logger.Info("PLC Polling Service started");
// 使用工厂方法创建服务实例
using (_plcService = new PlcConntectHepler(_ipAddress, _cpuType))
{
// 获取当前的工序/工站
WorkstationCode = await Context.Queryable<PlantWorkstation>()
.Where(it => it.PlcIP == _ipAddress)
.Select(it => it.WorkstationCode).FirstAsync();
while (!stoppingToken.IsCancellationRequested)
{
try
{
//心跳检测
await _plcService.WriteAsync("DB1020.DBW0", 1);
// 轮询进站请求信号/工位开始查询请求
int intoStationAsk = await _plcService.ReadAsync<int>("DB1001.DBW2000");
if (intoStationAsk == 1)
{
// 处理进站请求
_logger.Info("Processing into station request...");
//获取产品SN码
string productModel = await _plcService.ReadStringAsync("DB1001.DBB1000");
string productSN = await _plcService.ReadStringAsync("DB1001.DBB1054");
// 获取工单
//获取工艺路线工序
List<ProcessOperation> processOperations = await Context.Queryable<ProcessRouting>().LeftJoin<ProcessOperation>((r, o) => r.RoutingCode == o.FkRoutingCode)
.Where((r, o) => r.FkProductMaterialCode == productModel && r.Status == 1)
.Select((r, o) => o).ToListAsync();
//判断改产品是正常件还是返工件
//插入入站请求ASK过站记录
ProductPassStationRecord ASKintoStation = new ProductPassStationRecord
{
ProductSN = productSN,
WorkstationCode = WorkstationCode,
Routingcode = processOperations.First().FkRoutingCode,
OperationCode = WorkstationCode,
ProductionLifeStage = 1, // 1表示生产中
PasstationType = 0, // 0表示入站请求
PasstationDescription = "入站请求ASK",
EffectTime = DateTime.Now,
CreatedTime = DateTime.Now,
};
await Context.Insertable(ASKintoStation).ExecuteCommandAsync();
//判断该产品是否允许进站
EntryPermissionResult result = await checkEntryPermission(productModel, productSN, WorkstationCode, processOperations);
await _plcService.WriteAsync("DB1020.DBW2000", (int)result);
}
await Task.Delay(_pollingInterval, stoppingToken);
}
catch (OperationCanceledException)
{
_logger.Info("PLC Polling Service is stopping");
break;
}
catch (Exception ex)
{
_logger.Error(ex, "PLC polling error");
await Task.Delay(TimeSpan.FromSeconds(10), stoppingToken);
}
}
}
_logger.Info("PLC Polling Service stopped");
}
/// <summary>
/// 校验是否可以进站
/// </summary>
/// <param name="productModel">产品型号</param>
/// <param name="productSN">产品SN</param>
/// <returns></returns>
private async Task<EntryPermissionResult> checkEntryPermission(string productModel, string productSN, string workstationCode, List<ProcessOperation> processOperations)
{
EntryPermissionResult result = EntryPermissionResult.UnkownException;
//2上工位无记录
//ProcessOperation LastOperation = processOperations.Where(operation => operation.OperationSeq < processOperations.Where(it => it.OperationCode == workstationCode).Select(it => it.OperationSeq).First()).OrderByDescending(operation => operation.OperationSeq).First();
int LastOperationSeq = processOperations.Where(operation => operation.OperationCode == workstationCode).Select(operation => operation.LastOperationSeq ?? -1).First();
ProcessOperation LastOperation = processOperations.Where(it => it.OperationSeq == LastOperationSeq).First();
bool isExistLastOperationRecord = await DbScoped.SugarScope.CopyNew().Queryable<ProductPassStationRecord>()
.Where(it => it.ProductSN == productSN)
.Where(it => it.OperationCode == LastOperation.OperationCode)
.AnyAsync();
if (!isExistLastOperationRecord)
{
return EntryPermissionResult.NoRecordAtPreviousStation;
}
// 3 上工位NG 入站或者出站结果 NG
bool isExistLastOperationNG = await Context.Queryable<ProductPassStationRecord>()
.Where(it => it.ProductSN == productSN)
.Where(it => it.OperationCode == LastOperation.OperationCode)
.Where(it => it.PasstationType == 2 || it.PasstationType == 4)
.Where(it => it.ResultCode != 1)
.AnyAsync();
if (!isExistLastOperationNG)
{
result = EntryPermissionResult.PreviousStationNG;
goto InsertPassrecord;
}
// 4 扫码产品型号错误
bool isExistproductSN = await Context.Queryable<ProductLifecycle>()
.Where(it => it.ProductSN == productSN).AnyAsync();
bool isExistproductSNProducting = await Context.Queryable<ProductLifecycle>()
.Where(it => it.ProductSN == productSN && (it.ProductCurrentStatus != 1 && it.ProductCurrentStatus != 3)).AnyAsync();
if (!isExistproductSN || !isExistproductSNProducting)
{
result = EntryPermissionResult.ProductModelError;
goto InsertPassrecord;
}
//6时间超出规定无法生产
//7固化时间未到规定时间
int LastOperationStandardTime = processOperations.Where(operation => operation.OperationCode == LastOperation.OperationCode)
.Select(operation => operation.StandardTime??0).First();
if (LastOperationStandardTime > 0)
{
// 上一站的入站时间 和本站的请求时间差值
DateTime LastInStationTime= await Context.Queryable<ProductPassStationRecord>()
.Where(it => it.ProductSN == productSN)
.Where(it => it.OperationCode == LastOperation.OperationCode)
.Where(it => it.PasstationType == 1)
.MaxAsync(it => it.InStationTime??DateTime.MinValue);
TimeSpan timeDiff = DateTime.Now - LastInStationTime;
double totalSeconds = timeDiff.TotalSeconds;
if(totalSeconds < LastOperationStandardTime)
{
result = EntryPermissionResult.CuringTimeNotReached;
goto InsertPassrecord;
}
}
//12 最大重复入站次数
int MaxRepeatEntries = processOperations.Where(operation => operation.OperationCode == workstationCode).Select(operation => operation.MaxStationCount??1).First();
if(MaxRepeatEntries>1)
{
int currentStationEntryCount = await Context.Queryable<ProductPassStationRecord>()
.Where(it => it.ProductSN == productSN)
.Where(it => it.OperationCode == workstationCode)
.Where(it => it.PasstationType == 1)
.CountAsync();
if(currentStationEntryCount >= MaxRepeatEntries)
{
result = EntryPermissionResult.MaximumRepeatedEntries;
goto InsertPassrecord;
}
}
//OK 准许进站
result = EntryPermissionResult.AllowIntoStation;
goto InsertPassrecord;
InsertPassrecord:
//插入入站请求Resp过站记录
ProductPassStationRecord RespintoStation = new ProductPassStationRecord
{
ProductSN = productSN,
WorkstationCode = WorkstationCode,
Routingcode = processOperations.First().FkRoutingCode,
OperationCode = WorkstationCode,
ProductionLifeStage = 1, // 1表示生产中
PasstationType = 1, // 入站完毕
PasstationDescription = "入站完毕Resp",
EffectTime = DateTime.Now,
InStationTime = DateTime.Now,
ResultCode = (int)result,
ResultDescription = result.ToString(),
CreatedTime = DateTime.Now,
};
await Context.Insertable(RespintoStation).ExecuteCommandAsync();
return result;
}
}
}

View File

@ -0,0 +1,240 @@
using MDM.Model.Plant;
using MDM.Model.Process;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using NLog;
using RIZO.Model.Mes;
using RIZO.Model.MES.product_trace;
using RIZO.Repository;
using RIZO.Service.PLC;
using S7.Net;
using SqlSugar.IOC;
using System;
using System.Threading;
using System.Threading.Tasks;
namespace RIZO.Service.PLCBackground.Stations.Into
{
/// <summary>
/// OP80_03 PWM压装&CUP上料&拧紧
/// </summary>
public class PlcIntoStationService_OP80_03 : BackgroundService
{
private static Logger _logger = LogManager.GetCurrentClassLogger();
private PlcConntectHepler _plcService;
private readonly TimeSpan _pollingInterval = TimeSpan.FromSeconds(5);
private readonly string _ipAddress = "192.168.11.71";
private readonly CpuType _cpuType = CpuType.S71500;
private string WorkstationCode;
private readonly SqlSugarClient Context;
public PlcIntoStationService_OP80_03()
{
Context = DbScoped.SugarScope.CopyNew();
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
_logger.Info("PLC Polling Service started");
// 使用工厂方法创建服务实例
using (_plcService = new PlcConntectHepler(_ipAddress, _cpuType))
{
// 获取当前的工序/工站
WorkstationCode = await Context.Queryable<PlantWorkstation>().Where(it => it.PlcIP == _ipAddress)
.Select(it => it.WorkstationCode).FirstAsync();
while (!stoppingToken.IsCancellationRequested)
{
try
{
//心跳检测
await _plcService.WriteAsync("DB1020.DBW0", 1);
// 轮询进站请求信号/工位开始查询请求
int intoStationAsk = await _plcService.ReadAsync<int>("DB1021.DBW2000");
if (intoStationAsk == 1)
{
// 处理进站请求
_logger.Info("Processing into station request...");
//获取产品SN码
string productModel = await _plcService.ReadStringAsync("DB1001.DBB1000");
string productSN = await _plcService.ReadStringAsync("DB1001.DBB1054");
// 获取工单
//获取工艺路线工序
List<ProcessOperation> processOperations = await Context.Queryable<ProcessRouting>().LeftJoin<ProcessOperation>((r, o) => r.RoutingCode == o.FkRoutingCode)
.Where((r, o) => r.FkProductMaterialCode == productModel && r.Status == 1)
.Select((r, o) => o).ToListAsync();
//判断改产品是正常件还是返工件
//插入入站请求ASK过站记录
ProductPassStationRecord ASKintoStation = new ProductPassStationRecord
{
ProductSN = productSN,
WorkstationCode = WorkstationCode,
Routingcode = processOperations.First().FkRoutingCode,
OperationCode = WorkstationCode,
ProductionLifeStage = 1, // 1表示生产中
PasstationType = 0, // 0表示入站请求
PasstationDescription = "入站请求ASK",
EffectTime = DateTime.Now,
CreatedTime = DateTime.Now,
};
await Context.Insertable(ASKintoStation).ExecuteCommandAsync();
//判断该产品是否允许进站
EntryPermissionResult result = await checkEntryPermission(productModel, productSN, WorkstationCode, processOperations);
await _plcService.WriteAsync("DB1020.DBW2000", (int)result);
}
await Task.Delay(_pollingInterval, stoppingToken);
}
catch (OperationCanceledException)
{
_logger.Info("PLC Polling Service is stopping");
break;
}
catch (Exception ex)
{
_logger.Error(ex, "PLC polling error");
await Task.Delay(TimeSpan.FromSeconds(10), stoppingToken);
}
}
}
_logger.Info("PLC Polling Service stopped");
}
/// <summary>
/// 校验是否可以进站
/// </summary>
/// <param name="productModel">产品型号</param>
/// <param name="productSN">产品SN</param>
/// <returns></returns>
private async Task<EntryPermissionResult> checkEntryPermission(string productModel, string productSN, string workstationCode, List<ProcessOperation> processOperations)
{
EntryPermissionResult result = EntryPermissionResult.UnkownException;
//2上工位无记录
//ProcessOperation LastOperation = processOperations.Where(operation => operation.OperationSeq < processOperations.Where(it => it.OperationCode == workstationCode).Select(it => it.OperationSeq).First()).OrderByDescending(operation => operation.OperationSeq).First();
int LastOperationSeq = processOperations.Where(operation => operation.OperationCode == workstationCode).Select(operation => operation.LastOperationSeq ?? -1).First();
ProcessOperation LastOperation = processOperations.Where(it => it.OperationSeq == LastOperationSeq).First();
bool isExistLastOperationRecord = await DbScoped.SugarScope.CopyNew().Queryable<ProductPassStationRecord>()
.Where(it => it.ProductSN == productSN)
.Where(it => it.OperationCode == LastOperation.OperationCode)
.AnyAsync();
if (!isExistLastOperationRecord)
{
return EntryPermissionResult.NoRecordAtPreviousStation;
}
// 3 上工位NG 入站或者出站结果 NG
bool isExistLastOperationNG = await Context.Queryable<ProductPassStationRecord>()
.Where(it => it.ProductSN == productSN)
.Where(it => it.OperationCode == LastOperation.OperationCode)
.Where(it => it.PasstationType == 2 || it.PasstationType == 4)
.Where(it => it.ResultCode != 1)
.AnyAsync();
if (!isExistLastOperationNG)
{
result = EntryPermissionResult.PreviousStationNG;
goto InsertPassrecord;
}
// 4 扫码产品型号错误
bool isExistproductSN = await Context.Queryable<ProductLifecycle>()
.Where(it => it.ProductSN == productSN).AnyAsync();
bool isExistproductSNProducting = await Context.Queryable<ProductLifecycle>()
.Where(it => it.ProductSN == productSN && (it.ProductCurrentStatus != 1 && it.ProductCurrentStatus != 3)).AnyAsync();
if (!isExistproductSN || !isExistproductSNProducting)
{
result = EntryPermissionResult.ProductModelError;
goto InsertPassrecord;
}
//6时间超出规定无法生产
//7固化时间未到规定时间
int LastOperationStandardTime = processOperations.Where(operation => operation.OperationCode == LastOperation.OperationCode)
.Select(operation => operation.StandardTime??0).First();
if (LastOperationStandardTime > 0)
{
// 上一站的入站时间 和本站的请求时间差值
DateTime LastInStationTime= await Context.Queryable<ProductPassStationRecord>()
.Where(it => it.ProductSN == productSN)
.Where(it => it.OperationCode == LastOperation.OperationCode)
.Where(it => it.PasstationType == 1)
.MaxAsync(it => it.InStationTime??DateTime.MinValue);
TimeSpan timeDiff = DateTime.Now - LastInStationTime;
double totalSeconds = timeDiff.TotalSeconds;
if(totalSeconds < LastOperationStandardTime)
{
result = EntryPermissionResult.CuringTimeNotReached;
goto InsertPassrecord;
}
}
//12 最大重复入站次数
int MaxRepeatEntries = processOperations.Where(operation => operation.OperationCode == workstationCode).Select(operation => operation.MaxStationCount??1).First();
if(MaxRepeatEntries>1)
{
int currentStationEntryCount = await Context.Queryable<ProductPassStationRecord>()
.Where(it => it.ProductSN == productSN)
.Where(it => it.OperationCode == workstationCode)
.Where(it => it.PasstationType == 1)
.CountAsync();
if(currentStationEntryCount >= MaxRepeatEntries)
{
result = EntryPermissionResult.MaximumRepeatedEntries;
goto InsertPassrecord;
}
}
//OK 准许进站
result = EntryPermissionResult.AllowIntoStation;
goto InsertPassrecord;
InsertPassrecord:
//插入入站请求Resp过站记录
ProductPassStationRecord RespintoStation = new ProductPassStationRecord
{
ProductSN = productSN,
WorkstationCode = WorkstationCode,
Routingcode = processOperations.First().FkRoutingCode,
OperationCode = WorkstationCode,
ProductionLifeStage = 1, // 1表示生产中
PasstationType = 1, // 入站完毕
PasstationDescription = "入站完毕Resp",
EffectTime = DateTime.Now,
InStationTime = DateTime.Now,
ResultCode = (int)result,
ResultDescription = result.ToString(),
CreatedTime = DateTime.Now,
};
await Context.Insertable(RespintoStation).ExecuteCommandAsync();
return result;
}
}
}

View File

@ -15,7 +15,7 @@ using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
namespace RIZO.Service.PLCBackground.Stations
namespace RIZO.Service.PLCBackground.Stations.Out
{
/// <summary>
@ -360,56 +360,6 @@ namespace RIZO.Service.PLCBackground.Stations
}
/// <summary>
/// 出站校验结果
/// </summary>
public enum OutPermissionResult
{
/// <summary>
/// 1 OK - 成功
/// </summary>
OK = 1,
/// <summary>
/// 2 产品时间属性增加失败
/// </summary>
ProductTimeAttributeAddFailed = 2,
/// <summary>
/// 3 产品条码合并失败
/// </summary>
ProductBarcodeMergeFailed = 3,
/// <summary>
/// 4 产品结果上传次数超出
/// </summary>
ProductUploadCountExceeded = 4,
/// <summary>
/// 5 时间属性移除失败
/// </summary>
TimeAttributeRemoveFailed = 5,
/// <summary>
/// 6 数据保存失败
/// </summary>
DataSaveFailed = 6,
/// <summary>
/// 10 上传条码为空
/// </summary>
UploadBarcodeEmpty = 10,
/// <summary>
/// 11 未知异常
/// </summary>
UnkownException = 11,
/// <summary>
/// 工艺参数NG
/// </summary>
ProcessParameterNG = 12
}
}

View File

@ -0,0 +1,92 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace RIZO.Service.PLCBackground.Stations
{
/// <summary>
/// 入站校验结果
/// </summary>
public enum EntryPermissionResult
{
//1之前工位数据正常
AllowIntoStation = 1,
// 2上工位无记录
NoRecordAtPreviousStation = 2,
// 3上工位NG
PreviousStationNG = 3,
// 4扫码产品型号错误
ProductModelError = 4,
//5本工位NG
CurrentStationNG = 5,
//6时间超出规定无法生产
TimeExceeded = 6,
//7固化时间未到规定时间
CuringTimeNotReached = 7,
// 8未找到时间属性
TimeAttributeNotFound = 8,
// 10条码为空
BarcodeEmpty = 10,
//0之前工位数据异常
PreviousStationDataAbnormal = 0,
//11 未知异常
UnkownException = 11,
//12 最大重复入站次数
MaximumRepeatedEntries = 12
}
/// <summary>
/// 出站校验结果
/// </summary>
public enum OutPermissionResult
{
/// <summary>
/// 1 OK - 成功
/// </summary>
OK = 1,
/// <summary>
/// 2 产品时间属性增加失败
/// </summary>
ProductTimeAttributeAddFailed = 2,
/// <summary>
/// 3 产品条码合并失败
/// </summary>
ProductBarcodeMergeFailed = 3,
/// <summary>
/// 4 产品结果上传次数超出
/// </summary>
ProductUploadCountExceeded = 4,
/// <summary>
/// 5 时间属性移除失败
/// </summary>
TimeAttributeRemoveFailed = 5,
/// <summary>
/// 6 数据保存失败
/// </summary>
DataSaveFailed = 6,
/// <summary>
/// 10 上传条码为空
/// </summary>
UploadBarcodeEmpty = 10,
/// <summary>
/// 11 未知异常
/// </summary>
UnkownException = 11,
/// <summary>
/// 工艺参数NG
/// </summary>
ProcessParameterNG = 12
}
}