From db1bd49d605d1be0a7383d745b5108d50a946b20 Mon Sep 17 00:00:00 2001 From: ngfrolov Date: Thu, 9 Mar 2023 12:32:09 +0500 Subject: [PATCH] Add TelemetryDataSaubController.GetZippedCsv(..) --- AsbCloudApp/Data/TelemetryDto.cs | 5 ++ .../Services/ITelemetryDataSaubService.cs | 12 +++ .../Services/SAUB/CsvSerializer.cs | 85 +++++++++++++++++++ .../Services/SAUB/TelemetryDataSaubService.cs | 43 +++++++++- .../Services/SAUB/TelemetryTracker.cs | 2 +- .../SAUB/TelemetryDataBaseController.cs | 2 +- .../SAUB/TelemetryDataSaubController.cs | 36 ++++++++ ConsoleApp1/Program.cs | 25 ++---- 8 files changed, 186 insertions(+), 24 deletions(-) create mode 100644 AsbCloudInfrastructure/Services/SAUB/CsvSerializer.cs diff --git a/AsbCloudApp/Data/TelemetryDto.cs b/AsbCloudApp/Data/TelemetryDto.cs index d243c749..e4e3be80 100644 --- a/AsbCloudApp/Data/TelemetryDto.cs +++ b/AsbCloudApp/Data/TelemetryDto.cs @@ -19,6 +19,11 @@ namespace AsbCloudApp.Data /// информация о бурении, панели оператора и контроллерах /// public TelemetryInfoDto? Info { get; set; } + + /// + /// Смещение часового пояса от UTC + /// + public SimpleTimezoneDto? TimeZone { get; set; } } /// diff --git a/AsbCloudApp/Services/ITelemetryDataSaubService.cs b/AsbCloudApp/Services/ITelemetryDataSaubService.cs index f8d9c3be..a0bb271d 100644 --- a/AsbCloudApp/Services/ITelemetryDataSaubService.cs +++ b/AsbCloudApp/Services/ITelemetryDataSaubService.cs @@ -1,5 +1,7 @@ using AsbCloudApp.Data.SAUB; +using System; using System.Collections.Generic; +using System.IO; using System.Threading; using System.Threading.Tasks; @@ -20,5 +22,15 @@ namespace AsbCloudApp.Services /// /// Task> GetTelemetryDataStatAsync(int idTelemetry, CancellationToken token); + + /// + /// Получить упакованый csv файл + /// + /// + /// + /// + /// + /// + Task GetZippedCsv(int idWell, DateTime beginDate, DateTime endDate, CancellationToken token); } } \ No newline at end of file diff --git a/AsbCloudInfrastructure/Services/SAUB/CsvSerializer.cs b/AsbCloudInfrastructure/Services/SAUB/CsvSerializer.cs new file mode 100644 index 00000000..a7b5a80f --- /dev/null +++ b/AsbCloudInfrastructure/Services/SAUB/CsvSerializer.cs @@ -0,0 +1,85 @@ +using DocumentFormat.OpenXml.Drawing.Diagrams; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text.RegularExpressions; + +namespace System.Text.Csv +{ +#nullable enable + public class CsvSerializer + { + private readonly PropertyInfo[] props; + private readonly static Regex numbers = new Regex(@"^[0-9\-\+\.]+$"); + + public string Separator { get; set; } = ";"; + public string NewLine { get; set; } = "\r\n"; + public string Quot { get; set; } = "\""; + public Encoding Encoding { get; set; } = Encoding.UTF8; + public string FloatingPointFormat { get; set; } = "#0.000#"; + public string DateTimeFormat { get; set; } = "yyyy-MM-dd HH:mm:ss"; + public string objDateTimeOffsetFormat { get; set; } = "yyyy-MM-dd HH:mm:ss zzz"; + public string TimeOnlyFormat { get; set; } = "HH:mm:ss"; + public string DateOnlyFormat { get; set; } = "yyyy-MM-dd"; + + public CsvSerializer() + { + props = typeof(T).GetProperties(); + } + + public void Serialize(IEnumerable data, Stream toStream) + { + if (!data.Any()) + return; + + if(!props.Any()) + return; + + void HandleRow(IEnumerable rowData) + { + var row = string.Join(Separator, rowData); + var bytes = Encoding.GetBytes(row + NewLine); + toStream.Write(bytes); + } + + + HandleRow(props.Select(p => p.Name)); + + foreach ( var item in data) + HandleRow(props.Select(p => CsvSerializer.Escape(Fromat(p.GetValue(item))))); + } + + private string Fromat(object? obj) + { + if (obj is double objDouble) + return objDouble.ToString(FloatingPointFormat); + + if (obj is float objfloat) + return objfloat.ToString(FloatingPointFormat); + + if (obj is DateTime objDateTime) + return objDateTime.ToString(DateTimeFormat); + + if (obj is DateTimeOffset objDateTimeOffset) + return objDateTimeOffset.ToString(objDateTimeOffsetFormat); + + if (obj is DateOnly objDateOnly) + return objDateOnly.ToString(DateOnlyFormat); + + if (obj is TimeOnly objTimeOnly) + return objTimeOnly.ToString(TimeOnlyFormat); + + return obj?.ToString() ?? string.Empty; + } + + private static string Escape(string inString) + { + if (numbers.IsMatch(inString)) + return inString; + + return $"\"{inString}\""; + } + } +#nullable disable +} diff --git a/AsbCloudInfrastructure/Services/SAUB/TelemetryDataSaubService.cs b/AsbCloudInfrastructure/Services/SAUB/TelemetryDataSaubService.cs index 33899211..885edc03 100644 --- a/AsbCloudInfrastructure/Services/SAUB/TelemetryDataSaubService.cs +++ b/AsbCloudInfrastructure/Services/SAUB/TelemetryDataSaubService.cs @@ -1,4 +1,6 @@ -using AsbCloudApp.Data.SAUB; +using AsbCloudApp.Data; +using AsbCloudApp.Data.SAUB; +using AsbCloudApp.Exceptions; using AsbCloudApp.Services; using AsbCloudDb.Model; using Mapster; @@ -6,7 +8,10 @@ using Microsoft.EntityFrameworkCore; using System; using System.Collections.Generic; using System.IO; +using System.IO.Compression; using System.Linq; +using System.Text; +using System.Text.Csv; using System.Threading; using System.Threading.Tasks; @@ -99,10 +104,40 @@ namespace AsbCloudInfrastructure.Services.SAUB return dto; } - public Stream GetZippedCsv(int idWell, DateTime beginDate, DateTime endDate) + public async Task GetZippedCsv(int idWell, DateTime beginDate, DateTime endDate, CancellationToken token) { - var telemetry = telemetryService.GetOrDefaultTelemetryByIdWell(idWell); - return null; + double intervalSec = (endDate - beginDate).TotalSeconds; + if (intervalSec > 60*60*24*3) + throw new ArgumentInvalidException("Слишком большой диапазон", nameof(endDate)); + + var telemetry = telemetryService.GetOrDefaultTelemetryByIdWell(idWell) + ?? throw new ArgumentInvalidException($"Скважина id:{idWell} не содержит телеметрии", nameof(idWell)); + + var approxPointsCount = intervalSec switch + { + < 2048 => 2048, + < 8_192 => 4_096, + < 131_072 => 16_384, + _ => 32_768 + }; + + var data = await GetOrDefaultAsync(idWell, beginDate, intervalSec, approxPointsCount, token ); + + var fileName = $"DataSaub idWell{idWell}"; + if (telemetry.Info is not null) + fileName += $" {telemetry.Info?.Cluster}, {telemetry.Info?.Well}"; + fileName += $" {beginDate:yyyy-MM-DDTHH-mm} - {endDate:yyyy-MM-DDTHH-mm}.csv"; + + var outStream = new MemoryStream(); + using (var archive = new ZipArchive(outStream, ZipArchiveMode.Create, true)) + { + var entryFile = archive.CreateEntry(fileName, CompressionLevel.Optimal); + using var entryStream = entryFile.Open(); + var serializer = new CsvSerializer(); + serializer.Serialize(data, entryStream); + } + outStream.Seek(0, SeekOrigin.Begin); + return outStream; } } #nullable disable diff --git a/AsbCloudInfrastructure/Services/SAUB/TelemetryTracker.cs b/AsbCloudInfrastructure/Services/SAUB/TelemetryTracker.cs index b7d5eefe..ff5ca394 100644 --- a/AsbCloudInfrastructure/Services/SAUB/TelemetryTracker.cs +++ b/AsbCloudInfrastructure/Services/SAUB/TelemetryTracker.cs @@ -47,7 +47,7 @@ namespace AsbCloudInfrastructure.Services.SAUB .Options; var db = new AsbCloudDbContext(contextOptions); - var cacheTelemetry = memoryCache.GetOrCreateBasic(db.Set()); + var cacheTelemetry = memoryCache.GetOrCreateBasic(db.Set().Include(t=>t.Well)); var keyValuePairs = new Dictionary(cacheTelemetry.Count()); foreach (var telemetry in cacheTelemetry) { diff --git a/AsbCloudWebApi/Controllers/SAUB/TelemetryDataBaseController.cs b/AsbCloudWebApi/Controllers/SAUB/TelemetryDataBaseController.cs index 15e15f9b..7b210d71 100644 --- a/AsbCloudWebApi/Controllers/SAUB/TelemetryDataBaseController.cs +++ b/AsbCloudWebApi/Controllers/SAUB/TelemetryDataBaseController.cs @@ -18,9 +18,9 @@ namespace AsbCloudWebApi.Controllers.SAUB public abstract class TelemetryDataBaseController : ControllerBase where TDto : ITelemetryData { + protected readonly IWellService wellService; private readonly ITelemetryService telemetryService; private readonly ITelemetryDataService telemetryDataService; - private readonly IWellService wellService; private readonly IHubContext telemetryHubContext; public string SirnalRMethodGetDataName { get; protected set; } = "ReceiveData"; diff --git a/AsbCloudWebApi/Controllers/SAUB/TelemetryDataSaubController.cs b/AsbCloudWebApi/Controllers/SAUB/TelemetryDataSaubController.cs index 4248778b..3d20a057 100644 --- a/AsbCloudWebApi/Controllers/SAUB/TelemetryDataSaubController.cs +++ b/AsbCloudWebApi/Controllers/SAUB/TelemetryDataSaubController.cs @@ -3,6 +3,11 @@ using AsbCloudApp.Services; using AsbCloudWebApi.SignalR; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.SignalR; +using System.IO; +using System.Threading.Tasks; +using System.Threading; +using AsbCloudInfrastructure.Services; +using System; namespace AsbCloudWebApi.Controllers.SAUB { @@ -13,6 +18,8 @@ namespace AsbCloudWebApi.Controllers.SAUB [ApiController] public class TelemetryDataSaubController : TelemetryDataBaseController { + private readonly ITelemetryDataSaubService telemetryDataSaubService; + public TelemetryDataSaubController( ITelemetryService telemetryService, ITelemetryDataSaubService telemetryDataService, @@ -25,6 +32,35 @@ namespace AsbCloudWebApi.Controllers.SAUB telemetryHubContext) { SirnalRMethodGetDataName = "ReceiveDataSaub"; + telemetryDataSaubService = telemetryDataService; + } + + /// + /// Выгрузка архива. Не более 3-х суток. Формат даты строгий. + /// + /// id скважины (из адресной строки) + /// начало интервала в формате: yyyy-MM-DD HH:mm + /// конец интервала в формате: yyyy-MM-DD HH:mm + /// + /// + [HttpGet("{idWell}/export/csv")] + [ProducesResponseType(typeof(PhysicalFileResult), (int)System.Net.HttpStatusCode.OK)] + public async Task GetZippedCsv(int idWell, DateTime beginDate, DateTime endDate, CancellationToken token) + { + int? idCompany = User.GetCompanyId(); + + if (idCompany is null) + return Forbid(); + + bool isCompanyOwnsWell = await wellService.IsCompanyInvolvedInWellAsync((int)idCompany, + idWell, token).ConfigureAwait(false); + + if (!isCompanyOwnsWell) + return Forbid(); + + var stream = await telemetryDataSaubService.GetZippedCsv(idWell, beginDate, endDate, token).ConfigureAwait(false); + var fileName = $"DataSaub idWell{idWell} {beginDate:yyyy-MM-DDTHH-mm} - {endDate:yyyy-MM-DDTHH-mm}.zip"; + return File(stream, "application/octet-stream", fileName); } } } diff --git a/ConsoleApp1/Program.cs b/ConsoleApp1/Program.cs index 9c018348..9d4ff2c6 100644 --- a/ConsoleApp1/Program.cs +++ b/ConsoleApp1/Program.cs @@ -1,7 +1,5 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; +using System.IO; +using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using AsbCloudApp.Data; @@ -19,20 +17,11 @@ namespace ConsoleApp1 { static void Main(/*string[] args*/) { - var db = ServiceFactory.Context; - var q = db.TelemetryDataSaub - .Select(t => new { - t.IdTelemetry, - t.DateTime, - t.WellDepth, - }) - .GroupBy(t => t.IdTelemetry) - .Select(g => new { - Id = g.Key, - First = g.OrderBy(t => t.DateTime).FirstOrDefault(), - Last = g.OrderBy(t => t.DateTime).LastOrDefault(), - }); - var d = q.AsNoTracking().ToArray(); + + var n = "-159.99"; + var s = "159.99s"; + var r1 = reg.IsMatch( n ); + var r2 = reg.IsMatch( s ); } } }