diff --git a/Infrastructure/Model/OptionsSetting.cs b/Infrastructure/Model/OptionsSetting.cs
index 7f2ab3d..9bcd0ec 100644
--- a/Infrastructure/Model/OptionsSetting.cs
+++ b/Infrastructure/Model/OptionsSetting.cs
@@ -56,7 +56,7 @@ namespace Infrastructure.Model
///
public RedisServerConfig RedisServer { get; set; }
- public PlcSettings PlcSettings { get; set; }
+ public PlcSettings[] PlcSettings { get; set; }
@@ -193,6 +193,7 @@ namespace Infrastructure.Model
{
public string Id { get; set; }
public string WorkStationName { get; set; }
+ public string WorkStationCode { get; set; }
public string PlcType { get; set; }
public string IpAddress { get; set; }
@@ -203,6 +204,8 @@ namespace Infrastructure.Model
{
public string Heartbeat { get; set; }
public string IntoStationAsk { get; set; }
+
+ public string ProductModel { get; set; }
public string ProductSN { get; set; }
public string IntoStationResp { get; set; }
}
diff --git a/RIZO.Admin.WebApi/appsettings.json b/RIZO.Admin.WebApi/appsettings.json
index 90acf0a..2ba5eb6 100644
--- a/RIZO.Admin.WebApi/appsettings.json
+++ b/RIZO.Admin.WebApi/appsettings.json
@@ -114,14 +114,15 @@
"PlcSettings": [
{
"Id": 1,
- "WorkStationName": "OP080-2 PCBA拧紧",
- "WorkStation": "OP080-2",
+ "WorkStationCode": "OP70_01",
+ "WorkStationName": "OP70_01 点散热胶GF1500",
"PlcType": "S71500",
"IpAddress": "192.168.11.71",
"IntoStation": {
- "Heartbeat": "DB1020.DBW0", // 心跳地址
+ "Heartbeat": "DB1010.DBW0", // 心跳地址
"IntoStationAsk": "DB1001.DBW2000", // 进站请求地址
- "ProductSN": "",
+ "ProductModel": "DB1001.DBB1000",
+ "ProductSN": "DB1001.DBB1054",
"IntoStationResp": "DB1010.DBW2000" // 进站回复结果地址
}
}
diff --git a/RIZO.Model/MES/product_trace/ProductPassStationRecord.cs b/RIZO.Model/MES/product_trace/ProductPassStationRecord.cs
index 02887e8..8d40c6d 100644
--- a/RIZO.Model/MES/product_trace/ProductPassStationRecord.cs
+++ b/RIZO.Model/MES/product_trace/ProductPassStationRecord.cs
@@ -54,6 +54,13 @@ namespace RIZO.Model.MES.product_trace
[StringLength(50)]
public string OperationCode { get; set; }
+ ///
+ /// 工序name
+ ///
+ [SugarColumn(ColumnName = "operationName", Length = 50)]
+ [StringLength(50)]
+ public string OperationName { get; set; }
+
///
/// 当前产品处于的生命周期
///
diff --git a/RIZO.Service/PLCBackground/Stations/Into/PlcIntoStationService_Common.cs b/RIZO.Service/PLCBackground/Stations/Into/PlcIntoStationService_Common.cs
new file mode 100644
index 0000000..2d8b5d1
--- /dev/null
+++ b/RIZO.Service/PLCBackground/Stations/Into/PlcIntoStationService_Common.cs
@@ -0,0 +1,257 @@
+using Infrastructure.Attribute;
+using Infrastructure.Extensions;
+using Infrastructure.Model;
+using MDM.Model.Plant;
+using MDM.Model.Process;
+using Microsoft.AspNetCore.JsonPatch.Operations;
+using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
+using NLog;
+using RIZO.Model.Mes;
+using RIZO.Model.MES.product_trace;
+using RIZO.Repository;
+using RIZO.Service.MES.product.IService;
+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
+{
+ ///
+ /// OP点散热胶GF1500
+ ///
+ //[AppService(ServiceType = typeof(PlcIntoStationService_Common), ServiceLifetime = LifeTime.Singleton)]
+ public class PlcIntoStationService_Common : BackgroundService
+ {
+ protected static Logger _logger = LogManager.GetCurrentClassLogger();
+ protected PlcConntectHepler _plcService;
+ protected readonly TimeSpan _pollingInterval = TimeSpan.FromSeconds(5);
+ // private readonly string _ipAddress = "192.168.10.222";
+ // private readonly CpuType _cpuType = CpuType.S71500;
+ protected string WorkstationCode;
+ protected readonly SqlSugarClient Context;
+ protected readonly OptionsSetting _optionsSetting;
+ protected PlcSettings plcSetting;
+ public PlcIntoStationService_Common(IOptions options)
+ {
+ Context = DbScoped.SugarScope.CopyNew();
+ _optionsSetting= options.Value;
+
+ }
+
+ protected override async Task ExecuteAsync(CancellationToken stoppingToken)
+ {
+ _logger.Info("PLC Polling Service started");
+
+ // 使用工厂方法创建服务实例
+ Enum.TryParse(plcSetting.PlcType, out CpuType cpuType);
+ // 获取当前的工序/工站
+ //WorkstationCode = await Context.Queryable().Where(it => it.PlcIP == plcSetting.IpAddress)
+ // .Select(it => it.WorkstationCode).FirstAsync();
+ plcSetting = _optionsSetting.PlcSettings.Where(it => it.WorkStationCode == WorkstationCode).First();
+ using (_plcService = new PlcConntectHepler(plcSetting.IpAddress, cpuType))
+ {
+
+
+
+ while (!stoppingToken.IsCancellationRequested)
+ {
+ try
+ {
+ //心跳检测 "DB1010.DBW0"
+ await _plcService.WriteAsync(plcSetting.intoStation.Heartbeat, 1);
+
+ // 轮询进站请求信号/工位开始查询请求 "DB1001.DBW2000"
+ int intoStationAsk = await _plcService.ReadAsync(plcSetting.intoStation.IntoStationAsk);
+ if (intoStationAsk == 1)
+ {
+ // 处理进站请求
+ _logger.Info("Processing into station request...");
+
+ //获取产品SN码 "DB1001.DBB1000"
+ string productModel = await _plcService.ReadStringAsync(plcSetting.intoStation.ProductModel);
+ //"DB1001.DBB1054"
+ string productSN = await _plcService.ReadStringAsync(plcSetting.intoStation.ProductSN);
+
+ // 获取工单
+
+ //获取工艺路线工序
+ List processOperations = await Context.Queryable().LeftJoin((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,
+ OperationName= plcSetting.WorkStationName,
+ 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);
+ // "DB1010.DBW2000"
+ await _plcService.WriteAsync(plcSetting.intoStation.IntoStationResp, (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");
+ }
+
+ ///
+ /// 校验是否可以进站
+ ///
+ /// 产品型号
+ /// 产品SN
+ ///
+ protected async Task checkEntryPermission(string productModel, string productSN, string workstationCode, List 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()
+ .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()
+ .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()
+ .Where(it => it.ProductSN == productSN).AnyAsync();
+
+
+ bool isExistproductSNProducting = await Context.Queryable()
+ .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()
+ .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()
+ .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,
+ OperationName = plcSetting.WorkStationName,
+ 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;
+ }
+
+
+
+
+ }
+
+}
+
diff --git a/RIZO.Service/PLCBackground/Stations/Into/PlcIntoStationService_OP70_01.cs b/RIZO.Service/PLCBackground/Stations/Into/PlcIntoStationService_OP70_01.cs
index c0ca638..f35d459 100644
--- a/RIZO.Service/PLCBackground/Stations/Into/PlcIntoStationService_OP70_01.cs
+++ b/RIZO.Service/PLCBackground/Stations/Into/PlcIntoStationService_OP70_01.cs
@@ -3,6 +3,7 @@ using Infrastructure.Extensions;
using Infrastructure.Model;
using MDM.Model.Plant;
using MDM.Model.Process;
+using Microsoft.AspNetCore.JsonPatch.Operations;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
@@ -29,8 +30,8 @@ namespace RIZO.Service.PLCBackground.Stations.Into
private static Logger _logger = LogManager.GetCurrentClassLogger();
private PlcConntectHepler _plcService;
private readonly TimeSpan _pollingInterval = TimeSpan.FromSeconds(5);
- private readonly string _ipAddress = "192.168.10.222";
- private readonly CpuType _cpuType = CpuType.S71500;
+ // private readonly string _ipAddress = "192.168.10.222";
+ // private readonly CpuType _cpuType = CpuType.S71500;
private string WorkstationCode;
private readonly SqlSugarClient Context;
private readonly OptionsSetting _optionsSetting;
@@ -39,19 +40,19 @@ namespace RIZO.Service.PLCBackground.Stations.Into
{
Context = DbScoped.SugarScope.CopyNew();
_optionsSetting= options.Value;
- plcSetting = _optionsSetting.PlcSettings;
+ plcSetting = _optionsSetting.PlcSettings.Where(it=>it.WorkStationCode== "OP70_01").First();
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
_logger.Info("PLC Polling Service started");
-
// 使用工厂方法创建服务实例
- using (_plcService = new PlcConntectHepler(_ipAddress, _cpuType))
+ Enum.TryParse(plcSetting.PlcType, out CpuType cpuType);
+ using (_plcService = new PlcConntectHepler(plcSetting.IpAddress, cpuType))
{
// 获取当前的工序/工站
- WorkstationCode = await Context.Queryable().Where(it => it.PlcIP == _ipAddress)
+ WorkstationCode = await Context.Queryable().Where(it => it.PlcIP == plcSetting.IpAddress)
.Select(it => it.WorkstationCode).FirstAsync();
@@ -59,19 +60,20 @@ namespace RIZO.Service.PLCBackground.Stations.Into
{
try
{
- //心跳检测
- await _plcService.WriteAsync("DB1010.DBW0", 1);
+ //心跳检测 "DB1010.DBW0"
+ await _plcService.WriteAsync(plcSetting.intoStation.Heartbeat, 1);
- // 轮询进站请求信号/工位开始查询请求
- int intoStationAsk = await _plcService.ReadAsync("DB1001.DBW2000");
+ // 轮询进站请求信号/工位开始查询请求 "DB1001.DBW2000"
+ int intoStationAsk = await _plcService.ReadAsync(plcSetting.intoStation.IntoStationAsk);
if (intoStationAsk == 1)
{
// 处理进站请求
_logger.Info("Processing into station request...");
- //获取产品SN码
- string productModel = await _plcService.ReadStringAsync("DB1001.DBB1000");
- string productSN = await _plcService.ReadStringAsync("DB1001.DBB1054");
+ //获取产品SN码 "DB1001.DBB1000"
+ string productModel = await _plcService.ReadStringAsync(plcSetting.intoStation.ProductModel);
+ //"DB1001.DBB1054"
+ string productSN = await _plcService.ReadStringAsync(plcSetting.intoStation.ProductSN);
// 获取工单
@@ -89,6 +91,7 @@ namespace RIZO.Service.PLCBackground.Stations.Into
WorkstationCode = WorkstationCode,
Routingcode = processOperations.First().FkRoutingCode,
OperationCode = WorkstationCode,
+ OperationName= plcSetting.WorkStationName,
ProductionLifeStage = 1, // 1表示生产中
PasstationType = 0, // 0表示入站请求
PasstationDescription = "入站请求ASK",
@@ -98,7 +101,8 @@ namespace RIZO.Service.PLCBackground.Stations.Into
await Context.Insertable(ASKintoStation).ExecuteCommandAsync();
//判断该产品是否允许进站
EntryPermissionResult result = await checkEntryPermission(productModel, productSN, WorkstationCode, processOperations);
- await _plcService.WriteAsync("DB1010.DBW2000", (int)result);
+ // "DB1010.DBW2000"
+ await _plcService.WriteAsync(plcSetting.intoStation.IntoStationResp, (int)result);
}
await Task.Delay(_pollingInterval, stoppingToken);
@@ -226,6 +230,7 @@ namespace RIZO.Service.PLCBackground.Stations.Into
WorkstationCode = WorkstationCode,
Routingcode = processOperations.First().FkRoutingCode,
OperationCode = WorkstationCode,
+ OperationName = plcSetting.WorkStationName,
ProductionLifeStage = 1, // 1表示生产中
PasstationType = 1, // 入站完毕
PasstationDescription = "入站完毕Resp",
diff --git a/RIZO.Service/PLCBackground/Stations/Into/PlcIntoStationService_OP70_02.cs b/RIZO.Service/PLCBackground/Stations/Into/PlcIntoStationService_OP70_02.cs
new file mode 100644
index 0000000..7eb6c56
--- /dev/null
+++ b/RIZO.Service/PLCBackground/Stations/Into/PlcIntoStationService_OP70_02.cs
@@ -0,0 +1,37 @@
+using Infrastructure.Attribute;
+using Infrastructure.Extensions;
+using Infrastructure.Model;
+using MDM.Model.Plant;
+using MDM.Model.Process;
+using Microsoft.AspNetCore.JsonPatch.Operations;
+using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
+using NLog;
+using RIZO.Model.Mes;
+using RIZO.Model.MES.product_trace;
+using RIZO.Repository;
+using RIZO.Service.MES.product.IService;
+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
+{
+ ///
+ /// OP点散热胶GF1500
+ ///
+ [AppService(ServiceType = typeof(PlcIntoStationService_OP70_02), ServiceLifetime = LifeTime.Singleton)]
+ public class PlcIntoStationService_OP70_02 : PlcIntoStationService_Common
+ {
+ public PlcIntoStationService_OP70_02(IOptions options) : base(options)
+ {
+ WorkstationCode = "OP70_02";
+ }
+ }
+
+}
+