Add DetectorService

This commit is contained in:
ngfrolov 2022-04-22 17:17:38 +05:00
parent f76cc52fc8
commit 6738bb3b35
10 changed files with 412 additions and 2 deletions

View File

@ -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; }
}
}

View File

@ -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.#}\"}}";
}
}

View File

@ -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
}

View File

@ -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<DetectorAbstract> detectors = new List<DetectorAbstract>
{
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<IEnumerable<DetectedOperation>> 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<DetectedOperation>(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;
}
}
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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++;
}
}
/// <summary>
/// tan(alpha)
/// </summary>
public double A =>
(xSum * ySum - count * xySum) /
(xSum * xSum - count * x2Sum);
/// <summary>
/// y offset
/// </summary>
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;
}
}

View File

@ -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();
}
}

View File

@ -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<AsbCloudDbContext> options)
=> new AsbCloudDbContext(options);
public static AsbCloudDbContext MakeContext(string cusomConnectionString)
=> MakeContext(new DbContextOptionsBuilder<AsbCloudDbContext>().UseNpgsql(cusomConnectionString).Options);
public static void MapsterSetup()
=> AsbCloudInfrastructure.DependencyInjection.MapsterSetup();