diff --git a/AsbCloud.sln b/AsbCloud.sln
index 3539f040..b586b80d 100644
--- a/AsbCloud.sln
+++ b/AsbCloud.sln
@@ -13,6 +13,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConsoleApp1", "ConsoleApp1\
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AsbCloudDb", "AsbCloudDb\AsbCloudDb.csproj", "{40FBD29B-724B-4496-B5D9-1A5D14102456}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SaubPanelOnlineSender", "SaubPanelOnlineSender\SaubPanelOnlineSender.csproj", "{B156D582-4D32-4368-A103-687D15B9846C}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -39,6 +41,10 @@ Global
{40FBD29B-724B-4496-B5D9-1A5D14102456}.Debug|Any CPU.Build.0 = Debug|Any CPU
{40FBD29B-724B-4496-B5D9-1A5D14102456}.Release|Any CPU.ActiveCfg = Release|Any CPU
{40FBD29B-724B-4496-B5D9-1A5D14102456}.Release|Any CPU.Build.0 = Release|Any CPU
+ {B156D582-4D32-4368-A103-687D15B9846C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B156D582-4D32-4368-A103-687D15B9846C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B156D582-4D32-4368-A103-687D15B9846C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B156D582-4D32-4368-A103-687D15B9846C}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/AsbCloudApp/Data/DataSaubBaseDto.cs b/AsbCloudApp/Data/DataSaubBaseDto.cs
index 1dd57728..997636fe 100644
--- a/AsbCloudApp/Data/DataSaubBaseDto.cs
+++ b/AsbCloudApp/Data/DataSaubBaseDto.cs
@@ -1,25 +1,58 @@
using System;
+using System.Text.Json.Serialization;
namespace AsbCloudApp.Data
{
public class DataSaubBaseDto
{
+ //[JsonPropertyName("date")]
public DateTime Date { get; set; }
-
+ ///
+ /// Режим работы САУБ:
+ /// 0 - "РУЧНОЙ"
+ /// 1 - "БУРЕНИЕ В РОТОРЕ"
+ /// 2 - "ПРОРАБОТКА"
+ /// 3 - "БУРЕНИЕ В СЛАЙДЕ"
+ /// 4 - "СПУСК СПО"
+ /// 5 - "ПОДЪЕМ СПО"
+ /// 6 - "ПОДЪЕМ С ПРОРАБОТКОЙ"
+ /// 10 - "БЛОКИРОВКА"
+ ///
public int? Mode { get; set; }
+ ///
+ /// Глубина забоя
+ ///
public double? WellDepth { get; set; }
+ ///
+ /// Глубина долта
+ ///
public double? BitDepth { get; set; }
+ ///
+ /// Талевый блок. Высота
+ ///
public double? BlockHeight { get; set; }
+ ///
+ /// Талевый блок. Скорость
+ ///
public double? BlockSpeed { get; set; }
+ ///
+ /// Талевый блок. Задание скорости
+ ///
public double? BlockSpeedSp { get; set; }
+ ///
+ /// Давтение
+ ///
public double? Pressure { get; set; }
+ ///
+ /// Давтение при холостом ходе.
+ ///
public double? PressureIdle { get; set; }
public double? PressureSp { get; set; }
diff --git a/AsbCloudApp/Data/TelemetryDataDto.cs b/AsbCloudApp/Data/TelemetryDataDto.cs
new file mode 100644
index 00000000..7784c0b9
--- /dev/null
+++ b/AsbCloudApp/Data/TelemetryDataDto.cs
@@ -0,0 +1,28 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace AsbCloudApp.Data
+{
+ ///
+ /// Формат посылки от панели к облаку.
+ /// Панель копит у себя данные и при удачной отправке помечает как отправленные.
+ ///
+ public class TelemetryDataDto
+ {
+ public DateTime Date { get; set; }
+
+ ///
+ /// Версия ПО панели.
+ /// Нужна будет для разбора информации
+ ///
+ public string HmiVersion { get; set; }
+ public string UserName { get; set; }
+ public List DataSaub { get; set; }
+
+ /// TODO:
+ //public List EventsDictiotary { get; set; }
+
+ //public List Messages { get; set; }
+ }
+}
diff --git a/AsbCloudApp/Data/TelemetryInfoDto.cs b/AsbCloudApp/Data/TelemetryInfoDto.cs
new file mode 100644
index 00000000..aa971481
--- /dev/null
+++ b/AsbCloudApp/Data/TelemetryInfoDto.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace AsbCloudApp.Data
+{
+ public class TelemetryInfoDto
+ {
+ public string Caption { get; set; }
+ public string Cluster { get; set; }
+ public string Deposit { get; set; }
+ }
+}
diff --git a/AsbCloudApp/Data/WellDto.cs b/AsbCloudApp/Data/WellDto.cs
index 21f8903d..47f63ec6 100644
--- a/AsbCloudApp/Data/WellDto.cs
+++ b/AsbCloudApp/Data/WellDto.cs
@@ -1,11 +1,8 @@
namespace AsbCloudApp.Data
{
- public class WellDto
+ public class WellDto : WellInfoDto
{
public int Id { get; set; }
- public string Caption { get; set; }
- public string Cluster { get; set; }
- public string Deposit { get; set; }
public object LastData { get; set; }//DataSaubBaseDto
}
}
diff --git a/AsbCloudApp/Data/WellInfoDto.cs b/AsbCloudApp/Data/WellInfoDto.cs
new file mode 100644
index 00000000..20b78dd6
--- /dev/null
+++ b/AsbCloudApp/Data/WellInfoDto.cs
@@ -0,0 +1,9 @@
+namespace AsbCloudApp.Data
+{
+ public class WellInfoDto
+ {
+ public string Caption { get; set; }
+ public string Cluster { get; set; }
+ public string Deposit { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/AsbCloudApp/Services/ITelemetryDataService.cs b/AsbCloudApp/Services/ITelemetryDataService.cs
new file mode 100644
index 00000000..c579b932
--- /dev/null
+++ b/AsbCloudApp/Services/ITelemetryDataService.cs
@@ -0,0 +1,11 @@
+using AsbCloudApp.Data;
+using System;
+using System.Collections.Generic;
+
+namespace AsbCloudApp.Services
+{
+ public interface ITelemetryDataService
+ {
+ IEnumerable Get(int wellId, DateTime dateBegin = default, double intervalSec = 600d, int approxPointsCount = 1024);
+ }
+}
\ No newline at end of file
diff --git a/AsbCloudApp/Services/ITelemetryService.cs b/AsbCloudApp/Services/ITelemetryService.cs
new file mode 100644
index 00000000..c19c5bf0
--- /dev/null
+++ b/AsbCloudApp/Services/ITelemetryService.cs
@@ -0,0 +1,15 @@
+using AsbCloudApp.Data;
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace AsbCloudApp.Services
+{
+ public interface ITelemetryService
+ {
+ void UpdateData(string uid, TelemetryDataDto data);
+ void UpdateInfo(string uid, TelemetryInfoDto info);
+ }
+}
diff --git a/AsbCloudDb/Model/AsbCloudDbContext.cs b/AsbCloudDb/Model/AsbCloudDbContext.cs
index 1d189089..cb38a40d 100644
--- a/AsbCloudDb/Model/AsbCloudDbContext.cs
+++ b/AsbCloudDb/Model/AsbCloudDbContext.cs
@@ -1,6 +1,8 @@
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
#nullable disable
@@ -156,5 +158,20 @@ namespace AsbCloudDb.Model
.Include(e => e.Role)
.Include(e => e.Customer)
.Where(e => e.Login == login);
+
+ public async Task CreatePartitionAsync(string propertyName, int id, CancellationToken token = default)
+ where TEntity: class
+ {
+ var dbSet = Set();
+ var baseTableName = dbSet.EntityType.GetTableName();
+ var schema = dbSet.EntityType.GetSchema();
+ var tableObject = Microsoft.EntityFrameworkCore.Metadata.StoreObjectIdentifier.Table(baseTableName, schema);
+ var tableName = baseTableName.Replace("_base", "");
+ var property = dbSet.EntityType.GetProperty(propertyName).GetColumnName(tableObject);
+
+ var query = $"CREATE TABLE {tableName}_{id} (like {baseTableName} including all, constraint partitioning_check check ({property} = 1)) INHERITS ({baseTableName});";
+
+ return await Database.ExecuteSqlRawAsync(query, token).ConfigureAwait(false);
+ }
}
}
diff --git a/AsbCloudDb/Model/Cluster.cs b/AsbCloudDb/Model/Cluster.cs
index 8b01bd3b..77b06b61 100644
--- a/AsbCloudDb/Model/Cluster.cs
+++ b/AsbCloudDb/Model/Cluster.cs
@@ -8,7 +8,7 @@ using System.ComponentModel.DataAnnotations.Schema;
namespace AsbCloudDb.Model
{
[Table("t_cluster"), Comment("Кусты")]
- public partial class Cluster
+ public partial class Cluster: IId
{
public Cluster()
{
diff --git a/AsbCloudDb/Model/Customer.cs b/AsbCloudDb/Model/Customer.cs
index 47da6a50..d598dd76 100644
--- a/AsbCloudDb/Model/Customer.cs
+++ b/AsbCloudDb/Model/Customer.cs
@@ -7,7 +7,7 @@ using System.ComponentModel.DataAnnotations.Schema;
namespace AsbCloudDb.Model
{
[Table("t_customer")]
- public partial class Customer
+ public partial class Customer: IId
{
public Customer()
{
diff --git a/AsbCloudDb/Model/DataSaubBase.cs b/AsbCloudDb/Model/DataSaubBase.cs
index 96fb7ede..dd11cd66 100644
--- a/AsbCloudDb/Model/DataSaubBase.cs
+++ b/AsbCloudDb/Model/DataSaubBase.cs
@@ -8,7 +8,7 @@ using System.ComponentModel.DataAnnotations.Schema;
namespace AsbCloudDb.Model
{
[Table("t_data_saub_base"), Comment("набор основных данных по SAUB")]
- public partial class DataSaubBase
+ public partial class DataSaubBase: IId
{
[Key]
[Column("id")]
diff --git a/AsbCloudDb/Model/Deposit.cs b/AsbCloudDb/Model/Deposit.cs
index a835186d..9b142838 100644
--- a/AsbCloudDb/Model/Deposit.cs
+++ b/AsbCloudDb/Model/Deposit.cs
@@ -8,7 +8,7 @@ using System.ComponentModel.DataAnnotations.Schema;
namespace AsbCloudDb.Model
{
[Table("t_deposit"), Comment("Месторождение")]
- public partial class Deposit
+ public partial class Deposit: IId
{
public Deposit()
{
diff --git a/AsbCloudDb/Model/IAsbCloudDbContext.cs b/AsbCloudDb/Model/IAsbCloudDbContext.cs
index f2d1954c..8c0e3ce2 100644
--- a/AsbCloudDb/Model/IAsbCloudDbContext.cs
+++ b/AsbCloudDb/Model/IAsbCloudDbContext.cs
@@ -25,5 +25,6 @@ namespace AsbCloudDb.Model
IQueryable GetWellsByCustomer(int idCustomer);
IQueryable GetUsersByLogin(string login);
+ Task CreatePartitionAsync(string propertyName, int id, CancellationToken token = default) where TEntity : class;
}
}
\ No newline at end of file
diff --git a/AsbCloudDb/Model/IId.cs b/AsbCloudDb/Model/IId.cs
new file mode 100644
index 00000000..6aa063e7
--- /dev/null
+++ b/AsbCloudDb/Model/IId.cs
@@ -0,0 +1,11 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace AsbCloudDb.Model
+{
+ public interface IId
+ {
+ public int Id { get; }
+ }
+}
diff --git a/AsbCloudDb/Model/Message.cs b/AsbCloudDb/Model/Message.cs
index 32f2a8d5..be5e7bb1 100644
--- a/AsbCloudDb/Model/Message.cs
+++ b/AsbCloudDb/Model/Message.cs
@@ -8,7 +8,7 @@ using System.ComponentModel.DataAnnotations.Schema;
namespace AsbCloudDb.Model
{
[Table("t_message"), Comment("Сообщения на буровых")]
- public partial class Message
+ public partial class Message: IId
{
[Key]
[Column("id")]
diff --git a/AsbCloudDb/Model/Telemetry.cs b/AsbCloudDb/Model/Telemetry.cs
index b8cd9c05..d9f3d06b 100644
--- a/AsbCloudDb/Model/Telemetry.cs
+++ b/AsbCloudDb/Model/Telemetry.cs
@@ -11,7 +11,7 @@ namespace AsbCloudDb.Model
[Table("t_telemetry"), Comment("таблица привязки телеметрии от комплектов к конкретной скважине.")]
[Index(nameof(RemoteUid), Name = "t_telemetry_remote_uid_index")]
[Index(nameof(Version), Name = "t_telemetry_version_index")]
- public partial class Telemetry
+ public partial class Telemetry: IId
{
public Telemetry()
{
@@ -26,7 +26,7 @@ namespace AsbCloudDb.Model
[Column("remote_uid"), Comment("Идентификатор передающего устройства. Может посторяться в списке, так как комплекты оборудования переезжают от скв. к скв.")]
public string RemoteUid { get; set; }
- [Column("info", TypeName = "json"), Comment("Информация с панели о скважине")]
+ [Column("info", TypeName = "jsonb"), Comment("Информация с панели о скважине")]
public string Info { get; set; }
[Column("data", TypeName = "json"), Comment("последние пришедшие данные со скважины в сыром виде")]
@@ -39,6 +39,9 @@ namespace AsbCloudDb.Model
[StringLength(64)]
public string Version { get; set; }
+ //[Column("time_zone", TypeName = "json"), Comment("Временная зона панели САУБ.")]
+ //public TimeZoneInfo TimeZone { get; set; }
+
[InverseProperty(nameof(Model.Well.Telemetry))]
public virtual Well Well { get; set; }
diff --git a/AsbCloudDb/Model/User.cs b/AsbCloudDb/Model/User.cs
index 2163c9ab..df50f382 100644
--- a/AsbCloudDb/Model/User.cs
+++ b/AsbCloudDb/Model/User.cs
@@ -7,7 +7,7 @@ using System.ComponentModel.DataAnnotations.Schema;
namespace AsbCloudDb.Model
{
[Table("t_user"), Comment("Пользователи облака")]
- public partial class User
+ public partial class User: IId
{
[Key]
[Column("id")]
diff --git a/AsbCloudDb/Model/UserRole.cs b/AsbCloudDb/Model/UserRole.cs
index aeac0645..6eb46136 100644
--- a/AsbCloudDb/Model/UserRole.cs
+++ b/AsbCloudDb/Model/UserRole.cs
@@ -7,7 +7,7 @@ using System.ComponentModel.DataAnnotations.Schema;
namespace AsbCloudDb.Model
{
[Table("t_user_role"), Comment("Роли пользователей в системе")]
- public class UserRole
+ public class UserRole : IId
{
public UserRole()
{
diff --git a/AsbCloudDb/Model/Well.cs b/AsbCloudDb/Model/Well.cs
index 92983965..d8a9507f 100644
--- a/AsbCloudDb/Model/Well.cs
+++ b/AsbCloudDb/Model/Well.cs
@@ -7,7 +7,7 @@ using System.ComponentModel.DataAnnotations.Schema;
namespace AsbCloudDb.Model
{
[Table("t_well"), Comment("скважины")]
- public partial class Well
+ public partial class Well: IId
{
[Key]
[Column("id")]
diff --git a/AsbCloudInfrastructure/AsbCloudInfrastructure.csproj b/AsbCloudInfrastructure/AsbCloudInfrastructure.csproj
index 1b7857a3..10e30e6c 100644
--- a/AsbCloudInfrastructure/AsbCloudInfrastructure.csproj
+++ b/AsbCloudInfrastructure/AsbCloudInfrastructure.csproj
@@ -6,8 +6,7 @@
-
-
+
diff --git a/AsbCloudInfrastructure/DependencyInjection.cs b/AsbCloudInfrastructure/DependencyInjection.cs
index dc2e37ac..1772ff7c 100644
--- a/AsbCloudInfrastructure/DependencyInjection.cs
+++ b/AsbCloudInfrastructure/DependencyInjection.cs
@@ -2,6 +2,7 @@
using AsbCloudApp.Services;
using AsbCloudDb.Model;
using AsbCloudInfrastructure.Services;
+using AsbCloudInfrastructure.Services.Cache;
using AutoMapper;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
@@ -19,18 +20,20 @@ namespace AsbCloudInfrastructure
services.AddScoped(provider => provider.GetService());
services.AddSingleton(new MapperConfiguration(AutoMapperConfig));
+ services.AddSingleton(new CacheDb());
services.AddTransient();
services.AddTransient();
+ services.AddTransient();
+ services.AddTransient();
return services;
}
-
-
private static void AutoMapperConfig(IMapperConfigurationExpression cfg)
{
cfg.CreateMap();
+ cfg.CreateMap();
}
}
}
diff --git a/AsbCloudInfrastructure/Services/Cache/CacheDb.cs b/AsbCloudInfrastructure/Services/Cache/CacheDb.cs
new file mode 100644
index 00000000..aca8b345
--- /dev/null
+++ b/AsbCloudInfrastructure/Services/Cache/CacheDb.cs
@@ -0,0 +1,25 @@
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Internal;
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace AsbCloudInfrastructure.Services.Cache
+{
+
+ public class CacheDb
+ {
+ private Dictionary> cache = new Dictionary>();
+
+ public ICacheTable GetCachedTable(DbContext context)
+ where TEntity : class
+ {
+ var entityType = typeof(TEntity);
+ if (!cache.ContainsKey(entityType))
+ cache[entityType] = new List(8);
+
+ var tableCache = new CacheTable(context, (List)cache[entityType]);
+ return tableCache;
+ }
+ }
+}
diff --git a/AsbCloudInfrastructure/Services/Cache/CacheTable.cs b/AsbCloudInfrastructure/Services/Cache/CacheTable.cs
new file mode 100644
index 00000000..a8e19ba4
--- /dev/null
+++ b/AsbCloudInfrastructure/Services/Cache/CacheTable.cs
@@ -0,0 +1,272 @@
+using Microsoft.EntityFrameworkCore;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Linq.Expressions;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace AsbCloudInfrastructure.Services.Cache
+{
+ class CacheTable : ICacheTable where TEntity : class
+ {
+ private readonly DbContext context;
+ private List entities;
+
+ internal CacheTable(DbContext context, List entities)
+ {
+ this.context = context;
+ this.entities = entities;
+ }
+
+ public TEntity this[int index] { get => entities.ElementAt(index); }
+
+
+ public int Refresh()
+ {
+ entities.Clear();
+ var dbEntities = context.Set().ToList();
+ entities.AddRange(dbEntities);
+ return entities.Count();
+ }
+
+ public async Task RefreshAsync(CancellationToken token = default)
+ {
+ entities.Clear();
+ var dbEntities = await context.Set().ToListAsync(token).ConfigureAwait(false);
+ entities.AddRange(dbEntities);
+ return entities.Count();
+ }
+
+ private bool CheckRefresh(RefreshMode refreshMode)
+ {
+ if (refreshMode == RefreshMode.Force)
+ {
+ Refresh();
+ return true;
+ }
+
+ if ((!entities.Any()) && (refreshMode == RefreshMode.IfResultEmpty))
+ {
+ Refresh();
+ return true;
+ }
+
+ return false;
+ }
+
+ private async Task CheckRefreshAsync(RefreshMode refreshMode, CancellationToken token = default)
+ {
+ if (refreshMode == RefreshMode.Force)
+ {
+ await RefreshAsync(token);
+ return true;
+ }
+
+ if ((!entities.Any()) && (refreshMode == RefreshMode.IfResultEmpty))
+ {
+ await RefreshAsync(token);
+ return true;
+ }
+
+ return false;
+ }
+
+ public bool Contains(Func predicate, RefreshMode refreshMode = RefreshMode.IfResultEmpty)
+ => FirstOrDefault(predicate, refreshMode) != default;
+
+ public Task ContainsAsync(Func predicate, CancellationToken token = default)
+ => ContainsAsync(predicate, RefreshMode.IfResultEmpty, token);
+
+ public async Task ContainsAsync(Func predicate, RefreshMode refreshMode = RefreshMode.IfResultEmpty, CancellationToken token = default)
+ => await FirstOrDefaultAsync(predicate, refreshMode, token) != default;
+
+ public Task FirstOrDefaultAsync(CancellationToken token = default)
+ => FirstOrDefaultAsync(RefreshMode.IfResultEmpty, token);
+
+ public TEntity FirstOrDefault(RefreshMode refreshMode = RefreshMode.IfResultEmpty)
+ {
+ bool isUpdated = CheckRefresh(refreshMode);
+
+ var result = entities.FirstOrDefault();
+
+ if (result == default && refreshMode == RefreshMode.IfResultEmpty && !isUpdated)
+ {
+ Refresh();
+ return entities.FirstOrDefault();
+ }
+
+ return result;
+ }
+
+ public async Task FirstOrDefaultAsync(RefreshMode refreshMode = RefreshMode.IfResultEmpty, CancellationToken token = default)
+ {
+ bool isUpdated = await CheckRefreshAsync(refreshMode, token);
+
+ var result = entities.FirstOrDefault();
+
+ if (result == default && refreshMode == RefreshMode.IfResultEmpty && !isUpdated)
+ {
+ await RefreshAsync(token);
+ return entities.FirstOrDefault();
+ }
+
+ return result;
+ }
+
+ public Task FirstOrDefaultAsync(Func predicate, CancellationToken token = default)
+ => FirstOrDefaultAsync(predicate, RefreshMode.IfResultEmpty, token);
+
+ public TEntity FirstOrDefault(Func predicate, RefreshMode refreshMode = RefreshMode.IfResultEmpty)
+ {
+ bool isUpdated = CheckRefresh(refreshMode);
+
+ var result = entities.FirstOrDefault(predicate);
+
+ if (result == default && refreshMode == RefreshMode.IfResultEmpty && !isUpdated)
+ {
+ Refresh();
+ return entities.FirstOrDefault(predicate);
+ }
+
+ return result;
+ }
+
+ public async Task FirstOrDefaultAsync(Func predicate, RefreshMode refreshMode = RefreshMode.IfResultEmpty, CancellationToken token = default)
+ {
+ bool isUpdated = await CheckRefreshAsync(refreshMode, token);
+
+ var result = entities.FirstOrDefault(predicate);
+
+ if (result == default && refreshMode == RefreshMode.IfResultEmpty && !isUpdated)
+ {
+ await RefreshAsync(token);
+ return entities.FirstOrDefault(predicate);
+ }
+
+ return result;
+ }
+
+ public Task> SelectAsync(Func predicate, CancellationToken token = default)
+ => SelectAsync(predicate, RefreshMode.IfResultEmpty, token);
+
+ public IEnumerable Select(Func predicate, RefreshMode refreshMode = RefreshMode.IfResultEmpty)
+ {
+ bool isUpdated = CheckRefresh(refreshMode);
+
+ var result = entities.Where(predicate);
+
+ if (!result.Any() && refreshMode == RefreshMode.IfResultEmpty && !isUpdated)
+ {
+ Refresh();
+ return entities.Where(predicate);
+ }
+
+ return result;
+ }
+
+ public async Task> SelectAsync(Func predicate, RefreshMode refreshMode = RefreshMode.IfResultEmpty, CancellationToken token = default)
+ {
+ bool isUpdated = await CheckRefreshAsync(refreshMode, token);
+
+ var result = entities.Where(predicate);
+
+ if (!result.Any() && refreshMode == RefreshMode.IfResultEmpty && !isUpdated)
+ {
+ await RefreshAsync(token);
+ return entities.Where(predicate);
+ }
+
+ return result;
+ }
+
+ public IEnumerable Update(Func predicate, Action mutation)
+ {
+ var dbSet = context.Set();
+ var dbEntities = dbSet.Where(predicate);
+ if (dbEntities.Any())
+ {
+ foreach (var dbEntity in dbEntities)
+ mutation(dbEntity);
+ context.SaveChanges();
+ }
+ entities.RemoveAll(e => predicate(e));
+ entities.AddRange(dbEntities);
+ return dbEntities;
+ }
+
+ public async Task> UpdateAsync(Func predicate, Action mutation, CancellationToken token = default)
+ {
+ var dbSet = context.Set();
+ var dbEntities = dbSet.Where(predicate);
+ if (dbEntities.Any())
+ {
+ foreach (var dbEntity in dbEntities)
+ mutation(dbEntity);
+ await context.SaveChangesAsync(token).ConfigureAwait(false);
+ }
+ entities.RemoveAll(e => predicate(e));
+ entities.AddRange(dbEntities);
+ return dbEntities;
+ }
+
+ public void Remove(Func predicate)
+ {
+ var dbSet = context.Set();
+ entities.RemoveAll(e => predicate(e));
+ dbSet.RemoveRange(dbSet.Where(predicate));
+ context.SaveChanges();
+ return;
+ }
+
+ public async Task RemoveAsync(Func predicate, CancellationToken token = default)
+ {
+ var dbSet = context.Set();
+ entities.RemoveAll(e => predicate(e));
+ dbSet.RemoveRange(dbSet.Where(predicate));
+ await context.SaveChangesAsync(token).ConfigureAwait(false);
+ return;
+ }
+
+ public TEntity Insert(TEntity entity)
+ {
+ var dbSet = context.Set();
+ var dbEntity = dbSet.Add(entity).Entity;
+ context.SaveChanges();
+ entities.Add(dbEntity);
+ return dbEntity;
+ }
+
+ public async Task InsertAsync(TEntity entity, CancellationToken token = default)
+ {
+ var dbSet = context.Set();
+ var dbEntity = dbSet.Add(entity).Entity;
+ await context.SaveChangesAsync(token).ConfigureAwait(false);
+ entities.Add(dbEntity);
+ return dbEntity;
+ }
+
+ public IEnumerable Insert(IEnumerable newEntities)
+ {
+ var dbSet = context.Set();
+ var dbEntities = new List(newEntities.Count());
+ foreach (var item in newEntities)
+ dbEntities.Add(dbSet.Add(item).Entity);
+ context.SaveChanges();
+ entities.AddRange(dbEntities);
+ return dbEntities;
+ }
+
+ public async Task> InsertAsync(IEnumerable newEntities, CancellationToken token = default)
+ {
+ var dbSet = context.Set();
+ var dbEntities = new List(newEntities.Count());
+ foreach (var item in newEntities)
+ dbEntities.Add(dbSet.Add(item).Entity);
+ await context.SaveChangesAsync(token).ConfigureAwait(false);
+ entities.AddRange(dbEntities);
+ return dbEntities;
+ }
+
+ }
+}
diff --git a/AsbCloudInfrastructure/Services/Cache/ICacheTable.cs b/AsbCloudInfrastructure/Services/Cache/ICacheTable.cs
new file mode 100644
index 00000000..2e04e36e
--- /dev/null
+++ b/AsbCloudInfrastructure/Services/Cache/ICacheTable.cs
@@ -0,0 +1,37 @@
+using System;
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace AsbCloudInfrastructure.Services.Cache
+{
+ public interface ICacheTable where TEntity : class
+ {
+ TEntity this[int index] { get; }
+ int Refresh();
+ bool Contains(Func predicate, RefreshMode refreshMode = RefreshMode.IfResultEmpty);
+ TEntity FirstOrDefault(RefreshMode refreshMode = RefreshMode.IfResultEmpty);
+ TEntity FirstOrDefault(Func predicate, RefreshMode refreshMode = RefreshMode.IfResultEmpty);
+ IEnumerable Insert(IEnumerable newEntities);
+ TEntity Insert(TEntity entity);
+ void Remove(Func predicate);
+ IEnumerable Select(Func predicate, RefreshMode refreshMode = RefreshMode.IfResultEmpty);
+ IEnumerable Update(Func predicate, Action mutation);
+
+ //----- ASYNC ------
+
+ Task RefreshAsync(CancellationToken token = default);
+ Task ContainsAsync(Func predicate, CancellationToken token = default);
+ Task ContainsAsync(Func predicate, RefreshMode refreshMode = RefreshMode.IfResultEmpty, CancellationToken token = default);
+ Task FirstOrDefaultAsync(CancellationToken token = default);
+ Task FirstOrDefaultAsync(RefreshMode refreshMode = RefreshMode.IfResultEmpty, CancellationToken token = default);
+ Task FirstOrDefaultAsync(Func predicate, CancellationToken token = default);
+ Task FirstOrDefaultAsync(Func predicate, RefreshMode refreshMode = RefreshMode.IfResultEmpty, CancellationToken token = default);
+ Task> InsertAsync(IEnumerable newEntities, CancellationToken token = default);
+ Task InsertAsync(TEntity entity, CancellationToken token = default);
+ Task RemoveAsync(Func predicate, CancellationToken token = default);
+ Task> SelectAsync(Func predicate, CancellationToken token = default);
+ Task> SelectAsync(Func predicate, RefreshMode refreshMode = RefreshMode.IfResultEmpty, CancellationToken token = default);
+ Task> UpdateAsync(Func predicate, Action mutation, CancellationToken token = default);
+ }
+}
\ No newline at end of file
diff --git a/AsbCloudInfrastructure/Services/Cache/RefreshMode.cs b/AsbCloudInfrastructure/Services/Cache/RefreshMode.cs
new file mode 100644
index 00000000..bd60b4ee
--- /dev/null
+++ b/AsbCloudInfrastructure/Services/Cache/RefreshMode.cs
@@ -0,0 +1,4 @@
+namespace AsbCloudInfrastructure.Services.Cache
+{
+ public enum RefreshMode { None, IfResultEmpty, Force, }
+}
\ No newline at end of file
diff --git a/AsbCloudInfrastructure/Services/TelemetryDataService.cs b/AsbCloudInfrastructure/Services/TelemetryDataService.cs
new file mode 100644
index 00000000..da5a3015
--- /dev/null
+++ b/AsbCloudInfrastructure/Services/TelemetryDataService.cs
@@ -0,0 +1,68 @@
+using AsbCloudApp.Data;
+using AsbCloudApp.Services;
+using AsbCloudDb.Model;
+using AsbCloudInfrastructure.Services.Cache;
+using AutoMapper;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace AsbCloudInfrastructure.Services
+{
+ public class TelemetryDataService : ITelemetryDataService
+ {
+ private IAsbCloudDbContext db;
+ private IMapper mapper;
+ private ICacheTable cacheTelemetry;
+ private ICacheTable cacheWells;
+
+ public TelemetryDataService(IAsbCloudDbContext db, CacheDb cacheDb, MapperConfiguration mapperConfiguration)
+ {
+ this.db = db;
+ mapper = mapperConfiguration.CreateMapper();
+ cacheTelemetry = cacheDb.GetCachedTable((AsbCloudDbContext)db);
+ cacheWells = cacheDb.GetCachedTable((AsbCloudDbContext)db);
+ }
+
+ public IEnumerable Get(int wellId, DateTime dateBegin = default, double intervalSec = 600d, int approxPointsCount = 1024)
+ {
+ var well = cacheWells.FirstOrDefault(w => w.Id == wellId);
+ if (well is null)
+ return default;
+
+ var telemetry = cacheTelemetry.FirstOrDefault(t => t.Id == well.IdTelemetry);
+ if (telemetry is null)
+ return default;
+
+ if(dateBegin == default)
+ dateBegin = DateTime.Now.AddSeconds(-intervalSec);
+
+ var datEnd = dateBegin.AddSeconds(intervalSec);
+
+ var fullData = from data in db.DataSaubBases
+ where data.IdTelemetry == telemetry.Id
+ && data.Date >= dateBegin && data.Date < datEnd
+ select data;
+
+ var fullDataCount = fullData.Count();
+
+ if (fullDataCount == 0)
+ return default;
+
+ if (fullDataCount > 1.2 * approxPointsCount)
+ {
+ var m = approxPointsCount / fullDataCount;
+ fullData = fullData.Where(d => d.Id % m == 0);
+ }
+
+ var dbData = fullData.ToList();
+ var result = new List(dbData.Count);
+
+ foreach (var item in dbData)
+ result.Add(mapper.Map(item));
+
+ return result;
+ }
+ }
+}
diff --git a/AsbCloudInfrastructure/Services/TelemetryService.cs b/AsbCloudInfrastructure/Services/TelemetryService.cs
new file mode 100644
index 00000000..3e1d7f5d
--- /dev/null
+++ b/AsbCloudInfrastructure/Services/TelemetryService.cs
@@ -0,0 +1,71 @@
+using AsbCloudApp.Data;
+using AsbCloudApp.Services;
+using AsbCloudDb.Model;
+using AsbCloudInfrastructure.Services.Cache;
+using AutoMapper;
+using System.Text.Json;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace AsbCloudInfrastructure.Services
+{
+ public class TelemetryService : ITelemetryService
+ {
+ private readonly IAsbCloudDbContext db;
+ private readonly IMapper mapper;
+ private readonly ICacheTable cacheTelemetry;
+
+ public TelemetryService(IAsbCloudDbContext db, CacheDb cacheDb, MapperConfiguration mapperConfiguration)
+ {
+ this.db = db;
+ mapper = mapperConfiguration.CreateMapper();
+ cacheTelemetry = cacheDb.GetCachedTable((AsbCloudDbContext)db);
+ }
+
+ public void UpdateData(string uid, TelemetryDataDto data)
+ {
+ var telemetry = cacheTelemetry.FirstOrDefault(t => t.RemoteUid == uid, RefreshMode.IfResultEmpty);
+
+ if(telemetry is null)
+ {
+ var newTelemetry = new Telemetry
+ {
+ RemoteUid = uid,
+ Version = data.HmiVersion,
+ Data = JsonSerializer.Serialize(data),
+ LastDataSaub = JsonSerializer.Serialize(data.DataSaub),
+ };
+ telemetry = cacheTelemetry.Insert(newTelemetry);
+ }
+
+ if (data.DataSaub != default)
+ {
+ foreach (var item in data.DataSaub)
+ {
+ var dataSaub = mapper.Map(item);
+ dataSaub.IdTelemetry = telemetry.Id;
+ db.DataSaubBases.Add(dataSaub);
+ }
+
+ db.SaveChanges();
+ }
+ }
+
+ public void UpdateInfo(string uid, TelemetryInfoDto info)
+ {
+ var telemetry = cacheTelemetry.FirstOrDefault(t => t.RemoteUid == uid);
+ if (telemetry is null)
+ {
+ var newTelemetry = new Telemetry
+ {
+ RemoteUid = uid,
+ Info = JsonSerializer.Serialize(info),
+ };
+ telemetry = cacheTelemetry.Insert(newTelemetry);
+ }
+ }
+ }
+}
diff --git a/AsbCloudInfrastructure/TinyMapper.cs b/AsbCloudInfrastructure/TinyMapper.cs
deleted file mode 100644
index be15bd9c..00000000
--- a/AsbCloudInfrastructure/TinyMapper.cs
+++ /dev/null
@@ -1,24 +0,0 @@
-using System.Reflection;
-
-namespace AsbCloudApp
-{
- public class TinyMapper
- where Tout: new()
- {
- private ConstructorInfo[] ctors;
-
- public TinyMapper()
- {
- ctors = typeof(Tin).GetConstructors();
- typeof(Tin).GetProperties();
- }
-
- //public Tout Map(Tin original, params object?[]? ctorParameters)
- //{
- // var result = new Tout();
- // var convertion
- // return result;
- //}
-
- }
-}
diff --git a/AsbCloudWebApi/Controllers/DataController.cs b/AsbCloudWebApi/Controllers/DataController.cs
new file mode 100644
index 00000000..6e1166c2
--- /dev/null
+++ b/AsbCloudWebApi/Controllers/DataController.cs
@@ -0,0 +1,41 @@
+using AsbCloudApp.Data;
+using AsbCloudApp.Services;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Mvc;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace AsbCloudWebApi.Controllers
+{
+ ///
+ /// Контроллер сбора данных от буровых
+ ///
+ [Route("api/well")]
+ [ApiController]
+ [Authorize]
+ public class DataController : ControllerBase
+ {
+ private readonly ITelemetryDataService telemetryDataService;
+
+ public DataController(ITelemetryDataService telemetryDataService)
+ {
+ this.telemetryDataService = telemetryDataService;
+ }
+
+ ///
+ /// Возвращает данные САУБ по скважине
+ ///
+ ///
+ ///
+ ///
+ [HttpGet]
+ [Route("{wellId}/data")]
+ [ProducesResponseType(typeof(IEnumerable), (int)System.Net.HttpStatusCode.OK)]
+ public IActionResult Get(int wellId)
+ {
+ return Ok(telemetryDataService.Get(wellId));
+ }
+ }
+}
diff --git a/AsbCloudWebApi/Controllers/TelemetryController.cs b/AsbCloudWebApi/Controllers/TelemetryController.cs
new file mode 100644
index 00000000..66b98e75
--- /dev/null
+++ b/AsbCloudWebApi/Controllers/TelemetryController.cs
@@ -0,0 +1,51 @@
+using AsbCloudApp.Data;
+using AsbCloudApp.Services;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace AsbCloudWebApi.Controllers
+{
+ ///
+ /// Контроллер сбора данных от буровых
+ ///
+ [Route("api/telemetry")]
+ [ApiController]
+ public class TelemetryController : ControllerBase
+ {
+ private readonly ITelemetryService telemetryService;
+
+ public TelemetryController(ITelemetryService telemetryService)
+ {
+ this.telemetryService = telemetryService;
+ }
+
+ ///
+ /// Принимает общую информацию по скважине
+ ///
+ ///
+ [HttpPost]
+ [Route("{uid}/info")]
+ public IActionResult Info(string uid, [FromBody] TelemetryInfoDto info)
+ {
+ telemetryService.UpdateInfo(uid, info);
+ return Ok();
+ }
+
+ ///
+ /// Принимает данные от разных систем по скважине
+ ///
+ ///
+ [HttpPost]
+ [Route("{uid}/data")]
+ public IActionResult Data(string uid, [FromBody] TelemetryDataDto data)
+ {
+ telemetryService.UpdateData(uid, data);
+ return Ok();
+ }
+
+ }
+}
diff --git a/AsbCloudWebApi/Controllers/WellController.cs b/AsbCloudWebApi/Controllers/WellController.cs
index ec1c3dee..dd9d36cc 100644
--- a/AsbCloudWebApi/Controllers/WellController.cs
+++ b/AsbCloudWebApi/Controllers/WellController.cs
@@ -6,7 +6,7 @@ using System.Collections.Generic;
namespace AsbCloudWebApi.Controllers
{
- [Route("api/[controller]")]
+ [Route("api/well")]
[ApiController]
[Authorize]
public class WellController : ControllerBase
@@ -34,21 +34,5 @@ namespace AsbCloudWebApi.Controllers
return Ok(wells);
}
- //[Route("{id}")]
- //[HttpGet]
- //[ProducesResponseType(typeof(IEnumerable), (int)System.Net.HttpStatusCode.OK)]
- //public IActionResult GetByCustomer(int id)
- //{
- // var claimIdCustomer = User.FindFirst("IdCustomer");
-
- // if (claimIdCustomer is null)
- // return NoContent();
-
- // var idCustomer = int.Parse(claimIdCustomer.Value);
-
- // var wells = wellService.GetWellsByCustomer(idCustomer);
-
- // return Ok(wells);
- //}
}
}
diff --git a/ConsoleApp1/Header.cs b/ConsoleApp1/Header.cs
new file mode 100644
index 00000000..805652dd
--- /dev/null
+++ b/ConsoleApp1/Header.cs
@@ -0,0 +1,8 @@
+namespace ConsoleApp1
+{
+ public class Header
+ {
+ public string Name { get; set; }
+ public System.Type PropertyType { get; set; } = typeof(object);
+ }
+}
\ No newline at end of file
diff --git a/ConsoleApp1/Program.cs b/ConsoleApp1/Program.cs
index c2e73314..1fdbd139 100644
--- a/ConsoleApp1/Program.cs
+++ b/ConsoleApp1/Program.cs
@@ -1,18 +1,60 @@
using AsbCloudDb.Model;
+using AsbCloudInfrastructure.Services.Cache;
using AutoMapper;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
using Microsoft.EntityFrameworkCore;
using System;
+using System.Collections.Generic;
+using System.Linq.Expressions;
using System.Reflection;
namespace ConsoleApp1
{
+ public class A {
+ public int P1 { get; set; }
+ public int P2 { get; set; }
+ };
+
+ public class B
+ {
+ public int P1 { get; set; }
+ public int P3 { get; set; }
+ };
+
class Program
{
static void Main(string[] args)
{
+ //var options = new DbContextOptionsBuilder()
+ // .UseNpgsql("Host=localhost;Database=postgres;Username=postgres;Password=q;Persist Security Info=True")
+ // .Options;
+ //var context = new AsbCloudDbContext(options);
+
+ var table = new Table
+ {
+ Headers = new List
+ {
+ new Header {Name = "P1", PropertyType = typeof(int) },
+ new Header {Name = "P2", PropertyType = typeof(int) },
+ new Header {Name = "P3", PropertyType = typeof(int) },
+ },
+
+ Rows = new List