diff --git a/AsbCloudApp/Data/WellMapInfoDto.cs b/AsbCloudApp/Data/WellMapInfoDto.cs index 967afcca..392d1877 100644 --- a/AsbCloudApp/Data/WellMapInfoDto.cs +++ b/AsbCloudApp/Data/WellMapInfoDto.cs @@ -2,6 +2,7 @@ namespace AsbCloudApp.Data { +#nullable enable /// /// Инфо о скважине для отображения на карте /// @@ -22,7 +23,7 @@ namespace AsbCloudApp.Data /// /// Плановая и текущая глубина /// - public PlanFactDto WellDepth { get; set; } + public PlanFactBase WellDepth { get; set; } = null!; /// /// Отставание от ГГД, % @@ -32,12 +33,12 @@ namespace AsbCloudApp.Data /// /// Механическая скорость проходки, последней операции бурения /// - public PlanFactDto ROP { get; set; } + public PlanFactBase ROP { get; set; } = null!; /// /// Рейсовая скорость проходки, последнего рейса /// - public PlanFactDto RaceSpeed { get; set; } + public PlanFactBase RaceSpeed { get; set; } = null!; /// /// Процент использования АКБ @@ -49,4 +50,5 @@ namespace AsbCloudApp.Data /// public double SpinUsage { get; set; } } +#nullable disable } diff --git a/AsbCloudApp/Requests/WellRequest.cs b/AsbCloudApp/Requests/WellRequest.cs index feb7259a..0d7d6e85 100644 --- a/AsbCloudApp/Requests/WellRequest.cs +++ b/AsbCloudApp/Requests/WellRequest.cs @@ -1,4 +1,6 @@ -namespace AsbCloudApp.Requests +using System.Collections.Generic; + +namespace AsbCloudApp.Requests { #nullable enable /// @@ -15,6 +17,11 @@ /// id состояния /// public int? IdState { get; set; } + + /// + /// Идентификаторы скважин + /// + public IEnumerable? Ids { get; set; } } #nullable disable } diff --git a/AsbCloudApp/Services/IWellService.cs b/AsbCloudApp/Services/IWellService.cs index 6bd3da44..7ea8a20c 100644 --- a/AsbCloudApp/Services/IWellService.cs +++ b/AsbCloudApp/Services/IWellService.cs @@ -103,7 +103,6 @@ namespace AsbCloudApp.Services /// /// /// -#warning GetWellTreeAsync(..) is dummy. Remove it before pullrequest. Task> GetWellTreeAsync(int idCompany, CancellationToken token); } #nullable disable diff --git a/AsbCloudApp/Services/Subsystems/ISubsystemOperationTimeService.cs b/AsbCloudApp/Services/Subsystems/ISubsystemOperationTimeService.cs index 91b78023..8df7ee07 100644 --- a/AsbCloudApp/Services/Subsystems/ISubsystemOperationTimeService.cs +++ b/AsbCloudApp/Services/Subsystems/ISubsystemOperationTimeService.cs @@ -47,6 +47,7 @@ namespace AsbCloudApp.Services.Subsystems /// /// Task GetDateRangeOperationTimeAsync(SubsystemOperationTimeRequest request, CancellationToken token); + /// /// Получение статистики по наработке подсистем по активным скважинам /// @@ -56,6 +57,14 @@ namespace AsbCloudApp.Services.Subsystems /// /// Task> GetStatByActiveWells(int idCompany, DateTime? gtDate, DateTime? ltDate, CancellationToken token); + + /// + /// Получение статистики по наработке подсистем по активным скважинам + /// + /// + /// + /// + Task> GetStatByActiveWells(IEnumerable wellIds, CancellationToken token); } #nullable disable } diff --git a/AsbCloudDb/Model/Well.cs b/AsbCloudDb/Model/Well.cs index 74345480..df011310 100644 --- a/AsbCloudDb/Model/Well.cs +++ b/AsbCloudDb/Model/Well.cs @@ -67,5 +67,8 @@ namespace AsbCloudDb.Model [InverseProperty(nameof(DrillingProgramPart.Well))] public virtual ICollection DrillingProgramParts { get; set; } + + [InverseProperty(nameof(ProcessMap.Well))] + public virtual ICollection ProcessMaps { get; set; } } } diff --git a/AsbCloudInfrastructure/Services/Subsystems/SubsystemOperationTimeService.cs b/AsbCloudInfrastructure/Services/Subsystems/SubsystemOperationTimeService.cs index 280b0d92..f74100bf 100644 --- a/AsbCloudInfrastructure/Services/Subsystems/SubsystemOperationTimeService.cs +++ b/AsbCloudInfrastructure/Services/Subsystems/SubsystemOperationTimeService.cs @@ -187,20 +187,27 @@ namespace AsbCloudInfrastructure.Services.Subsystems return depthIntervalSubsystem; } - - private async Task> GetActiveWellsByCompany(int idCompany, CancellationToken token) - { - var listWell = await wellService.GetAsync(new() { IdCompany = idCompany }, token); - var active = listWell.Where(w => w.IdState == 1); - return active; - } /// public async Task> GetStatByActiveWells(int idCompany, DateTime? gtDate, DateTime? ltDate, CancellationToken token) - { - var wells = await GetActiveWellsByCompany(idCompany, token); + { + var activeWells = await wellService.GetAsync(new() { IdCompany = idCompany, IdState = 1 }, token); + var result = await GetStatAsync(activeWells, gtDate, ltDate, token); + return result; + } + + /// + public async Task> GetStatByActiveWells(IEnumerable wellIds, CancellationToken token) + { + var activeWells = await wellService.GetAsync(new() { Ids = wellIds, IdState = 1 }, token); + var result = await GetStatAsync(activeWells, null, null, token); + return result; + } + + private async Task> GetStatAsync(IEnumerable wells, DateTime? gtDate, DateTime? ltDate, CancellationToken token) + { if (!wells.Any()) - return Enumerable.Empty(); + return Enumerable.Empty(); var hoursOffset = wells .FirstOrDefault(well => well.Timezone is not null) @@ -209,7 +216,7 @@ namespace AsbCloudInfrastructure.Services.Subsystems var beginUTC = gtDate.HasValue ? gtDate.Value.ToUtcDateTimeOffset(hoursOffset) - :DateTime.Today.AddDays(-1).ToUtcDateTimeOffset(hoursOffset); + : DateTime.Today.AddDays(-1).ToUtcDateTimeOffset(hoursOffset); var endUTC = ltDate.HasValue ? ltDate.Value.ToUtcDateTimeOffset(hoursOffset) @@ -243,7 +250,7 @@ namespace AsbCloudInfrastructure.Services.Subsystems var subsystemStat = idTelemetry > 0 && dtos.Any() ? CalcStat(dtos, (depthIntervalRotor, depthIntervalSlide)) : Enumerable.Empty(); - + return new SubsystemActiveWellStatDto { Well = well, diff --git a/AsbCloudInfrastructure/Services/WellInfoService.cs b/AsbCloudInfrastructure/Services/WellInfoService.cs new file mode 100644 index 00000000..b8b6128a --- /dev/null +++ b/AsbCloudInfrastructure/Services/WellInfoService.cs @@ -0,0 +1,154 @@ +using AsbCloudApp.Data; +using AsbCloudApp.Data.ProcessMap; +using AsbCloudApp.Repositories; +using AsbCloudApp.Requests; +using AsbCloudApp.Services; +using AsbCloudApp.Services.Subsystems; +using AsbCloudDb.Model; +using AsbCloudInfrastructure.Background; +using Mapster; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace AsbCloudInfrastructure.Services +{ +#nullable enable + public class WellInfoService + { + class WellMapInfoWithComanies : WellMapInfoDto + { + public IEnumerable IdsCompanies { get; set; } = null!; + } + + private const string workId = "Well statistics update"; + private static readonly TimeSpan workPeriod = TimeSpan.FromMinutes(30); + + private static IEnumerable WellMapInfo = Enumerable.Empty(); + + public static WorkPeriodic MakeWork() + { + var workPeriodic = new WorkPeriodic(workId, WorkAction, workPeriod) + { + Timeout = TimeSpan.FromMinutes(30) + }; + return workPeriodic; + } + + private static async Task WorkAction(string workName, IServiceProvider serviceProvider, CancellationToken token) + { + var db = serviceProvider.GetRequiredService(); + var wellService = serviceProvider.GetRequiredService(); + var operationsStatService = serviceProvider.GetRequiredService(); + var processMapRepository = serviceProvider.GetRequiredService(); + var subsystemOperationTimeService = serviceProvider.GetRequiredService(); + + var activeWells = await wellService.GetAsync(new() {IdState = 1}, token); + + IEnumerable activeWellsIds = activeWells + .Select(w => w.Id); + + var idTelemetries = activeWells + .Where(w => w.IdTelemetry != null) + .Select(t => t.IdTelemetry); + + var lastTelemetryInfo = await db.TelemetryDataSaub + .Where(t => idTelemetries.Contains(t.IdTelemetry)) + .Select(t => new + { + t.IdTelemetry, + t.WellDepth, + t.DateTime, + }) + .GroupBy(t => t.IdTelemetry) + .Select(g => g.OrderByDescending(t => t.DateTime) + .First() + ) + .AsNoTracking() + .ToArrayAsync(token); + + var processMapRequests = activeWellsIds.Select(id => new ProcessMapRequest { IdWell = id }); + var processMaps = await processMapRepository.GetProcessMapAsync(processMapRequests, token); + + var wellDepthByProcessMap = processMaps + .GroupBy(p => p.IdWell) + .Select(g => new + { + Id = g.Key, + DepthEnd = g.Max(p => p.DepthEnd) + }); + + var operationsStat = await operationsStatService.GetWellsStatAsync(activeWellsIds, token); + var subsystemStat = await subsystemOperationTimeService.GetStatByActiveWells(activeWellsIds, token); + + WellMapInfo = activeWells.Select(well => { + var wellMapInfo = well.Adapt(); + + // From teltemetryTracker + var wellLastTelemetryInfo = lastTelemetryInfo.FirstOrDefault(t => t.IdTelemetry == well.IdTelemetry); + + var wellOperationsStat = operationsStat.FirstOrDefault(s => s.Id == well.Id); + var wellLastFactSection = wellOperationsStat?.Sections.LastOrDefault(s => s.Fact is not null); + + var wellSubsystemStat = subsystemStat.FirstOrDefault(s => s.Well.Id == well.Id); + + double currentDepth = wellLastTelemetryInfo?.WellDepth + ?? wellLastFactSection?.Fact.WellDepthEnd + ?? 0d; + + var wellProcessMaps = processMaps + .Where(p => p.IdWell == well.Id) + .OrderBy(p => p.DepthEnd); + + int? idSection = wellLastFactSection?.Id; + + ProcessMapDto? welllProcessMap; + if (idSection is not null) + { + welllProcessMap = wellProcessMaps.FirstOrDefault(p => p.IdWellSectionType == idSection); + } + else + { + welllProcessMap = wellProcessMaps.FirstOrDefault(p => p.DepthStart <= currentDepth && p.DepthEnd >= currentDepth); + idSection ??= welllProcessMap?.IdWellSectionType; + } + + wellMapInfo.LastTelemetryDate = wellLastTelemetryInfo?.DateTime.ToRemoteDateTime(5) ?? new DateTime(); + wellMapInfo.WellDepth = new() + { + Plan = wellDepthByProcessMap.FirstOrDefault(p => p.Id == well.Id)?.DepthEnd, + Fact = currentDepth, + }; + + wellMapInfo.ROP = new() + { + Plan = welllProcessMap?.RopPlan, + Fact = wellOperationsStat?.Total.Fact?.Rop, + }; + + wellMapInfo.RaceSpeed = new() + { + Plan = wellOperationsStat?.Total.Plan?.RouteSpeed, + Fact = wellOperationsStat?.Total.Fact?.RouteSpeed, + }; + + wellMapInfo.SaubUsage = wellSubsystemStat?.SubsystemAKB?.KUsage ?? 0d; + wellMapInfo.SpinUsage = wellSubsystemStat?.SubsystemSpinMaster?.KUsage ?? 0d; + wellMapInfo.TvdLagPercent = 0;// From WellOperationService? + wellMapInfo.IdsCompanies = well.Companies.Select(c => c.Id); + return wellMapInfo; + }).ToArray(); + } + + public static IEnumerable Where(Func predicate) + => WellMapInfo.Where(predicate); + + public static WellMapInfoDto? FirstOrDefault(Func predicate) + => WellMapInfo.FirstOrDefault(predicate); + } +#nullable disable +} diff --git a/AsbCloudInfrastructure/Services/WellService.cs b/AsbCloudInfrastructure/Services/WellService.cs index a29d1573..8023fe41 100644 --- a/AsbCloudInfrastructure/Services/WellService.cs +++ b/AsbCloudInfrastructure/Services/WellService.cs @@ -69,7 +69,7 @@ namespace AsbCloudInfrastructure.Services return lastTelemetryDate; } -#warning GetWellTreeAsync(..) is dummy. Remove it before pullrequest. + /// public async Task> GetWellTreeAsync(int idCompany, CancellationToken token) { var wells = await GetEntitiesAsync(new() { IdCompany = idCompany }, token); @@ -93,15 +93,13 @@ namespace AsbCloudInfrastructure.Services Longitude = gCluster.Key.Longitude ?? gDeposit.Key.Longitude, Wells = gCluster.Select(well => { - var dto = well.Adapt(); - if (dto.Latitude is null) - dto.Latitude = gCluster.Key.Latitude ?? gDeposit.Key.Latitude; - - if (dto.Longitude is null) - dto.Longitude = gCluster.Key.Longitude ?? gDeposit.Key.Longitude; - + var dto = WellInfoService.FirstOrDefault(w => w.Id == well.Id); + dto ??= well.Adapt(); + dto.Latitude ??= gCluster.Key.Latitude ?? gDeposit.Key.Latitude; + dto.Longitude ??= gCluster.Key.Longitude ?? gDeposit.Key.Longitude; if (well.IdTelemetry is not null) dto.LastTelemetryDate = telemetryService.GetLastTelemetryDate(well.IdTelemetry.Value); + return dto; }), }), @@ -120,10 +118,12 @@ namespace AsbCloudInfrastructure.Services { var wells = await GetCacheAsync(token); + if (request.Ids?.Any() == true) + wells = wells.Where(well => request.Ids.Contains(well.Id)); + if (request.IdCompany.HasValue) wells = wells.Where(well => well.RelationCompaniesWells.Any(r => r.IdCompany == request.IdCompany.Value)); - if (request.IdState.HasValue) wells = wells.Where(well => well.IdState == request.IdState.Value); diff --git a/AsbCloudInfrastructure/Startup.cs b/AsbCloudInfrastructure/Startup.cs index 36a85cc1..e93dc5e5 100644 --- a/AsbCloudInfrastructure/Startup.cs +++ b/AsbCloudInfrastructure/Startup.cs @@ -27,7 +27,8 @@ namespace AsbCloudInfrastructure var wellService = provider.GetRequiredService(); wellService.EnshureTimezonesIsSetAsync(CancellationToken.None).Wait();// TODO: make this background work - var backgroundWorker = provider.GetRequiredService(); + var backgroundWorker = provider.GetRequiredService(); + backgroundWorker.Push(WellInfoService.MakeWork()); backgroundWorker.Push(OperationDetectionWorkFactory.MakeWork()); backgroundWorker.Push(SubsystemOperationTimeCalcWorkFactory.MakeWork()); backgroundWorker.Push(LimitingParameterCalcWorkFactory.MakeWork()); diff --git a/ConsoleApp1/Program.cs b/ConsoleApp1/Program.cs index 0336fb7e..9c018348 100644 --- a/ConsoleApp1/Program.cs +++ b/ConsoleApp1/Program.cs @@ -1,11 +1,17 @@ -using iTextSharp.text.pdf; -using System; +using System; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Threading; using System.Threading.Tasks; -using Document = iTextSharp.text.Document; +using AsbCloudApp.Data; +using AsbCloudApp.Services; +using AsbCloudDb.Model; +using AsbCloudInfrastructure; using CliWrap; +using Mapster; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; namespace ConsoleApp1 { @@ -13,160 +19,20 @@ namespace ConsoleApp1 { static void Main(/*string[] args*/) { - // string[] fileExtension = { ".xlsx", ".xls", ".ods", ".odt", ".doc", ".docx", ".pdf" }; - // Console.WriteLine($"start convert"); - // var inputFiles = new List(); - // var resultFile = "C:\\Test\\result.pdf"; - // inputFiles.Add("11112222.docx"); - // inputFiles.Add("11117777.pdf"); - // inputFiles.Add("22223333.xls"); - // inputFiles.Add("33334444.xlsx"); - // //inputFiles.Add("33334444.tts"); - - // var listOutNames = new List(); - // var filteredFilesNames = inputFiles - // .Distinct() - // .Where(f => fileExtension.Any(fe => f.ToLower().EndsWith(fe))) - // .ToList(); - // FileInfo fileInfo = new FileInfo(resultFile); - - // //matchesExtensions(inputFiles); - // foreach (var FileName in inputFiles) - // { - // var outputFile = Path.ChangeExtension(FileName, ".pdf"); - // var outFile = StartConvertProcessAsync(FileName, outputFile); - // Console.WriteLine($"convert file - {FileName}"); - // Console.ReadLine(); - // listOutNames.Add(outFile.Result.ToString()); - // } - // Console.WriteLine("merged files"); - // Console.ReadLine(); - // DoMerged(listOutNames, resultFile); - - - - //static void matchesExtensions(List inputFiles) - // { - // string[] fileExtension = { ".xlsx", ".xls", ".ods", ".odt", ".doc", ".docx", ".pdf" }; - // foreach (var file in inputFiles) - // { - // var fileExt = Path.GetExtension(file); - // if (fileExtension.All(fe => fileExt != fe)) - // { - // throw new FileFormatException($"Файл с именем: {file} не может быть добавлен в список файлов для конвертации и слияния в общий файл программы бурения. Не поддерживаемый формат файла"); - // } - - // } - // } - if (OperatingSystem.IsWindows()) - { - Console.WriteLine("win"); - Console.ReadLine(); - } - if (OperatingSystem.IsLinux()) - { - Console.WriteLine("linux"); - Console.ReadLine(); - } - - } - - public static void DoMerged(IEnumerable inputFiles, string outFile) - { - using var stream = new FileStream(outFile, FileMode.Create); - using var doc = new Document(); - using var pdf = new PdfCopy(doc, stream); - doc.Open(); - var inputFilesList = inputFiles.ToList(); - foreach (var file in inputFilesList) - { - var reader = new PdfReader(file); - for (int i = 0; i < reader.NumberOfPages; i++) - { - PdfImportedPage page = pdf.GetImportedPage(reader, i + 1); - pdf.AddPage(page); - } - pdf.FreeReader(reader); - reader.Close(); - }; - } - - private static (string programFile, string programArg) getOptionsStartupProcess(string inputFileName, string resultFileDir) - { - (string programFile, string programArg) startupOptions; - if (OperatingSystem.IsWindows()) - { - startupOptions.programFile = "C:\\Program Files\\LibreOffice\\program\\soffice.exe"; - startupOptions.programArg = $"-headless -convert-to pdf {inputFileName} --outdir {resultFileDir}"; - return startupOptions; - } - if (OperatingSystem.IsLinux()) - { - startupOptions.programFile = "/usr/bin/soffice"; - startupOptions.programArg = $"--headless --convert-to pdf {inputFileName} --outdir {resultFileDir}"; - return (startupOptions); - } - - throw new NotSupportedException("Вызов процесса в текущей операционной системе не возможен"); - } - - //public static void StartConvertProcess(string inputFileName, string outFileName) - //{ - // using (Process pdfprocess = new Process()) - // { - // pdfprocess.StartInfo.UseShellExecute = true; - // //pdfprocess.StartInfo.LoadUserProfile = true; - // pdfprocess.StartInfo.FileName = "soffice"; - // pdfprocess.StartInfo.Arguments = $"--headless --convert-to pdf {inputFileName} --outdir {outFileName}"; - // pdfprocess.StartInfo.WorkingDirectory = "/usr/bin"; - // pdfprocess.StartInfo.WindowStyle = ProcessWindowStyle.Hidden; - // pdfprocess.Start(); - // if (!pdfprocess.WaitForExit(1000 * 60 * 1)) - // { - // pdfprocess.Kill(); - // } - // pdfprocess.Close(); - // } - //} - private static async Task StartConvertProcessAsync(string inputFileName, string outFileName) - { - - var progrAndArg = getOptionsStartupProcess(inputFileName, outFileName); - - //string outPath = "/home/eddie/Test/OutFiles"; - string outPath = "C:\\Test\\OutFiles"; - var result = Cli.Wrap("C:\\Program Files\\LibreOffice\\program\\soffice.exe") - .WithArguments($"-headless -convert-to pdf C:\\Test\\InFiles\\{inputFileName} -outdir {outPath}"); - await result.ExecuteAsync(); - var outFile = $"{outPath}\\{outFileName}"; - return outFile; - } - - public static async Task GetConverteAndMergedFileAsync(IEnumerable filesNames, string resultPath) - { - string[] fileExtension = { ".xlsx", ".xls", ".ods", ".odt", ".doc", ".docx", ".pdf" }; - //var filteredFilesNames = filesNames.Distinct(); - var filteredFilesNames = filesNames - .Distinct() - .Where(f => fileExtension.Any(fe => f.ToLower().EndsWith(fe))) - .ToList(); - var listFileNames = filteredFilesNames - .ToList() - .Select(o => new { - inputFile = o, - convertedFile = Path.ChangeExtension(o, ".pdf") + var db = ServiceFactory.Context; + var q = db.TelemetryDataSaub + .Select(t => new { + t.IdTelemetry, + t.DateTime, + t.WellDepth, + }) + .GroupBy(t => t.IdTelemetry) + .Select(g => new { + Id = g.Key, + First = g.OrderBy(t => t.DateTime).FirstOrDefault(), + Last = g.OrderBy(t => t.DateTime).LastOrDefault(), }); - foreach (var excelFileName in listFileNames) - { - await StartConvertProcessAsync(excelFileName.inputFile, excelFileName.convertedFile); - Console.WriteLine($"convert file - {excelFileName.inputFile}"); - Console.ReadLine(); - } - - Console.WriteLine("merged files"); - Console.ReadLine(); - DoMerged(listFileNames.Select(c => c.convertedFile), resultPath); - - } + var d = q.AsNoTracking().ToArray(); + } } }