using AsbCloudApp.Data; using AsbCloudApp.Exceptions; using AsbCloudApp.Services; using AsbCloudDb.Model; using AsbCloudInfrastructure.Services.Cache; using Mapster; using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; namespace AsbCloudInfrastructure.Services { public class WellService : CrudCacheServiceBase, IWellService { private static readonly TypeAdapterConfig typeAdapterConfig = TypeAdapterConfig .NewConfig() .Ignore(dst => dst.Cluster, dst => dst.RelationCompaniesWells, dst => dst.Telemetry, dst => dst.WellComposites, dst => dst.WellCompositeSrcs, dst => dst.WellOperations, dst => dst.WellType) .Config; private readonly ITelemetryService telemetryService; private readonly CacheTable cacheRelationCompaniesWells; private readonly CacheTable cacheCompanyWellTypes; private readonly ITimezoneService timezoneService; private readonly Lazy wellOperationService; public ITelemetryService TelemetryService => telemetryService; public WellService(IAsbCloudDbContext db, CacheDb cacheDb, ITelemetryService telemetryService, ITimezoneService timezoneService) : base(db, cacheDb) { this.telemetryService = telemetryService; this.timezoneService = timezoneService; this.wellOperationService = new Lazy(() => new WellOperationService.WellOperationService(db, cacheDb, this)); cacheRelationCompaniesWells = cacheDb.GetCachedTable((AsbCloudDbContext)db, nameof(RelationCompanyWell.Company), nameof(RelationCompanyWell.Well)); cacheCompanyWellTypes = cacheDb.GetCachedTable((AsbCloudDbContext)db); Includes.Add($"{nameof(Well.Cluster)}.{nameof(Cluster.Deposit)}"); Includes.Add(nameof(Well.Telemetry)); Includes.Add($"{nameof(Well.RelationCompaniesWells)}.{nameof(RelationCompanyWell.Company)}"); Includes.Add(nameof(Well.WellType)); } public DateTimeOffset GetLastTelemetryDate(int idWell) { var well = Cache.FirstOrDefault(w => w.Id == idWell); if (well?.IdTelemetry is null) return DateTimeOffset.MinValue; var lastTelemetryDate = telemetryService.GetLastTelemetryDate((int)well.IdTelemetry); return lastTelemetryDate; } public async Task> GetWellsByCompanyAsync(int idCompany, CancellationToken token) { var relations = await cacheRelationCompaniesWells .WhereAsync(r => r.IdCompany == idCompany, token); var wellsIds = relations.Select(r => r.IdWell); var wells = await Cache.WhereAsync(w => wellsIds.Contains(w.Id), token); var dtos = wells.Select(Convert); return dtos; } public override async Task InsertAsync(WellDto dto, CancellationToken token = default) { if (dto.IdWellType is < 1 or > 2) throw new ArgumentInvalidException("Тип скважины указан неправильно.", nameof(dto)); if (dto.IdState is < 0 or > 2) throw new ArgumentInvalidException("Текущее состояние работы скважины указано неправильно.", nameof(dto)); if (dto.Id != 0 && await Cache.ContainsAsync(w => w.Id == dto.Id, token)) throw new ArgumentInvalidException($"Нельзя повторно добавить скважину с id: {dto.Id}", nameof(dto)); var entity = Convert(dto); var result = await Cache.InsertAsync(entity, token); if (dto.Companies.Any()) { var newRelations = dto.Companies.Select(c => new RelationCompanyWell { IdWell = result.Id, IdCompany = c.Id }); await cacheRelationCompaniesWells.InsertAsync(newRelations, token); } return result.Id; } public override Task InsertRangeAsync(IEnumerable dtos, CancellationToken token) { throw new NotImplementedException(); } public override async Task UpdateAsync(int idWell, WellDto dto, CancellationToken token = default) { if (dto.IdWellType is < 1 or > 2) throw new ArgumentInvalidException("Тип скважины указан неправильно.", nameof(dto)); if (dto.IdState is < 0 or > 2) throw new ArgumentInvalidException("Текущее состояние работы скважины указано неправильно.", nameof(dto)); if (dto.Id != idWell) throw new ArgumentInvalidException($"Нельзя поменять id для скважины: {idWell} => {dto.Id}.", nameof(dto)); var entity = Convert(dto); var oldRelations = await cacheRelationCompaniesWells .WhereAsync(r => r.IdWell == idWell, token); if (dto.Companies.Count() != oldRelations.Count() || dto.Companies.Any(c => !oldRelations.Any(oldC => oldC.IdCompany == c.Id))) { await cacheRelationCompaniesWells.RemoveAsync(r => r.IdWell == idWell, token); var newRelations = dto.Companies.Select(c => new RelationCompanyWell { IdWell = idWell, IdCompany = c.Id }); await cacheRelationCompaniesWells.InsertAsync(newRelations, token); } var result = await Cache.UpsertAsync(entity, token); return result; } public bool IsCompanyInvolvedInWell(int idCompany, int idWell) => cacheRelationCompaniesWells.Contains(r => r.IdWell == idWell && r.IdCompany == idCompany); public async Task IsCompanyInvolvedInWellAsync(int idCompany, int idWell, CancellationToken token) => await cacheRelationCompaniesWells.ContainsAsync(r => r.IdWell == idWell && r.IdCompany == idCompany, token).ConfigureAwait(false); public async Task GetWellCaptionByIdAsync(int idWell, CancellationToken token) { var entity = await Cache.FirstOrDefaultAsync(w => w.Id == idWell, token).ConfigureAwait(false); var dto = Convert(entity); return dto.Caption; } public async Task> GetCompaniesAsync(int idWell, CancellationToken token) { var relations = await cacheRelationCompaniesWells.WhereAsync(r => r.IdWell == idWell, token); var dtos = relations.Select(r => Convert(r.Company)); return dtos; } private IEnumerable GetCompanies(int idWell) { var relations = cacheRelationCompaniesWells.Where(r => r.IdWell == idWell); var dtos = relations.Select(r => Convert(r.Company)); return dtos; } public string GetStateText(int state) { return state switch { 1 => "В работе", 2 => "Завершена", _ => "Неизвестно", }; } public async Task> GetClusterWellsIdsAsync(int idWell, CancellationToken token) { var well = await Cache.FirstOrDefaultAsync(w => w.Id == idWell, token) .ConfigureAwait(false); if (well is null) return null; var clusterWells = await Cache.WhereAsync(w => w.IdCluster == well.IdCluster, token) .ConfigureAwait(false); return clusterWells.Select(w => w.Id); } protected override Well Convert(WellDto dto) { var entity = dto.Adapt(typeAdapterConfig); entity.IdTelemetry = entity.IdTelemetry ?? dto.IdTelemetry ?? dto.Telemetry?.Id; if (dto.Timezone is null) if (TryGetTimezone(dto.Id, out var timezoneDto)) entity.Timezone = timezoneDto.Adapt(); return entity; } protected override WellDto Convert(Well entity) { if (entity is null) return null; var dto = base.Convert(entity); if (entity.Timezone is null) if (TryGetTimezone(entity, out var timezone)) dto.Timezone = timezone; dto.StartDate = wellOperationService.Value.FirstOperationDate(entity.Id)?.ToRemoteDateTime(dto.Timezone.Hours); dto.WellType = entity.WellType?.Caption; dto.Cluster = entity.Cluster?.Caption; dto.Deposit = entity.Cluster?.Deposit?.Caption; dto.LastTelemetryDate = GetLastTelemetryDate(entity.Id).DateTime; dto.Companies = GetCompanies(entity.Id); return dto; } private CompanyDto Convert(Company entity) { var dto = entity.Adapt(); dto.CompanyTypeCaption = entity.CompanyType?.Caption ?? cacheCompanyWellTypes.FirstOrDefault(c => c.Id == entity.IdCompanyType).Caption; return dto; } public void EnshureTimezonesIsSet() { var wells = Cache.Where(w => w.Timezone is null).ToList(); foreach (var well in wells) { if (TryGetTimezone(well, out var timezone)) well.Timezone = timezone.Adapt(); else well.Timezone = new SimpleTimezone { Hours = 5, IsOverride = false, TimeZoneId = "Assumed", }; } var wellsWithTz = wells.Where(w => w.Timezone is not null); if (wellsWithTz.Any()) { var adaptedWells = wellsWithTz.Adapt().Select(Convert); Cache.Upsert(adaptedWells); } } private bool TryGetTimezone(int idWell, out SimpleTimezoneDto timezone) { timezone = null; try { timezone = GetTimezone(idWell); return timezone is not null; } catch { return false; } } public SimpleTimezoneDto GetTimezone(int idWell) { var well = Cache.FirstOrDefault(c => c.Id == idWell); if (well == null) throw new ArgumentInvalidException($"idWell: {idWell} does not exist.", nameof(idWell)); return GetTimezone(well); } private bool TryGetTimezone(Well well, out SimpleTimezoneDto timezone) { timezone = null; try { timezone = GetTimezone(well); return timezone is not null; } catch { return false; } } private SimpleTimezoneDto GetTimezone(Well well) { if (well == null) throw new ArgumentNullException(nameof(well)); if (well.Timezone is not null) return well.Timezone.Adapt(); if (well.Telemetry is not null) { var timezone = telemetryService.GetTimezone(well.Telemetry.Id); if (timezone is not null) { well.Timezone = timezone.Adapt(); return timezone; } } var point = GetCoordinates(well); if (point is not null) { if (point.Timezone is not null) { well.Timezone = point.Timezone; return point.Timezone.Adapt(); } if (point.Latitude is not null & point.Longitude is not null) { var timezone = timezoneService.GetByCoordinates((double)point.Latitude, (double)point.Longitude); if (timezone is not null) { well.Timezone = timezone.Adapt(); return timezone; } } } throw new Exception($"Can't find timezone for well {well.Caption} id: {well.Id}"); } private static AsbCloudDb.Model.IMapPoint GetCoordinates(Well well) { if (well is null) throw new ArgumentNullException(nameof(well)); if (well.Latitude is not null & well.Longitude is not null) return well; if (well.Cluster is null) throw new Exception($"Can't find coordinates of well {well.Caption} id: {well.Id}"); var cluster = well.Cluster; if (cluster.Latitude is not null & cluster.Longitude is not null) return cluster; if (cluster.Deposit is null) throw new Exception($"Can't find coordinates of well by cluster {cluster.Caption} id: {cluster.Id}"); var deposit = cluster.Deposit; if (deposit.Latitude is not null & deposit.Longitude is not null) return deposit; throw new Exception($"Can't find coordinates of well by deposit {deposit.Caption} id: {deposit.Id}"); } public DatesRangeDto GetDatesRange(int idWell) { var well = Cache.FirstOrDefault(w => w.Id == idWell); if (well is null) throw new Exception($"Well id: {idWell} does not exist."); if (well.IdTelemetry is null) throw new Exception($"Well id: {idWell} does not contain telemetry."); return telemetryService.GetDatesRange((int)well.IdTelemetry); } } }