не доделано перед праздниками

This commit is contained in:
Фролов 2021-12-30 17:05:44 +05:00
parent 962d6e15b6
commit 4872b4a495
26 changed files with 244 additions and 179 deletions

View File

@ -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; }
}

View File

@ -5,7 +5,7 @@ namespace AsbCloudApp.Data
{
public class TelemetryOperationInfoDto
{
public DateTimeOffset IntervalBegin { get; set; }
public DateTime IntervalBegin { get; set; }
public IList<TelemetryOperationDetailsDto> Operations { get; set; }
}
}

View File

@ -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<double?> GetTelemetryTimeZoneOffsetAsync(int idTelemetry, CancellationToken token);
double GetTimezoneOffset(int idTelemetry);
Task<double?> GetTimeZoneOffsetAsync(int idTelemetry, CancellationToken token);
IEnumerable<TelemetryDto> 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
/// <returns></returns>
Task<int> MergeAsync(int from, int to, CancellationToken token);
void SaveRequestDate(string uid, DateTime remoteDate);
void SaveRequestDate(string uid, DateTimeOffset remoteDate);
Task<DatesRangeDto> GetDatesRangeAsync(int idWell, bool isUtc, CancellationToken token = default);
}
}

View File

@ -6,9 +6,9 @@ namespace AsbCloudApp.Services
{
public interface ITelemetryTracker
{
DateTime GetLastTelemetryDateByUid(string uid);
DateTimeOffset GetLastTelemetryDateByUid(string uid);
DatesRangeDto GetTelemetryDateRangeByUid(string uid);
IEnumerable<string> GetTransmittingTelemetriesUids();
void SaveRequestDate(string uid, DateTime remoteDate);
void SaveRequestDate(string uid, DateTimeOffset remoteDate);
}
}

View File

@ -6,8 +6,6 @@ namespace AsbCloudApp.Services
{
public interface ITimeZoneService
{
DateTime DateToUtc(DateTime date, double remoteTimezoneOffsetHours);
DateTime DateToTimeZone(DateTime date, double remoteTimezoneOffsetHours);
Task<Data.TelemetryTimeZoneDto> GetByCoordinatesAsync(double latitude, double longitude, CancellationToken token);
}
}

View File

@ -8,6 +8,8 @@ namespace AsbCloudApp.Services
{
public interface IWellService: ICrudService<WellDto>
{
ITelemetryService TelemetryService { get; }
Task<IEnumerable<WellDto>> GetWellsByCompanyAsync(int idCompany, CancellationToken token);
Task<bool> IsCompanyInvolvedInWellAsync(int idCompany, int idWell, CancellationToken token);
Task<string> GetWellCaptionByIdAsync(int idWell, CancellationToken token);
@ -15,7 +17,8 @@ namespace AsbCloudApp.Services
Task<IEnumerable<CompanyDto>> GetCompaniesAsync(int idWell, CancellationToken token);
bool IsCompanyInvolvedInWell(int idCompany, int idWell);
string GetStateText(int state);
DateTime GetLastTelemetryDate(int idWell);
DateTimeOffset GetLastTelemetryDate(int idWell);
Task<IEnumerable<int>> GetClusterWellsIdsAsync(int idWell, CancellationToken token);
double? GetTimeZoneOffset(int idWell);
}
}

View File

@ -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; }

View File

@ -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;
}
}
}

View File

@ -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],

View File

@ -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; }

View File

@ -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<List<DataSaubAnalyse>> GetDataSaubPartOrDefaultAsync(int idTelemetry, DateTime analyzeStartDate, CancellationToken token) =>
private Task<List<DataSaubAnalyse>> GetDataSaubPartOrDefaultAsync(int idTelemetry, DateTimeOffset analyzeStartDate, CancellationToken token) =>
db.TelemetryDataSaub
.Where(d =>
d.IdTelemetry == idTelemetry &&

View File

@ -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
{

View File

@ -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);
}

View File

