using System; using System.Security.Cryptography.X509Certificates; using System.Threading; using System.Threading.Tasks; using DOAN.Infrastructure.PLC; using DOAN.Model.PBL; using DOAN.Model.System; using Microsoft.Extensions.Hosting; using SqlSugar; using SqlSugar.IOC; namespace DOAN.ServiceCore { /// /// 永驻线程 /// 功能:检测传感器信号,判断箱子数 /// public class DoanBackgroundService : IHostedService, IDisposable { private readonly CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource(); private Task _executingTask; private PLCTool pLCTool; public Task StartAsync(CancellationToken cancellationToken) { pLCTool = new PLCTool(); pLCTool.ConnectPLC(); // 当服务开始时,启动后台任务 _executingTask = ExecuteAsync(_cancellationTokenSource.Token); return _executingTask.IsCompleted ? _executingTask : Task.CompletedTask; } public async Task StopAsync(CancellationToken cancellationToken) { // 请求取消后台任务 _cancellationTokenSource.Cancel(); // 等待后台任务完成 await Task.WhenAny(_executingTask, Task.Delay(Timeout.Infinite, cancellationToken)); } private static bool GetInvertedBit(byte b, int position) { if (position < 0 || position > 7) { throw new ArgumentOutOfRangeException(nameof(position), "Position must be between 0 and 7."); } // 创建一个掩码,其中只有要检查的那一位是1 byte mask = (byte)(1 << position); // 使用按位与运算符(&)来检查该位是否为1 bool isSet = (b & mask) != 0; // 返回取反后的值 return !isSet; } /// /// 功能:检测传感器信号,判断箱子数并更新库存及日志 /// /// 取消令牌 /// private async Task ExecuteAsync(CancellationToken stoppingToken) { try { while (!stoppingToken.IsCancellationRequested) { // 读取PLC I/O状态 12个数组 byte[] plcSensorValues = pLCTool.ReadAllValue("VB100", 12); // 获取所有料架层 List storagelocationList = await DbScoped.SugarScope.CopyNew().Queryable().ToListAsync(); // 获取点位表 List pointPositionList = await DbScoped.SugarScope.CopyNew().Queryable().ToListAsync(); List updateStoragelocationList = []; List inventoryLogs = []; foreach (Storagelocation layerItem in storagelocationList) { // 获取这个料架层的点位表 List layerPoints = pointPositionList.Where(it => it.FkStorageId == layerItem.Id).ToList(); if (layerPoints != null && layerPoints.Count > 0) { int currentPackageCount = 0; // 默认最小值为0 foreach (PlcAddressTable point in layerPoints) { int row = point.ByteNum - 100; int col = point.BitNum - 1; if (plcSensorValues != null) { currentPackageCount += GetInvertedBit(plcSensorValues[row], col) ? 1 : 0; } } // 检查箱子数量变化并记录日志 if (currentPackageCount > layerItem.PackageNum) { UpdateAndLog(layerItem, currentPackageCount, 2, "补料", inventoryLogs); updateStoragelocationList.Add(layerItem); } else if (currentPackageCount < layerItem.PackageNum) { UpdateAndLog(layerItem, currentPackageCount, 1, "出料", inventoryLogs); updateStoragelocationList.Add(layerItem); } } } // 更新库存 if (updateStoragelocationList.Count > 0) { await DbScoped.SugarScope.CopyNew().Updateable(updateStoragelocationList).ExecuteCommandAsync(); } // 插入库存变更日志 if (inventoryLogs.Count > 0) { await DbScoped.SugarScope.CopyNew().Insertable(inventoryLogs).ExecuteCommandAsync(); } // 添加延迟以避免频繁查询 await Task.Delay(200, stoppingToken); } } catch (OperationCanceledException) { Console.WriteLine("任务已取消"); } catch (Exception ex) { Console.WriteLine($"DoanBackGround线程ExecuteAsync异常: {ex.Message}\n堆栈跟踪: {ex.StackTrace}"); } } /// /// 更新料架位置的箱子数量并记录库存日志 /// /// 料架位置对象 /// 新的箱子数量 /// 操作类型 (1-出库, 2-入库) /// 操作员名称 /// 库存日志列表 private void UpdateAndLog(Storagelocation storageLocation, int newPackageCount, int operation, string operatorName, List inventoryLogs) { storageLocation.PackageNum = newPackageCount; storageLocation.UpdatedBy = operatorName; storageLocation.UpdatedTime = DateTime.Now; Inventorylog inventoryLog = new Inventorylog { Id = SnowFlakeSingle.Instance.NextId().ToString(), RackCode = storageLocation.RackCode, Partnumber = storageLocation.Partnumber, Operation = operation, PackageNum = Math.Abs((int)(newPackageCount - storageLocation.PackageNum)), CreatedBy = operatorName, CreatedTime = DateTime.Now.ToLocalTime() }; inventoryLogs.Add(inventoryLog); } public void Dispose() { try { pLCTool.ConnectClose(); _cancellationTokenSource.Cancel(); _executingTask.Wait(); _cancellationTokenSource.Dispose(); } catch (Exception ex) { Console.WriteLine("DoanBackGround线程Dispose异常:" + ex.Message); } } } }