From 909f8abb4d05ddb256e5fd63896c4af661d0b08a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D1=82=D0=B5=D0=BF=D0=B0=D0=BD=D0=BE=D0=B2=20=D0=94?= =?UTF-8?q?=D0=BC=D0=B8=D1=82=D1=80=D0=B8=D0=B9?= Date: Fri, 20 Oct 2023 11:03:14 +0500 Subject: [PATCH 1/3] =?UTF-8?q?=D0=9D=D0=BE=D0=B2=D1=8B=D0=B5=20=D0=B0?= =?UTF-8?q?=D0=BB=D0=B3=D0=BE=D1=80=D0=B8=D1=82=D0=BC=D1=8B=20=D0=B0=D0=B2?= =?UTF-8?q?=D1=82=D0=BE=20=D0=BE=D0=BF=D1=80=D0=B5=D0=B4=D0=B5=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D1=8F=20=D0=BE=D0=BF=D0=B5=D1=80=D0=B0=D1=86=D0=B8?= =?UTF-8?q?=D0=B9.=20=D0=9E=D1=82=D0=BB=D0=B0=D0=B4=D0=BE=D1=87=D0=BD?= =?UTF-8?q?=D1=8B=D0=B9=20=D0=B2=D0=B0=D1=80=D0=B8=D0=B0=D0=BD=D1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Detectors/DetectorAbstract.cs | 37 +-- .../Detectors/DetectorDevelopment.cs | 11 +- .../Detectors/DetectorDrilling.cs | 63 +++++ .../Detectors/DetectorFlashing.cs | 118 ++++----- .../DetectorFlashingBeforeConnection.cs | 110 ++++----- .../Detectors/DetectorRotor.cs | 116 +++++---- .../Detectors/DetectorSlide.cs | 110 ++++----- .../Detectors/DetectorSlipsTime.cs | 11 +- .../Detectors/DetectorStaticSurveying.cs | 138 +++++------ .../Detectors/DetectorTemplating.cs | 130 +++++----- .../DetectorTemplatingWhileDrilling.cs | 122 +++++----- .../Detectors/OperationDetectorResult.cs | 13 +- .../Specifications/Бурение ротор и слайд.md | 43 ++-- .../WorkOperationDetection.cs | 4 +- ConsoleApp1/ConsoleApp1.csproj | 4 + .../DetectedOperationExportService.cs | 227 ++++++++++++++++++ .../DetectedOperations/Files/Operations.xlsx | Bin 0 -> 15436 bytes ConsoleApp1/Program.cs | 22 +- 18 files changed, 782 insertions(+), 497 deletions(-) create mode 100644 AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorDrilling.cs create mode 100644 ConsoleApp1/DetectedOperations/DetectedOperationExportService.cs create mode 100644 ConsoleApp1/DetectedOperations/Files/Operations.xlsx diff --git a/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorAbstract.cs b/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorAbstract.cs index d14a5829..280f77f0 100644 --- a/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorAbstract.cs +++ b/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorAbstract.cs @@ -5,9 +5,8 @@ using System.Linq; namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors { - internal abstract class DetectorAbstract + public abstract class DetectorAbstract { - private readonly int idOperation; private readonly int stepLength = 3; protected const int IdReasonOfEnd_NotDetected = 0; @@ -32,33 +31,39 @@ namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors protected const int IdReasonOfEnd_BlockPositionIsHi = 501; protected const int IdReasonOfEnd_BlockPositionDeviates = 502; - protected const int IdReasonOfEnd_Custom1 = 10_000; + protected const int IdReasonOfEnd_Drilling = 600; - protected DetectorAbstract(int idOperation) - { - this.idOperation = idOperation; - } + protected const int IdReasonOfEnd_Custom1 = 10_000; + + public abstract Func GetIdOperation { get; } public bool TryDetect(int idTelemetry, DetectableTelemetry[] telemetry, int begin, int end, DetectedOperation? previousOperation, out OperationDetectorResult? result) { // Проверка соответствия критерию начала операции if (DetectBegin(telemetry, begin, previousOperation)) - { + { // Поиск окончания соответствия критерию + int idReasonOfEnd = 0; var positionEnd = begin; while (positionEnd < end) { positionEnd += stepLength; - if ((positionEnd > end)) + if (positionEnd > end) break; - var idReasonOfEnd = DetectEnd(telemetry, positionEnd, previousOperation); - if (idReasonOfEnd != IdReasonOfEnd_NotDetected) - { - result = MakeOperation(idTelemetry, telemetry, begin, positionEnd, idReasonOfEnd); - return true; - } + idReasonOfEnd = DetectEnd(telemetry, positionEnd, previousOperation); + + if(idReasonOfEnd is IdReasonOfEnd_DeltaDepthIsHi or IdReasonOfEnd_PressureIsLo && + !IsValidByWellDepthDoesNotChange(telemetry, begin, positionEnd)) + break; + + if (idReasonOfEnd != IdReasonOfEnd_NotDetected) + break; } + + result = null; + result = MakeOperation(idTelemetry, telemetry, begin, positionEnd, idReasonOfEnd); + return true; } result = null; return false; @@ -81,7 +86,7 @@ namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors Operation = new DetectedOperation { IdTelemetry = idTelemetry, - IdCategory = idOperation, + IdCategory = GetIdOperation.Invoke(telemetry, begin, end), IdUsersAtStart = pBegin.IdUser ?? -1, DateStart = pBegin.DateTime, DateEnd = pEnd.DateTime, diff --git a/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorDevelopment.cs b/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorDevelopment.cs index e858b49b..64c01f6c 100644 --- a/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorDevelopment.cs +++ b/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorDevelopment.cs @@ -1,4 +1,5 @@ -using AsbCloudDb.Model; +using System; +using AsbCloudDb.Model; namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors { @@ -7,12 +8,12 @@ namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors /// Проработка перед наращиванием /// internal class DetectorDevelopment : DetectorAbstract - { - public DetectorDevelopment() - : base(WellOperationCategory.IdDevelopment) { } - + { protected override double CalcValue(DetectableTelemetry[] telemetry, int begin, int end) => CalcDeltaMinutes(telemetry, begin, end); + + public override Func GetIdOperation => (_, _, _) + => WellOperationCategory.IdDevelopment; protected override bool DetectBegin(DetectableTelemetry[] telemetry, int position, DetectedOperation? previousOperation) { diff --git a/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorDrilling.cs b/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorDrilling.cs new file mode 100644 index 00000000..a507c2da --- /dev/null +++ b/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorDrilling.cs @@ -0,0 +1,63 @@ +using System; +using System.Linq; +using AsbCloudDb.Model; + +namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors; + +public class DetectorDrilling : DetectorAbstract +{ + public override Func GetIdOperation => DefineDrillingOperation; + + protected override bool DetectBegin(DetectableTelemetry[] telemetry, int position, DetectedOperation? previousOperation) + { + var point0 = telemetry[position]; + var delta = point0.WellDepth - point0.BitDepth; + if (delta > 0.03d) + return false; + + if (point0.Pressure < 25) + return false; + + if (point0.RotorSpeed < 5) + return false; + + return true; + } + + protected override int DetectEnd(DetectableTelemetry[] telemetry, int position, DetectedOperation? previousOperation) + { + var point0 = telemetry[position]; + var delta = point0.WellDepth - point0.BitDepth; + + if (delta > 0.03d) + return IdReasonOfEnd_DeltaDepthIsHi; + + if (point0.Pressure < 25) + return IdReasonOfEnd_PressureIsLo; + + return IdReasonOfEnd_NotDetected; + } + + protected override bool IsValid(DetectableTelemetry[] telemetry, int begin, int end) + => IsValidByWellDepthIncreasing(telemetry, begin, end); + + protected override double CalcValue(DetectableTelemetry[] telemetry, int begin, int end) + => CalcRop(telemetry, begin, end); + + private static int DefineDrillingOperation(DetectableTelemetry[] telemetry, int begin, int end) + { + const int idSlideWithOscillation = 12000; + + var telemetryRange = telemetry[begin.. end]; + + var avgRotorSpeed = telemetryRange.Average(t => t.RotorSpeed); + + if (avgRotorSpeed < 10) + return WellOperationCategory.IdSlide; + + var despersion = telemetryRange + .Average(t => Math.Pow((t.RotorSpeed - avgRotorSpeed) / avgRotorSpeed, 2)); + + return despersion < 0.2d ? WellOperationCategory.IdRotor : idSlideWithOscillation; + } +} \ No newline at end of file diff --git a/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorFlashing.cs b/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorFlashing.cs index 65040e9a..ac93fd45 100644 --- a/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorFlashing.cs +++ b/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorFlashing.cs @@ -1,59 +1,59 @@ -using AsbCloudDb.Model; - -namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors -{ - - /// - /// Промывка - /// - internal class DetectorFlashing : DetectorAbstract - { - public DetectorFlashing() - : base(WellOperationCategory.IdFlashing) - { } - - protected override double CalcValue(DetectableTelemetry[] telemetry, int begin, int end) - => CalcDeltaMinutes(telemetry, begin, end); - - protected override bool DetectBegin(DetectableTelemetry[] telemetry, int position, DetectedOperation? previousOperation) - { - if (!((previousOperation?.IdCategory == WellOperationCategory.IdRotor) || - (previousOperation?.IdCategory == WellOperationCategory.IdSlide))) - return false; - - var point0 = telemetry[position]; - var delta = point0.WellDepth - point0.BitDepth; - if (delta > 0.05d) - return false; - - if (point0.Pressure < 15) - return false; - - if (point0.BlockPosition < 3) - return false; - - if (ContainsDeviationApprox(telemetry, t => t.WellDepth, position, 150, 0.0001)) - return false; - - return true; - } - - protected override int DetectEnd(DetectableTelemetry[] telemetry, int position, DetectedOperation? previousOperation) - { - var point0 = telemetry[position]; - var delta = point0.WellDepth - point0.BitDepth; - if ((delta > 0.03d ) - && (point0.Pressure > 15) - && ContainsDeviationApprox(telemetry, t=>t.BlockPosition, position, 60, 0.03)) - return IdReasonOfEnd_Custom1; - - return IdReasonOfEnd_NotDetected; - } - - protected override bool IsValid(DetectableTelemetry[] telemetry, int begin, int end) - => IsValidByWellDepthDoesNotChange(telemetry, begin, end); - } - - -} - +// using AsbCloudDb.Model; +// +// namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors +// { +// +// /// +// /// Промывка +// /// +// internal class DetectorFlashing : DetectorAbstract +// { +// public DetectorFlashing() +// : base(WellOperationCategory.IdFlashing) +// { } +// +// protected override double CalcValue(DetectableTelemetry[] telemetry, int begin, int end) +// => CalcDeltaMinutes(telemetry, begin, end); +// +// protected override bool DetectBegin(DetectableTelemetry[] telemetry, int position, DetectedOperation? previousOperation) +// { +// if (!((previousOperation?.IdCategory == WellOperationCategory.IdRotor) || +// (previousOperation?.IdCategory == WellOperationCategory.IdSlide))) +// return false; +// +// var point0 = telemetry[position]; +// var delta = point0.WellDepth - point0.BitDepth; +// if (delta > 0.05d) +// return false; +// +// if (point0.Pressure < 15) +// return false; +// +// if (point0.BlockPosition < 3) +// return false; +// +// if (ContainsDeviationApprox(telemetry, t => t.WellDepth, position, 150, 0.0001)) +// return false; +// +// return true; +// } +// +// protected override int DetectEnd(DetectableTelemetry[] telemetry, int position, DetectedOperation? previousOperation) +// { +// var point0 = telemetry[position]; +// var delta = point0.WellDepth - point0.BitDepth; +// if ((delta > 0.03d ) +// && (point0.Pressure > 15) +// && ContainsDeviationApprox(telemetry, t=>t.BlockPosition, position, 60, 0.03)) +// return IdReasonOfEnd_Custom1; +// +// return IdReasonOfEnd_NotDetected; +// } +// +// protected override bool IsValid(DetectableTelemetry[] telemetry, int begin, int end) +// => IsValidByWellDepthDoesNotChange(telemetry, begin, end); +// } +// +// +// } +// diff --git a/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorFlashingBeforeConnection.cs b/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorFlashingBeforeConnection.cs index 4e447e67..ffc74129 100644 --- a/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorFlashingBeforeConnection.cs +++ b/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorFlashingBeforeConnection.cs @@ -1,55 +1,55 @@ -using AsbCloudDb.Model; - -namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors -{ - - /// - /// Промывка перед наращиванием - /// - internal class DetectorFlashingBeforeConnection : DetectorAbstract - { - public DetectorFlashingBeforeConnection() - : base(WellOperationCategory.IdFlashingBeforeConnection) { } - - protected override double CalcValue(DetectableTelemetry[] telemetry, int begin, int end) - => CalcDeltaMinutes(telemetry, begin, end); - - protected override bool DetectBegin(DetectableTelemetry[] telemetry, int position, DetectedOperation? previousOperation) - { - if (!((previousOperation?.IdCategory == WellOperationCategory.IdRotor) || - (previousOperation?.IdCategory == WellOperationCategory.IdSlide))) - return false; - - var point0 = telemetry[position]; - var delta = point0.WellDepth - point0.BitDepth; - if (delta > 0.05d) - return false; - - if (point0.Pressure < 15) - return false; - - if (point0.BlockPosition > 3) - return false; - - return true; - } - - protected override int DetectEnd(DetectableTelemetry[] telemetry, int position, DetectedOperation? previousOperation) - { - var point0 = telemetry[position]; - var delta = point0.WellDepth - point0.BitDepth; - if ((delta > 0.03d ) - && (point0.Pressure > 15) - && ContainsDeviationApprox(telemetry, t=>t.BlockPosition, position, 60, 0.03)) - return IdReasonOfEnd_Custom1; - - return IdReasonOfEnd_NotDetected; - } - - protected override bool IsValid(DetectableTelemetry[] telemetry, int begin, int end) - => IsValidByWellDepthDoesNotChange(telemetry, begin, end); - } - - -} - +// using AsbCloudDb.Model; +// +// namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors +// { +// +// /// +// /// Промывка перед наращиванием +// /// +// internal class DetectorFlashingBeforeConnection : DetectorAbstract +// { +// public DetectorFlashingBeforeConnection() +// : base(WellOperationCategory.IdFlashingBeforeConnection) { } +// +// protected override double CalcValue(DetectableTelemetry[] telemetry, int begin, int end) +// => CalcDeltaMinutes(telemetry, begin, end); +// +// protected override bool DetectBegin(DetectableTelemetry[] telemetry, int position, DetectedOperation? previousOperation) +// { +// if (!((previousOperation?.IdCategory == WellOperationCategory.IdRotor) || +// (previousOperation?.IdCategory == WellOperationCategory.IdSlide))) +// return false; +// +// var point0 = telemetry[position]; +// var delta = point0.WellDepth - point0.BitDepth; +// if (delta > 0.05d) +// return false; +// +// if (point0.Pressure < 15) +// return false; +// +// if (point0.BlockPosition > 3) +// return false; +// +// return true; +// } +// +// protected override int DetectEnd(DetectableTelemetry[] telemetry, int position, DetectedOperation? previousOperation) +// { +// var point0 = telemetry[position]; +// var delta = point0.WellDepth - point0.BitDepth; +// if ((delta > 0.03d ) +// && (point0.Pressure > 15) +// && ContainsDeviationApprox(telemetry, t=>t.BlockPosition, position, 60, 0.03)) +// return IdReasonOfEnd_Custom1; +// +// return IdReasonOfEnd_NotDetected; +// } +// +// protected override bool IsValid(DetectableTelemetry[] telemetry, int begin, int end) +// => IsValidByWellDepthDoesNotChange(telemetry, begin, end); +// } +// +// +// } +// diff --git a/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorRotor.cs b/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorRotor.cs index 2d5ce962..9562645d 100644 --- a/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorRotor.cs +++ b/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorRotor.cs @@ -1,62 +1,54 @@ -using AsbCloudDb.Model; -using System.Linq; - -namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors -{ - - internal class DetectorRotor : DetectorAbstract - { - public DetectorRotor() - : base(WellOperationCategory.IdRotor) { } - - protected override bool DetectBegin(DetectableTelemetry[] telemetry, int position, DetectedOperation? previousOperation) - { - var point0 = telemetry[position]; - var delta = point0.WellDepth - point0.BitDepth; - if (delta > 0.03d) - return false; - - if (point0.Pressure < 25) - return false; - - if (point0.RotorSpeed < 5) - return false; - - var point1 = telemetry[position + 1]; - if (point1.WellDepth - point0.WellDepth <= 0.003) - return false; - - return true; - } - - protected override int DetectEnd(DetectableTelemetry[] telemetry, int position, DetectedOperation? previousOperation) - { - var point0 = telemetry[position]; - var delta = point0.WellDepth - point0.BitDepth; - if (delta > 0.03d) - return IdReasonOfEnd_DeltaDepthIsHi; - - if (point0.Pressure < 25) - return IdReasonOfEnd_PressureIsLo; - - var avgRotorSpeed = CalcAvgAppr(d => d.RotorSpeed, telemetry, position, 60); - - if (avgRotorSpeed < 10) - return IdReasonOfEnd_AvgRotorSpeedIsLo; - - if (!DeviatesFromBegin(telemetry, t => t.WellDepth, position, 150, 0.003)) - return IdReasonOfEnd_WellDepthDeviates; - - return IdReasonOfEnd_NotDetected; - } - - protected override bool IsValid(DetectableTelemetry[] telemetry, int begin, int end) - => IsValidByWellDepthIncreasing(telemetry, begin, end); - - protected override double CalcValue(DetectableTelemetry[] telemetry, int begin, int end) - => CalcRop(telemetry, begin, end); - } - - -} - +// using AsbCloudDb.Model; +// using System.Linq; +// +// namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors +// { +// +// public class DetectorRotor : DetectorAbstract +// { +// public DetectorRotor() +// : base(WellOperationCategory.IdRotor) { } +// +// protected override bool DetectBegin(DetectableTelemetry[] telemetry, int position, DetectedOperation? previousOperation) +// { +// var point0 = telemetry[position]; +// var delta = point0.WellDepth - point0.BitDepth; +// if (delta > 0.03d) +// return false; +// +// if (point0.Pressure < 25) +// return false; +// +// if (point0.RotorSpeed < 5) +// return false; +// +// var point1 = telemetry[position + 1]; +// if (point1.WellDepth - point0.WellDepth <= 0.003) +// return false; +// +// return true; +// } +// +// protected override int DetectEnd(DetectableTelemetry[] telemetry, int position, DetectedOperation? previousOperation) +// { +// var point0 = telemetry[position]; +// var delta = point0.WellDepth - point0.BitDepth; +// if (delta > 0.03d) +// return IdReasonOfEnd_DeltaDepthIsHi; +// +// if (point0.Pressure < 25) +// return IdReasonOfEnd_PressureIsLo; +// +// return IdReasonOfEnd_NotDetected; +// } +// +// protected override bool IsValid(DetectableTelemetry[] telemetry, int begin, int end) +// => IsValidByWellDepthIncreasing(telemetry, begin, end); +// +// protected override double CalcValue(DetectableTelemetry[] telemetry, int begin, int end) +// => CalcRop(telemetry, begin, end); +// } +// +// +// } +// diff --git a/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorSlide.cs b/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorSlide.cs index 48f6fe7b..6244b4d6 100644 --- a/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorSlide.cs +++ b/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorSlide.cs @@ -1,61 +1,49 @@ -using AsbCloudDb.Model; - -namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors -{ - - internal class DetectorSlide : DetectorAbstract - { - public DetectorSlide() - : base(WellOperationCategory.IdSlide) { } - - protected override bool DetectBegin(DetectableTelemetry[] telemetry, int position, DetectedOperation? previousOperation) - { - var point0 = telemetry[position]; - var delta = point0.WellDepth - point0.BitDepth; - if (delta > 0.03d) - return false; - - if (point0.Pressure < 25) - return false; - - if (point0.RotorSpeed > 5) - return false; - - var point1 = telemetry[position + 1]; - if (point1.WellDepth - point0.WellDepth <= 0.003) - return false; - - return true; - } - - protected override int DetectEnd(DetectableTelemetry[] telemetry, int position, DetectedOperation? previousOperation) - { - var point0 = telemetry[position]; - var delta = point0.WellDepth - point0.BitDepth; - if (delta > 0.03d) - return IdReasonOfEnd_DeltaDepthIsHi; - - if (point0.Pressure < 25) - return IdReasonOfEnd_PressureIsLo; - - var avgRotorSpeed = CalcAvgAppr(d => d.RotorSpeed, telemetry, position, 60); - - if (avgRotorSpeed > 10) - return IdReasonOfEnd_AvgRotorSpeedIsHi; - - if (!DeviatesFromBegin(telemetry, t => t.WellDepth, position, 150, 0.003)) - return IdReasonOfEnd_WellDepthDeviates; - - return IdReasonOfEnd_NotDetected; - } - - protected override bool IsValid(DetectableTelemetry[] telemetry, int begin, int end) - => IsValidByWellDepthIncreasing(telemetry, begin, end); - - protected override double CalcValue(DetectableTelemetry[] telemetry, int begin, int end) - => CalcRop(telemetry, begin, end); - } - - -} - +// using AsbCloudDb.Model; +// +// namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors +// { +// +// public class DetectorSlide : DetectorAbstract +// { +// public DetectorSlide() +// : base(WellOperationCategory.IdSlide) { } +// +// protected override bool DetectBegin(DetectableTelemetry[] telemetry, int position, DetectedOperation? previousOperation) +// { +// var point0 = telemetry[position]; +// var delta = point0.WellDepth - point0.BitDepth; +// if (delta > 0.03d) +// return false; +// +// if (point0.Pressure < 25) +// return false; +// +// return true; +// } +// +// protected override int DetectEnd(DetectableTelemetry[] telemetry, int position, DetectedOperation? previousOperation) +// { +// var point0 = telemetry[position]; +// var delta = point0.WellDepth - point0.BitDepth; +// if (delta > 0.03d) +// return IdReasonOfEnd_DeltaDepthIsHi; +// +// if (point0.Pressure < 25) +// return IdReasonOfEnd_PressureIsLo; +// +// if (!DeviatesFromBegin(telemetry, t => t.WellDepth, position, 150, 0.003)) +// return IdReasonOfEnd_WellDepthDeviates; +// +// return IdReasonOfEnd_NotDetected; +// } +// +// protected override bool IsValid(DetectableTelemetry[] telemetry, int begin, int end) +// => IsValidByWellDepthIncreasing(telemetry, begin, end); +// +// protected override double CalcValue(DetectableTelemetry[] telemetry, int begin, int end) +// => CalcRop(telemetry, begin, end); +// } +// +// +// } +// diff --git a/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorSlipsTime.cs b/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorSlipsTime.cs index f8883005..6bd75389 100644 --- a/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorSlipsTime.cs +++ b/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorSlipsTime.cs @@ -1,17 +1,16 @@ -using AsbCloudDb.Model; +using System; +using AsbCloudDb.Model; namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors { - internal class DetectorSlipsTime : DetectorAbstract + public class DetectorSlipsTime : DetectorAbstract { - public DetectorSlipsTime() - : base(WellOperationCategory.IdSlipsTime) - { } - protected override double CalcValue(DetectableTelemetry[] telemetry, int begin, int end) => CalcDeltaMinutes(telemetry, begin, end); + public override Func GetIdOperation => (_, _, _) => WellOperationCategory.IdSlipsTime; + protected override bool DetectBegin(DetectableTelemetry[] telemetry, int position, DetectedOperation? previousOperation) { var point0 = telemetry[position]; diff --git a/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorStaticSurveying.cs b/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorStaticSurveying.cs index f999dbd5..20d411dc 100644 --- a/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorStaticSurveying.cs +++ b/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorStaticSurveying.cs @@ -1,69 +1,69 @@ -using AsbCloudDb.Model; -using System.Linq; -using System; - -namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors -{ - - - /// - /// Статический замер телесистемы - /// - internal class DetectorStaticSurveying: DetectorAbstract - { - public DetectorStaticSurveying() - : base(WellOperationCategory.IdStaticSurveying) { } - - protected override bool DetectBegin(DetectableTelemetry[] telemetry, int position, DetectedOperation? previousOperation) - { - var point0 = telemetry[position]; - - if (point0.Pressure < 15) - return false; - - var delta = point0.WellDepth - point0.BitDepth; - if (delta > 2.5d || delta < 0.15d) - return false; - - if (point0.RotorSpeed > 30) - return false; - - if (ContainsDeviation(telemetry, t => t.BlockPosition, position, 120, 0.03)) - return false; - - if (ContainsDeviation(telemetry, t => t.Pressure, position, 60, 10)) - return false; - - return true; - } - - protected override int DetectEnd(DetectableTelemetry[] telemetry, int position, DetectedOperation? previousOperation) - { - var point0 = telemetry[position]; - - var delta = point0.WellDepth - point0.BitDepth; - if (delta > 2.5d ) - return IdReasonOfEnd_DeltaDepthIsHi; - - if (point0.RotorSpeed > 30) - return IdReasonOfEnd_RotorSpeedIsHi; - - if (RisesFromBegin(telemetry, t => t.Pressure, position, 10, 15)) - return IdReasonOfEnd_PressureIsRising; - - if (ContainsDeviation(telemetry, t => t.BlockPosition, position, 10, 0.05)) - return IdReasonOfEnd_BlockPositionDeviates; - - return IdReasonOfEnd_NotDetected; - } - - protected override bool IsValid(DetectableTelemetry[] telemetry, int begin, int end) - => IsValidByWellDepthDoesNotChange(telemetry, begin, end); - - protected override double CalcValue(DetectableTelemetry[] telemetry, int begin, int end) - => CalcDeltaMinutes(telemetry, begin, end); - } - - -} - +// using AsbCloudDb.Model; +// using System.Linq; +// using System; +// +// namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors +// { +// +// +// /// +// /// Статический замер телесистемы +// /// +// internal class DetectorStaticSurveying: DetectorAbstract +// { +// public DetectorStaticSurveying() +// : base(WellOperationCategory.IdStaticSurveying) { } +// +// protected override bool DetectBegin(DetectableTelemetry[] telemetry, int position, DetectedOperation? previousOperation) +// { +// var point0 = telemetry[position]; +// +// if (point0.Pressure < 15) +// return false; +// +// var delta = point0.WellDepth - point0.BitDepth; +// if (delta > 2.5d || delta < 0.15d) +// return false; +// +// if (point0.RotorSpeed > 30) +// return false; +// +// if (ContainsDeviation(telemetry, t => t.BlockPosition, position, 120, 0.03)) +// return false; +// +// if (ContainsDeviation(telemetry, t => t.Pressure, position, 60, 10)) +// return false; +// +// return true; +// } +// +// protected override int DetectEnd(DetectableTelemetry[] telemetry, int position, DetectedOperation? previousOperation) +// { +// var point0 = telemetry[position]; +// +// var delta = point0.WellDepth - point0.BitDepth; +// if (delta > 2.5d ) +// return IdReasonOfEnd_DeltaDepthIsHi; +// +// if (point0.RotorSpeed > 30) +// return IdReasonOfEnd_RotorSpeedIsHi; +// +// if (RisesFromBegin(telemetry, t => t.Pressure, position, 10, 15)) +// return IdReasonOfEnd_PressureIsRising; +// +// if (ContainsDeviation(telemetry, t => t.BlockPosition, position, 10, 0.05)) +// return IdReasonOfEnd_BlockPositionDeviates; +// +// return IdReasonOfEnd_NotDetected; +// } +// +// protected override bool IsValid(DetectableTelemetry[] telemetry, int begin, int end) +// => IsValidByWellDepthDoesNotChange(telemetry, begin, end); +// +// protected override double CalcValue(DetectableTelemetry[] telemetry, int begin, int end) +// => CalcDeltaMinutes(telemetry, begin, end); +// } +// +// +// } +// diff --git a/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorTemplating.cs b/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorTemplating.cs index dde3f12e..96e11346 100644 --- a/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorTemplating.cs +++ b/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorTemplating.cs @@ -1,65 +1,65 @@ -using AsbCloudDb.Model; -using System.Collections.Generic; - -namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors -{ - - internal class DetectorTemplating : DetectorAbstract - { - public DetectorTemplating() - : base(WellOperationCategory.IdTemplating) { } - - protected override double CalcValue(DetectableTelemetry[] telemetry, int begin, int end) - => CalcDeltaMinutes(telemetry, begin, end); - - protected override bool DetectBegin(DetectableTelemetry[] telemetry, int position, DetectedOperation? previousOperation) - { - if(previousOperation?.IdCategory == WellOperationCategory.IdSlipsTime) - return false; - - var point0 = telemetry[position]; - var delta = point0.WellDepth - point0.BitDepth; - if (delta < 0.03d || delta > 30) - return false; - - if (point0.Pressure < 15) - return false; - - if (point0.BlockPosition > 2.5) - return false; - - if (point0.RotorSpeed > 10) - return false; - - if (!ContainsDeviationApprox(telemetry, d => d.BlockPosition, position, 60, 0.03)) - return false; - - return true; - } - - protected override int DetectEnd(DetectableTelemetry[] telemetry, int position, DetectedOperation? previousOperation) - { - var point0 = telemetry[position]; - var delta = point0.WellDepth - point0.BitDepth; - if (delta < 0.03d || delta > 30) - return IdReasonOfEnd_DeltaDepthOutOfRange; - - if (point0.Pressure < 15) - return IdReasonOfEnd_PressureIsLo; - - if (point0.BlockPosition > 31) - return IdReasonOfEnd_BlockPositionIsHi; - - if (point0.RotorSpeed > 10) - return IdReasonOfEnd_RotorSpeedIsHi; - - return IdReasonOfEnd_NotDetected; - } - - protected override bool IsValid(DetectableTelemetry[] telemetry, int begin, int end) - => IsValidByWellDepthDoesNotChange(telemetry, begin, end); - } - - -} - +// using AsbCloudDb.Model; +// using System.Collections.Generic; +// +// namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors +// { +// +// internal class DetectorTemplating : DetectorAbstract +// { +// public DetectorTemplating() +// : base(WellOperationCategory.IdTemplating) { } +// +// protected override double CalcValue(DetectableTelemetry[] telemetry, int begin, int end) +// => CalcDeltaMinutes(telemetry, begin, end); +// +// protected override bool DetectBegin(DetectableTelemetry[] telemetry, int position, DetectedOperation? previousOperation) +// { +// if(previousOperation?.IdCategory == WellOperationCategory.IdSlipsTime) +// return false; +// +// var point0 = telemetry[position]; +// var delta = point0.WellDepth - point0.BitDepth; +// if (delta < 0.03d || delta > 30) +// return false; +// +// if (point0.Pressure < 15) +// return false; +// +// if (point0.BlockPosition > 2.5) +// return false; +// +// if (point0.RotorSpeed > 10) +// return false; +// +// if (!ContainsDeviationApprox(telemetry, d => d.BlockPosition, position, 60, 0.03)) +// return false; +// +// return true; +// } +// +// protected override int DetectEnd(DetectableTelemetry[] telemetry, int position, DetectedOperation? previousOperation) +// { +// var point0 = telemetry[position]; +// var delta = point0.WellDepth - point0.BitDepth; +// if (delta < 0.03d || delta > 30) +// return IdReasonOfEnd_DeltaDepthOutOfRange; +// +// if (point0.Pressure < 15) +// return IdReasonOfEnd_PressureIsLo; +// +// if (point0.BlockPosition > 31) +// return IdReasonOfEnd_BlockPositionIsHi; +// +// if (point0.RotorSpeed > 10) +// return IdReasonOfEnd_RotorSpeedIsHi; +// +// return IdReasonOfEnd_NotDetected; +// } +// +// protected override bool IsValid(DetectableTelemetry[] telemetry, int begin, int end) +// => IsValidByWellDepthDoesNotChange(telemetry, begin, end); +// } +// +// +// } +// diff --git a/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorTemplatingWhileDrilling.cs b/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorTemplatingWhileDrilling.cs index 7d48f361..656ad29e 100644 --- a/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorTemplatingWhileDrilling.cs +++ b/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorTemplatingWhileDrilling.cs @@ -1,61 +1,61 @@ -using AsbCloudDb.Model; - -namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors -{ - - /// - /// Шаблонировка при бурении - /// - internal class DetectorTemplatingWhileDrilling : DetectorAbstract - { - public DetectorTemplatingWhileDrilling() - : base(WellOperationCategory.IdTemplatingWhileDrilling) { } - - protected override double CalcValue(DetectableTelemetry[] telemetry, int begin, int end) - => CalcDeltaMinutes(telemetry, begin, end); - - protected override bool DetectBegin(DetectableTelemetry[] telemetry, int position, DetectedOperation? previousOperation) - { - if (previousOperation?.IdCategory != WellOperationCategory.IdFlashing) - return false; - - var point0 = telemetry[position]; - - if (point0.Pressure < 15) - return false; - - if (point0.RotorSpeed < 1) - return false; - - if (RisesFromBegin(telemetry, t => t.BlockPosition, position, 30, 0.5)) - return false; - - return true; - } - - protected override int DetectEnd(DetectableTelemetry[] telemetry, int position, DetectedOperation? previousOperation) - { - var point0 = telemetry[position]; - - if (point0.Pressure < 15) - return IdReasonOfEnd_PressureIsLo; - - if (RisesFromBegin(telemetry, t=>t.WellDepth, position, 10, 0.01)) - return IdReasonOfEnd_WellDepthDeviates; - - var delta = point0.WellDepth - point0.BitDepth; - if ( (delta > 0.03d ) - && (point0.Pressure > 15) - && (!ContainsDeviationApprox(telemetry, t=>t.BlockPosition, position, 60, 0.03))) - return IdReasonOfEnd_Custom1; - - return IdReasonOfEnd_NotDetected; - } - - protected override bool IsValid(DetectableTelemetry[] telemetry, int begin, int end) - => IsValidByWellDepthDoesNotChange(telemetry, begin, end); - } - - -} - +// using AsbCloudDb.Model; +// +// namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors +// { +// +// /// +// /// Шаблонировка при бурении +// /// +// internal class DetectorTemplatingWhileDrilling : DetectorAbstract +// { +// public DetectorTemplatingWhileDrilling() +// : base(WellOperationCategory.IdTemplatingWhileDrilling) { } +// +// protected override double CalcValue(DetectableTelemetry[] telemetry, int begin, int end) +// => CalcDeltaMinutes(telemetry, begin, end); +// +// protected override bool DetectBegin(DetectableTelemetry[] telemetry, int position, DetectedOperation? previousOperation) +// { +// if (previousOperation?.IdCategory != WellOperationCategory.IdFlashing) +// return false; +// +// var point0 = telemetry[position]; +// +// if (point0.Pressure < 15) +// return false; +// +// if (point0.RotorSpeed < 1) +// return false; +// +// if (RisesFromBegin(telemetry, t => t.BlockPosition, position, 30, 0.5)) +// return false; +// +// return true; +// } +// +// protected override int DetectEnd(DetectableTelemetry[] telemetry, int position, DetectedOperation? previousOperation) +// { +// var point0 = telemetry[position]; +// +// if (point0.Pressure < 15) +// return IdReasonOfEnd_PressureIsLo; +// +// if (RisesFromBegin(telemetry, t=>t.WellDepth, position, 10, 0.01)) +// return IdReasonOfEnd_WellDepthDeviates; +// +// var delta = point0.WellDepth - point0.BitDepth; +// if ( (delta > 0.03d ) +// && (point0.Pressure > 15) +// && (!ContainsDeviationApprox(telemetry, t=>t.BlockPosition, position, 60, 0.03))) +// return IdReasonOfEnd_Custom1; +// +// return IdReasonOfEnd_NotDetected; +// } +// +// protected override bool IsValid(DetectableTelemetry[] telemetry, int begin, int end) +// => IsValidByWellDepthDoesNotChange(telemetry, begin, end); +// } +// +// +// } +// diff --git a/AsbCloudInfrastructure/Services/DetectOperations/Detectors/OperationDetectorResult.cs b/AsbCloudInfrastructure/Services/DetectOperations/Detectors/OperationDetectorResult.cs index 2c7d8f09..fcab00bd 100644 --- a/AsbCloudInfrastructure/Services/DetectOperations/Detectors/OperationDetectorResult.cs +++ b/AsbCloudInfrastructure/Services/DetectOperations/Detectors/OperationDetectorResult.cs @@ -3,11 +3,10 @@ namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors { - class OperationDetectorResult - { - public int TelemetryBegin { get; set; } - public int TelemetryEnd { get; set; } - public DetectedOperation Operation { get; set; } = null!; - } - + public class OperationDetectorResult + { + public int TelemetryBegin { get; set; } + public int TelemetryEnd { get; set; } + public DetectedOperation Operation { get; set; } = null!; + } } diff --git a/AsbCloudInfrastructure/Services/DetectOperations/Specifications/Бурение ротор и слайд.md b/AsbCloudInfrastructure/Services/DetectOperations/Specifications/Бурение ротор и слайд.md index 21332ad4..965f271e 100644 --- a/AsbCloudInfrastructure/Services/DetectOperations/Specifications/Бурение ротор и слайд.md +++ b/AsbCloudInfrastructure/Services/DetectOperations/Specifications/Бурение ротор и слайд.md @@ -1,22 +1,29 @@ -# Алгоритм определения бурения в роторе -## Описание +Метод определения бурения -## Метод определения бурения в роторе +Признак начала операции = - Признак начала операции = - ( расстояние от долота до забоя < 0.03м ) И - ( давление > 25атм ) И - ( глубина забоя за следующую секунду больше текущей на 0.003м ) И - ( обороты ротора > 5 об/м ); +расстояние от долота до забоя < 0.03м  И - Признак окончания операции = - ( расстояние от долота до забоя > 0.03м ) ИЛИ - ( давление < 25атм ) ИЛИ - ( среднее арифметическое оборотов ротора за 60 сек < 10 об/м ) ИЛИ - ( глубина забоя в течении следующих 150 сек не изменяется больше чем на 0.003 ); - -## Метод определения бурения в слайде -Повторяет метод определения бурения в роторе, за исключением условия с оборотами ротора. Это уловие нужно инвертировать. +давление > 25атм -## Ключевой параметр -МСП = разность глубины забоя на конец и начало операции / продолжительность операции. \ No newline at end of file +Признак окончания операции = + +расстояние от долота до забоя > 0.03м  ИЛИ + +давление < 25атм + +Находим границы + +После того когда мы нашли границы, мы должны определить операцию, тогда мы смотрим на забой точки окончания операций сравниваем с забоем точками начала операций: + +Если они равны друг другу, то мы эту операцию дальше не обрабатываем, а выбрасываем. + +Если они не равны, то у нас произошло увеличение забоя, значит эта операция бурения. + +Дальше мы определяем как мы бурили в роторе или слайде, для этого нам необходимо рассчитать среднюю скорость(среднее арифметическое) за всю операцию бурения . Если среднее арифметическое больше константы (10 об/мин), то это бурение в роторе, если меньше, то это бурение в слайде. + +Если бурение в роторе, то мы считаем только дисперсию нормированных оборотов ротора(по среднему значению). (Так как это может быть бурение в слайде с осцилляцией и выглядеть как бурение в роторе): + +Если полученное значение меньше константы(0,2), то мы подтвердили что бурение в роторе. + +Если полученное значение больше константы, то это бурение в слайде с осцилляцией. \ No newline at end of file diff --git a/AsbCloudInfrastructure/Services/DetectOperations/WorkOperationDetection.cs b/AsbCloudInfrastructure/Services/DetectOperations/WorkOperationDetection.cs index eef423b9..582c208b 100644 --- a/AsbCloudInfrastructure/Services/DetectOperations/WorkOperationDetection.cs +++ b/AsbCloudInfrastructure/Services/DetectOperations/WorkOperationDetection.cs @@ -16,8 +16,8 @@ public class WorkOperationDetection: Work { private static readonly DetectorAbstract[] detectors = new DetectorAbstract[] { - new DetectorRotor(), - new DetectorSlide(), + // new DetectorRotor(), + // new DetectorSlide(), //new DetectorDevelopment(), //new DetectorTemplating(), new DetectorSlipsTime(), diff --git a/ConsoleApp1/ConsoleApp1.csproj b/ConsoleApp1/ConsoleApp1.csproj index c0c5ae85..d043fb8e 100644 --- a/ConsoleApp1/ConsoleApp1.csproj +++ b/ConsoleApp1/ConsoleApp1.csproj @@ -22,6 +22,10 @@ + + + + diff --git a/ConsoleApp1/DetectedOperations/DetectedOperationExportService.cs b/ConsoleApp1/DetectedOperations/DetectedOperationExportService.cs new file mode 100644 index 00000000..5b9623b4 --- /dev/null +++ b/ConsoleApp1/DetectedOperations/DetectedOperationExportService.cs @@ -0,0 +1,227 @@ +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 diff --git a/ConsoleApp1/DetectedOperations/Files/Operations.xlsx b/ConsoleApp1/DetectedOperations/Files/Operations.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..9073c58dcc6ba789363fbf4d6d438636f41260c1 GIT binary patch literal 15436 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 literal 0 HcmV?d00001 diff --git a/ConsoleApp1/Program.cs b/ConsoleApp1/Program.cs index 15f086ae..ffe1d128 100644 --- a/ConsoleApp1/Program.cs +++ b/ConsoleApp1/Program.cs @@ -1,24 +1,24 @@ -using System.IO; -using System.Text.RegularExpressions; + using System.Threading; using System.Threading.Tasks; -using AsbCloudApp.Data; -using AsbCloudApp.Services; using AsbCloudDb.Model; -using AsbCloudInfrastructure; -using CliWrap; -using Mapster; +using ConsoleApp1.DetectedOperations; using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.DependencyInjection; namespace ConsoleApp1 { class Program { - static void Main(/*string[] args*/) + private static DbContextOptions options = new DbContextOptionsBuilder() + .UseNpgsql("Host=localhost;Database=postgres;Port=5433;Username=postgres;Password=root;Persist Security Info=True") + .Options; + + static async Task Main(/*string[] args*/) { - - + using var db = new AsbCloudDbContext(options); + + await new DetectedOperationExportService(db).Export(483, CancellationToken.None); + } } } From 081c0c50700f6ea824856f1be7f995b88d0d98f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D1=82=D0=B5=D0=BF=D0=B0=D0=BD=D0=BE=D0=B2=20=D0=94?= =?UTF-8?q?=D0=BC=D0=B8=D1=82=D1=80=D0=B8=D0=B9?= Date: Wed, 15 Nov 2023 09:33:26 +0500 Subject: [PATCH 2/3] =?UTF-8?q?=D0=9F=D1=80=D0=B0=D0=B2=D0=BA=D0=B8=20+=20?= =?UTF-8?q?=D0=BF=D0=B5=D1=80=D0=B5=D0=BD=D1=91=D1=81=20=D1=8D=D0=BA=D1=81?= =?UTF-8?q?=D0=BF=D0=BE=D1=80=D1=82=20=D0=B0=D0=B2=D1=82=D0=BE=D0=BE=D0=BF?= =?UTF-8?q?=D1=80=D0=B5=D0=B4=D0=B5=D0=BB=D1=91=D0=BD=D0=BD=D1=8B=D1=85=20?= =?UTF-8?q?=D0=BE=D0=BF=D0=B5=D1=80=D0=B0=D1=86=D0=B8=D0=B9=20=D0=B2=20?= =?UTF-8?q?=D0=B0=D0=BF=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Services/IDetectedOperationService.cs | 10 - .../AsbCloudInfrastructure.csproj | 1 + AsbCloudInfrastructure/DependencyInjection.cs | 2 + .../DetectOperations/DetectOperations.xlsx | Bin .../DetectedOperationExportService.cs | 288 ++++++++++++------ .../DetectedOperationService.cs | 9 - .../Detectors/DetectorAbstract.cs | 2 +- .../Detectors/DetectorDrilling.cs | 2 +- .../Detectors/DetectorSlipsTime.cs | 2 +- .../SAUB/DetectedOperationController.cs | 44 +-- .../DetectedOperationExportService.cs | 227 -------------- 11 files changed, 216 insertions(+), 371 deletions(-) rename ConsoleApp1/DetectedOperations/Files/Operations.xlsx => AsbCloudInfrastructure/Services/DetectOperations/DetectOperations.xlsx (100%) delete mode 100644 ConsoleApp1/DetectedOperations/DetectedOperationExportService.cs 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 From 491d9d4abee3b0ad837f423e698400bb7f5962bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D1=82=D0=B5=D0=BF=D0=B0=D0=BD=D0=BE=D0=B2=20=D0=94?= =?UTF-8?q?=D0=BC=D0=B8=D1=82=D1=80=D0=B8=D0=B9?= Date: Wed, 15 Nov 2023 09:43:13 +0500 Subject: [PATCH 3/3] =?UTF-8?q?=D0=A4=D0=B8=D0=BA=D1=81=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DetectedOperationExportService.cs | 12 ++++++------ .../Controllers/SAUB/DetectedOperationController.cs | 3 ++- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/AsbCloudInfrastructure/Services/DetectOperations/DetectedOperationExportService.cs b/AsbCloudInfrastructure/Services/DetectOperations/DetectedOperationExportService.cs index 83955b10..3c1550c1 100644 --- a/AsbCloudInfrastructure/Services/DetectOperations/DetectedOperationExportService.cs +++ b/AsbCloudInfrastructure/Services/DetectOperations/DetectedOperationExportService.cs @@ -163,12 +163,12 @@ public class DetectedOperationExportService { 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, + WellDepth = d.WellDepth, + Pressure = d.Pressure, + HookWeight = d.HookWeight, + BlockPosition = d.BlockPosition, + BitDepth = d.BitDepth, + RotorSpeed = d.RotorSpeed, }) .OrderBy(d => d.DateTime); diff --git a/AsbCloudWebApi/Controllers/SAUB/DetectedOperationController.cs b/AsbCloudWebApi/Controllers/SAUB/DetectedOperationController.cs index 11c58f92..33255f21 100644 --- a/AsbCloudWebApi/Controllers/SAUB/DetectedOperationController.cs +++ b/AsbCloudWebApi/Controllers/SAUB/DetectedOperationController.cs @@ -123,12 +123,13 @@ namespace AsbCloudWebApi.Controllers.SAUB /// /// 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, [Range(1, 2)]int idDomain, CancellationToken token) + public async Task ExportAsync(int idWell, [Range(1, 2)] int idDomain, CancellationToken token) { var idCompany = User.GetCompanyId();