317 lines
13 KiB
C#
317 lines
13 KiB
C#
using Cowain.Base.DBContext;
|
||
using Cowain.Base.Helpers;
|
||
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;
|
||
using System.Threading;
|
||
|
||
namespace Plugin.Cowain.Wcs.Services;
|
||
|
||
public class FindFlowTask6180Service : BaseService, IFindFlowTask6180Service
|
||
{
|
||
private class ProcessFlowInfo
|
||
{
|
||
public ProcessFlowInfo(ProcessDto process, ProcessFlow6180Dto flow)
|
||
{
|
||
Process = process;
|
||
Flow = flow;
|
||
}
|
||
|
||
public ProcessDto Process { get; set; }
|
||
public ProcessFlow6180Dto Flow { get; set; }
|
||
}
|
||
|
||
private static SemaphoreSlim semaphore = new SemaphoreSlim(1);
|
||
private IDeviceMonitor _deviceMonitor;
|
||
private IProcessService _processService;
|
||
private IServiceScopeFactory _scopeFactory;
|
||
private readonly ILogger<FindFlowTask6180Service> _logger;
|
||
|
||
public FindFlowTask6180Service(SqlDbContext dbContext, IServiceScopeFactory scopeFactory, IDeviceMonitor deviceMonitor, IProcessService processService, ILogger<FindFlowTask6180Service> logger) : base(dbContext)
|
||
{
|
||
_deviceMonitor = deviceMonitor;
|
||
_processService = processService;
|
||
_scopeFactory = scopeFactory;
|
||
_logger = logger;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 根据工站列表和流程工艺列表,构建所有可执行的候选任务
|
||
/// </summary>
|
||
private List<TaskData6180Dto> BuildCandidateTasks(List<StationDto> stations, List<ProcessFlowInfo> processAndFlows)
|
||
{
|
||
var candidateTasks = new List<TaskData6180Dto>();
|
||
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 TaskData6180Dto
|
||
{
|
||
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(TaskData6180Dto 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<IFindFlowTask6180Service>();
|
||
await tempService.FindTaskAsync(originalTask);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_logger.LogError(ex, "延时查询任务失败");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 保存任务并执行
|
||
/// </summary>
|
||
private async Task SaveAndExecuteTask(TaskData6180Dto task)
|
||
{
|
||
var taskSet = _dBContext.GetDbSet<TaskData6180Dto>();
|
||
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<IRgvTask6180Service>();
|
||
var execute = await taskService.ExecuteAsync(taskJsonParam.Data, RgvUpdateSourceEnum.First);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
_logger.LogError(getJson.ErrorMessage);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
_logger.LogError("任务添加失败");
|
||
}
|
||
}
|
||
|
||
public async Task FindTaskAsync(TaskData6180Dto? taskData = null)
|
||
{
|
||
try
|
||
{
|
||
await semaphore.WaitAsync();
|
||
var taskSet = _dBContext.GetDbSet<TaskData6180Dto>();
|
||
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<Rgv6180Dto>();
|
||
var processSet = _dBContext.GetDbSet<ProcessDto>();
|
||
var flowSet = _dBContext.GetDbSet<ProcessFlow6180Dto>();
|
||
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)最小选取
|
||
TaskData6180Dto? 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();
|
||
}
|
||
}
|
||
}
|