using Microsoft.EntityFrameworkCore.Storage.ValueConversion; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using PARR.Constants; using PARR.DAL.Contracts; using PARR.DAL.DomainServices.Interfaces; using PARR.DAL.Models; using PARR.DAL.Services.Interfaces; using System.Reflection; namespace PARR.EsppSync { internal class SyncService : ISyncService where EsppObject : class, IEsppObject { private readonly ILogger> logger; private readonly IServiceProvider serviceProvider; //private readonly ITemplateService templateService; //private readonly IRobotConfigurationService robotConfigurationService; //private readonly IShortcodesService shortcodesService; public SyncService( ILogger> logger, IServiceProvider serviceProvider //ITemplateService templateService, //IRobotConfigurationService robotConfigurationService, //IShortcodesService shortcodesService ) { this.logger = logger; this.serviceProvider = serviceProvider; //this.templateService = templateService; //this.robotConfigurationService = robotConfigurationService; //this.shortcodesService = shortcodesService; } public async Task SyncEsppObjectAsync( string str, ParserHandlerDelegate parser, ConvertDbObjToComparisonObjHandlerDelegate converterToEsppObject ) { logger.LogDebug($"Получил строку. Начинаю работать. Строка: {str}"); if (string.IsNullOrEmpty(str)) { logger.LogWarning("Получил пустую строку, ничего не делаю."); return; } var esppObject = parser.Invoke(str); if (esppObject == null) { logger.LogWarning("После парсинга строки, esppObject = null. Дальше ничего не буду делать."); return; } using (var scope = serviceProvider.CreateScope()) { var templateService = GetServiceInScope(scope); var robotConfigurationService = GetServiceInScope(scope); var shortcodesService = GetServiceInScope(scope); try { var template = await templateService.GetTemplateByNameAsync(esppObject.TemplateName); //todo: существует в ЕСПП но отсутствует в ПАРР. Может его деактивировать или еще что-то сделать. Пока просто пропустим if (template == null) { logger.LogWarning($"Найден объект в ЕСПП с именем шаблона {esppObject.TemplateName} незарегистрированный в ПАРР."); return; } else { var dbObjectInEsppObject = converterToEsppObject.Invoke(template); //Проверяем наличие Shortcode в полях объекта из БД var properties = dbObjectInEsppObject.GetType().GetProperties(); foreach (PropertyInfo property in properties) { var value = property.GetValue(dbObjectInEsppObject)?.ToString(); if (value != null && shortcodesService.isAnyShortcodes(value)) property.SetValue(dbObjectInEsppObject, await shortcodesService.ApplyShortcodesAsync(value, template.UnitId, template.JobId)); } bool isChanged = false; //TODO: FIX ME Please, BRO //bool isChanged; // если в БД isActive == false, то синхронизировать только по полям из IEsppObject if (dbObjectInEsppObject.IsActive == false) { logger.LogDebug($"Объект деактивирован в ПАРР. Сравниваем только обязательные поля. {esppObject.TemplateName}"); var lightDbObj = new EsppLightObject(dbObjectInEsppObject); var lightEsppObject = new EsppLightObject(esppObject); isChanged = IsChanged(lightEsppObject, lightDbObj, esppObject.TemplateName); } else { logger.LogDebug($"Объект активирован в ПАРР. Сравниваем все поля. {esppObject.TemplateName}"); isChanged = IsChanged(esppObject, dbObjectInEsppObject, esppObject.TemplateName); } if (isChanged) { logger.LogDebug($"Есть изменения, требуется обновление. {esppObject.TemplateName}"); var config = robotConfigurationService.GetFromTemplateByRobotCode(esppObject.Robot, template); // Если предыдущий статус был Create или Ок, то ставим ему Update // пусть даже Create завершился с ошибками, но раз он уже есть в ЕСПП, то изменим на Update, сбросим все счетчики и пусть попробует обновить и исправить все if (config.TaskStatusCode != (int)TaskStatusEnum.Updating) { SetUpdateStatus(ref template, robotConfigurationService, esppObject.Robot); if (!await templateService.CommitAsync()) logger.LogError($"Не удалось изменить запись Template {template.Name}, Robot: {esppObject.Robot}"); else logger.LogInformation($"Установлен принудительный статус {TaskStatusEnum.Updating}, Template {template.Name}, Robot: {esppObject.Robot}"); } else { // Если пред статус был Update, то ничего не делаем, так его и оставляем, не сбрасывам кол-во попыток и ошибок logger.LogInformation($"Есть изменения в Template {template.Name}, но предыдущий статус TaskStatusCode: {(TaskStatusEnum)config.TaskStatusCode}. Не меняем статус, будем разбираться вручную."); } #region Old logic //SetUpdateStatus(ref template, robotConfigurationService, esppObject.Robot); //if (!await templateService.CommitAsync()) // logger.LogError($"Не удалось изменить запись Template {template.Name}, Robot: {esppObject.Robot}"); //else // logger.LogInformation($"Установлен принудительный статус {TaskStatusEnum.Updating}, Template {template.Name}, Robot: {esppObject.Robot}"); #endregion }//надо ли проверять если не изменился, но был статус Updating не понятно. Доверяем роботу пока, что после окончания работ он точно сообщит else { logger.LogDebug($"Нет изменений, обновление не требуется. {esppObject.TemplateName}"); //если все поля совпали //проверяем, какой был статус предыдущий статус в БД, если он был не Ок, то ставим ему ОК var robotConfig = robotConfigurationService.GetFromTemplateByRobotCode(esppObject.Robot, template); if (robotConfig.TaskStatusCode != (int)TaskStatusEnum.Ok) { robotConfigurationService.ChangeTaskStatus(TaskStatusEnum.Ok, robotConfig); if (!await templateService.CommitAsync()) logger.LogError($"Не удалось изменить запись Template {template.Name}, Robot: {esppObject.Robot}"); else logger.LogInformation($"Установлен принудительный статус {TaskStatusEnum.Ok}, Template {template.Name}, Robot: {esppObject.Robot}"); } } } } catch (Exception ex) { logger.LogError(ex, $"Ошибка синхронизации объекта АСУ ЕСПП {esppObject.TemplateName}"); } } } private void SetUpdateStatus(ref Template template, IRobotConfigurationService robotConfigurationService, RobotsEnum robot) { var robotConfig = robotConfigurationService.GetFromTemplateByRobotCode(robot, template); robotConfigurationService.ChangeTaskStatus(TaskStatusEnum.Updating, robotConfig); } private Service GetServiceInScope(IServiceScope scope) { var service = scope.ServiceProvider.GetService(); if (service == null) throw new Exception($"Не найден сервис: {nameof(Service)}"); return service; } //private bool IsChanged(EsppObject esppObj, EsppObject dbObj) private bool IsChanged(object esppObj, object dbObj, string templateName) { foreach (var prop in dbObj.GetType().GetProperties()) { if (prop == null) continue; var dbValue = dbObj.GetType().GetProperty(prop.Name)?.GetValue(dbObj, null); var esppValue = esppObj.GetType().GetProperty(prop.Name)?.GetValue(esppObj, null); if (dbValue == null || esppValue == null) continue; //Replace("\r","").Replace("\n","") - в подробном описании могут быть переносы строк, в Rabbit прилетает без переносов. Убираем переносы для стравнения var dbValueStr = Normalize(dbValue!.ToString()!); var esppValueStr = Normalize(esppValue!.ToString()!); if (dbValueStr != esppValueStr) { logger.LogInformation($"Не совпадают поля ({prop.Name}). dbValueStr: {dbValueStr}, esppValueStr: {esppValueStr}. Имя шаблона: {templateName}"); return true; } } return false; } /// /// Удаляет ненужные символы из строки /// /// /// private string Normalize(string str) { str = str.Replace("\r", string.Empty); str = str.Replace("\n", string.Empty); str = str.Replace(" ", string.Empty); return str.ToLower(); } } }