@ -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<MessageDto>
{
@ -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<TelemetryMessage>();
var entity = dto.Adapt<TelemetryMessage>();
entity.Id = 0;
entity.IdTelemetry = telemetryId;
entity.Date = dto.Date.ToUtcDateTimeOffset(timeZoneOffset);
db.TelemetryMessages.Add(entity);
}

View File

@ -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<TModel>();
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;
});

View File

@ -16,7 +16,7 @@ namespace AsbCloudInfrastructure.Services
public class TelemetryService : ITelemetryService
{
private readonly CacheTable<Telemetry> cacheTelemetry;
private readonly CacheTable<Well> cacheWells;//TODO: use wellService insad of this
private readonly CacheTable<Well> 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<TelemetryInfo>();
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<double?> GetTelemetryTimeZoneOffsetAsync(int idTelemetry, CancellationToken token)
public double GetTimezoneOffset(int idTelemetry) =>
cacheTelemetry.FirstOrDefault(t => t.Id == idTelemetry).Info?.TimeZoneOffsetTotalHours ?? 0d;
public async Task<double?> 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<TelemetryTimeZone>();
telemetry.TimeZone = requestedTimeZone.Adapt<TelemetryTimeZone>();
}
await cacheTelemetry.UpsertAsync(telemetry, token).ConfigureAwait(false);
return telemetry.TelemetryTimeZone.Hours;
return telemetry.TimeZone.Hours;
}
public async Task<DatesRangeDto> 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<TelemetryTimeZone>();
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)

View File

@ -23,17 +23,17 @@ namespace AsbCloudInfrastructure.Services
/// <summary>
/// Время последнего запроса (по времени сервера)
/// </summary>
public DateTime LastTimeServer { get; set; }
public DateTimeOffset LastTimeServer { get; set; }
/// <summary>
/// Дата первых данных в БД
/// </summary>
public DateTime TelemetryDateMin { get; set; }
public DateTimeOffset TelemetryDateMin { get; set; }
/// <summary>
/// Дата последних данных в БД
/// </summary>
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;
}

View File

@ -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);
}
}
}

View File

@ -15,12 +15,14 @@ namespace AsbCloudInfrastructure.Services.WellOperationService
public class WellOperationService : IWellOperationService
{
private readonly IAsbCloudDbContext db;
private readonly IWellService wellService;
private readonly CacheTable<WellOperationCategory> cachedOperationCategories;
private readonly CacheTable<WellSectionType> cachedSectionTypes;
public WellOperationService(IAsbCloudDbContext db, CacheDb cache)
public WellOperationService(IAsbCloudDbContext db, CacheDb cache, IWellService wellService)
{
this.db = db;
this.wellService = wellService;
cachedOperationCategories = cache.GetCachedTable<WellOperationCategory>((DbContext)db);
cachedSectionTypes = cache.GetCachedTable<WellSectionType>((DbContext)db);
}
@ -31,7 +33,6 @@ namespace AsbCloudInfrastructure.Services.WellOperationService
public IEnumerable<WellOperationCategoryDto> GetCategories()
{
var operationTypes = cachedOperationCategories
//.Where(oc => oc.Code > 999)
.Distinct().OrderBy(o => o.Name);
var result = operationTypes.Adapt<WellOperationCategoryDto>();
@ -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<WellOperationDto>
{

View File

@ -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<RelationCompanyWell> cacheRelationCompaniesWells;
private readonly CacheTable<CompanyType> 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;
}
}
}

View File

@ -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);

View File

@ -85,7 +85,7 @@ namespace AsbCloudWebApi.Controllers
if (!isCompanyOwnsWell)
return Forbid();
var content = await telemetryDataService.GetAsync(idWell, begin,
intervalSec, approxPointsCount, isUtc, token).ConfigureAwait(false);

View File

