2026-03-02 09:13:29 +08:00
|
|
|
|
using Cowain.Base.DBContext;
|
|
|
|
|
|
using Cowain.Base.Models;
|
|
|
|
|
|
using Cowain.Base.Services;
|
|
|
|
|
|
using HslCommunication.Core;
|
|
|
|
|
|
using Microsoft.EntityFrameworkCore;
|
|
|
|
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
|
|
|
|
using Microsoft.Extensions.Hosting;
|
|
|
|
|
|
using Microsoft.Extensions.Logging;
|
|
|
|
|
|
using Newtonsoft.Json;
|
|
|
|
|
|
using Plugin.Cowain.Driver.Abstractions;
|
|
|
|
|
|
using Plugin.Cowain.Wcs.Actions;
|
|
|
|
|
|
using Plugin.Cowain.Wcs.IServices;
|
|
|
|
|
|
using Plugin.Cowain.Wcs.Models;
|
|
|
|
|
|
using Plugin.Cowain.Wcs.Models.Dto;
|
|
|
|
|
|
using Plugin.Cowain.Wcs.Models.Enum;
|
|
|
|
|
|
using Plugin.Cowain.Wcs.ViewModels;
|
|
|
|
|
|
using System.Text;
|
|
|
|
|
|
|
|
|
|
|
|
namespace Plugin.Cowain.Wcs.Services;
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-03-02 10:56:30 +08:00
|
|
|
|
public class RgvToWcsData
|
|
|
|
|
|
{
|
|
|
|
|
|
public short Command { get; set; }
|
|
|
|
|
|
public short Count { get; set; }
|
|
|
|
|
|
public short Result { get; set; }
|
|
|
|
|
|
public int TaskId { get; set; }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-02 09:13:29 +08:00
|
|
|
|
public class WcsToRgvData
|
|
|
|
|
|
{
|
|
|
|
|
|
public short Command { get; set; }
|
|
|
|
|
|
public short Count { get; set; }
|
|
|
|
|
|
public int TaskId { get; set; }
|
2026-03-02 10:56:30 +08:00
|
|
|
|
public short FromStation1 { get; set; }
|
|
|
|
|
|
public short FromStation2 { get; set; }
|
|
|
|
|
|
public short ToStation1 { get; set; }
|
|
|
|
|
|
public short ToStation2 { get; set; }
|
|
|
|
|
|
public string QrCode1 { get; set; } = string.Empty;
|
|
|
|
|
|
public string QrCode2 { get; set; } = string.Empty;
|
2026-03-02 09:13:29 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-02 10:56:30 +08:00
|
|
|
|
|
2026-03-02 09:13:29 +08:00
|
|
|
|
public class RgvTaskService : BaseService, IRgvTaskService
|
|
|
|
|
|
{
|
|
|
|
|
|
private IServiceScopeFactory _scopeFactory;
|
|
|
|
|
|
private IDeviceMonitor _deviceMonitor;
|
|
|
|
|
|
private readonly ILogger<RgvTaskService> _logger;
|
2026-03-02 10:56:30 +08:00
|
|
|
|
|
|
|
|
|
|
public RgvTaskService(IDbContextFactory<SqlDbContext> dbContextFactory, IDeviceMonitor deviceMonitor, IServiceScopeFactory scopeFactory, ILogger<RgvTaskService> logger) : base(dbContextFactory)
|
2026-03-02 09:13:29 +08:00
|
|
|
|
{
|
|
|
|
|
|
_scopeFactory = scopeFactory;
|
|
|
|
|
|
_deviceMonitor = deviceMonitor;
|
|
|
|
|
|
_logger = logger;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public List<RgvActionViewModel>? ActionToList(string action)
|
|
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
var actions = JsonConvert.DeserializeObject<List<string>>(action) ?? [];
|
|
|
|
|
|
var rgvActionViewModels = actions
|
|
|
|
|
|
.Select((name, i) => new RgvActionViewModel { Id = i + 1, Name = name })
|
|
|
|
|
|
.ToList();
|
|
|
|
|
|
rgvActionViewModels.Insert(0, new RgvActionViewModel { Id = 0, Name = RgvCommandEnum.UnKnown.ToString() });
|
|
|
|
|
|
return rgvActionViewModels;
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
_logger.LogError(ex, "反序列化失败");
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public RgvCommandEnum GetStepAction(List<RgvActionViewModel> actions, int step)
|
|
|
|
|
|
{
|
|
|
|
|
|
var name = actions.FirstOrDefault(x => x.Id == step)?.Name;
|
|
|
|
|
|
return Enum.TryParse(name, out RgvCommandEnum cmd) ? cmd : RgvCommandEnum.UnKnown;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public async Task<ResultModel> FinishTaskAsync(int id)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (id <= 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
return ResultModel.Error("id不能小于0");
|
|
|
|
|
|
}
|
2026-03-02 10:56:30 +08:00
|
|
|
|
using var dbContext = _dbContextFactory.CreateDbContext();
|
|
|
|
|
|
var DbSet = dbContext.GetDbSet<TaskDataDto>();
|
2026-03-02 09:13:29 +08:00
|
|
|
|
var existingModel = await DbSet.FirstOrDefaultAsync(x => x.Id == id);
|
|
|
|
|
|
if (existingModel == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
return ResultModel.Error("Taskid不存在");
|
|
|
|
|
|
}
|
|
|
|
|
|
existingModel.IsFinished = true;
|
2026-03-02 10:56:30 +08:00
|
|
|
|
int count = await dbContext.SaveChangesAsync();
|
2026-03-02 09:13:29 +08:00
|
|
|
|
return count > 0 ? ResultModel.Success("Task直接更新为结束") : ResultModel.Error("Task直接更新为结束失败");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public async Task<List<TaskViewModel>> GetAllAsync()
|
|
|
|
|
|
{
|
2026-03-02 10:56:30 +08:00
|
|
|
|
using var dbContext = _dbContextFactory.CreateDbContext();
|
|
|
|
|
|
var DbSet = dbContext.GetDbSet<TaskDataDto>();
|
2026-03-02 09:13:29 +08:00
|
|
|
|
var data = await DbSet.ToListAsync();
|
|
|
|
|
|
return new List<TaskViewModel>(data.Select(x => new TaskViewModel
|
|
|
|
|
|
{
|
|
|
|
|
|
Id = x.Id,
|
|
|
|
|
|
ProcessId = x.ProcessId,
|
|
|
|
|
|
ProcessName = x.ProcessName,
|
|
|
|
|
|
Priority = x.Priority,
|
2026-03-02 10:56:30 +08:00
|
|
|
|
FromStationId1 = x.FromStationId1,
|
|
|
|
|
|
ToStationId1 = x.ToStationId1,
|
|
|
|
|
|
FromStatus1 = x.FromStatus1,
|
|
|
|
|
|
ToStatus1 = x.ToStatus1,
|
|
|
|
|
|
FromStationId2 = x.FromStationId2,
|
|
|
|
|
|
ToStationId2 = x.ToStationId2,
|
|
|
|
|
|
FromStatus2 = x.FromStatus2,
|
|
|
|
|
|
ToStatus2 = x.ToStatus2,
|
2026-03-02 09:13:29 +08:00
|
|
|
|
CreatTime = x.CreateTime,
|
|
|
|
|
|
IsFinished = x.IsFinished,
|
|
|
|
|
|
ExecuteAction = x.ExecuteAction,
|
|
|
|
|
|
FinishedTime = x.FinishedTime,
|
2026-03-02 10:56:30 +08:00
|
|
|
|
QrCode1 = x.QrCode1,
|
|
|
|
|
|
QrCode2 = x.QrCode2,
|
2026-03-02 09:13:29 +08:00
|
|
|
|
Action = x.Action
|
|
|
|
|
|
}));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public async Task<(List<TaskViewModel>, int totals)> GetAllAsync(int pageIndex, int pageSize)
|
|
|
|
|
|
{
|
2026-03-02 10:56:30 +08:00
|
|
|
|
using var dbContext = _dbContextFactory.CreateDbContext();
|
|
|
|
|
|
var DbSet = dbContext.GetDbSet<TaskDataDto>();
|
2026-03-02 09:13:29 +08:00
|
|
|
|
// 按照CreateTime倒序排列,最新的任务排第一个
|
2026-03-02 10:56:30 +08:00
|
|
|
|
var data = await DbSet.OrderByDescending(x => x.Id).ToListAsync();
|
2026-03-02 09:13:29 +08:00
|
|
|
|
var list = data.Skip((pageIndex - 1) * pageSize).Take(pageSize);
|
|
|
|
|
|
var ret = new List<TaskViewModel>(list.Select(x => new TaskViewModel
|
|
|
|
|
|
{
|
|
|
|
|
|
Id = x.Id,
|
|
|
|
|
|
ProcessId = x.ProcessId,
|
|
|
|
|
|
ProcessName = x.ProcessName,
|
|
|
|
|
|
Priority = x.Priority,
|
2026-03-02 10:56:30 +08:00
|
|
|
|
FromStationId1 = x.FromStationId1,
|
|
|
|
|
|
ToStationId1 = x.ToStationId1,
|
|
|
|
|
|
FromStatus1 = x.FromStatus1,
|
|
|
|
|
|
ToStatus1 = x.ToStatus1,
|
|
|
|
|
|
FromStationId2 = x.FromStationId2,
|
|
|
|
|
|
ToStationId2 = x.ToStationId2,
|
|
|
|
|
|
FromStatus2 = x.FromStatus2,
|
|
|
|
|
|
ToStatus2 = x.ToStatus2,
|
2026-03-02 09:13:29 +08:00
|
|
|
|
CreatTime = x.CreateTime,
|
|
|
|
|
|
IsFinished = x.IsFinished,
|
|
|
|
|
|
ExecuteAction = x.ExecuteAction,
|
|
|
|
|
|
FinishedTime = x.FinishedTime,
|
2026-03-02 10:56:30 +08:00
|
|
|
|
QrCode1 = x.QrCode1,
|
|
|
|
|
|
QrCode2 = x.QrCode2,
|
|
|
|
|
|
Action = x.Action
|
2026-03-02 09:13:29 +08:00
|
|
|
|
//Actions = new(ActionToList(x.Action) ?? new List<RgvActionViewModel>())
|
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
|
|
return (ret, data.Count());
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public async Task<ResultModel<RgvCommandModel>> GetRgvCommandAsync(RgvUpdateInfoModel? infoModel)
|
|
|
|
|
|
{
|
|
|
|
|
|
RgvCommandModel? rgvCommand = null;
|
|
|
|
|
|
if (infoModel == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
return ResultModel<RgvCommandModel>.Error("RgvUpdateInfoModel不能为空");
|
|
|
|
|
|
}
|
|
|
|
|
|
using var scope = _scopeFactory.CreateScope();
|
|
|
|
|
|
var dBContext = scope.ServiceProvider.GetRequiredService<SqlDbContext>();
|
|
|
|
|
|
var taskSet = dBContext.GetDbSet<TaskDataDto>();
|
|
|
|
|
|
var unfinishedTask = await taskSet.AsNoTracking().FirstOrDefaultAsync(t => !t.IsFinished);
|
|
|
|
|
|
|
|
|
|
|
|
if (unfinishedTask == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
_logger.LogInformation($"没有找到在执行的任务,不需要处理");
|
|
|
|
|
|
return ResultModel<RgvCommandModel>.Error("没有找到在执行的任务,不需要处理");
|
|
|
|
|
|
}
|
2026-03-02 10:56:30 +08:00
|
|
|
|
var actionResult = ActionToList(unfinishedTask.Action); //转换动作列表
|
|
|
|
|
|
if (actionResult == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
_logger.LogError($"工艺未配置Action,请修改,taskId={unfinishedTask.Id}");
|
|
|
|
|
|
return ResultModel<RgvCommandModel>.Error($"工艺未配置Action,请修改,taskId={unfinishedTask.Id}");
|
|
|
|
|
|
}
|
|
|
|
|
|
var actions = actionResult;
|
2026-03-02 09:13:29 +08:00
|
|
|
|
int step = 0;
|
|
|
|
|
|
if (infoModel.Source == RgvUpdateSourceEnum.PLC)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (infoModel.RetResult == 2)
|
|
|
|
|
|
{
|
2026-03-02 10:56:30 +08:00
|
|
|
|
_logger.LogError($"PLC反馈结果是NG,任务id={unfinishedTask.Id}");
|
2026-03-02 09:13:29 +08:00
|
|
|
|
return ResultModel<RgvCommandModel>.Error($"PLC反馈结果是NG,任务id={unfinishedTask.Id}");
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (infoModel.TaskId != unfinishedTask.Id)
|
|
|
|
|
|
{
|
2026-03-02 10:56:30 +08:00
|
|
|
|
_logger.LogError($"PLC反馈结果任务号不匹配,plc任务号={infoModel.TaskId},执行任务id={unfinishedTask.Id}");
|
2026-03-02 09:13:29 +08:00
|
|
|
|
return ResultModel<RgvCommandModel>.Error($"PLC反馈结果任务号不匹配,plc任务号={infoModel.TaskId},执行任务id={unfinishedTask.Id}");
|
|
|
|
|
|
}
|
|
|
|
|
|
step = unfinishedTask.ExecuteAction;
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (infoModel.Source == RgvUpdateSourceEnum.First)
|
|
|
|
|
|
{
|
|
|
|
|
|
step = 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (infoModel.Source == RgvUpdateSourceEnum.User)
|
|
|
|
|
|
{
|
|
|
|
|
|
//手动下发的任务
|
|
|
|
|
|
if (infoModel.ExecuteAction > 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
step = infoModel.ExecuteAction - 1;
|
2026-03-02 10:56:30 +08:00
|
|
|
|
_logger.LogInformation($"手动下发任务,执行任务id={unfinishedTask.Id}:{GetStepAction(actions, step).ToString()}");
|
2026-03-02 09:13:29 +08:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2026-03-02 10:56:30 +08:00
|
|
|
|
_logger.LogError($"手动选择的动作不能为0");
|
2026-03-02 09:13:29 +08:00
|
|
|
|
return ResultModel<RgvCommandModel>.Error($"手动选择的动作不能为0");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
var stationService = scope.ServiceProvider.GetRequiredService<IStationService>();
|
|
|
|
|
|
var stationTaskHandler = scope.ServiceProvider.GetRequiredService<IEnumerable<IHostedService>>();
|
|
|
|
|
|
var stationQueueHostedService = stationTaskHandler.OfType<StationQueueHostedService>().FirstOrDefault();
|
|
|
|
|
|
|
|
|
|
|
|
var stations = await stationService.GetAllAsync();
|
2026-03-02 10:56:30 +08:00
|
|
|
|
var fromStation1 = stations.FirstOrDefault(x => x.Id == unfinishedTask.FromStationId1);
|
|
|
|
|
|
if (fromStation1 == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
return ResultModel<RgvCommandModel>.Error($"From1工站不存在,工站Id={unfinishedTask.FromStationId1}");
|
|
|
|
|
|
}
|
|
|
|
|
|
var toStation1 = stations.FirstOrDefault(x => x.Id == unfinishedTask.ToStationId1);
|
|
|
|
|
|
if (toStation1 == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
return ResultModel<RgvCommandModel>.Error($"To1工站不存在,工站Id={unfinishedTask.ToStationId1}");
|
|
|
|
|
|
}
|
|
|
|
|
|
var fromStation2 = stations.FirstOrDefault(x => x.Id == unfinishedTask.FromStationId2);
|
|
|
|
|
|
if (unfinishedTask.FromStationId2 > 0 && fromStation2 == null)
|
2026-03-02 09:13:29 +08:00
|
|
|
|
{
|
2026-03-02 10:56:30 +08:00
|
|
|
|
return ResultModel<RgvCommandModel>.Error($"From2工站不存在,工站Id={unfinishedTask.FromStationId2}");
|
2026-03-02 09:13:29 +08:00
|
|
|
|
}
|
2026-03-02 10:56:30 +08:00
|
|
|
|
var toStation2 = stations.FirstOrDefault(x => x.Id == unfinishedTask.ToStationId2);
|
|
|
|
|
|
if (unfinishedTask.ToStationId2 > 0 && toStation2 == null)
|
2026-03-02 09:13:29 +08:00
|
|
|
|
{
|
2026-03-02 10:56:30 +08:00
|
|
|
|
return ResultModel<RgvCommandModel>.Error($"To2站不存在,工站Id={unfinishedTask.ToStationId2}");
|
2026-03-02 09:13:29 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//数据库存储的是上一步
|
|
|
|
|
|
var stepCommand = GetStepAction(actions, step); //获取上一步动作
|
|
|
|
|
|
var nextCommand = GetStepAction(actions, step + 1); //获取下一步动作
|
|
|
|
|
|
|
|
|
|
|
|
if (infoModel.Source == RgvUpdateSourceEnum.PLC)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (infoModel.RetCmd != (int)stepCommand)
|
|
|
|
|
|
{
|
2026-03-02 10:56:30 +08:00
|
|
|
|
_logger.LogError($"PLC反馈动作不正确,PLC动作{infoModel.RetCmd},实际动作{(int)stepCommand}");
|
2026-03-02 09:13:29 +08:00
|
|
|
|
return ResultModel<RgvCommandModel>.Error($"PLC反馈动作不正确,PLC动作{infoModel.RetCmd},实际动作{(int)stepCommand}");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (stepCommand == RgvCommandEnum.UnKnown && nextCommand == RgvCommandEnum.UnKnown)
|
|
|
|
|
|
{
|
2026-03-02 10:56:30 +08:00
|
|
|
|
_logger.LogError($"获取RGV命令失败,配置有误");
|
2026-03-02 09:13:29 +08:00
|
|
|
|
return ResultModel<RgvCommandModel>.Error("获取RGV命令失败,配置有误");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-02 10:56:30 +08:00
|
|
|
|
rgvCommand = new RgvCommandModel
|
2026-03-02 09:13:29 +08:00
|
|
|
|
{
|
2026-03-02 10:56:30 +08:00
|
|
|
|
Command = nextCommand,
|
|
|
|
|
|
TaskId = unfinishedTask.Id,
|
|
|
|
|
|
FromPos1 = fromStation1.StationPos,
|
|
|
|
|
|
FromPos2 = fromStation2?.StationPos ?? 0,
|
|
|
|
|
|
TargetPos1 = toStation1.StationPos,
|
|
|
|
|
|
TargetPos2 = toStation2?.StationPos ?? 0,
|
|
|
|
|
|
QrCode1 = unfinishedTask.QrCode1,
|
|
|
|
|
|
QrCode2 = unfinishedTask.QrCode2
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2026-03-02 09:13:29 +08:00
|
|
|
|
var processService = scope.ServiceProvider.GetRequiredService<IProcessService>();
|
|
|
|
|
|
var getJson = await processService.GetJsonData(unfinishedTask.ProcessId);
|
|
|
|
|
|
string? rgvName = string.Empty;
|
|
|
|
|
|
if (getJson != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
rgvName = getJson.Data!.RgvName;
|
|
|
|
|
|
}
|
|
|
|
|
|
//更新RGV
|
2026-03-02 10:56:30 +08:00
|
|
|
|
|
|
|
|
|
|
if (stepCommand == RgvCommandEnum.MoveFrom)
|
2026-03-02 09:13:29 +08:00
|
|
|
|
{
|
2026-03-02 10:56:30 +08:00
|
|
|
|
//更新rgv的目标位置
|
|
|
|
|
|
var rgvService = scope.ServiceProvider.GetRequiredService<IRgvService>();
|
|
|
|
|
|
var rgvs = await rgvService.GetAllAsync();
|
|
|
|
|
|
if (rgvs != null)
|
2026-03-02 09:13:29 +08:00
|
|
|
|
{
|
2026-03-02 10:56:30 +08:00
|
|
|
|
var rgv = rgvs.FirstOrDefault(x => x.StationName == rgvName);
|
|
|
|
|
|
if (rgv != null)
|
2026-03-02 09:13:29 +08:00
|
|
|
|
{
|
2026-03-02 10:56:30 +08:00
|
|
|
|
rgv.LayOutX = fromStation1.LayOutX;
|
|
|
|
|
|
await rgvService.UpdateAsync(rgv);
|
2026-03-02 09:13:29 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-03-02 10:56:30 +08:00
|
|
|
|
}
|
|
|
|
|
|
else if (stepCommand == RgvCommandEnum.MoveTo)
|
|
|
|
|
|
{
|
|
|
|
|
|
//更新rgv的目标位置
|
|
|
|
|
|
var rgvService = scope.ServiceProvider.GetRequiredService<IRgvService>();
|
|
|
|
|
|
var rgvs = await rgvService.GetAllAsync();
|
|
|
|
|
|
if (rgvs != null)
|
2026-03-02 09:13:29 +08:00
|
|
|
|
{
|
2026-03-02 10:56:30 +08:00
|
|
|
|
var rgv = rgvs.FirstOrDefault(x => x.StationName == rgvName);
|
|
|
|
|
|
if (rgv != null)
|
2026-03-02 09:13:29 +08:00
|
|
|
|
{
|
2026-03-02 10:56:30 +08:00
|
|
|
|
rgv.LayOutX = toStation1.LayOutX;
|
|
|
|
|
|
await rgvService.UpdateAsync(rgv);
|
2026-03-02 09:13:29 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//更新工站状态
|
2026-03-02 10:56:30 +08:00
|
|
|
|
if (stepCommand == RgvCommandEnum.UnKnown && nextCommand != RgvCommandEnum.UnKnown)
|
|
|
|
|
|
{
|
|
|
|
|
|
//【1】上一步未知,任务刚开始
|
|
|
|
|
|
fromStation1.UpdateSource = StationUpdateSourceEnum.Task;
|
|
|
|
|
|
fromStation1.Status = StationStateEnum.Picking.ToString();
|
|
|
|
|
|
toStation1.UpdateSource = StationUpdateSourceEnum.Task;
|
|
|
|
|
|
toStation1.Status = StationStateEnum.Placing.ToString();
|
|
|
|
|
|
stationQueueHostedService?.Enqueue(fromStation1);
|
|
|
|
|
|
stationQueueHostedService?.Enqueue(toStation1);
|
|
|
|
|
|
if (fromStation2 != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
fromStation2.UpdateSource = StationUpdateSourceEnum.Task;
|
|
|
|
|
|
fromStation2.Status = StationStateEnum.Picking.ToString();
|
|
|
|
|
|
stationQueueHostedService?.Enqueue(fromStation2);
|
|
|
|
|
|
}
|
|
|
|
|
|
if (toStation2 != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
toStation2.UpdateSource = StationUpdateSourceEnum.Task;
|
|
|
|
|
|
toStation2.Status = StationStateEnum.Placing.ToString();
|
|
|
|
|
|
stationQueueHostedService?.Enqueue(toStation2);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-02 09:13:29 +08:00
|
|
|
|
//任务刚开始,更新Rgv
|
|
|
|
|
|
var rgvService = scope.ServiceProvider.GetRequiredService<IRgvService>();
|
|
|
|
|
|
var rgvs = await rgvService.GetAllAsync();
|
|
|
|
|
|
if (rgvs != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
var rgv = rgvs.FirstOrDefault(x => x.StationName == rgvName);
|
|
|
|
|
|
if (rgv != null)
|
|
|
|
|
|
{
|
2026-03-02 10:56:30 +08:00
|
|
|
|
rgv.FromStationId1 = fromStation1.Id;
|
|
|
|
|
|
rgv.ToStationId1 = toStation1.Id;
|
|
|
|
|
|
rgv.FromStationId2 = fromStation2?.Id ?? 0;
|
|
|
|
|
|
rgv.ToStationId2 = toStation2?.Id ?? 0;
|
2026-03-02 09:13:29 +08:00
|
|
|
|
rgv.ProcessName = unfinishedTask.ProcessName;
|
|
|
|
|
|
await rgvService.UpdateAsync(rgv);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-03-02 10:56:30 +08:00
|
|
|
|
else if (nextCommand == RgvCommandEnum.Place)
|
2026-03-02 09:13:29 +08:00
|
|
|
|
{
|
|
|
|
|
|
//【2】下一步要放料,将源QR复制到目标QR
|
2026-03-02 10:56:30 +08:00
|
|
|
|
toStation1.UpdateSource = StationUpdateSourceEnum.Task;
|
|
|
|
|
|
toStation1.QrCode = fromStation1.QrCode;
|
|
|
|
|
|
stationQueueHostedService?.Enqueue(toStation1);
|
|
|
|
|
|
if (fromStation2 != null && toStation2 != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
toStation2.UpdateSource = StationUpdateSourceEnum.Task;
|
|
|
|
|
|
toStation2.QrCode = fromStation2.QrCode;
|
|
|
|
|
|
stationQueueHostedService?.Enqueue(toStation2);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (stepCommand == RgvCommandEnum.Pick && nextCommand != RgvCommandEnum.Pick)
|
|
|
|
|
|
{
|
|
|
|
|
|
//【3】取料完成
|
|
|
|
|
|
fromStation1.UpdateSource = StationUpdateSourceEnum.Task;
|
|
|
|
|
|
fromStation1.Status = StationStateEnum.PickFinished.ToString();
|
|
|
|
|
|
stationQueueHostedService?.Enqueue(fromStation1);
|
2026-03-02 09:13:29 +08:00
|
|
|
|
|
2026-03-02 10:56:30 +08:00
|
|
|
|
if (fromStation2 != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
fromStation2.UpdateSource = StationUpdateSourceEnum.Task;
|
|
|
|
|
|
fromStation2.Status = StationStateEnum.PickFinished.ToString();
|
|
|
|
|
|
stationQueueHostedService?.Enqueue(fromStation2);
|
|
|
|
|
|
}
|
2026-03-02 09:13:29 +08:00
|
|
|
|
}
|
2026-03-02 10:56:30 +08:00
|
|
|
|
else if (stepCommand == RgvCommandEnum.Place && nextCommand != RgvCommandEnum.Place)
|
2026-03-02 09:13:29 +08:00
|
|
|
|
{
|
2026-03-02 10:56:30 +08:00
|
|
|
|
//【4】放料完成,任务结束
|
|
|
|
|
|
toStation1.UpdateSource = StationUpdateSourceEnum.Task;
|
|
|
|
|
|
toStation1.Status = StationStateEnum.PlaceFinished.ToString();
|
|
|
|
|
|
stationQueueHostedService?.Enqueue(toStation1);
|
|
|
|
|
|
if (toStation2 != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
toStation2.UpdateSource = StationUpdateSourceEnum.Task;
|
|
|
|
|
|
toStation2.Status = StationStateEnum.PlaceFinished.ToString();
|
|
|
|
|
|
stationQueueHostedService?.Enqueue(toStation2);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-02 09:13:29 +08:00
|
|
|
|
var rgvService = scope.ServiceProvider.GetRequiredService<IRgvService>();
|
|
|
|
|
|
var rgvs = await rgvService.GetAllAsync();
|
|
|
|
|
|
if (rgvs != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
var rgv = rgvs.FirstOrDefault(x => x.StationName == rgvName);
|
|
|
|
|
|
if (rgv != null)
|
|
|
|
|
|
{
|
2026-03-02 10:56:30 +08:00
|
|
|
|
rgv.FromStationId1 = 0;
|
|
|
|
|
|
rgv.ToStationId1 = 0;
|
|
|
|
|
|
rgv.FromStationId2 = 0;
|
|
|
|
|
|
rgv.ToStationId2 = 0;
|
2026-03-02 09:13:29 +08:00
|
|
|
|
rgv.ProcessName = "";
|
|
|
|
|
|
await rgvService.UpdateAsync(rgv);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
int save = 0;
|
2026-03-02 10:56:30 +08:00
|
|
|
|
if (stepCommand == RgvCommandEnum.Place && nextCommand == RgvCommandEnum.UnKnown)
|
2026-03-02 09:13:29 +08:00
|
|
|
|
{
|
2026-03-02 10:56:30 +08:00
|
|
|
|
//任务结束
|
2026-03-02 09:13:29 +08:00
|
|
|
|
unfinishedTask.IsFinished = true;
|
|
|
|
|
|
unfinishedTask.FinishedTime = DateTime.Now;
|
|
|
|
|
|
// 手动将实体状态标记为Modified
|
|
|
|
|
|
dBContext.Entry(unfinishedTask).State = EntityState.Modified;
|
|
|
|
|
|
save = await dBContext.SaveChangesAsync();
|
|
|
|
|
|
if (save > 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
_logger.LogInformation($"任务完成,任务id={unfinishedTask.Id}");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
rgvCommand.TaskId = unfinishedTask.Id;
|
|
|
|
|
|
unfinishedTask.ExecuteAction = step + 1;
|
|
|
|
|
|
// 手动将实体状态标记为Modified
|
|
|
|
|
|
dBContext.Entry(unfinishedTask).State = EntityState.Modified;
|
|
|
|
|
|
save = await dBContext.SaveChangesAsync();
|
|
|
|
|
|
if (save > 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
_logger.LogInformation($"任务继续,任务id={unfinishedTask.Id},动作={JsonConvert.SerializeObject(rgvCommand)}");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
if (save > 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
return ResultModel<RgvCommandModel>.Success(rgvCommand);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
return ResultModel<RgvCommandModel>.Error($"任务更新异常,任务id={unfinishedTask.Id}");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
_logger.LogError(ex, $"任务异常,任务id={unfinishedTask.Id}");
|
|
|
|
|
|
return ResultModel<RgvCommandModel>.Error($"任务异常,任务id={unfinishedTask.Id}");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public async Task<ResultModel> ExecuteAsync(string? actionParam, RgvUpdateSourceEnum source, int? execute = 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (string.IsNullOrEmpty(actionParam))
|
|
|
|
|
|
{
|
|
|
|
|
|
return ResultModel.Error("参数不能为空");
|
|
|
|
|
|
}
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
var _param = JsonConvert.DeserializeObject<RgvFinishedParamData>(actionParam);
|
|
|
|
|
|
if (_param == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
return ResultModel.Error("参数解析失败");
|
|
|
|
|
|
}
|
|
|
|
|
|
var driver = _deviceMonitor.GetDriver(_param.PlcName);
|
|
|
|
|
|
if (!driver.IsSuccess)
|
|
|
|
|
|
{
|
|
|
|
|
|
return ResultModel.Error($"未找到名称为 {_param.PlcName} 的Driver");
|
|
|
|
|
|
}
|
|
|
|
|
|
var plc = driver.Data.GetReadWrite();
|
|
|
|
|
|
if (plc == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
return ResultModel.Error($"GetReadWrite为空,PLC={_param.PlcName}");
|
|
|
|
|
|
}
|
|
|
|
|
|
//读数据
|
|
|
|
|
|
var read = await ReadAsync(plc, _param.RetCmdAddress);
|
|
|
|
|
|
|
|
|
|
|
|
RgvUpdateInfoModel updateInfo = new RgvUpdateInfoModel
|
|
|
|
|
|
{
|
|
|
|
|
|
Source = source,
|
2026-03-02 10:56:30 +08:00
|
|
|
|
TaskId = read.IsSuccess ? read.Data.TaskId : 0,
|
|
|
|
|
|
RetResult = read.IsSuccess ? read.Data.Result : 0,
|
|
|
|
|
|
RetCmd = read.IsSuccess ? read.Data.Command : 0,
|
2026-03-02 09:13:29 +08:00
|
|
|
|
ExecuteAction = execute == null ? 0 : execute.Value,
|
|
|
|
|
|
};
|
|
|
|
|
|
var rgvCommand = await GetRgvCommandAsync(updateInfo);
|
2026-03-02 10:56:30 +08:00
|
|
|
|
if (!read.IsSuccess)
|
|
|
|
|
|
{
|
|
|
|
|
|
_logger.LogError($"从PLC读取调度数据失败:{_param.PlcName}->{_param.RetCmdAddress},{read.ErrorMessage}");
|
|
|
|
|
|
return ResultModel.Error($"读命令失败:{_param.PlcName}->{_param.RetCmdAddress},{read.ErrorMessage}");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-02 09:13:29 +08:00
|
|
|
|
var readToRgvData = await ReadToRgvDataAsync(plc, _param.CmdAddress);
|
|
|
|
|
|
if (!readToRgvData.IsSuccess)
|
|
|
|
|
|
{
|
|
|
|
|
|
_logger.LogError($"从PLC读取命令失败:{_param.PlcName}->{_param.CmdAddress},{read.ErrorMessage}");
|
|
|
|
|
|
return ResultModel.Error($"从PLC读取命令失败:{_param.PlcName}->{_param.CmdAddress},{read.ErrorMessage}");
|
|
|
|
|
|
}
|
|
|
|
|
|
short count = readToRgvData.Data.Count; //计数
|
|
|
|
|
|
if (source == RgvUpdateSourceEnum.First && count != 1)
|
|
|
|
|
|
{
|
|
|
|
|
|
//新任务,且计数不为1,重置计数,这个情况出现在任务生成后被手动结束了
|
|
|
|
|
|
count = 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
count = count + 1 <= 100 ? (short)(count + 1) : (short)1; //最大100
|
|
|
|
|
|
if (rgvCommand.IsSuccess)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (rgvCommand.Data!.Command != RgvCommandEnum.UnKnown)
|
|
|
|
|
|
{
|
|
|
|
|
|
WcsToRgvData wcsToRgv = new WcsToRgvData
|
|
|
|
|
|
{
|
|
|
|
|
|
Command = (short)rgvCommand.Data!.Command,
|
|
|
|
|
|
Count = count,
|
2026-03-02 10:56:30 +08:00
|
|
|
|
FromStation1 = (short)rgvCommand.Data!.FromPos1,
|
|
|
|
|
|
FromStation2 = (short)rgvCommand.Data!.FromPos2,
|
|
|
|
|
|
ToStation1 = (short)rgvCommand.Data!.TargetPos1,
|
|
|
|
|
|
ToStation2 = (short)rgvCommand.Data!.TargetPos2,
|
2026-03-02 09:13:29 +08:00
|
|
|
|
TaskId = rgvCommand.Data!.TaskId,
|
2026-03-02 10:56:30 +08:00
|
|
|
|
QrCode1 = rgvCommand.Data!.QrCode1,
|
|
|
|
|
|
QrCode2 = rgvCommand.Data!.QrCode2
|
2026-03-02 09:13:29 +08:00
|
|
|
|
};
|
|
|
|
|
|
var write = await WriteAsync(plc, _param.CmdAddress, wcsToRgv);
|
|
|
|
|
|
if (!write.IsSuccess) return ResultModel.Error($"写变量失败:{_param.PlcName}->{_param.CmdAddress}");
|
|
|
|
|
|
_logger.LogInformation($"调度任务下发成功:TaskId={wcsToRgv.TaskId}->{JsonConvert.SerializeObject(wcsToRgv)}");
|
|
|
|
|
|
return ResultModel.Success();
|
2026-03-02 10:56:30 +08:00
|
|
|
|
|
2026-03-02 09:13:29 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
return ResultModel.Error($"没有需要执行的动作:TaskId={updateInfo.TaskId}");
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
return ResultModel.Error($"参数解析失败:{ex.Message}");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[LogAndSwallow]
|
|
|
|
|
|
private async Task<ResultModel<RgvToWcsData>> ReadAsync(IReadWriteDevice driver, string address)
|
|
|
|
|
|
{
|
|
|
|
|
|
var readResult = await driver.ReadAsync(address, 10);
|
|
|
|
|
|
if (!readResult.IsSuccess)
|
|
|
|
|
|
{
|
2026-03-02 10:56:30 +08:00
|
|
|
|
return ResultModel<RgvToWcsData>.Error(readResult.Message);
|
2026-03-02 09:13:29 +08:00
|
|
|
|
}
|
|
|
|
|
|
RgvToWcsData requestData = new RgvToWcsData
|
|
|
|
|
|
{
|
|
|
|
|
|
Command = driver.ByteTransform.TransInt16(readResult.Content, 0),
|
|
|
|
|
|
Count = driver.ByteTransform.TransInt16(readResult.Content, 2),
|
|
|
|
|
|
Result = driver.ByteTransform.TransInt16(readResult.Content, 4),
|
|
|
|
|
|
TaskId = driver.ByteTransform.TransInt32(readResult.Content, 6)
|
|
|
|
|
|
};
|
|
|
|
|
|
return ResultModel<RgvToWcsData>.Success(requestData);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
[LogAndSwallow]
|
|
|
|
|
|
private async Task<ResultModel<WcsToRgvData>> ReadToRgvDataAsync(IReadWriteDevice driver, string address)
|
|
|
|
|
|
{
|
2026-03-02 10:56:30 +08:00
|
|
|
|
var readResult = await driver.ReadAsync(address, 60);
|
2026-03-02 09:13:29 +08:00
|
|
|
|
if (!readResult.IsSuccess)
|
|
|
|
|
|
{
|
2026-03-02 10:56:30 +08:00
|
|
|
|
return ResultModel<WcsToRgvData>.Error(readResult.Message);
|
2026-03-02 09:13:29 +08:00
|
|
|
|
}
|
|
|
|
|
|
WcsToRgvData requestData = new WcsToRgvData
|
|
|
|
|
|
{
|
|
|
|
|
|
Command = driver.ByteTransform.TransInt16(readResult.Content, 0),
|
|
|
|
|
|
Count = driver.ByteTransform.TransInt16(readResult.Content, 14),
|
|
|
|
|
|
TaskId = driver.ByteTransform.TransInt32(readResult.Content, 6)
|
|
|
|
|
|
};
|
|
|
|
|
|
return ResultModel<WcsToRgvData>.Success(requestData);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
[LogAndSwallow]
|
|
|
|
|
|
private async Task<ResultModel> WriteAsync(IReadWriteDevice driver, string address, WcsToRgvData toRgvData, int retryCount = 5)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 准备数据
|
2026-03-02 10:56:30 +08:00
|
|
|
|
byte[] data = new byte[60];
|
2026-03-02 09:13:29 +08:00
|
|
|
|
driver.ByteTransform.TransByte(toRgvData.Command).CopyTo(data, 0);
|
|
|
|
|
|
driver.ByteTransform.TransByte(toRgvData.TaskId).CopyTo(data, 2);
|
2026-03-02 10:56:30 +08:00
|
|
|
|
driver.ByteTransform.TransByte(toRgvData.FromStation1).CopyTo(data, 6);
|
|
|
|
|
|
driver.ByteTransform.TransByte(toRgvData.FromStation2).CopyTo(data, 8);
|
|
|
|
|
|
driver.ByteTransform.TransByte(toRgvData.ToStation1).CopyTo(data, 10);
|
|
|
|
|
|
driver.ByteTransform.TransByte(toRgvData.ToStation2).CopyTo(data, 12);
|
2026-03-02 09:13:29 +08:00
|
|
|
|
driver.ByteTransform.TransByte(toRgvData.Count).CopyTo(data, 14);
|
|
|
|
|
|
data[16] = 20;//字符串最大长度
|
2026-03-02 10:56:30 +08:00
|
|
|
|
data[17] = (byte)toRgvData.QrCode1.Length;//字符串实际长度
|
|
|
|
|
|
Encoding.ASCII.GetBytes(toRgvData.QrCode1).CopyTo(data, 18);
|
|
|
|
|
|
data[38] = 20;//字符串最大长度
|
|
|
|
|
|
data[39] = (byte)toRgvData.QrCode2.Length;//字符串实际长度
|
|
|
|
|
|
Encoding.ASCII.GetBytes(toRgvData.QrCode2).CopyTo(data, 40);
|
2026-03-02 09:13:29 +08:00
|
|
|
|
|
|
|
|
|
|
int baseDelay = 100; // 基础延迟100ms
|
|
|
|
|
|
var random = new Random();
|
|
|
|
|
|
for (int retry = 0; retry < retryCount; retry++)
|
|
|
|
|
|
{
|
|
|
|
|
|
var write = await driver.WriteAsync(address, data);
|
|
|
|
|
|
if (write.IsSuccess)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (retry > 1)
|
|
|
|
|
|
{
|
|
|
|
|
|
//第一次就成功,不需要记录日志
|
|
|
|
|
|
_logger.LogInformation($"写PLC数据调度任务成功:{address}->{JsonConvert.SerializeObject(toRgvData)},第{retry + 1}次尝试");
|
|
|
|
|
|
}
|
|
|
|
|
|
return ResultModel.Success();
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
_logger.LogError($"写PLC数据调度任务失败:{address}->{JsonConvert.SerializeObject(toRgvData)},第{retry + 1}次尝试");
|
|
|
|
|
|
}
|
|
|
|
|
|
// 如果不是最后一次尝试,则等待后重试
|
|
|
|
|
|
if (retry < retryCount - 1)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 计算指数退避+抖动
|
|
|
|
|
|
int jitter = random.Next(0, 100); // 0~100ms
|
|
|
|
|
|
int delay = baseDelay * (int)Math.Pow(2, retry) + jitter;
|
|
|
|
|
|
await Task.Delay(delay);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
_logger.LogError($"写PLC数据调度任务失败:{address}->{JsonConvert.SerializeObject(toRgvData)},总共{retryCount}次尝试");
|
|
|
|
|
|
return ResultModel.Error("写PLC数据失败");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|