diff --git a/AsbCloudApp/Data/OperationDetailsDto.cs b/AsbCloudApp/Data/OperationDetailsDto.cs new file mode 100644 index 00000000..a487df4e --- /dev/null +++ b/AsbCloudApp/Data/OperationDetailsDto.cs @@ -0,0 +1,11 @@ +using System; + +namespace AsbCloudApp.Data +{ + public class OperationDetailsDto + { + public string OperationName { get; set; } + public DateTime OperationStartTime { get; set; } + public double DurationHours { get; set; } + } +} diff --git a/AsbCloudApp/Data/OperationInfoDto.cs b/AsbCloudApp/Data/OperationInfoDto.cs new file mode 100644 index 00000000..0cfcb53e --- /dev/null +++ b/AsbCloudApp/Data/OperationInfoDto.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; + +namespace AsbCloudApp.Data +{ + public class OperationInfoDto + { + public DateTime IntervalBegin { get; set; } + public DateTime IntervalEnd { get; set; } + public IEnumerable OperationData { get; set; } + } +} diff --git a/AsbCloudApp/Services/IAnalyticsService.cs b/AsbCloudApp/Services/IAnalyticsService.cs index 3bdaad89..e8efb169 100644 --- a/AsbCloudApp/Services/IAnalyticsService.cs +++ b/AsbCloudApp/Services/IAnalyticsService.cs @@ -1,12 +1,17 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using AsbCloudApp.Data; namespace AsbCloudApp.Services { public interface IAnalyticsService { - IEnumerable GetWellDepthToDayData(int wellId); - IEnumerable GetWellDepthToIntervalData(int wellId, - double interval = 24, int beginHour = 8, int beginMinutes = 0); + IEnumerable GetWellDepthToDay(int wellId); + IEnumerable GetWellDepthToInterval(int wellId, + int intervalHours = 24, int intervalMinutes = 0, int beginHour = 8, int beginMinutes = 0); + IEnumerable GetOperationsSummary(int wellId, + DateTime begin = default, DateTime end = default); + IEnumerable GetOperationsToTime(int wellId, + DateTime begin = default, DateTime end = default); } } diff --git a/AsbCloudDb/Model/AsbCloudDbContext.cs b/AsbCloudDb/Model/AsbCloudDbContext.cs index c03e9e99..00b42f46 100644 --- a/AsbCloudDb/Model/AsbCloudDbContext.cs +++ b/AsbCloudDb/Model/AsbCloudDbContext.cs @@ -216,6 +216,33 @@ namespace AsbCloudDb.Model return (datesRange.From, datesRange.To); } + public IEnumerable<(double?, double?, DateTime)> GetDepthToInterval (int telemetryId, + int intervalHoursTimestamp, int workStartTimestamp, double timezoneOffset) + { + //TODO: Сменить на LINQ группирование + using var command = Database.GetDbConnection().CreateCommand(); + + command.CommandText = $@"SELECT Min(t.bit_depth) AS MinDepth, Max(t.bit_depth) AS MaxDepth, Min(t.Date) AS dateStart + FROM t_data_saub_base AS t + WHERE id_telemetry = {telemetryId} AND t.Id % 10 = 0 + GROUP BY floor((extract(epoch from t.date) - {workStartTimestamp} + {timezoneOffset}) / {intervalHoursTimestamp});"; + + Database.OpenConnection(); + using var reader = command.ExecuteReader(); + if (reader.HasRows) + { + while (reader.Read()) + { + yield return + ( + (double?)reader.GetValue(0), + (double?)reader.GetValue(1), + (DateTime)reader.GetValue(2) + ); + } + } + } + public async Task CreatePartitionAsync(string propertyName, int id, CancellationToken token = default) where TEntity : class { diff --git a/AsbCloudDb/Model/IAsbCloudDbContext.cs b/AsbCloudDb/Model/IAsbCloudDbContext.cs index 367b57a0..285df6e9 100644 --- a/AsbCloudDb/Model/IAsbCloudDbContext.cs +++ b/AsbCloudDb/Model/IAsbCloudDbContext.cs @@ -1,5 +1,6 @@ using Microsoft.EntityFrameworkCore; using System; +using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -28,6 +29,8 @@ namespace AsbCloudDb.Model IQueryable GetWellsByCustomer(int idCustomer); IQueryable GetUsersByLogin(string login); (DateTime From, DateTime To) GetDatesRange(int idTelemetry) where T : class, IIdTelemetryDate; + IEnumerable<(double?, double?, DateTime)> GetDepthToInterval(int telemetryId, + int intervalHoursTimestamp, int workStartTimestamp, double timezoneOffset); Task CreatePartitionAsync(string propertyName, int id, CancellationToken token = default) where TEntity : class; } } \ No newline at end of file diff --git a/AsbCloudInfrastructure/Services/AnalyticsService.cs b/AsbCloudInfrastructure/Services/AnalyticsService.cs index 8a47064a..9273a14b 100644 --- a/AsbCloudInfrastructure/Services/AnalyticsService.cs +++ b/AsbCloudInfrastructure/Services/AnalyticsService.cs @@ -2,6 +2,7 @@ using System.Linq; using AsbCloudApp.Data; using AsbCloudApp.Services; +using AsbCloudInfrastructure.Services.Cache; using AsbCloudDb.Model; using System.Collections.Generic; @@ -11,14 +12,16 @@ namespace AsbCloudInfrastructure.Services { private readonly IAsbCloudDbContext db; private readonly ITelemetryService telemetryService; + private readonly CacheTable cacheTelemetry; - public AnalyticsService(IAsbCloudDbContext db, ITelemetryService telemetryService) + public AnalyticsService(IAsbCloudDbContext db, ITelemetryService telemetryService, CacheDb cacheDb) { this.db = db; this.telemetryService = telemetryService; + cacheTelemetry = cacheDb.GetCachedTable((AsbCloudDbContext)db); } - public IEnumerable GetWellDepthToDayData(int wellId) + public IEnumerable GetWellDepthToDay(int wellId) { var telemetry = telemetryService.GetTelemetryByWellId(wellId); @@ -27,76 +30,192 @@ namespace AsbCloudInfrastructure.Services var depthToTimeData = (from d in db.DataSaubBases where d.IdTelemetry == telemetry.Id - select new WellDepthToDayDto + select new { - WellDepth = d.WellDepth, - BitDepth =d.BitDepth, - Date = d.Date - }).ToList(); + d.Id, + d.WellDepth, + d.BitDepth, + d.Date + }); - var m = (int)Math.Round(1d * depthToTimeData.Count / 2048); + var m = (int)Math.Round(1d * depthToTimeData.Count() / 2048); if (m > 1) - depthToTimeData = depthToTimeData.Where((d, i) => i % m == 0).ToList(); + depthToTimeData = depthToTimeData.Where(d => d.Id % m == 0); - return depthToTimeData; + return depthToTimeData.Select(d => new WellDepthToDayDto + { + WellDepth = d.WellDepth, + BitDepth = d.BitDepth, + Date = d.Date + }).ToList(); } - public IEnumerable GetWellDepthToIntervalData(int wellId, - double interval = 24.0, int beginHour = 8, int beginMinutes = 0) + public IEnumerable GetWellDepthToInterval(int wellId, + int intervalHours = 24, int intervalMinutes = 0, int workBeginHour = 8, int workBeginMinutes = 0) { - var intervalHours = TimeSpan.FromHours(interval); - var parseResult = TimeSpan.TryParse($"{beginHour}:{beginMinutes}", out var workDayBeginTime); - - if (!parseResult) - workDayBeginTime = TimeSpan.FromHours(8); + var intervalTime = new TimeSpan(intervalHours, intervalMinutes, 0) == default + ? new TimeSpan(24, 0, 0) + : new TimeSpan(intervalHours, intervalMinutes, 0); + var workDayBeginTime = new TimeSpan(workBeginHour, workBeginMinutes, 0) == default + ? new TimeSpan(8, 0, 0) + : new TimeSpan(intervalHours, intervalMinutes, 0); ; var telemetry = telemetryService.GetTelemetryByWellId(wellId); if (telemetry is null) return null; - var drillingData = (from d in db.DataSaubBases - where d.IdTelemetry == telemetry.Id - select new - { - d.WellDepth, - d.Date - }).ToList(); + var timezoneOffset = cacheTelemetry.FirstOrDefault(t => t.Id == telemetry.Id).Info.TimeZoneOffsetTotalHours; - if (!drillingData.Any()) - return null; + var drillingPeriodsInfo = db.GetDepthToInterval(telemetry.Id, (int)intervalTime.TotalSeconds, + (int)workDayBeginTime.TotalSeconds, timezoneOffset); - var drillingStartDate = drillingData.First().Date; - var periodStart = drillingStartDate.Hour < 8 - ? new DateTime(drillingStartDate.Year, drillingStartDate.Month, drillingStartDate.Day).AddDays(-1) + workDayBeginTime - : new DateTime(drillingStartDate.Year, drillingStartDate.Month, drillingStartDate.Day) + workDayBeginTime; - - var periodEnd = periodStart + intervalHours; - var onePeriodData = new List<(double?, DateTime)>(); - var drillingPeriods = new List>(); - - foreach (var d in drillingData) + var wellDepthToIntervalData = drillingPeriodsInfo.Select(d => new WellDepthToIntervalDto { - if (d.Date < periodEnd) - { - onePeriodData.Add((d.WellDepth, d.Date)); - continue; - } - drillingPeriods.Add(onePeriodData); - onePeriodData.Clear(); - periodStart = d.Date; - periodEnd = periodStart + intervalHours; - onePeriodData.Add((d.WellDepth, d.Date)); - } - - var wellDepthToIntervalData = drillingPeriods.Select(d => new WellDepthToIntervalDto - { - IntervalStartDate = d.FirstOrDefault().Item2, - IntervalDepthProgress = (d.Last().Item1 - d.FirstOrDefault().Item1) ?? 0.0 / interval - }); + IntervalStartDate = d.Item3, + IntervalDepthProgress = (d.Item2 - d.Item1) ?? 0.0 / intervalHours + }).OrderBy(d => d.IntervalStartDate).ToList(); return wellDepthToIntervalData; } + + public IEnumerable GetOperationsSummary(int wellId, + DateTime begin = default, DateTime end = default) + { + return new List + { + new OperationPercentageDto { ProcessName = "Роторное бурение", Percentage = 19.7 }, + new OperationPercentageDto { ProcessName = "Подъем с проработкой", Percentage = 6.2 }, + new OperationPercentageDto { ProcessName = "Спуск с проработкой", Percentage = 9.4 }, + new OperationPercentageDto { ProcessName = "Подъем с промывкой", Percentage = 18.4 }, + new OperationPercentageDto { ProcessName = "Неподвижное состояние", Percentage = 12.1 }, + new OperationPercentageDto { ProcessName = "Вращение без циркуляции", Percentage = 7.4 }, + new OperationPercentageDto { ProcessName = "Спуск в скважину", Percentage = 16.7 }, + new OperationPercentageDto { ProcessName = "На поверхности", Percentage = 10.1 } + }; + } + + public IEnumerable GetOperationsToTime(int wellId, + DateTime begin = default, DateTime end = default) + { + return new List + { + new OperationInfoDto + { + IntervalBegin = new DateTime(2021, 06, 01, 08, 00, 00), + IntervalEnd = new DateTime(2021, 06, 02, 08, 00, 00), + OperationData = new List + { + new OperationDetailsDto + { + OperationName = "Роторное бурение", + OperationStartTime = new DateTime(2021, 06, 01, 10, 00, 00), + DurationHours = 1.2 + }, + new OperationDetailsDto + { + OperationName = "Подъем с проработкой", + OperationStartTime = new DateTime(2021, 06, 01, 11, 00, 00), + DurationHours = 3.2 + }, + new OperationDetailsDto + { + OperationName = "Роторное бурение", + OperationStartTime = new DateTime(2021, 06, 01, 12, 00, 00), + DurationHours = 1.5 + }, + new OperationDetailsDto + { + OperationName = "Неподвижное состояние", + OperationStartTime = new DateTime(2021, 06, 01, 13, 00, 00), + DurationHours = 0.2 + }, + new OperationDetailsDto + { + OperationName = "Роторное бурение", + OperationStartTime = new DateTime(2021, 06, 01, 14, 00, 00), + DurationHours = 3.2 + } + } + }, + new OperationInfoDto + { + IntervalBegin = new DateTime(2021, 06, 02, 08, 00, 00), + IntervalEnd = new DateTime(2021, 06, 03, 08, 00, 00), + OperationData = new List + { + new OperationDetailsDto + { + OperationName = "На поверхности", + OperationStartTime = new DateTime(2021, 06, 13, 10, 01, 00), + DurationHours = 2.2 + }, + new OperationDetailsDto + { + OperationName = "Спуск в скважину", + OperationStartTime = new DateTime(2021, 06, 13, 11, 10, 00), + DurationHours = 0.4 + }, + new OperationDetailsDto + { + OperationName = "На поверхности", + OperationStartTime = new DateTime(2021, 06, 13, 12, 20, 00), + DurationHours = 2.5 + }, + new OperationDetailsDto + { + OperationName = "Вращение без циркуляции", + OperationStartTime = new DateTime(2021, 06, 13, 13, 00, 00), + DurationHours = 1.2 + }, + new OperationDetailsDto + { + OperationName = "Роторное бурение", + OperationStartTime = new DateTime(2021, 06, 13, 14, 00, 00), + DurationHours = 5.2 + } + } + }, + new OperationInfoDto + { + IntervalBegin = new DateTime(2021, 06, 03, 08, 00, 00), + IntervalEnd = new DateTime(2021, 06, 04, 08, 00, 00), + OperationData = new List + { + new OperationDetailsDto + { + OperationName = "Подъем с проработкой", + OperationStartTime = new DateTime(2021, 06, 12, 10, 00, 00), + DurationHours = 3.2 + }, + new OperationDetailsDto + { + OperationName = "Спуск с проработкой", + OperationStartTime = new DateTime(2021, 06, 12, 11, 00, 00), + DurationHours = 1.4 + }, + new OperationDetailsDto + { + OperationName = "Подъем с проработкой", + OperationStartTime = new DateTime(2021, 06, 12, 12, 00, 00), + DurationHours = 0.5 + }, + new OperationDetailsDto + { + OperationName = "На поверхности", + OperationStartTime = new DateTime(2021, 06, 12, 13, 00, 00), + DurationHours = 3.2 + }, + new OperationDetailsDto + { + OperationName = "Роторное бурение", + OperationStartTime = new DateTime(2021, 06, 13, 14, 00, 00), + DurationHours = 1.2 + } + } + } + }; + } } } diff --git a/AsbCloudWebApi/Controllers/AnalyticsController.cs b/AsbCloudWebApi/Controllers/AnalyticsController.cs index dc2354ad..c22eda19 100644 --- a/AsbCloudWebApi/Controllers/AnalyticsController.cs +++ b/AsbCloudWebApi/Controllers/AnalyticsController.cs @@ -28,7 +28,7 @@ namespace AsbCloudWebApi.Controllers /// Коллекцию данных по скважине "глубина-день" [HttpGet] [Route("{wellId}/wellDepthToDay")] - [ProducesResponseType(typeof(List), (int)System.Net.HttpStatusCode.OK)] + [ProducesResponseType(typeof(IEnumerable), (int)System.Net.HttpStatusCode.OK)] public IActionResult GetWellDepthToDay(int wellId) { int? idCustomer = User.GetCustomerId(); @@ -39,7 +39,7 @@ namespace AsbCloudWebApi.Controllers if (!wellService.CheckWellOwnership((int)idCustomer, wellId)) return Forbid(); - var wellDepthToDayData = analyticsService.GetWellDepthToDayData(wellId); + var wellDepthToDayData = analyticsService.GetWellDepthToDay(wellId); return Ok(wellDepthToDayData); } @@ -48,11 +48,15 @@ namespace AsbCloudWebApi.Controllers /// Возвращает данные по глубине скважины за период /// /// id скважины + /// количество часов в интервале выборки + /// количество минут в интервале выборки + /// время начала рабочей смены (в часах) + /// время начала рабочей смены (в минутах) /// Коллекцию данных по глубине скважины за период [HttpGet] [Route("{wellId}/wellDepthToInterval")] - [ProducesResponseType(typeof(List), (int)System.Net.HttpStatusCode.OK)] - public IActionResult GetWellDepthToInterval(int wellId) + [ProducesResponseType(typeof(IEnumerable), (int)System.Net.HttpStatusCode.OK)] + public IActionResult GetWellDepthToInterval(int wellId, int intervalHours, int intervalMinutes, int workBeginHour, int workBeginMinutes) { int? idCustomer = User.GetCustomerId(); @@ -62,18 +66,44 @@ namespace AsbCloudWebApi.Controllers if (!wellService.CheckWellOwnership((int)idCustomer, wellId)) return Forbid(); - var wellDepthToIntervalData = analyticsService.GetWellDepthToIntervalData(wellId); + var wellDepthToIntervalData = analyticsService.GetWellDepthToInterval(wellId, intervalHours, intervalMinutes, workBeginHour, workBeginMinutes); return Ok(wellDepthToIntervalData); } /// - /// Возвращает данные по операциям на скважине за период + /// Возвращает данные по операциям на скважине "операции-время" /// /// id скважины /// дата начала интервала /// дата окончания интервала /// Коллекцию операций на скважине + [HttpGet] + [Route("{wellId}/operationsSummary")] + [ProducesResponseType(typeof(List), (int)System.Net.HttpStatusCode.OK)] + public IActionResult GetOperationsSummary(int wellId, DateTime begin = default, DateTime end = default) + { + int? idCustomer = User.GetCustomerId(); + + if (idCustomer is null) + return BadRequest(); + + if (!wellService.CheckWellOwnership((int)idCustomer, wellId)) + return Forbid(); + + var analytics = analyticsService.GetOperationsSummary(wellId, begin, end); + + return Ok(analytics); + } + + /// + /// Возвращает детальные данные по операциям на скважине за период + /// + /// id скважины + /// дата начала интервала + /// дата окончания интервала + /// Коллекцию операций на скважине + [HttpGet] [Route("{wellId}/operationsToTime")] [ProducesResponseType(typeof(List), (int)System.Net.HttpStatusCode.OK)] @@ -87,17 +117,7 @@ namespace AsbCloudWebApi.Controllers if (!wellService.CheckWellOwnership((int)idCustomer, wellId)) return Forbid(); - var analytics = new List - { - new OperationPercentageDto { ProcessName = "Роторное бурение", Percentage = 19.7 }, - new OperationPercentageDto { ProcessName = "Подъем с проработкой", Percentage = 6.2 }, - new OperationPercentageDto { ProcessName = "Спуск с проработкой", Percentage = 9.4 }, - new OperationPercentageDto { ProcessName = "Подъем с промывкой", Percentage = 18.4 }, - new OperationPercentageDto { ProcessName = "Неподвижное состояние", Percentage = 12.1 }, - new OperationPercentageDto { ProcessName = "Вращение без циркуляции", Percentage = 7.4 }, - new OperationPercentageDto { ProcessName = "Спуск в скважину", Percentage = 16.7 }, - new OperationPercentageDto { ProcessName = "На поверхности", Percentage = 10.1 } - }; + var analytics = GetOperationsToTime(wellId, begin, end); return Ok(analytics); }