添加部分代码注释

This commit is contained in:
2248356998 qq.com
2023-03-29 16:22:01 +08:00
parent 4d85462a85
commit 208ae2bb88
102 changed files with 1503 additions and 472 deletions

View File

@@ -2,6 +2,7 @@
<PropertyGroup>
<LangVersion>latestMajor</LangVersion>
<!--<GenerateDocumentationFile>True</GenerateDocumentationFile>-->
<TargetFrameworks>net4.6.2;net6.0;net7.0</TargetFrameworks>
<Version>1.0.5</Version>
<Title>ThingsGateway.Foundation.Adapter.Modbus</Title>

View File

@@ -3,6 +3,7 @@
<PropertyGroup>
<LangVersion>latestMajor</LangVersion>
<TargetFrameworks>net4.6.2;net6.0;net7.0</TargetFrameworks>
<!--<GenerateDocumentationFile>True</GenerateDocumentationFile>-->
<Version>1.0.5</Version>
<Platforms>AnyCPU</Platforms>
<OutputType>Library</OutputType>

View File

@@ -3,6 +3,7 @@
<PropertyGroup>
<LangVersion>latestMajor</LangVersion>
<TargetFrameworks>net4.6.2;net6.0;net7.0</TargetFrameworks>
<!--<GenerateDocumentationFile>True</GenerateDocumentationFile>-->
<Version>1.0.5</Version>
<Platforms>AnyCPU</Platforms>
<OutputType>Library</OutputType>

View File

@@ -3,6 +3,7 @@
<PropertyGroup>
<LangVersion>latestMajor</LangVersion>
<TargetFrameworks>net4.6.2;net6.0;net7.0</TargetFrameworks>
<!--<GenerateDocumentationFile>True</GenerateDocumentationFile>-->
<Version>1.0.5</Version>
<Title>ThingsGateway.Foundation.Adapter.Siemens</Title>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>

View File

@@ -3,6 +3,7 @@
<PropertyGroup>
<TargetFrameworks>net6.0;net7.0</TargetFrameworks>
<ImplicitUsings>enable</ImplicitUsings>
<!--<GenerateDocumentationFile>True</GenerateDocumentationFile>-->
<IsPackable>false</IsPackable>
<Platforms>AnyCPU;x86</Platforms>

View File

