feat(core): Логика выдачи задания роботу для переименованных шаблонов
This commit is contained in:
@@ -49,5 +49,11 @@ namespace PARR.Core.Repositories.Interfaces
|
|||||||
/// <param name="id"></param>
|
/// <param name="id"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<bool> SetInProgressStatusAsync(Guid id);
|
Task<bool> SetInProgressStatusAsync(Guid id);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Установить статус робота - Ошибка, и поставить максимальное значение попыток
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="configuration"></param>
|
||||||
|
void SetErrorRobotStatusAndMaxAttempts(RobotConfiguration configuration);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,15 @@
|
|||||||
using AutoMapper;
|
using AutoMapper;
|
||||||
|
using InfluxDB.Client.Api.Domain;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using PARR.BLL.Helpers;
|
using PARR.BLL.Helpers;
|
||||||
using PARR.Core.Repositories.Interfaces;
|
using PARR.Core.Repositories.Interfaces;
|
||||||
|
using PARR.Core.Repositories.Interfaces.TemplateRepositories;
|
||||||
using PARR.Core.Services.NextRunServices;
|
using PARR.Core.Services.NextRunServices;
|
||||||
using PARR.Core.Services.RobotTask.Interfaces;
|
using PARR.Core.Services.RobotTask.Interfaces;
|
||||||
|
using PARR.Core.Services.RobotTask.Models;
|
||||||
using PARR.Core.Services.Shortcodes;
|
using PARR.Core.Services.Shortcodes;
|
||||||
|
using PARR.Domain.Common.Template;
|
||||||
using PARR.Domain.DTOs.RobotTask;
|
using PARR.Domain.DTOs.RobotTask;
|
||||||
using PARR.Domain.Entities;
|
using PARR.Domain.Entities;
|
||||||
using PARR.Domain.Entities.Base.History;
|
using PARR.Domain.Entities.Base.History;
|
||||||
@@ -20,16 +24,18 @@ namespace PARR.Core.Services.RobotTask.Implementations
|
|||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Количество заданий которые рассматриваем для взятия в работу.
|
/// Количество заданий которые рассматриваем для взятия в работу.
|
||||||
|
/// Рекомендованное значение, кол-во роботов * 3
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private readonly int TakeTasks = 10;
|
private readonly int TakeTasks = 15 * 3;
|
||||||
|
|
||||||
private readonly ILogger<RobotTaskService> logger;
|
private readonly ILogger<RobotTaskService> _logger;
|
||||||
private readonly IRobotConfigurationRepository robotConfigurationRepository;
|
private readonly IRobotConfigurationRepository _robotConfigurationRepository;
|
||||||
private readonly SettingsFromDb settingsFromDb;
|
private readonly SettingsFromDb _settingsFromDb;
|
||||||
private readonly IRobotHistoryRepository robotHistoryRepository;
|
private readonly IRobotHistoryRepository _robotHistoryRepository;
|
||||||
private readonly IMapper mapper;
|
private readonly IMapper _mapper;
|
||||||
private readonly IShortcodesService shortcodesService;
|
private readonly IShortcodesService _shortcodesService;
|
||||||
private readonly INextRunService nextRunService;
|
private readonly INextRunService _nextRunService;
|
||||||
|
private readonly ITemplateRenamePendingRepository _templateRenamePendingRepository;
|
||||||
|
|
||||||
public RobotTaskService(
|
public RobotTaskService(
|
||||||
ILogger<RobotTaskService> logger,
|
ILogger<RobotTaskService> logger,
|
||||||
@@ -38,16 +44,18 @@ namespace PARR.Core.Services.RobotTask.Implementations
|
|||||||
IRobotHistoryRepository robotHistoryRepository,
|
IRobotHistoryRepository robotHistoryRepository,
|
||||||
IMapper mapper,
|
IMapper mapper,
|
||||||
IShortcodesService shortcodesService,
|
IShortcodesService shortcodesService,
|
||||||
INextRunService nextRunService
|
INextRunService nextRunService,
|
||||||
|
ITemplateRenamePendingRepository templateRenamePendingRepository
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
this.logger = logger;
|
this._logger = logger;
|
||||||
this.robotConfigurationRepository = robotConfigurationRepository;
|
this._robotConfigurationRepository = robotConfigurationRepository;
|
||||||
this.settingsFromDb = settingsFromDb;
|
this._settingsFromDb = settingsFromDb;
|
||||||
this.robotHistoryRepository = robotHistoryRepository;
|
this._robotHistoryRepository = robotHistoryRepository;
|
||||||
this.mapper = mapper;
|
this._mapper = mapper;
|
||||||
this.shortcodesService = shortcodesService;
|
this._shortcodesService = shortcodesService;
|
||||||
this.nextRunService = nextRunService;
|
this._nextRunService = nextRunService;
|
||||||
|
_templateRenamePendingRepository = templateRenamePendingRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -55,19 +63,19 @@ namespace PARR.Core.Services.RobotTask.Implementations
|
|||||||
{
|
{
|
||||||
var templateTask = await GetTaskAsync(RobotsEnum.TemplateOrder, taskStatusCode, acquireTask, robotIp, robotId, TimeSpan.Zero);
|
var templateTask = await GetTaskAsync(RobotsEnum.TemplateOrder, taskStatusCode, acquireTask, robotIp, robotId, TimeSpan.Zero);
|
||||||
|
|
||||||
var task = mapper.Map<RobotTaskTemplate>(templateTask);
|
var task = _mapper.Map<RobotTaskTemplate>(templateTask);
|
||||||
|
|
||||||
task = task with { FullDescription = NormalizeLineEndingsToCrlf(await shortcodesService.ApplyShortcodesAsync(task.FullDescription, templateTask.Template!)) };
|
task = task with { FullDescription = NormalizeLineEndingsToCrlf(await _shortcodesService.ApplyShortcodesAsync(task.FullDescription, templateTask.Template!)) };
|
||||||
task = task with { ShortDescription = await shortcodesService.ApplyShortcodesAsync(task.ShortDescription, templateTask.Template!) };
|
task = task with { ShortDescription = await _shortcodesService.ApplyShortcodesAsync(task.ShortDescription, templateTask.Template!) };
|
||||||
task = task with { Solution = NormalizeLineEndingsToCrlf(await shortcodesService.ApplyShortcodesAsync(task.Solution, templateTask.Template!)) };
|
task = task with { Solution = NormalizeLineEndingsToCrlf(await _shortcodesService.ApplyShortcodesAsync(task.Solution, templateTask.Template!)) };
|
||||||
task = task with { TnkName = await shortcodesService.ApplyShortcodesAsync(task.TnkName, templateTask.Template!) };
|
task = task with { TnkName = await _shortcodesService.ApplyShortcodesAsync(task.TnkName, templateTask.Template!) };
|
||||||
task = task with { WorkName = await shortcodesService.ApplyShortcodesAsync(task.WorkName, templateTask.Template!) };
|
task = task with { WorkName = await _shortcodesService.ApplyShortcodesAsync(task.WorkName, templateTask.Template!) };
|
||||||
task = task with { WorkGroup = await shortcodesService.ApplyShortcodesAsync(task.WorkGroup, templateTask.Template!) };
|
task = task with { WorkGroup = await _shortcodesService.ApplyShortcodesAsync(task.WorkGroup, templateTask.Template!) };
|
||||||
task = task with { ResponseArea = await shortcodesService.ApplyShortcodesAsync(task.ResponseArea, templateTask.Template!) };
|
task = task with { ResponseArea = await _shortcodesService.ApplyShortcodesAsync(task.ResponseArea, templateTask.Template!) };
|
||||||
|
|
||||||
task = task with { ClosingCode = settingsFromDb.ClosingCode };
|
task = task with { ClosingCode = _settingsFromDb.ClosingCode };
|
||||||
task = task with { Initiator = settingsFromDb.Initiator };
|
task = task with { Initiator = _settingsFromDb.Initiator };
|
||||||
task = task with { Category = settingsFromDb.Category };
|
task = task with { Category = _settingsFromDb.Category };
|
||||||
|
|
||||||
return task;
|
return task;
|
||||||
}
|
}
|
||||||
@@ -82,22 +90,22 @@ namespace PARR.Core.Services.RobotTask.Implementations
|
|||||||
var resultUpdateNextRun = await UpdateNextRunAsync(scheduleTask, historyInitiator);
|
var resultUpdateNextRun = await UpdateNextRunAsync(scheduleTask, historyInitiator);
|
||||||
if (!resultUpdateNextRun)
|
if (!resultUpdateNextRun)
|
||||||
{
|
{
|
||||||
logger.LogError("Ошибка при расчете NextRun для templateId: {templateId}", scheduleTask.TemplateId);
|
_logger.LogError("Ошибка при расчете NextRun для templateId: {templateId}", scheduleTask.TemplateId);
|
||||||
throw new NextRunException($"Ошибка при расчете NextRun для templateId: {scheduleTask.TemplateId}");
|
throw new NextRunException($"Ошибка при расчете NextRun для templateId: {scheduleTask.TemplateId}");
|
||||||
}
|
}
|
||||||
|
|
||||||
var task = mapper.Map<RobotTaskSchedule>(scheduleTask);
|
var task = _mapper.Map<RobotTaskSchedule>(scheduleTask);
|
||||||
|
|
||||||
task = task with { Timezone = settingsFromDb.EsppScheduleTimezone };
|
task = task with { Timezone = _settingsFromDb.EsppScheduleTimezone };
|
||||||
task = task with { WorkGroup = await shortcodesService.ApplyShortcodesAsync(task.WorkGroup, scheduleTask.Template!) };
|
task = task with { WorkGroup = await _shortcodesService.ApplyShortcodesAsync(task.WorkGroup, scheduleTask.Template!) };
|
||||||
task = task with { ResponseArea = await shortcodesService.ApplyShortcodesAsync(task.ResponseArea, scheduleTask.Template!) };
|
task = task with { ResponseArea = await _shortcodesService.ApplyShortcodesAsync(task.ResponseArea, scheduleTask.Template!) };
|
||||||
|
|
||||||
//nextRun в часовой зоне УЗ Робота ЕСПП
|
//nextRun в часовой зоне УЗ Робота ЕСПП
|
||||||
var nextRunWithRobotTz = scheduleTask.Template!.NextRun.Add(nextRunService.GetEsppAccountOffset());
|
var nextRunWithRobotTz = scheduleTask.Template!.NextRun.Add(_nextRunService.GetEsppAccountOffset());
|
||||||
//на всякий случай еще раз проверяем, что дата не устарела и отправляем задание
|
//на всякий случай еще раз проверяем, что дата не устарела и отправляем задание
|
||||||
if (nextRunWithRobotTz < DateTimeOffset.UtcNow)
|
if (nextRunWithRobotTz < DateTimeOffset.UtcNow)
|
||||||
{
|
{
|
||||||
logger.LogError("Ошибка при расчете NextRun для templateId: {templateId}, итоговое значение для робота, меньше чем сейчас {nextRunWithRobotTz}<{now}",
|
_logger.LogError("Ошибка при расчете NextRun для templateId: {templateId}, итоговое значение для робота, меньше чем сейчас {nextRunWithRobotTz}<{now}",
|
||||||
task.TemplateId, nextRunWithRobotTz, DateTimeOffset.UtcNow);
|
task.TemplateId, nextRunWithRobotTz, DateTimeOffset.UtcNow);
|
||||||
throw new NextRunException($"Ошибка при расчете NextRun для templateId: {scheduleTask.TemplateId}");
|
throw new NextRunException($"Ошибка при расчете NextRun для templateId: {scheduleTask.TemplateId}");
|
||||||
}
|
}
|
||||||
@@ -105,7 +113,7 @@ namespace PARR.Core.Services.RobotTask.Implementations
|
|||||||
task = task with { NextStart = EsppScheduleHelpers.GetNextRun(nextRunWithRobotTz) };
|
task = task with { NextStart = EsppScheduleHelpers.GetNextRun(nextRunWithRobotTz) };
|
||||||
task = task with { GenerationTime = EsppScheduleHelpers.GetGenerationTime(nextRunWithRobotTz) };
|
task = task with { GenerationTime = EsppScheduleHelpers.GetGenerationTime(nextRunWithRobotTz) };
|
||||||
|
|
||||||
task = task with { RepeatRange = settingsFromDb.ScheduleRepeatRange };
|
task = task with { RepeatRange = _settingsFromDb.ScheduleRepeatRange };
|
||||||
task = task with { };
|
task = task with { };
|
||||||
|
|
||||||
return task;
|
return task;
|
||||||
@@ -125,7 +133,7 @@ namespace PARR.Core.Services.RobotTask.Implementations
|
|||||||
private async Task<RobotConfiguration> GetTaskAsync(RobotsEnum robotCode, TaskStatusEnum taskStatusCode, bool acquireTask, string? robotIp, string? robotId, TimeSpan scheduleCooldownDuration)
|
private async Task<RobotConfiguration> GetTaskAsync(RobotsEnum robotCode, TaskStatusEnum taskStatusCode, bool acquireTask, string? robotIp, string? robotId, TimeSpan scheduleCooldownDuration)
|
||||||
{
|
{
|
||||||
// 1. Ищем все задания с превышенным кол-вом попыток и просроченным временем, ставим им статус ошибки
|
// 1. Ищем все задания с превышенным кол-вом попыток и просроченным временем, ставим им статус ошибки
|
||||||
await robotConfigurationRepository.MarkExpiredTasksAsFailedAsync(settingsFromDb.RobotAttemptsNumber, settingsFromDb.RobotWaitTime);
|
await _robotConfigurationRepository.MarkExpiredTasksAsFailedAsync(_settingsFromDb.RobotAttemptsNumber, _settingsFromDb.RobotWaitTime);
|
||||||
|
|
||||||
|
|
||||||
// 2. Ищем доступные задания
|
// 2. Ищем доступные задания
|
||||||
@@ -148,7 +156,7 @@ namespace PARR.Core.Services.RobotTask.Implementations
|
|||||||
{
|
{
|
||||||
// Берем первую задачу из списка доступных
|
// Берем первую задачу из списка доступных
|
||||||
acquiredTaskId = availableTasks.First();
|
acquiredTaskId = availableTasks.First();
|
||||||
logger.LogDebug("Задача не требует захвата, взята первая из доступных: {TaskId}", acquiredTaskId);
|
_logger.LogDebug("Задача не требует захвата, взята первая из доступных: {TaskId}", acquiredTaskId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -168,20 +176,14 @@ namespace PARR.Core.Services.RobotTask.Implementations
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
private async Task<List<Guid>> GetAvailableTasksAsync(RobotsEnum robotCode, TaskStatusEnum taskStatusCode, TimeSpan scheduleCooldownDuration)
|
private async Task<List<Guid>> GetAvailableTasksAsync(RobotsEnum robotCode, TaskStatusEnum taskStatusCode, TimeSpan scheduleCooldownDuration)
|
||||||
{
|
{
|
||||||
var query = robotConfigurationRepository.Get()
|
var query = _robotConfigurationRepository.Get()
|
||||||
.AsNoTracking()
|
.AsNoTracking()
|
||||||
.Where(t => t.RobotCode == (int)robotCode/* && t.TaskStatusCode == (int)taskStatusCode*/);
|
.Where(t => t.RobotCode == (int)robotCode);
|
||||||
|
|
||||||
// Если это задание для робота расписаний
|
// Если это задание для робота расписаний
|
||||||
if (robotCode == RobotsEnum.ScheduleOrder)
|
if (robotCode == RobotsEnum.ScheduleOrder)
|
||||||
{
|
{
|
||||||
// Выбираем только записи с созданными шаблонами (у которых статус 30), а только потом ищем у них расписания
|
// Выбираем только записи с созданными шаблонами (у которых статус 30), а только потом ищем у них расписания
|
||||||
#region Старый не оптимизированный запрос
|
|
||||||
//var createdTemplates = robotConfigurationRepository.Get()
|
|
||||||
// .Where(t => t.RobotCode == (int)RobotsEnum.TemplateOrder && t.TaskStatusCode == (int)TaskStatusEnum.Ok)
|
|
||||||
// .Select(t => t.TemplateId);
|
|
||||||
//query = query.Where(t => createdTemplates.Contains(t.TemplateId));
|
|
||||||
#endregion
|
|
||||||
query = query.Where(t => t.Template!.RobotConfigurations.Any(x => x.RobotCode == (int)RobotsEnum.TemplateOrder && x.TaskStatusCode == (int)TaskStatusEnum.Ok));
|
query = query.Where(t => t.Template!.RobotConfigurations.Any(x => x.RobotCode == (int)RobotsEnum.TemplateOrder && x.TaskStatusCode == (int)TaskStatusEnum.Ok));
|
||||||
|
|
||||||
|
|
||||||
@@ -200,20 +202,22 @@ namespace PARR.Core.Services.RobotTask.Implementations
|
|||||||
// Сортируем по nextRun, чтобы те, у кого nextRun ближе к текущей, выполнились скорее
|
// Сортируем по nextRun, чтобы те, у кого nextRun ближе к текущей, выполнились скорее
|
||||||
query = query.OrderBy(t => t.Template!.NextRun).ThenBy(t => t.Template!.IsActiveSchedule).ThenBy(t => t.Template!.IsActiveTemplate);
|
query = query.OrderBy(t => t.Template!.NextRun).ThenBy(t => t.Template!.IsActiveSchedule).ThenBy(t => t.Template!.IsActiveTemplate);
|
||||||
|
|
||||||
// Кандидаты заданий
|
// Кандидаты заданий, Id задания и имя шаблона
|
||||||
var tasks = new List<Guid>();
|
//var tasks = new List<Guid>();
|
||||||
|
var tasks = new List<RobotTaskDetails>();
|
||||||
|
|
||||||
// Ещем первые 10 заданий в статусе ОЖИДАНИЕ
|
// Ищем первые TakeTasks заданий в статусе ОЖИДАНИЕ
|
||||||
tasks = await query
|
tasks = await query
|
||||||
.Where(t =>
|
.Where(t =>
|
||||||
t.RobotStatusCode == (int)RobotStatusEnum.Wait
|
t.RobotStatusCode == (int)RobotStatusEnum.Wait
|
||||||
&& t.TaskStatusCode == (int)taskStatusCode
|
&& t.TaskStatusCode == (int)taskStatusCode
|
||||||
).Take(TakeTasks)
|
).Take(TakeTasks)
|
||||||
.Select(t => t.Id)
|
//.Select(t => t.Id)
|
||||||
|
.Select(t => new RobotTaskDetails(t.Id, t.Template!.Name, t.Template.NextRun))
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
|
||||||
|
|
||||||
logger.LogDebug("Найдено заданий в статусе 'Ожидание' {Count} шт. Робот '{Robot}'", tasks.Count, robotCode.ToString());
|
_logger.LogDebug("Найдено заданий в статусе 'Ожидание' {Count} шт. Робот '{Robot}'", tasks.Count, robotCode.ToString());
|
||||||
|
|
||||||
if (tasks.Count == 0)
|
if (tasks.Count == 0)
|
||||||
{
|
{
|
||||||
@@ -222,20 +226,171 @@ namespace PARR.Core.Services.RobotTask.Implementations
|
|||||||
// Далее проверяется `LastStatusUpdated`, что время последнего смены статуса не превышает допустимого(берется из настроек, поле `RobotWaitTime`)
|
// Далее проверяется `LastStatusUpdated`, что время последнего смены статуса не превышает допустимого(берется из настроек, поле `RobotWaitTime`)
|
||||||
// и что текущая попытка не больше разрешенной(берется из настроек, поле `RobotAttemptsNumber`) - если это так, берется эта запись.
|
// и что текущая попытка не больше разрешенной(берется из настроек, поле `RobotAttemptsNumber`) - если это так, берется эта запись.
|
||||||
|
|
||||||
var endDate = DateTimeOffset.UtcNow.Add(-settingsFromDb.RobotWaitTime);
|
var endDate = DateTimeOffset.UtcNow.Add(-_settingsFromDb.RobotWaitTime);
|
||||||
|
|
||||||
tasks = await query.Where(t => t.RobotStatusCode == (int)RobotStatusEnum.InProgress
|
tasks = await query.Where(t => t.RobotStatusCode == (int)RobotStatusEnum.InProgress
|
||||||
&& t.TaskStatusCode==(int)taskStatusCode
|
&& t.TaskStatusCode == (int)taskStatusCode
|
||||||
&& t.AttemptsNumber < settingsFromDb.RobotAttemptsNumber
|
&& t.AttemptsNumber < _settingsFromDb.RobotAttemptsNumber
|
||||||
&& t.LastRobotStatusUpdated < endDate)
|
&& t.LastRobotStatusUpdated < endDate)
|
||||||
.Take(TakeTasks)
|
.Take(TakeTasks)
|
||||||
.Select(t => t.Id)
|
//.Select(t => t.Id)
|
||||||
|
.Select(t => new RobotTaskDetails(t.Id, t.Template!.Name, t.Template.NextRun))
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
|
||||||
logger.LogDebug("Найдено заданий в статусе 'В работе' {Count} шт. Робот '{Robot}'", tasks.Count, robotCode.ToString());
|
_logger.LogDebug("Найдено заданий в статусе 'В работе' {Count} шт. Робот '{Robot}'", tasks.Count, robotCode.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (robotCode == RobotsEnum.TemplateOrder)
|
||||||
|
{
|
||||||
|
// Если запрашиваем шаблоны, смотрим корректируем список заданий в зависимости от статуса переименования.
|
||||||
|
// Это не относится к расписаниям, потому что у переименованных расписаний статус Updating, а оно не возьмется в работу, пока не обновится шаблон
|
||||||
|
tasks = await ReplaceTemplateTasksForRenameAsync(tasks, robotCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
return tasks.Select(t => t.TaskId).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Проверяет наличие шаблонов в процессе переименования и заменяет обычные задания на задания по переименованию.
|
||||||
|
/// Если связанный шаблон не переименован, и у него статус ошибки, целевому шаблону устанавливается статус ошибки.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="tasks"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
private async Task<List<RobotTaskDetails>> ReplaceTemplateTasksForRenameAsync(List<RobotTaskDetails> tasks, RobotsEnum robotCode)
|
||||||
|
{
|
||||||
|
if (tasks.Count == 0 || robotCode != RobotsEnum.TemplateOrder)
|
||||||
return tasks;
|
return tasks;
|
||||||
|
|
||||||
|
|
||||||
|
// Ищем есть ли связанные шаблоны с таким имененм на переименование
|
||||||
|
var taskTemplateNames = tasks.Select(t => t.TemplateName);
|
||||||
|
var templatesToRename = await _templateRenamePendingRepository.Get().AsNoTracking()
|
||||||
|
.Where(t => taskTemplateNames.Contains(t.OldName))
|
||||||
|
.ToListAsync();
|
||||||
|
|
||||||
|
_logger.LogDebug("Шаблонов для переименования в текущем задании: {Count} шт.", templatesToRename.Count);
|
||||||
|
|
||||||
|
if (templatesToRename.Count == 0)
|
||||||
|
return tasks;
|
||||||
|
|
||||||
|
// Ищем связанные переименованные шаблоны по ИД, смотрим можем ли взять их в работу
|
||||||
|
var renameTemplateIds = templatesToRename.Select(t => t.TemplateId).ToList();
|
||||||
|
var renameTasks = await _robotConfigurationRepository.Get()
|
||||||
|
.AsNoTracking()
|
||||||
|
.Include(t => t.Template)
|
||||||
|
.Where(t =>
|
||||||
|
t.RobotCode == (int)robotCode
|
||||||
|
&& renameTemplateIds.Contains(t.TemplateId)
|
||||||
|
// Это может быть только обновление. Так как переименования для создаваемого шаблона быть не может
|
||||||
|
&& t.TaskStatusCode == (int)TaskStatusEnum.Updating
|
||||||
|
).ToListAsync();
|
||||||
|
|
||||||
|
// Ищем связанные задания которые в ошибке, и ставим целевому шаблону задание в ошибку
|
||||||
|
var errorTasks = renameTasks.Where(t => t.RobotStatusCode == (int)RobotStatusEnum.Error && t.AttemptsNumber >= _settingsFromDb.RobotAttemptsNumber).ToList();
|
||||||
|
|
||||||
|
var tasksToSetErrorStatus = new List<Guid>();
|
||||||
|
if (errorTasks.Count > 0)
|
||||||
|
{
|
||||||
|
_logger.LogDebug("Найдено связанных заданий на переименование с ошибками: {ErrorCount}. Будем целевым шаблонам тоже ставить статус ошибки.", errorTasks.Count);
|
||||||
|
|
||||||
|
var errorTemplateNames = errorTasks.Select(t => t.Template!.Name).ToList();
|
||||||
|
// Берем целевые таски, находим в них задания которым надо поставить ошибку
|
||||||
|
tasksToSetErrorStatus = tasks.Where(t => errorTemplateNames.Contains(t.TemplateName)).Select(t => t.TaskId).ToList();
|
||||||
|
|
||||||
|
// Устанавливаем ошибку целевым + пишем комментарий от робота + нажимаем комит
|
||||||
|
var logMessage = "[RobotTaskService] Установлен статус ошибки, так как не переименован связанный шаблон";
|
||||||
|
await SetErrorStatusAsync(tasksToSetErrorStatus, logMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
var endDate = DateTimeOffset.UtcNow.Add(-_settingsFromDb.RobotWaitTime);
|
||||||
|
// Смотрим статусы роботов, можно взять в работу, только если (RobotStatus == Wait) или (InpRogress но которые еще не просрочены)
|
||||||
|
var allowedTasks = renameTasks.Where(t =>
|
||||||
|
t.RobotStatusCode == (int)RobotStatusEnum.Wait
|
||||||
|
|| (t.RobotStatusCode == (int)RobotStatusEnum.InProgress && t.AttemptsNumber < _settingsFromDb.RobotAttemptsNumber && t.LastRobotStatusUpdated < endDate)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Формируем список заданий
|
||||||
|
|
||||||
|
var originalCount = tasks.Count; // Запоминаем сколько было изначально
|
||||||
|
|
||||||
|
// Из исходных тасков удалить те которым установлен статус ошибки
|
||||||
|
var filteredOriginalTasks = tasks.Where(t => !tasksToSetErrorStatus.Contains(t.TaskId)).ToList();
|
||||||
|
var errorCount = tasksToSetErrorStatus.Count; // Столько ушло в ошибку
|
||||||
|
|
||||||
|
// Определяем, какие имена шаблонов мы БУДЕМ подменять на задачи переименования
|
||||||
|
var allowedRenameTemplateNames = allowedTasks.Select(t => t.Template!.Name).ToList();
|
||||||
|
|
||||||
|
// Из исходных удаляем те, которые мы сейчас заменим (подменим)
|
||||||
|
var resultsTasks = filteredOriginalTasks.Where(t => !allowedRenameTemplateNames.Contains(t.TemplateName)).ToList();
|
||||||
|
|
||||||
|
// Считаем сколько именно задач мы ВЫКИНУЛИ из исходного списка ради подмены
|
||||||
|
var replacedCount = filteredOriginalTasks.Count - resultsTasks.Count;
|
||||||
|
|
||||||
|
// В исходные добавляем подменные таски для переименования (мапим их в RobotTaskDetails)
|
||||||
|
var mappedRenameTasks = allowedTasks.Select(t => new RobotTaskDetails(t.Id, t.Template!.Name, t.Template.NextRun)).ToList();
|
||||||
|
resultsTasks.AddRange(mappedRenameTasks);
|
||||||
|
|
||||||
|
var injectedCount = mappedRenameTasks.Count; // Столько задач переименования добавили взамен
|
||||||
|
|
||||||
|
// Логируем итоговую статистику трансформации пула задач
|
||||||
|
// TODO: Позже этот лог можно понизить до Debug!!!
|
||||||
|
_logger.LogInformation(
|
||||||
|
"Трансформация пула задач завершена. Исходных: {OriginalCount} шт. " +
|
||||||
|
"Отклонено (ошибка): {ErrorCount} шт. Удалено обычных для подмены: {ReplacedCount} шт. " +
|
||||||
|
"Внедрено задач переименования: {InjectedCount} шт. Итого к выдаче: {FinalCount} шт.",
|
||||||
|
originalCount, errorCount, replacedCount, injectedCount, resultsTasks.Count);
|
||||||
|
|
||||||
|
// Затем отсортируем по NextRun, чтоб ближайшие были выше
|
||||||
|
return resultsTasks.OrderBy(t => t.NextRun).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Установить статус задания - ошибка
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="taskIds"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
private async Task SetErrorStatusAsync(List<Guid> taskIds, string logMessage)
|
||||||
|
{
|
||||||
|
var tasks = await _robotConfigurationRepository.Get()
|
||||||
|
.Include(t => t.Template)
|
||||||
|
.Where(t => taskIds.Contains(t.Id)).ToListAsync();
|
||||||
|
|
||||||
|
if (tasks.Count == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
foreach (var task in tasks)
|
||||||
|
{
|
||||||
|
// Так как это целевой шаблон, то ставим ему сразу максимальное кол-во попыток и ошибку, чтоб больше он не выдавался в заданиях, пока не исправим связанный
|
||||||
|
// Устанавливаем статус ошибки
|
||||||
|
_robotConfigurationRepository.SetErrorRobotStatusAndMaxAttempts(task);
|
||||||
|
|
||||||
|
// Пишем в лог роботу
|
||||||
|
var history = new RobotHistory
|
||||||
|
{
|
||||||
|
Id = Guid.NewGuid(),
|
||||||
|
HistoryLevel = (int)RobotStatusEnum.Error,
|
||||||
|
TaskStatusCode = task.TaskStatusCode,
|
||||||
|
RobotConfigurationId = task.Id,
|
||||||
|
RobotIp = null,
|
||||||
|
RobotId = ParrComponentsEnum.Api.ToString(),
|
||||||
|
RobotMessage = logMessage
|
||||||
|
};
|
||||||
|
|
||||||
|
await _robotHistoryRepository.CreateAsync(history);
|
||||||
|
|
||||||
|
_logger.LogInformation("Для целевого задания {TaskId}, по переименованию шаблона в {TemplateName}, установлен статус ошибки, так как связанное задание со старым шаблоном со статусом ошибка.", task.Id, task.Template!.Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (await _robotHistoryRepository.CommitAsync())
|
||||||
|
{
|
||||||
|
_logger.LogDebug("Установлен статус Ошибка, для заданий {TaskCount} шт.", tasks.Count);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.LogError("Ошибка при установке статуса задания Ошибка, для заданий {TaskCount} шт.", tasks.Count);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -248,12 +403,12 @@ namespace PARR.Core.Services.RobotTask.Implementations
|
|||||||
{
|
{
|
||||||
foreach (var taskId in tasks)
|
foreach (var taskId in tasks)
|
||||||
{
|
{
|
||||||
var isChangedStatus = await robotConfigurationRepository.SetInProgressStatusAsync(taskId);
|
var isChangedStatus = await _robotConfigurationRepository.SetInProgressStatusAsync(taskId);
|
||||||
if (isChangedStatus)
|
if (isChangedStatus)
|
||||||
{
|
{
|
||||||
logger.LogDebug("Захвачена задача {TaskId}", taskId);
|
_logger.LogDebug("Захвачена задача {TaskId}", taskId);
|
||||||
|
|
||||||
var task = await robotConfigurationRepository.Get()
|
var task = await _robotConfigurationRepository.Get()
|
||||||
.AsNoTracking()
|
.AsNoTracking()
|
||||||
.FirstAsync(t => t.Id == taskId);
|
.FirstAsync(t => t.Id == taskId);
|
||||||
|
|
||||||
@@ -268,18 +423,18 @@ namespace PARR.Core.Services.RobotTask.Implementations
|
|||||||
RobotId = robotId
|
RobotId = robotId
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!await robotHistoryRepository.CreateAsync(history) || !await robotHistoryRepository.CommitAsync())
|
if (!await _robotHistoryRepository.CreateAsync(history) || !await _robotHistoryRepository.CommitAsync())
|
||||||
throw new DbErrorException("Ошибка при добавлении истории робота, при взятии задания в работу.");
|
throw new DbErrorException("Ошибка при добавлении истории робота, при взятии задания в работу.");
|
||||||
|
|
||||||
return taskId;
|
return taskId;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
logger.LogDebug("Не удалось захватить задачу {TaskId}", taskId);
|
_logger.LogDebug("Не удалось захватить задачу {TaskId}", taskId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.LogDebug("Не удалось захватить ни одну из доступных задач для робота");
|
_logger.LogDebug("Не удалось захватить ни одну из доступных задач для робота");
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -293,7 +448,7 @@ namespace PARR.Core.Services.RobotTask.Implementations
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
private async Task<RobotConfiguration> GetTaskWithAllDataAsync(Guid taskId, RobotsEnum robotCode)
|
private async Task<RobotConfiguration> GetTaskWithAllDataAsync(Guid taskId, RobotsEnum robotCode)
|
||||||
{
|
{
|
||||||
IQueryable<RobotConfiguration> query = robotConfigurationRepository.Get()
|
IQueryable<RobotConfiguration> query = _robotConfigurationRepository.Get()
|
||||||
//.AsNoTracking() // нужно обязательно трекать, так как может измениться nextRun и его нужно будет сохранить
|
//.AsNoTracking() // нужно обязательно трекать, так как может измениться nextRun и его нужно будет сохранить
|
||||||
.AsSingleQuery()
|
.AsSingleQuery()
|
||||||
// Общие инклуды для шаблонов и расписаний
|
// Общие инклуды для шаблонов и расписаний
|
||||||
@@ -372,23 +527,23 @@ namespace PARR.Core.Services.RobotTask.Implementations
|
|||||||
var template = task.Template!;
|
var template = task.Template!;
|
||||||
|
|
||||||
//var nextRun = await esppScheduleTransformService.GetNextDateAsync(template.Job!.GroupId, template!.Job!.Group!.ReferenceDate);
|
//var nextRun = await esppScheduleTransformService.GetNextDateAsync(template.Job!.GroupId, template!.Job!.Group!.ReferenceDate);
|
||||||
var nextRun = await nextRunService.GetNextRunForTemplateAsync(template.Id, false);
|
var nextRun = await _nextRunService.GetNextRunForTemplateAsync(template.Id, false);
|
||||||
|
|
||||||
if (!nextRun.HasValue)
|
if (!nextRun.HasValue)
|
||||||
{
|
{
|
||||||
logger.LogError("При обновлении nextRun для шаблона {templateId}, расчитанный nextRun=null, ошибка в расчетах.", template.Id);
|
_logger.LogError("При обновлении nextRun для шаблона {templateId}, расчитанный nextRun=null, ошибка в расчетах.", template.Id);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nextRun.Value < DateTimeOffset.UtcNow)
|
if (nextRun.Value < DateTimeOffset.UtcNow)
|
||||||
{
|
{
|
||||||
logger.LogError("При обновлении nextRun для шаблона {templateId}, расчитанный nextRun<Now [{nextRun}<{now}], ошибка в расчетах.", template.Id, nextRun.Value, DateTimeOffset.UtcNow);
|
_logger.LogError("При обновлении nextRun для шаблона {templateId}, расчитанный nextRun<Now [{nextRun}<{now}], ошибка в расчетах.", template.Id, nextRun.Value, DateTimeOffset.UtcNow);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nextRun != template.NextRun)
|
if (nextRun != template.NextRun)
|
||||||
{
|
{
|
||||||
logger.LogDebug($"Для шаблона id {template.Id} обновляю nextRun, новое значение {nextRun}, старое значение {template.NextRun}");
|
_logger.LogDebug($"Для шаблона id {template.Id} обновляю nextRun, новое значение {nextRun}, старое значение {template.NextRun}");
|
||||||
|
|
||||||
template.LastRun = template.NextRun;
|
template.LastRun = template.NextRun;
|
||||||
template.NextRun = nextRun.Value;
|
template.NextRun = nextRun.Value;
|
||||||
@@ -399,7 +554,7 @@ namespace PARR.Core.Services.RobotTask.Implementations
|
|||||||
? suffix
|
? suffix
|
||||||
: $"{historyInitiator.InitiatorComment}. {suffix}";
|
: $"{historyInitiator.InitiatorComment}. {suffix}";
|
||||||
|
|
||||||
if (!await robotConfigurationRepository.CommitAsync(historyInitiator))
|
if (!await _robotConfigurationRepository.CommitAsync(historyInitiator))
|
||||||
throw new DbErrorException("Ошибка при сохранении изменения NextRun");
|
throw new DbErrorException("Ошибка при сохранении изменения NextRun");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
9
PARR.Core/Services/RobotTask/Models/RobotTaskDetails.cs
Normal file
9
PARR.Core/Services/RobotTask/Models/RobotTaskDetails.cs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
namespace PARR.Core.Services.RobotTask.Models
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Модель задания для робота
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="TaskId"></param>
|
||||||
|
/// <param name="TemplateName"></param>
|
||||||
|
internal record RobotTaskDetails(Guid TaskId, string TemplateName, DateTimeOffset NextRun);
|
||||||
|
}
|
||||||
@@ -1,9 +1,11 @@
|
|||||||
using Microsoft.EntityFrameworkCore;
|
using InfluxDB.Client.Api.Domain;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using PARR.Core.Repositories.Interfaces;
|
using PARR.Core.Repositories.Interfaces;
|
||||||
using PARR.DAL.Context;
|
using PARR.DAL.Context;
|
||||||
using PARR.DAL.Repositories.Base;
|
using PARR.DAL.Repositories.Base;
|
||||||
using PARR.Domain.Entities;
|
using PARR.Domain.Entities;
|
||||||
|
using PARR.Domain.Entities.RobotEntities;
|
||||||
using PARR.Domain.Enums;
|
using PARR.Domain.Enums;
|
||||||
using PARR.Domain.Settings;
|
using PARR.Domain.Settings;
|
||||||
|
|
||||||
@@ -94,8 +96,8 @@ namespace PARR.DAL.Repositories
|
|||||||
configuration.AttemptsNumber++;
|
configuration.AttemptsNumber++;
|
||||||
configuration.LastRobotStatusUpdated = DateTimeOffset.UtcNow;
|
configuration.LastRobotStatusUpdated = DateTimeOffset.UtcNow;
|
||||||
break;
|
break;
|
||||||
//case RobotStatusEnum.Error:
|
case RobotStatusEnum.Error:
|
||||||
// break;
|
break;
|
||||||
case RobotStatusEnum.Complete:
|
case RobotStatusEnum.Complete:
|
||||||
configuration.LastRobotStatusUpdated = DateTimeOffset.UtcNow;
|
configuration.LastRobotStatusUpdated = DateTimeOffset.UtcNow;
|
||||||
break;
|
break;
|
||||||
@@ -108,6 +110,14 @@ namespace PARR.DAL.Repositories
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetErrorRobotStatusAndMaxAttempts(RobotConfiguration configuration)
|
||||||
|
{
|
||||||
|
ChangeRobotStatus(RobotStatusEnum.Error, configuration);
|
||||||
|
|
||||||
|
configuration.AttemptsNumber = settingsFromDb.RobotAttemptsNumber;
|
||||||
|
configuration.LastRobotStatusUpdated = DateTimeOffset.UtcNow;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public async Task<bool> SetInProgressStatusAsync(Guid id)
|
public async Task<bool> SetInProgressStatusAsync(Guid id)
|
||||||
{
|
{
|
||||||
@@ -175,10 +185,55 @@ namespace PARR.DAL.Repositories
|
|||||||
logger.LogInformation($"Устанавливаю RobotStatus: {RobotStatusEnum.Error} для RobotConfigurationId {item.Id}");
|
logger.LogInformation($"Устанавливаю RobotStatus: {RobotStatusEnum.Error} для RobotConfigurationId {item.Id}");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
#region Ищем, есть ли связанные шаблоны, которые должны переименоваться, им тоже нужно установить статус ошибки, но только для Шаблонов
|
||||||
|
|
||||||
|
var linksCount = 0;
|
||||||
|
|
||||||
|
var templateIds = configObjs.Where(t => t.RobotCode == (int)RobotsEnum.TemplateOrder).Select(t => t.TemplateId).ToList();
|
||||||
|
if (templateIds.Any())
|
||||||
|
{
|
||||||
|
// По этим именам находим целевые шаблоны, берем их задания, и ставим им статус Ошибка + записать в лог робота
|
||||||
|
var configs = await EntitySet
|
||||||
|
.Where(t => t.RobotCode == (int)RobotsEnum.TemplateOrder
|
||||||
|
&& t.RobotStatusCode != (int)RobotStatusEnum.Error
|
||||||
|
&& EntityContext.Templates.Any(tmpl =>
|
||||||
|
templateIds.Contains(tmpl.Id)
|
||||||
|
&& tmpl.TemplateRenamePending != null
|
||||||
|
&& tmpl.TemplateRenamePending.OldName == t.Template!.Name))
|
||||||
|
.ToListAsync();
|
||||||
|
|
||||||
|
linksCount = configs.Count;
|
||||||
|
|
||||||
|
foreach (var item in configs)
|
||||||
|
{
|
||||||
|
SetErrorRobotStatusAndMaxAttempts(item);
|
||||||
|
|
||||||
|
// Пишем в лог роботу
|
||||||
|
var history = new RobotHistory
|
||||||
|
{
|
||||||
|
Id = Guid.NewGuid(),
|
||||||
|
HistoryLevel = (int)RobotStatusEnum.Error,
|
||||||
|
TaskStatusCode = item.TaskStatusCode,
|
||||||
|
RobotConfigurationId = item.Id,
|
||||||
|
RobotIp = null,
|
||||||
|
RobotId = ParrComponentsEnum.Api.ToString(),
|
||||||
|
RobotMessage = "[RobotConfigurationRepository] Установлен статус ошибки, так как не переименован связанный шаблон"
|
||||||
|
};
|
||||||
|
|
||||||
|
// Синхронный Add работает быстрее и безопаснее внутри цикла
|
||||||
|
EntityContext.RobotHistories.Add(history);
|
||||||
|
|
||||||
|
logger.LogInformation($"Устанавливаю RobotStatus: {RobotStatusEnum.Error} для RobotConfigurationId {item.Id}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
var result = await CommitAsync();
|
var result = await CommitAsync();
|
||||||
|
|
||||||
if (!result)
|
if (!result)
|
||||||
logger.LogError($"Ошибка при сохранении изменений RobotStatus для RobotConfigurationId: item.Id, RobotStatus: {RobotStatusEnum.Error}");
|
logger.LogError("Ошибка при сохранении изменений RobotStatus для просроченных заданий в количестве {Count} шт.", configObjs.Count + linksCount);
|
||||||
|
//else
|
||||||
|
// logger.LogInformation("Успешно обработано и переведено в статус Ошибки просроченных заданий: {Count} шт.", configObjs.Count + linksCount);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user