进站处理
This commit is contained in:
parent
59856db96e
commit
a75fcf579f
@ -37,6 +37,13 @@ namespace MDM.Model.Process
|
||||
[SugarColumn(ColumnName = "operation_seq", Length = 50)]
|
||||
public int? OperationSeq { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 上一站工序序号(工序流转号)
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "last_operation_seq", Length = 50)]
|
||||
public int? LastOperationSeq { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 工序类别
|
||||
/// </summary>
|
||||
|
||||
92
RIZO.Model/MES/product_trace/ProductLifecycle.cs
Normal file
92
RIZO.Model/MES/product_trace/ProductLifecycle.cs
Normal file
@ -0,0 +1,92 @@
|
||||
namespace RIZO.Model.MES.product_trace
|
||||
{
|
||||
/// <summary>
|
||||
/// 产品生命周期表
|
||||
/// </summary>
|
||||
[SugarTable("product_lifecycle")]
|
||||
public class ProductLifecycle
|
||||
{
|
||||
/// <summary>
|
||||
/// 主键ID
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "Id", IsPrimaryKey = true, IsIdentity = true)]
|
||||
public int Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 工单号
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "workorder", Length = 50)]
|
||||
[StringLength(50)]
|
||||
public string Workorder { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 当前状态(0 未生产,1 生产中,2 完工中,3 返工中,4报废中)
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "product_current_status")]
|
||||
public int? ProductCurrentStatus { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 产品SN
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "product_SN", Length = 50)]
|
||||
[StringLength(50)]
|
||||
public string ProductSN { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 线别
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "line_code", Length = 50)]
|
||||
[StringLength(50)]
|
||||
public string LineCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 组别
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "group_code", Length = 50)]
|
||||
[StringLength(50)]
|
||||
public string GroupCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 工艺路线
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "routing_code", Length = 50)]
|
||||
[StringLength(50)]
|
||||
public string RoutingCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 产品开工时间
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "product_start_time")]
|
||||
public DateTime? ProductStartTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 产品返工时间
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "product_rework_time")]
|
||||
public DateTime? ProductReworkTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 产品完工时间
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "product_finish_time")]
|
||||
public DateTime? ProductFinishTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 产品报废时间
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "product_scrap_time")]
|
||||
public DateTime? ProductScrapTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 创建时间
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "created_time")]
|
||||
public DateTime? CreatedTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 更新时间
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "updated_time")]
|
||||
public DateTime? UpdatedTime { get; set; }
|
||||
}
|
||||
}
|
||||
132
RIZO.Model/MES/product_trace/ProductPassStationRecord.cs
Normal file
132
RIZO.Model/MES/product_trace/ProductPassStationRecord.cs
Normal file
@ -0,0 +1,132 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace RIZO.Model.MES.product_trace
|
||||
{
|
||||
/// <summary>
|
||||
/// 过站记录表
|
||||
/// </summary>
|
||||
[SugarTable("product_passstation_record")]
|
||||
public class ProductPassStationRecord
|
||||
{
|
||||
/// <summary>
|
||||
/// 主键ID
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "Id", IsPrimaryKey = true, IsIdentity = true)]
|
||||
public long Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 工单号
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "workorder", Length = 50)]
|
||||
[StringLength(50)]
|
||||
public string Workorder { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 产品SN
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "product_SN", Length = 100)]
|
||||
[StringLength(100)]
|
||||
public string ProductSN { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 工站code
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "workstationCode", Length = 50)]
|
||||
[StringLength(50)]
|
||||
public string WorkstationCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 工艺路线code
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "routingcode", Length = 50)]
|
||||
[StringLength(50)]
|
||||
public string Routingcode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 工序code
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "operationCode", Length = 50)]
|
||||
[StringLength(50)]
|
||||
public string OperationCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 当前产品处于的生命周期
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "production_life_stage")]
|
||||
public int? ProductionLifeStage { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 过站类型(0 入站请求,1入站完毕,2出站请求,3出站完毕)
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "passtation_type")]
|
||||
public int? PasstationType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 过站描述
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "passtation_description", Length = 255)]
|
||||
[StringLength(255)]
|
||||
public string PasstationDescription { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 生效时间
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "effect_time")]
|
||||
public DateTime? EffectTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 进站时间
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "InStationTime")]
|
||||
public DateTime? InStationTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 出站时间
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "OutStationTime")]
|
||||
public DateTime? OutStationTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 加工时间(s)
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "DurationSeconds")]
|
||||
public int? DurationSeconds { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 操作人
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "operator", Length = 50)]
|
||||
[StringLength(50)]
|
||||
public string Operator { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 结果代码1 是OK 其他NG
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "result_code")]
|
||||
public int? ResultCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 结果描述
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "result_description", Length = 255)]
|
||||
[StringLength(255)]
|
||||
public string ResultDescription { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 创建时间
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "created_time")]
|
||||
public DateTime? CreatedTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 更新时间
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnName = "updated_time")]
|
||||
public DateTime? UpdatedTime { get; set; }
|
||||
}
|
||||
}
|
||||
@ -1,7 +1,8 @@
|
||||
using System;
|
||||
using S7.Net; // 确保引用 S7.Net 库
|
||||
using System;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using S7.Net; // 确保引用 S7.Net 库
|
||||
|
||||
namespace RIZO.Service.PLC
|
||||
{
|
||||
@ -60,8 +61,15 @@ namespace RIZO.Service.PLC
|
||||
}
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 读取指定类型数据
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="address"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns></returns>
|
||||
|
||||
public async Task<object> ReadAsync(string address, CancellationToken cancellationToken = default)
|
||||
public async Task<T> ReadAsync<T>(string address, CancellationToken cancellationToken = default)
|
||||
{
|
||||
await _semaphore.WaitAsync(cancellationToken);
|
||||
try
|
||||
@ -73,12 +81,15 @@ namespace RIZO.Service.PLC
|
||||
throw new InvalidOperationException("PLC未连接");
|
||||
}
|
||||
|
||||
return await Task.Run(() => _plc.Read(address), cancellationToken);
|
||||
var result = await Task.Run(() => _plc.Read(address), cancellationToken);
|
||||
|
||||
// 根据泛型类型转换结果
|
||||
return ConvertResult<T>(result);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Read error: {ex.Message}");
|
||||
return null;
|
||||
return default(T);
|
||||
}
|
||||
finally
|
||||
{
|
||||
@ -86,6 +97,14 @@ namespace RIZO.Service.PLC
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 写入指定类型数据
|
||||
/// </summary>
|
||||
/// <param name="address"></param>
|
||||
/// <param name="value"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns></returns>
|
||||
public async Task WriteAsync(string address, object value, CancellationToken cancellationToken = default)
|
||||
{
|
||||
await _semaphore.WaitAsync(cancellationToken);
|
||||
@ -110,6 +129,209 @@ namespace RIZO.Service.PLC
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private T ConvertResult<T>(object result)
|
||||
{
|
||||
if (result == null) return default(T);
|
||||
|
||||
if (typeof(T).IsAssignableFrom(result.GetType()))
|
||||
{
|
||||
return (T)result;
|
||||
}
|
||||
|
||||
return typeof(T) switch
|
||||
{
|
||||
Type t when t == typeof(bool) => (T)(object)Convert.ToBoolean(result),
|
||||
Type t when t == typeof(short) => (T)(object)Convert.ToInt16(result),
|
||||
Type t when t == typeof(int) => (T)(object)Convert.ToInt32(result),
|
||||
Type t when t == typeof(float) => (T)(object)Convert.ToSingle(result),
|
||||
Type t when t == typeof(double) => (T)(object)Convert.ToDouble(result),
|
||||
Type t when t == typeof(string) => (T)(object)result.ToString(),
|
||||
_ => throw new InvalidCastException($"无法将类型 {result.GetType()} 转换为 {typeof(T)}")
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从PLC读取字符串(主要方法)
|
||||
/// </summary>
|
||||
/// <param name="address">地址字符串(如:"DB1001.DBB1000")</param>
|
||||
/// <returns>读取到的字符串</returns>
|
||||
public async Task<string> ReadStringAsync(string address)
|
||||
{
|
||||
return await ReadStringAsync(address, CancellationToken.None);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从PLC读取字符串(带取消令牌)
|
||||
/// </summary>
|
||||
/// <param name="address">地址字符串(如:"DB1001.DBB1000")</param>
|
||||
/// <param name="cancellationToken">取消令牌</param>
|
||||
/// <returns>读取到的字符串</returns>
|
||||
public async Task<string> ReadStringAsync(string address, CancellationToken cancellationToken = default)
|
||||
{
|
||||
await _semaphore.WaitAsync(cancellationToken);
|
||||
try
|
||||
{
|
||||
if (_plc == null || !_plc.IsConnected)
|
||||
{
|
||||
await ConnectAsync(cancellationToken);
|
||||
if (_plc == null || !_plc.IsConnected)
|
||||
throw new InvalidOperationException("PLC未连接");
|
||||
}
|
||||
|
||||
// 解析地址
|
||||
if (!ParseAddress(address, out int dbNumber, out int startByte))
|
||||
{
|
||||
throw new ArgumentException($"无效的地址格式: {address}");
|
||||
}
|
||||
|
||||
// 读取第一个字节(字符串长度)
|
||||
byte stringLength = await Task.Run(() =>
|
||||
(byte)_plc.Read(DataType.DataBlock, dbNumber, startByte, VarType.Byte, 1), cancellationToken);
|
||||
|
||||
// 如果长度为0,直接返回空字符串
|
||||
if (stringLength == 0)
|
||||
return string.Empty;
|
||||
|
||||
// 读取字符串内容(从startByte+1开始,读取stringLength个字节)
|
||||
byte[] stringBytes = await Task.Run(() =>
|
||||
(byte[])_plc.Read(DataType.DataBlock, dbNumber, startByte + 1, VarType.Byte, (int)stringLength), cancellationToken);
|
||||
|
||||
// 将字节数组转换为ASCII字符串
|
||||
return Encoding.ASCII.GetString(stringBytes);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"ReadString error: {ex.Message}");
|
||||
return string.Empty;
|
||||
}
|
||||
finally
|
||||
{
|
||||
_semaphore.Release();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 向PLC写入字符串
|
||||
/// </summary>
|
||||
/// <param name="address">地址字符串(如:"DB1001.DBB1000")</param>
|
||||
/// <param name="value">要写入的字符串</param>
|
||||
/// <returns>是否写入成功</returns>
|
||||
public async Task<bool> WriteStringAsync(string address, string value)
|
||||
{
|
||||
return await WriteStringAsync(address, value, CancellationToken.None);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 向PLC写入字符串(带取消令牌)
|
||||
/// </summary>
|
||||
/// <param name="address">地址字符串(如:"DB1001.DBB1000")</param>
|
||||
/// <param name="value">要写入的字符串</param>
|
||||
/// <param name="cancellationToken">取消令牌</param>
|
||||
/// <returns>是否写入成功</returns>
|
||||
public async Task<bool> WriteStringAsync(string address, string value, CancellationToken cancellationToken = default)
|
||||
{
|
||||
await _semaphore.WaitAsync(cancellationToken);
|
||||
try
|
||||
{
|
||||
if (_plc == null || !_plc.IsConnected)
|
||||
{
|
||||
await ConnectAsync(cancellationToken);
|
||||
if (_plc == null || !_plc.IsConnected)
|
||||
throw new InvalidOperationException("PLC未连接");
|
||||
}
|
||||
|
||||
if (value == null)
|
||||
value = string.Empty;
|
||||
|
||||
// 限制字符串长度(S7字符串最大254字符)
|
||||
if (value.Length > 254)
|
||||
{
|
||||
value = value.Substring(0, 254);
|
||||
}
|
||||
|
||||
// 准备写入的数据
|
||||
List<byte> writeData = new List<byte>();
|
||||
|
||||
// 第一个字节:字符串长度
|
||||
writeData.Add((byte)value.Length);
|
||||
|
||||
// 后续字节:字符串内容(ASCII编码)
|
||||
byte[] contentBytes = Encoding.ASCII.GetBytes(value);
|
||||
writeData.AddRange(contentBytes);
|
||||
|
||||
// 解析地址
|
||||
if (!ParseAddress(address, out int dbNumber, out int startByte))
|
||||
{
|
||||
throw new ArgumentException($"无效的地址格式: {address}");
|
||||
}
|
||||
|
||||
// 写入数据
|
||||
await Task.Run(() =>
|
||||
_plc.WriteBytes(DataType.DataBlock, dbNumber, startByte, writeData.ToArray()), cancellationToken);
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"WriteString error: {ex.Message}");
|
||||
return false;
|
||||
}
|
||||
finally
|
||||
{
|
||||
_semaphore.Release();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 解析PLC地址字符串,提取DB号和起始字节
|
||||
/// 支持格式:DB1001.DBB1000, DB1001.DBW1000, DB1001.DBD1000 等
|
||||
/// </summary>
|
||||
private bool ParseAddress(string address, out int dbNumber, out int startByte)
|
||||
{
|
||||
dbNumber = 0;
|
||||
startByte = 0;
|
||||
|
||||
try
|
||||
{
|
||||
// 移除空格并转为大写
|
||||
address = address.Replace(" ", "").ToUpper();
|
||||
|
||||
// 分割DB部分和数据部分
|
||||
string[] parts = address.Split('.');
|
||||
if (parts.Length < 2) return false;
|
||||
|
||||
// 解析DB号
|
||||
string dbPart = parts[0];
|
||||
if (!dbPart.StartsWith("DB") || !int.TryParse(dbPart.Substring(2), out dbNumber))
|
||||
return false;
|
||||
|
||||
// 解析起始字节
|
||||
string dataPart = parts[1];
|
||||
|
||||
// 提取偏移量数字(去掉DBB/DBW/DBD前缀)
|
||||
string offsetStr = "";
|
||||
if (dataPart.StartsWith("DBB"))
|
||||
offsetStr = dataPart.Substring(3);
|
||||
else if (dataPart.StartsWith("DBW"))
|
||||
offsetStr = dataPart.Substring(3);
|
||||
else if (dataPart.StartsWith("DBD"))
|
||||
offsetStr = dataPart.Substring(3);
|
||||
else
|
||||
return false;
|
||||
|
||||
if (!int.TryParse(offsetStr, out startByte))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
|
||||
@ -1,70 +0,0 @@
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using RIZO.Service.PLC;
|
||||
using S7.Net;
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace RIZO.Service.PLCBackground
|
||||
{
|
||||
|
||||
|
||||
public class PlcPollingServiceOP72 : BackgroundService
|
||||
{
|
||||
private readonly ILogger<PlcPollingServiceOP72> _logger;
|
||||
private PlcConntectHepler _plcService;
|
||||
private readonly TimeSpan _pollingInterval = TimeSpan.FromSeconds(5);
|
||||
private readonly string _ipAddress = "192.168.0.1";
|
||||
private readonly CpuType _cpuType = CpuType.S71500;
|
||||
|
||||
public PlcPollingServiceOP72(ILogger<PlcPollingServiceOP72> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
|
||||
|
||||
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||
{
|
||||
_logger.LogInformation("PLC Polling Service started");
|
||||
|
||||
// 使用工厂方法创建服务实例
|
||||
using (_plcService = new PlcConntectHepler(_ipAddress, _cpuType))
|
||||
{
|
||||
while (!stoppingToken.IsCancellationRequested)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 使用异步读写方法
|
||||
var data1 = await _plcService.ReadAsync("DB1.DBW0");
|
||||
var data2 = await _plcService.ReadAsync("DB1.DBD4");
|
||||
var flag = await _plcService.ReadAsync("M10.0");
|
||||
|
||||
_logger.LogInformation($"DB1.DBW0: {data1}, DB1.DBD4: {data2}, M10.0: {flag}");
|
||||
|
||||
await _plcService.WriteAsync("DB1.DBW10", (short)123);
|
||||
await _plcService.WriteAsync("DB1.DBD20", 45.67d);
|
||||
|
||||
await Task.Delay(_pollingInterval, stoppingToken);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
_logger.LogInformation("PLC Polling Service is stopping");
|
||||
break;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "PLC polling error");
|
||||
await Task.Delay(TimeSpan.FromSeconds(10), stoppingToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_logger.LogInformation("PLC Polling Service stopped");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
189
RIZO.Service/PLCBackground/Stations/PlcPollingService_OP07_01.cs
Normal file
189
RIZO.Service/PLCBackground/Stations/PlcPollingService_OP07_01.cs
Normal file
@ -0,0 +1,189 @@
|
||||
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.MES.product_trace;
|
||||
using RIZO.Service.PLC;
|
||||
using S7.Net;
|
||||
using SqlSugar.IOC;
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace RIZO.Service.PLCBackground.Stations
|
||||
{
|
||||
|
||||
|
||||
public class PlcPollingService_OP07_01 : BackgroundService
|
||||
{
|
||||
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 string WorkstationCode;
|
||||
|
||||
|
||||
|
||||
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||
{
|
||||
_logger.Info("PLC Polling Service started");
|
||||
|
||||
// 使用工厂方法创建服务实例
|
||||
using (_plcService = new PlcConntectHepler(_ipAddress, _cpuType))
|
||||
{
|
||||
|
||||
// 获取当前的工序/工站
|
||||
WorkstationCode = await DbScoped.SugarScope.CopyNew().Queryable<PlantWorkstation>().Where(it => it.PlcIP == _ipAddress)
|
||||
.Select(it => it.WorkstationCode).FirstAsync();
|
||||
|
||||
|
||||
while (!stoppingToken.IsCancellationRequested)
|
||||
{
|
||||
try
|
||||
{
|
||||
//心跳检测
|
||||
await _plcService.WriteAsync("DB1010.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");
|
||||
|
||||
|
||||
|
||||
|
||||
//判断该产品是否允许进站
|
||||
|
||||
EntryPermissionResult result = await checkEntryPermission(productModel, productSN, WorkstationCode);
|
||||
await _plcService.WriteAsync("DB1010.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 = await DbScoped.SugarScope.CopyNew().Queryable<ProcessRouting>().LeftJoin<ProcessOperation>((r, o) => r.RoutingCode == o.FkRoutingCode)
|
||||
.Where((r, o) => r.FkProductMaterialCode == productModel && r.Status == 1)
|
||||
.Select((r, o) => o).ToListAsync();
|
||||
|
||||
|
||||
|
||||
//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 DbScoped.SugarScope.CopyNew().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)
|
||||
{
|
||||
return EntryPermissionResult.PreviousStationNG;
|
||||
}
|
||||
|
||||
|
||||
// 4 扫码产品型号错误
|
||||
bool isExistproductSN = await DbScoped.SugarScope.CopyNew().Queryable<ProductLifecycle>()
|
||||
.Where(it => it.ProductSN == productSN).AnyAsync();
|
||||
bool isExistproductSNProducting = await DbScoped.SugarScope.CopyNew().Queryable<ProductLifecycle>()
|
||||
.Where(it => it.ProductSN == productSN && (it.ProductCurrentStatus != 1 && it.ProductCurrentStatus != 3)).AnyAsync();
|
||||
if (!isExistproductSN || !isExistproductSNProducting)
|
||||
{
|
||||
return EntryPermissionResult.ProductModelError;
|
||||
}
|
||||
//6时间超出规定无法生产
|
||||
|
||||
//查询上一站标准时间
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
return EntryPermissionResult.UnkownException;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,6 +9,7 @@
|
||||
<NoWarn>1591</NoWarn>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\MDM\MDM.csproj" />
|
||||
<ProjectReference Include="..\RIZO.ServiceCore\RIZO.ServiceCore.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user