From 6738bb3b35bebf4215d50e524cc7079b4085705b Mon Sep 17 00:00:00 2001 From: ngfrolov Date: Fri, 22 Apr 2022 17:17:38 +0500 Subject: [PATCH] Add DetectorService --- .../DetectOperations/DetectableTelemetry.cs | 16 +++ .../DetectOperations/DetectedOperation.cs | 19 +++ .../DetectOperations/DetectorAbstract.cs | 70 +++++++++++ .../DetectOperations/DetectorService.cs | 118 ++++++++++++++++++ .../Detectors/DetectorDrillingRotor.cs | 42 +++++++ .../Detectors/DetectorDrillingSlide.cs | 41 ++++++ .../Detectors/DetectorSlipsTime.cs | 36 ++++++ .../DetectOperations/InterpolationLine.cs | 55 ++++++++ ConsoleApp1/Program.cs | 11 +- ConsoleApp1/ServiceFactory.cs | 6 + 10 files changed, 412 insertions(+), 2 deletions(-) create mode 100644 AsbCloudInfrastructure/Services/DetectOperations/DetectableTelemetry.cs create mode 100644 AsbCloudInfrastructure/Services/DetectOperations/DetectedOperation.cs create mode 100644 AsbCloudInfrastructure/Services/DetectOperations/DetectorAbstract.cs create mode 100644 AsbCloudInfrastructure/Services/DetectOperations/DetectorService.cs create mode 100644 AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorDrillingRotor.cs create mode 100644 AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorDrillingSlide.cs create mode 100644 AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorSlipsTime.cs create mode 100644 AsbCloudInfrastructure/Services/DetectOperations/InterpolationLine.cs diff --git a/AsbCloudInfrastructure/Services/DetectOperations/DetectableTelemetry.cs b/AsbCloudInfrastructure/Services/DetectOperations/DetectableTelemetry.cs new file mode 100644 index 00000000..f357396a --- /dev/null +++ b/AsbCloudInfrastructure/Services/DetectOperations/DetectableTelemetry.cs @@ -0,0 +1,16 @@ +using System; + +namespace AsbCloudInfrastructure.Services.DetectOperations +{ + public class DetectableTelemetry + { + public DateTimeOffset DateTime { get; set; } + public int? IdUser { get; set; } + public float? WellDepth { get; set; } + public float? Pressure { get; set; } + public float? HookWeight { get; set; } + public float? BlockPosition { get; set; } + public float? BitDepth { get; set; } + public float? RotorSpeed { get; set; } + } +} diff --git a/AsbCloudInfrastructure/Services/DetectOperations/DetectedOperation.cs b/AsbCloudInfrastructure/Services/DetectOperations/DetectedOperation.cs new file mode 100644 index 00000000..9eb6aaae --- /dev/null +++ b/AsbCloudInfrastructure/Services/DetectOperations/DetectedOperation.cs @@ -0,0 +1,19 @@ +using System; + +namespace AsbCloudInfrastructure.Services.DetectOperations +{ + public class DetectedOperation + { + public int IdType { get; set; } + public int IdUsersAtStart { get; set; } + public DateTimeOffset Begin { get; set; } + public DateTimeOffset End { get; set; } + public double DurationMinutes => (End - Begin).TotalMinutes; + public double BeginWellDepth { get; set; } + public double EndWellDepth { get; set; } + + public override string ToString() + => $"{IdType}\t{Begin:G}\t{End:G}\t{DurationMinutes:#0.#}\t{BeginWellDepth:#0.#}\t{EndWellDepth:#0.#}"; + //=> $"{{\"type\": {IdType},\t\"begin\":\"{Begin:G}\",\t\"end\":\"{End:G}\",\t\"depthBegin\":\"{BeginWellDepth:#0.#}\",\t\"depthEnd\":\"{EndWellDepth:#0.#}\"}}"; + } +} \ No newline at end of file diff --git a/AsbCloudInfrastructure/Services/DetectOperations/DetectorAbstract.cs b/AsbCloudInfrastructure/Services/DetectOperations/DetectorAbstract.cs new file mode 100644 index 00000000..dfb434d3 --- /dev/null +++ b/AsbCloudInfrastructure/Services/DetectOperations/DetectorAbstract.cs @@ -0,0 +1,70 @@ +using AsbCloudDb.Model; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace AsbCloudInfrastructure.Services.DetectOperations +{ + +#nullable enable + abstract class DetectorAbstract + { + /* ## Концепт определения операций + * Есть словарь детекторов. + * Определить начальную позицию архива. + * Пройти детекторами до конца архива. + * Каждый детектор на небольшому фрагменту определяет начало операции. + * При нахождении начала ищет конец и смещает положение поиска + */ + public int StepLength { get; set; } = 3; + public int FragmentLength { get; set; } = 6; + public int IdOperationType { get; } + public string OperationName { get; } + + // TODO: assert MaxDurationSeconds and MinDurationSeconds + public double MaxDurationSeconds { get; } = 31 * 24 * 60 * 60; + public double MinDurationSeconds { get; } = 3; + + public DetectorAbstract(int idType, string operationName) + { + IdOperationType = idType; + OperationName = operationName; + } + + public virtual DetectedOperation? DetectOrDefault(DetectableTelemetry[] telemetry, ref int position) + { + if ((telemetry.Length > position + FragmentLength + StepLength) && DetectStart(telemetry, position)) + { + var skip = position + StepLength; + while (telemetry.Length > skip + FragmentLength) + { + if (DetectEnd(telemetry, skip)) + { + var result = new DetectedOperation + { + IdType = IdOperationType, + IdUsersAtStart = telemetry[position].IdUser ?? -1, + Begin = telemetry[position].DateTime, + End = telemetry[skip].DateTime, + BeginWellDepth = telemetry[position].WellDepth ?? -1d, + EndWellDepth = telemetry[skip].WellDepth ?? -1d, + }; + position = skip + FragmentLength; + return result; + } + + skip = skip + StepLength; + } + } + return null; + } + + protected abstract bool DetectStart(DetectableTelemetry[] telemetry, int position); + + protected abstract bool DetectEnd(DetectableTelemetry[] telemetry, int position); + + } +#nullable disable +} diff --git a/AsbCloudInfrastructure/Services/DetectOperations/DetectorService.cs b/AsbCloudInfrastructure/Services/DetectOperations/DetectorService.cs new file mode 100644 index 00000000..0e8fe1d3 --- /dev/null +++ b/AsbCloudInfrastructure/Services/DetectOperations/DetectorService.cs @@ -0,0 +1,118 @@ +using AsbCloudDb.Model; +using Microsoft.EntityFrameworkCore; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace AsbCloudInfrastructure.Services.DetectOperations +{ + public class DetectorService + { + readonly IEnumerable detectors = new List + { + new Detectors.DetectorSlipsTime(), + // new Detectors.DetectorDrillingRotor(), + // new Detectors.DetectorDrillingSlide(), + }; + + private readonly int minStepLength; + private readonly int minFragmentLength; + + public DetectorService() + { + minStepLength = detectors.Min(d => d.StepLength); + minStepLength = minStepLength > 0 ? minStepLength : 3; + + minFragmentLength = detectors.Min(d => d.FragmentLength); + minFragmentLength = minFragmentLength > 0 ? minFragmentLength : 6; + } + + public async Task> DetectOperationsAsync(int idTelemetry, DateTimeOffset begin, IAsbCloudDbContext db, CancellationToken token) + { + var query = db.TelemetryDataSaub + .AsNoTracking() + .Where(d => d.IdTelemetry == idTelemetry) + .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 take = 4 * 86_400; + var startDate = begin; + var detectedOperations = new List(8); + + var dbRequests_ = 0; + var dbTime_ = 0d; + var sw_ = new System.Diagnostics.Stopwatch(); + var otherTime_ = 0d; + + while (true) + { + sw_.Restart(); + var data = await query + .Where(d => d.DateTime > startDate) + .Take(take) + .ToArrayAsync(token); + + sw_.Stop(); + dbTime_ += sw_.ElapsedMilliseconds; + dbRequests_++; + sw_.Restart(); + + if (data.Length < minFragmentLength) + break; + + var skip = 0; + + var isDetected = false; + + while (data.Length > skip + minFragmentLength) + { + var isDetected1 = false; + + foreach (var detector in detectors) + { + if(data.Length < skip + detector.StepLength + detector.FragmentLength) + continue; + + var detectedOperation = detector.DetectOrDefault(data, ref skip); + if (detectedOperation is not null) + { + isDetected1 = true; + isDetected = true; + detectedOperations.Add(detectedOperation); + startDate = detectedOperation.End; + break; + } + } + + if (!isDetected1) + skip += minStepLength; + } + + sw_.Stop(); + otherTime_ += sw_.ElapsedMilliseconds; + + if (!isDetected) + { + if (data.Length < take) + break; + + var lastPartDate = data.Last().DateTime; + startDate = startDate + (0.75 * (lastPartDate - startDate)); + } + } + + return detectedOperations; + } + } +} diff --git a/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorDrillingRotor.cs b/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorDrillingRotor.cs new file mode 100644 index 00000000..10412dc4 --- /dev/null +++ b/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorDrillingRotor.cs @@ -0,0 +1,42 @@ +using System.Linq; + +namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors +{ +#nullable enable + class DetectorDrillingRotor : DetectorAbstract + { + public DetectorDrillingRotor() : base(10_002, "Бурение в роторе") + { + FragmentLength = 15; + StepLength = 10; + } + + protected override bool DetectStart(DetectableTelemetry[] telemetry, int position) + { + var fragment = telemetry[position .. (position + FragmentLength)]; + + const double minRop = 5; //м/час + const double minRotorSpeed = 5; //об/мин + const double ticksPerHour = 60 * 60 * 10_000_000d; + + var lineBlockPosition = new InterpolationLine(fragment.Select(d => (d.BlockPosition ?? 0d, d.DateTime.Ticks / ticksPerHour))); + if (!lineBlockPosition.IsYDecreases(minRop)) + return false; + + var lineWellDepth = new InterpolationLine(fragment.Select(d => (d.WellDepth ?? 0d, d.DateTime.Ticks / ticksPerHour))); + if(!lineWellDepth.IsYIncreases(minRop)) + return false; + + var lineRotorSpeed = new InterpolationLine(fragment.Select(d => (d.RotorSpeed ?? 0d, d.DateTime.Ticks / ticksPerHour))); + if (!lineRotorSpeed.IsAverageYLessThanBound(minRotorSpeed)) + return false; + + return true; + } + + protected override bool DetectEnd(DetectableTelemetry[] telemetry, int position) + => !DetectStart(telemetry, position); + + } +#nullable disable +} diff --git a/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorDrillingSlide.cs b/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorDrillingSlide.cs new file mode 100644 index 00000000..ff20f6c9 --- /dev/null +++ b/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorDrillingSlide.cs @@ -0,0 +1,41 @@ +using System.Linq; + +namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors +{ +#nullable enable + class DetectorDrillingSlide : DetectorAbstract + { + public DetectorDrillingSlide() : base(10_003, "Бурение в слайде") + { + FragmentLength = 10; + } + + protected override bool DetectStart(DetectableTelemetry[] telemetry, int position) + { + var fragment = telemetry[position .. (position + FragmentLength)]; + + const double minRop = 5; //м/час + const double minRotorSpeed = 5; //об/мин + const double ticksPerHour = 60 * 60 * 10_000_000d; + + var lineBlockPosition = new InterpolationLine(fragment.Select(d => (d.BlockPosition ?? 0d, d.DateTime.Ticks / ticksPerHour))); + if (!lineBlockPosition.IsYDecreases(minRop)) + return false; + + var lineWellDepth = new InterpolationLine(fragment.Select(d => (d.WellDepth ?? 0d, d.DateTime.Ticks / ticksPerHour))); + if(!lineWellDepth.IsYIncreases(minRop)) + return false; + + var lineRotorSpeed = new InterpolationLine(fragment.Select(d => (d.RotorSpeed ?? 0d, d.DateTime.Ticks / ticksPerHour))); + if (!lineRotorSpeed.IsAverageYMoreThanBound(minRotorSpeed)) + return false; + + return true; + } + + protected override bool DetectEnd(DetectableTelemetry[] telemetry, int position) + => !DetectStart(telemetry, position); + + } +#nullable disable +} diff --git a/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorSlipsTime.cs b/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorSlipsTime.cs new file mode 100644 index 00000000..d04ccbc3 --- /dev/null +++ b/AsbCloudInfrastructure/Services/DetectOperations/Detectors/DetectorSlipsTime.cs @@ -0,0 +1,36 @@ +namespace AsbCloudInfrastructure.Services.DetectOperations.Detectors +{ +#nullable enable + class DetectorSlipsTime : DetectorAbstract + { + public DetectorSlipsTime() : base(10_001, "Удержание в клиньях") + { + } + + public double HookWeightSP { get; set; } = 20; + public double PressureSP { get; set; } = 15; + public double PosisionSP { get; set; } = 8; + + protected override bool DetectStart(DetectableTelemetry[] telemetry, int position) + { + var item = telemetry[position]; + + var result = item.HookWeight < HookWeightSP && + item.Pressure < PressureSP && + item.BlockPosition < PosisionSP; + + return result; + } + + protected override bool DetectEnd(DetectableTelemetry[] telemetry, int position) + { + var item = telemetry[position]; + + var result = item.Pressure > PressureSP && + item.BlockPosition > PosisionSP; + + return result; + } + } +#nullable disable +} diff --git a/AsbCloudInfrastructure/Services/DetectOperations/InterpolationLine.cs b/AsbCloudInfrastructure/Services/DetectOperations/InterpolationLine.cs new file mode 100644 index 00000000..e7ab4fec --- /dev/null +++ b/AsbCloudInfrastructure/Services/DetectOperations/InterpolationLine.cs @@ -0,0 +1,55 @@ +using System.Collections.Generic; + +namespace AsbCloudInfrastructure.Services.DetectOperations +{ + public class InterpolationLine + { + private readonly double xSum; + private readonly double ySum; + private readonly double xySum; + private readonly double x2Sum; + private readonly int count; + + public InterpolationLine(IEnumerable<(double Y, double X)> rawData) + { + var iterator = rawData.GetEnumerator(); + while (iterator.MoveNext()) + { + xSum += iterator.Current.X; + ySum += iterator.Current.Y; + xySum += iterator.Current.X * iterator.Current.Y; + x2Sum += iterator.Current.X * iterator.Current.X; + count++; + } + } + + /// + /// tan(alpha) + /// + public double A => + (xSum * ySum - count * xySum) / + (xSum * xSum - count * x2Sum); + + /// + /// y offset + /// + public double B => + (xSum * xySum - x2Sum * ySum) / + (xSum * xSum - count * x2Sum); + + public bool IsYNotChanges(double upperBound = 0d, double lowerBound = 0d) => + A < upperBound && A > lowerBound; + + public bool IsYIncreases(double bound = 0d) => + A > bound; + + public bool IsYDecreases(double bound = 0d) => + A < bound; + + public bool IsAverageYLessThanBound(double bound) => + (ySum / count) < bound; + + public bool IsAverageYMoreThanBound(double bound) => + (ySum / count) >= bound; + } +} diff --git a/ConsoleApp1/Program.cs b/ConsoleApp1/Program.cs index 1eaeb875..9da430b2 100644 --- a/ConsoleApp1/Program.cs +++ b/ConsoleApp1/Program.cs @@ -1,16 +1,23 @@ using System; +using System.Linq; +using System.Threading; namespace ConsoleApp1 { - class Program { static void Main(/*string[] args*/) { // use ServiceFactory to make services + var db = ServiceFactory.MakeContext(); + var service = new AsbCloudInfrastructure.Services.DetectOperations.DetectorService(); + var start = new DateTimeOffset(2021, 1, 30, 23, 00, 00, TimeSpan.FromHours(0)); + var operations = service.DetectOperationsAsync(146, start, db, CancellationToken.None).Result; + foreach (var operation in operations) + Console.WriteLine(operation); - Console.WriteLine("End of Test"); + Console.WriteLine("End of program"); Console.ReadLine(); } } diff --git a/ConsoleApp1/ServiceFactory.cs b/ConsoleApp1/ServiceFactory.cs index 3fb9f1f6..b71f727e 100644 --- a/ConsoleApp1/ServiceFactory.cs +++ b/ConsoleApp1/ServiceFactory.cs @@ -50,8 +50,14 @@ namespace ConsoleApp1 static TimezoneService TimezoneService { get; } = new TimezoneService(); public static AsbCloudDbContext Context { get; } = MakeContext(); public static AsbCloudDbContext MakeContext() + => MakeContext(options); + + public static AsbCloudDbContext MakeContext(DbContextOptions options) => new AsbCloudDbContext(options); + public static AsbCloudDbContext MakeContext(string cusomConnectionString) + => MakeContext(new DbContextOptionsBuilder().UseNpgsql(cusomConnectionString).Options); + public static void MapsterSetup() => AsbCloudInfrastructure.DependencyInjection.MapsterSetup();