using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; using AsbCloudApp.Data; using AsbCloudApp.Data.AutogeneratedDailyReport; using AsbCloudApp.Data.Subsystems; using AsbCloudApp.Exceptions; using AsbCloudApp.Repositories; using AsbCloudApp.Requests; using AsbCloudApp.Services; using AsbCloudApp.Services.AutoGeneratedDailyReports; using AsbCloudApp.Services.Subsystems; using AsbCloudDb.Model; namespace AsbCloudInfrastructure.Services.AutoGeneratedDailyReports; public class AutoGeneratedDailyReportService : IAutoGeneratedDailyReportService { private const string fileNameTemplate = "Суточный_отчёт_по_скважине_{0}_куст_{1}_от_{2}.xlsx"; private readonly IWellService wellService; private readonly IWellOperationRepository wellOperationRepository; private readonly ISubsystemOperationTimeService subsystemOperationTimeService; private readonly ICrudRepository subsystemRepository; private readonly ILimitingParameterService limitingParameterService; private readonly IAutoGeneratedDailyReportMakerService autoGeneratedDailyReportMakerService; public AutoGeneratedDailyReportService(IWellService wellService, IWellOperationRepository wellOperationRepository, ISubsystemOperationTimeService subsystemOperationTimeService, ICrudRepository subsystemRepository, ILimitingParameterService limitingParameterService, IAutoGeneratedDailyReportMakerService autoGeneratedDailyReportMakerService) { this.wellOperationRepository = wellOperationRepository; this.wellService = wellService; this.subsystemOperationTimeService = subsystemOperationTimeService; this.subsystemRepository = subsystemRepository; this.limitingParameterService = limitingParameterService; this.autoGeneratedDailyReportMakerService = autoGeneratedDailyReportMakerService; } public async Task> GetListAsync(int idWell, AutoGeneratedDailyReportRequest request, CancellationToken cancellationToken) { var result = new PaginationContainer { Skip = request.Skip ?? 0, Take = request.Take ?? 10, Items = Enumerable.Empty() }; var reports = new List(); var well = await wellService.GetOrDefaultAsync(idWell, cancellationToken) ?? throw new ArgumentInvalidException(nameof(idWell), "Скважина не найдена"); if (!well.IdTelemetry.HasValue) throw new ArgumentInvalidException(nameof(idWell), "Телеметрия для скважины отсутствует"); var datesRange = await GetDatesRangeAsync(idWell, cancellationToken); if (datesRange is null) return result; if (request.StartDate.HasValue) { var startDate = new DateTime(request.StartDate.Value.Year, request.StartDate.Value.Month, request.StartDate.Value.Day); if(startDate.Date >= datesRange.From.Date) datesRange.From = startDate; } if (request.FinishDate.HasValue) { var finishDate = new DateTime(request.FinishDate.Value.Year, request.FinishDate.Value.Month, request.FinishDate.Value.Day); if (finishDate.Date <= datesRange.To.Date) datesRange.To = finishDate; } if (datesRange.From.AddDays(result.Skip) <= datesRange.To) result.Count = (int)(Math.Ceiling((datesRange.To - DateTime.UnixEpoch).TotalDays) - Math.Floor((datesRange.From - DateTime.UnixEpoch).TotalDays)); for (int day = result.Skip; (day - result.Skip) < result.Take && (datesRange.From.AddDays(day)) <= datesRange.To; day++) { var reportDate = DateOnly.FromDateTime(datesRange.From.AddDays(day)); reports.Add(new AutoGeneratedDailyReportInfoDto { FileName = string.Format(fileNameTemplate, well.Caption, well.Cluster, reportDate), ReportDate = reportDate, FileSize = GetFileSize(reportDate, idWell), }); } result.Items = reports; return result; } public async Task<(string fileName, Stream stream)> GenerateAsync(int idWell, DateOnly reportDate, CancellationToken cancellationToken) { var startDate = new DateTime(reportDate.Year, reportDate.Month, reportDate.Day); var finishDate = startDate.AddDays(1); var well = await wellService.GetOrDefaultAsync(idWell, cancellationToken) ?? throw new ArgumentInvalidException(nameof(idWell), "Скважина не найдена"); if (!well.IdTelemetry.HasValue) throw new ArgumentInvalidException(nameof(idWell), "Телеметрия для скважины отсутствует"); var factOperations = await GetFactOperationsAsync(well.Id, startDate, finishDate, cancellationToken); var report = new AutoGeneratedDailyReportDto { FileName = string.Format(fileNameTemplate, well.Caption, well.Cluster, reportDate), FileSize = GetFileSize(reportDate, idWell), ReportDate = reportDate, Head = CreateHeadBlock(well, factOperations), Subsystems = (await CreateSubsystemBlockAsync(idWell, startDate, finishDate, cancellationToken)).ToArray(), LimitingParameters = (await CreateLimitingParameterBlockAsync(idWell, startDate, finishDate, cancellationToken)).ToArray(), TimeBalance = factOperations.GroupBy(w => w.CategoryName).Select(x => new TimeBalanceRecordDto { Name = x.Key ?? "Название операции отсутствует", DurationHours = x.Sum(o => o.DurationHours) }).ToArray(), }; var stream = await autoGeneratedDailyReportMakerService.MakeReportAsync(report, cancellationToken); return (report.FileName, stream); } public async Task GetDatesRangeAsync(int idWell, CancellationToken cancellationToken) { var factOperations = await GetFactOperationsAsync(idWell, null, null, cancellationToken); if (!factOperations.Any()) return null; return new DatesRangeDto { From = factOperations.Min(o => o.DateStart).Date, To = factOperations.Max(o => o.DateStart).Date }; } private HeadBlockDto CreateHeadBlock(WellDto well, IEnumerable factOperations) { var customer = well.Companies.FirstOrDefault(company => company.IdCompanyType == 1); var sortedFactOperations = factOperations.OrderBy(o => o.DateStart); return new HeadBlockDto { Customer = customer?.Caption ?? string.Empty, Deposit = well.Deposit ?? string.Empty, Cluster = well.Cluster ?? string.Empty, Well = well.Caption, DepthFrom = sortedFactOperations.FirstOrDefault()?.DepthStart ?? 0.00, DepthTo = sortedFactOperations.LastOrDefault()?.DepthEnd ?? 0.00 }; } private async Task> CreateSubsystemBlockAsync(int idWell, DateTime startDate, DateTime finishDate, CancellationToken cancellationToken) { var subsystems = await subsystemRepository.GetAllAsync(cancellationToken); var subsystemStats = await GetSubsystemStatsAsync(idWell, startDate, finishDate, cancellationToken); return subsystems.Select(subsystem => { var subsytemStat = subsystemStats?.FirstOrDefault(s => s.IdSubsystem == subsystem.Id); return new SubsystemRecordDto { Name = subsystem.Name, KUsage = subsytemStat?.KUsage ?? 0.00, UsedTimeHours = subsytemStat?.UsedTimeHours ?? 0.00, Depth = subsytemStat?.SumDepthInterval ?? 0.00, }; }); } private async Task> CreateLimitingParameterBlockAsync(int idWell, DateTime startDate, DateTime finishDate, CancellationToken cancellationToken) { var limitingParameterStats = (await GetLimitingParameterStatsAsync(idWell, startDate, finishDate, cancellationToken)).ToArray(); var sumDepths = limitingParameterStats.Sum(x => x.Depth); return limitingParameterStats.Select(l => new LimitingParameterRecordDto { NameFeedRegulator = l.NameFeedRegulator, Hours = l.TotalMinutes, PercentDepth = sumDepths != 0 ? l.Depth / sumDepths : 0, Depth = l.Depth, }); } private async Task> GetFactOperationsAsync(int idWell, DateTime? startDate, DateTime? finishDate, CancellationToken cancellationToken) { var request = new WellOperationRequest { IdWell = idWell, OperationType = WellOperation.IdOperationTypeFact, GeDate = startDate, LtDate = finishDate, SortFields = new[] { "DateStart asc" }, }; return (await wellOperationRepository.GetAsync(request, cancellationToken)) .OrderBy(w => w.DateStart); } private Task> GetSubsystemStatsAsync(int idWell, DateTime startDate, DateTime finishDate, CancellationToken cancellationToken) { var request = new SubsystemOperationTimeRequest { IdWell = idWell, GtDate = startDate, LtDate = finishDate, }; return subsystemOperationTimeService.GetStatAsync(request, cancellationToken); } private Task> GetLimitingParameterStatsAsync(int idWell, DateTime startDate, DateTime finishDate, CancellationToken cancellationToken) { var request = new LimitingParameterRequest { IdWell = idWell, GtDate = startDate, LtDate = finishDate, }; return limitingParameterService.GetStatAsync(request, cancellationToken); } private int GetFileSize(DateOnly reportDate, int idWell) { const int fileSizeTemplate = 10240; long ticks = 1L * reportDate.Year * reportDate.Month * reportDate.Day * idWell; int remainder = (int)(ticks % (fileSizeTemplate / 10)); return fileSizeTemplate + remainder; } }