среда:)

This commit is contained in:
Фролов 2021-04-07 18:01:56 +05:00
parent 31a2e1e878
commit ef9bb8f39f
41 changed files with 1192 additions and 60 deletions

View File

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

View File

@ -1,25 +1,58 @@
using System;
using System.Text.Json.Serialization;
namespace AsbCloudApp.Data
{
public class DataSaubBaseDto
{
//[JsonPropertyName("date")]
public DateTime Date { get; set; }
/// <summary>
/// Режим работы САУБ:
/// 0 - "РУЧНОЙ"
/// 1 - "БУРЕНИЕ В РОТОРЕ"
/// 2 - "ПРОРАБОТКА"
/// 3 - "БУРЕНИЕ В СЛАЙДЕ"
/// 4 - "СПУСК СПО"
/// 5 - "ПОДЪЕМ СПО"
/// 6 - "ПОДЪЕМ С ПРОРАБОТКОЙ"
/// 10 - "БЛОКИРОВКА"
/// </summary>
public int? Mode { get; set; }
/// <summary>
/// Глубина забоя
/// </summary>
public double? WellDepth { get; set; }
/// <summary>
/// Глубина долта
/// </summary>
public double? BitDepth { get; set; }
/// <summary>
/// Талевый блок. Высота
/// </summary>
public double? BlockHeight { get; set; }
/// <summary>
/// Талевый блок. Скорость
/// </summary>
public double? BlockSpeed { get; set; }
/// <summary>
/// Талевый блок. Задание скорости
/// </summary>
public double? BlockSpeedSp { get; set; }
/// <summary>
/// Давтение
/// </summary>
public double? Pressure { get; set; }
/// <summary>
/// Давтение при холостом ходе.
/// </summary>
public double? PressureIdle { get; set; }
public double? PressureSp { get; set; }

View File

@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace AsbCloudApp.Data
{
/// <summary>
/// Формат посылки от панели к облаку.
/// Панель копит у себя данные и при удачной отправке помечает как отправленные.
/// </summary>
public class TelemetryDataDto
{
public DateTime Date { get; set; }
/// <summary>
/// Версия ПО панели.
/// Нужна будет для разбора информации
/// </summary>
public string HmiVersion { get; set; }
public string UserName { get; set; }
public List<DataSaubBaseDto> DataSaub { get; set; }
/// TODO:
//public List<EventDto> EventsDictiotary { get; set; }
//public List<MessageDto> Messages { get; set; }
}
}

View File

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

View File

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

View File

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

View File

@ -0,0 +1,11 @@
using AsbCloudApp.Data;
using System;
using System.Collections.Generic;
namespace AsbCloudApp.Services
{
public interface ITelemetryDataService
{
IEnumerable<DataSaubBaseDto> Get(int wellId, DateTime dateBegin = default, double intervalSec = 600d, int approxPointsCount = 1024);
}
}

View File

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

View File

@ -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<int> CreatePartitionAsync<TEntity>(string propertyName, int id, CancellationToken token = default)
where TEntity: class
{
var dbSet = Set<TEntity>();
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);
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

@ -25,5 +25,6 @@ namespace AsbCloudDb.Model
IQueryable<Well> GetWellsByCustomer(int idCustomer);
IQueryable<User> GetUsersByLogin(string login);
Task<int> CreatePartitionAsync<TEntity>(string propertyName, int id, CancellationToken token = default) where TEntity : class;
}
}

11
AsbCloudDb/Model/IId.cs Normal file
View File

@ -0,0 +1,11 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace AsbCloudDb.Model
{
public interface IId
{
public int Id { get; }
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -6,8 +6,7 @@
<ItemGroup>
<PackageReference Include="AutoMapper" Version="10.1.1" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.8.0" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.10.0" />
</ItemGroup>
<ItemGroup>

View File

@ -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<IAsbCloudDbContext>(provider => provider.GetService<AsbCloudDbContext>());
services.AddSingleton(new MapperConfiguration(AutoMapperConfig));
services.AddSingleton(new CacheDb());
services.AddTransient<IAuthService, AuthService>();
services.AddTransient<IWellService, WellService>();
services.AddTransient<ITelemetryService, TelemetryService>();
services.AddTransient<ITelemetryDataService, TelemetryDataService>();
return services;
}
private static void AutoMapperConfig(IMapperConfigurationExpression cfg)
{
cfg.CreateMap<DataSaubBase, DataSaubBaseDto>();
cfg.CreateMap<DataSaubBaseDto, DataSaubBase>();
}
}
}

