using AsbCloudApp.Data; using AsbCloudApp.Services; using AsbCloudDb.Model; using AsbCloudInfrastructure.Services.Cache; using Mapster; using System.Collections.Generic; using System.Linq; using System; using Microsoft.EntityFrameworkCore; using System.Threading.Tasks; using System.Threading; namespace AsbCloudInfrastructure.Services { public class TelemetryService : ITelemetryService { private readonly CacheTable cacheTelemetry; private readonly CacheTable cacheWells; private readonly IAsbCloudDbContext db; public TelemetryService(IAsbCloudDbContext db, CacheDb cacheDb) { cacheTelemetry = cacheDb.GetCachedTable((AsbCloudDbContext)db); cacheWells = cacheDb.GetCachedTable((AsbCloudDbContext)db); this.db = db; } public int GetOrCreateTemetryIdByUid(string uid) => GetOrCreateTelemetryByUid(uid).Id; public int? GetidWellByTelemetryUid(string uid) => GetWellByTelemetryUid(uid)?.Id; public double GetTimezoneOffsetByTelemetryId(int idTelemetry) => cacheTelemetry.FirstOrDefault(t => t.Id == idTelemetry).Info?.TimeZoneOffsetTotalHours ?? 0d; public void UpdateInfo(string uid, TelemetryInfoDto info) { var telemetry = GetOrCreateTelemetryByUid(uid); telemetry.Info = info.Adapt(); cacheTelemetry.Upsert(telemetry); } public int? GetIdTelemetryByIdWell(int idWell) { var well = cacheWells.FirstOrDefault(w => w.Id == idWell); if (well is null) return null; return well.IdTelemetry; } private Well GetWellByTelemetryUid(string uid) { var tele = cacheTelemetry.FirstOrDefault(t => t.RemoteUid == uid); if (tele is null) return null; return cacheWells.FirstOrDefault(w => w?.IdTelemetry == tele.Id); } private Telemetry GetOrCreateTelemetryByUid(string uid) => cacheTelemetry.GetOrCreate(t => t.RemoteUid == uid, () => new Telemetry { RemoteUid = uid }); public IEnumerable<(string Key, int[] Ids)> GetRedundentRemoteUids() { return db.Telemetries .ToList() .GroupBy(t => t.RemoteUid) .Where(g => g.Count() > 1) .Select(g => (g.Key, g.Select(t=>t.Id).ToArray())); } public int Merge(IEnumerable telemetryIds) { if (telemetryIds.Count() < 2) throw new ArgumentException($"telemetryIds {telemetryIds} < 2. nothing to merge.", nameof(telemetryIds)); // найти телеметрию с наиболее полными справочниками и принять её за основную // отделить основную от остальных // связонные сущности: // Telemetry // TelemetryInfo // TelemetryEvent // TelemetryUser // TelemetryMessage // TelemetryDataSpin // TelemetryDataSaub // TelemetryAnalysis // Well // Оценка трудоебкости var telemetriesGrade = db.Telemetries .Include(t => t.Messages) .Include(t => t.DataSaub) .Include(t => t.DataSpin) .Include(t => t.Well) .Where(t => telemetryIds.Contains(t.Id)) .Select(t => new { t.Id, t.RemoteUid, t.Info, IdWell = t.Well != null ? t.Well.Id : int.MinValue, Records = t.Messages.Count + t.DataSaub.Count + t.DataSpin.Count, EventsAny = t.Events.Any(), UsersAny = t.Users.Any(), }) .OrderByDescending(t=>t.Records) .ToList(); var telemetryDestId = telemetriesGrade.FirstOrDefault().Id; if (telemetryDestId == default) return 0; var telemetriesSrcIds = telemetryIds.Where(t => t != telemetryDestId).ToList(); if(!telemetriesSrcIds.Any()) return 0; var telemetriesSrcIdsSql = $"({string.Join(',', telemetriesSrcIds)})"; var telemetryInfoAndUid = telemetriesGrade .Where(t => t.Info != null) .OrderByDescending(t => t.Id) .Select(t => (t.RemoteUid, t.Info)) .FirstOrDefault(); var wellId = telemetriesGrade .Where(t => t.IdWell > 0) .OrderByDescending(t => t.Id) .Select(t => t.IdWell) .FirstOrDefault(); // начало изменений Console.WriteLine($"Start merge telemetries ids: [{string.Join(',', telemetriesSrcIds)}] to {telemetryDestId}"); var sw = new System.Diagnostics.Stopwatch(); sw.Start(); var transaction = db.Database.BeginTransaction(); int rows = 0; try { var telemetryDst = db.Telemetries.FirstOrDefault(t => t.Id == telemetryDestId); telemetryDst.RemoteUid = telemetryInfoAndUid.RemoteUid; telemetryDst.Info = telemetryInfoAndUid.Info; if (wellId != default) { var well = db.Wells.FirstOrDefault(w => w.Id == wellId); well.IdTelemetry = telemetryDestId; } // events merge var telemetryDstEventsIds = db.TelemetryEvents.Where(t => t.IdTelemetry == telemetryDestId).Select(t => t.IdEvent).ToList(); var telemetrySrcEvents = db.TelemetryEvents .Where(t => telemetriesSrcIds.Contains(t.IdTelemetry) && !telemetryDstEventsIds.Contains(t.IdEvent)) .Select(t => new TelemetryEvent { IdTelemetry = telemetryDestId, IdEvent = t.IdEvent, IdCategory = t.IdCategory, MessageTemplate = t.MessageTemplate, }) .ToList(); var telemetryEventNewUniq = new Dictionary(); foreach (var telemetryEvent in telemetrySrcEvents) telemetryEventNewUniq[telemetryEvent.IdEvent] = telemetryEvent; if (telemetrySrcEvents.Any()) db.TelemetryEvents.AddRange(telemetryEventNewUniq.Values); // users merge var telemetryDstUsersIds = db.TelemetryUsers.Where(t => t.IdTelemetry == telemetryDestId).Select(t => t.IdUser).ToList(); var telemetrySrcUsers = db.TelemetryUsers .Where(t => telemetriesSrcIds.Contains(t.IdTelemetry) && !telemetryDstUsersIds.Contains(t.IdUser)) .Select(t => new TelemetryUser { IdTelemetry = telemetryDestId, IdUser = t.IdUser, Level = t.Level, Name = t.Name, Patronymic = t.Patronymic, Surname = t.Surname, }).ToList(); var telemetryUserNewUniq = new Dictionary(); foreach (var telemetryUser in telemetrySrcUsers) telemetryUserNewUniq[telemetryUser.IdUser] = telemetryUser; if (telemetrySrcUsers.Any()) db.TelemetryUsers.AddRange(telemetryUserNewUniq.Values); db.SaveChanges(); db.Database.SetCommandTimeout(3_000); // 5 мин db.Database.ExecuteSqlRaw($"ALTER TABLE t_telemetry_data_saub DISABLE TRIGGER ALL;"); rows += db.Database.ExecuteSqlRaw($"UPDATE t_telemetry_data_saub SET id_telemetry = {telemetryDestId} WHERE id_telemetry IN {telemetriesSrcIdsSql};"); db.Database.ExecuteSqlRaw($"ALTER TABLE t_telemetry_data_saub ENABLE TRIGGER ALL;"); db.Database.ExecuteSqlRaw($"ALTER TABLE t_telemetry_data_spin DISABLE TRIGGER ALL;"); rows += db.Database.ExecuteSqlRaw($"UPDATE t_telemetry_data_spin SET id_telemetry = {telemetryDestId} WHERE id_telemetry IN {telemetriesSrcIdsSql};"); db.Database.ExecuteSqlRaw($"ALTER TABLE t_telemetry_data_spin ENABLE TRIGGER ALL;"); db.Database.ExecuteSqlRaw($"ALTER TABLE t_telemetry_analysis DISABLE TRIGGER ALL;"); rows += db.Database.ExecuteSqlRaw($"UPDATE t_telemetry_analysis SET id_telemetry = {telemetryDestId} WHERE id_telemetry IN {telemetriesSrcIdsSql};"); db.Database.ExecuteSqlRaw($"ALTER TABLE t_telemetry_analysis ENABLE TRIGGER ALL;"); db.Database.ExecuteSqlRaw($"ALTER TABLE t_telemetry_message DISABLE TRIGGER ALL;"); rows += db.Database.ExecuteSqlRaw($"UPDATE t_telemetry_message SET id_telemetry = {telemetryDestId} WHERE id_telemetry IN {telemetriesSrcIdsSql};"); db.Database.ExecuteSqlRaw($"ALTER TABLE t_telemetry_message ENABLE TRIGGER ALL;"); rows += db.Database.ExecuteSqlRaw($"DELETE FROM t_telemetry_event WHERE id_telemetry IN {telemetriesSrcIdsSql};"); rows += db.Database.ExecuteSqlRaw($"DELETE FROM t_telemetry_user WHERE id_telemetry IN {telemetriesSrcIdsSql};"); rows += db.Database.ExecuteSqlRaw($"DELETE FROM t_telemetry WHERE id IN {telemetriesSrcIdsSql};"); transaction.Commit(); sw.Stop(); Console.WriteLine($"Successfully commited in {1d*sw.ElapsedMilliseconds/1000d: #0.00} sec. Affected {rows} rows."); } catch(Exception ex) { Console.WriteLine($"Fail. Rollback. Reason is:{ex.Message}"); transaction.Rollback(); return 0; } return rows; } } }