@ -132,7 +132,7 @@ namespace AsbCloudWebApi.Controllers
/// <param name="idWell">id скважины</param>
/// <param name="values">Данные о добавляемых операциях</param>
/// <param name="token">Токен отмены задачи</param>
/// <returns>Количество добавленых в БД строк</returns>
/// <returns>Количество добавленных в БД строк</returns>
[HttpPost]
[ProducesResponseType(typeof(IEnumerable<WellOperationDto>), (int)System.Net.HttpStatusCode.OK)]
public async Task<IActionResult> InsertRangeAsync(int idWell, [FromBody] IEnumerable<WellOperationDto> values,
@ -150,8 +150,8 @@ namespace AsbCloudWebApi.Controllers
/// Обновляет выбранную операцию на скважине
/// </summary>
/// <param name="idWell">id скважины</param>
/// <param name="idOperation">id выбраной операции</param>
/// <param name="value">Новые данные для выбраной операции</param>
/// <param name="idOperation">id выбранной операции</param>
/// <param name="value">Новые данные для выбранной операции</param>
/// <param name="token">Токен отмены задачи</param>
/// <returns>Количество обновленных в БД строк</returns>
[HttpPut("{idOperation}")]
@ -168,10 +168,10 @@ namespace AsbCloudWebApi.Controllers
}
/// <summary>
/// Удаляет выбраную операцию на скважине
/// Удаляет выбранную операцию на скважине
/// </summary>
/// <param name="idWell">id скважины</param>
/// <param name="idOperation">id выбраной операции</param>
/// <param name="idOperation">id выбранной операции</param>
/// <param name="token">Токен отмены задачи</param>
/// <returns>Количество удаленных из БД строк</returns>
[HttpDelete("{idOperation}")]
@ -264,7 +264,7 @@ namespace AsbCloudWebApi.Controllers
/// </summary>
/// <returns>Запрашиваемый файл</returns>
[HttpGet]
[Route("tamplate")]
[Route("template")]
[AllowAnonymous]
[ProducesResponseType(typeof(PhysicalFileResult), (int)System.Net.HttpStatusCode.OK)]
public IActionResult GetTamplate()

View File

@ -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

View File

@ -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"
]
}

View File

