diff --git a/AsbCloudApp/Services/IAnalyticsService.cs b/AsbCloudApp/Services/IAnalyticsService.cs index e8efb169..dccfd467 100644 --- a/AsbCloudApp/Services/IAnalyticsService.cs +++ b/AsbCloudApp/Services/IAnalyticsService.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using AsbCloudApp.Data; +using AsbCloudDb.Model; namespace AsbCloudApp.Services { @@ -13,5 +14,6 @@ namespace AsbCloudApp.Services DateTime begin = default, DateTime end = default); IEnumerable GetOperationsToTime(int wellId, DateTime begin = default, DateTime end = default); + DrillingAnalysis GetDrillingAnalysis(IEnumerable dataSaubBases); } } diff --git a/AsbCloudApp/Services/ISaubDataCache.cs b/AsbCloudApp/Services/ISaubDataCache.cs new file mode 100644 index 00000000..0f01fd49 --- /dev/null +++ b/AsbCloudApp/Services/ISaubDataCache.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; +using AsbCloudDb.Model; + +namespace AsbCloudApp.Services +{ + public interface ISaubDataCache + { + Dictionary> GetSaubData(); + } +} diff --git a/AsbCloudDb/Model/AsbCloudDbContext.cs b/AsbCloudDb/Model/AsbCloudDbContext.cs index 00b42f46..c762892a 100644 --- a/AsbCloudDb/Model/AsbCloudDbContext.cs +++ b/AsbCloudDb/Model/AsbCloudDbContext.cs @@ -25,6 +25,8 @@ namespace AsbCloudDb.Model public virtual DbSet UserRoles { get; set; } public virtual DbSet Wells { get; set; } public virtual DbSet Reports { get; set; } + public virtual DbSet Operations { get; set; } + public virtual DbSet DrillingAnalysis { get; set; } //public AsbCloudDbContext(string connectionString = "Host=localhost;Database=postgres;Username=postgres;Password=q;Persist Security Info=True") //{ @@ -86,6 +88,20 @@ namespace AsbCloudDb.Model .HasConstraintName("t_telemetry_user_t_telemetry_id_fk"); }); + modelBuilder.Entity(entity => + { + entity.HasOne(d => d.Telemetry) + .WithMany(p => p.Analysis) + .HasForeignKey(d => d.IdTelemetry) + .HasConstraintName("t_analysis_t_telemetry_id_fk"); + + entity.HasOne(d => d.Operation) + .WithMany(p => p.Analysis) + .HasForeignKey(d => d.IdOperation) + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("t_analysis_t_operation_id_fk"); + }); + modelBuilder.Entity(entity => { entity.HasKey(nameof(Event.IdTelemetry), nameof(Event.IdEvent)); @@ -178,6 +194,29 @@ namespace AsbCloudDb.Model new Well{Id = 2, IdCluster = 1, IdCustomer = 1, Caption = "скв 2" }, }); }); + + modelBuilder.Entity(entity => + { + entity.HasData(new List { + new Operation {Id = 1, Name = "Невозможно определить операцию"}, + new Operation {Id = 2, Name = "Роторное бурение" }, + new Operation {Id = 3, Name = "Слайдирование" }, + new Operation {Id = 4, Name = "Подъем с проработкой" }, + new Operation {Id = 5, Name = "Спуск с проработкой" }, + new Operation {Id = 6, Name = "Подъем с промывкой" }, + new Operation {Id = 7, Name = "Спуск с промывкой" }, + new Operation {Id = 8, Name = "Спуск в скважину" }, + new Operation {Id = 9, Name = "Спуск с вращением" }, + new Operation {Id = 10, Name = "Подъем из скважины" }, + new Operation {Id = 11, Name = "Подъем с вращением" }, + new Operation {Id = 12, Name = "Промывка в покое" }, + new Operation {Id = 13, Name = "Промывка с вращением" }, + new Operation {Id = 14, Name = "Удержание в клиньях" }, + new Operation {Id = 15, Name = "Неподвижное состояние" }, + new Operation {Id = 16, Name = "Вращение без циркуляции" }, + new Operation {Id = 17, Name = "На поверхности" } + }); + }); } public IQueryable GetWellsByCustomer(int idCustomer) diff --git a/AsbCloudDb/Model/DrillingAnalysis.cs b/AsbCloudDb/Model/DrillingAnalysis.cs new file mode 100644 index 00000000..8e171981 --- /dev/null +++ b/AsbCloudDb/Model/DrillingAnalysis.cs @@ -0,0 +1,82 @@ +using System; +using Microsoft.EntityFrameworkCore; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Text.Json.Serialization; + +namespace AsbCloudDb.Model +{ + + [Table("t_analysis"), Comment("События на скважине")] + public class DrillingAnalysis + { + [Key] + [Column("id")] + public int Id { get; set; } + + [Column("id_telemetry")] + public int IdTelemetry { get; set; } + + [Column("date", TypeName = "timestamp with time zone"), Comment("'2021-10-19 18:23:54+05'")] + public DateTime Date { get; set; } + + [Column("id_operation")] + public int? IdOperation { get; set; } + + + [JsonIgnore] + [ForeignKey(nameof(IdTelemetry))] + [InverseProperty(nameof(Model.Telemetry.Analysis))] + public virtual Telemetry Telemetry { get; set; } + + [JsonIgnore] + [ForeignKey(nameof(IdOperation))] + [InverseProperty(nameof(Model.Operation.Analysis))] + public virtual Operation Operation { get; set; } + + [Column("depth_changes"), Comment("Глубина забоя увеличивается")] + public bool IsDepthChanges { get; set; } + + [Column("depth_not_changes"), Comment("Глубина забоя не увеличивается")] + public bool IsDepthNotChanges { get; set; } + + [Column("bit_is_rising"), Comment("Долото поднимается")] + public bool IsBitRising { get; set; } + + [Column("bit_goes_down"), Comment("Глубина спускается")] + public bool IsBitGoesDown { get; set; } + + [Column("bit_stands_still"), Comment("Положение долота не изменяется")] + public bool IsBitStandsStill { get; set; } + + [Column("bit_depth_less_20"), Comment("Положение долота меньше 20м")] + public bool IsBitDepthLess20 { get; set; } + + [Column("block_is_rising"), Comment("Талевый блок поднимается")] + public bool IsBlockRising { get; set; } + + [Column("block_goes_down"), Comment("Талевый блок спускается")] + public bool IsBlockGoesDown { get; set; } + + [Column("block_stands_still"), Comment("Положение блок не изменяется")] + public bool IsBlockStandsStill { get; set; } + + [Column("rotor_speed_less_3"), Comment("Обороты ротора ниже 3")] + public bool IsRotorSpeedLess3 { get; set; } + + [Column("rotor_speed_more_3"), Comment("Обороты ротора выше 3")] + public bool IsRotorSpeedMore3 { get; set; } + + [Column("pressure_less_20"), Comment("Давление менее 20")] + public bool IsPressureLess20 { get; set; } + + [Column("pressure_more_20"), Comment("Давоение более 20")] + public bool IsPressureMore20 { get; set; } + + [Column("hook_weight_not_changes"), Comment("Вес на крюке не меняется")] + public bool IsHookWeightNotChanges { get; set; } + + [Column("hook_weight_less_3"), Comment("Вес на крюке менее 3т")] + public bool IsHookWeightLess3 { get; set; } + } +} diff --git a/AsbCloudDb/Model/IAsbCloudDbContext.cs b/AsbCloudDb/Model/IAsbCloudDbContext.cs index 285df6e9..1341bf61 100644 --- a/AsbCloudDb/Model/IAsbCloudDbContext.cs +++ b/AsbCloudDb/Model/IAsbCloudDbContext.cs @@ -21,6 +21,8 @@ namespace AsbCloudDb.Model DbSet Wells { get; set; } DbSet UserRoles { get; set; } DbSet Reports { get; set; } + DbSet Operations { get; set; } + DbSet DrillingAnalysis { get; set; } int SaveChanges(); int SaveChanges(bool acceptAllChangesOnSuccess); diff --git a/AsbCloudDb/Model/Operation.cs b/AsbCloudDb/Model/Operation.cs new file mode 100644 index 00000000..02a7a78e --- /dev/null +++ b/AsbCloudDb/Model/Operation.cs @@ -0,0 +1,20 @@ +using Microsoft.EntityFrameworkCore; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Collections.Generic; + +namespace AsbCloudDb.Model +{ + [Table("t_operation"), Comment("Справочник операций на скважине")] + public class Operation + { + [Key] + [Column("id")] + public int Id { get; set; } + [Column("name"), Comment("Название операции")] + public string Name { get; set; } + + [InverseProperty(nameof(DrillingAnalysis.Operation))] + public virtual ICollection Analysis { get; set; } + } +} diff --git a/AsbCloudDb/Model/Telemetry.cs b/AsbCloudDb/Model/Telemetry.cs index 5c6f7e59..e1bfb43d 100644 --- a/AsbCloudDb/Model/Telemetry.cs +++ b/AsbCloudDb/Model/Telemetry.cs @@ -43,5 +43,7 @@ namespace AsbCloudDb.Model [InverseProperty(nameof(Event.Telemetry))] public virtual ICollection Events { get; set; } + [InverseProperty(nameof(DrillingAnalysis.Telemetry))] + public virtual ICollection Analysis { get; set; } } } diff --git a/AsbCloudInfrastructure/DependencyInjection.cs b/AsbCloudInfrastructure/DependencyInjection.cs index 63d56782..62c481ba 100644 --- a/AsbCloudInfrastructure/DependencyInjection.cs +++ b/AsbCloudInfrastructure/DependencyInjection.cs @@ -25,6 +25,7 @@ namespace AsbCloudInfrastructure services.AddSingleton(new CacheDb()); services.AddSingleton(); services.AddSingleton(); + services.AddSingleton(); services.AddTransient(); services.AddTransient(); diff --git a/AsbCloudInfrastructure/Services/AnalyticsService.cs b/AsbCloudInfrastructure/Services/AnalyticsService.cs index 9273a14b..5293e987 100644 --- a/AsbCloudInfrastructure/Services/AnalyticsService.cs +++ b/AsbCloudInfrastructure/Services/AnalyticsService.cs @@ -13,12 +13,18 @@ namespace AsbCloudInfrastructure.Services private readonly IAsbCloudDbContext db; private readonly ITelemetryService telemetryService; private readonly CacheTable cacheTelemetry; + private readonly CacheTable cacheOperations; + private readonly IEnumerable operationDetectors; + private readonly IEnumerable operations; public AnalyticsService(IAsbCloudDbContext db, ITelemetryService telemetryService, CacheDb cacheDb) { this.db = db; this.telemetryService = telemetryService; cacheTelemetry = cacheDb.GetCachedTable((AsbCloudDbContext)db); + cacheOperations = cacheDb.GetCachedTable((AsbCloudDbContext)db); + operations = cacheOperations.Select(c => true); + operationDetectors = new OperationDetectorsContainer(operations).Detectors; } public IEnumerable GetWellDepthToDay(int wellId) @@ -217,5 +223,80 @@ namespace AsbCloudInfrastructure.Services } }; } + + public DrillingAnalysis GetDrillingAnalysis(IEnumerable dataSaubBases) + { + var saubWellDepths = dataSaubBases.Select(s => s.WellDepth); + var saubBitDepths = dataSaubBases.Select(s => s.BitDepth); + var saubBlockPositions = dataSaubBases.Select(s => s.BlockPosition); + var saubRotorSpeeds = dataSaubBases.Select(s => s.RotorSpeed); + var saubPressures = dataSaubBases.Select(s => s.Pressure); + var saubHookWeights = dataSaubBases.Select(s => s.HookWeight); + + var wellDepthChangingIndex = GetAForLinearFormula(saubWellDepths); + var bitPositionChangingIndex = GetAForLinearFormula(saubBitDepths); + var blockPositionChangingIndex = GetAForLinearFormula(saubBlockPositions); + var rotorSpeedChangingIndex = GetAForLinearFormula(saubRotorSpeeds); + var pressureChangingIndex = GetAForLinearFormula(saubPressures); + var hookWeightChangingIndex = GetAForLinearFormula(saubHookWeights); + + var drillingAnalysis = new DrillingAnalysis + { + IdTelemetry = dataSaubBases.First().IdTelemetry, + Date = dataSaubBases.Last().Date, + IsDepthChanges = wellDepthChangingIndex >= 1.0 || wellDepthChangingIndex <= -1.0, + IsDepthNotChanges = wellDepthChangingIndex < 1.0 && wellDepthChangingIndex > -1.0, + IsBitRising = bitPositionChangingIndex <= -1.0, + IsBitGoesDown = bitPositionChangingIndex >= 1.0, + IsBitStandsStill = bitPositionChangingIndex < 1.0 && bitPositionChangingIndex > -1.0, + IsBitDepthLess20 = (saubBitDepths.Sum() / saubBitDepths.Count()) < 20.0, + IsBlockRising = blockPositionChangingIndex <= -1.0, + IsBlockGoesDown = blockPositionChangingIndex >= 1.0, + IsBlockStandsStill = blockPositionChangingIndex < 1.0 && blockPositionChangingIndex > -1.0, + IsRotorSpeedLess3 = (saubRotorSpeeds.Sum() / saubRotorSpeeds.Count()) < 3.0, + IsRotorSpeedMore3 = (saubRotorSpeeds.Sum() / saubRotorSpeeds.Count()) >= 3.0, + IsPressureLess20 = (saubPressures.Sum() / saubPressures.Count()) < 20.0, + IsPressureMore20 = (saubPressures.Sum() / saubPressures.Count()) >= 20.0, + IsHookWeightNotChanges = hookWeightChangingIndex < 1.0 && hookWeightChangingIndex > 1.0, + IsHookWeightLess3 = (saubHookWeights.Sum() / saubHookWeights.Count()) < 3.0, + IdOperation = 1 + }; + + drillingAnalysis.IdOperation = GetOperation(drillingAnalysis).Id; + + return drillingAnalysis; + } + + private Operation GetOperation(DrillingAnalysis data) + { + var operation = operationDetectors.OrderBy(d => d.Order).First(o => o.Detect(data)).Operation + ?? new Operation { Id = 1, Name = "Невозможно определить операцию" }; + + return operations.FirstOrDefault(o => o.Name.Equals(operation.Name)); + } + + private static double GetAForLinearFormula(IEnumerable rawData) + { + var (xSum, ySum, xySum, x2Sum) = GetFormulaVariables(rawData); + + return (xSum * ySum - rawData.Count() * xySum) / + (xSum * xSum - rawData.Count() * x2Sum); + } + + private static (int xSum, double ySum, double xySum, int x2Sum) GetFormulaVariables( + IEnumerable rawData) + { + var data = rawData.Select((d, i) => new + { + X = i, + Y = d ?? 0.0 + }); + var xSum = data.Sum(d => d.X); + var ySum = data.Sum(d => d.Y); + var xySum = data.Sum(d => d.X * d.Y); + var x2Sum = data.Sum(d => d.X * d.X); + + return (xSum, ySum, xySum, x2Sum); + } } } diff --git a/AsbCloudInfrastructure/Services/DataService.cs b/AsbCloudInfrastructure/Services/DataService.cs index 9a066f90..d8ecaf59 100644 --- a/AsbCloudInfrastructure/Services/DataService.cs +++ b/AsbCloudInfrastructure/Services/DataService.cs @@ -13,14 +13,20 @@ namespace AsbCloudInfrastructure.Services { private readonly IAsbCloudDbContext db; private readonly ITelemetryService telemetryService; + private readonly IAnalyticsService analyticsService; + private readonly ISaubDataCache saubEventsCache; private readonly IMapper mapper; private readonly CacheTable cacheTelemetry; private readonly CacheTable cacheWells; - public DataService(IAsbCloudDbContext db, ITelemetryService telemetryService, CacheDb cacheDb, MapperConfiguration mapperConfiguration) + public DataService(IAsbCloudDbContext db, ITelemetryService telemetryService, + IAnalyticsService analyticsService, ISaubDataCache saubEventsCache, + CacheDb cacheDb, MapperConfiguration mapperConfiguration) { this.db = db; this.telemetryService = telemetryService; + this.analyticsService = analyticsService; + this.saubEventsCache = saubEventsCache; mapper = mapperConfiguration.CreateMapper(); cacheTelemetry = cacheDb.GetCachedTable((AsbCloudDbContext)db); cacheWells = cacheDb.GetCachedTable((AsbCloudDbContext)db); @@ -92,6 +98,24 @@ namespace AsbCloudInfrastructure.Services var dataSaub = mapper.Map(item); dataSaub.IdTelemetry = telemetryId; db.DataSaubBases.Add(dataSaub); + + if (!saubEventsCache.GetSaubData().ContainsKey(dataSaub.IdTelemetry)) + saubEventsCache.GetSaubData()[dataSaub.IdTelemetry] = new List(); + + var cachedSaubData = saubEventsCache.GetSaubData()[dataSaub.IdTelemetry]; + + cachedSaubData.Add(dataSaub); + + if (cachedSaubData.Count > 1) + { + if (cachedSaubData.Count > 10) + cachedSaubData.RemoveAt(1); + + var drillingAnalysis = analyticsService.GetDrillingAnalysis( + cachedSaubData); + + db.DrillingAnalysis.Add(drillingAnalysis); + } } db.SaveChanges(); diff --git a/AsbCloudInfrastructure/Services/OperationDetector.cs b/AsbCloudInfrastructure/Services/OperationDetector.cs new file mode 100644 index 00000000..0f15c484 --- /dev/null +++ b/AsbCloudInfrastructure/Services/OperationDetector.cs @@ -0,0 +1,12 @@ +using System; +using AsbCloudDb.Model; + +namespace AsbCloudInfrastructure.Services +{ + public class OperationDetector + { + public int Order { get; set; } + public Operation Operation { get; set; } + public Func Detect { get; set; } + } +} diff --git a/AsbCloudInfrastructure/Services/OperationDetectorsContainer.cs b/AsbCloudInfrastructure/Services/OperationDetectorsContainer.cs new file mode 100644 index 00000000..bbec1bf9 --- /dev/null +++ b/AsbCloudInfrastructure/Services/OperationDetectorsContainer.cs @@ -0,0 +1,163 @@ +using System.Collections.Generic; +using System.Linq; +using AsbCloudDb.Model; + +namespace AsbCloudInfrastructure.Services +{ + public class OperationDetectorsContainer + { + public IEnumerable Detectors; + + public OperationDetectorsContainer(IEnumerable operations) + { + Detectors = new List() + { + new OperationDetector + { + Order = 1, + Operation = operations.FirstOrDefault(o => o.Name.Equals("На поверхности")), + Detect = (data) => + { + return data.IsDepthNotChanges && data.IsBitDepthLess20 && data.IsHookWeightLess3; + } + }, + new OperationDetector + { + Order = 2, + Operation = operations.FirstOrDefault(o => o.Name.Equals("Удержание в клиньях")), + Detect = (data) => + { + return data.IsDepthNotChanges && data.IsBitStandsStill && + data.IsBlockStandsStill && data.IsHookWeightLess3; + } + }, + new OperationDetector + { + Order = 3, + Operation = operations.FirstOrDefault(o => o.Name.Equals("Подъем с проработкой")), + Detect = (data) => + { + return data.IsDepthNotChanges && data.IsBitRising && + data.IsBlockRising && data.IsRotorSpeedMore3 && data.IsPressureMore20; + } + }, + new OperationDetector + { + Order = 4, + Operation = operations.FirstOrDefault(o => o.Name.Equals("Спуск с проработкой")), + Detect = (data) => + { + return data.IsDepthNotChanges && data.IsBitGoesDown && + data.IsBlockGoesDown && data.IsRotorSpeedMore3 && data.IsPressureMore20; + } + }, + new OperationDetector + { + Order = 5, + Operation = operations.FirstOrDefault(o => o.Name.Equals("Подъем с промывкой")), + Detect = (data) => + { + return data.IsDepthNotChanges && data.IsBitRising && + data.IsBlockRising && data.IsRotorSpeedLess3 && data.IsPressureMore20; + } + }, + new OperationDetector + { + Order = 6, + Operation = operations.FirstOrDefault(o => o.Name.Equals("Спуск с промывкой")), + Detect = (data) => + { + return data.IsDepthNotChanges && data.IsBitGoesDown && + data.IsBlockGoesDown && data.IsRotorSpeedLess3 && data.IsPressureMore20; + } + }, + new OperationDetector + { + Order = 7, + Operation = operations.FirstOrDefault(o => o.Name.Equals("Спуск в скважину")), + Detect = (data) => + { + return data.IsDepthNotChanges && data.IsBitGoesDown && + data.IsBlockGoesDown && data.IsRotorSpeedLess3 && data.IsPressureLess20; + } + }, + new OperationDetector + { + Order = 8, + Operation = operations.FirstOrDefault(o => o.Name.Equals("Спуск с вращением")), + Detect = (data) => + { + return data.IsDepthNotChanges && data.IsBitGoesDown && + data.IsBlockGoesDown && data.IsRotorSpeedMore3 && data.IsPressureLess20; + } + }, + new OperationDetector + { + Order = 9, + Operation = operations.FirstOrDefault(o => o.Name.Equals("Подъем из скважины")), + Detect = (data) => + { + return data.IsDepthNotChanges && data.IsBitRising && + data.IsBlockRising && data.IsRotorSpeedLess3 && data.IsPressureLess20; + } + }, + new OperationDetector + { + Order = 10, + Operation = operations.FirstOrDefault(o => o.Name.Equals("Подъем с вращением")), + Detect = (data) => + { + return data.IsDepthNotChanges && data.IsBitRising && + data.IsBlockRising && data.IsRotorSpeedMore3 && data.IsPressureLess20; + } + }, + new OperationDetector + { + Order = 11, + Operation = operations.FirstOrDefault(o => o.Name.Equals("Промывка в покое")), + Detect = (data) => + { + return data.IsDepthNotChanges && data.IsBitStandsStill && + data.IsBlockStandsStill && data.IsRotorSpeedLess3 && data.IsPressureMore20; + } + }, + new OperationDetector + { + Order = 12, + Operation = operations.FirstOrDefault(o => o.Name.Equals("Промывка с вращением")), + Detect = (data) => + { + return data.IsDepthNotChanges && data.IsBitStandsStill && + data.IsBlockStandsStill && data.IsRotorSpeedMore3 && data.IsPressureMore20; + } + }, + new OperationDetector + { + Order = 13, + Operation = operations.FirstOrDefault(o => o.Name.Equals("Неподвижное состояние")), + Detect = (data) => + { + return data.IsDepthNotChanges && data.IsBitStandsStill && data.IsBlockStandsStill + && data.IsRotorSpeedLess3 && data.IsPressureLess20 && data.IsHookWeightNotChanges; + } + }, + new OperationDetector + { + Order = 14, + Operation = operations.FirstOrDefault(o => o.Name.Equals("Вращение без циркуляции")), + Detect = (data) => + { + return data.IsDepthNotChanges && data.IsBitStandsStill && + data.IsBlockStandsStill && data.IsRotorSpeedMore3 && data.IsPressureLess20; + } + }, + new OperationDetector + { + Order = 15, + Operation = operations.FirstOrDefault(o => o.Name.Equals("Невозможно определить операцию")), + Detect = (data) => true + } + }; + } + } +} diff --git a/AsbCloudInfrastructure/Services/SaubEventsCache.cs b/AsbCloudInfrastructure/Services/SaubEventsCache.cs new file mode 100644 index 00000000..f265e4ce --- /dev/null +++ b/AsbCloudInfrastructure/Services/SaubEventsCache.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; +using AsbCloudApp.Services; +using AsbCloudDb.Model; + +namespace AsbCloudInfrastructure.Services +{ + public class SaubEventsCache : ISaubDataCache + { + private readonly Dictionary> saubData = + new Dictionary>(); + + public Dictionary> GetSaubData() => + saubData; + } +} diff --git a/AsbCloudWebApi/Controllers/AnalyticsController.cs b/AsbCloudWebApi/Controllers/AnalyticsController.cs index c22eda19..8ffb62c4 100644 --- a/AsbCloudWebApi/Controllers/AnalyticsController.cs +++ b/AsbCloudWebApi/Controllers/AnalyticsController.cs @@ -80,7 +80,7 @@ namespace AsbCloudWebApi.Controllers /// Коллекцию операций на скважине [HttpGet] [Route("{wellId}/operationsSummary")] - [ProducesResponseType(typeof(List), (int)System.Net.HttpStatusCode.OK)] + [ProducesResponseType(typeof(IEnumerable), (int)System.Net.HttpStatusCode.OK)] public IActionResult GetOperationsSummary(int wellId, DateTime begin = default, DateTime end = default) { int? idCustomer = User.GetCustomerId(); @@ -106,7 +106,7 @@ namespace AsbCloudWebApi.Controllers [HttpGet] [Route("{wellId}/operationsToTime")] - [ProducesResponseType(typeof(List), (int)System.Net.HttpStatusCode.OK)] + [ProducesResponseType(typeof(IEnumerable), (int)System.Net.HttpStatusCode.OK)] public IActionResult GetOperationsToTime(int wellId, DateTime begin = default, DateTime end = default) { int? idCustomer = User.GetCustomerId(); diff --git a/AsbCloudWebApi/Docs/Алгоритм_определения_операций_буровой.ods b/AsbCloudWebApi/Docs/Алгоритм_определения_операций_буровой.ods new file mode 100644 index 00000000..4779d5f2 Binary files /dev/null and b/AsbCloudWebApi/Docs/Алгоритм_определения_операций_буровой.ods differ diff --git a/AsbCloudWebApi/Docs/Алгоритм_определения_операций_буровой.pdf b/AsbCloudWebApi/Docs/Алгоритм_определения_операций_буровой.pdf new file mode 100644 index 00000000..c6531eea Binary files /dev/null and b/AsbCloudWebApi/Docs/Алгоритм_определения_операций_буровой.pdf differ