View File

@ -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<Type, IEnumerable<object>> cache = new Dictionary<Type, IEnumerable<object>>();
public ICacheTable<TEntity> GetCachedTable<TEntity>(DbContext context)
where TEntity : class
{
var entityType = typeof(TEntity);
if (!cache.ContainsKey(entityType))
cache[entityType] = new List<TEntity>(8);
var tableCache = new CacheTable<TEntity>(context, (List<TEntity>)cache[entityType]);
return tableCache;
}
}
}

View File

@ -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<TEntity> : ICacheTable<TEntity> where TEntity : class
{
private readonly DbContext context;
private List<TEntity> entities;
internal CacheTable(DbContext context, List<TEntity> 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<TEntity>().ToList();
entities.AddRange(dbEntities);
return entities.Count();
}
public async Task<int> RefreshAsync(CancellationToken token = default)
{
entities.Clear();
var dbEntities = await context.Set<TEntity>().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<bool> 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<TEntity, bool> predicate, RefreshMode refreshMode = RefreshMode.IfResultEmpty)
=> FirstOrDefault(predicate, refreshMode) != default;
public Task<bool> ContainsAsync(Func<TEntity, bool> predicate, CancellationToken token = default)
=> ContainsAsync(predicate, RefreshMode.IfResultEmpty, token);
public async Task<bool> ContainsAsync(Func<TEntity, bool> predicate, RefreshMode refreshMode = RefreshMode.IfResultEmpty, CancellationToken token = default)
=> await FirstOrDefaultAsync(predicate, refreshMode, token) != default;
public Task<TEntity> 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<TEntity> 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<TEntity> FirstOrDefaultAsync(Func<TEntity, bool> predicate, CancellationToken token = default)
=> FirstOrDefaultAsync(predicate, RefreshMode.IfResultEmpty, token);
public TEntity FirstOrDefault(Func<TEntity, bool> 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<TEntity> FirstOrDefaultAsync(Func<TEntity, bool> 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<IEnumerable<TEntity>> SelectAsync(Func<TEntity, bool> predicate, CancellationToken token = default)
=> SelectAsync(predicate, RefreshMode.IfResultEmpty, token);
public IEnumerable<TEntity> Select(Func<TEntity, bool> 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<IEnumerable<TEntity>> SelectAsync(Func<TEntity, bool> 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<TEntity> Update(Func<TEntity, bool> predicate, Action<TEntity> mutation)
{
var dbSet = context.Set<TEntity>();
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<IEnumerable<TEntity>> UpdateAsync(Func<TEntity, bool> predicate, Action<TEntity> mutation, CancellationToken token = default)
{
var dbSet = context.Set<TEntity>();
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<TEntity, bool> predicate)
{
var dbSet = context.Set<TEntity>();
entities.RemoveAll(e => predicate(e));
dbSet.RemoveRange(dbSet.Where(predicate));
context.SaveChanges();
return;
}
public async Task RemoveAsync(Func<TEntity, bool> predicate, CancellationToken token = default)
{
var dbSet = context.Set<TEntity>();
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<TEntity>();
var dbEntity = dbSet.Add(entity).Entity;
context.SaveChanges();
entities.Add(dbEntity);
return dbEntity;
}
public async Task<TEntity> InsertAsync(TEntity entity, CancellationToken token = default)
{
var dbSet = context.Set<TEntity>();
var dbEntity = dbSet.Add(entity).Entity;
await context.SaveChangesAsync(token).ConfigureAwait(false);
entities.Add(dbEntity);
return dbEntity;
}
public IEnumerable<TEntity> Insert(IEnumerable<TEntity> newEntities)
{
var dbSet = context.Set<TEntity>();
var dbEntities = new List<TEntity>(newEntities.Count());
foreach (var item in newEntities)
dbEntities.Add(dbSet.Add(item).Entity);
context.SaveChanges();
entities.AddRange(dbEntities);
return dbEntities;
}
public async Task<IEnumerable<TEntity>> InsertAsync(IEnumerable<TEntity> newEntities, CancellationToken token = default)
{
var dbSet = context.Set<TEntity>();
var dbEntities = new List<TEntity>(newEntities.Count());
foreach (var item in newEntities)
dbEntities.Add(dbSet.Add(item).Entity);
await context.SaveChangesAsync(token).ConfigureAwait(false);
entities.AddRange(dbEntities);
return dbEntities;
}
}
}

View File

@ -0,0 +1,37 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace AsbCloudInfrastructure.Services.Cache
{
public interface ICacheTable<TEntity> where TEntity : class
{
TEntity this[int index] { get; }
int Refresh();
bool Contains(Func<TEntity, bool> predicate, RefreshMode refreshMode = RefreshMode.IfResultEmpty);
TEntity FirstOrDefault(RefreshMode refreshMode = RefreshMode.IfResultEmpty);
TEntity FirstOrDefault(Func<TEntity, bool> predicate, RefreshMode refreshMode = RefreshMode.IfResultEmpty);
IEnumerable<TEntity> Insert(IEnumerable<TEntity> newEntities);
TEntity Insert(TEntity entity);
void Remove(Func<TEntity, bool> predicate);
IEnumerable<TEntity> Select(Func<TEntity, bool> predicate, RefreshMode refreshMode = RefreshMode.IfResultEmpty);
IEnumerable<TEntity> Update(Func<TEntity, bool> predicate, Action<TEntity> mutation);
//----- ASYNC ------
Task<int> RefreshAsync(CancellationToken token = default);
Task<bool> ContainsAsync(Func<TEntity, bool> predicate, CancellationToken token = default);
Task<bool> ContainsAsync(Func<TEntity, bool> predicate, RefreshMode refreshMode = RefreshMode.IfResultEmpty, CancellationToken token = default);
Task<TEntity> FirstOrDefaultAsync(CancellationToken token = default);
Task<TEntity> FirstOrDefaultAsync(RefreshMode refreshMode = RefreshMode.IfResultEmpty, CancellationToken token = default);
Task<TEntity> FirstOrDefaultAsync(Func<TEntity, bool> predicate, CancellationToken token = default);
Task<TEntity> FirstOrDefaultAsync(Func<TEntity, bool> predicate, RefreshMode refreshMode = RefreshMode.IfResultEmpty, CancellationToken token = default);
Task<IEnumerable<TEntity>> InsertAsync(IEnumerable<TEntity> newEntities, CancellationToken token = default);
Task<TEntity> InsertAsync(TEntity entity, CancellationToken token = default);
Task RemoveAsync(Func<TEntity, bool> predicate, CancellationToken token = default);
Task<IEnumerable<TEntity>> SelectAsync(Func<TEntity, bool> predicate, CancellationToken token = default);
Task<IEnumerable<TEntity>> SelectAsync(Func<TEntity, bool> predicate, RefreshMode refreshMode = RefreshMode.IfResultEmpty, CancellationToken token = default);
Task<IEnumerable<TEntity>> UpdateAsync(Func<TEntity, bool> predicate, Action<TEntity> mutation, CancellationToken token = default);
}
}

View File

@ -0,0 +1,4 @@
namespace AsbCloudInfrastructure.Services.Cache
{
public enum RefreshMode { None, IfResultEmpty, Force, }
}

View File

@ -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<Telemetry> cacheTelemetry;
private ICacheTable<Well> cacheWells;
public TelemetryDataService(IAsbCloudDbContext db, CacheDb cacheDb, MapperConfiguration mapperConfiguration)
{
this.db = db;
mapper = mapperConfiguration.CreateMapper();
cacheTelemetry = cacheDb.GetCachedTable<Telemetry>((AsbCloudDbContext)db);
cacheWells = cacheDb.GetCachedTable<Well>((AsbCloudDbContext)db);
}
public IEnumerable<DataSaubBaseDto> 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<DataSaubBaseDto>(dbData.Count);
foreach (var item in dbData)
result.Add(mapper.Map<DataSaubBaseDto>(item));
return result;
}
}
}

View File

@ -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<Telemetry> cacheTelemetry;
public TelemetryService(IAsbCloudDbContext db, CacheDb cacheDb, MapperConfiguration mapperConfiguration)
{
this.db = db;
mapper = mapperConfiguration.CreateMapper();
cacheTelemetry = cacheDb.GetCachedTable<Telemetry>((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<DataSaubBase>(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);
}
}
}
}

