DD.WellWorkover.Cloud/AsbCloudInfrastructure/Services/TelemetryService.cs

383 lines
16 KiB
C#
Raw Normal View History

2021-04-07 18:01:56 +05:00
using AsbCloudApp.Data;
using AsbCloudApp.Services;
using AsbCloudDb.Model;
using AsbCloudInfrastructure.Services.Cache;
2021-07-28 09:46:58 +05:00
using Mapster;
2021-09-29 10:12:54 +05:00
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
2021-09-29 10:12:54 +05:00
using System;
using System.Text.Json;
using System.Net.Http;
using System.Threading;
2021-09-29 10:12:54 +05:00
using Microsoft.EntityFrameworkCore;
using TimeZoneInfo = AsbCloudApp.Data.TimeZoneInfo;
2021-04-07 18:01:56 +05:00
namespace AsbCloudInfrastructure.Services
{
public class TelemetryService : ITelemetryService
{
private readonly CacheTable<Telemetry> cacheTelemetry;
private readonly CacheTable<Well> cacheWells;
private readonly CacheTable<Cluster> cacheClusters;
private readonly CacheTable<Deposit> cacheDeposits;
private readonly IAsbCloudDbContext db;
private readonly ITelemetryTracker telemetryTracker;
private readonly string timeZoneApiUrl = "http://api.geonames.org/timezoneJSON";
private readonly string timezoneApiUserName = "asbautodrilling";
2021-04-07 18:01:56 +05:00
public TelemetryService(IAsbCloudDbContext db, ITelemetryTracker telemetryTracker,
CacheDb cacheDb)
2021-04-07 18:01:56 +05:00
{
cacheTelemetry = cacheDb.GetCachedTable<Telemetry>((AsbCloudDbContext)db);
cacheWells = cacheDb.GetCachedTable<Well>((AsbCloudDbContext)db);
this.db = db;
this.telemetryTracker = telemetryTracker;
2021-04-07 18:01:56 +05:00
}
public IEnumerable<TelemetryDto> GetTransmittingTelemetriesAsync(int idCompany)
{
var telemetryDtos = new List<TelemetryDto>();
IEnumerable<string> activeTelemetriesUids = telemetryTracker.GetTransmittingTelemetriesUids();
if (activeTelemetriesUids.Any())
{
var telemetries = cacheTelemetry
.Where(t => activeTelemetriesUids.Contains(t.RemoteUid));
telemetryDtos = telemetries.Adapt<TelemetryDto>().ToList();
}
return telemetryDtos;
}
public void SaveRequestDate(string uid, DateTime remoteDate) =>
telemetryTracker.SaveRequestDate(uid, remoteDate);
2021-04-07 18:01:56 +05:00
public DateTime GetLastTelemetryDate(string telemetryUid) =>
telemetryTracker.GetLastTelemetryDateByUid(telemetryUid);
public DateTime GetLastTelemetryDate(int telemetryId)
{
var lastTelemetryDate = DateTime.MinValue;
var telemetry = cacheTelemetry.FirstOrDefault(t => t.Id == telemetryId);
if (telemetry is null)
return lastTelemetryDate;
var uid = telemetry.RemoteUid;
lastTelemetryDate = GetLastTelemetryDate(uid);
return lastTelemetryDate;
}
2021-04-23 10:21:25 +05:00
public int GetOrCreateTemetryIdByUid(string uid)
=> GetOrCreateTelemetryByUid(uid).Id;
2021-04-07 18:01:56 +05:00
2021-07-27 14:43:30 +05:00
public int? GetidWellByTelemetryUid(string uid)
2021-04-23 10:21:25 +05:00
=> GetWellByTelemetryUid(uid)?.Id;
public double GetTimezoneOffsetByTelemetryId(int idTelemetry) =>
cacheTelemetry.FirstOrDefault(t => t.Id == idTelemetry).Info?.TimeZoneOffsetTotalHours ?? 0d;
public async Task UpdateInfoAsync(string uid, TelemetryInfoDto info,
CancellationToken token)
{
2021-04-23 10:21:25 +05:00
var telemetry = GetOrCreateTelemetryByUid(uid);
2021-07-28 09:46:58 +05:00
telemetry.Info = info.Adapt<TelemetryInfo>();
var isTimeZoneToUpdate = telemetry.TelemetryTimeZone is null || telemetry.TelemetryTimeZone.IsOverride ||
(!telemetry.TelemetryTimeZone.IsOverride &&
telemetry.TelemetryTimeZone.Hours != info.TimeZoneOffsetTotalHours);
if (isTimeZoneToUpdate)
telemetry.TelemetryTimeZone = new TelemetryTimeZone()
{
Hours = info.TimeZoneOffsetTotalHours,
TimeZoneId = info.TimeZoneId
};
await cacheTelemetry.UpsertAsync(telemetry, token)
.ConfigureAwait(false);
}
public async Task<DateTime> FixDateToTimeZoneAsync(int idTelemetry, DateTime date,
CancellationToken token)
{
if(date.Kind == DateTimeKind.Utc)
return date;
if (date.Kind == DateTimeKind.Local)
return date.ToUniversalTime();
var telemetry =
await cacheTelemetry.FirstOrDefaultAsync(t => t.Id == idTelemetry, token);
if (telemetry is null)
return date;
if (telemetry.TelemetryTimeZone is null)
{
var well = await cacheWells.FirstOrDefaultAsync(w => w.IdTelemetry == telemetry.Id, token)
.ConfigureAwait(false);
if (well is null)
return date;
var requestedTimeZoneInfo = await GetTimeZoneInfoAsync(well.Id, token)
.ConfigureAwait(false);
if (requestedTimeZoneInfo.TimezoneId is null)
return date;
telemetry.TelemetryTimeZone = new TelemetryTimeZone()
{
Hours = requestedTimeZoneInfo.GmtOffset,
TimeZoneId = requestedTimeZoneInfo.TimezoneId
};
await cacheTelemetry.UpsertAsync(telemetry, token).ConfigureAwait(false);
}
var offsetHours = telemetry.TelemetryTimeZone.Hours;
return date.AddHours(offsetHours * -1);
}
public async Task<TimeZoneInfo> GetTimeZoneInfoAsync(int idWell, CancellationToken token)
{
var coordinates = await GetWellCoordinatesAsync(idWell, token);
if (coordinates is null)
return null;
using var client = new HttpClient();
var latitude = coordinates.Value.latitude.Replace(',', '.');
var longitude = coordinates.Value.longitude.Replace(',', '.');
var url =
$"{timeZoneApiUrl}?lat={latitude}&lng={longitude}&username={timezoneApiUserName}";
var response = await client.GetAsync(url, token).ConfigureAwait(false);
var responseJson = await response.Content.ReadAsStringAsync(token)
.ConfigureAwait(false);
var options = new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
};
var timeZoneInfo = JsonSerializer.Deserialize<TimeZoneInfo>(responseJson, options);
return timeZoneInfo;
}
public async Task UpdateTimeZoneAsync(string uid, TelemetryTimeZoneDto timeZoneInfo,
CancellationToken token)
{
var telemetry = GetOrCreateTelemetryByUid(uid);
telemetry.TelemetryTimeZone = timeZoneInfo.Adapt<TelemetryTimeZone>();
await cacheTelemetry.UpsertAsync(telemetry, token)
.ConfigureAwait(false);
2021-04-07 18:01:56 +05:00
}
public int? GetIdTelemetryByIdWell(int idWell)
{
2021-07-27 14:43:30 +05:00
var well = cacheWells.FirstOrDefault(w => w.Id == idWell);
if (well is null)
return null;
return well.IdTelemetry;
}
private async Task<(string latitude, string longitude)?> GetWellCoordinatesAsync(int idWell,
CancellationToken token)
{
var well = await cacheWells.FirstOrDefaultAsync(w => w.Id == idWell, token)
.ConfigureAwait(false);
if (well is null)
return null;
if (well.Latitude is not null && well.Longitude is not null)
return ($"{well.Latitude}", $"{well.Longitude}");
var cluster = await cacheClusters.FirstOrDefaultAsync(c => c.Id == well.IdCluster, token)
.ConfigureAwait(false);
if (cluster.Latitude is not null && cluster.Longitude is not null)
return ($"{cluster.Latitude}", $"{cluster.Longitude}");
var deposit = await cacheDeposits.FirstOrDefaultAsync(d => d.Id == cluster.IdDeposit, token)
.ConfigureAwait(false);
if (deposit.Latitude is not null && deposit.Longitude is not null)
return ($"{deposit.Latitude}", $"{deposit.Longitude}");
return null;
}
2021-04-23 10:21:25 +05:00
private Well GetWellByTelemetryUid(string uid)
2021-04-07 18:01:56 +05:00
{
var tele = cacheTelemetry.FirstOrDefault(t => t.RemoteUid == uid);
2021-04-23 10:21:25 +05:00
if (tele is null)
return null;
return cacheWells.FirstOrDefault(w => w?.IdTelemetry == tele.Id);
2021-04-07 18:01:56 +05:00
}
2021-04-23 10:21:25 +05:00
private Telemetry GetOrCreateTelemetryByUid(string uid)
=> cacheTelemetry.GetOrCreate(t => t.RemoteUid == uid, () => new Telemetry { RemoteUid = uid });
2021-09-29 10:12:54 +05:00
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<int> telemetryIds)
{
if (telemetryIds.Count() < 2)
throw new ArgumentException($"telemetryIds {telemetryIds} < 2. nothing to merge.", nameof(telemetryIds));
// найти телеметрию с наиболее полными справочниками и принять её за основную
// отделить основную от остальных
// Оценка трудоебкости
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 (RemoteUid, Info) = telemetriesGrade
2021-09-29 10:12:54 +05:00
.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 = RemoteUid;
telemetryDst.Info = Info;
2021-09-29 10:12:54 +05:00
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<int, TelemetryEvent>();
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<int, TelemetryUser>();
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.");
2021-09-29 10:12:54 +05:00
}
catch(Exception ex)
{
Console.WriteLine($"Fail. Rollback. Reason is:{ex.Message}");
2021-09-29 10:12:54 +05:00
transaction.Rollback();
return 0;
}
return rows;
}
2021-04-07 18:01:56 +05:00
}
}