Files
WCS/Plugins/Wcs/Plugin.Cowain.Wcs/Services/FindFlowTaskService.cs
2026-03-02 10:56:30 +08:00

317 lines
13 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using Cowain.Base.DBContext;
using Cowain.Base.Services;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Plugin.Cowain.Driver.Abstractions;
using Plugin.Cowain.Wcs.IServices;
using Plugin.Cowain.Wcs.Models.Dto;
using Plugin.Cowain.Wcs.Models.Enum;
namespace Plugin.Cowain.Wcs.Services;
public class FindFlowTaskService : BaseService, IFindFlowTaskService
{
private class ProcessFlowInfo
{
public ProcessFlowInfo(ProcessDto process, ProcessFlowDto flow)
{
Process = process;
Flow = flow;
}
public ProcessDto Process { get; set; }
public ProcessFlowDto Flow { get; set; }
}
private static SemaphoreSlim semaphore = new SemaphoreSlim(1);
private IDeviceMonitor _deviceMonitor;
private IProcessService _processService;
private IServiceScopeFactory _scopeFactory;
private readonly ILogger<FindFlowTaskService> _logger;
public FindFlowTaskService(IDbContextFactory<SqlDbContext> dbContextFactory, IServiceScopeFactory scopeFactory, IDeviceMonitor deviceMonitor, IProcessService processService, ILogger<FindFlowTaskService> logger) : base(dbContextFactory)
{
_deviceMonitor = deviceMonitor;
_processService = processService;
_scopeFactory = scopeFactory;
_logger = logger;
}
/// <summary>
/// 根据工站列表和流程工艺列表,构建所有可执行的候选任务
/// </summary>
private List<TaskDataDto> BuildCandidateTasks(List<StationDto> stations, List<ProcessFlowInfo> processAndFlows)
{
var candidateTasks = new List<TaskDataDto>();
foreach (var item in processAndFlows)
{
var flow = item.Flow;
var process = item.Process;
// 第一段工站匹配
var fromStation1 = stations.FirstOrDefault(s =>
s.ProcessName == process.Name &&
s.Id == flow.FromStationId1 &&
s.Status == flow.FromStatus1);
var toStation1 = stations.FirstOrDefault(s =>
s.ProcessName == process.Name &&
s.Id == flow.ToStationId1 &&
s.Status == flow.ToStatus1);
if (fromStation1 == null || toStation1 == null)
continue;
// 第二段工站匹配(如有)
bool secondMatched = true;
if (!(flow.FromStationId2 == 0 && flow.ToStationId2 == 0))
{
var fromStation2 = stations.FirstOrDefault(s =>
s.ProcessName == process.Name &&
s.Id == flow.FromStationId2 &&
s.Status == flow.FromStatus2);
var toStation2 = stations.FirstOrDefault(s =>
s.ProcessName == process.Name &&
s.Id == flow.ToStationId2 &&
s.Status == flow.ToStatus2);
if (fromStation2 == null || toStation2 == null)
secondMatched = false;
}
if (secondMatched)
{
candidateTasks.Add(new TaskDataDto
{
ProcessId = process.Id,
ProcessName = process.Name,
Priority = flow.Priority,
FromStationId1 = flow.FromStationId1,
ToStationId1 = flow.ToStationId1,
FromStatus1 = flow.FromStatus1,
ToStatus1 = flow.ToStatus1,
FromStationId2 = flow.FromStationId2,
ToStationId2 = flow.ToStationId2,
FromStatus2 = flow.FromStatus2,
ToStatus2 = flow.ToStatus2,
ExecuteAction = 0,
IsFinished = false,
Action = flow.Action,
});
}
}
return candidateTasks;
}
/// <summary>
/// 延时查询任务(非阻塞方式)
/// </summary>
private async Task DelayedQueryAsync(TaskDataDto originalTask)
{
try
{
await Task.Delay(10000).ConfigureAwait(false);
_logger.LogInformation($"延时10秒后重新查询任务原任务{JsonConvert.SerializeObject(originalTask)}");
// 使用新的作用域创建独立的DbContext
using var scope = _scopeFactory.CreateScope();
var tempService = scope.ServiceProvider.GetRequiredService<IFindFlowTaskService>();
await tempService.FindTaskAsync(originalTask);
}
catch (Exception ex)
{
_logger.LogError(ex, "延时查询任务失败");
}
}
/// <summary>
/// 保存任务并执行
/// </summary>
private async Task SaveAndExecuteTask(TaskDataDto task)
{
using var dbContext = _dbContextFactory.CreateDbContext();
var taskSet = dbContext.GetDbSet<TaskDataDto>();
await taskSet.AddAsync(task);
var saveCount = await dbContext.SaveChangesAsync();
if (saveCount > 0)
{
_logger.LogInformation($"找到并添加任务:{JsonConvert.SerializeObject(task)}");
var getJson = await _processService.GetJsonData(task.ProcessId);
if (getJson.IsSuccess)
{
var taskJsonParam = _deviceMonitor.GetActionParam(getJson.Data!.PlcName!, getJson.Data!.Address!);
if (taskJsonParam.IsSuccess)
{
using var scope = _scopeFactory.CreateScope();
var taskService = scope.ServiceProvider.GetRequiredService<IRgvTaskService>();
var execute = await taskService.ExecuteAsync(taskJsonParam.Data, RgvUpdateSourceEnum.First);
}
}
else
{
_logger.LogError(getJson.ErrorMessage);
}
}
else
{
_logger.LogError("任务添加失败");
}
}
public async Task FindTaskAsync(TaskDataDto? taskData = null)
{
try
{
await semaphore.WaitAsync();
using var dbContext = _dbContextFactory.CreateDbContext();
var taskSet = dbContext.GetDbSet<TaskDataDto>();
var unfinishedTask = await taskSet.AsNoTracking().FirstOrDefaultAsync(t => !t.IsFinished);
if (unfinishedTask != null)
{
_logger.LogInformation($"已有未完成的任务任务ID{unfinishedTask.Id},任务内容:{JsonConvert.SerializeObject(unfinishedTask)}");
return;
}
var rgvSet = dbContext.GetDbSet<RgvDto>();
var processSet = dbContext.GetDbSet<ProcessDto>();
var flowSet = dbContext.GetDbSet<ProcessFlowDto>();
var stationSet = dbContext.GetDbSet<StationDto>();
// 只查可用工站
var enabledStations = await stationSet.AsNoTracking().Where(s => s.Enable && s.NextStationId <= 0).ToListAsync();
// 组合所有流程和工艺
var query = from flow in flowSet
join process in processSet on flow.ProcessId equals process.Id
select new ProcessFlowInfo(process, flow);
var processAndFlows = await query.ToListAsync();
// 查找候选任务
var candidateTasks = BuildCandidateTasks(enabledStations, processAndFlows);
// 取优先级最高的
//var findTask = candidateTasks
// .OrderByDescending(x => x.Priority)
// .FirstOrDefault();
// 取优先级最高的如果有多条则按距离RGV->From1 + From1->To1最小选取
TaskDataDto? findTask = null;
if (candidateTasks != null && candidateTasks.Count > 0)
{
var maxPriority = candidateTasks.Max(x => x.Priority);
var topCandidates = candidateTasks.Where(x => x.Priority == maxPriority).ToList();
if (topCandidates.Count == 1)
{
//只有一条最高优先级任务,直接选取
findTask = topCandidates[0];
_logger.LogInformation($"最优任务只有一条,任务内容:{JsonConvert.SerializeObject(findTask)}");
}
else
{
//优先级最高的任务大于1个尝试获取当前RGV位置
var rgv = await rgvSet.AsNoTracking().FirstOrDefaultAsync();
if (rgv == null)
{
// 无RGV位置时退回到取第一条
findTask = topCandidates[0];
_logger.LogInformation($"rgv位置获取失败使用默认最优任务任务内容{JsonConvert.SerializeObject(findTask)}");
}
else
{
double bestDistance = double.MaxValue;
foreach (var cand in topCandidates)
{
//遍历优先级最高的任务,计算距离
var fromStation = enabledStations.FirstOrDefault(s => s.Id == cand.FromStationId1);
var toStation = enabledStations.FirstOrDefault(s => s.Id == cand.ToStationId1);
if (fromStation == null || toStation == null)
continue;
// 计算:|RGV.X - From.X| + |From.X - To.X|
var distance = Math.Abs((double)rgv.LayOutX - (double)fromStation.LayOutX)
+ Math.Abs((double)fromStation.LayOutX - (double)toStation.LayOutX);
if (distance < bestDistance)
{
bestDistance = distance;
findTask = cand;
}
}
// 如果遍历未找到合法候选(异常情况),退回到第一条
if (findTask == null)
{
findTask = topCandidates[0];
}
else
{
_logger.LogInformation($"最优任务找到,距离:{bestDistance},任务内容:{JsonConvert.SerializeObject(findTask)}");
}
}
}
}
// 如果taskData不为空说明是通过延时查询触发的
if (taskData != null)
{
if (findTask == null)
{
_logger.LogInformation($"延时查询后,没有找到新任务,保存原任务");
await SaveAndExecuteTask(taskData);
}
else
{
_logger.LogInformation($"通过延时查询触发的查询,原任务:{JsonConvert.SerializeObject(taskData)}");
// 如果查询到第二段还是0且第一段相同保存这个task
if (findTask.FromStationId2 == 0 && findTask.ToStationId2 == 0)
{
_logger.LogInformation($"延时查询后,还是只有单段任务,保存原任务");
await SaveAndExecuteTask(taskData);
}
// 如果查询到第二段不是0的任务保存新任务
else if (findTask.FromStationId2 != 0 || findTask.ToStationId2 != 0)
{
_logger.LogInformation($"延时查询后,找到更好的两段任务,保存新任务");
await SaveAndExecuteTask(findTask);
}
else
{
_logger.LogInformation($"延时查询后,没有找到更好的任务,不保存");
}
}
}
else
{
if (findTask == null)
{
_logger.LogInformation($"没有需要执行的任务");
return;
}
// 首次查询
if (findTask.FromStationId2 == 0 && findTask.ToStationId2 == 0)
{
// 任务的第二段是0启动非阻塞的延时查询
_logger.LogInformation($"当前任务第二段为0需要重新查询原任务{JsonConvert.SerializeObject(findTask)}");
// 使用Task.Run启动非阻塞的延时查询不等待其完成
_ = Task.Run(async () => await DelayedQueryAsync(findTask));
}
else
{
// 直接保存任务
await SaveAndExecuteTask(findTask);
}
}
}
catch (Exception ex)
{
_logger.LogError(ex, "查找任务失败");
}
finally
{
semaphore.Release();
}
}
}