243 lines
12 KiB
C#
243 lines
12 KiB
C#
using InfluxDB.Client.Api.Domain;
|
||
using Microsoft.EntityFrameworkCore;
|
||
using Microsoft.Extensions.Logging;
|
||
using PARR.Core.Repositories.Interfaces;
|
||
using PARR.DAL.Context;
|
||
using PARR.DAL.Repositories.Base;
|
||
using PARR.Domain.Entities;
|
||
using PARR.Domain.Entities.RobotEntities;
|
||
using PARR.Domain.Enums;
|
||
using PARR.Domain.Settings;
|
||
|
||
namespace PARR.DAL.Repositories
|
||
{
|
||
internal class RobotConfigurationRepository : BaseRepository<RobotConfiguration>, IRobotConfigurationRepository
|
||
{
|
||
private readonly SettingsFromDb settingsFromDb;
|
||
|
||
public RobotConfigurationRepository(DataContext dataContext, ILogger<RobotConfigurationRepository> logger, SettingsFromDb settingsFromDb) : base(logger, dataContext)
|
||
{
|
||
this.settingsFromDb = settingsFromDb;
|
||
}
|
||
|
||
|
||
public void ChangeTaskStatus(TaskStatusEnum taskStatus, RobotConfiguration configuration)
|
||
{
|
||
configuration.TaskStatusCode = (int)taskStatus;
|
||
|
||
switch (taskStatus)
|
||
{
|
||
case TaskStatusEnum.Creating:
|
||
ChangeRobotStatus(RobotStatusEnum.Wait, configuration);
|
||
break;
|
||
case TaskStatusEnum.Updating:
|
||
ChangeRobotStatus(RobotStatusEnum.Wait, configuration);
|
||
break;
|
||
case TaskStatusEnum.Ok:
|
||
ChangeRobotStatus(RobotStatusEnum.Complete, configuration);
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
}
|
||
|
||
|
||
public bool SetUpdateTaskStatusIfAllow(RobotConfiguration configuration)
|
||
{
|
||
var updatingStatus = TaskStatusEnum.Updating;
|
||
|
||
// ставить статус Updating можно только если текущий статус == Ok
|
||
if (configuration.TaskStatusCode != (int)TaskStatusEnum.Ok)
|
||
{
|
||
int taskStatusValue = configuration.TaskStatusCode;
|
||
string taskStatusName = Enum.IsDefined(typeof(TaskStatusEnum), taskStatusValue)
|
||
? ((TaskStatusEnum)taskStatusValue).ToString()
|
||
: $"Unknown ({taskStatusValue})";
|
||
|
||
logger.LogInformation("Нельзя установить статус {newStatus} для конфигурации {configurationId}, templateId: {templateId}, так как текущий статус {currentStatus}",
|
||
updatingStatus, configuration.Id, configuration.TemplateId, taskStatusName);
|
||
return false;
|
||
}
|
||
|
||
// Если хотим поставить Updating у шаблона, можно ставить только в том случае, если у шаблона уже есть ScheduleEsppId
|
||
if (configuration.RobotCode == (int)RobotsEnum.TemplateOrder)
|
||
{
|
||
// есть ли связь у config с templetes, может инклуда нет, мало ли
|
||
if (configuration.Template == null)
|
||
{
|
||
logger.LogWarning("При изменении статуса задания на обновление шаблона, не смог проверить наличае ScheduleEsppId, так как нет Include с Templates. Пропустил эту проверку. configurationId: {configurationId}", configuration.Id);
|
||
}
|
||
else
|
||
{
|
||
if (configuration.Template.ScheduleEsppId == null)
|
||
{
|
||
logger.LogInformation("Нельзя установить статус {newStatus} для конфигурации {configurationId}, templateId: {templateId}, так как у шаблона отсутсвтует ScheduleEsppId=null",
|
||
updatingStatus, configuration.Id, configuration.TemplateId);
|
||
return false;
|
||
}
|
||
}
|
||
}
|
||
|
||
// Статус ОК, можно ставить Updating
|
||
ChangeTaskStatus(updatingStatus, configuration);
|
||
logger.LogInformation("Установлен статус {newStatus} для конфигурации {configurationId}, templateId: {templateId}", updatingStatus, configuration.Id, configuration.TemplateId);
|
||
|
||
return true;
|
||
}
|
||
|
||
|
||
public void ChangeRobotStatus(RobotStatusEnum robotStatus, RobotConfiguration configuration)
|
||
{
|
||
configuration.RobotStatusCode = (int)robotStatus;
|
||
|
||
switch (robotStatus)
|
||
{
|
||
case RobotStatusEnum.InProgress:
|
||
configuration.AttemptsNumber++;
|
||
configuration.LastRobotStatusUpdated = DateTimeOffset.UtcNow;
|
||
break;
|
||
case RobotStatusEnum.Error:
|
||
break;
|
||
case RobotStatusEnum.Complete:
|
||
configuration.LastRobotStatusUpdated = DateTimeOffset.UtcNow;
|
||
break;
|
||
case RobotStatusEnum.Wait:
|
||
configuration.LastRobotStatusUpdated = null;
|
||
configuration.AttemptsNumber = 0;
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
}
|
||
|
||
public void SetErrorRobotStatusAndMaxAttempts(RobotConfiguration configuration)
|
||
{
|
||
ChangeRobotStatus(RobotStatusEnum.Error, configuration);
|
||
|
||
configuration.AttemptsNumber = settingsFromDb.RobotAttemptsNumber;
|
||
configuration.LastRobotStatusUpdated = DateTimeOffset.UtcNow;
|
||
}
|
||
|
||
|
||
public async Task<bool> SetInProgressStatusAsync(Guid id)
|
||
{
|
||
// Если задание все еще в статусе Wait (RobotStatusEnum.Wait), установить ему статус "InProgress"
|
||
|
||
var endDate = DateTimeOffset.UtcNow.Add(-settingsFromDb.RobotWaitTime);
|
||
|
||
// Выполняем атомарный UPDATE напрямую в базе данных
|
||
var affectedRows = await EntitySet.Where(t => t.Id == id
|
||
&& (
|
||
// Задания в статусе ожидание
|
||
t.RobotStatusCode == (int)RobotStatusEnum.Wait
|
||
|| (
|
||
// Задание в статусе в работе, но они зависли
|
||
t.RobotStatusCode == (int)RobotStatusEnum.InProgress
|
||
&& t.AttemptsNumber < settingsFromDb.RobotAttemptsNumber
|
||
&& t.LastRobotStatusUpdated < endDate
|
||
)
|
||
)
|
||
)
|
||
.ExecuteUpdateAsync(s => s
|
||
.SetProperty(t => t.RobotStatusCode, (int)RobotStatusEnum.InProgress)
|
||
.SetProperty(t => t.LastRobotStatusUpdated, DateTimeOffset.UtcNow)
|
||
.SetProperty(t => t.AttemptsNumber, t => t.AttemptsNumber + 1)
|
||
);
|
||
|
||
return affectedRows > 0;
|
||
}
|
||
|
||
|
||
public RobotConfiguration GetFromTemplateByRobotCode(RobotsEnum robotsEnum, Template template)
|
||
{
|
||
var config = template.RobotConfigurations.FirstOrDefault(t => t.RobotCode == (int)robotsEnum);
|
||
|
||
if (config == null)
|
||
{
|
||
logger.LogError($"У шаблона нет конфигурации роботов. TemplateId: {template.Id}");
|
||
throw new Exception($"У шаблона нет конфигурации роботов. TemplateId: {template.Id}");
|
||
}
|
||
|
||
return config;
|
||
}
|
||
|
||
|
||
// Поиск невыполненных заданий и установка им статуса ошибки робота
|
||
public async Task MarkExpiredTasksAsFailedAsync(int robotAttemptsNumber, TimeSpan robotWaitTime)
|
||
{
|
||
//Ищем `RobotStatusCode` = 22 и `LastStatusUpdated` истекло и `AttemptsNumber` >= допустимого значения из настроек,
|
||
//ставим всем этим записям `RobotStatusCode`= 33
|
||
|
||
var endDate = DateTimeOffset.UtcNow.Add(-robotWaitTime);
|
||
|
||
var configObjs = await EntitySet.Where(t =>
|
||
t.RobotStatusCode == (int)RobotStatusEnum.InProgress
|
||
&& t.AttemptsNumber >= robotAttemptsNumber
|
||
&& t.LastRobotStatusUpdated <= endDate
|
||
).ToListAsync();
|
||
|
||
if (!configObjs.Any())
|
||
return;
|
||
|
||
configObjs.ForEach(item =>
|
||
{
|
||
ChangeRobotStatus(RobotStatusEnum.Error, item);
|
||
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();
|
||
|
||
if (!result)
|
||
logger.LogError("Ошибка при сохранении изменений RobotStatus для просроченных заданий в количестве {Count} шт.", configObjs.Count + linksCount);
|
||
//else
|
||
// logger.LogInformation("Успешно обработано и переведено в статус Ошибки просроченных заданий: {Count} шт.", configObjs.Count + linksCount);
|
||
|
||
}
|
||
|
||
|
||
}
|
||
}
|