diff --git a/AsbCloudApp/Data/ReportPropertiesDto.cs b/AsbCloudApp/Data/ReportPropertiesDto.cs index a8acfe71..8b889a2d 100644 --- a/AsbCloudApp/Data/ReportPropertiesDto.cs +++ b/AsbCloudApp/Data/ReportPropertiesDto.cs @@ -9,8 +9,8 @@ namespace AsbCloudApp.Data public FileInfoDto File { get; set; } public int IdWell { get; set; } public DateTime Date { get; set; } - public DateTimeOffset Begin { get; set; } - public DateTimeOffset End { get; set; } + public DateTime Begin { get; set; } + public DateTime End { get; set; } public int Step { get; set; } public string Format { get; set; } } diff --git a/AsbCloudApp/Data/TelemetryOperationInfoDto.cs b/AsbCloudApp/Data/TelemetryOperationInfoDto.cs index 39a50e86..5ce268d7 100644 --- a/AsbCloudApp/Data/TelemetryOperationInfoDto.cs +++ b/AsbCloudApp/Data/TelemetryOperationInfoDto.cs @@ -5,7 +5,7 @@ namespace AsbCloudApp.Data { public class TelemetryOperationInfoDto { - public DateTimeOffset IntervalBegin { get; set; } + public DateTime IntervalBegin { get; set; } public IList Operations { get; set; } } } diff --git a/AsbCloudApp/Services/ITelemetryService.cs b/AsbCloudApp/Services/ITelemetryService.cs index 0cdd312f..266306b8 100644 --- a/AsbCloudApp/Services/ITelemetryService.cs +++ b/AsbCloudApp/Services/ITelemetryService.cs @@ -10,14 +10,13 @@ namespace AsbCloudApp.Services { ITimeZoneService TimeZoneService { get; } ITelemetryTracker TelemetryTracker { get; } - int? GetIdWellByTelemetryUid(string uid); int GetOrCreateTelemetryIdByUid(string uid); - double GetTimezoneOffsetByTelemetryId(int idTelemetry); - Task GetTelemetryTimeZoneOffsetAsync(int idTelemetry, CancellationToken token); + double GetTimezoneOffset(int idTelemetry); + Task GetTimeZoneOffsetAsync(int idTelemetry, CancellationToken token); IEnumerable GetTransmittingTelemetries(); - DateTime GetLastTelemetryDate(string telemetryUid); - DateTime GetLastTelemetryDate(int telemetryId); + DateTimeOffset GetLastTelemetryDate(string telemetryUid); + DateTimeOffset GetLastTelemetryDate(int telemetryId); int? GetIdTelemetryByIdWell(int idWell); Task UpdateInfoAsync(string uid, TelemetryInfoDto info, CancellationToken token); @@ -34,7 +33,7 @@ namespace AsbCloudApp.Services /// Task MergeAsync(int from, int to, CancellationToken token); - void SaveRequestDate(string uid, DateTime remoteDate); + void SaveRequestDate(string uid, DateTimeOffset remoteDate); Task GetDatesRangeAsync(int idWell, bool isUtc, CancellationToken token = default); } } \ No newline at end of file diff --git a/AsbCloudApp/Services/ITelemetryTracker.cs b/AsbCloudApp/Services/ITelemetryTracker.cs index a7aad4f2..73a98d93 100644 --- a/AsbCloudApp/Services/ITelemetryTracker.cs +++ b/AsbCloudApp/Services/ITelemetryTracker.cs @@ -6,9 +6,9 @@ namespace AsbCloudApp.Services { public interface ITelemetryTracker { - DateTime GetLastTelemetryDateByUid(string uid); + DateTimeOffset GetLastTelemetryDateByUid(string uid); DatesRangeDto GetTelemetryDateRangeByUid(string uid); IEnumerable GetTransmittingTelemetriesUids(); - void SaveRequestDate(string uid, DateTime remoteDate); + void SaveRequestDate(string uid, DateTimeOffset remoteDate); } } diff --git a/AsbCloudApp/Services/ITimeZoneService.cs b/AsbCloudApp/Services/ITimeZoneService.cs index 213aed03..4997a9f4 100644 --- a/AsbCloudApp/Services/ITimeZoneService.cs +++ b/AsbCloudApp/Services/ITimeZoneService.cs @@ -6,8 +6,6 @@ namespace AsbCloudApp.Services { public interface ITimeZoneService { - DateTime DateToUtc(DateTime date, double remoteTimezoneOffsetHours); - DateTime DateToTimeZone(DateTime date, double remoteTimezoneOffsetHours); Task GetByCoordinatesAsync(double latitude, double longitude, CancellationToken token); } } \ No newline at end of file diff --git a/AsbCloudApp/Services/IWellService.cs b/AsbCloudApp/Services/IWellService.cs index b8206976..c8ee6ae5 100644 --- a/AsbCloudApp/Services/IWellService.cs +++ b/AsbCloudApp/Services/IWellService.cs @@ -8,6 +8,8 @@ namespace AsbCloudApp.Services { public interface IWellService: ICrudService { + ITelemetryService TelemetryService { get; } + Task> GetWellsByCompanyAsync(int idCompany, CancellationToken token); Task IsCompanyInvolvedInWellAsync(int idCompany, int idWell, CancellationToken token); Task GetWellCaptionByIdAsync(int idWell, CancellationToken token); @@ -15,7 +17,8 @@ namespace AsbCloudApp.Services Task> GetCompaniesAsync(int idWell, CancellationToken token); bool IsCompanyInvolvedInWell(int idCompany, int idWell); string GetStateText(int state); - DateTime GetLastTelemetryDate(int idWell); + DateTimeOffset GetLastTelemetryDate(int idWell); Task> GetClusterWellsIdsAsync(int idWell, CancellationToken token); + double? GetTimeZoneOffset(int idWell); } } diff --git a/AsbCloudDb/Model/Telemetry.cs b/AsbCloudDb/Model/Telemetry.cs index 2f18c729..c63c8386 100644 --- a/AsbCloudDb/Model/Telemetry.cs +++ b/AsbCloudDb/Model/Telemetry.cs @@ -28,7 +28,7 @@ namespace AsbCloudDb.Model public TelemetryInfo Info { get; set; } [Column("timezone", TypeName = "jsonb"), Comment("Смещение часового пояса от UTC")] - public TelemetryTimeZone TelemetryTimeZone { get; set; } + public TelemetryTimeZone TimeZone { get; set; } [InverseProperty(nameof(Model.Well.Telemetry))] public virtual Well Well { get; set; } diff --git a/AsbCloudInfrastructure/DateTimeExtentions.cs b/AsbCloudInfrastructure/DateTimeExtentions.cs new file mode 100644 index 00000000..5e9f1423 --- /dev/null +++ b/AsbCloudInfrastructure/DateTimeExtentions.cs @@ -0,0 +1,29 @@ +using System; + +namespace AsbCloudInfrastructure +{ + public static class DateTimeExtentions + { + public static DateTimeOffset ToUtcDateTimeOffset(this DateTime date, double remoteTimezoneOffsetHours) + { + if (date == default) + return new DateTimeOffset(); + + var dateUtc = date.Kind switch + { + DateTimeKind.Local => date.ToUniversalTime(), + DateTimeKind.Unspecified => date.AddHours(-remoteTimezoneOffsetHours), + _ => date, + }; + return new DateTimeOffset(dateUtc); + } + + public static DateTime ToRemoteDateTime(this DateTimeOffset date, double remoteTimezoneOffsetHours) + { + if (date == default) + return new DateTime(0, DateTimeKind.Unspecified); + var dateTz = date.ToOffset(TimeSpan.FromHours(remoteTimezoneOffsetHours)); + return dateTz.DateTime; + } + } +} diff --git a/AsbCloudInfrastructure/ReportDataSourcePgCloud.cs b/AsbCloudInfrastructure/ReportDataSourcePgCloud.cs index 8f5bff6b..7c88a057 100644 --- a/AsbCloudInfrastructure/ReportDataSourcePgCloud.cs +++ b/AsbCloudInfrastructure/ReportDataSourcePgCloud.cs @@ -74,8 +74,8 @@ namespace AsbCloudInfrastructure var result = new AnalyzeResult { - MinDate = dataStat?.min ?? messagesStat?.min ?? default, - MaxDate = dataStat?.max ?? messagesStat?.max ?? default, + MinDate = dataStat?.min.DateTime ?? messagesStat?.min.DateTime ?? default, + MaxDate = dataStat?.max.DateTime ?? messagesStat?.max.DateTime ?? default, MessagesCount = messagesStat?.count ?? 0, }; @@ -91,7 +91,7 @@ namespace AsbCloudInfrastructure select new DataSaubReport { //Id = item.Id, - Date = item.Date, + Date = item.Date.DateTime, Mode = item.Mode, WellDepth = item.WellDepth, BitDepth = item.BitDepth, @@ -121,7 +121,7 @@ namespace AsbCloudInfrastructure select new MessageReport { Id = item.Id, - Date = item.Date, + Date = item.Date.DateTime, Category = events.GetValueOrDefault(item.IdEvent) == null ? $"" : categories[events[item.IdEvent].IdCategory], diff --git a/AsbCloudInfrastructure/Services/Analysis/DataSaubAnalyse.cs b/AsbCloudInfrastructure/Services/Analysis/DataSaubAnalyse.cs index 1e45fccc..ff8d1119 100644 --- a/AsbCloudInfrastructure/Services/Analysis/DataSaubAnalyse.cs +++ b/AsbCloudInfrastructure/Services/Analysis/DataSaubAnalyse.cs @@ -5,7 +5,7 @@ namespace AsbCloudInfrastructure.Services.Analysis class DataSaubAnalyse { public int IdTelemetry { get; internal set; } - public DateTime Date { get; internal set; } + public DateTimeOffset Date { get; internal set; } public double WellDepth { get; internal set; } public double BitDepth { get; internal set; } public double BlockPosition { get; internal set; } diff --git a/AsbCloudInfrastructure/Services/Analysis/TelemetryAnalyticsService.cs b/AsbCloudInfrastructure/Services/Analysis/TelemetryAnalyticsService.cs index 16810da1..25e20bc1 100644 --- a/AsbCloudInfrastructure/Services/Analysis/TelemetryAnalyticsService.cs +++ b/AsbCloudInfrastructure/Services/Analysis/TelemetryAnalyticsService.cs @@ -70,7 +70,7 @@ namespace AsbCloudInfrastructure.Services.Analysis if (telemetryId is null) return null; - var timezoneOffset = telemetryService.GetTimezoneOffsetByTelemetryId((int)telemetryId); + var timezoneOffset = telemetryService.GetTimezoneOffset((int)telemetryId); var drillingPeriodsInfo = await db.TelemetryDataSaub .Where(t => t.IdTelemetry == telemetryId) @@ -201,7 +201,7 @@ namespace AsbCloudInfrastructure.Services.Analysis if (telemetryId is null) return null; - var timezoneOffset = telemetryService.GetTimezoneOffsetByTelemetryId((int)telemetryId); + var timezoneOffset = telemetryService.GetTimezoneOffset((int)telemetryId); // Get'n'Group all operations only by start date and by name (if there were several operations in interval). // Without dividing these operations duration by given interval @@ -251,7 +251,7 @@ namespace AsbCloudInfrastructure.Services.Analysis } } - private async Task AnalyseAndSaveTelemetryAsync(int idTelemetry, DateTime analyzeStartDate, CancellationToken token = default) + private async Task AnalyseAndSaveTelemetryAsync(int idTelemetry, DateTimeOffset analyzeStartDate, CancellationToken token = default) { const int step = 10; const int take = step * 2; @@ -355,7 +355,7 @@ namespace AsbCloudInfrastructure.Services.Analysis return lastAnalysisDate; } - private Task> GetDataSaubPartOrDefaultAsync(int idTelemetry, DateTime analyzeStartDate, CancellationToken token) => + private Task> GetDataSaubPartOrDefaultAsync(int idTelemetry, DateTimeOffset analyzeStartDate, CancellationToken token) => db.TelemetryDataSaub .Where(d => d.IdTelemetry == idTelemetry && diff --git a/AsbCloudInfrastructure/Services/AuthService.cs b/AsbCloudInfrastructure/Services/AuthService.cs index e39a1757..6b37716f 100644 --- a/AsbCloudInfrastructure/Services/AuthService.cs +++ b/AsbCloudInfrastructure/Services/AuthService.cs @@ -5,15 +5,14 @@ using Microsoft.EntityFrameworkCore; using Microsoft.IdentityModel.Tokens; using System; using System.Collections.Generic; -using System.IdentityModel.Tokens.Jwt; using System.Linq; using System.Security.Claims; using System.Security.Cryptography; using System.Text; using System.Threading; using System.Threading.Tasks; -using AsbCloudInfrastructure.Services.Cache; using Mapster; +using System.IdentityModel.Tokens.Jwt; namespace AsbCloudInfrastructure.Services { diff --git a/AsbCloudInfrastructure/Services/MeasureService.cs b/AsbCloudInfrastructure/Services/MeasureService.cs index d1beed71..ed5c0108 100644 --- a/AsbCloudInfrastructure/Services/MeasureService.cs +++ b/AsbCloudInfrastructure/Services/MeasureService.cs @@ -95,7 +95,7 @@ namespace AsbCloudInfrastructure.Services entity.IdWell = idWell; entity.Timestamp = data.Timestamp; - entity.Data = data.Data; + entity.Data = (RawData)data.Data; return await db.SaveChangesAsync(token).ConfigureAwait(false); } diff --git a/AsbCloudInfrastructure/Services/MessageService.cs b/AsbCloudInfrastructure/Services/MessageService.cs index 7cd3e2d9..c815ceba 100644 --- a/AsbCloudInfrastructure/Services/MessageService.cs +++ b/AsbCloudInfrastructure/Services/MessageService.cs @@ -68,21 +68,20 @@ namespace AsbCloudInfrastructure.Services query = query.OrderByDescending(m => m.Date); - - var timeOffset = await telemetryService.GetTelemetryTimeZoneOffsetAsync(idTelemetry??default, token) - .ConfigureAwait(false); - - if (timeOffset is not null) - { - begin = telemetryService.TimeZoneService.DateToUtc(begin, timeOffset ?? default); - end = telemetryService.TimeZoneService.DateToUtc(end, timeOffset ?? default); - } + var timeZoneOffset = (await telemetryService.GetTimeZoneOffsetAsync(idTelemetry??default, token) + .ConfigureAwait(false)) ?? 0; if (begin != default) - query = query.Where(m => m.Date >= begin); + { + var beginUtc = telemetryService.TimeZoneService.DateToUtc(begin, timeZoneOffset); + query = query.Where(m => m.Date >= beginUtc); + } if (end != default) - query = query.Where(m => m.Date <= end); + { + var endUtc = telemetryService.TimeZoneService.DateToUtc(end, timeZoneOffset); + query = query.Where(m => m.Date <= endUtc); + } var result = new PaginationContainer { @@ -106,11 +105,14 @@ namespace AsbCloudInfrastructure.Services { var messageDto = new MessageDto { - Date = message.Date, Id = message.Id, WellDepth = message.WellDepth }; + messageDto.Date = isUtc + ? message.Date.UtcDateTime + : telemetryService.TimeZoneService.DateToTimeZone(message.Date, timeZoneOffset); + if (message.IdTelemetryUser is not null) { if (users.Any()) @@ -131,16 +133,7 @@ namespace AsbCloudInfrastructure.Services result.Items.Add(messageDto); } - - if (isUtc && timeOffset is not null) - return result; - - result.Items = result.Items.Select(m => - { - m.Date = telemetryService.TimeZoneService.DateToTimeZone( m.Date, timeOffset ?? default); - return m; - }).ToList(); - + return result; } @@ -156,15 +149,18 @@ namespace AsbCloudInfrastructure.Services return null; var telemetryId = telemetryService.GetOrCreateTelemetryIdByUid(uid); + var timeZoneOffset = telemetryService.GetTimezoneOffset(telemetryId); - var maxDateDto = dtos.Max(m => m.Date); + var maxDateDto = dtos.Max(m => m.Date); + var maxDateUtc = maxDateDto.ToUtcDateTimeOffset(timeZoneOffset); telemetryService.SaveRequestDate(uid, maxDateDto); foreach (var dto in dtos) { - var entity = dto.Adapt(); + var entity = dto.Adapt(); entity.Id = 0; entity.IdTelemetry = telemetryId; + entity.Date = dto.Date.ToUtcDateTimeOffset(timeZoneOffset); db.TelemetryMessages.Add(entity); } diff --git a/AsbCloudInfrastructure/Services/TelemetryDataBaseService.cs b/AsbCloudInfrastructure/Services/TelemetryDataBaseService.cs index 8a3024e1..289593d7 100644 --- a/AsbCloudInfrastructure/Services/TelemetryDataBaseService.cs +++ b/AsbCloudInfrastructure/Services/TelemetryDataBaseService.cs @@ -57,14 +57,13 @@ namespace AsbCloudInfrastructure.Services dtosList.Remove(duplicate); } - var offsetHours = await telemetryService.GetTelemetryTimeZoneOffsetAsync(idTelemetry, token); + var timeZoneOffset = (await telemetryService.GetTimeZoneOffsetAsync(idTelemetry, token)) ?? 0; - var entities = dtosList.Select(d => { - var e = Convert(d); - e.IdTelemetry = idTelemetry; - if(offsetHours is not null) - e.Date = telemetryService.TimeZoneService.DateToUtc(d.Date, (double)offsetHours); - return e; + var entities = dtosList.Select(dto => { + var entity = Convert(dto); + entity.Date = dto.Date.ToUtcDateTimeOffset(timeZoneOffset); + entity.IdTelemetry = idTelemetry; + return entity; }); var entityMaxDate = entities.Max(e => e.Date); @@ -79,7 +78,7 @@ namespace AsbCloudInfrastructure.Services catch(Exception ex) { stopwatch.Stop(); - Trace.WriteLine($"Fail to save data telemerty " + + Trace.WriteLine($"Fail to save data telemetry " + $"uid: {uid}, " + $"idTelemetry {idTelemetry}, " + $"count: {entities.Count()}, " + @@ -100,32 +99,31 @@ namespace AsbCloudInfrastructure.Services var idTelemetry = well?.IdTelemetry ?? default; + var timeZoneOffset = (await telemetryService.GetTimeZoneOffsetAsync(idTelemetry, token)) ?? 0d; + var filterByDateEnd = dateBegin != default; + DateTimeOffset dateBeginUtc; if (dateBegin == default) { - dateBegin = telemetryService.GetLastTelemetryDate(idTelemetry); - if (dateBegin != default) - dateBegin = dateBegin.AddSeconds(-intervalSec); - } + dateBeginUtc = telemetryService.GetLastTelemetryDate(idTelemetry) + .UtcDateTime; + if (dateBeginUtc != default) + dateBeginUtc = dateBeginUtc.AddSeconds(-intervalSec); + } + else + { + dateBeginUtc = dateBegin.ToUtcDateTimeOffset(timeZoneOffset); + } - if (dateBegin == default) - dateBegin = DateTime.Now.AddSeconds(-intervalSec); + if (dateBeginUtc == default) + dateBeginUtc = DateTime.UtcNow.AddSeconds(-intervalSec); - if (dateBegin.Kind == DateTimeKind.Unspecified) - dateBegin = DateTime.SpecifyKind(dateBegin, DateTimeKind.Utc); - - var timeOffset = await telemetryService.GetTelemetryTimeZoneOffsetAsync(idTelemetry, token) - .ConfigureAwait(false); - - if(timeOffset is not null) - dateBegin = telemetryService.TimeZoneService.DateToUtc(dateBegin, timeOffset?? default); - - var dateEnd = dateBegin.AddSeconds(intervalSec); + var dateEnd = dateBeginUtc.AddSeconds(intervalSec); var dbSet = db.Set(); var query = dbSet .Where(d => d.IdTelemetry == idTelemetry - && d.Date >= dateBegin); + && d.Date >= dateBeginUtc); if (filterByDateEnd) query = query.Where(d => d.Date < dateEnd); @@ -173,12 +171,9 @@ namespace AsbCloudInfrastructure.Services if (isUtc) return dtos; - if (timeOffset is null) - return dtos; - dtos = dtos.Select(d => { - d.Date = telemetryService.TimeZoneService.DateToTimeZone(d.Date, timeOffset ?? default); + d.Date = d.Date.ToTimeZoneOffsetHours(timeZoneOffset); return d; }); diff --git a/AsbCloudInfrastructure/Services/TelemetryService.cs b/AsbCloudInfrastructure/Services/TelemetryService.cs index 5b496a15..1ffb1048 100644 --- a/AsbCloudInfrastructure/Services/TelemetryService.cs +++ b/AsbCloudInfrastructure/Services/TelemetryService.cs @@ -16,7 +16,7 @@ namespace AsbCloudInfrastructure.Services public class TelemetryService : ITelemetryService { private readonly CacheTable cacheTelemetry; - private readonly CacheTable cacheWells;//TODO: use wellService insad of this + private readonly CacheTable cacheWells;//TODO: use wellService instead of this private readonly IAsbCloudDbContext db; private readonly ITelemetryTracker telemetryTracker; private readonly ITimeZoneService timeZoneService; @@ -55,15 +55,15 @@ namespace AsbCloudInfrastructure.Services return telemetryDtos; } - public void SaveRequestDate(string uid, DateTime remoteDate) => + public void SaveRequestDate(string uid, DateTimeOffset remoteDate) => telemetryTracker.SaveRequestDate(uid, remoteDate); - public DateTime GetLastTelemetryDate(string telemetryUid) => + public DateTimeOffset GetLastTelemetryDate(string telemetryUid) => telemetryTracker.GetLastTelemetryDateByUid(telemetryUid); - public DateTime GetLastTelemetryDate(int telemetryId) + public DateTimeOffset GetLastTelemetryDate(int telemetryId) { - var lastTelemetryDate = DateTime.MinValue; + var lastTelemetryDate = DateTimeOffset.MinValue; var telemetry = cacheTelemetry.FirstOrDefault(t => t.Id == telemetryId); if (telemetry is null) @@ -105,9 +105,6 @@ namespace AsbCloudInfrastructure.Services public int? GetIdWellByTelemetryUid(string uid) => GetWellByTelemetryUid(uid)?.Id; - public double GetTimezoneOffsetByTelemetryId(int idTelemetry) => - cacheTelemetry.FirstOrDefault(t => t.Id == idTelemetry).Info?.TimeZoneOffsetTotalHours ?? 0d; - public async Task UpdateInfoAsync(string uid, TelemetryInfoDto info, CancellationToken token) { @@ -115,8 +112,8 @@ namespace AsbCloudInfrastructure.Services telemetry.Info = info.Adapt(); if (!string.IsNullOrEmpty(info.TimeZoneId) && - telemetry.TelemetryTimeZone?.IsOverride != true) - telemetry.TelemetryTimeZone = new TelemetryTimeZone() + telemetry.TimeZone?.IsOverride != true) + telemetry.TimeZone = new TelemetryTimeZone() { Hours = info.TimeZoneOffsetTotalHours, TimeZoneId = info.TimeZoneId @@ -126,17 +123,20 @@ namespace AsbCloudInfrastructure.Services .ConfigureAwait(false); } - public async Task GetTelemetryTimeZoneOffsetAsync(int idTelemetry, CancellationToken token) + public double GetTimezoneOffset(int idTelemetry) => + cacheTelemetry.FirstOrDefault(t => t.Id == idTelemetry).Info?.TimeZoneOffsetTotalHours ?? 0d; + + public async Task GetTimeZoneOffsetAsync(int idTelemetry, CancellationToken token) { var telemetry = await cacheTelemetry.FirstOrDefaultAsync(t => t.Id == idTelemetry, token); - if (!string.IsNullOrEmpty(telemetry.TelemetryTimeZone?.TimeZoneId)) - return telemetry.TelemetryTimeZone.Hours; + if (!string.IsNullOrEmpty(telemetry.TimeZone?.TimeZoneId)) + return telemetry.TimeZone.Hours; if (!string.IsNullOrEmpty(telemetry.Info?.TimeZoneId)) { - telemetry.TelemetryTimeZone = new TelemetryTimeZone + telemetry.TimeZone = new TelemetryTimeZone { Hours = telemetry.Info.TimeZoneOffsetTotalHours, IsOverride = false, @@ -162,26 +162,26 @@ namespace AsbCloudInfrastructure.Services if (requestedTimeZone is null) return null; - telemetry.TelemetryTimeZone = requestedTimeZone.Adapt(); + telemetry.TimeZone = requestedTimeZone.Adapt(); } await cacheTelemetry.UpsertAsync(telemetry, token).ConfigureAwait(false); - return telemetry.TelemetryTimeZone.Hours; + return telemetry.TimeZone.Hours; } public async Task DatesRangeToTelemetryTimeZoneAsync(int idTelemetry, DatesRangeDto range, CancellationToken token) { - var offset = await GetTelemetryTimeZoneOffsetAsync(idTelemetry, token); + var offset = await GetTimeZoneOffsetAsync(idTelemetry, token); if (offset is null) return range; return new DatesRangeDto() { - From = timeZoneService.DateToTimeZone(range.From, offset ?? default), - To = timeZoneService.DateToTimeZone(range.To, offset ?? default), + From = range.From.ToTimeZoneOffsetHours(offset ?? default), + To = range.To.ToTimeZoneOffsetHours(offset ?? default), }; } @@ -190,7 +190,7 @@ namespace AsbCloudInfrastructure.Services { var telemetry = GetOrCreateTelemetryByUid(uid); var newTelemetryTimeZone = timeZoneInfo.Adapt(); - if (newTelemetryTimeZone?.Equals(telemetry.TelemetryTimeZone) == true) + if (newTelemetryTimeZone?.Equals(telemetry.TimeZone) == true) return; await cacheTelemetry.UpsertAsync(telemetry, token) .ConfigureAwait(false); @@ -231,7 +231,8 @@ namespace AsbCloudInfrastructure.Services if (tele is null) return null; - return cacheWells.FirstOrDefault(w => w?.IdTelemetry == tele.Id); + var well = cacheWells.FirstOrDefault(w => w?.IdTelemetry == tele.Id); + return well; } private Telemetry GetOrCreateTelemetryByUid(string uid) @@ -275,7 +276,7 @@ namespace AsbCloudInfrastructure.Services await transaction.CommitAsync(token).ConfigureAwait(false); stopwath.Stop(); - Console.WriteLine($"Successfully commited in {1d * stopwath.ElapsedMilliseconds / 1000d: #0.00} sec. Affected {affected} rows."); + Console.WriteLine($"Successfully committed in {1d * stopwath.ElapsedMilliseconds / 1000d: #0.00} sec. Affected {affected} rows."); return affected; } catch(Exception ex) diff --git a/AsbCloudInfrastructure/Services/TelemetryTracker.cs b/AsbCloudInfrastructure/Services/TelemetryTracker.cs index 801d687f..1808dea4 100644 --- a/AsbCloudInfrastructure/Services/TelemetryTracker.cs +++ b/AsbCloudInfrastructure/Services/TelemetryTracker.cs @@ -23,17 +23,17 @@ namespace AsbCloudInfrastructure.Services /// /// Время последнего запроса (по времени сервера) /// - public DateTime LastTimeServer { get; set; } + public DateTimeOffset LastTimeServer { get; set; } /// /// Дата первых данных в БД /// - public DateTime TelemetryDateMin { get; set; } + public DateTimeOffset TelemetryDateMin { get; set; } /// /// Дата последних данных в БД /// - public DateTime TelemetryDateMax { get; set; } + public DateTimeOffset TelemetryDateMax { get; set; } } @@ -88,15 +88,9 @@ namespace AsbCloudInfrastructure.Services foreach (var oldReq in oldRequests) { var telemetryStat = telemetriesStats.GetOrAdd(oldReq.Uid, (uid) => new TrackerStat { RemoteUid = uid }); - var dateMin = oldReq.DateMin.Kind == DateTimeKind.Local - ? oldReq.DateMin.ToUniversalTime() - : oldReq.DateMin; - var dateMax = oldReq.DateMax.Kind == DateTimeKind.Local - ? oldReq.DateMax.ToUniversalTime() - : oldReq.DateMax; - telemetryStat.TelemetryDateMin = dateMin; - telemetryStat.TelemetryDateMax = dateMax; - telemetryStat.LastTimeServer = dateMax; + telemetryStat.TelemetryDateMin = oldReq.DateMin; + telemetryStat.TelemetryDateMax = oldReq.DateMax; + telemetryStat.LastTimeServer = oldReq.DateMax; } }).ContinueWith((t) => { @@ -105,9 +99,9 @@ namespace AsbCloudInfrastructure.Services }); } - private static DateTime ParseDateFromUidOrDefault(string remoteUid, DateTime defaultValue = default) + private static DateTimeOffset ParseDateFromUidOrDefault(string remoteUid, DateTime defaultValue = default) { - //eg: uid = 20211102_173407926 + //example: uid = 20211102_173407926 if (string.IsNullOrEmpty(remoteUid) || (remoteUid.Length != 18)) return defaultValue; @@ -120,7 +114,7 @@ namespace AsbCloudInfrastructure.Services return defaultValue; } - public void SaveRequestDate(string uid, DateTime remoteDate) + public void SaveRequestDate(string uid, DateTimeOffset remoteDate) { var stat = telemetriesStats.GetOrAdd(uid, _ => new TrackerStat { RemoteUid = uid, @@ -133,17 +127,16 @@ namespace AsbCloudInfrastructure.Services stat.TelemetryDateMax = remoteDate; } - public DateTime GetLastTelemetryDateByUid(string uid) => + public DateTimeOffset GetLastTelemetryDateByUid(string uid) => telemetriesStats.GetValueOrDefault(uid)?.TelemetryDateMax ?? default; - public DatesRangeDto GetTelemetryDateRangeByUid(string uid) { var stat = telemetriesStats.GetValueOrDefault(uid); var range = new DatesRangeDto { - From = stat?.TelemetryDateMin ?? default, - To = stat?.TelemetryDateMax ?? default, + From = stat?.TelemetryDateMin.UtcDateTime ?? default, + To = stat?.TelemetryDateMax.UtcDateTime ?? default, }; return range; } diff --git a/AsbCloudInfrastructure/Services/TimeZoneService.cs b/AsbCloudInfrastructure/Services/TimeZoneService.cs index 9bf0465e..6400b044 100644 --- a/AsbCloudInfrastructure/Services/TimeZoneService.cs +++ b/AsbCloudInfrastructure/Services/TimeZoneService.cs @@ -61,32 +61,6 @@ namespace AsbCloudInfrastructure.Services }; } - public DateTime DateToUtc(DateTime date, double remoteTimezoneOffsetHours) - { - if (date == default) - return new DateTime(0, DateTimeKind.Utc); - var newDate = date.Kind switch - { - DateTimeKind.Local => date.ToUniversalTime(), - DateTimeKind.Unspecified => date.AddHours(-remoteTimezoneOffsetHours), - _ => date, - }; - return DateTime.SpecifyKind(newDate, DateTimeKind.Utc); - } - - public DateTime DateToTimeZone(DateTime date, double remoteTimezoneOffsetHours) - { - if (date == default) - return new DateTime(0, DateTimeKind.Unspecified); - - var newDate = date.Kind switch - { - DateTimeKind.Local => date.ToUniversalTime().AddHours(remoteTimezoneOffsetHours), - DateTimeKind.Utc => date.AddHours(remoteTimezoneOffsetHours), - _ => date, - }; - return DateTime.SpecifyKind(newDate, DateTimeKind.Unspecified); - } } } diff --git a/AsbCloudInfrastructure/Services/WellOperationService/WellOperationService.cs b/AsbCloudInfrastructure/Services/WellOperationService/WellOperationService.cs index 9ac6334e..37482077 100644 --- a/AsbCloudInfrastructure/Services/WellOperationService/WellOperationService.cs +++ b/AsbCloudInfrastructure/Services/WellOperationService/WellOperationService.cs @@ -15,12 +15,14 @@ namespace AsbCloudInfrastructure.Services.WellOperationService public class WellOperationService : IWellOperationService { private readonly IAsbCloudDbContext db; + private readonly IWellService wellService; private readonly CacheTable cachedOperationCategories; private readonly CacheTable cachedSectionTypes; - public WellOperationService(IAsbCloudDbContext db, CacheDb cache) + public WellOperationService(IAsbCloudDbContext db, CacheDb cache, IWellService wellService) { this.db = db; + this.wellService = wellService; cachedOperationCategories = cache.GetCachedTable((DbContext)db); cachedSectionTypes = cache.GetCachedTable((DbContext)db); } @@ -31,7 +33,6 @@ namespace AsbCloudInfrastructure.Services.WellOperationService public IEnumerable GetCategories() { var operationTypes = cachedOperationCategories - //.Where(oc => oc.Code > 999) .Distinct().OrderBy(o => o.Name); var result = operationTypes.Adapt(); @@ -56,6 +57,8 @@ namespace AsbCloudInfrastructure.Services.WellOperationService .Include(s => s.OperationCategory) .Where(s => s.IdWell == idWell); + var timeZoneOffset = await wellService.GetTimeZoneOffsetAsync(idWell,token); + if (operationType != default) query = query.Where(e => e.IdType == (int)operationType); @@ -72,10 +75,14 @@ namespace AsbCloudInfrastructure.Services.WellOperationService query = query.Where(e => e.DepthEnd <= maxDepth); if (begin != default) + { query = query.Where(e => e.DateStart >= begin); + } if (end != default) + { query = query.Where(e => e.DateStart <= end); + } var result = new PaginationContainer { diff --git a/AsbCloudInfrastructure/Services/WellService.cs b/AsbCloudInfrastructure/Services/WellService.cs index c459a277..77e081bb 100644 --- a/AsbCloudInfrastructure/Services/WellService.cs +++ b/AsbCloudInfrastructure/Services/WellService.cs @@ -5,6 +5,7 @@ using AsbCloudInfrastructure.Services.Cache; using Mapster; using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -28,6 +29,8 @@ namespace AsbCloudInfrastructure.Services private readonly CacheTable cacheRelationCompaniesWells; private readonly CacheTable cacheCompanyWellTypes; + public ITelemetryService TelemetryService => telemetryService; + public WellService(IAsbCloudDbContext db, CacheDb cacheDb, ITelemetryService telemetryService) :base(db, cacheDb) { @@ -40,12 +43,12 @@ namespace AsbCloudInfrastructure.Services Includes.Add(nameof(Well.WellType)); } - public DateTime GetLastTelemetryDate(int idWell) + public DateTimeOffset GetLastTelemetryDate(int idWell) { var well = Cache.FirstOrDefault(w => w.Id == idWell); if (well?.IdTelemetry is null) - return DateTime.MinValue; + return DateTimeOffset.MinValue; var lastTelemetryDate = telemetryService.GetLastTelemetryDate((int)well.IdTelemetry); return lastTelemetryDate; @@ -155,7 +158,7 @@ namespace AsbCloudInfrastructure.Services { 1 => "В работе", 2 => "Завершена", - _ => "Незвестно", + _ => "Неизвестно", }; } @@ -190,7 +193,7 @@ namespace AsbCloudInfrastructure.Services dto.WellType = entity.WellType?.Caption; dto.Cluster = entity.Cluster?.Caption; dto.Deposit = entity.Cluster?.Deposit?.Caption; - dto.LastTelemetryDate = GetLastTelemetryDate(entity.Id); + dto.LastTelemetryDate = GetLastTelemetryDate(entity.Id).DateTime; dto.Companies = GetCompanies(entity.Id); return dto; } @@ -202,5 +205,26 @@ namespace AsbCloudInfrastructure.Services ?? cacheCompanyWellTypes.FirstOrDefault(c => c.Id == entity.IdCompanyType).Caption; return dto; } + + public DateTimeOffset DateToUtc(int idWell, DateTime date) + { + var GetTimeZoneOffset(int idWell) + } + + public DateTime DateToTimeZone(DateTimeOffset date, double remoteTimezoneOffsetHours); + + public double? GetTimeZoneOffset(int idWell) + { + // TODO: Add timeZoneOffset into Db.Well. + var idTelemetry = telemetryService.GetIdTelemetryByIdWell(idWell); + if (idTelemetry is not null) + { + var timeZoneOffset = telemetryService.GetTimeZoneOffset((int)idTelemetry); + if (timeZoneOffset is not null) + return timeZoneOffset; + } + Trace.WriteLine("No timeZoneOffset"); + return null; + } } } diff --git a/AsbCloudWebApi/Controllers/MessageController.cs b/AsbCloudWebApi/Controllers/MessageController.cs index b4bbd35d..1638106e 100644 --- a/AsbCloudWebApi/Controllers/MessageController.cs +++ b/AsbCloudWebApi/Controllers/MessageController.cs @@ -46,9 +46,6 @@ namespace AsbCloudWebApi.Controllers if (take > 1024) return BadRequest("limit mast be less then 1024"); - if (begin > DateTime.Now) - begin = default; - var result = await messageService.GetMessagesAsync(idWell, categoryids, begin, end, searchString, skip, take, isUtc, token).ConfigureAwait(false); diff --git a/AsbCloudWebApi/Controllers/TelemetryDataBaseController.cs b/AsbCloudWebApi/Controllers/TelemetryDataBaseController.cs index accf1064..51cb7079 100644 --- a/AsbCloudWebApi/Controllers/TelemetryDataBaseController.cs +++ b/AsbCloudWebApi/Controllers/TelemetryDataBaseController.cs @@ -85,7 +85,7 @@ namespace AsbCloudWebApi.Controllers if (!isCompanyOwnsWell) return Forbid(); - + var content = await telemetryDataService.GetAsync(idWell, begin, intervalSec, approxPointsCount, isUtc, token).ConfigureAwait(false); diff --git a/AsbCloudWebApi/Controllers/WellOperationController.cs b/AsbCloudWebApi/Controllers/WellOperationController.cs index 6fef70ea..ced24d5a 100644 --- a/AsbCloudWebApi/Controllers/WellOperationController.cs +++ b/AsbCloudWebApi/Controllers/WellOperationController.cs @@ -132,7 +132,7 @@ namespace AsbCloudWebApi.Controllers /// id скважины /// Данные о добавляемых операциях /// Токен отмены задачи - /// Количество добавленых в БД строк + /// Количество добавленных в БД строк [HttpPost] [ProducesResponseType(typeof(IEnumerable), (int)System.Net.HttpStatusCode.OK)] public async Task InsertRangeAsync(int idWell, [FromBody] IEnumerable values, @@ -150,8 +150,8 @@ namespace AsbCloudWebApi.Controllers /// Обновляет выбранную операцию на скважине /// /// id скважины - /// id выбраной операции - /// Новые данные для выбраной операции + /// id выбранной операции + /// Новые данные для выбранной операции /// Токен отмены задачи /// Количество обновленных в БД строк [HttpPut("{idOperation}")] @@ -168,10 +168,10 @@ namespace AsbCloudWebApi.Controllers } /// - /// Удаляет выбраную операцию на скважине + /// Удаляет выбранную операцию на скважине /// /// id скважины - /// id выбраной операции + /// id выбранной операции /// Токен отмены задачи /// Количество удаленных из БД строк [HttpDelete("{idOperation}")] @@ -264,7 +264,7 @@ namespace AsbCloudWebApi.Controllers /// /// Запрашиваемый файл [HttpGet] - [Route("tamplate")] + [Route("template")] [AllowAnonymous] [ProducesResponseType(typeof(PhysicalFileResult), (int)System.Net.HttpStatusCode.OK)] public IActionResult GetTamplate() diff --git a/AsbCloudWebApi/Docs/about using DateTime[Offset].md b/AsbCloudWebApi/Docs/about using DateTime[Offset].md new file mode 100644 index 00000000..eb533447 --- /dev/null +++ b/AsbCloudWebApi/Docs/about using DateTime[Offset].md @@ -0,0 +1,34 @@ +## Проблема +Скважины и пользователи ЕЦП расположены на различных часовых поясах. +В БД время хранится только в UTC. +На страницах ЕЦП время везде должно отображаться в часовом поясе скважины. +Web Api должен понимать при обращении время как в UTC `2021-12-29T07:00:00Z`, так и с указанием часового пояса `2021-12-29T12:00:00Z5`. + +## Решение +В БД уже хранится часовой пояс скважины в таблице телеметрии. +На стороне backEnd публичные методы контроллеров и методы сервисов, которые вызываются из контроллеров должны понимать параметры времени как тип DateTime, затем приводить его к UTC. +Если DateTime.Kind == unspecified, то считается что это время указанное в часовом поясе скважины. +При переходе на DateTimeOffset флаги isUTC не нужны +Даты в Model - используют DateTimeOffset. +Даты в Dto - используют DateTime без указания часового пояса во времени скважины. + При получении Dto от фронта с kind == unspecified дата приводится к UTC как будто она в часовом поясе скважины. + Перед отправкой клиенту в Dto все даты приводятся к часовому поясу скважины и kind устанавливается как unspecified. + +## Affected +ReportController + .CreateReportAsync + .GetReportSizeAsync + .GetReportsDateRangeAsync + +TelemetryDataBaseController + .GetDataAsync + .GetDataDatesRangeAsync + +MessageController + .GetMessagesAsync + .GetMessagesDateRangeAsync + +WellOperationController + .GetOperationsAsync + .InsertRangeAsync + .UpdateAsync diff --git a/AsbCloudWebApi/wwwroot/asset-manifest.json b/AsbCloudWebApi/wwwroot/asset-manifest.json index 52bab4e6..1babaff4 100644 --- a/AsbCloudWebApi/wwwroot/asset-manifest.json +++ b/AsbCloudWebApi/wwwroot/asset-manifest.json @@ -1,24 +1,40 @@ { "files": { - "main.css": "/static/css/main.fb3df553.chunk.css", - "main.js": "/static/js/main.47b40915.chunk.js", - "main.js.map": "/static/js/main.47b40915.chunk.js.map", - "runtime-main.js": "/static/js/runtime-main.6870c5e2.js", - "runtime-main.js.map": "/static/js/runtime-main.6870c5e2.js.map", - "static/js/2.8306ac4d.chunk.js": "/static/js/2.8306ac4d.chunk.js", - "static/js/2.8306ac4d.chunk.js.map": "/static/js/2.8306ac4d.chunk.js.map", - "static/js/3.c22d92b4.chunk.js": "/static/js/3.c22d92b4.chunk.js", - "static/js/3.c22d92b4.chunk.js.map": "/static/js/3.c22d92b4.chunk.js.map", + "main.css": "/static/css/main.dd1fcee2.chunk.css", + "main.js": "/static/js/main.b9f2543d.chunk.js", + "main.js.map": "/static/js/main.b9f2543d.chunk.js.map", + "runtime-main.js": "/static/js/runtime-main.e4d23dc4.js", + "runtime-main.js.map": "/static/js/runtime-main.e4d23dc4.js.map", + "static/js/2.c978f7e7.chunk.js": "/static/js/2.c978f7e7.chunk.js", + "static/js/2.c978f7e7.chunk.js.map": "/static/js/2.c978f7e7.chunk.js.map", + "static/js/3.6446ff6d.chunk.js": "/static/js/3.6446ff6d.chunk.js", + "static/js/3.6446ff6d.chunk.js.map": "/static/js/3.6446ff6d.chunk.js.map", + "static/js/4.eb6dbaeb.chunk.js": "/static/js/4.eb6dbaeb.chunk.js", + "static/js/4.eb6dbaeb.chunk.js.map": "/static/js/4.eb6dbaeb.chunk.js.map", + "static/js/5.7a9fb752.chunk.js": "/static/js/5.7a9fb752.chunk.js", + "static/js/5.7a9fb752.chunk.js.map": "/static/js/5.7a9fb752.chunk.js.map", + "static/js/6.61991758.chunk.js": "/static/js/6.61991758.chunk.js", + "static/js/6.61991758.chunk.js.map": "/static/js/6.61991758.chunk.js.map", + "static/js/7.a1c2547f.chunk.js": "/static/js/7.a1c2547f.chunk.js", + "static/js/7.a1c2547f.chunk.js.map": "/static/js/7.a1c2547f.chunk.js.map", + "static/js/8.2a766587.chunk.js": "/static/js/8.2a766587.chunk.js", + "static/js/8.2a766587.chunk.js.map": "/static/js/8.2a766587.chunk.js.map", + "static/js/9.6ba456d9.chunk.js": "/static/js/9.6ba456d9.chunk.js", + "static/js/9.6ba456d9.chunk.js.map": "/static/js/9.6ba456d9.chunk.js.map", + "static/js/10.f5187699.chunk.js": "/static/js/10.f5187699.chunk.js", + "static/js/10.f5187699.chunk.js.map": "/static/js/10.f5187699.chunk.js.map", + "static/js/11.37e8bbc7.chunk.js": "/static/js/11.37e8bbc7.chunk.js", + "static/js/11.37e8bbc7.chunk.js.map": "/static/js/11.37e8bbc7.chunk.js.map", "index.html": "/index.html", - "static/css/main.fb3df553.chunk.css.map": "/static/css/main.fb3df553.chunk.css.map", - "static/js/2.8306ac4d.chunk.js.LICENSE.txt": "/static/js/2.8306ac4d.chunk.js.LICENSE.txt", + "static/css/main.dd1fcee2.chunk.css.map": "/static/css/main.dd1fcee2.chunk.css.map", + "static/js/2.c978f7e7.chunk.js.LICENSE.txt": "/static/js/2.c978f7e7.chunk.js.LICENSE.txt", "static/media/ClusterIcon.a395f860.svg": "/static/media/ClusterIcon.a395f860.svg", "static/media/DepositIcon.6de7c7ae.svg": "/static/media/DepositIcon.6de7c7ae.svg" }, "entrypoints": [ - "static/js/runtime-main.6870c5e2.js", - "static/js/2.8306ac4d.chunk.js", - "static/css/main.fb3df553.chunk.css", - "static/js/main.47b40915.chunk.js" + "static/js/runtime-main.e4d23dc4.js", + "static/js/2.c978f7e7.chunk.js", + "static/css/main.dd1fcee2.chunk.css", + "static/js/main.b9f2543d.chunk.js" ] } \ No newline at end of file diff --git a/AsbCloudWebApi/wwwroot/index.html b/AsbCloudWebApi/wwwroot/index.html index 3e80849f..eba35987 100644 --- a/AsbCloudWebApi/wwwroot/index.html +++ b/AsbCloudWebApi/wwwroot/index.html @@ -1 +1 @@ -АСБ Vision
\ No newline at end of file +АСБ Vision
\ No newline at end of file