forked from ddrilling/AsbCloudServer
Add DetectorService
This commit is contained in:
parent
f76cc52fc8
commit
6738bb3b35
@ -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; }
|
||||
}
|
||||
}
|
@ -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.#}\"}}";
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
@ -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
|
||||
}
|
@ -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
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user