using Infrastructure.Model; using JinianNet.JNTemplate; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Options; using NLog; using RIZO.Infrastructure.Helper; using RIZO.Model.Mes; using RIZO.Model.MES.product_trace; using S7.Net; using SqlSugar.IOC; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace RIZO.Service.PLCbackTask { public class MES_PLC_InterationTask : BackgroundService { private static Logger _logger = LogManager.GetCurrentClassLogger(); private SiemensS7Helper s7Helper; private readonly SqlSugarClient Context; private readonly PlcAddress _plcAddress; // 添加一个取消源来控制循环 private CancellationTokenSource _internalCts; public MES_PLC_InterationTask(IOptions options) { Context = DbScoped.SugarScope.CopyNew(); _plcAddress = options.Value.plcAddress; } protected override async Task ExecuteAsync(CancellationToken stoppingToken) { _logger.Info("PLC Polling Service started"); // 创建内部取消源,用于手动触发停止 _internalCts = CancellationTokenSource.CreateLinkedTokenSource(stoppingToken); using (s7Helper = new SiemensS7Helper(_plcAddress.IP)) { try { while (!stoppingToken.IsCancellationRequested && !_internalCts.Token.IsCancellationRequested) { if (!s7Helper.IsConnected) { var isConnected = await s7Helper.ConnectAsync(); if (!isConnected) { _logger.Error("Failed to connect to PLC."); break; } } #region 业务 bool? ReadHeartBeat = await s7Helper.ReadBoolAsync(_plcAddress.Read.ReadHeartBeat);//心跳 if (ReadHeartBeat != null && !ReadHeartBeat.Value) { Console.WriteLine("plc心跳 死亡"); // 使用内部取消源停止循环,而不是直接return _internalCts.Cancel(); break; // 跳出循环,但会继续执行finally块和Dispose } //TODO :1. 轮询出站请求 bool? OutStationAsk = await s7Helper.ReadBoolAsync(_plcAddress.Read.OutStationAsk); if (OutStationAsk != null && OutStationAsk.Value) { //TODO : 2.读取业务数据 int WorkStationId = await s7Helper.ReadIntAsync(_plcAddress.Read.WorkStation) ?? 0;//工站 int ProductModelID = await s7Helper.ReadIntAsync(_plcAddress.Read.ProductModelId) ?? 0;//产品型号ID float temperature = await s7Helper.ReadFloatAsync(_plcAddress.Read.ProductModelId) ?? 0.0f;//温度 float waterPressure = await s7Helper.ReadFloatAsync(_plcAddress.Read.WaterPressure) ?? 0.0f; // 水压,默认0.0 float airPressure = await s7Helper.ReadFloatAsync(_plcAddress.Read.AirPressure) ?? 0.0f; // 气压,默认0.0 int workTime = await s7Helper.ReadIntAsync(_plcAddress.Read.WorkTime) ?? 0; // 工作时间,默认0 // 同时记录到日志 _logger.Info($"业务数据读取完成 - 工站:{WorkStationId}, 产品型号:{ProductModelID}, 温度:{temperature}°C, 水压:{waterPressure}bar, 气压:{airPressure}bar, 工作时间:{workTime}秒"); List paramss = new(); // 存入工艺参数表 ProductTraceProcessParameters temperatureParam = new ProductTraceProcessParameters() { // 基础信息 ProductSn = WorkStationId.ToString(), // 产品序列号 RoutingCode = "ROUTING_PCB_ASSEMBLY", // 工艺路线代码 OperationCode = WorkStationId.ToString(), // 工序代码 FlowCode = "", // 流程代码 ProductlinebodyCode = "LINE_SMT_01", // 生产线体代码 WorkstationCode = WorkStationId.ToString(), // 工位代码 // 参数信息 ParameterCode = "temperature", // 参数代码 ParameterName = "温度", // 参数名称 PlcPoint = _plcAddress.Read.ProductModelId, // PLC点位 Description = "温度", // 描述 DataType = "float", // 数据类型 Unit = "°C", ActualValue = (decimal)temperature, CreatedTime = DateTime.Now, // 创建时间 }; paramss.Add(temperatureParam); ProductTraceProcessParameters airPressureParm = new ProductTraceProcessParameters() { // 基础信息 ProductSn = WorkStationId.ToString(), // 产品序列号 RoutingCode = "ROUTING_PCB_ASSEMBLY", // 工艺路线代码 OperationCode = WorkStationId.ToString(), // 工序代码 FlowCode = "", // 流程代码 ProductlinebodyCode = "LINE_SMT_01", // 生产线体代码 WorkstationCode = WorkStationId.ToString(), // 工位代码 // 参数信息 ParameterCode = "waterPressure", // 参数代码 ParameterName = "气压", // 参数名称 PlcPoint = _plcAddress.Read.WaterPressure, // PLC点位 Description = "气压", // 描述 DataType = "float", // 数据类型 Unit = "N", ActualValue = (decimal)airPressure, CreatedTime = DateTime.Now, // 创建时间 }; paramss.Add(airPressureParm); ProductTraceProcessParameters waterPressureParam = new ProductTraceProcessParameters() { // 基础信息 ProductSn = WorkStationId.ToString(), // 产品序列号 RoutingCode = "ROUTING_PCB_ASSEMBLY", // 工艺路线代码 OperationCode = WorkStationId.ToString(), // 工序代码 FlowCode = "", // 流程代码 ProductlinebodyCode = "LINE_SMT_01", // 生产线体代码 WorkstationCode = WorkStationId.ToString(), // 工位代码 // 参数信息 ParameterCode = "waterPressure", // 参数代码 ParameterName = "水压", // 参数名称 PlcPoint = _plcAddress.Read.WaterPressure, // PLC点位 Description = "水压", // 描述 DataType = "float", // 数据类型 Unit = "N", ActualValue = (decimal)waterPressure, CreatedTime = DateTime.Now, // 创建时间 }; paramss.Add(waterPressureParam); ProductTraceProcessParameters workTimeParam = new ProductTraceProcessParameters() { // 基础信息 ProductSn = WorkStationId.ToString(), // 产品序列号 RoutingCode = "ROUTING_PCB_ASSEMBLY", // 工艺路线代码 OperationCode = WorkStationId.ToString(), // 工序代码 FlowCode = "", // 流程代码 ProductlinebodyCode = "LINE_SMT_01", // 生产线体代码 WorkstationCode = WorkStationId.ToString(), // 工位代码 // 参数信息 ParameterCode = "waterPressure", // 参数代码 ParameterName = "工作时间", // 参数名称 PlcPoint = _plcAddress.Read.WaterPressure, // PLC点位 Description = "工作时间", // 描述 DataType = "int", // 数据类型 Unit = "秒", ActualValue = (decimal)workTime, CreatedTime = DateTime.Now, // 创建时间 }; paramss.Add(workTimeParam); await Context.Insertable(paramss).ExecuteCommandAsync(); //存入过站记录表 ProductPassStationRecord record = new ProductPassStationRecord() { ProductionLifeStage = 1, PasstationType = 3, PasstationDescription = "出站完毕", EffectTime = DateTime.Now, OutStationTime = DateTime.Now, ResultCode = 1, ResultDescription = "OK", CreatedTime = DateTime.Now }; // 出站完毕响应 await s7Helper.WriteBoolAsync(_plcAddress.Write.Resp, true); } #endregion // Wait for a defined interval before the next poll await Task.Delay(1000, stoppingToken); } } catch (Exception ex) { _logger.Error(ex, "Fatal error in PLC polling service"); throw; } finally { _logger.Info("PLC Polling Service stopped"); // 确保资源被正确释放 if (_internalCts != null) { _internalCts.Dispose(); _internalCts = null; } Console.WriteLine("后台任务关闭!!!!!"); } } } } }