View File

@ -1,24 +0,0 @@
using System.Reflection;
namespace AsbCloudApp
{
public class TinyMapper<Tout, Tin>
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;
//}
}
}

View File

@ -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
{
/// <summary>
/// Контроллер сбора данных от буровых
/// </summary>
[Route("api/well")]
[ApiController]
[Authorize]
public class DataController : ControllerBase
{
private readonly ITelemetryDataService telemetryDataService;
public DataController(ITelemetryDataService telemetryDataService)
{
this.telemetryDataService = telemetryDataService;
}
/// <summary>
/// Возвращает данные САУБ по скважине
/// </summary>
/// <param name="wellId"></param>
/// <param name=""></param>
/// <returns></returns>
[HttpGet]
[Route("{wellId}/data")]
[ProducesResponseType(typeof(IEnumerable<DataSaubBaseDto>), (int)System.Net.HttpStatusCode.OK)]
public IActionResult Get(int wellId)
{
return Ok(telemetryDataService.Get(wellId));
}
}
}

View File

@ -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
{
/// <summary>
/// Контроллер сбора данных от буровых
/// </summary>
[Route("api/telemetry")]
[ApiController]
public class TelemetryController : ControllerBase
{
private readonly ITelemetryService telemetryService;
public TelemetryController(ITelemetryService telemetryService)
{
this.telemetryService = telemetryService;
}
/// <summary>
/// Принимает общую информацию по скважине
/// </summary>
/// <returns></returns>
[HttpPost]
[Route("{uid}/info")]
public IActionResult Info(string uid, [FromBody] TelemetryInfoDto info)
{
telemetryService.UpdateInfo(uid, info);
return Ok();
}
/// <summary>
/// Принимает данные от разных систем по скважине
/// </summary>
/// <returns></returns>
[HttpPost]
[Route("{uid}/data")]
public IActionResult Data(string uid, [FromBody] TelemetryDataDto data)
{
telemetryService.UpdateData(uid, data);
return Ok();
}
}
}

