diff --git a/AsbCloudApp/Services/IDetectedOperationService.cs b/AsbCloudApp/Services/IDetectedOperationService.cs
index 120cfef8..1117d8e3 100644
--- a/AsbCloudApp/Services/IDetectedOperationService.cs
+++ b/AsbCloudApp/Services/IDetectedOperationService.cs
@@ -1,9 +1,7 @@
using AsbCloudApp.Data;
using AsbCloudApp.Data.DetectedOperation;
using AsbCloudApp.Requests;
-using System;
using System.Collections.Generic;
-using System.IO;
using System.Threading;
using System.Threading.Tasks;
@@ -62,13 +60,5 @@ namespace AsbCloudApp.Services
///
///
Task?> GetOperationsStatAsync(DetectedOperationRequest request, CancellationToken token);
-
- ///
- /// Выгрузка в Excel
- ///
- ///
- ///
- ///
- Task ExportAsync(IEnumerable idsWells, CancellationToken token);
}
}
diff --git a/AsbCloudInfrastructure/AsbCloudInfrastructure.csproj b/AsbCloudInfrastructure/AsbCloudInfrastructure.csproj
index bc825b4b..0ed4b266 100644
--- a/AsbCloudInfrastructure/AsbCloudInfrastructure.csproj
+++ b/AsbCloudInfrastructure/AsbCloudInfrastructure.csproj
@@ -29,6 +29,7 @@
+
diff --git a/AsbCloudInfrastructure/DependencyInjection.cs b/AsbCloudInfrastructure/DependencyInjection.cs
index ef248c13..3c4d8045 100644
--- a/AsbCloudInfrastructure/DependencyInjection.cs
+++ b/AsbCloudInfrastructure/DependencyInjection.cs
@@ -289,6 +289,8 @@ namespace AsbCloudInfrastructure
services.AddTransient, WellOperationDefaultExcelParser>();
services.AddTransient, WellOperationGazpromKhantosExcelParser>();
+ services.AddTransient();
+
return services;
}
diff --git a/ConsoleApp1/DetectedOperations/Files/Operations.xlsx b/AsbCloudInfrastructure/Services/DetectOperations/DetectOperations.xlsx
similarity index 100%
rename from ConsoleApp1/DetectedOperations/Files/Operations.xlsx
rename to AsbCloudInfrastructure/Services/DetectOperations/DetectOperations.xlsx
diff --git a/AsbCloudInfrastructure/Services/DetectOperations/DetectedOperationExportService.cs b/AsbCloudInfrastructure/Services/DetectOperations/DetectedOperationExportService.cs
index f8f0296d..83955b10 100644
--- a/AsbCloudInfrastructure/Services/DetectOperations/DetectedOperationExportService.cs
+++ b/AsbCloudInfrastructure/Services/DetectOperations/DetectedOperationExportService.cs
@@ -1,120 +1,224 @@
-using AsbCloudApp.Data;
-using AsbCloudApp.Services;
-using AsbCloudDb.Model;
+using AsbCloudDb.Model;
using ClosedXML.Excel;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
+using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
+using AsbCloudInfrastructure.Services.DetectOperations.Detectors;
-namespace AsbCloudInfrastructure.Services.DetectOperations
+namespace AsbCloudInfrastructure.Services.DetectOperations;
+
+public class DetectedOperationExportService
{
+ private readonly DetectorAbstract[] detectors = { new DetectorDrilling(), new DetectorSlipsTime() };
- internal class DetectedOperationExportService
- {
- private readonly IAsbCloudDbContext db;
- private readonly IWellService wellService;
+ private readonly IDictionary domains = new Dictionary
+ {
+ { 1, "https://cloud.digitaldrilling.ru" },
+ { 2, "https://cloud.autodrilling.ru" }
+ };
- public DetectedOperationExportService(IAsbCloudDbContext db, IWellService wellService)
- {
- this.db = db;
- this.wellService = wellService;
- }
+ private const int headerRowsCount = 1;
- public async Task ExportAsync(IEnumerable idsWells, CancellationToken token)
- {
- using var workbook = new XLWorkbook(XLEventTracking.Disabled);
+ private const string cellDepositName = "B1";
+ private const string cellClusterName = "B2";
+ private const string cellWellName = "B3";
+ private const string cellDeltaDate = "H2";
- await AddSheetsAsync(workbook, idsWells, token);
+ private const int columnOperationName = 1;
+ private const int columnDateStart = 2;
+ private const int columnDateEnd = 3;
+ private const int columnDuration = 4;
+ private const int columnDepthStart = 5;
+ private const int columnDepthEnd = 6;
+ private const int columnDeltaDepth = 7;
+ private const int columnDepth = 8;
+ private const int columnIdReasonOfEnd = 9;
- MemoryStream memoryStream = new MemoryStream();
- workbook.SaveAs(memoryStream, new SaveOptions { });
- memoryStream.Seek(0, SeekOrigin.Begin);
- return memoryStream;
- }
+ private readonly IAsbCloudDbContext dbContext;
- private async Task AddSheetsAsync(XLWorkbook workbook, IEnumerable idsWells, CancellationToken token)
- {
- if(!idsWells.Any())
- return;
+ public DetectedOperationExportService(IAsbCloudDbContext dbContext)
+ {
+ this.dbContext = dbContext;
+ }
- var wells = idsWells.Select(i => wellService.GetOrDefault(i))
- .Where(w => w is not null && w.IdTelemetry is not null)
- .Select(w => w!);
+ public async Task ExportAsync(int idWell, int idDomain, CancellationToken cancellationToken)
+ {
+ var well = await dbContext.Wells
+ .Include(w => w.Cluster)
+ .ThenInclude(c => c.Deposit)
+ .SingleOrDefaultAsync(w => w.Id == idWell, cancellationToken);
- if (!wells.Any())
- return;
+ if (well is null)
+ throw new ArgumentNullException(nameof(well));
- var idsTelemetries = wells.Select(w => w.IdTelemetry);
- if (!idsTelemetries.Any())
- return;
+ if (!well.IdTelemetry.HasValue)
+ throw new ArgumentNullException(nameof(well));
- var operations = await db.DetectedOperations
- .Include(o => o.OperationCategory)
- .AsNoTracking()
- .Where(o => idsTelemetries.Contains(o.IdTelemetry))
- .OrderBy(o => o.IdTelemetry)
- .ThenBy(o => o.DateStart)
- .ToListAsync(token);
+ var operations = await DetectOperationsAsync(well.IdTelemetry.Value, new DateTime(2023, 10, 14)
+ .ToUtcDateTimeOffset(well.Timezone.Hours), cancellationToken);
- var groups = operations.GroupBy(o => o.IdTelemetry);
+ return await GenerateExcelFileStreamAsync(well, idDomain, operations, cancellationToken);
+ }
- foreach (var well in wells)
- {
- var ops = groups.FirstOrDefault(g => g.Key == well.IdTelemetry)
- ?.ToList();
-
- if(ops?.Any() != true)
- continue;
+ private async Task GenerateExcelFileStreamAsync(Well well, int idDomain, IEnumerable detectedOperations,
+ CancellationToken cancellationToken)
+ {
+ using var excelTemplateStream = await GetExcelTemplateStreamAsync(cancellationToken);
- var sheetName = $"{well.Cluster}_{well.Caption}"
- .Replace('.','_');
+ using var workbook = new XLWorkbook(excelTemplateStream, XLEventTracking.Disabled);
- var sheet = workbook.AddWorksheet(sheetName);
- AddHeader(sheet);
- const int headerHeight = 1;
- for(var i = 0; i< ops.Count; i++ )
- AddRow(sheet, ops[i], well, i + 1 + headerHeight);
- }
- }
+ await AddToWorkbookAsync(workbook, well, idDomain, detectedOperations, cancellationToken);
- private static void AddHeader(IXLWorksheet sheet)
- {
- var rowNumber = 1;
- sheet.Cell(rowNumber, 1).Value = "Name";
- sheet.Column(1).Width = 34;
-
- sheet.Cell(rowNumber, 2).Value = "DateStart";
- sheet.Column(2).Width = 17;
-
- sheet.Cell(rowNumber, 3).Value = "DateEnd";
- sheet.Column(3).Width = 17;
-
- sheet.Cell(rowNumber, 4).Value = "DepthStart";
- sheet.Column(4).Width = 9;
-
- sheet.Cell(rowNumber, 5).Value = "DepthEnd";
- sheet.Column(5).Width = 9;
-
- sheet.Cell(rowNumber, 6).Value = "KeyValue";
- sheet.Column(6).Width = 9;
+ MemoryStream memoryStream = new MemoryStream();
+ workbook.SaveAs(memoryStream, new SaveOptions { });
+ memoryStream.Seek(0, SeekOrigin.Begin);
+ return memoryStream;
+ }
- sheet.SheetView.FreezeRows(rowNumber);
- }
+ private async Task AddToWorkbookAsync(XLWorkbook workbook, Well well, int idDomain, IEnumerable detectedOperations,
+ CancellationToken cancellationToken)
+ {
+ const string sheetName = "Операции";
- private static void AddRow(IXLWorksheet sheet, DetectedOperation operation, WellDto well, int rowNumber)
- {
- var timezoneoffsetHours = well.Timezone.Hours;
- sheet.Cell(rowNumber, 1).Value = operation.OperationCategory.Name;
- sheet.Cell(rowNumber, 2).Value = operation.DateStart.ToRemoteDateTime(timezoneoffsetHours);
- sheet.Cell(rowNumber, 3).Value = operation.DateEnd.ToRemoteDateTime(timezoneoffsetHours);
- sheet.Cell(rowNumber, 4).Value = operation.DepthStart;
- sheet.Cell(rowNumber, 5).Value = operation.DepthEnd;
- sheet.Cell(rowNumber, 6).Value = operation.Value;
- }
- }
+ if (!detectedOperations.Any())
+ return;
-}
+ var sheet = workbook.Worksheets.FirstOrDefault(ws => ws.Name == sheetName)
+ ?? throw new FileFormatException($"Книга excel не содержит листа {sheetName}.");
+
+ await AddToSheetAsync(sheet, well, idDomain, detectedOperations.OrderBy(x => x.DateStart).ThenBy(x => x.DepthStart).ToArray(),
+ cancellationToken);
+ }
+
+ private async Task AddToSheetAsync(IXLWorksheet sheet, Well well, int idDomain, IList detectedOperations,
+ CancellationToken cancellationToken)
+ {
+ var wellOperationCategories = await dbContext.WellOperationCategories.ToListAsync(cancellationToken);
+
+ sheet.Cell(cellDepositName).Value = well.Cluster.Deposit.Caption;
+ sheet.Cell(cellClusterName).Value = well.Cluster.Caption;
+ sheet.Cell(cellWellName).Value = well.Caption;
+ sheet.Cell(cellDeltaDate).Value = detectedOperations.Max(o => o.DateEnd) - detectedOperations.Min(o => o.DateStart);
+
+ var timeZoneWell = TimeSpan.FromHours(well.Timezone.Hours);
+
+ for (int i = 0; i < detectedOperations.Count; i++)
+ {
+ var dateStart = detectedOperations[i].DateStart.ToOffset(timeZoneWell);
+ var dateEnd = detectedOperations[i].DateEnd.ToOffset(timeZoneWell);
+
+ var row = sheet.Row(5 + i + headerRowsCount);
+
+ row.Cell(columnOperationName).Value = detectedOperations[i].IdCategory == 12000
+ ? "Бурение в слайде с осцилляцией"
+ : wellOperationCategories.Single(o => o.Id == detectedOperations[i].IdCategory).Name;
+ row.Cell(columnDateEnd).Value = dateEnd;
+ row.Cell(columnDuration).Value = (dateEnd - dateStart).TotalMinutes;
+ row.Cell(columnDepthStart).Value = detectedOperations[i].DepthStart;
+ row.Cell(columnDepthEnd).Value = detectedOperations[i].DepthEnd;
+ row.Cell(columnDepth).Value = detectedOperations[i].DepthEnd - detectedOperations[i].DepthStart;
+ row.Cell(columnIdReasonOfEnd).Value = detectedOperations[i].IdReasonOfEnd;
+
+ var link =
+ $"{domains[idDomain]}/well/{well.Id}/telemetry/monitoring?end={Uri.EscapeDataString(dateStart.AddSeconds(3544).ToString("yyyy-MM-ddTHH:mm:ss.fff"))}&range=3600";
+
+ row.Cell(columnDateStart).Value = dateStart;
+ row.Cell(columnDateStart).SetHyperlink(new XLHyperlink(link));
+
+ row.Cell(columnDeltaDepth).Value = i > 0 && i + 1 < detectedOperations.Count
+ ? detectedOperations[i].DepthStart - detectedOperations[i - 1].DepthEnd
+ : 0;
+ }
+ }
+
+ private async Task GetExcelTemplateStreamAsync(CancellationToken cancellationToken)
+ {
+ string resourceName = Assembly.GetExecutingAssembly()
+ .GetManifestResourceNames()
+ .FirstOrDefault(n => n.EndsWith("DetectOperations.xlsx"))!;
+
+ using var stream = Assembly.GetExecutingAssembly()
+ .GetManifestResourceStream(resourceName)!;
+
+ var memoryStream = new MemoryStream();
+ await stream.CopyToAsync(memoryStream, cancellationToken);
+ memoryStream.Position = 0;
+
+ return memoryStream;
+ }
+
+ private async Task> DetectOperationsAsync(int idTelemetry, DateTimeOffset begin,
+ CancellationToken token)
+ {
+ var query = dbContext.TelemetryDataSaub
+ .AsNoTracking()
+ .Where(d => d.IdTelemetry == idTelemetry)
+ .Where(d => d.BlockPosition >= 0)
+ .Select(d => new DetectableTelemetry
+ {
+ DateTime = d.DateTime,
+ IdUser = d.IdUser,
+ WellDepth = d.WellDepth ?? float.NaN,
+ Pressure = d.Pressure ?? float.NaN,
+ HookWeight = d.HookWeight ?? float.NaN,
+ BlockPosition = d.BlockPosition ?? float.NaN,
+ BitDepth = d.BitDepth ?? float.NaN,
+ RotorSpeed = d.RotorSpeed ?? float.NaN,
+ })
+ .OrderBy(d => d.DateTime);
+
+ var startDate = begin;
+ var detectedOperations = new List(8);
+ DetectedOperation? lastDetectedOperation = null;
+ const int minOperationLength = 5;
+ const int maxDetectorsInterpolationFrameLength = 30;
+ const int gap = maxDetectorsInterpolationFrameLength + minOperationLength;
+
+ while (true)
+ {
+ var data = await query
+ .Where(d => d.DateTime > startDate)
+ .ToArrayAsync(token);
+
+ if (data.Length < gap)
+ break;
+
+ var isDetected = false;
+ var positionBegin = 0;
+ var positionEnd = data.Length - gap;
+ var step = 10;
+ while (positionEnd > positionBegin)
+ {
+ step++;
+ foreach (var detector in detectors)
+ {
+ if (!detector.TryDetect(idTelemetry, data, positionBegin, positionEnd, lastDetectedOperation, out var result))
+ continue;
+
+ detectedOperations.Add(result!.Operation);
+ lastDetectedOperation = result.Operation;
+ isDetected = true;
+ step = 1;
+ positionBegin = result.TelemetryEnd;
+ break;
+ }
+
+ if (step > 20)
+ step = 10;
+ positionBegin += step;
+ }
+
+ if (isDetected)
+ startDate = lastDetectedOperation!.DateEnd;
+ else
+ startDate = data[positionEnd].DateTime;
+ }
+
+ return detectedOperations;
+ }
+}
\ No newline at end of file
diff --git a/AsbCloudInfrastructure/Services/DetectOperations/DetectedOperationService.cs b/AsbCloudInfrastructure/Services/DetectOperations/DetectedOperationService.cs
index c3246e05..a3cf05c4 100644
--- a/AsbCloudInfrastructure/Services/DetectOperations/DetectedOperationService.cs
+++ b/AsbCloudInfrastructure/Services/DetectOperations/DetectedOperationService.cs
@@ -6,9 +6,7 @@ using AsbCloudDb;
using AsbCloudDb.Model;
using Mapster;
using Microsoft.EntityFrameworkCore;
-using System;
using System.Collections.Generic;
-using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
@@ -333,13 +331,6 @@ namespace AsbCloudInfrastructure.Services.DetectOperations
.ToArrayAsync(token);
return result;
}
-
- public Task ExportAsync(IEnumerable idsWells, CancellationToken token)
- {
- var exportService = new DetectedOperationExportService(db, wellService);
- return exportService.ExportAsync(idsWells, token);
- }
-
}
}
diff --git a/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorAbstract.cs b/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorAbstract.cs
index 280f77f0..38a3aeac 100644
--- a/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorAbstract.cs
+++ b/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorAbstract.cs
@@ -5,7 +5,7 @@ using System.Linq;
namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors
{
- public abstract class DetectorAbstract
+ internal abstract class DetectorAbstract
{
private readonly int stepLength = 3;
diff --git a/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorDrilling.cs b/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorDrilling.cs
index a507c2da..3e7af4af 100644
--- a/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorDrilling.cs
+++ b/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorDrilling.cs
@@ -4,7 +4,7 @@ using AsbCloudDb.Model;
namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors;
-public class DetectorDrilling : DetectorAbstract
+internal class DetectorDrilling : DetectorAbstract
{
public override Func GetIdOperation => DefineDrillingOperation;
diff --git a/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorSlipsTime.cs b/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorSlipsTime.cs
index 6bd75389..8be8b197 100644
--- a/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorSlipsTime.cs
+++ b/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorSlipsTime.cs
@@ -4,7 +4,7 @@ using AsbCloudDb.Model;
namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors
{
- public class DetectorSlipsTime : DetectorAbstract
+ internal class DetectorSlipsTime : DetectorAbstract
{
protected override double CalcValue(DetectableTelemetry[] telemetry, int begin, int end)
=> CalcDeltaMinutes(telemetry, begin, end);
diff --git a/AsbCloudWebApi/Controllers/SAUB/DetectedOperationController.cs b/AsbCloudWebApi/Controllers/SAUB/DetectedOperationController.cs
index 58950cba..11c58f92 100644
--- a/AsbCloudWebApi/Controllers/SAUB/DetectedOperationController.cs
+++ b/AsbCloudWebApi/Controllers/SAUB/DetectedOperationController.cs
@@ -5,9 +5,10 @@ using AsbCloudApp.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
-using System.Linq;
+using System.ComponentModel.DataAnnotations;
using System.Threading;
using System.Threading.Tasks;
+using AsbCloudInfrastructure.Services.DetectOperations;
using Microsoft.AspNetCore.Http;
namespace AsbCloudWebApi.Controllers.SAUB
@@ -22,11 +23,14 @@ namespace AsbCloudWebApi.Controllers.SAUB
{
private readonly IDetectedOperationService detectedOperationService;
private readonly IWellService wellService;
-
- public DetectedOperationController(IDetectedOperationService detectedOperationService, IWellService wellService)
+ private readonly DetectedOperationExportService detectedOperationExportService;
+
+ public DetectedOperationController(IDetectedOperationService detectedOperationService, IWellService wellService,
+ DetectedOperationExportService detectedOperationExportService)
{
this.detectedOperationService = detectedOperationService;
this.wellService = wellService;
+ this.detectedOperationExportService = detectedOperationExportService;
}
///
@@ -118,42 +122,22 @@ namespace AsbCloudWebApi.Controllers.SAUB
/// Создает excel файл с операциями по скважине
///
/// id скважины
- ///
- /// Токен отмены задачи
- /// Запрашиваемый файл
+ /// Идентификатор домена
[HttpGet("export")]
[Permission]
[ProducesResponseType(typeof(PhysicalFileResult), (int)System.Net.HttpStatusCode.OK, "application/octet-stream")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(typeof(ValidationProblemDetails), (int)System.Net.HttpStatusCode.BadRequest)]
- public async Task ExportAsync(int? idWell, int? idCluster, CancellationToken token)
+ public async Task ExportAsync(int idWell, [Range(1, 2)]int idDomain, CancellationToken token)
{
- if (idCluster is null && idWell is null)
- return this.ValidationBadRequest(nameof(idWell), $"One of {nameof(idWell)} or {nameof(idCluster)} mast be set.");
-
- int? idCompany = User.GetCompanyId();
+ var idCompany = User.GetCompanyId();
if (idCompany is null)
return Forbid();
-
- IEnumerable idsWells;
- if (idCluster is not null)
- {
- var companyWells = await wellService.GetAsync(new() { IdCompany = idCompany }, token);
- idsWells = companyWells.Where(w => w.IdCluster == idCluster)
- .Select(w=>w.Id);
- }
- else
- {
- if (!await wellService.IsCompanyInvolvedInWellAsync((int)idCompany,
- idWell!.Value, token).ConfigureAwait(false))
- return Forbid();
- idsWells = new List { (int)idWell };
- }
-
- var stream = await detectedOperationService.ExportAsync(idsWells, token);
- var fileName = "operations.xlsx";
- return File(stream, fileName);
+
+ var stream = await detectedOperationExportService.ExportAsync(idWell, idDomain, token);
+
+ return File(stream, "application/octet-stream", "operations.xlsx");
}
}
}
diff --git a/ConsoleApp1/DetectedOperations/DetectedOperationExportService.cs b/ConsoleApp1/DetectedOperations/DetectedOperationExportService.cs
deleted file mode 100644
index 5b9623b4..00000000
--- a/ConsoleApp1/DetectedOperations/DetectedOperationExportService.cs
+++ /dev/null
@@ -1,227 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Reflection;
-using System.Threading;
-using System.Threading.Tasks;
-using AsbCloudDb.Model;
-using AsbCloudInfrastructure;
-using AsbCloudInfrastructure.Services.DetectOperations;
-using AsbCloudInfrastructure.Services.DetectOperations.Detectors;
-using ClosedXML.Excel;
-using Microsoft.EntityFrameworkCore;
-
-namespace ConsoleApp1.DetectedOperations;
-
-public class DetectedOperationExportService
-{
- private static readonly DetectorAbstract[] detectors = new DetectorAbstract[]
- {
- new DetectorDrilling(),
- new DetectorSlipsTime(),
- };
-
- private const int headerRowsCount = 1;
-
- private const string cellDepositName = "B1";
- private const string cellClusterName = "B2";
- private const string cellWellName = "B3";
- private const string cellDeltaDate = "H2";
-
- private const int columnOperationName = 1;
- private const int columnDateStart = 2;
- private const int columnDateEnd = 3;
- private const int columnDuration = 4;
- private const int columnDepthStart = 5;
- private const int columnDepthEnd = 6;
- private const int columnDeltaDepth = 7;
- private const int columnDepth = 8;
- private const int columnIdReasonOfEnd = 9;
-
- private readonly IAsbCloudDbContext dbContext;
-
- public DetectedOperationExportService(IAsbCloudDbContext dbContext)
- {
- this.dbContext = dbContext;
- }
-
- public async Task Export(int idWell, CancellationToken cancellationToken)
- {
- var well = await dbContext.Wells
- .Include(w => w.Cluster)
- .ThenInclude(c => c.Deposit)
- .SingleOrDefaultAsync(w => w.Id == idWell, cancellationToken);
-
- if (well is null)
- throw new ArgumentNullException(nameof(well));
-
- if (!well.IdTelemetry.HasValue)
- throw new ArgumentNullException(nameof(well));
-
- var operations = await DetectOperationsAsync(well.IdTelemetry.Value, new DateTime(2023, 10,14)
- .ToUtcDateTimeOffset(well.Timezone.Hours), cancellationToken);
-
- var stream = await GenerateExcelFileStreamAsync(well, operations, cancellationToken);
-
- using var fileStream = File.Create("DetectedOperations.xlsx");
-
- stream.CopyTo(fileStream);
- }
-
- private async Task> DetectOperationsAsync(int idTelemetry, DateTimeOffset begin, CancellationToken token)
- {
- var query = dbContext.TelemetryDataSaub
- .AsNoTracking()
- .Where(d => d.IdTelemetry == idTelemetry)
- .Where(d => d.BlockPosition >= 0)
- .Select(d => new DetectableTelemetry
- {
- DateTime = d.DateTime,
- IdUser = d.IdUser,
- WellDepth = d.WellDepth ?? float.NaN,
- Pressure = d.Pressure ?? float.NaN,
- HookWeight = d.HookWeight ?? float.NaN,
- BlockPosition = d.BlockPosition ?? float.NaN,
- BitDepth = d.BitDepth ?? float.NaN,
- RotorSpeed = d.RotorSpeed ?? float.NaN,
- })
- .OrderBy(d => d.DateTime);
-
- int take = 4 * 86_400; // 4 дня
- var startDate = begin;
- var detectedOperations = new List(8);
- DetectedOperation? lastDetectedOperation = null;
- const int minOperationLength = 5;
- const int maxDetectorsInterpolationFrameLength = 30;
- const int gap = maxDetectorsInterpolationFrameLength + minOperationLength;
-
- while (true)
- {
- var data = await query
- .Where(d => d.DateTime > startDate)
- .Take(take)
- .ToArrayAsync(token);
-
- if (data.Length < gap)
- break;
-
- bool isDetected = false;
- int positionBegin = 0;
- int positionEnd = data.Length - gap;
- int step = 10;
- while (positionEnd > positionBegin)
- {
- step++;
- for (int i = 0; i < detectors.Length; i++)
- {
- if (detectors[i].TryDetect(idTelemetry, data, positionBegin, positionEnd, lastDetectedOperation, out var result))
- {
- detectedOperations.Add(result!.Operation);
- lastDetectedOperation = result.Operation;
- isDetected = true;
- step = 1;
- positionBegin = result.TelemetryEnd;
- break;
- }
- }
-
- if (step > 20)
- step = 10;
- positionBegin += step;
- }
-
- if (isDetected)
- startDate = lastDetectedOperation!.DateEnd;
- else
- startDate = data[positionEnd].DateTime;
- }
-
- return detectedOperations;
- }
-
- private async Task GenerateExcelFileStreamAsync(Well well, IEnumerable detectedOperations,
- CancellationToken cancellationToken)
- {
- using var excelTemplateStream = await GetExcelTemplateStreamAsync(cancellationToken);
-
- using var workbook = new XLWorkbook(excelTemplateStream, XLEventTracking.Disabled);
-
- await AddToWorkbookAsync(workbook, well, detectedOperations, cancellationToken);
-
- MemoryStream memoryStream = new MemoryStream();
- workbook.SaveAs(memoryStream, new SaveOptions { });
- memoryStream.Seek(0, SeekOrigin.Begin);
- return memoryStream;
- }
-
- private async Task AddToWorkbookAsync(XLWorkbook workbook, Well well, IEnumerable detectedOperations,
- CancellationToken cancellationToken)
- {
- const string sheetName = "Операции";
-
- if (!detectedOperations.Any())
- return;
-
- var sheet = workbook.Worksheets.FirstOrDefault(ws => ws.Name == sheetName)
- ?? throw new FileFormatException($"Книга excel не содержит листа {sheetName}.");
-
- await AddToSheetAsync(sheet, well, detectedOperations.OrderBy(x => x.DateStart).ThenBy(x => x.DepthStart).ToArray(), cancellationToken);
- }
-
- private async Task AddToSheetAsync(IXLWorksheet sheet, Well well, IList detectedOperations,
- CancellationToken cancellationToken)
- {
- var wellOperationCategories = await dbContext.WellOperationCategories.ToListAsync(cancellationToken);
-
- sheet.Cell(cellDepositName).Value = well.Cluster.Deposit.Caption;
- sheet.Cell(cellClusterName).Value = well.Cluster.Caption;
- sheet.Cell(cellWellName).Value = well.Caption;
- sheet.Cell(cellDeltaDate).Value = detectedOperations.Max(o => o.DateEnd) - detectedOperations.Min(o => o.DateStart);
-
- var timeZoneWell = TimeSpan.FromHours(well.Timezone.Hours);
-
- for (int i = 0; i < detectedOperations.Count; i++)
- {
- var dateStart = detectedOperations[i].DateStart.ToOffset(timeZoneWell);
- var dateEnd = detectedOperations[i].DateEnd.ToOffset(timeZoneWell);
-
- var row = sheet.Row(5 + i + headerRowsCount);
-
- row.Cell(columnOperationName).Value = detectedOperations[i].IdCategory == 12000 ? "Бурение в слайде с осцилляцией" :
- wellOperationCategories.Single(o => o.Id == detectedOperations[i].IdCategory).Name;
- row.Cell(columnDateEnd).Value = dateEnd;
- row.Cell(columnDuration).Value = (dateEnd - dateStart).TotalMinutes;
- row.Cell(columnDepthStart).Value = detectedOperations[i].DepthStart;
- row.Cell(columnDepthEnd).Value = detectedOperations[i].DepthEnd;
- row.Cell(columnDepth).Value = detectedOperations[i].DepthEnd - detectedOperations[i].DepthStart;
- row.Cell(columnIdReasonOfEnd).Value = detectedOperations[i].IdReasonOfEnd;
-
- var link =
- $"https://cloud.digitaldrilling.ru/well/{well.Id}/telemetry/monitoring?end={Uri.EscapeDataString(dateStart.AddSeconds(3544).ToString("yyyy-MM-ddTHH:mm:ss.fff"))}&range=3600";
- row.Cell(columnDateStart).Value = dateStart;
- row.Cell(columnDateStart).SetHyperlink(new XLHyperlink(link));
-
- row.Cell(columnDeltaDepth).Value = i > 0 && i + 1 < detectedOperations.Count
- ? detectedOperations[i].DepthStart - detectedOperations[i - 1].DepthEnd
- : 0;
- }
- }
-
-
- private async Task GetExcelTemplateStreamAsync(CancellationToken cancellationToken)
- {
- string resourceName = Assembly.GetExecutingAssembly()
- .GetManifestResourceNames()
- .FirstOrDefault(n => n.EndsWith("Operations.xlsx"))!;
-
- using var stream = Assembly.GetExecutingAssembly()
- .GetManifestResourceStream(resourceName)!;
-
- var memoryStream = new MemoryStream();
- await stream.CopyToAsync(memoryStream, cancellationToken);
- memoryStream.Position = 0;
-
- return memoryStream;
- }
-}
\ No newline at end of file