DD.WellWorkover.Cloud/AsbCloudInfrastructure/Services/DetectOperations/DetectedOperationExportService.cs
Степанов Дмитрий 491d9d4abe Фиксы
2023-11-15 09:43:13 +05:00

224 lines
7.5 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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;
public class DetectedOperationExportService
{
private readonly DetectorAbstract[] detectors = { new DetectorDrilling(), new DetectorSlipsTime() };
private readonly IDictionary<int, string> domains = new Dictionary<int, string>
{
{ 1, "https://cloud.digitaldrilling.ru" },
{ 2, "https://cloud.autodrilling.ru" }
};
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<Stream> 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 (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);
return await GenerateExcelFileStreamAsync(well, idDomain, operations, cancellationToken);
}
private async Task<Stream> GenerateExcelFileStreamAsync(Well well, int idDomain, IEnumerable<DetectedOperation> detectedOperations,
CancellationToken cancellationToken)
{
using var excelTemplateStream = await GetExcelTemplateStreamAsync(cancellationToken);
using var workbook = new XLWorkbook(excelTemplateStream, XLEventTracking.Disabled);
await AddToWorkbookAsync(workbook, well, idDomain, 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, int idDomain, 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, idDomain, detectedOperations.OrderBy(x => x.DateStart).ThenBy(x => x.DepthStart).ToArray(),
cancellationToken);
}
private async Task AddToSheetAsync(IXLWorksheet sheet, Well well, int idDomain, 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 =
$"{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<Stream> 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<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,
Pressure = d.Pressure,
HookWeight = d.HookWeight,
BlockPosition = d.BlockPosition,
BitDepth = d.BitDepth,
RotorSpeed = d.RotorSpeed,
})
.OrderBy(d => d.DateTime);
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)
.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;
}
}