@ -1 +1 @@
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="Web site created using create-react-app"/><link rel="manifest" href="/manifest.json"/><title>АСБ Vision</title><link href="/static/css/main.fb3df553.chunk.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div><script>!function(e){function r(r){for(var n,a,i=r[0],c=r[1],l=r[2],s=0,p=[];s<i.length;s++)a=i[s],Object.prototype.hasOwnProperty.call(o,a)&&o[a]&&p.push(o[a][0]),o[a]=0;for(n in c)Object.prototype.hasOwnProperty.call(c,n)&&(e[n]=c[n]);for(f&&f(r);p.length;)p.shift()();return u.push.apply(u,l||[]),t()}function t(){for(var e,r=0;r<u.length;r++){for(var t=u[r],n=!0,i=1;i<t.length;i++){var c=t[i];0!==o[c]&&(n=!1)}n&&(u.splice(r--,1),e=a(a.s=t[0]))}return e}var n={},o={1:0},u=[];function a(r){if(n[r])return n[r].exports;var t=n[r]={i:r,l:!1,exports:{}};return e[r].call(t.exports,t,t.exports,a),t.l=!0,t.exports}a.e=function(e){var r=[],t=o[e];if(0!==t)if(t)r.push(t[2]);else{var n=new Promise((function(r,n){t=o[e]=[r,n]}));r.push(t[2]=n);var u,i=document.createElement("script");i.charset="utf-8",i.timeout=120,a.nc&&i.setAttribute("nonce",a.nc),i.src=function(e){return a.p+"static/js/"+({}[e]||e)+"."+{3:"c22d92b4"}[e]+".chunk.js"}(e);var c=new Error;u=function(r){i.onerror=i.onload=null,clearTimeout(l);var t=o[e];if(0!==t){if(t){var n=r&&("load"===r.type?"missing":r.type),u=r&&r.target&&r.target.src;c.message="Loading chunk "+e+" failed.\n("+n+": "+u+")",c.name="ChunkLoadError",c.type=n,c.request=u,t[1](c)}o[e]=void 0}};var l=setTimeout((function(){u({type:"timeout",target:i})}),12e4);i.onerror=i.onload=u,document.head.appendChild(i)}return Promise.all(r)},a.m=e,a.c=n,a.d=function(e,r,t){a.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},a.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},a.t=function(e,r){if(1&r&&(e=a(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(a.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)a.d(t,n,function(r){return e[r]}.bind(null,n));return t},a.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return a.d(r,"a",r),r},a.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},a.p="/",a.oe=function(e){throw console.error(e),e};var i=this.webpackJsonpasb_cloud_front_react=this.webpackJsonpasb_cloud_front_react||[],c=i.push.bind(i);i.push=r,i=i.slice();for(var l=0;l<i.length;l++)r(i[l]);var f=c;t()}([])</script><script src="/static/js/2.8306ac4d.chunk.js"></script><script src="/static/js/main.47b40915.chunk.js"></script></body></html>
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="Web site created using create-react-app"/><link rel="manifest" href="/manifest.json"/><title>АСБ Vision</title><link href="/static/css/main.dd1fcee2.chunk.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div><script>!function(e){function r(r){for(var n,u,i=r[0],c=r[1],f=r[2],s=0,p=[];s<i.length;s++)u=i[s],Object.prototype.hasOwnProperty.call(o,u)&&o[u]&&p.push(o[u][0]),o[u]=0;for(n in c)Object.prototype.hasOwnProperty.call(c,n)&&(e[n]=c[n]);for(l&&l(r);p.length;)p.shift()();return a.push.apply(a,f||[]),t()}function t(){for(var e,r=0;r<a.length;r++){for(var t=a[r],n=!0,i=1;i<t.length;i++){var c=t[i];0!==o[c]&&(n=!1)}n&&(a.splice(r--,1),e=u(u.s=t[0]))}return e}var n={},o={1:0},a=[];function u(r){if(n[r])return n[r].exports;var t=n[r]={i:r,l:!1,exports:{}};return e[r].call(t.exports,t,t.exports,u),t.l=!0,t.exports}u.e=function(e){var r=[],t=o[e];if(0!==t)if(t)r.push(t[2]);else{var n=new Promise((function(r,n){t=o[e]=[r,n]}));r.push(t[2]=n);var a,i=document.createElement("script");i.charset="utf-8",i.timeout=120,u.nc&&i.setAttribute("nonce",u.nc),i.src=function(e){return u.p+"static/js/"+({}[e]||e)+"."+{3:"6446ff6d",4:"eb6dbaeb",5:"7a9fb752",6:"61991758",7:"a1c2547f",8:"2a766587",9:"6ba456d9",10:"f5187699",11:"37e8bbc7"}[e]+".chunk.js"}(e);var c=new Error;a=function(r){i.onerror=i.onload=null,clearTimeout(f);var t=o[e];if(0!==t){if(t){var n=r&&("load"===r.type?"missing":r.type),a=r&&r.target&&r.target.src;c.message="Loading chunk "+e+" failed.\n("+n+": "+a+")",c.name="ChunkLoadError",c.type=n,c.request=a,t[1](c)}o[e]=void 0}};var f=setTimeout((function(){a({type:"timeout",target:i})}),12e4);i.onerror=i.onload=a,document.head.appendChild(i)}return Promise.all(r)},u.m=e,u.c=n,u.d=function(e,r,t){u.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},u.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},u.t=function(e,r){if(1&r&&(e=u(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(u.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)u.d(t,n,function(r){return e[r]}.bind(null,n));return t},u.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return u.d(r,"a",r),r},u.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},u.p="/",u.oe=function(e){throw console.error(e),e};var i=this.webpackJsonpasb_cloud_front_react=this.webpackJsonpasb_cloud_front_react||[],c=i.push.bind(i);i.push=r,i=i.slice();for(var f=0;f<i.length;f++)r(i[f]);var l=c;t()}([])</script><script src="/static/js/2.c978f7e7.chunk.js"></script><script src="/static/js/main.b9f2543d.chunk.js"></script></body></html>