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 + { + new object[] {1,2,3 }, + new object[] {4,5,6 }, + new object[] {7,8,9 }, + } + }; + var mapper = new TableMapper(); + + var b = new B(); + mapper.UpdateObjectFromTable(ref b, table, 1); + Console.WriteLine("Done"); } + + } } diff --git a/ConsoleApp1/Table.cs b/ConsoleApp1/Table.cs new file mode 100644 index 00000000..5d76e1ef --- /dev/null +++ b/ConsoleApp1/Table.cs @@ -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
Headers { get; set; } + + public IEnumerable> Rows { get; set; } + + + } +} diff --git a/ConsoleApp1/TableMapper.cs b/ConsoleApp1/TableMapper.cs new file mode 100644 index 00000000..2c03d33f --- /dev/null +++ b/ConsoleApp1/TableMapper.cs @@ -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 + { + Dictionary props; + + public TableMapper() + { + var props = typeof(T).GetProperties(); + this.props = new Dictionary(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; + } + } + + /// + /// Ускоренный обработчик свойства + /// + 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.Convert(Expression.ArrayIndex(argumentsExpression, Expression.Constant(0)), PropertyType) }; + var callExpression = Expression.Call(Expression.Convert(instanceExpression, setter.ReflectedType), setter, argumentExpressions); + Setter = Expression.Lambda(callExpression, instanceExpression, argumentsExpression).Compile(); + + callExpression = Expression.Call(Expression.Convert(instanceExpression, getter.ReflectedType), getter); + Getter = Expression.Lambda(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(); + } +} diff --git a/SaubPanelOnlineSender/CloudDataSender.cs b/SaubPanelOnlineSender/CloudDataSender.cs new file mode 100644 index 00000000..8c991b62 --- /dev/null +++ b/SaubPanelOnlineSender/CloudDataSender.cs @@ -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 { dataSaub }, + HmiVersion = "3.1", + UserName = "online sender", + }; + + return Newtonsoft.Json.JsonConvert.SerializeObject(telemetry); + } + + } +} diff --git a/SaubPanelOnlineSender/DbPlayerService.cs b/SaubPanelOnlineSender/DbPlayerService.cs new file mode 100644 index 00000000..1a487495 --- /dev/null +++ b/SaubPanelOnlineSender/DbPlayerService.cs @@ -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 OnProcessUpdate; + public EventHandler 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, + }; + } + } +} diff --git a/SaubPanelOnlineSender/Program.cs b/SaubPanelOnlineSender/Program.cs new file mode 100644 index 00000000..64eefeaa --- /dev/null +++ b/SaubPanelOnlineSender/Program.cs @@ -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(); + } + } +} diff --git a/SaubPanelOnlineSender/REAMDE.md b/SaubPanelOnlineSender/REAMDE.md new file mode 100644 index 00000000..18bf5001 --- /dev/null +++ b/SaubPanelOnlineSender/REAMDE.md @@ -0,0 +1,2 @@ +* Назначение +проект предназначен для иммитации работы панели САУБ для НОВОГО облака. \ No newline at end of file diff --git a/SaubPanelOnlineSender/SaubPanelOnlineSender.csproj b/SaubPanelOnlineSender/SaubPanelOnlineSender.csproj new file mode 100644 index 00000000..d81cb665 --- /dev/null +++ b/SaubPanelOnlineSender/SaubPanelOnlineSender.csproj @@ -0,0 +1,12 @@ + + + + Exe + netcoreapp3.1 + + + + + + +