View File

@ -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<WellDto>), (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);
//}
}
}

8
ConsoleApp1/Header.cs Normal file
View File

@ -0,0 +1,8 @@
namespace ConsoleApp1
{
public class Header
{
public string Name { get; set; }
public System.Type PropertyType { get; set; } = typeof(object);
}
}

View File

@ -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<AsbCloudDbContext>()
// .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<Header>
{
new Header {Name = "P1", PropertyType = typeof(int) },
new Header {Name = "P2", PropertyType = typeof(int) },
new Header {Name = "P3", PropertyType = typeof(int) },
},
Rows = new List<object[]>
{
new object[] {1,2,3 },
new object[] {4,5,6 },
new object[] {7,8,9 },
}
};
var mapper = new TableMapper<B>();
var b = new B();
mapper.UpdateObjectFromTable(ref b, table, 1);
Console.WriteLine("Done");
}
}
}

17
ConsoleApp1/Table.cs Normal file
View File

@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp1
{
public class Table
{
public IEnumerable<Header> Headers { get; set; }
public IEnumerable<IEnumerable<object>> Rows { get; set; }
}
}

View File

@ -0,0 +1,97 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp1
{
// для работы с таблицами
public class TableMapper<T>
{
Dictionary<string, PropertyHelper> props;
public TableMapper()
{
var props = typeof(T).GetProperties();
this.props = new Dictionary<string, PropertyHelper>(props.Length);
foreach (var prop in props)
{
var helper = new PropertyHelper(prop);
this.props.Add(helper.Id, helper);
}
}
public int UpdateObjectFromTable(ref T obj, Table table, int rowIndex)
{
var updatesCount = 0;
if (table.Rows.Count() <= rowIndex)
return 0;
var row = table.Rows.ElementAt(rowIndex);
var headerIndex = 0;
foreach (var header in table.Headers)
{
var headerId = PropertyHelper.MakeIdentity(header.PropertyType.Name, header.Name);
if (props.ContainsKey(headerId))
{
props[headerId].Set(obj, row.ElementAt(headerIndex));
updatesCount++;
}
headerIndex++;
}
return updatesCount;
}
}
/// <summary>
/// Ускоренный обработчик свойства
/// </summary>
class PropertyHelper
{
public PropertyHelper(PropertyInfo property)
{
PropertyName = property.Name;
PropertyType = property.PropertyType;
Id = MakeIdentity(property.PropertyType.Name, property.Name);
var setter = property.SetMethod;
var getter = property.GetMethod;
var instanceExpression = Expression.Parameter(typeof(object), "instance");
var argumentsExpression = Expression.Parameter(typeof(object[]), "arguments");
var argumentExpressions = new List<Expression> { Expression.Convert(Expression.ArrayIndex(argumentsExpression, Expression.Constant(0)), PropertyType) };
var callExpression = Expression.Call(Expression.Convert(instanceExpression, setter.ReflectedType), setter, argumentExpressions);
Setter = Expression.Lambda<SetterDelegate>(callExpression, instanceExpression, argumentsExpression).Compile();
callExpression = Expression.Call(Expression.Convert(instanceExpression, getter.ReflectedType), getter);
Getter = Expression.Lambda<GetterDelegate>(Expression.Convert(callExpression, typeof(object)), instanceExpression).Compile();
}
public string PropertyName { get;}
public Type PropertyType { get; }
public string Id { get; }
delegate void SetterDelegate(object instanse, object[] values);
delegate object GetterDelegate(object instanse);
SetterDelegate Setter { get; }
GetterDelegate Getter { get; }
void SetValues(object instance, params object[] values)
=> Setter(instance, values);
public void Set(object instance, object value)
=> SetValues(instance, value);
public object Get(object instance)
=> Getter(instance);
public static string MakeIdentity(string propertyTypeName, string propertyName) => $"{propertyTypeName}_{propertyName}".ToLower();
}
}