@@ -3,9 +3,16 @@ using Xunit.Abstractions;
namespace ThingsGateway.Foundation.Tests
{
/// <summary>
/// 测试超时等待
/// </summary>
public class WaitingTest
{
private ITestOutputHelper _output;
/// <summary>
/// 测试超时等待
/// </summary>
/// <param name="output"></param>
public WaitingTest(ITestOutputHelper output)
{
_output = output;

View File

@@ -60,7 +60,6 @@ namespace ThingsGateway.Modbus
_plc?.Dispose();
}
private UploadDevice curDevice;
private TouchSocketConfig TouchSocketConfig = new();
protected override void Init(UploadDevice device)
{
curDevice = device;

View File

@@ -2,9 +2,9 @@
<PropertyGroup>
<LangVersion>latestMajor</LangVersion>
<!--<GenerateDocumentationFile>True</GenerateDocumentationFile>-->
<TargetFrameworks>net6.0;net7.0</TargetFrameworks>
<ImplicitUsings>enable</ImplicitUsings>
<GenerateDocumentationFile>False</GenerateDocumentationFile>
<Version>1.1.0</Version>
<RunPostBuildEvent>OnBuildSuccess</RunPostBuildEvent>
<EnableDynamicLoading>true</EnableDynamicLoading>

View File

@@ -4,7 +4,7 @@
<LangVersion>latestMajor</LangVersion>
<TargetFrameworks>net6.0;net7.0</TargetFrameworks>
<ImplicitUsings>enable</ImplicitUsings>
<GenerateDocumentationFile>False</GenerateDocumentationFile>
<!--<GenerateDocumentationFile>True</GenerateDocumentationFile>-->
<Version>1.1.0</Version>
<RunPostBuildEvent>OnBuildSuccess</RunPostBuildEvent>
<EnableDynamicLoading>true</EnableDynamicLoading>

View File

@@ -17,7 +17,7 @@ namespace ThingsGateway.OPCDA
internal CollectDeviceRunTime Device;
private List<CollectVariableRunTime> _deviceVariables = new();
internal ThingsGateway.Foundation.Adapter.OPCDA.OPCDAClient PLC = null;
public override System.Type DriverUI => typeof(ImportVariable);
public override System.Type DriverImportUI => typeof(ImportVariable);
public OPCDAClient(IServiceScopeFactory scopeFactory) : base(scopeFactory)
{

View File

@@ -4,7 +4,7 @@
<LangVersion>latestMajor</LangVersion>
<TargetFrameworks>net6.0;net7.0</TargetFrameworks>
<ImplicitUsings>enable</ImplicitUsings>
<GenerateDocumentationFile>False</GenerateDocumentationFile>
<!--<GenerateDocumentationFile>True</GenerateDocumentationFile>-->
<Version>1.1.0</Version>
<RunPostBuildEvent>OnBuildSuccess</RunPostBuildEvent>
<EnableDynamicLoading>true</EnableDynamicLoading>

View File

@@ -29,7 +29,7 @@ namespace ThingsGateway.OPCUA
[DeviceProperty("登录密码", "")] public string Password { get; set; }
[DeviceProperty("激活订阅", "")] public bool ActiveSubscribe { get; set; } = true;
[DeviceProperty("死区", "")] public float DeadBand { get; set; } = 0;
public override Type DriverUI => typeof(ImportVariable);
public override Type DriverImportUI => typeof(ImportVariable);
[DeviceProperty("自动分组大小", "")] public int GroupSize { get; set; } = 500;
public override ThingsGatewayBitConverter ThingsGatewayBitConverter { get; } = new(EndianType.Little);
[DeviceProperty("重连频率", "")] public int ReconnectPeriod { get; set; } = 5000;

View File

@@ -4,7 +4,7 @@
<LangVersion>latestMajor</LangVersion>
<TargetFrameworks>net6.0;net7.0</TargetFrameworks>
<ImplicitUsings>enable</ImplicitUsings>
<GenerateDocumentationFile>False</GenerateDocumentationFile>
<!--<GenerateDocumentationFile>True</GenerateDocumentationFile>-->
<Version>1.1.0</Version>
<RunPostBuildEvent>OnBuildSuccess</RunPostBuildEvent>
<EnableDynamicLoading>true</EnableDynamicLoading>

View File

@@ -2,9 +2,9 @@
<PropertyGroup>
<LangVersion>latestMajor</LangVersion>
<!--<GenerateDocumentationFile>True</GenerateDocumentationFile>-->
<TargetFrameworks>net6.0;net7.0</TargetFrameworks>
<ImplicitUsings>enable</ImplicitUsings>
<GenerateDocumentationFile>False</GenerateDocumentationFile>
<Version>1.1.0</Version>
<RunPostBuildEvent>OnBuildSuccess</RunPostBuildEvent>
<EnableDynamicLoading>true</EnableDynamicLoading>

View File

@@ -3,6 +3,7 @@
<PropertyGroup>
<LangVersion>latestMajor</LangVersion>
<TargetFrameworks>net6.0;net7.0</TargetFrameworks>
<!--<GenerateDocumentationFile>True</GenerateDocumentationFile>-->
<ImplicitUsings>enable</ImplicitUsings>
<Version>1.1.0</Version>
<RunPostBuildEvent>OnBuildSuccess</RunPostBuildEvent>

View File

@@ -7,6 +7,7 @@
{
private readonly IServiceProvider _services;
/// <inheritdoc cref="UserEventSubscriber"/>
public UserEventSubscriber(IServiceProvider services)
{
this._services = services;

View File

@@ -1,5 +1,6 @@
namespace ThingsGateway.Application
{
/// <inheritdoc cref="IUserCenterService"/>
[Injection(Proxy = typeof(OperDispatchProxy))]
public class UserCenterService : DbRepository<SysUser>, IUserCenterService
@@ -11,6 +12,7 @@
private readonly ISysUserService _userService;
private readonly SysCacheService _sysCacheService;
/// <inheritdoc cref="IUserCenterService"/>
public UserCenterService(ISysUserService userService,
IRelationService relationService,
IResourceService resourceService,

View File

@@ -7,6 +7,7 @@ namespace ThingsGateway.Application
/// </summary>
public class UserIdProvider : IUserIdProvider
{
/// <inheritdoc/>
public string GetUserId(HubConnectionContext connection)
{
var feature = connection.Features.Get<IHttpContextFeature>();

View File

@@ -5,6 +5,7 @@
<TargetFrameworks>net6.0;net7.0</TargetFrameworks>
<ImplicitUsings>enable</ImplicitUsings>
<Version>1.1.0</Version>
<!--<GenerateDocumentationFile>True</GenerateDocumentationFile>-->
</PropertyGroup>
<ItemGroup>

View File

@@ -4,6 +4,7 @@
<PropertyGroup>
<TargetFrameworks>net6.0;net7.0</TargetFrameworks>
<Version>1.1.0</Version>
<!--<GenerateDocumentationFile>True</GenerateDocumentationFile>-->
</PropertyGroup>
<ItemGroup>

View File

@@ -5,6 +5,7 @@
<TargetFrameworks>net4.6.2;net6.0;net7.0</TargetFrameworks>
<NoWarn>SYSLIB0014;CS8981;</NoWarn>
<!--<GenerateDocumentationFile>True</GenerateDocumentationFile>-->
<Version>1.0.5</Version>
<Title>ThingsGateway.Foundation</Title>

View File

@@ -4,6 +4,7 @@
<PropertyGroup>
<TargetFrameworks>net6.0;net7.0</TargetFrameworks>
<Version>1.1.0</Version>
<!--<GenerateDocumentationFile>True</GenerateDocumentationFile>-->
</PropertyGroup>

View File

@@ -8,28 +8,31 @@ namespace ThingsGateway.Web.Foundation
public class ThingsGatewayCacheConst
{
/// <summary>
/// 设备变量名称
/// 采集设备
/// </summary>
public const string Cache_DeviceVariableName = CacheConst.Cache_Prefix_Web + "DeviceVariableName";
/// <summary>
/// 设备变量Id
/// </summary>
public const string Cache_DeviceVariableId = CacheConst.Cache_Prefix_Web + "DeviceVariableId";
public const string Cache_CollectDevice = CacheConst.Cache_Prefix_Web + "CollectDevice";
/// <summary>
/// 设备变量组
/// </summary>
public const string Cache_DeviceVariableGroup = CacheConst.Cache_Prefix_Web + "DeviceVariableGroup";
/// <summary>
/// 设备变量Id
/// </summary>
public const string Cache_DeviceVariableId = CacheConst.Cache_Prefix_Web + "DeviceVariableId";
public const string Cache_CollectDevice = CacheConst.Cache_Prefix_Web + "CollectDevice";
public const string Cache_UploadDevice = CacheConst.Cache_Prefix_Web + "UploadDevice";
/// <summary>
/// 设备变量名称
/// </summary>
public const string Cache_DeviceVariableName = CacheConst.Cache_Prefix_Web + "DeviceVariableName";
/// <summary>
/// 插件
/// </summary>
public const string Cache_DriverPlugin = CacheConst.Cache_Prefix_Web + "Cache_DriverPlugin";
/// <summary>
/// 上传设备
/// </summary>
public const string Cache_UploadDevice = CacheConst.Cache_Prefix_Web + "UploadDevice";
}
}

View File

@@ -1,22 +1,47 @@
namespace ThingsGateway.Web.Foundation
{
/// <summary>
/// 常量
/// </summary>
public class ThingsGatewayConst
{
public const string ThingGateway_AlarmConfig_Base = "THINGSGATEWAY_ALARMCONFIG_BASE";
public const string ThingGateway_HisConfig_Base = "THINGSGATEWAY_HISCONFIG_BASE";
public const string Config_Alarm_Enable = "CONFIG_ALARM_ENABLE";
/// <summary>
/// 报警链接
/// </summary>
public const string Config_Alarm_ConnStr = "CONFIG_ALARM_CONNSTR";
/// <summary>
/// 报警数据库
/// </summary>
public const string Config_Alarm_DbType = "CONFIG_ALARM_DBTYPE";
public const string Config_His_Enable = "CONFIG_HIS_ENABLE";
public const string Config_His_DbType = "CONFIG_HIS_DBTYPE";
/// <summary>
/// 报警使能
/// </summary>
public const string Config_Alarm_Enable = "CONFIG_ALARM_ENABLE";
/// <summary>
/// 历史链接
/// </summary>
public const string Config_His_ConnStr = "CONFIG_HIS_CONNSTR";
/// <summary>
/// 历史数据库
/// </summary>
public const string Config_His_DbType = "CONFIG_HIS_DBTYPE";
/// <summary>
/// 历史使能
/// </summary>
public const string Config_His_Enable = "CONFIG_HIS_ENABLE";
/// <summary>
/// 报警配置
/// </summary>
public const string ThingGateway_AlarmConfig_Base = "THINGSGATEWAY_ALARMCONFIG_BASE";
/// <summary>
/// 历史配置
/// </summary>
public const string ThingGateway_HisConfig_Base = "THINGSGATEWAY_HISCONFIG_BASE";
}
}

View File

@@ -18,13 +18,14 @@ namespace ThingsGateway.Web.Foundation
public class CollectDbInfoControler : IDynamicApiController
{
IServiceScopeFactory _scopeFactory;
IVariableService _variableService { get; set; }
/// <inheritdoc cref="CollectDbInfoControler"/>
public CollectDbInfoControler(IServiceScopeFactory scopeFactory, IVariableService variableService)
{
_scopeFactory = scopeFactory;
_variableService = variableService;
}
IVariableService _variableService { get; set; }
/// <summary>
/// 获取变量信息
/// </summary>

View File

@@ -13,7 +13,7 @@ using ThingsGateway.Core.Extension;
namespace ThingsGateway.Web.Foundation
{
/// <summary>
/// 采集设备
/// 采集状态信息
/// </summary>
[ApiDescriptionSettings(CateGoryConst.ThingsGatewayOpenApi, Order = 200)]
[Route("openApi/collectInfo")]
@@ -24,8 +24,7 @@ namespace ThingsGateway.Web.Foundation
public class CollectInfoControler : IDynamicApiController
{
IServiceScopeFactory _scopeFactory;
CollectDeviceHostService _collectDeviceHostService { get; set; }
AlarmHostService _alarmHostService { get; set; }
/// <inheritdoc cref="CollectInfoControler"/>
public CollectInfoControler(IServiceScopeFactory scopeFactory)
{
_scopeFactory = scopeFactory;
@@ -34,6 +33,8 @@ namespace ThingsGateway.Web.Foundation
_alarmHostService = serviceScope.ServiceProvider.GetBackgroundService<AlarmHostService>();
}
AlarmHostService _alarmHostService { get; set; }
CollectDeviceHostService _collectDeviceHostService { get; set; }
/// <summary>
/// 获取设备信息
/// </summary>

View File

@@ -9,18 +9,18 @@ using ThingsGateway.Foundation;
namespace ThingsGateway.Web.Foundation
{
/// <summary>
/// 设备写入
/// 变量写入
/// </summary>
[ApiDescriptionSettings(CateGoryConst.ThingsGatewayOpenApi, Order = 200)]
[Route("openApi/rpc")]
[Description("设备写入")]
[Description("变量写入")]
[OpenApiPermission]
[LoggingMonitor]
[Authorize(AuthenticationSchemes = "Bearer")]
public class RpcControler : IDynamicApiController
{
IServiceScopeFactory _scopeFactory;
RpcCore _rpcCore { get; set; }
/// <inheritdoc cref="RpcControler"/>
public RpcControler(IServiceScopeFactory scopeFactory)
{
_scopeFactory = scopeFactory;
@@ -28,6 +28,7 @@ namespace ThingsGateway.Web.Foundation
_rpcCore = serviceScope.ServiceProvider.GetService<RpcCore>();
}
RpcCore _rpcCore { get; set; }
/// <summary>
/// 写入设备
/// </summary>

View File

@@ -9,47 +9,64 @@ namespace ThingsGateway.Web.Foundation;
[Tenant(SqlsugarConst.DB_CustomId)]
public class AlarmHis : PrimaryIdEntity
{
/// <inheritdoc cref="MemoryVariable.Name"/>
[SugarColumn(ColumnName = "Name", ColumnDescription = "变量名称", IsNullable = false)]
public string Name { get; set; }
/// <inheritdoc cref="MemoryVariable.Description"/>
[SugarColumn(ColumnName = "Description", ColumnDescription = "描述", IsNullable = true)]
public string Description { get; set; }
/// <inheritdoc cref="CollectVariableRunTime.DeviceName"/>
[SugarColumn(ColumnName = "DeviceName", ColumnDescription = "设备名称", IsNullable = true)]
public string DeviceName { get; set; }
/// <inheritdoc cref="CollectDeviceVariable.VariableAddress"/>
[SugarColumn(ColumnName = "VariableAddress", ColumnDescription = "变量地址")]
public string VariableAddress { get; set; }
/// <inheritdoc cref="MemoryVariable.DataTypeEnum"/>
[SugarColumn(ColumnName = "DataTypeEnum", ColumnDescription = "数据类型", ColumnDataType = "varchar(100)")]
public DataTypeEnum DataTypeEnum { get; set; }
/// <inheritdoc cref="CollectVariableRunTime.Quality"/>
[SugarColumn(ColumnName = "Quality", ColumnDescription = "质量戳")]
public int Quality { get; set; }
/// <inheritdoc cref="CollectVariableRunTime.Value"/>
[SugarColumn(ColumnName = "Value", ColumnDescription = "变量值", IsNullable = false)]
public string Value { get; set; }
/// <inheritdoc cref="CollectVariableRunTime.AlarmCode"/>
[SugarColumn(ColumnName = "AlarmCode", ColumnDescription = "报警值", IsNullable = false)]
public string AlarmCode { get; set; }
/// <inheritdoc cref="CollectVariableRunTime.AlarmLimit"/>
[SugarColumn(ColumnName = "AlarmLimit", ColumnDescription = "报警限值", IsNullable = false)]
public string AlarmLimit { get; set; }
/// <inheritdoc cref="CollectVariableRunTime.AlarmText"/>
[SugarColumn(ColumnName = "AlarmText", ColumnDescription = "报警文本", IsNullable = true)]
public string AlarmText { get; set; }
/// <inheritdoc cref="CollectVariableRunTime.AlarmTime"/>
[SugarColumn(ColumnName = "AlarmTime", ColumnDescription = "报警时间", IsNullable = false)]
public DateTime AlarmTime { get; set; }
/// <inheritdoc cref="CollectVariableRunTime.EventTime"/>
[SugarColumn(ColumnName = "EventTime", ColumnDescription = "事件时间", IsNullable = false)]
public DateTime EventTime { get; set; }
/// <summary>
/// 报警类型
/// </summary>
[SugarColumn(ColumnName = "AlarmTypeEnum", ColumnDescription = "报警类型", IsNullable = false, ColumnDataType = "varchar(100)")]
public AlarmEnum AlarmTypeEnum { get; set; }
/// <summary>
/// 事件类型
/// </summary>
[SugarColumn(ColumnName = "EventTypeEnum", ColumnDescription = "事件类型", IsNullable = false, ColumnDataType = "varchar(100)")]
public EventEnum EventTypeEnum { get; set; }
@@ -61,12 +78,33 @@ public class AlarmHis : PrimaryIdEntity
/// </summary>
public enum AlarmEnum
{
/// <summary>
/// 无
/// </summary>
None,
/// <summary>
/// Bool On
/// </summary>
Open,
/// <summary>
/// Bool Off
/// </summary>
Close,
/// <summary>
/// HH
/// </summary>
HH,
/// <summary>
/// H
/// </summary>
H,
/// <summary>
/// L
/// </summary>
L,
/// <summary>
/// LL
/// </summary>
LL,
}
/// <summary>
@@ -74,16 +112,42 @@ public enum AlarmEnum
/// </summary>
public enum EventEnum
{
/// <summary>
/// 报警产生
/// </summary>
Alarm,
/// <summary>
/// 报警确认
/// </summary>
Check,
/// <summary>
/// 报警恢复
/// </summary>
Finish,
}
/// <summary>
/// 数据库类型
/// </summary>
public enum SqlDbType
{
/// <summary>
/// SqlServer
/// </summary>
SqlServer,
/// <summary>
/// Mysql
/// </summary>
Mysql,
/// <summary>
/// Sqlite
/// </summary>
Sqlite,
/// <summary>
/// PostgreSQL
/// </summary>
PostgreSQL,
/// <summary>
/// Oracle
/// </summary>
Oracle,
}

View File

@@ -1,4 +1,5 @@
using ThingsGateway.Core;
using ThingsGateway.Foundation;
namespace ThingsGateway.Web.Foundation;
/// <summary>

View File

@@ -31,8 +31,18 @@ public class DriverPlugin : BaseEntity
public string FilePath { get; set; }
}
/// <summary>
/// 插件类型
/// </summary>
public enum DriverEnum
{
/// <summary>
/// 采集
/// </summary>
Collect,
/// <summary>
/// 上传
/// </summary>
Upload,
}

View File

@@ -21,13 +21,6 @@ public class MemoryVariable : BaseEntity
[OrderData(Order = 2)]
public string Description { get; set; }
/// <summary>
/// 初始值
/// </summary>
[SugarColumn(ColumnName = "InitialValue", ColumnDescription = "初始值", IsNullable = true)]
[OrderData(Order = 4)]
public string InitialValue { get; set; }
/// <summary>
/// 读写权限
/// </summary>
@@ -43,7 +36,7 @@ public class MemoryVariable : BaseEntity
public DataTypeEnum DataTypeEnum { get; set; }
/// <summary>
/// 变量额外属性Json通常使用为<上传设备List属性>
/// 变量额外属性Json通常使用为上传设备,List属性
/// </summary>
[SugarColumn(IsJson = true, ColumnName = "VariablePropertys", ColumnDescription = "变量属性Json", IsNullable = true)]
public Dictionary<long, List<DependencyProperty>> VariablePropertys { get; set; } = new();
@@ -199,10 +192,18 @@ public class MemoryVariable : BaseEntity
#endregion
}
/// <summary>
/// 历史类型
/// </summary>
public enum HisType
{
/// <summary>
/// 改变存储
/// </summary>
Change,
/// <summary>
/// 采集存储
/// </summary>
Collect,
}

View File

@@ -2,28 +2,33 @@
namespace ThingsGateway.Web.Foundation;
/// <summary>
/// 附加属性
/// </summary>
public class DependencyProperty
{
/// <summary>
/// 属性描述
/// </summary>
[Description("描述")]
public string Description { get; set; }
/// <summary>
/// 属性名称
/// </summary>
[Description("名称")]
public string PropertyName { get; set; }
/// <summary>
/// 属性描述
/// </summary>
[Description("描述")]
public string Description { get; set; }
/// <summary>
/// 属性值
/// </summary>
[Description("属性值")]
public string Value { get; set; }
/// <summary>
/// 备注
/// </summary>
[Description("备注")]
[MaxLength(50)]
public string Remark { get; set; }
/// <summary>
/// 属性值
/// </summary>
[Description("属性值")]
public string Value { get; set; }
}

View File

@@ -12,25 +12,46 @@ namespace ThingsGateway.Web.Foundation;
[Tenant(SqlsugarConst.DB_CustomId)]
public class ValueHis : PrimaryIdEntity
{
/// <summary>
/// 忽略Id无实际上传字段
/// </summary>
[SugarColumn(IsIgnore = true)]
public override long Id { get; set; }
/// <summary>
/// 上传时间
/// </summary>
[TimeDbSplitField(DateType.Month)]
[JsonConverter(typeof(IsoDateTimeConverter))]
[Description("上传时间")]
public DateTime CollectTime { get; set; }
/// <summary>
/// 变量名称
/// </summary>
[SugarColumn(ColumnDataType = "symbol")]
[Description("变量名称")]
public string Name { get; set; }
/// <summary>
/// 质量戳
/// </summary>
[Description("质量戳")]
public int Quality { get; set; }
/// <summary>
/// 变量值
/// </summary>
[Description("变量值")]
public double Value { get; set; }
}
/// <summary>
/// 数据库类型
/// </summary>
public enum HisDbType
{
/// <summary>
/// 时序库QuestDB
/// </summary>
QuestDB,
}

View File

@@ -5,22 +5,36 @@
/// </summary>
public enum DataTypeEnum
{
/// <inheritdoc/>
Object,
/// <inheritdoc/>
Bcd,
/// <inheritdoc/>
DateTime,
/// <inheritdoc/>
String,
/// <inheritdoc/>
Boolean,
/// <inheritdoc/>
Byte,
/// <inheritdoc/>
SByte,
/// <inheritdoc/>
Int16,
/// <inheritdoc/>
UInt16,
/// <inheritdoc/>
Int32,
/// <inheritdoc/>
UInt32,
/// <inheritdoc/>
Int64,
/// <inheritdoc/>
UInt64,
/// <inheritdoc/>
Single,
/// <inheritdoc/>
Double,
}
/// <summary>
@@ -28,6 +42,11 @@ public enum DataTypeEnum
/// </summary>
public static class DataTypeExtension
{
/// <summary>
/// 获取DOTNET RUNTIME TYPE
/// </summary>
/// <param name="coreDataType"></param>
/// <returns></returns>
public static Type GetNetType(this DataTypeEnum coreDataType)
{
switch (coreDataType)
@@ -67,6 +86,12 @@ public static class DataTypeExtension
return typeof(string);
}
}
/// <summary>
/// 获取实际字节长度不足1写1
/// </summary>
/// <param name="coreDataType"></param>
/// <returns></returns>
public static int GetByteLength(this DataTypeEnum coreDataType)
{
switch (coreDataType)
@@ -106,5 +131,4 @@ public static class DataTypeExtension
}
}
}

View File

@@ -5,12 +5,19 @@
/// </summary>
public enum ProtectTypeEnum
{
/// <summary>
/// 只读
/// </summary>
[Description("只读")]
ReadOnly,
/// <summary>
/// 读写
/// </summary>
[Description("读写")]
ReadWrite,
/// <summary>
/// 只写
/// </summary>
[Description("只写")]
WriteOnly,
}

View File

@@ -4,10 +4,13 @@ using System.Linq;
namespace ThingsGateway.Web.Foundation
{
/// <summary>
/// 获取后台服务扩展类
/// </summary>
public static class ServiceExtension
{
/// <summary>
/// 获取后台服务
/// 获取后台服务,用于非IHostService
/// </summary>
public static T GetBackgroundService<T>(this IServiceScopeFactory @this) where T : class, IHostedService
{
@@ -15,7 +18,7 @@ namespace ThingsGateway.Web.Foundation
return hostedService;
}
/// <summary>
/// 获取后台服务
/// 获取后台服务,注意在后台上直接获取可能会出错
/// </summary>
public static T GetBackgroundService<T>(this object @this) where T : class, IHostedService
{

View File

@@ -22,22 +22,38 @@ namespace ThingsGateway.Web.Foundation;
/// </summary>
public class AlarmHostService : BackgroundService, ISingleton
{
private static IServiceScopeFactory _scopeFactory;
private readonly ILogger<AlarmHostService> _logger;
private GlobalCollectDeviceData _globalCollectDeviceData;
private ConcurrentQueue<CollectVariableRunTime> CollectDeviceVariables { get; set; } = new();
public ConcurrentList<CollectVariableRunTime> RealAlarmDeviceVariables { get; set; } = new();
private ConcurrentQueue<CollectVariableRunTime> HisAlarmDeviceVariables { get; set; } = new();
public event VariableCahngeEventHandler OnAlarmChanged;
public event DelegateOnDeviceChanged OnDeviceStatusChanged;
public OperResult StatuString { get; set; } = new OperResult("初始化");
private static IServiceScopeFactory _scopeFactory;
/// <inheritdoc cref="AlarmHostService"/>
public AlarmHostService(ILogger<AlarmHostService> logger, IServiceScopeFactory scopeFactory)
{
_scopeFactory = scopeFactory;
_logger = logger;
_globalCollectDeviceData = scopeFactory.CreateScope().ServiceProvider.GetService<GlobalCollectDeviceData>();
}
/// <summary>
/// 报警变化事件
/// </summary>
public event VariableCahngeEventHandler OnAlarmChanged;
/// <summary>
/// 设备状态变化事件
/// </summary>
public event DelegateOnDeviceChanged OnDeviceStatusChanged;
/// <summary>
/// 实时报警列表
/// </summary>
public ConcurrentList<CollectVariableRunTime> RealAlarmDeviceVariables { get; set; } = new();
/// <summary>
/// 服务状态
/// </summary>
public OperResult StatuString { get; set; } = new OperResult("初始化");
private ConcurrentQueue<CollectVariableRunTime> CollectDeviceVariables { get; set; } = new();
private ConcurrentQueue<CollectVariableRunTime> HisAlarmDeviceVariables { get; set; } = new();
/// <summary>
/// 获取数据库链接
/// </summary>
/// <returns></returns>
public async Task<OperResult<SqlSugarClient>> AlarmConfig()
{
await Task.CompletedTask;
@@ -90,18 +106,21 @@ public class AlarmHostService : BackgroundService, ISingleton
return OperResult.CreateSuccessResult(sqlSugarClient);
}
#region worker服务
/// <inheritdoc/>
public override async Task StartAsync(CancellationToken cancellationToken)
{
_logger?.LogInformation("报警服务启动");
await base.StartAsync(cancellationToken);
}
/// <inheritdoc/>
public override Task StopAsync(CancellationToken cancellationToken)
{
_logger?.LogInformation("报警服务停止");
return base.StopAsync(cancellationToken);
}
/// <inheritdoc/>
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
await Task.Delay(5000, stoppingToken);
@@ -127,9 +146,9 @@ public class AlarmHostService : BackgroundService, ISingleton
/// 循环线程取消标识
/// </summary>
public ConcurrentList<CancellationTokenSource> StoppingTokens = new();
private Task<Task> RealAlarmTask;
private ExpressionEvaluator expressionEvaluator;
private Task<Task> HisAlarmTask;
private Task<Task> RealAlarmTask;
/// <summary>
/// 初始化
/// </summary>
@@ -275,6 +294,27 @@ public class AlarmHostService : BackgroundService, ISingleton
}
);
}
/// <summary>
/// 重启服务
/// </summary>
public void Restart()
{
Stop(_globalCollectDeviceData.CollectDevices);
Start();
}
internal void Start()
{
foreach (var item in _globalCollectDeviceData.CollectDevices)
{
DeviceChange(item);
}
StoppingTokens.Add(new());
Init();
RealAlarmTask.Start();
HisAlarmTask.Start();
}
internal void Stop(IEnumerable<CollectDeviceRunTime> devices = null)
{
@@ -322,45 +362,6 @@ public class AlarmHostService : BackgroundService, ISingleton
StoppingTokens.Remove(StoppingToken);
}
internal void Start()
{
foreach (var item in _globalCollectDeviceData.CollectDevices)
{
DeviceChange(item);
}
StoppingTokens.Add(new());
Init();
RealAlarmTask.Start();
HisAlarmTask.Start();
}
public void Restart()
{
Stop(_globalCollectDeviceData.CollectDevices);
Start();
}
private void DeviceChange(CollectDeviceRunTime device)
{
device.DeviceStatusCahnge += DeviceStatusCahnge;
device.DeviceVariableRunTimes?.ForEach(v => { v.VariableCollectChange += DeviceVariableChange; });
}
private void DeviceStatusCahnge(CollectDeviceRunTime device)
{
OnDeviceStatusChanged?.Invoke(device.Adapt<CollectDeviceRunTime>());
}
private void DeviceVariableChange(CollectVariableRunTime variable)
{
//这里不能序列化变量,报警服务需改变同一个变量指向的属性
CollectDeviceVariables.Enqueue(variable);
}
private ExpressionEvaluator expressionEvaluator;
private void AlarmAnalysis(CollectVariableRunTime item)
{
string limit = string.Empty;
@@ -466,7 +467,22 @@ public class AlarmHostService : BackgroundService, ISingleton
}
}
private void DeviceChange(CollectDeviceRunTime device)
{
device.DeviceStatusCahnge += DeviceStatusCahnge;
device.DeviceVariableRunTimes?.ForEach(v => { v.VariableCollectChange += DeviceVariableChange; });
}
private void DeviceStatusCahnge(CollectDeviceRunTime device)
{
OnDeviceStatusChanged?.Invoke(device.Adapt<CollectDeviceRunTime>());
}
private void DeviceVariableChange(CollectVariableRunTime variable)
{
//这里不能序列化变量,报警服务需改变同一个变量指向的属性
CollectDeviceVariables.Enqueue(variable);
}
#endregion
}

View File

@@ -13,6 +13,9 @@ using TouchSocket.Core;
namespace ThingsGateway.Web.Foundation;
/// <summary>
/// 设备子线程服务
/// </summary>
public class CollectDeviceCore : DisposableObject
{
/// <summary>
@@ -24,8 +27,12 @@ public class CollectDeviceCore : DisposableObject
/// 循环线程取消标识
/// </summary>
public ConcurrentList<CancellationTokenSource> StoppingTokens = new();
/// <summary>
/// 当前的驱动插件实例
/// </summary>
internal DriverBase _driver;
protected ILogger _logger;
internal bool isInitSuccess;
/// <summary>
/// 当前设备信息
@@ -33,10 +40,9 @@ public class CollectDeviceCore : DisposableObject
protected CollectDeviceRunTime _device;
/// <summary>
/// 当前的驱动插件实例
/// 日志
/// </summary>
internal DriverBase _driver;
protected ILogger _logger;
/// <summary>
/// 全局插件服务
/// </summary>
@@ -46,8 +52,8 @@ public class CollectDeviceCore : DisposableObject
/// 分包变量
/// </summary>
protected List<DeviceVariableSourceRead> DeviceVariableSourceReads = new();
protected IServiceScopeFactory _scopeFactory;
private IServiceScopeFactory _scopeFactory;
/// <inheritdoc cref="CollectDeviceCore"/>
public CollectDeviceCore(IServiceScopeFactory scopeFactory)
{
@@ -78,9 +84,9 @@ public class CollectDeviceCore : DisposableObject
/// 当前设备全部设备属性,执行初始化后获取正确值
/// </summary>
public List<DependencyProperty> Propertys { get; protected set; }
/// <inheritdoc cref="GlobalCollectDeviceData"/>
protected GlobalCollectDeviceData _globalCollectDeviceData { get; set; }
protected IDriverPluginService _driverPluginService { get; set; }
internal bool isInitSuccess;
private IDriverPluginService _driverPluginService { get; set; }
/// <summary>
/// 初始化,在设备子线程创建或更新时才会执行
/// </summary>
@@ -136,9 +142,77 @@ public class CollectDeviceCore : DisposableObject
}
#region 线
/// <summary>
/// 线程
/// </summary>
protected Task<Task> DeviceTask;
/// <summary>
/// 暂停采集
/// </summary>
public void PasueThread(bool enable)
{
lock (this)
{
var str = enable == false ? "设备线程采集暂停" : "设备线程采集继续";
_logger?.LogInformation($"{str}:{_device.Name}");
this.Device.Enable = enable;
}
}
/// <summary>
/// 开始采集
/// </summary>
public virtual void StartThread()
{
DeviceTask?.Start();
}
/// <summary>
/// 停止采集
/// </summary>
public virtual void StopThread()
{
try
{
CancellationTokenSource StoppingToken = StoppingTokens.LastOrDefault();
StoppingToken?.Cancel();
_logger?.LogInformation($"{_device.Name}采集线程停止中");
var devResult = DeviceTask?.Result;
if (devResult?.Status != TaskStatus.Canceled)
{
if (devResult?.Wait(5000) == true)
{
_logger?.LogInformation($"{_device.Name}采集线程已停止");
}
else
{
_logger?.LogInformation($"{_device.Name}采集线程停止超时,已强制取消");
}
}
DeviceTask?.Dispose();
if (StoppingToken != null)
{
StoppingTokens.Remove(StoppingToken);
}
_globalCollectDeviceData.CollectDevices.RemoveWhere(it => it.Id == Device.Id);
try
{
_driver?.AfterStop();
_driver?.Dispose();
}
catch (Exception ex)
{
_logger.LogError($"{Device.Name} Dispose Error: {ex.Message}");
}
}
finally
{
_pluginService.DeleteDriver(DeviceId, Device.PluginId);
}
}
/// <summary>
/// 初始化
/// </summary>
@@ -298,69 +372,6 @@ public class CollectDeviceCore : DisposableObject
}
);
}
/// <summary>
/// 暂停采集
/// </summary>
public void PasueThread(bool enable)
{
lock (this)
{
var str = enable == false ? "设备线程采集暂停" : "设备线程采集继续";
_logger?.LogInformation($"{str}:{_device.Name}");
this.Device.Enable = enable;
}
}
/// <summary>
/// 开始采集
/// </summary>
public virtual void StartThread()
{
DeviceTask?.Start();
}
public virtual void StopThread()
{
try
{
CancellationTokenSource StoppingToken = StoppingTokens.LastOrDefault();
StoppingToken?.Cancel();
_logger?.LogInformation($"{_device.Name}采集线程停止中");
var devResult = DeviceTask?.Result;
if (devResult?.Status != TaskStatus.Canceled)
{
if (devResult?.Wait(5000) == true)
{
_logger?.LogInformation($"{_device.Name}采集线程已停止");
}
else
{
_logger?.LogInformation($"{_device.Name}采集线程停止超时,已强制取消");
}
}
DeviceTask?.Dispose();
if (StoppingToken != null)
{
StoppingTokens.Remove(StoppingToken);
}
_globalCollectDeviceData.CollectDevices.RemoveWhere(it => it.Id == Device.Id);
try
{
_driver?.AfterStop();
_driver?.Dispose();
}
catch (Exception ex)
{
_logger.LogError($"{Device.Name} Dispose Error: {ex.Message}");
}
}
finally
{
_pluginService.DeleteDriver(DeviceId, Device.PluginId);
}
}
#endregion
#region
@@ -475,6 +486,13 @@ public class CollectDeviceCore : DisposableObject
}
}
/// <summary>
/// 执行方法
/// </summary>
/// <param name="coreMethod"></param>
/// <param name="par"></param>
/// <returns></returns>
public async Task<OperResult> InvokeMed(Method coreMethod, params object[] par)
{
return (OperResult)await coreMethod.InvokeObjectAsync(_driver, par);
@@ -531,6 +549,8 @@ public class CollectDeviceCore : DisposableObject
}
}
/// <inheritdoc/>
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);

View File

@@ -16,12 +16,11 @@ namespace ThingsGateway.Web.Foundation;
/// </summary>
public class CollectDeviceHostService : BackgroundService
{
private static IServiceScopeFactory _scopeFactory;
private readonly ILogger<CollectDeviceHostService> _logger;
private GlobalCollectDeviceData _globalCollectDeviceData;
private PluginCore _pluginService;
public ConcurrentList<CollectDeviceCore> CollectDeviceCores { get; private set; } = new();
ICollectDeviceService _collectDeviceService { get; set; }
private static IServiceScopeFactory _scopeFactory;
/// <inheritdoc/>
public CollectDeviceHostService(ILogger<CollectDeviceHostService> logger,
IServiceScopeFactory scopeFactory)
{
@@ -34,42 +33,27 @@ public class CollectDeviceHostService : BackgroundService
serviceScope.ServiceProvider.GetService<HardwareInfoService>();
_collectDeviceService = serviceScope.ServiceProvider.GetService<ICollectDeviceService>();
}
/// <summary>
/// 设备子线程列表
/// </summary>
public ConcurrentList<CollectDeviceCore> CollectDeviceCores { get; private set; } = new();
ICollectDeviceService _collectDeviceService { get; set; }
#region
/// <summary>
/// 更新设备线程
/// 控制设备线程启停
/// </summary>
public async Task UpDeviceThread(long devId, bool isUpdateDb = true)
public void ConfigDeviceThread(long deviceId, bool isStart)
{
if (!_stoppingToken.IsCancellationRequested)
{
StopOtherHostService(CollectDeviceCores.Select(a => a.Device).ToList());
var devcore = CollectDeviceCores.FirstOrDefault(it => it?.DeviceId == devId);
if (devcore == null) { throw Oops.Bah($"更新设备线程失败,不存在{devId}为id的设备"); }
//这里先停止采集,操作会使线程取消,需要重新恢复线程
devcore.StopThread();
CollectDeviceRunTime dev = null;
if (isUpdateDb)
dev = (await _collectDeviceService.GetCollectDeviceRuntime(devId)).FirstOrDefault();
else
dev = devcore.Device;
if (dev == null) { _logger.LogError($"更新设备线程失败,不存在{devId}为id的设备"); }
devcore.Init(dev);
devcore.StartThread();
StartOtherHostService();
}
if (deviceId == 0)
CollectDeviceCores.ForEach(it => it.PasueThread(isStart));
else
CollectDeviceCores.FirstOrDefault(it => it.DeviceId == deviceId)?.PasueThread(isStart);
}
/// <summary>
/// 删除设备线程,并且释放资源
/// </summary>
/// <param name="devices"></param>
public void RemoveDeviceThread(long devId)
{
var deviceThread = CollectDeviceCores.FirstOrDefault(x => x.DeviceId == devId);
@@ -80,7 +64,9 @@ public class CollectDeviceHostService : BackgroundService
CollectDeviceCores.Remove(deviceThread);
}
}
/// <summary>
/// 重启采集服务
/// </summary>
public async Task RestartDeviceThread()
{
try
@@ -131,7 +117,23 @@ public class CollectDeviceHostService : BackgroundService
_logger.LogError(ex, nameof(RestartDeviceThread));
}
}
/// <summary>
/// 启动其他后台服务
/// </summary>
public void StartOtherHostService()
{
var alarmHostService = _scopeFactory.GetBackgroundService<AlarmHostService>();
var valueHisHostService = _scopeFactory.GetBackgroundService<ValueHisHostService>();
alarmHostService?.Start();
valueHisHostService?.Start();
var uploadDeviceHostService = _scopeFactory.GetBackgroundService<UploadDeviceHostService>();
uploadDeviceHostService.StartDeviceThread();
}
/// <summary>
/// 停止其他后台服务
/// </summary>
public void StopOtherHostService(List<CollectDeviceRunTime> oldDeviceRuntime)
{
if (oldDeviceRuntime?.Count > 0)
@@ -150,35 +152,42 @@ public class CollectDeviceHostService : BackgroundService
}
public void StartOtherHostService()
{
var alarmHostService = _scopeFactory.GetBackgroundService<AlarmHostService>();
var valueHisHostService = _scopeFactory.GetBackgroundService<ValueHisHostService>();
alarmHostService?.Start();
valueHisHostService?.Start();
var uploadDeviceHostService = _scopeFactory.GetBackgroundService<UploadDeviceHostService>();
uploadDeviceHostService.StartDeviceThread();
}
/// <summary>
/// 控制设备线程启停
/// 更新设备线程
/// </summary>
public void ConfigDeviceThread(long deviceId, bool isStart)
public async Task UpDeviceThread(long devId, bool isUpdateDb = true)
{
if (deviceId == 0)
CollectDeviceCores.ForEach(it => it.PasueThread(isStart));
else
CollectDeviceCores.FirstOrDefault(it => it.DeviceId == deviceId)?.PasueThread(isStart);
}
if (!_stoppingToken.IsCancellationRequested)
{
StopOtherHostService(CollectDeviceCores.Select(a => a.Device).ToList());
var devcore = CollectDeviceCores.FirstOrDefault(it => it?.DeviceId == devId);
if (devcore == null) { throw Oops.Bah($"更新设备线程失败,不存在{devId}为id的设备"); }
//这里先停止采集,操作会使线程取消,需要重新恢复线程
devcore.StopThread();
CollectDeviceRunTime dev = null;
if (isUpdateDb)
dev = (await _collectDeviceService.GetCollectDeviceRuntime(devId)).FirstOrDefault();
else
dev = devcore.Device;
if (dev == null) { _logger.LogError($"更新设备线程失败,不存在{devId}为id的设备"); }
devcore.Init(dev);
devcore.StartThread();
StartOtherHostService();
}
}
#endregion
#region
/// <summary>
/// 获取设备方法
/// </summary>
/// <param name="devId"></param>
/// <returns></returns>
public List<string> GetDeviceMethods(long devId)
{
var id = YitIdHelper.NextId();
@@ -199,21 +208,12 @@ public class CollectDeviceHostService : BackgroundService
}
public DriverBase GetImportUI(long devId)
{
var result = CollectDeviceCores.FirstOrDefault(a => a.DeviceId == devId);
if (result == null)
{
return null;
}
else
{
return result._driver;
}
}
/// <summary>
/// 获取设备属性
/// </summary>
/// <param name="driverId"></param>
/// <param name="devId"></param>
/// <returns></returns>
public List<DependencyProperty> GetDevicePropertys(long driverId, long devId = 0)
{
using var serviceScope = _scopeFactory.CreateScope();
@@ -243,14 +243,37 @@ public class CollectDeviceHostService : BackgroundService
_pluginService.DeleteDriver(id, driverId);
}
}
/// <summary>
/// 获取导入变量UI
/// </summary>
/// <param name="devId"></param>
/// <returns></returns>
public DriverBase GetImportUI(long devId)
{
var result = CollectDeviceCores.FirstOrDefault(a => a.DeviceId == devId);
if (result == null)
{
return null;
}
else
{
return result._driver;
}
}
#endregion
#region worker服务
private CancellationToken _stoppingToken;
/// <inheritdoc/>
public override async Task StartAsync(CancellationToken cancellationToken)
{
await base.StartAsync(cancellationToken);
}
/// <inheritdoc/>
public override async Task StopAsync(CancellationToken cancellationToken)
{
var stoppingToken = new CancellationTokenSource();
@@ -273,12 +296,7 @@ public class CollectDeviceHostService : BackgroundService
await Task.Delay(2000);
await base.StopAsync(cancellationToken);
}
private CancellationToken _stoppingToken;
/// <summary>
/// 变量触发变化
/// </summary>
public delegate void VariableCahngeListEventHandler(List<CollectVariableRunTime> collectVariableRunTimes);
/// <inheritdoc/>
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
await RestartDeviceThread();
@@ -292,15 +310,15 @@ public class CollectDeviceHostService : BackgroundService
CollectDeviceCore devcore = CollectDeviceCores[i];
if (
(devcore.Device.ActiveTime != DateTime.MinValue && devcore.Device.ActiveTime.AddMinutes(3) <= DateTime.Now)
|| devcore.isInitSuccess==false
|| devcore.isInitSuccess == false
)
{
if (devcore.StoppingTokens.Last().Token.IsCancellationRequested)
continue;
if (devcore.Device.DeviceStatus == DeviceStatusEnum.Pause)
continue;
if(devcore.isInitSuccess)
_logger?.LogWarning(devcore.Device.Name + "初始化失败,重启线程中");
if (!devcore.isInitSuccess)
_logger?.LogWarning(devcore.Device.Name + "初始化失败,重启线程中");
else
_logger?.LogWarning(devcore.Device.Name + "采集线程假死,重启线程中");
await UpDeviceThread(devcore.DeviceId, false);

View File

@@ -1,8 +1,12 @@
namespace ThingsGateway.Web.Foundation;
/// <summary>
/// <inheritdoc/>
/// <br></br>
/// 未完成
/// </summary>
public class CollectMulDeviceCore : CollectDeviceCore
{
/// <inheritdoc/>
public CollectMulDeviceCore(IServiceScopeFactory scopeFactory) : base(scopeFactory)
{
@@ -10,10 +14,12 @@ public class CollectMulDeviceCore : CollectDeviceCore
/// <inheritdoc/>
public override void StartThread()
{
DeviceTask?.Start();
}
/// <inheritdoc/>
public override void StopThread()
{
}

View File

@@ -8,9 +8,15 @@
[AttributeUsage(AttributeTargets.Method, Inherited = false, AllowMultiple = false)]
public class DeviceMethodAttribute : Attribute
{
/// <summary>
/// 名称
/// </summary>
public string Name { get; }
/// <summary>
/// 描述
/// </summary>
public string Description { get; }
/// <inheritdoc cref="DeviceMethodAttribute"/>
public DeviceMethodAttribute(string name, string description = "")
{
Name = name;

View File

@@ -8,10 +8,15 @@
[AttributeUsage(AttributeTargets.Property, Inherited = true, AllowMultiple = false)]
public class DevicePropertyAttribute : Attribute
{
/// <summary>
/// 名称
/// </summary>
public string Name { get; }
/// <summary>
/// 描述
/// </summary>
public string Remark { get; }
/// <inheritdoc cref="DevicePropertyAttribute"/>
public DevicePropertyAttribute(string name, string remark = null)
{
Name = name;

View File

@@ -3,15 +3,20 @@
/// <summary>
/// 变量属性的特性说明
/// <br></br>
/// 继承<see cref="DriverBase"/>的上传插件,在需主动暴露的变量配置属性中加上这个特性<see cref="VariablePropertyAttribute"/>
/// 继承<see cref="UpLoadBase"/>的上传插件,在需主动暴露的变量配置属性中加上这个特性<see cref="VariablePropertyAttribute"/>
/// </summary>
[AttributeUsage(AttributeTargets.Property, Inherited = true, AllowMultiple = false)]
public class VariablePropertyAttribute : Attribute
{
/// <summary>
/// 变量属性名称
/// </summary>
public string Name { get; }
/// <summary>
/// 附加说明
/// </summary>
public string Remark { get; }
/// <inheritdoc cref="VariablePropertyAttribute"/>>
public VariablePropertyAttribute(string name, string remark = null)
{
Name = name;

View File

@@ -8,6 +8,7 @@ namespace ThingsGateway.Web.Foundation;
/// </summary>
public class GlobalCollectDeviceData : ISingleton
{
/// <inheritdoc cref="GlobalCollectDeviceData"/>
public GlobalCollectDeviceData()
{

View File

@@ -1,7 +1,10 @@
namespace ThingsGateway.Web.Foundation;
/// <summary>
/// 报警扩展
/// </summary>
public static class AlarmHostServiceHelpers
{
/// <summary>
/// 获取bool报警类型
/// </summary>

View File

@@ -6,32 +6,21 @@ using TouchSocket.Core;
namespace ThingsGateway.Web.Foundation
{
/// <summary>
/// 表达式扩展
/// </summary>
public static class ExpressionEvaluatorExtension
{
static ExpressionEvaluator ExpressionEvaluator;
static ExpressionEvaluatorExtension()
{
ExpressionEvaluator = new();
ExpressionEvaluator.PreEvaluateVariable += Evaluator_PreEvaluateVariable;
}
public static void Evaluator_PreEvaluateVariable(object sender, VariablePreEvaluationEventArg e)
{
var data = App.GetService<GlobalCollectDeviceData>();
var obj = data.CollectVariables.FirstOrDefault(it => it.Name == e.Name);
if (obj == null)
{
return;
}
if (obj.Value != null)
e.Value = Convert.ChangeType(obj.Value, obj.DataType);
}
public static ExpressionEvaluator ExpressionEvaluator;
/// <summary>
/// 计算表达式raw*100
/// 计算表达式
/// </summary>
/// <param name="expressions"></param>
/// <returns></returns>
public static object GetExpressionsResult(this string expressions, object rawvalue)
{
if (expressions.IsNullOrEmpty())
@@ -46,7 +35,21 @@ namespace ThingsGateway.Web.Foundation
var value = ExpressionEvaluator.Evaluate(expressions);
return value;
}
/// <summary>
/// 表达式的扩展变量来源
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public static void Evaluator_PreEvaluateVariable(object sender, VariablePreEvaluationEventArg e)
{
var data = App.GetService<GlobalCollectDeviceData>();
var obj = data.CollectVariables.FirstOrDefault(it => it.Name == e.Name);
if (obj == null)
{
return;
}
if (obj.Value != null)
e.Value = Convert.ChangeType(obj.Value, obj.DataType);
}
}
}

View File

@@ -4,9 +4,29 @@ using System.Reflection;
using ThingsGateway.Foundation;
namespace ThingsGateway.Web.Foundation;
/// <summary>
/// 读写扩展
/// </summary>
public static class ReadWriteHelpers
{
/// <summary>
/// 根据<see cref="OperResult.IsSuccess"/>执行action
/// </summary>
public static OperResult<T> DealWithReadResult<T>(OperResult<T> read, Action<T> action)
{
if (!read.IsSuccess || action == null)
return read;
action(read.Content);
return read;
}
/// <summary>
/// 根据<see cref="PropertyInfo"/> 数据类型转化返回值类型
/// </summary>
/// <param name="p"></param>
/// <param name="value"></param>
/// <returns></returns>
public static object ObjToTypeValue(PropertyInfo p, string value)
{
object _value = null;
@@ -45,18 +65,6 @@ public static class ReadWriteHelpers
return _value;
}
/// <summary>
/// 根据<see cref="OperResult.IsSuccess"/>执行action
/// </summary>
public static OperResult<T> DealWithReadResult<T>(OperResult<T> read, Action<T> action)
{
if (!read.IsSuccess || action == null)
return read;
action(read.Content);
return read;
}
/// <summary>
/// 在返回的字节数组中解析每个变量的值
/// 根据每个变量的<see cref="CollectVariableRunTime.Index"/>

View File

@@ -1,16 +1,20 @@
namespace ThingsGateway.Web.Foundation;
/// <summary>
/// 计时器
/// </summary>
public class TimerTick
{
/// <summary>
/// 误差
/// </summary>
private int offsetTime = 60;
/// <summary>
/// 时间差
/// </summary>
private int milliSeconds = 1000;
/// <summary>
/// 误差
/// </summary>
private int offsetTime = 60;
/// <inheritdoc cref="TimerTick"/>
public TimerTick(int milliSeconds = 1000)
{
if (milliSeconds < 20)
@@ -39,6 +43,10 @@ public class TimerTick
return true;
}
/// <summary>
/// 是否到达设置时间
/// </summary>
/// <returns></returns>
public bool IsTickHappen() => IsTickHappen(DateTime.Now);
/// <summary>

View File

@@ -27,6 +27,16 @@ public class DeviceVariableMedRead
/// 字符串转换器默认支持基础类型和Json。可以自定义。
/// </summary>
public TouchSocket.Core.StringConverter Converter { get; }
/// <summary>
/// 需分配的变量
/// </summary>
public CollectVariableRunTime DeviceVariable { get; set; } = new();
/// <summary>
/// 方法
/// </summary>
public Method MedInfo { get; set; }
/// <summary>
/// 方法参数
/// </summary>
@@ -36,14 +46,6 @@ public class DeviceVariableMedRead
/// </summary>
public string MedStr { get; set; }
/// <summary>
/// 方法
/// </summary>
public Method MedInfo { get; set; }
/// <summary>
/// 需分配的变量
/// </summary>
public CollectVariableRunTime DeviceVariable { get; set; } = new();
/// <summary>
/// 检测是否达到读取间隔
/// </summary>
/// <param name="time"></param>

View File

@@ -22,13 +22,14 @@ public class DeviceVariableSourceRead
/// </summary>
public string Address { get; set; }
/// <summary>
/// 读取长度
/// </summary>
public string Length { get; set; }
/// <summary>
/// 需分配的变量列表
/// </summary>
public List<CollectVariableRunTime> DeviceVariables { get; set; } = new();
/// <summary>
/// 读取长度
/// </summary>
public string Length { get; set; }
/// <summary>
/// 检测是否达到读取间隔
/// </summary>

View File

@@ -15,19 +15,27 @@ namespace ThingsGateway.Web.Foundation;
/// </summary>
public abstract class DriverBase : IDisposable
{
/// <summary>
/// <see cref="TouchSocketConfig"/>
/// </summary>
public TouchSocketConfig TouchSocketConfig;
/// <summary>
/// 日志
/// </summary>
protected ILogger _logger;
private bool isLogOut;
private ILogger privateLogger;
private IServiceScopeFactory _scopeFactory;
/// <inheritdoc cref="DriverBase"/>
public DriverBase(IServiceScopeFactory scopeFactory)
{
_scopeFactory = scopeFactory;
TouchSocketConfig = new TouchSocketConfig();
TouchSocketConfig.ConfigureContainer(a => a.RegisterSingleton<ILog>(new EasyLogger(Log_Out)));
}
/// <summary>
/// 是否输出日志
/// </summary>
public bool IsLogOut
{
get => isLogOut;
@@ -87,9 +95,11 @@ public abstract class DriverBase : IDisposable
/// <returns></returns>
public abstract Task BeforStart();
public virtual Type DriverUI { get; }
/// <summary>
/// 导入变量UI
/// </summary>
public virtual Type DriverImportUI { get; }
/// <inheritdoc/>
public abstract void Dispose();
/// <summary>
/// 初始化
@@ -125,8 +135,6 @@ public abstract class DriverBase : IDisposable
/// <summary>
/// 采集驱动读取
/// </summary>
/// <param name="deviceVariableSourceRead"></param>
/// <returns></returns>
public virtual async Task<OperResult<byte[]>> ReadSourceAsync(DeviceVariableSourceRead deviceVariableSourceRead, CancellationToken cancellationToken)
{
ushort length;
@@ -152,9 +160,6 @@ public abstract class DriverBase : IDisposable
/// <br></br>
/// 通常使用<see cref="IReadWrite.ReadAsync(string, int, System.Threading.CancellationToken)"/>可以直接返回正确信息
/// </summary>
/// <param name="address">变量地址</param>
/// <param name="length">读取长度</param>
/// <returns></returns>
protected abstract Task<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken cancellationToken);
private void Log_Out(LogType arg1, object arg2, string arg3, Exception arg4)

View File

@@ -2,10 +2,20 @@
namespace ThingsGateway.Web.Foundation;
/// <summary>
/// 导入变量UI
/// </summary>
public abstract class DriverUI : ComponentBase
{
/// <summary>
/// 设备通讯类
/// </summary>
[Parameter]
public virtual object Driver { get; set; }
/// <summary>
/// 获取导入变量列表
/// </summary>
/// <returns></returns>
public abstract List<CollectDeviceVariable> GetVariableList();
}

View File

@@ -16,15 +16,28 @@ namespace ThingsGateway.Web.Foundation;
/// </summary>
public abstract class UpLoadBase : IDisposable
{
/// <summary>
/// <see cref="TouchSocketConfig"/>
/// </summary>
public TouchSocketConfig TouchSocketConfig=new();
/// <summary>
/// 日志
/// </summary>
protected ILogger _logger;
private bool isLogOut;
private ILogger privateLogger;
/// <summary>
/// 服务工厂
/// </summary>
protected IServiceScopeFactory _scopeFactory;
/// <inheritdoc cref="UpLoadBase"/>
public UpLoadBase(IServiceScopeFactory scopeFactory)
{
_scopeFactory = scopeFactory;
}
/// <summary>
/// 是否输出日志
/// </summary>
public bool IsLogOut
{
get => isLogOut;
@@ -52,6 +65,7 @@ public abstract class UpLoadBase : IDisposable
/// 返回是否已经在线/成功启动
/// </summary>
public abstract OperResult Success();
/// <inheritdoc/>
public abstract void Dispose();
/// <summary>
/// 初始化
@@ -72,9 +86,15 @@ public abstract class UpLoadBase : IDisposable
/// <summary>
/// 循环执行
/// </summary>
/// <param name="deviceVariableSourceRead"></param>
/// <returns></returns>
public abstract Task ExecuteAsync(CancellationToken cancellationToken);
/// <summary>
/// <see cref="TouchSocket"/> 日志输出
/// </summary>
/// <param name="arg1"></param>
/// <param name="arg2"></param>
/// <param name="arg3"></param>
/// <param name="arg4"></param>
protected void Log_Out(LogType arg1, object arg2, string arg3, Exception arg4)
{
switch (arg1)

View File

@@ -4,14 +4,29 @@
/// </summary>
public enum DeviceStatusEnum
{
/// <summary>
/// 在线
/// </summary>
[Description("在线")]
OnLine = 1,
/// <summary>
/// 离线
/// </summary>
[Description("离线")]
OffLine = 2,
/// <summary>
/// 暂停
/// </summary>
[Description("暂停")]
Pause = 3,
/// <summary>
/// 部分失败
/// </summary>
[Description("部分失败")]
OnLineButNoInitialValue = 4,
/// <summary>
/// 初始化
/// </summary>
[Description("初始化")]
Default = 5,
}

View File

@@ -1,5 +1,8 @@
namespace ThingsGateway.Web.Foundation;
/// <summary>
/// 变量名称/值表示
/// </summary>
public class NameVaue
{
/// <summary>

View File

@@ -20,6 +20,7 @@ public class PluginCore : ISingleton
{
private readonly ILogger<PluginCore> _logger;
private static IServiceScopeFactory _scopeFactory;
/// <inheritdoc cref="PluginCore"/>
public PluginCore(ILogger<PluginCore> logger, IServiceScopeFactory scopeFactory)
{
_scopeFactory = scopeFactory;
@@ -42,7 +43,12 @@ public class PluginCore : ISingleton
/// 插件ID/设备ID集合
/// </summary>
public ConcurrentDictionary<long, List<long>> DeviceOnDriverPlugins { get; private set; } = new();
/// <summary>
/// 获取插件
/// </summary>
/// <param name="devId"></param>
/// <param name="plugin"></param>
/// <returns></returns>
public object AddDriver(long devId, DriverPlugin plugin)
{
//先判断是否已经拥有插件模块
@@ -134,7 +140,11 @@ public class PluginCore : ISingleton
return assembly;
}
}
/// <summary>
/// 尝试添加插件,返回插件表示类
/// </summary>
/// <param name="plugin"></param>
/// <returns></returns>
public async Task<List<DriverPlugin>> TestAddDriver(DriverPluginAddInput plugin)
{
var devId = YitIdHelper.NextId();
@@ -222,7 +232,11 @@ public class PluginCore : ISingleton
assemblyLoadContext.Unload();
}
}
/// <summary>
/// 删除插件
/// </summary>
/// <param name="devId"></param>
/// <param name="pluginId"></param>
public void DeleteDriver(long devId, long pluginId)
{
try

View File

@@ -10,7 +10,7 @@ using TouchSocket.Core;
namespace ThingsGateway.Web.Foundation;
/// <summary>
/// 驱动插件服务
/// 变量写入值服务
/// </summary>
public class RpcCore : ISingleton
{
@@ -22,6 +22,7 @@ public class RpcCore : ISingleton
private CollectDeviceHostService _collectDeviceHostService;
private IServiceScopeFactory _scopeFactory;
/// <inheritdoc cref="RpcCore"/>
public RpcCore(ILogger<RpcCore> logger, IServiceScopeFactory scopeFactory)
{
_scopeFactory = scopeFactory;
@@ -46,10 +47,6 @@ public class RpcCore : ISingleton
/// <summary>
/// 反向RPC入口方法
/// </summary>
/// <param name="MethodBase"></param>
/// <param name="par"></param>
/// <param name="id"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public async Task<OperResult> InvokeDeviceMethod(string sourceName, NameVaue item, bool isBlazorWeb = false)
{

View File

@@ -11,6 +11,9 @@ using TouchSocket.Core;
namespace ThingsGateway.Web.Foundation;
/// <summary>
/// 设备子线程服务
/// </summary>
public class UploadDeviceCore : DisposableObject
{
@@ -27,11 +30,7 @@ public class UploadDeviceCore : DisposableObject
/// <summary>
/// 当前的驱动插件实例
/// </summary>
private UpLoadBase _driver
{
get;
set;
}
private UpLoadBase _driver { get; set; }
private ILogger _logger;
/// <summary>
@@ -42,6 +41,7 @@ public class UploadDeviceCore : DisposableObject
private IServiceScopeFactory _scopeFactory;
/// <inheritdoc cref="UploadDeviceCore"/>
public UploadDeviceCore(IServiceScopeFactory scopeFactory)
{
@@ -86,11 +86,11 @@ public class UploadDeviceCore : DisposableObject
SetPluginProperties(_device.DevicePropertys);
_driver.IsLogOut = _device.IsLogOut;
_driver.Init(_logger, _device);
isInitSuccess=true;
isInitSuccess = true;
}
catch (Exception ex)
{
isInitSuccess=false;
isInitSuccess = false;
_logger.LogError(ex, $"{_device.Name}Init失败");
}
StoppingTokens.Add(new());
@@ -227,6 +227,9 @@ public class UploadDeviceCore : DisposableObject
{
DeviceTask?.Start();
}
/// <summary>
/// 停止上传
/// </summary>
public void StopThread()
{
try
@@ -288,7 +291,7 @@ public class UploadDeviceCore : DisposableObject
}
/// <inheritdoc/>
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);

View File

@@ -16,9 +16,7 @@ namespace ThingsGateway.Web.Foundation;
/// </summary>
public class UploadDeviceHostService : BackgroundService
{
private readonly ILogger<UploadDeviceHostService> _logger;
private PluginCore _pluginService;
private static IServiceScopeFactory _scopeFactory;
/// <inheritdoc cref="UploadDeviceHostService"/>
public UploadDeviceHostService(ILogger<UploadDeviceHostService> logger,
IServiceScopeFactory scopeFactory)
{
@@ -30,8 +28,15 @@ public class UploadDeviceHostService : BackgroundService
_uploadDeviceService = serviceScope.ServiceProvider.GetService<IUploadDeviceService>();
}
/// <summary>
/// 全部设备子线程
/// </summary>
public ConcurrentList<UploadDeviceCore> UploadDeviceCores { get; private set; } = new();
IUploadDeviceService _uploadDeviceService { get; set; }
private ILogger<UploadDeviceHostService> _logger { get; set; }
private PluginCore _pluginService { get; set; }
private IServiceScopeFactory _scopeFactory { get; set; }
private IUploadDeviceService _uploadDeviceService { get; set; }
#region
/// <summary>
@@ -48,7 +53,6 @@ public class UploadDeviceHostService : BackgroundService
/// <summary>
/// 删除设备线程,并且释放资源
/// </summary>
/// <param name="devices"></param>
public void RemoveDeviceThread()
{
var dev = UploadDeviceCores;
@@ -67,23 +71,9 @@ public class UploadDeviceHostService : BackgroundService
}
/// <summary>
/// 启动设备线程
/// 重启全部设备
/// </summary>
/// <param name="devices"></param>
public void StartDeviceThread()
{
var devs = (_uploadDeviceService.GetUploadDeviceRuntime());
foreach (var item in devs)
{
if (!_stoppingToken.IsCancellationRequested)
{
UploadDeviceCore deviceCollectCore = new(_scopeFactory);
deviceCollectCore.Init(item);
deviceCollectCore.StartThread();
UploadDeviceCores.Add(deviceCollectCore);
}
}
}
/// <returns></returns>
public async Task RestartDeviceThread()
{
try
@@ -103,6 +93,23 @@ public class UploadDeviceHostService : BackgroundService
}
}
/// <summary>
/// 启动设备线程
/// </summary>
public void StartDeviceThread()
{
var devs = (_uploadDeviceService.GetUploadDeviceRuntime());
foreach (var item in devs)
{
if (!_stoppingToken.IsCancellationRequested)
{
UploadDeviceCore deviceCollectCore = new(_scopeFactory);
deviceCollectCore.Init(item);
deviceCollectCore.StartThread();
UploadDeviceCores.Add(deviceCollectCore);
}
}
}
/// <summary>
/// 更新设备线程
/// </summary>
@@ -129,7 +136,11 @@ public class UploadDeviceHostService : BackgroundService
#endregion
#region
/// <summary>
/// 获取设备方法
/// </summary>
/// <param name="devId"></param>
/// <returns></returns>
public List<string> GetDeviceMethods(long devId)
{
var id = YitIdHelper.NextId();
@@ -151,7 +162,12 @@ public class UploadDeviceHostService : BackgroundService
}
/// <summary>
/// 获取设备属性
/// </summary>
/// <param name="driverId"></param>
/// <param name="devId"></param>
/// <returns></returns>
public List<DependencyProperty> GetDevicePropertys(long driverId, long devId = 0)
{
using var serviceScope = _scopeFactory.CreateScope();
@@ -182,7 +198,12 @@ public class UploadDeviceHostService : BackgroundService
}
}
/// <summary>
/// 获取变量上传属性
/// </summary>
/// <param name="driverId"></param>
/// <param name="dependencyProperties"></param>
/// <returns></returns>
public List<DependencyProperty> GetVariablePropertys(long driverId, List<DependencyProperty> dependencyProperties = null)
{
using var serviceScope = _scopeFactory.CreateScope();
@@ -216,12 +237,13 @@ public class UploadDeviceHostService : BackgroundService
#region worker服务
private CancellationToken _stoppingToken;
/// <inheritdoc/>
public override async Task StartAsync(CancellationToken cancellationToken)
{
await base.StartAsync(cancellationToken);
}
/// <inheritdoc/>
public override async Task StopAsync(CancellationToken cancellationToken)
{
var stoppingToken = new CancellationTokenSource();
@@ -231,6 +253,7 @@ public class UploadDeviceHostService : BackgroundService
RemoveDeviceThread();
await base.StopAsync(cancellationToken);
}
/// <inheritdoc/>
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
@@ -251,10 +274,10 @@ public class UploadDeviceHostService : BackgroundService
continue;
if (devcore.Device.DeviceStatus == DeviceStatusEnum.Pause)
continue;
if (devcore.isInitSuccess)
if (!devcore.isInitSuccess)
_logger?.LogWarning(devcore.Device.Name + "初始化失败,重启线程中");
else
_logger?.LogWarning(devcore.Device.Name + "上传线程假死,重启线程中");
_logger?.LogWarning(devcore.Device.Name + "上传线程假死,重启线程中");
UpDeviceThread(devcore.DeviceId, false);
i--;
num--;

View File

@@ -24,17 +24,25 @@ public class ValueHisHostService : BackgroundService, ISingleton
private GlobalCollectDeviceData _globalCollectDeviceData;
private ConcurrentQueue<ValueHis> CollectDeviceVariables { get; set; } = new();
private ConcurrentQueue<ValueHis> ChangeDeviceVariables { get; set; } = new();
/// <summary>
/// 服务状态
/// </summary>
public OperResult StatuString { get; set; } = new OperResult("初始化");
private static IServiceScopeFactory _scopeFactory;
/// <inheritdoc cref="ValueHisHostService"/>
public ValueHisHostService(ILogger<ValueHisHostService> logger, IServiceScopeFactory scopeFactory)
{
_scopeFactory = scopeFactory;
_logger = logger;
_globalCollectDeviceData = scopeFactory.CreateScope().ServiceProvider.GetService<GlobalCollectDeviceData>();
}
/// <summary>
/// 获取数据库链接
/// </summary>
/// <returns></returns>
public async Task<OperResult<SqlSugarClient>> HisConfig()
{
await Task.CompletedTask;
@@ -82,18 +90,20 @@ public class ValueHisHostService : BackgroundService, ISingleton
return OperResult.CreateSuccessResult(sqlSugarClient);
}
#region worker服务
/// <inheritdoc/>
public override async Task StartAsync(CancellationToken cancellationToken)
{
_logger?.LogInformation("历史服务启动");
await base.StartAsync(cancellationToken);
}
/// <inheritdoc/>
public override Task StopAsync(CancellationToken cancellationToken)
{
_logger?.LogInformation("历史服务停止");
return base.StopAsync(cancellationToken);
}
/// <inheritdoc/>
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
await Task.Delay(5000, stoppingToken);
@@ -276,6 +286,9 @@ public class ValueHisHostService : BackgroundService, ISingleton
}
/// <summary>
/// 重新启动服务
/// </summary>
public void Restart()
{
Stop(_globalCollectDeviceData.CollectDevices);
@@ -305,8 +318,13 @@ public class ValueHisHostService : BackgroundService, ISingleton
#endregion
}
/// <summary>
/// <see cref="ValueHis"/> Master规则
/// </summary>
public class ValueHisMapper : IRegister
{
/// <inheritdoc/>
public void Register(TypeAdapterConfig config)
{
config.ForType<CollectVariableRunTime, ValueHis>()

View File

@@ -15,11 +15,12 @@ public class TGLogJob : IJob
{
private readonly IServiceProvider _serviceProvider;
/// <inheritdoc cref="TGLogJob"/>
public TGLogJob(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
/// <inheritdoc/>
public async Task ExecuteAsync(JobExecutingContext context, CancellationToken stoppingToken)
{
var db = DbContext.Db.CopyNew();

View File

@@ -13,6 +13,7 @@ namespace ThingsGateway.Web.Foundation
{
private readonly SqlSugarScope _db;
/// <inheritdoc cref="TGRunTimeDatabaseLoggingWriter"/>
public TGRunTimeDatabaseLoggingWriter()
{
_db = DbContext.Db;
@@ -31,6 +32,7 @@ namespace ThingsGateway.Web.Foundation
await Task.Delay(3000);
}
}
/// <inheritdoc/>
public void Write(LogMessage logMsg, bool flush)
{
var customLevel = App.GetConfig<LogLevel?>("Logging:LogLevel:RunTimeLogCustom") ?? LogLevel.Trace;

View File

@@ -1,6 +1,9 @@

namespace ThingsGateway.Web.Foundation;
/// <summary>
/// 采集设备状态表示
/// </summary>
public class CollectDeviceRunTime : CollectDevice
{
/// <summary>
@@ -60,9 +63,15 @@ public class CollectDeviceRunTime : CollectDevice
}
}
private DeviceStatusEnum deviceStatus = DeviceStatusEnum.Default;
/// <summary>
/// 设备状态变化事件
/// </summary>
public event DelegateOnDeviceChanged DeviceStatusCahnge;
private string deviceOffMsg;
/// <summary>
/// 失败原因
/// </summary>
[Description("失败原因")]
public string DeviceOffMsg
{
@@ -79,5 +88,9 @@ public class CollectDeviceRunTime : CollectDevice
set => deviceOffMsg = value;
}
}
/// <summary>
/// 设备变化委托
/// </summary>
/// <param name="collectDeviceRunTime"></param>
public delegate void DelegateOnDeviceChanged(CollectDeviceRunTime collectDeviceRunTime);

View File

@@ -7,6 +7,9 @@ using TouchSocket.Core;
namespace ThingsGateway.Web.Foundation;
/// <summary>
/// 变量运行状态表示
/// </summary>
public class CollectVariableRunTime : CollectDeviceVariable
{
/// <summary>
@@ -15,6 +18,9 @@ public class CollectVariableRunTime : CollectDeviceVariable
[Description("设备名称")]
[OrderData(Order = 2)]
public string DeviceName { get; set; }
/// <summary>
/// 数据类型
/// </summary>
[Description("数据类型")]
public Type DataType
{
@@ -31,19 +37,32 @@ public class CollectVariableRunTime : CollectDeviceVariable
}
}
/// <summary>
/// 原始值
/// </summary>
[Description("原始值")]
[OrderData(Order = 3)]
public object RawValue { get; set; }
private object _value;
/// <summary>
/// 实时值
/// </summary>
[Description("实时值")]
[OrderData(Order = 3)]
public object Value { get => _value; private set => _value = value; }
/// <summary>
/// 上次值
/// </summary>
[Newtonsoft.Json.JsonIgnore]
[System.Text.Json.Serialization.JsonIgnore]
[AdaptIgnore]
public object LastSetValue;
/// <summary>
///
/// </summary>
/// <param name="value"></param>
/// <param name="dateTime"></param>
public void SetValue(object value,DateTime dateTime=default)
{
if (value != null)
@@ -89,24 +108,29 @@ public class CollectVariableRunTime : CollectDeviceVariable
time = dateTime;
}
CollectTime = DateTime.Now;
if (data?.ToString() != _value?.ToString() && LastSetValue != data)
if (data?.ToString() != _value?.ToString() && LastSetValue?.ToString() != data?.ToString())
{
ChangeTime = DateTime.Now;
if (Quality == 192)
{
_value = data;
}
LastSetValue = data;
VariableValueChange?.Invoke(this);
}
VariableCollectChange?.Invoke(this);
LastSetValue = data;
}
}
/// <summary>
/// 变化时间
/// </summary>
[Description("变化时间")]
[OrderData(Order = 4)]
public DateTime ChangeTime { get; set; }
/// <summary>
/// 采集时间
/// </summary>
[Description("采集时间")]
[OrderData(Order = 4)]
public DateTime CollectTime { get; set; }
@@ -127,7 +151,9 @@ public class CollectVariableRunTime : CollectDeviceVariable
[System.Text.Json.Serialization.JsonIgnore]
[AdaptIgnore]
public VariableCahngeEventHandler VariableValueChange { get; set; }
/// <summary>
/// 质量戳
/// </summary>
[Description("质量戳")]
[OrderData(Order = 5)]
public int Quality { get; private set; }
@@ -135,7 +161,7 @@ public class CollectVariableRunTime : CollectDeviceVariable
#region LoadSourceRead
/// <summary>
/// <see cref="DriverBase.ReadAsync(string, ushort)"/>返回字节组中的索引位置
/// <see cref="DriverBase.ReadAsync(string, int, System.Threading.CancellationToken)"/>返回字节组中的索引位置
/// 这个参数值由自动分包方法写入<see cref="DriverBase.LoadSourceRead(List{CollectVariableRunTime})"/>
/// </summary>
[Description("分包索引")]
@@ -170,7 +196,9 @@ public class CollectVariableRunTime : CollectDeviceVariable
#region
/// <summary>
/// 报警使能
/// </summary>
public bool AlarmEnable
{
get
@@ -178,19 +206,33 @@ public class CollectVariableRunTime : CollectDeviceVariable
return LAlarmEnable || LLAlarmEnable || HAlarmEnable || HHAlarmEnable || BoolOpenAlarmEnable || BoolCloseAlarmEnable;
}
}
/// <summary>
/// 报警时间
/// </summary>
public DateTime AlarmTime { get; set; }
/// <summary>
/// 事件时间
/// </summary>
public DateTime EventTime { get; set; }
/// <summary>
/// 报警类型
/// </summary>
public AlarmEnum AlarmTypeEnum { get; set; }
/// <summary>
/// 事件类型
/// </summary>
public EventEnum EventTypeEnum { get; set; }
/// <summary>
/// 报警值
/// </summary>
public string AlarmCode { get; set; }
[SugarColumn(ColumnName = "AlamLimit", ColumnDescription = "报警限值", IsNullable = false)]
/// <summary>
/// 报警限值
/// </summary>
public string AlarmLimit { get; set; }
/// <summary>
/// 报警文本
/// </summary>
public string AlarmText { get; set; }
#endregion
}

View File

@@ -1,14 +1,26 @@
public class DeviceData

using ThingsGateway.Web.Foundation;
/// <summary>
/// 设备上传DTO
/// </summary>
public class DeviceData
{
/// <inheritdoc cref="CollectDeviceRunTime.PluginName"/>
public string pluginName { get; set; }
/// <inheritdoc cref="CollectDeviceRunTime.DeviceVariablesNum"/>
public int deviceVariablesNum { get; set; }
/// <inheritdoc cref="CollectDeviceRunTime.ActiveTime"/>
public DateTime activeTime { get; set; }
/// <inheritdoc cref="CollectDeviceRunTime.DeviceStatus"/>
public int deviceStatus { get; set; }
/// <inheritdoc cref="CollectDeviceRunTime.DeviceOffMsg"/>
public string deviceOffMsg { get; set; }
/// <inheritdoc cref="UploadDevice.Name"/>
public string name { get; set; }
/// <inheritdoc cref="UploadDevice.Description"/>
public string description { get; set; }
/// <inheritdoc cref="UploadDevice.Enable"/>
public bool enable { get; set; }
public DateTime createTime { get; set; }
public DateTime updateTime { get; set; }
}

View File

@@ -1,6 +1,9 @@

namespace ThingsGateway.Web.Foundation;
/// <summary>
/// 上传设备运行状态
/// </summary>
public class UploadDeviceRunTime : UploadDevice
{
/// <summary>
@@ -36,6 +39,10 @@ public class UploadDeviceRunTime : UploadDevice
private DeviceStatusEnum deviceStatus = DeviceStatusEnum.Default;
private string deviceOffMsg;
/// <summary>
/// 失败原因
/// </summary>
[Description("失败原因")]
public string DeviceOffMsg
{

View File

@@ -1,21 +1,40 @@

using ThingsGateway.Web.Foundation;
/// <summary>
/// 上传DTO
/// </summary>
public class VariableData
{
/// <inheritdoc cref="MemoryVariable.Name"/>
public string name { get; set; }
public string deviceName { get; set; }
public string rawValue { get; set; }
public string value { get; set; }
public DateTime changeTime { get; set; }
public DateTime collectTime { get; set; }
public int quality { get; set; }
public string readExpressions { get; set; }
public string writeExpressions { get; set; }
public int intervalTime { get; set; }
public object otherMethod { get; set; }
public string variableAddress { get; set; }
/// <inheritdoc cref="MemoryVariable.Description"/>
public object description { get; set; }
public object initialValue { get; set; }
/// <inheritdoc cref="CollectVariableRunTime.DeviceName"/>
public string deviceName { get; set; }
/// <inheritdoc cref="CollectVariableRunTime.RawValue"/>
public string rawValue { get; set; }
/// <inheritdoc cref="CollectVariableRunTime.Value"/>
public string value { get; set; }
/// <inheritdoc cref="CollectVariableRunTime.ChangeTime"/>
public DateTime changeTime { get; set; }
/// <inheritdoc cref="CollectVariableRunTime.CollectTime"/>
public DateTime collectTime { get; set; }
/// <inheritdoc cref="CollectVariableRunTime.Quality"/>
public int quality { get; set; }
/// <inheritdoc cref="CollectDeviceVariable.ReadExpressions"/>
public string readExpressions { get; set; }
/// <inheritdoc cref="CollectDeviceVariable.WriteExpressions"/>
public string writeExpressions { get; set; }
/// <inheritdoc cref="CollectDeviceVariable.IntervalTime"/>
public int intervalTime { get; set; }
/// <inheritdoc cref="CollectDeviceVariable.OtherMethod"/>
public object otherMethod { get; set; }
/// <inheritdoc cref="CollectDeviceVariable.VariableAddress"/>
public string variableAddress { get; set; }
/// <inheritdoc cref="MemoryVariable.ProtectTypeEnum"/>
public int protectTypeEnum { get; set; }
/// <inheritdoc cref="MemoryVariable.DataTypeEnum"/>
public int dataTypeEnum { get; set; }
}

View File

@@ -8,6 +8,7 @@ namespace ThingsGateway.Web.Foundation
/// </summary>
public class DevConfigSeedData : ISqlSugarEntitySeedData<DriverPlugin>
{
/// <inheritdoc/>
public IEnumerable<DriverPlugin> SeedData()
{
return SeedDataUtil.GetSeedData<DriverPlugin>("driver_plugin.json");

View File

@@ -15,6 +15,7 @@ using ThingsGateway.Core;
namespace ThingsGateway.Web.Foundation
{
/// <inheritdoc cref="ICollectDeviceService"/>
[Injection(Proxy = typeof(OperDispatchProxy))]
public class CollectDeviceService : DbRepository<CollectDevice>, ICollectDeviceService
{
@@ -23,6 +24,7 @@ namespace ThingsGateway.Web.Foundation
private readonly IFileService _fileService;
private readonly IServiceScopeFactory _scopeFactory;
/// <inheritdoc cref="ICollectDeviceService"/>
public CollectDeviceService(SysCacheService sysCacheService
, IDriverPluginService driverPluginService, IFileService fileService,
IServiceScopeFactory scopeFactory)
@@ -115,6 +117,7 @@ namespace ThingsGateway.Web.Foundation
var data = GetCacheList();
return data.FirstOrDefault(it => it.Id == id)?.Name;
}
/// <inheritdoc/>
public List<DeviceTree> GetTree()
{
var data = GetCacheList();
@@ -122,6 +125,7 @@ namespace ThingsGateway.Web.Foundation
return trees;
}
/// <inheritdoc cref="GetTree()"/>
public static List<DeviceTree> GetTree(List<CollectDevice> data)
{
Dictionary<string, DeviceTree> trees = new();
@@ -208,6 +212,7 @@ namespace ThingsGateway.Web.Foundation
var data = GetCacheList();
return data.FirstOrDefault(it => it.Id == Id);
}
/// <inheritdoc/>
public List<CollectDevice> GetCacheList()
{
//先从Cache拿
@@ -265,6 +270,7 @@ namespace ThingsGateway.Web.Foundation
}
#region
/// <inheritdoc/>
[OperDesc("导出采集设备模板", IsRecordPar = false)]
public async Task<MemoryStream> Template()
{
@@ -275,6 +281,7 @@ namespace ThingsGateway.Web.Foundation
return result;
}
/// <inheritdoc/>
[OperDesc("导出采集设备表", IsRecordPar = false)]
public async Task<MemoryStream> ExportFile()
{

View File

@@ -10,36 +10,52 @@ using ThingsGateway.Web.Foundation;
namespace ThingsGateway.Application
{
/// <summary>
/// 采集设备添加DTO
/// </summary>
public class CollectDeviceAddInput : CollectDeviceEditInput
{
/// <inheritdoc/>
[Required(ErrorMessage = "不能为空")]
public override string Name { get; set; }
/// <inheritdoc/>
[MinValue(1, ErrorMessage = "插件不能为空")]
public override long PluginId { get; set; }
}
/// <summary>
/// 采集设备编辑DTO
/// </summary>
public class CollectDeviceEditInput : CollectDevice
{
/// <inheritdoc/>
[Required(ErrorMessage = "不能为空")]
public override string Name { get; set; }
/// <inheritdoc/>
[MinValue(1, ErrorMessage = "插件不能为空")]
public override long PluginId { get; set; }
}
/// <summary>
/// 采集设备分页查询DTO
/// </summary>
public class CollectDevicePageInput : BasePageInput
{
/// <inheritdoc/>
[Description("设备名称")]
public string Name { get; set; }
/// <inheritdoc/>
[Description("插件名称")]
public string PluginName { get; set; }
/// <inheritdoc/>
[Description("设备组")]
public string DeviceGroup { get; set; }
}
#region
/// <inheritdoc/>
public class CollectDeviceImport : UploadDeviceImport
{
@@ -47,7 +63,7 @@ namespace ThingsGateway.Application
}
/// <summary>
/// 采集设备
/// 采集设备导出DTO
/// </summary>
[ExcelExporter(Name = "采集设备", TableStyle = TableStyles.Light10, AutoFitAllColumn = true)]
public class CollectDeviceExport : UploadDeviceExport
@@ -57,9 +73,14 @@ namespace ThingsGateway.Application
}
/// <summary>
/// 采集设备导入DTO
/// </summary>
public class DevicePropertyImport : ImportPreviewInput
{
/// <summary>
/// 设备ID已忽略
/// </summary>
[ImporterHeader(IsIgnore = true)]
public virtual long DeviceId { get; set; }
@@ -93,6 +114,9 @@ namespace ThingsGateway.Application
[ExcelExporter(Name = "设备附加属性", TableStyle = TableStyles.Light10, AutoFitAllColumn = true)]
public class DevicePropertyExport
{
/// <summary>
/// 设备ID已忽略
/// </summary>
[ExporterHeader(IsIgnore = true)]
public virtual long DeviceId { get; set; }
/// <summary>
@@ -119,12 +143,19 @@ namespace ThingsGateway.Application
}
/// <summary>
/// 采集设备Excel导入表示类
/// </summary>
public class CollectDeviceWithPropertyImport
{
/// <summary>
/// 采集设备基本属性
/// </summary>
[ExcelImporter(SheetName = "采集设备")]
public CollectDeviceImport CollectDeviceExport { get; set; }
/// <summary>
/// 采集设备附加属性
/// </summary>
[ExcelImporter(SheetName = "设备附加属性")]
public DevicePropertyImport DevicePropertyExcel { get; set; }
}

View File

@@ -6,29 +6,117 @@ using ThingsGateway.Core;
namespace ThingsGateway.Web.Foundation
{
/// <summary>
/// 采集设备服务
/// </summary>
public interface ICollectDeviceService : ITransient
{
/// <summary>
/// 添加设备
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
Task Add(CollectDevice input);
/// <summary>
/// 复制设备
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
Task CopyDev(IEnumerable<CollectDevice> input);
/// <summary>
/// 复制设备与变量
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
Task CopyDevAndVar(IEnumerable<CollectDevice> input);
/// <summary>
/// 上传设备
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
Task Delete(List<BaseIdInput> input);
/// <summary>
/// 编辑设备
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
Task Edit(CollectDeviceEditInput input);
/// <summary>
/// 导出Excel
/// </summary>
/// <returns></returns>
Task<MemoryStream> ExportFile();
/// <summary>
/// 获取缓存
/// </summary>
/// <returns></returns>
List<CollectDevice> GetCacheList();
/// <summary>
/// 获取设备运行状态DTO
/// </summary>
/// <param name="devId"></param>
/// <returns></returns>
Task<List<CollectDeviceRunTime>> GetCollectDeviceRuntime(long devId = 0);
/// <summary>
/// 根据ID获取设备
/// </summary>
/// <param name="Id"></param>
/// <returns></returns>
CollectDevice GetDeviceById(long Id);
/// <summary>
/// 根据名称获取ID
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
long? GetIdByName(string name);
/// <summary>
/// 根据ID获取名称
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
string GetNameById(long id);
/// <summary>
/// 获取设备组/名称树
/// </summary>
/// <returns></returns>
List<DeviceTree> GetTree();
/// <summary>
/// 导入
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
Task Import(Dictionary<string, ImportPreviewOutputBase> input);
/// <summary>
/// 分页查询
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
Task<SqlSugarPagedList<CollectDevice>> Page(CollectDevicePageInput input);
/// <summary>
/// 导入验证
/// </summary>
/// <param name="file"></param>
/// <returns></returns>
Task<Dictionary<string, ImportPreviewOutputBase>> Preview(IBrowserFile file);
/// <summary>
/// 导出模板
/// </summary>
/// <returns></returns>
Task<MemoryStream> Template();
}
/// <summary>
/// 设备组/名称树
/// </summary>
public class DeviceTree
{
/// <summary>
/// 节点名称
/// </summary>
public string Name { get; set; }
/// <summary>
/// 子节点
/// </summary>
public List<DeviceTree> Childrens { get; set; } = new();
}
}

View File

@@ -1,5 +1,6 @@
namespace ThingsGateway.Web.Foundation
{
/// <inheritdoc/>
public class CPU
{
/// <summary>
@@ -98,6 +99,7 @@
[Description("CPU总占用率")]
public UInt64 PercentProcessorTime { get; set; }
/// <inheritdoc/>
public List<CpuCore> CpuCoreList { get; set; } = new List<CpuCore>();
}

View File

@@ -1,5 +1,6 @@
namespace ThingsGateway.Web.Foundation
{
/// <inheritdoc/>
public class CpuCore
{
/// <summary>

View File

@@ -47,6 +47,7 @@
[Description("名称")]
public string Name { get; set; } = string.Empty;
/// <inheritdoc/>
public List<Partition> PartitionList { get; set; } = new List<Partition>();
/// <summary>
/// 这个物理磁盘驱动器上的分区数量所识别出的操作系统。
@@ -133,7 +134,7 @@
/// </summary>
[Description("分区起始偏移量")]
public UInt64 StartingOffset { get; set; }
/// <inheritdoc/>
public List<Volume> VolumeList { get; set; } = new List<Volume>();
}

View File

@@ -1,5 +1,6 @@
namespace ThingsGateway.Web.Foundation
{
/// <inheritdoc/>
public class MemoryStatus
{
/// <summary>

View File

@@ -11,36 +11,74 @@ using UAParser;
namespace ThingsGateway.Web.Foundation
{
/// <inheritdoc/>
public class TGHardwareInfo
{
/// <inheritdoc/>
public MemoryStatus MemoryStatus { get; private set; } = new MemoryStatus();
/// <inheritdoc/>
public List<CPU> CpuList { get; private set; } = new List<CPU>();
/// <inheritdoc/>
public List<Drive> DriveList { get; private set; } = new List<Drive>();
/// <inheritdoc/>
public List<Volume> VolumeList => DriveList.SelectMany(a => a.PartitionList.SelectMany(b => b.VolumeList)).ToList();
/// <inheritdoc/>
public List<Memory> MemoryList { get; private set; } = new List<Memory>();
/// <inheritdoc/>
public List<NetworkAdapter> NetworkAdapterList { get; private set; } = new List<NetworkAdapter>();
}
/// <inheritdoc/>
public class TGAPPInfo
{
/// <summary>
/// 主机名称
/// </summary>
[Description("主机名称")]
public string HostName { get; set; }
/// <summary>
/// 操作系统
/// </summary>
[Description("操作系统")]
public string SystemOs { get; set; }
/// <summary>
/// 系统架构
/// </summary>
[Description("系统架构")]
public string OsArchitecture { get; set; }
/// <summary>
/// 外网地址
/// </summary>
[Description("外网地址")]
public string RemoteIp { get; set; }
/// <summary>
/// 本地地址
/// </summary>
[Description("本地地址")]
public string LocalIp { get; set; }
/// <summary>
/// NET框架
/// </summary>
[Description("NET框架")]
public string FrameworkDescription { get; set; }
/// <summary>
/// 主机环境
/// </summary>
[Description("主机环境")]
public string Environment { get; set; }
/// <summary>
/// Stage环境
/// </summary>
[Description("Stage环境")]
public string Stage { get; set; }
}
/// <summary>
/// 硬件信息获取
/// </summary>
public class HardwareInfoService : ISingleton
{
/// <summary>
/// 硬件信息获取
/// </summary>
public TGHardwareInfo HardwareInfo
{
get
@@ -49,6 +87,9 @@ namespace ThingsGateway.Web.Foundation
return data;
}
}
/// <summary>
/// 运行信息获取
/// </summary>
public TGAPPInfo APPInfo
{
get
@@ -68,6 +109,7 @@ namespace ThingsGateway.Web.Foundation
}
private readonly Hardware.Info.HardwareInfo hardwareInfo = new();
ILogger _logger;
/// <inheritdoc cref="HardwareInfoService"/>
public HardwareInfoService()
{
Scoped.Create((factory, scope) =>

View File

@@ -7,11 +7,13 @@ using ThingsGateway.Core;
namespace ThingsGateway.Web.Foundation
{
/// <inheritdoc cref="IDriverPluginService"/>
[Injection(Proxy = typeof(OperDispatchProxy))]
public partial class DriverPluginService : DbRepository<DriverPlugin>, IDriverPluginService
{
private readonly SysCacheService _sysCacheService;
/// <inheritdoc cref="IDriverPluginService"/>
public DriverPluginService(SysCacheService sysCacheService)
{
_sysCacheService = sysCacheService;
@@ -99,6 +101,7 @@ namespace ThingsGateway.Web.Foundation
});
return driverPluginCategories.ToList();
}
/// <inheritdoc/>
public List<DriverPlugin> GetCacheListAsync()
{
//先从Cache拿

View File

@@ -1,9 +1,21 @@
namespace ThingsGateway.Web.Foundation
{
/// <summary>
/// 插件分组
/// </summary>
public class DriverPluginCategory
{
/// <summary>
/// 插件ID
/// </summary>
public long Id { get; set; }
/// <summary>
/// 插件名称
/// </summary>
public string Name { get; set; }
/// <summary>
/// 插件子组
/// </summary>
public List<DriverPluginCategory> Children { get; set; }
}

View File

@@ -7,23 +7,33 @@ using ThingsGateway.Web.Foundation;
namespace ThingsGateway.Application
{
/// <summary>
/// 插件添加DTO
/// </summary>
public class DriverPluginAddInput
{
/// <summary>
/// 主程序集
/// </summary>
[Description("主程序集")]
[Required(ErrorMessage = "主程序集不能为空")]
public IBrowserFile MainFile { get; set; }
/// <summary>
/// 附属程序集
/// </summary>
[Description("附属程序集")]
public List<IBrowserFile> OtherFiles { get; set; } = new();
}
public class DriverPluginEditInput
{
public DriverEnum DriverTypeEnum { get; set; }
}
/// <summary>
/// 插件分页
/// </summary>
public class DriverPluginPageInput : BasePageInput
{
/// <summary>
/// 插件名称
/// </summary>
[Description("插件名称")]
public string Name { get; set; }
}

View File

@@ -2,14 +2,51 @@
namespace ThingsGateway.Web.Foundation
{
/// <summary>
/// 插件服务
/// </summary>
public interface IDriverPluginService : ITransient
{
/// <summary>
/// 添加/更新插件
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
Task Add(DriverPluginAddInput input);
/// <summary>
/// 获取缓存
/// </summary>
/// <returns></returns>
List<DriverPlugin> GetCacheListAsync();
/// <summary>
/// 根据ID获取插件信息
/// </summary>
/// <param name="Id"></param>
/// <returns></returns>
DriverPlugin GetDriverPluginById(long Id);
/// <summary>
/// 根据分类获取插件树
/// </summary>
/// <param name="driverTypeEnum"></param>
/// <returns></returns>
List<DriverPluginCategory> GetDriverPluginChildrenList(DriverEnum driverTypeEnum);
/// <summary>
/// 根据ID获取名称
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
long? GetIdByName(string name);
/// <summary>
/// 根据名称获取ID
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
string GetNameById(long id);
/// <summary>
/// 分页
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
Task<SqlSugarPagedList<DriverPlugin>> Page(DriverPluginPageInput input);
}
}

View File

@@ -10,36 +10,55 @@ using ThingsGateway.Web.Foundation;
namespace ThingsGateway.Application
{
/// <summary>
/// 上传设备添加DTO
/// </summary>
public class UploadDeviceAddInput : UploadDeviceEditInput
{
/// <inheritdoc/>
[Required(ErrorMessage = "不能为空")]
public override string Name { get; set; }
/// <inheritdoc/>
[MinValue(1, ErrorMessage = "插件不能为空")]
public override long PluginId { get; set; }
}
/// <summary>
/// 上传设备修改DTO
/// </summary>
public class UploadDeviceEditInput : UploadDevice
{
/// <inheritdoc/>
[Required(ErrorMessage = "不能为空")]
public override string Name { get; set; }
/// <inheritdoc/>
[MinValue(1, ErrorMessage = "插件不能为空")]
public override long PluginId { get; set; }
}
/// <summary>
/// 上传设备分页查询
/// </summary>
public class UploadDevicePageInput : BasePageInput
{
/// <inheritdoc/>
[Description("设备名称")]
public string Name { get; set; }
/// <inheritdoc/>
[Description("插件名称")]
public string PluginName { get; set; }
/// <inheritdoc/>
[Description("设备组")]
public string DeviceGroup { get; set; }
}
#region
/// <summary>
/// 上传设备导入DTO
/// </summary>
public class UploadDeviceImport : ImportPreviewInput
{
@@ -86,7 +105,7 @@ namespace ThingsGateway.Application
}
/// <summary>
/// 上传设备
/// 上传设备导出DTO
/// </summary>
[ExcelExporter(Name = "上传设备", TableStyle = TableStyles.Light10, AutoFitAllColumn = true)]
public class UploadDeviceExport
@@ -131,12 +150,19 @@ namespace ThingsGateway.Application
public virtual bool IsLogOut { get; set; }
}
/// <summary>
/// 上传设备导入表示类
/// </summary>
public class UploadDeviceWithPropertyImport
{
/// <summary>
/// 上传设备基本属性
/// </summary>
[ExcelImporter(SheetName = "上传设备")]
public UploadDeviceImport UploadDeviceExport { get; set; }
/// <summary>
/// 上传设备附加属性
/// </summary>
[ExcelImporter(SheetName = "设备附加属性")]
public DevicePropertyImport DevicePropertyExcel { get; set; }
}

View File

@@ -6,21 +6,91 @@ using ThingsGateway.Core;
namespace ThingsGateway.Web.Foundation
{
/// <summary>
/// 上传设备服务
/// </summary>
public interface IUploadDeviceService : ITransient
{
/// <summary>
/// 添加上传设备
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
Task Add(UploadDevice input);
/// <summary>
/// 复制设备
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
Task CopyDev(IEnumerable<UploadDevice> input);
/// <summary>
/// 删除设备
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
Task Delete(List<BaseIdInput> input);
/// <summary>
/// 编辑设备
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
Task Edit(UploadDeviceEditInput input);
/// <summary>
/// 导出
/// </summary>
/// <returns></returns>
Task<MemoryStream> ExportFile();
/// <summary>
/// 获取缓存
/// </summary>
/// <returns></returns>
List<UploadDevice> GetCacheList();
/// <summary>
/// 根据ID获取设备
/// </summary>
/// <param name="Id"></param>
/// <returns></returns>
UploadDevice GetDeviceById(long Id);
/// <summary>
/// 根据名称获取ID
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
long? GetIdByName(string name);
/// <summary>
/// 根据ID获取名称
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
string GetNameById(long id);
/// <summary>
/// 获取上传设备运行状态DTO
/// </summary>
/// <param name="devId"></param>
/// <returns></returns>
List<UploadDeviceRunTime> GetUploadDeviceRuntime(long devId = 0);
/// <summary>
/// 导入
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
Task Import(Dictionary<string, ImportPreviewOutputBase> input);
/// <summary>
/// 分页
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
Task<SqlSugarPagedList<UploadDevice>> Page(UploadDevicePageInput input);
/// <summary>
/// 导入验证
/// </summary>
/// <param name="file"></param>
/// <returns></returns>
Task<Dictionary<string, ImportPreviewOutputBase>> Preview(IBrowserFile file);
/// <summary>
/// 导出模板
/// </summary>
/// <returns></returns>
Task<MemoryStream> Template();
}
}

View File

@@ -13,6 +13,7 @@ using ThingsGateway.Core;
namespace ThingsGateway.Web.Foundation
{
/// <inheritdoc cref="IUploadDeviceService"/>
[Injection(Proxy = typeof(OperDispatchProxy))]
public class UploadDeviceService : DbRepository<UploadDevice>, IUploadDeviceService
{
@@ -21,6 +22,7 @@ namespace ThingsGateway.Web.Foundation
private readonly IFileService _fileService;
private readonly IServiceScopeFactory _scopeFactory;
/// <inheritdoc cref="IUploadDeviceService"/>
public UploadDeviceService(SysCacheService sysCacheService
, IDriverPluginService driverPluginService, IFileService fileService,
IServiceScopeFactory scopeFactory)
@@ -129,6 +131,7 @@ namespace ThingsGateway.Web.Foundation
var data = GetCacheList();
return data.FirstOrDefault(it => it.Id == Id);
}
/// <inheritdoc cref="IUploadDeviceService"/>
public List<UploadDevice> GetCacheList()
{
//先从Cache拿
@@ -182,6 +185,7 @@ namespace ThingsGateway.Web.Foundation
}
#region
/// <inheritdoc/>
[OperDesc("导出上传设备模板", IsRecordPar = false)]
public async Task<MemoryStream> Template()
{
@@ -192,6 +196,7 @@ namespace ThingsGateway.Web.Foundation
return result;
}
/// <inheritdoc/>
[OperDesc("导出上传设备表", IsRecordPar = false)]
public async Task<MemoryStream> ExportFile()
{

View File

@@ -6,34 +6,41 @@ using OfficeOpenXml.Table;
using System.ComponentModel.DataAnnotations;
using ThingsGateway.Core;
using ThingsGateway.Foundation;
using ThingsGateway.Web.Foundation;
namespace ThingsGateway.Application
{
/// <summary>
/// 添加变量DTO
/// </summary>
public class VariableAddInput : VariableEditInput
{
/// <inheritdoc/>
[MinValue(100, ErrorMessage = "低于最小值")]
public override int IntervalTime { get; set; } = 1000;
public override long DeviceId
{
get;
set;
}
/// <inheritdoc/>
public override long DeviceId { get; set; }
}
/// <summary>
/// 修改变量DTO
/// </summary>
public class VariableEditInput : CollectDeviceVariable, IValidatableObject
{
/// <inheritdoc/>
[Required(ErrorMessage = "不能为空")]
public override string Name { get; set; }
/// <inheritdoc/>
[MinValue(1, ErrorMessage = "不能为空")]
public override long DeviceId
{
get;
set;
}
public override long DeviceId { get; set; }
/// <inheritdoc/>
[MinValue(100, ErrorMessage = "低于最小值")]
public override int IntervalTime { get; set; }
/// <inheritdoc/>
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (string.IsNullOrEmpty(VariableAddress) && string.IsNullOrEmpty(OtherMethod))
@@ -46,10 +53,13 @@ namespace ThingsGateway.Application
/// </summary>
public class VariablePageInput : BasePageInput
{
/// <inheritdoc/>
[Description("变量名称")]
public string Name { get; set; }
/// <inheritdoc/>
[Description("设备名称")]
public string DeviceName { get; set; }
/// <inheritdoc/>
[Description("变量地址")]
public string VariableAddress { get; set; }
@@ -59,8 +69,12 @@ namespace ThingsGateway.Application
#region
/// <summary>
/// 变量导入DTO
/// </summary>
public class CollectDeviceVariableImport : ImportPreviewInput, IValidatableObject
{
/// <inheritdoc/>
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (string.IsNullOrEmpty(VariableAddress) && string.IsNullOrEmpty(OtherMethod))
@@ -123,12 +137,6 @@ namespace ThingsGateway.Application
public string Description { get; set; }
/// <summary>
/// 初始值
/// </summary>
[ImporterHeader(Name = "初始值")]
public string InitialValue { get; set; }
/// <summary>
/// 读写权限
/// </summary>
@@ -326,7 +334,9 @@ namespace ThingsGateway.Application
#endregion
}
/// <summary>
/// 变量导出DTO
/// </summary>
[ExcelExporter(Name = "变量", TableStyle = TableStyles.Light10, AutoFitAllColumn = true)]
public class CollectDeviceVariableExport
{
@@ -363,12 +373,6 @@ namespace ThingsGateway.Application
[ExporterHeader(DisplayName = "变量地址")]
public string VariableAddress { get; set; }
/// <summary>
/// 初始值
/// </summary>
[ExporterHeader(DisplayName = "初始值")]
public string InitialValue { get; set; }
/// <summary>
/// 读写权限
/// </summary>
@@ -585,16 +589,29 @@ namespace ThingsGateway.Application
public bool HisEnable { get; set; }
#endregion
}
/// <summary>
/// 变量上传属性导入DTO
/// </summary>
public class VariablePropertyImport : ImportPreviewInput
{
/// <summary>
/// 设备ID
/// </summary>
[ImporterHeader(IsIgnore = true)]
public virtual long DeviceId { get; set; }
/// <summary>
/// 变量ID
/// </summary>
[ImporterHeader(IsIgnore = true)]
public long VariableId { get; set; }
/// <summary>
/// 变量名称
/// </summary>
[ImporterHeader(Name = "变量名称")]
public string VariableName { get; set; }
/// <summary>
/// 设备名称
/// </summary>
[ImporterHeader(Name = "上传设备名称")]
public string DeviceName { get; set; }
@@ -617,13 +634,19 @@ namespace ThingsGateway.Application
}
/// <summary>
/// 变量上传属性
/// 变量上传属性导出DTO
/// </summary>
[ExcelExporter(Name = "变量上传属性", TableStyle = TableStyles.Light10, AutoFitAllColumn = true)]
public class VariablePropertyExport
{
/// <summary>
/// 设备ID
/// </summary>
[ExporterHeader(IsIgnore = true)]
public virtual long DeviceId { get; set; }
/// <summary>
/// 变量ID
/// </summary>
[ExporterHeader(IsIgnore = true)]
public long VariableId { get; set; }
/// <summary>
@@ -655,11 +678,19 @@ namespace ThingsGateway.Application
}
/// <summary>
/// 变量Excel导入表示类
/// </summary>
public class CollectDeviceVariableWithPropertyImport
{
/// <summary>
/// 变量基本属性
/// </summary>
[ExcelImporter(SheetName = "变量")]
public CollectDeviceVariableImport CollectDeviceVariableExport { get; set; }
/// <summary>
/// 变量上传属性
/// </summary>
[ExcelImporter(SheetName = "变量上传属性")]
public VariablePropertyImport DevicePropertyExcel { get; set; }

View File

@@ -6,23 +6,98 @@ using ThingsGateway.Core;
namespace ThingsGateway.Web.Foundation
{
/// <summary>
/// 变量数据服务
/// </summary>
public interface IVariableService : ITransient
{
/// <summary>
/// 数据库DB
/// </summary>
ISqlSugarClient Context { get; set; }
/// <summary>
/// 添加变量
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
Task Add(CollectDeviceVariable input);
/// <summary>
/// 清空变量
/// </summary>
/// <returns></returns>
Task Clear();
/// <summary>
/// 删除变量
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
Task Delete(List<BaseIdInput> input);
/// <summary>
/// 删除变量缓存
/// </summary>
/// <param name="ids"></param>
void DeleteVariableFromCache(List<long> ids = null);
void DeleteVariableFromCache(long userId);
/// <summary>
/// 删除变量缓存
/// </summary>
void DeleteVariableFromCache(long id);
/// <summary>
/// 编辑变量
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
Task Edit(CollectDeviceVariable input);
/// <summary>
/// 导出
/// </summary>
/// <returns></returns>
Task<MemoryStream> ExportFile();
/// <summary>
/// 导出
/// </summary>
/// <param name="collectDeviceVariables"></param>
/// <returns></returns>
Task<MemoryStream> ExportFile(List<CollectDeviceVariable> collectDeviceVariables);
/// <summary>
/// 获取变量运行状态DTO
/// </summary>
/// <param name="devId"></param>
/// <returns></returns>
Task<List<CollectVariableRunTime>> GetCollectDeviceVariableRuntime(long devId = 0);
/// <summary>
/// 根据名称获取ID
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
long GetIdByName(string name);
/// <summary>
/// 根据ID获取名称
/// </summary>
/// <param name="Id"></param>
/// <returns></returns>
string GetNameById(long Id);
/// <summary>
/// 导入
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
Task Import(Dictionary<string, ImportPreviewOutputBase> input);
/// <summary>
/// 分页查询
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
Task<SqlSugarPagedList<CollectDeviceVariable>> Page(VariablePageInput input);
/// <summary>
/// 导入验证
/// </summary>
/// <param name="file"></param>
/// <returns></returns>
Task<Dictionary<string, ImportPreviewOutputBase>> Preview(IBrowserFile file);
/// <summary>
/// 导出模板
/// </summary>
/// <returns></returns>
Task<MemoryStream> Template();
}
}

View File

@@ -17,6 +17,7 @@ using ThingsGateway.Core;
namespace ThingsGateway.Web.Foundation
{
/// <inheritdoc cref="IVariableService"/>
[Injection(Proxy = typeof(OperDispatchProxy))]
public class VariableService : DbRepository<CollectDeviceVariable>, IVariableService
{
@@ -25,6 +26,7 @@ namespace ThingsGateway.Web.Foundation
private readonly IUploadDeviceService _uploadDeviceService;
private readonly FileService _fileService;
/// <inheritdoc cref="IVariableService"/>
public VariableService(SysCacheService sysCacheService,
ICollectDeviceService collectDeviceService, FileService fileService,
IUploadDeviceService uploadDeviceService
@@ -198,6 +200,7 @@ namespace ThingsGateway.Web.Foundation
#region
/// <inheritdoc/>
[OperDesc("导出变量模板", IsRecordPar = false)]
public async Task<MemoryStream> Template()
{
@@ -209,6 +212,7 @@ namespace ThingsGateway.Web.Foundation
}
/// <inheritdoc/>
[OperDesc("导出变量表", IsRecordPar = false)]
public async Task<MemoryStream> ExportFile()
{
@@ -266,6 +270,7 @@ namespace ThingsGateway.Web.Foundation
}
/// <inheritdoc/>
[OperDesc("导出变量表", IsRecordPar = false)]
public async Task<MemoryStream> ExportFile(List<CollectDeviceVariable> collectDeviceVariables)
{

View File

@@ -2,7 +2,9 @@
namespace ThingsGateway.Web.Foundation
{
/// <summary>
/// 运行日志分页DTO
/// </summary>
public class RuntimeLogPageInput : BasePageInput
{
/// <summary>
@@ -17,6 +19,9 @@ namespace ThingsGateway.Web.Foundation
public string Level { get; set; }
}
/// <summary>
/// RPC日志分页DTO
/// </summary>
public class RpcLogPageInput : BasePageInput
{
/// <summary>

View File

@@ -2,9 +2,21 @@
namespace ThingsGateway.Web.Foundation
{
/// <summary>
/// RPC日志服务
/// </summary>
public interface IRpcLogService : ITransient
{
/// <summary>
/// 删除
/// </summary>
/// <returns></returns>
Task Delete();
/// <summary>
/// 分页查询
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
Task<SqlSugarPagedList<RpcLog>> Page(RpcLogPageInput input);
}
}

View File

@@ -2,9 +2,21 @@
namespace ThingsGateway.Web.Foundation
{
/// <summary>
/// 运行日志服务
/// </summary>
public interface IRuntimeLogService : ITransient
{
/// <summary>
/// 删除
/// </summary>
/// <returns></returns>
Task Delete();
/// <summary>
/// 分页查询
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
Task<SqlSugarPagedList<RuntimeLog>> Page(RuntimeLogPageInput input);
}
}

View File

@@ -2,6 +2,7 @@
namespace ThingsGateway.Web.Foundation
{
/// <inheritdoc cref="IRpcLogService"/>
[Injection(Proxy = typeof(OperDispatchProxy))]
public class RpcLogService : DbRepository<RpcLog>, IRpcLogService
{

View File

@@ -2,6 +2,7 @@
namespace ThingsGateway.Web.Foundation
{
/// <inheritdoc cref="IRuntimeLogService"/>
[Injection(Proxy = typeof(OperDispatchProxy))]
public class RuntimeLogService : DbRepository<RuntimeLog>, IRuntimeLogService
{

View File

@@ -6,6 +6,7 @@
[AppStartup(99)]
public class Startup : AppStartup
{
/// <inheritdoc/>
public void ConfigureServices(IServiceCollection services)
{

View File

@@ -5,7 +5,6 @@
<TargetFrameworks>net6.0;net7.0</TargetFrameworks>
<Version>1.1.0</Version>
<GenerateDocumentationFile>True</GenerateDocumentationFile>
<NoWarn>CS1591;</NoWarn>
</PropertyGroup>

View File

@@ -214,11 +214,6 @@
</MCol>
<MCol Md=6 Cols=12 class="px-1">
<MSubheader Class="font-weight-black"> @(context.Description(x => x.InitialValue)) </MSubheader>
<MTextField Dense Outlined HideDetails="@("auto")" @bind-Value=@context.InitialValue />
</MCol>
<MCol Md=6 Cols=12 class="px-1">
<MSubheader Class="font-weight-black"> @(context.Description(x => x.ProtectTypeEnum)) </MSubheader>

View File

@@ -528,12 +528,12 @@
async Task ImportVaiable(long devId)
{
var driver = this.GetBackgroundService<CollectDeviceHostService>().GetImportUI(devId);
if (driver?.DriverUI == null)
if (driver?.DriverImportUI == null)
{
await PopupService.EnqueueSnackbarAsync("插件未实现导入变量", AlertTypes.Warning);
return;
}
_importComponent = new BootstrapDynamicComponent(driver.DriverUI, new Dictionary<string, object>()
_importComponent = new BootstrapDynamicComponent(driver.DriverImportUI, new Dictionary<string, object>()
{
[nameof(DriverUI.Driver)] = driver
}

View File

@@ -11,7 +11,7 @@
@inject UserResoures UserResoures
@layout MainLayout
<AppDataTable @ref="_datatable" TItem="DriverPlugin" SearchItem="DriverPluginPageInput"
AddItem="DriverPluginAddInput" EditItem="DriverPluginEditInput"
AddItem="DriverPluginAddInput" EditItem="object"
SearchModel="@search" IsMenuOperTemplate=false
QueryCall="QueryCall" AddCall="AddCall"
FilterHeaders="FilterHeaders" Filters="Filters" ShowDetailButton

View File

@@ -4,6 +4,7 @@
<TargetFrameworks>net6.0;net7.0</TargetFrameworks>
<Version>1.1.0</Version>
<NoWarn>BL0005;CS0168;</NoWarn>
<!--<GenerateDocumentationFile>True</GenerateDocumentationFile>-->
</PropertyGroup>
<ItemGroup>

View File

@@ -5,17 +5,26 @@ using Masa.Blazor;
namespace ThingsGateway.Web.Rcl.Core
{
/// <summary>
/// 标签页表示
/// </summary>
/// <param name="Title">标题</param>
/// <param name="Href">Path</param>
/// <param name="Icon">图标</param>
public record PageTabItem(string Title, string Href, string Icon);
/// <summary>
/// 用户菜单等资源管理
/// 自定义资源管理
/// </summary>
public class UserResoures : IDisposable
{
/// <summary>
/// 主题
/// </summary>
public APPThemes Themes = new();
private IAuthService _authService;
private CookieStorage _cookieStorage;
private IUserCenterService _userCenterService;
/// <inheritdoc cref="UserResoures"/>>
public UserResoures(CookieStorage cookieStorage, MasaBlazor masaBlazor, IHttpContextAccessor httpContextAccessor,
IAuthService authService,
ResourceService resourceService,
@@ -28,31 +37,64 @@ namespace ThingsGateway.Web.Rcl.Core
_resourceService = resourceService;
if (httpContextAccessor.HttpContext is not null) InitCookie(httpContextAccessor.HttpContext.Request.Cookies);
}
/// <summary>
/// 当前用户
/// </summary>
public SysUser CurrentUser { get; set; }
/// <summary>
/// 是否黑暗主题
/// </summary>
public bool IsDark => _masaBlazor.Theme.Dark;
/// <summary>
/// 全部个人菜单
/// </summary>
public List<SysResource> Menus { get; set; }
/// <summary>
/// 全部快捷方式
/// </summary>
public List<SysResource> WorkbenchOutputs { get; set; }
/// <summary>
/// 相同等级的个人菜单
/// </summary>
public List<SysResource> SameLevelMenus { get; private set; } = new();
/// <summary>
/// 全部相同等级的菜单
/// </summary>
public List<SysResource> AllSameLevelMenus { get; private set; } = new();
/// <summary>
/// 全部标签页
/// </summary>
public List<PageTabItem> PageTabItems = new();
private MasaBlazor _masaBlazor { get; set; }
private IResourceService _resourceService { get; set; }
/// <inheritdoc/>
public void Dispose()
{
GC.SuppressFinalize(this);
}
/// <summary>
/// 初始化全部内容
/// </summary>
/// <returns></returns>
public async Task InitAllAsync()
{
await InitUserAsync();
await InitMenuAsync();
}
/// <summary>
/// 初始化用户信息
/// </summary>
/// <returns></returns>
public async Task InitUserAsync()
{
CurrentUser = await _authService.GetLoginUser();
}
/// <summary>
/// 初始化菜单信息
/// </summary>
/// <returns></returns>
public async Task InitMenuAsync()
{
var ids = await _userCenterService.GetLoginWorkbench();
@@ -63,18 +105,29 @@ namespace ThingsGateway.Web.Rcl.Core
SameLevelMenus = Menus.TreeToList();
PageTabItems = AllSameLevelMenus.SameLevelMenuPasePageTab();
}
/// <summary>
/// 按钮授权检查
/// </summary>
/// <param name="code"></param>
/// <returns></returns>
public bool IsHasButtonWithRole(string code)
{
if (UserManager.SuperAdmin) return true;
return CurrentUser.ButtonCodeList.Contains(code.ToLower());
}
/// <summary>
/// 页面授权检查
/// </summary>
/// <param name="code"></param>
/// <returns></returns>
public bool IsHasPageWithRole(string code)
{
if (UserManager.SuperAdmin) return true;
return AllSameLevelMenus.Select(a => a.Component).Contains(code.ToLower());
}
//设置深浅主题统一由这个方法为入口
/// <summary>
/// 设置深浅主题统一由这个方法为入口
/// </summary>
public void SetDarkOrLightTheme()
{
Themes.IsDark = !Themes.IsDark;

View File

@@ -4,6 +4,7 @@
<TargetFrameworks>net6.0;net7.0</TargetFrameworks>
<Version>1.1.0</Version>
<NoWarn>BL0005;CS0168;</NoWarn>
<!--<GenerateDocumentationFile>True</GenerateDocumentationFile>-->
</PropertyGroup>
<ItemGroup>

View File

@@ -4,11 +4,20 @@ using System.Linq;
namespace ThingsGateway.Web.Rcl
{
/// <summary>
/// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>־ҳ<D6BE><D2B3>
/// </summary>
public partial class Vislog
{
private IAppDataTable _datatable;
private VisitLogPageInput search = new();
/// <summary>
/// <20><>־<EFBFBD><D6BE><EFBFBD><EFBFBD><EFBFBD>˵<EFBFBD>
/// </summary>
public List<StringFilters> CategoryFilters { get; set; } = new();
/// <summary>
/// ִ<>н<EFBFBD><D0BD><EFBFBD><EFBFBD>˵<EFBFBD>
/// </summary>
public List<StringFilters> ExeStatus { get; set; } = new();
private void FilterHeaders(List<DataTableHeader<DevLogVisit>> datas)
@@ -65,7 +74,7 @@ namespace ThingsGateway.Web.Rcl
}
}
}
/// <inheritdoc/>
protected override void OnInitialized()
{
CategoryFilters.Add(new StringFilters() { Key = T("<22><>¼"), Value = CateGoryConst.Log_LOGIN });

Some files were not shown because too many files have changed in this diff Show More