forked from ddrilling/AsbCloudServer
среда:)
This commit is contained in:
parent
31a2e1e878
commit
ef9bb8f39f
@ -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
|
||||
|
@ -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; }
|
||||
|
28
AsbCloudApp/Data/TelemetryDataDto.cs
Normal file
28
AsbCloudApp/Data/TelemetryDataDto.cs
Normal 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; }
|
||||
}
|
||||
}
|
13
AsbCloudApp/Data/TelemetryInfoDto.cs
Normal file
13
AsbCloudApp/Data/TelemetryInfoDto.cs
Normal 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; }
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
||||
|
9
AsbCloudApp/Data/WellInfoDto.cs
Normal file
9
AsbCloudApp/Data/WellInfoDto.cs
Normal 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; }
|
||||
}
|
||||
}
|
11
AsbCloudApp/Services/ITelemetryDataService.cs
Normal file
11
AsbCloudApp/Services/ITelemetryDataService.cs
Normal 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);
|
||||
}
|
||||
}
|
15
AsbCloudApp/Services/ITelemetryService.cs
Normal file
15
AsbCloudApp/Services/ITelemetryService.cs
Normal 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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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()
|
||||
{
|
||||
|
@ -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()
|
||||
{
|
||||
|
@ -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")]
|
||||
|
@ -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()
|
||||
{
|
||||
|
@ -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
11
AsbCloudDb/Model/IId.cs
Normal file
@ -0,0 +1,11 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace AsbCloudDb.Model
|
||||
{
|
||||
public interface IId
|
||||
{
|
||||
public int Id { get; }
|
||||
}
|
||||
}
|
@ -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")]
|
||||
|
@ -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; }
|
||||
|
||||
|
@ -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")]
|
||||
|
@ -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()
|
||||
{
|
||||
|
@ -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")]
|
||||
|
@ -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>
|
||||
|
@ -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>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
25
AsbCloudInfrastructure/Services/Cache/CacheDb.cs
Normal file
25
AsbCloudInfrastructure/Services/Cache/CacheDb.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
272
AsbCloudInfrastructure/Services/Cache/CacheTable.cs
Normal file
272
AsbCloudInfrastructure/Services/Cache/CacheTable.cs
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
37
AsbCloudInfrastructure/Services/Cache/ICacheTable.cs
Normal file
37
AsbCloudInfrastructure/Services/Cache/ICacheTable.cs
Normal 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);
|
||||
}
|
||||
}
|
4
AsbCloudInfrastructure/Services/Cache/RefreshMode.cs
Normal file
4
AsbCloudInfrastructure/Services/Cache/RefreshMode.cs
Normal file
@ -0,0 +1,4 @@
|
||||
namespace AsbCloudInfrastructure.Services.Cache
|
||||
{
|
||||
public enum RefreshMode { None, IfResultEmpty, Force, }
|
||||
}
|
68
AsbCloudInfrastructure/Services/TelemetryDataService.cs
Normal file
68
AsbCloudInfrastructure/Services/TelemetryDataService.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
71
AsbCloudInfrastructure/Services/TelemetryService.cs
Normal file
71
AsbCloudInfrastructure/Services/TelemetryService.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
//}
|
||||
|
||||
}
|
||||
}
|
41
AsbCloudWebApi/Controllers/DataController.cs
Normal file
41
AsbCloudWebApi/Controllers/DataController.cs
Normal 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));
|
||||
}
|
||||
}
|
||||
}
|
51
AsbCloudWebApi/Controllers/TelemetryController.cs
Normal file
51
AsbCloudWebApi/Controllers/TelemetryController.cs
Normal 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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -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
8
ConsoleApp1/Header.cs
Normal file
@ -0,0 +1,8 @@
|
||||
namespace ConsoleApp1
|
||||
{
|
||||
public class Header
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public System.Type PropertyType { get; set; } = typeof(object);
|
||||
}
|
||||
}
|
@ -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
17
ConsoleApp1/Table.cs
Normal 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; }
|
||||
|
||||
|
||||
}
|
||||
}
|
97
ConsoleApp1/TableMapper.cs
Normal file
97
ConsoleApp1/TableMapper.cs
Normal 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();
|
||||
}
|
||||
}
|
51
SaubPanelOnlineSender/CloudDataSender.cs
Normal file
51
SaubPanelOnlineSender/CloudDataSender.cs
Normal 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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
140
SaubPanelOnlineSender/DbPlayerService.cs
Normal file
140
SaubPanelOnlineSender/DbPlayerService.cs
Normal 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,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
88
SaubPanelOnlineSender/Program.cs
Normal file
88
SaubPanelOnlineSender/Program.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
2
SaubPanelOnlineSender/REAMDE.md
Normal file
2
SaubPanelOnlineSender/REAMDE.md
Normal file
@ -0,0 +1,2 @@
|
||||
* Назначение
|
||||
проект предназначен для иммитации работы панели САУБ для НОВОГО облака.
|
12
SaubPanelOnlineSender/SaubPanelOnlineSender.csproj
Normal file
12
SaubPanelOnlineSender/SaubPanelOnlineSender.csproj
Normal 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>
|
Loading…
Reference in New Issue
Block a user