View File

@ -0,0 +1,51 @@
using AsbCloudApp.Data;
using System;
using System.Collections.Generic;
using System.Net;
using System.Text;
namespace SaubPanelOnlineSender
{
public class CloudDataSender
{
public CloudDataSender() { }
public string OnlineUrl => "http://localhost:5000/api/telemetry/0A:00:27:00:11:12/data";
public void Send(DataSaubBaseDto data)
{
var request = MakeRequest();
using var streamWriter = request.GetRequestStream();
var requestBodyJson = MakeRequestBody(data);
var dataBytes = Encoding.UTF8.GetBytes(requestBodyJson);
streamWriter.Write(dataBytes, 0, dataBytes.Length);
var response = request.GetResponse();
streamWriter.Close();
}
private HttpWebRequest MakeRequest()
{
var request = WebRequest.CreateHttp(OnlineUrl);
request.Method = "POST";
request.Timeout = 4900;
request.ContentType = "application/json";
request.AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip;
request.ServicePoint.Expect100Continue = false;
return request;
}
private string MakeRequestBody(DataSaubBaseDto dataSaub)
{
var telemetry = new TelemetryDataDto
{
Date = DateTime.Now,
DataSaub = new List<DataSaubBaseDto> { dataSaub },
HmiVersion = "3.1",
UserName = "online sender",
};
return Newtonsoft.Json.JsonConvert.SerializeObject(telemetry);
}
}
}

View File

