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

637 lines
27 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.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;
public class RgvToWcsData
{
public short Command { get; set; }
public short Count { get; set; }
public short Result { get; set; }
public int TaskId { get; set; }
}
public class WcsToRgvData
{
public short Command { get; set; }
public short Count { get; set; }
public int TaskId { get; set; }
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;
}
public class RgvTaskService : BaseService, IRgvTaskService
{
private IServiceScopeFactory _scopeFactory;
private IDeviceMonitor _deviceMonitor;
private readonly ILogger<RgvTaskService> _logger;
public RgvTaskService(IDbContextFactory<SqlDbContext> dbContextFactory, IDeviceMonitor deviceMonitor, IServiceScopeFactory scopeFactory, ILogger<RgvTaskService> logger) : base(dbContextFactory)
{
_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");
}
using var dbContext = _dbContextFactory.CreateDbContext();
var DbSet = dbContext.GetDbSet<TaskDataDto>();
var existingModel = await DbSet.FirstOrDefaultAsync(x => x.Id == id);
if (existingModel == null)
{
return ResultModel.Error("Taskid不存在");
}
existingModel.IsFinished = true;
int count = await dbContext.SaveChangesAsync();
return count > 0 ? ResultModel.Success("Task直接更新为结束") : ResultModel.Error("Task直接更新为结束失败");
}
public async Task<List<TaskViewModel>> GetAllAsync()
{
using var dbContext = _dbContextFactory.CreateDbContext();
var DbSet = dbContext.GetDbSet<TaskDataDto>();
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,
FromStationId1 = x.FromStationId1,
ToStationId1 = x.ToStationId1,
FromStatus1 = x.FromStatus1,
ToStatus1 = x.ToStatus1,
FromStationId2 = x.FromStationId2,
ToStationId2 = x.ToStationId2,
FromStatus2 = x.FromStatus2,
ToStatus2 = x.ToStatus2,
CreatTime = x.CreateTime,
IsFinished = x.IsFinished,
ExecuteAction = x.ExecuteAction,
FinishedTime = x.FinishedTime,
QrCode1 = x.QrCode1,
QrCode2 = x.QrCode2,
Action = x.Action
}));
}
public async Task<(List<TaskViewModel>, int totals)> GetAllAsync(int pageIndex, int pageSize)
{
using var dbContext = _dbContextFactory.CreateDbContext();
var DbSet = dbContext.GetDbSet<TaskDataDto>();
// 按照CreateTime倒序排列最新的任务排第一个
var data = await DbSet.OrderByDescending(x => x.Id).ToListAsync();
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,
FromStationId1 = x.FromStationId1,
ToStationId1 = x.ToStationId1,
FromStatus1 = x.FromStatus1,
ToStatus1 = x.ToStatus1,
FromStationId2 = x.FromStationId2,
ToStationId2 = x.ToStationId2,
FromStatus2 = x.FromStatus2,
ToStatus2 = x.ToStatus2,
CreatTime = x.CreateTime,
IsFinished = x.IsFinished,
ExecuteAction = x.ExecuteAction,
FinishedTime = x.FinishedTime,
QrCode1 = x.QrCode1,
QrCode2 = x.QrCode2,
Action = x.Action
//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("没有找到在执行的任务,不需要处理");
}
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;
int step = 0;
if (infoModel.Source == RgvUpdateSourceEnum.PLC)
{
if (infoModel.RetResult == 2)
{
_logger.LogError($"PLC反馈结果是NG,任务id={unfinishedTask.Id}");
return ResultModel<RgvCommandModel>.Error($"PLC反馈结果是NG,任务id={unfinishedTask.Id}");
}
else if (infoModel.TaskId != unfinishedTask.Id)
{
_logger.LogError($"PLC反馈结果任务号不匹配plc任务号={infoModel.TaskId},执行任务id={unfinishedTask.Id}");
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;
_logger.LogInformation($"手动下发任务,执行任务id={unfinishedTask.Id}:{GetStepAction(actions, step).ToString()}");
}
else
{
_logger.LogError($"手动选择的动作不能为0");
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();
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)
{
return ResultModel<RgvCommandModel>.Error($"From2工站不存在工站Id={unfinishedTask.FromStationId2}");
}
var toStation2 = stations.FirstOrDefault(x => x.Id == unfinishedTask.ToStationId2);
if (unfinishedTask.ToStationId2 > 0 && toStation2 == null)
{
return ResultModel<RgvCommandModel>.Error($"To2站不存在工站Id={unfinishedTask.ToStationId2}");
}
//数据库存储的是上一步
var stepCommand = GetStepAction(actions, step); //获取上一步动作
var nextCommand = GetStepAction(actions, step + 1); //获取下一步动作
if (infoModel.Source == RgvUpdateSourceEnum.PLC)
{
if (infoModel.RetCmd != (int)stepCommand)
{
_logger.LogError($"PLC反馈动作不正确PLC动作{infoModel.RetCmd},实际动作{(int)stepCommand}");
return ResultModel<RgvCommandModel>.Error($"PLC反馈动作不正确PLC动作{infoModel.RetCmd},实际动作{(int)stepCommand}");
}
}
if (stepCommand == RgvCommandEnum.UnKnown && nextCommand == RgvCommandEnum.UnKnown)
{
_logger.LogError($"获取RGV命令失败配置有误");
return ResultModel<RgvCommandModel>.Error("获取RGV命令失败配置有误");
}
rgvCommand = new RgvCommandModel
{
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
};
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
if (stepCommand == RgvCommandEnum.MoveFrom)
{
//更新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)
{
rgv.LayOutX = fromStation1.LayOutX;
await rgvService.UpdateAsync(rgv);
}
}
}
else if (stepCommand == RgvCommandEnum.MoveTo)
{
//更新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)
{
rgv.LayOutX = toStation1.LayOutX;
await rgvService.UpdateAsync(rgv);
}
}
}
//更新工站状态
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);
}
//任务刚开始更新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)
{
rgv.FromStationId1 = fromStation1.Id;
rgv.ToStationId1 = toStation1.Id;
rgv.FromStationId2 = fromStation2?.Id ?? 0;
rgv.ToStationId2 = toStation2?.Id ?? 0;
rgv.ProcessName = unfinishedTask.ProcessName;
await rgvService.UpdateAsync(rgv);
}
}
}
else if (nextCommand == RgvCommandEnum.Place)
{
//【2】下一步要放料将源QR复制到目标QR
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);
if (fromStation2 != null)
{
fromStation2.UpdateSource = StationUpdateSourceEnum.Task;
fromStation2.Status = StationStateEnum.PickFinished.ToString();
stationQueueHostedService?.Enqueue(fromStation2);
}
}
else if (stepCommand == RgvCommandEnum.Place && nextCommand != RgvCommandEnum.Place)
{
//【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);
}
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)
{
rgv.FromStationId1 = 0;
rgv.ToStationId1 = 0;
rgv.FromStationId2 = 0;
rgv.ToStationId2 = 0;
rgv.ProcessName = "";
await rgvService.UpdateAsync(rgv);
}
}
}
int save = 0;
if (stepCommand == RgvCommandEnum.Place && nextCommand == RgvCommandEnum.UnKnown)
{
//任务结束
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,
TaskId = read.IsSuccess ? read.Data.TaskId : 0,
RetResult = read.IsSuccess ? read.Data.Result : 0,
RetCmd = read.IsSuccess ? read.Data.Command : 0,
ExecuteAction = execute == null ? 0 : execute.Value,
};
var rgvCommand = await GetRgvCommandAsync(updateInfo);
if (!read.IsSuccess)
{
_logger.LogError($"从PLC读取调度数据失败{_param.PlcName}->{_param.RetCmdAddress}{read.ErrorMessage}");
return ResultModel.Error($"读命令失败:{_param.PlcName}->{_param.RetCmdAddress}{read.ErrorMessage}");
}
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,
FromStation1 = (short)rgvCommand.Data!.FromPos1,
FromStation2 = (short)rgvCommand.Data!.FromPos2,
ToStation1 = (short)rgvCommand.Data!.TargetPos1,
ToStation2 = (short)rgvCommand.Data!.TargetPos2,
TaskId = rgvCommand.Data!.TaskId,
QrCode1 = rgvCommand.Data!.QrCode1,
QrCode2 = rgvCommand.Data!.QrCode2
};
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();
}
}
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)
{
return ResultModel<RgvToWcsData>.Error(readResult.Message);
}
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)
{
var readResult = await driver.ReadAsync(address, 60);
if (!readResult.IsSuccess)
{
return ResultModel<WcsToRgvData>.Error(readResult.Message);
}
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)
{
// 准备数据
byte[] data = new byte[60];
driver.ByteTransform.TransByte(toRgvData.Command).CopyTo(data, 0);
driver.ByteTransform.TransByte(toRgvData.TaskId).CopyTo(data, 2);
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);
driver.ByteTransform.TransByte(toRgvData.Count).CopyTo(data, 14);
data[16] = 20;//字符串最大长度
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);
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数据失败");
}
}