DD.WellWorkover.Cloud/ConsoleApp1/DetectedOperations/DetectedOperationExportService.cs

227 lines
7.6 KiB
C#
Raw Normal View History

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<IEnumerable<DetectedOperation>> 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<DetectedOperation>(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<Stream> GenerateExcelFileStreamAsync(Well well, IEnumerable<DetectedOperation> 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<DetectedOperation> 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<DetectedOperation> 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<Stream> 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;
}
}