@ -0,0 +1,140 @@
using AsbCloudApp.Data;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace SaubPanelOnlineSender
{
public enum DbPlayerServiceState
{
Unaviable,
Stopped,
Paused,
Working
}
public class DbPlayerService
{
public DbPlayerServiceState State
{
get => state;
private set
{
if (state == value)
return;
state = value;
OnStateUpdate?.Invoke(this, EventArgs.Empty);
}
}
public DateTime LastSentDate { get; private set; }
public EventHandler<double> OnProcessUpdate;
public EventHandler<Exception> OnError;
public EventHandler OnStateUpdate;
private CancellationTokenSource cancellationTokenSource;
private DbPlayerServiceState state;
private static readonly Random rnd = new Random((int)DateTime.Now.Ticks);
public void Start()
{
State = DbPlayerServiceState.Working;
cancellationTokenSource = new CancellationTokenSource();
//dataSender = new ModbusDataSender(Host);
Task.Run(async () => await Work(cancellationTokenSource.Token));
}
public void Pause()
{
State = State == DbPlayerServiceState.Paused
? DbPlayerServiceState.Working
: DbPlayerServiceState.Paused;
}
public void Stop()
{
State = DbPlayerServiceState.Stopped;
cancellationTokenSource.Cancel();
}
async Task Work(CancellationToken token)
{
var period = TimeSpan.FromSeconds(1);
DateTime nextTime;
var dataSender = new CloudDataSender();
while (!token.IsCancellationRequested)
{
nextTime = DateTime.Now + period;
if ((State != DbPlayerServiceState.Working)
&& ((State != DbPlayerServiceState.Paused)))
break;
if (State == DbPlayerServiceState.Paused)
{
await Task.Delay(100, token).ConfigureAwait(false);
continue;
}
if (State == DbPlayerServiceState.Working)
{
try
{
dataSender.Send(MakeDataSaubBaseDto());
}
catch (Exception ex)
{
OnError?.Invoke(this, ex);
}
OnProcessUpdate?.Invoke(this, 0);
}
var waitTime = nextTime - DateTime.Now;
if (waitTime.TotalMilliseconds > 0)
await Task.Delay(waitTime, token).ConfigureAwait(false);
}
Stop();
}
private static DataSaubBaseDto MakeDataSaubBaseDto()
{
return new DataSaubBaseDto
{
Date = DateTime.Now,
AxialLoad = rnd.NextDouble()*100d,
AxialLoadLimitMax = rnd.NextDouble()*100d,
AxialLoadSp = rnd.NextDouble()*100d,
BitDepth = rnd.NextDouble()*100d,
BlockHeight = rnd.NextDouble()*100d,
BlockSpeed = rnd.NextDouble()*100d,
WellDepth = rnd.NextDouble()*100d,
BlockSpeedSp = rnd.NextDouble()*100d,
Flow = rnd.NextDouble()*100d,
FlowDeltaLimitMax = rnd.NextDouble()*100d,
FlowIdle = rnd.NextDouble()*100d,
HookWeight = rnd.NextDouble()*100d,
HookWeightIdle = rnd.NextDouble()*100d,
HookWeightLimitMax = rnd.NextDouble()*100d,
HookWeightLimitMin= rnd.NextDouble() * 100d,
Mode= 1,
Pressure= rnd.NextDouble() * 100d,
PressureDeltaLimitMax= rnd.NextDouble() * 100d,
PressureIdle= rnd.NextDouble() * 100d,
PressureSp= rnd.NextDouble() * 100d,
RotorSpeed= rnd.NextDouble() * 100d,
RotorTorque= rnd.NextDouble() * 100d,
RotorTorqueIdle= rnd.NextDouble() * 100d,
RotorTorqueLimitMax= rnd.NextDouble() * 100d,
RotorTorqueSp= rnd.NextDouble() * 100d,
};
}
}
}

View File

@ -0,0 +1,88 @@
using System;
using System.Threading;
namespace SaubPanelOnlineSender
{
class Program
{
static void Main(/*string[] args*/)
{
Console.Write("player starting\n");
Console.Write("press `Esc` or `Q` to quit\n");
Console.Write("press `p` to pause/unpause\n");
Console.Write("press `s` to stop/start\n");
var playerService = new DbPlayerService();
playerService.OnProcessUpdate =
(s, a) => Console.Write($"Sent: {playerService.LastSentDate:G}\n");
playerService.OnStateUpdate =
(s, a) => Console.Write($"\nState: {playerService.State}\n");
playerService.OnError =
(s, a) =>
{
var color = Console.ForegroundColor;
Console.ForegroundColor = ConsoleColor.Red;
Console.Write($"ERROR: {a.Message}");
Console.ForegroundColor = color;
Console.Write("\n");
};
playerService.Start();
if (Console.IsInputRedirected)
{
while (true)
Thread.Sleep(1000);
}
else
{
while (true)
{
if (Console.KeyAvailable)
{
var key = Console.ReadKey().Key;
if ((key == ConsoleKey.Escape) ||
(key == ConsoleKey.Q))
break;
if (key == ConsoleKey.P)
{
switch (playerService.State)
{
case DbPlayerServiceState.Paused:
case DbPlayerServiceState.Working:
playerService.Pause();
break;
case DbPlayerServiceState.Unaviable:
case DbPlayerServiceState.Stopped:
default:
break;
}
}
if (key == ConsoleKey.S)
{
switch (playerService.State)
{
case DbPlayerServiceState.Working:
playerService.Stop();
break;
case DbPlayerServiceState.Stopped:
playerService.Start();
break;
case DbPlayerServiceState.Unaviable:
case DbPlayerServiceState.Paused:
default:
break;
}
}
}
Thread.Sleep(200);
}
}
playerService.Stop();
}
}
}

View File

@ -0,0 +1,2 @@
* Назначение
проект предназначен для иммитации работы панели САУБ для НОВОГО облака.

View File

@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\AsbCloudApp\AsbCloudApp.csproj" />
</ItemGroup>
</Project>