Compare commits
21 Commits
10.10.1.0
...
10.10.15.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6660ce3e34 | ||
|
|
7499162c1a | ||
|
|
40208a5cd6 | ||
|
|
fa347f4f68 | ||
|
|
d7df6fc605 | ||
|
|
eb4bb2fd48 | ||
|
|
faa9858974 | ||
|
|
1b3d2dda49 | ||
|
|
a8a9453611 | ||
|
|
e84f42ce14 | ||
|
|
6f814cf6b8 | ||
|
|
e36432e4e9 | ||
|
|
ebd71e807b | ||
|
|
34000d8d7d | ||
|
|
e785f6660c | ||
|
|
831c611797 | ||
|
|
453817ef86 | ||
|
|
8ce0b981c1 | ||
|
|
4e5c51b54c | ||
|
|
3cc9d31f28 | ||
|
|
10391f869b |
@@ -38,6 +38,7 @@ public class VerificatInfo : PrimaryIdEntity
|
|||||||
[AutoGenerateColumn(Filterable = true, Sortable = true)]
|
[AutoGenerateColumn(Filterable = true, Sortable = true)]
|
||||||
[SugarColumn(ColumnDescription = "Id", IsPrimaryKey = true)]
|
[SugarColumn(ColumnDescription = "Id", IsPrimaryKey = true)]
|
||||||
[IgnoreExcel]
|
[IgnoreExcel]
|
||||||
|
[System.ComponentModel.DataAnnotations.Key]
|
||||||
public override long Id { get; set; }
|
public override long Id { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
// QQ群:605534569
|
// QQ群:605534569
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
namespace ThingsGateway.Gateway.Application;
|
namespace ThingsGateway.Admin.Application;
|
||||||
|
|
||||||
public class USheetDatas
|
public class USheetDatas
|
||||||
{
|
{
|
||||||
@@ -18,6 +18,7 @@ public class SessionOutput : PrimaryIdEntity
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 主键Id
|
/// 主键Id
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[System.ComponentModel.DataAnnotations.Key]
|
||||||
public override long Id { get; set; }
|
public override long Id { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -0,0 +1,57 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://thingsgateway.cn/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
namespace ThingsGateway.Admin.Application;
|
||||||
|
|
||||||
|
public static class USheetDataHelpers
|
||||||
|
{
|
||||||
|
public static USheetDatas GetUSheetDatas(Dictionary<string, object> data)
|
||||||
|
{
|
||||||
|
var uSheetDatas = new USheetDatas();
|
||||||
|
|
||||||
|
foreach (var a in data)
|
||||||
|
{
|
||||||
|
var value = (a.Value as IEnumerable<Dictionary<string, object>>).ToList();
|
||||||
|
|
||||||
|
var uSheetData = new USheetData();
|
||||||
|
uSheetData.id = a.Key;
|
||||||
|
uSheetData.name = a.Key;
|
||||||
|
|
||||||
|
for (int row1 = 0; row1 < value.Count; row1++)
|
||||||
|
{
|
||||||
|
if (row1 == 0)
|
||||||
|
{
|
||||||
|
Dictionary<int, USheetCelldata> usheetColldata = new();
|
||||||
|
int col = 0;
|
||||||
|
foreach (var colData in value[row1])
|
||||||
|
{
|
||||||
|
usheetColldata.Add(col, new USheetCelldata() { v = colData.Key });
|
||||||
|
col++;
|
||||||
|
}
|
||||||
|
uSheetData.cellData.Add(row1, usheetColldata);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
Dictionary<int, USheetCelldata> usheetColldata = new();
|
||||||
|
int col = 0;
|
||||||
|
foreach (var colData in value[row1])
|
||||||
|
{
|
||||||
|
usheetColldata.Add(col, new USheetCelldata() { v = colData.Value });
|
||||||
|
col++;
|
||||||
|
}
|
||||||
|
uSheetData.cellData.Add(row1 + 1, usheetColldata);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
uSheetData.rowCount = uSheetData.cellData.Count + 100;
|
||||||
|
uSheetData.columnCount = uSheetData.cellData.FirstOrDefault().Value?.Count ?? 0;
|
||||||
|
uSheetDatas.sheets.Add(a.Key, uSheetData);
|
||||||
|
}
|
||||||
|
return uSheetDatas;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
@namespace ThingsGateway.Gateway.Razor
|
@namespace ThingsGateway.Admin.Razor
|
||||||
@using ThingsGateway.Admin.Application
|
@using ThingsGateway.Admin.Application
|
||||||
@using ThingsGateway.Admin.Razor
|
@using ThingsGateway.Admin.Razor
|
||||||
@using ThingsGateway.Gateway.Application
|
|
||||||
|
|
||||||
<div class="h-600px">
|
<div class="h-600px">
|
||||||
<UniverSheet @ref="_sheetExcel" OnReadyAsync="OnReadyAsync"></UniverSheet>
|
<UniverSheet @ref="_sheetExcel" OnReadyAsync="OnReadyAsync"></UniverSheet>
|
||||||
@@ -8,9 +8,10 @@
|
|||||||
// QQ群:605534569
|
// QQ群:605534569
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
using ThingsGateway.Admin.Application;
|
||||||
using ThingsGateway.NewLife.Json.Extension;
|
using ThingsGateway.NewLife.Json.Extension;
|
||||||
|
|
||||||
namespace ThingsGateway.Gateway.Razor;
|
namespace ThingsGateway.Admin.Razor;
|
||||||
|
|
||||||
public partial class USheet
|
public partial class USheet
|
||||||
{
|
{
|
||||||
@@ -6,6 +6,7 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\ThingsGateway.Admin.Application\ThingsGateway.Admin.Application.csproj" />
|
<ProjectReference Include="..\ThingsGateway.Admin.Application\ThingsGateway.Admin.Application.csproj" />
|
||||||
<PackageReference Include="BootstrapBlazor.Chart" Version="9.0.0" />
|
<PackageReference Include="BootstrapBlazor.Chart" Version="9.0.0" />
|
||||||
|
<PackageReference Include="BootstrapBlazor.UniverSheet" Version="9.0.5" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup Condition="'$(TargetFramework)'=='net8.0'">
|
<ItemGroup Condition="'$(TargetFramework)'=='net8.0'">
|
||||||
|
|||||||
@@ -39,7 +39,7 @@
|
|||||||
<BlazorReconnector @rendermode="new InteractiveServerRenderMode(false)" />
|
<BlazorReconnector @rendermode="new InteractiveServerRenderMode(false)" />
|
||||||
|
|
||||||
<script src=@($"_content/BootstrapBlazor/js/bootstrap.blazor.bundle.min.js?v={this.GetType().Assembly.GetName().Version}")></script>
|
<script src=@($"_content/BootstrapBlazor/js/bootstrap.blazor.bundle.min.js?v={this.GetType().Assembly.GetName().Version}")></script>
|
||||||
<script src=@($"{WebsiteConst.DefaultResourceUrl}js/culture.js?v={this.GetType().Assembly.GetName().Version}")></script>
|
<script src=@($"{WebsiteConst.DefaultResourceUrl}js/localStorageUtil.js?v={this.GetType().Assembly.GetName().Version}")></script>
|
||||||
<script src="_framework/blazor.web.js"></script>
|
<script src="_framework/blazor.web.js"></script>
|
||||||
<!-- PWA Service Worker -->
|
<!-- PWA Service Worker -->
|
||||||
<script type="text/javascript">'serviceWorker' in navigator && navigator.serviceWorker.register('./service-worker.js')</script>
|
<script type="text/javascript">'serviceWorker' in navigator && navigator.serviceWorker.register('./service-worker.js')</script>
|
||||||
|
|||||||
@@ -45,7 +45,7 @@
|
|||||||
</app>
|
</app>
|
||||||
|
|
||||||
<script src="_content/BootstrapBlazor/js/bootstrap.blazor.bundle.min.js"></script>
|
<script src="_content/BootstrapBlazor/js/bootstrap.blazor.bundle.min.js"></script>
|
||||||
<script src=@($"{WebsiteConst.DefaultResourceUrl}js/culture.js")></script>
|
<script src=@($"{WebsiteConst.DefaultResourceUrl}js/localStorageUtil.js")></script>
|
||||||
<script src="_framework/blazor.server.js"></script>
|
<script src="_framework/blazor.server.js"></script>
|
||||||
|
|
||||||
<!-- PWA Service Worker -->
|
<!-- PWA Service Worker -->
|
||||||
|
|||||||
@@ -45,11 +45,11 @@ public class Startup : AppStartup
|
|||||||
options.ServicesStopConcurrently = true;
|
options.ServicesStopConcurrently = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
//// 事件总线
|
// 事件总线
|
||||||
//services.AddEventBus(options =>
|
services.AddEventBus(options =>
|
||||||
//{
|
{
|
||||||
|
|
||||||
//});
|
});
|
||||||
|
|
||||||
// 任务调度
|
// 任务调度
|
||||||
services.AddSchedule(options => options.AddPersistence<JobPersistence>());
|
services.AddSchedule(options => options.AddPersistence<JobPersistence>());
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
// QQ群:605534569
|
// QQ群:605534569
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
namespace ThingsGateway.Gateway.Application;
|
namespace ThingsGateway.Common;
|
||||||
|
|
||||||
public class SmartTriggerScheduler
|
public class SmartTriggerScheduler
|
||||||
{
|
{
|
||||||
@@ -8,7 +8,7 @@
|
|||||||
// QQ群:605534569
|
// QQ群:605534569
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
namespace ThingsGateway.Gateway.Application;
|
namespace ThingsGateway.Common;
|
||||||
|
|
||||||
public sealed class StringOrdinalIgnoreCaseEqualityComparer : EqualityComparer<string>
|
public sealed class StringOrdinalIgnoreCaseEqualityComparer : EqualityComparer<string>
|
||||||
{
|
{
|
||||||
@@ -27,11 +27,11 @@ public class WebsiteOptions : IConfigurableOptions
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public bool Demo { get; set; }
|
public bool Demo { get; set; }
|
||||||
|
|
||||||
public bool WebPageEnable { get; set; } = true;
|
|
||||||
|
|
||||||
public int MaxBlazorConnections { get; set; } = 5;
|
public int MaxBlazorConnections { get; set; } = 5;
|
||||||
public bool BlazorConnectionLimitEnable { get; set; } = false;
|
public bool BlazorConnectionLimitEnable { get; set; } = false;
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 是否显示关于页面
|
/// 是否显示关于页面
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="BootstrapBlazor.TableExport" Version="9.2.6" />
|
<PackageReference Include="BootstrapBlazor.TableExport" Version="9.2.6" />
|
||||||
<PackageReference Include="Yitter.IdGenerator" Version="1.0.14" />
|
<PackageReference Include="Yitter.IdGenerator" Version="1.0.14" />
|
||||||
<PackageReference Include="BootstrapBlazor" Version="9.9.0" />
|
<PackageReference Include="BootstrapBlazor" Version="9.9.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ public abstract class PrimaryIdEntity : IPrimaryIdEntity
|
|||||||
[SugarColumn(ColumnDescription = "Id", IsPrimaryKey = true)]
|
[SugarColumn(ColumnDescription = "Id", IsPrimaryKey = true)]
|
||||||
[IgnoreExcel]
|
[IgnoreExcel]
|
||||||
[AutoGenerateColumn(Visible = false, IsVisibleWhenEdit = false, IsVisibleWhenAdd = false, Sortable = true, DefaultSort = true, DefaultSortOrder = SortOrder.Asc)]
|
[AutoGenerateColumn(Visible = false, IsVisibleWhenEdit = false, IsVisibleWhenAdd = false, Sortable = true, DefaultSort = true, DefaultSortOrder = SortOrder.Asc)]
|
||||||
|
[System.ComponentModel.DataAnnotations.Key]
|
||||||
public virtual long Id { get; set; }
|
public virtual long Id { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -123,6 +123,15 @@ public static class QueryPageOptionsExtensions
|
|||||||
};
|
};
|
||||||
var items = datas.GetData(option, out var totalCount, where);
|
var items = datas.GetData(option, out var totalCount, where);
|
||||||
ret.TotalCount = totalCount;
|
ret.TotalCount = totalCount;
|
||||||
|
|
||||||
|
if (totalCount > 0)
|
||||||
|
{
|
||||||
|
if (!items.Any() && option.PageIndex != 1)
|
||||||
|
{
|
||||||
|
option.PageIndex = 1;
|
||||||
|
items = datas.GetData(option, out totalCount, where);
|
||||||
|
}
|
||||||
|
}
|
||||||
ret.Items = items.ToList();
|
ret.Items = items.ToList();
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ public class BaseService<T> : IDataService<T>, IDisposable where T : class, new(
|
|||||||
public async Task<bool> DeleteAsync(IEnumerable<T> models)
|
public async Task<bool> DeleteAsync(IEnumerable<T> models)
|
||||||
{
|
{
|
||||||
using var db = GetDB();
|
using var db = GetDB();
|
||||||
return await db.Deleteable<T>().In(models.ToList()).ExecuteCommandHasChangeAsync().ConfigureAwait(false);
|
return await db.Deleteable<T>(models.ToList()).ExecuteCommandHasChangeAsync().ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
@@ -140,18 +140,22 @@ public class BaseService<T> : IDataService<T>, IDisposable where T : class, new(
|
|||||||
return (await db.UpdateableT(model).ExecuteCommandAsync().ConfigureAwait(false)) > 0;
|
return (await db.UpdateableT(model).ExecuteCommandAsync().ConfigureAwait(false)) > 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public virtual async Task<bool> SaveAsync(List<T> model, ItemChangedType changedType)
|
public virtual async Task<bool> SaveAsync(List<T> model, ItemChangedType changedType)
|
||||||
|
{
|
||||||
|
return (await SaveReturnCountAsync(model, changedType).ConfigureAwait(false)) > 0;
|
||||||
|
}
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public async Task<int> SaveReturnCountAsync(List<T> model, ItemChangedType changedType)
|
||||||
{
|
{
|
||||||
using var db = GetDB();
|
using var db = GetDB();
|
||||||
if (changedType == ItemChangedType.Add)
|
if (changedType == ItemChangedType.Add)
|
||||||
{
|
{
|
||||||
return (await db.Insertable(model).ExecuteCommandAsync().ConfigureAwait(false)) > 0;
|
return (await db.Insertable(model).ExecuteCommandAsync().ConfigureAwait(false));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return (await db.Updateable(model).ExecuteCommandAsync().ConfigureAwait(false)) > 0;
|
return (await db.Updateable(model).ExecuteCommandAsync().ConfigureAwait(false));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -27,18 +27,27 @@ using System.Security.Claims;
|
|||||||
using ThingsGateway.ConfigurableOptions;
|
using ThingsGateway.ConfigurableOptions;
|
||||||
using ThingsGateway.NewLife.Caching;
|
using ThingsGateway.NewLife.Caching;
|
||||||
using ThingsGateway.NewLife.Collections;
|
using ThingsGateway.NewLife.Collections;
|
||||||
|
using ThingsGateway.NewLife.Extension;
|
||||||
using ThingsGateway.NewLife.Log;
|
using ThingsGateway.NewLife.Log;
|
||||||
using ThingsGateway.Reflection;
|
using ThingsGateway.Reflection;
|
||||||
using ThingsGateway.Templates;
|
using ThingsGateway.Templates;
|
||||||
|
|
||||||
namespace ThingsGateway;
|
namespace ThingsGateway;
|
||||||
|
|
||||||
|
|
||||||
|
public static class WebEnableVariable
|
||||||
|
{
|
||||||
|
public static bool WebEnable => Environment.GetEnvironmentVariable(nameof(WebEnable)).ToBoolean(true);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 全局应用类
|
/// 全局应用类
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[SuppressSniffer]
|
[SuppressSniffer]
|
||||||
public static class App
|
public static class App
|
||||||
{
|
{
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 私有设置,避免重复解析
|
/// 私有设置,避免重复解析
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -157,7 +166,7 @@ public static class App
|
|||||||
var httpContextAccessor = RootServices?.GetService<IHttpContextAccessor>();
|
var httpContextAccessor = RootServices?.GetService<IHttpContextAccessor>();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return httpContextAccessor.HttpContext;
|
return httpContextAccessor?.HttpContext;
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -213,12 +213,18 @@ public static class AppServiceCollectionExtensions
|
|||||||
// 缓存
|
// 缓存
|
||||||
if (cacheOptions.CacheType == CacheType.Memory)
|
if (cacheOptions.CacheType == CacheType.Memory)
|
||||||
{
|
{
|
||||||
services.AddSingleton<ICache, MemoryCache>(a => new()
|
services.AddSingleton<ICache>(a =>
|
||||||
{
|
{
|
||||||
Capacity = cacheOptions.MemoryCacheOptions.Capacity,
|
Cache.Default = new MemoryCache()
|
||||||
Expire = cacheOptions.MemoryCacheOptions.Expire,
|
{
|
||||||
Period = cacheOptions.MemoryCacheOptions.Period
|
Capacity = cacheOptions.MemoryCacheOptions.Capacity,
|
||||||
});
|
Expire = cacheOptions.MemoryCacheOptions.Expire,
|
||||||
|
Period = cacheOptions.MemoryCacheOptions.Period
|
||||||
|
};
|
||||||
|
return Cache.Default;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
}
|
}
|
||||||
else if (cacheOptions.CacheType == CacheType.Redis)
|
else if (cacheOptions.CacheType == CacheType.Redis)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -85,11 +85,14 @@ internal static class InternalApp
|
|||||||
// 存储根服务(解决 Web 主机还未启动时在 HostedService 中使用 App.GetService 问题
|
// 存储根服务(解决 Web 主机还未启动时在 HostedService 中使用 App.GetService 问题
|
||||||
services.AddHostedService<GenericHostLifetimeEventsHostedService>();
|
services.AddHostedService<GenericHostLifetimeEventsHostedService>();
|
||||||
|
|
||||||
// 注册 Startup 过滤器
|
if (WebEnableVariable.WebEnable == true)
|
||||||
services.AddTransient<IStartupFilter, StartupFilter>();
|
{
|
||||||
|
// 注册 Startup 过滤器
|
||||||
|
services.AddTransient<IStartupFilter, StartupFilter>();
|
||||||
|
|
||||||
// 注册 HttpContextAccessor 服务
|
// 注册 HttpContextAccessor 服务
|
||||||
services.AddHttpContextAccessor();
|
services.AddHttpContextAccessor();
|
||||||
|
}
|
||||||
|
|
||||||
// 初始化应用服务
|
// 初始化应用服务
|
||||||
services.AddApp();
|
services.AddApp();
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ namespace ThingsGateway;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class AppSettingsOptions : IConfigurableOptions<AppSettingsOptions>
|
public sealed class AppSettingsOptions : IConfigurableOptions<AppSettingsOptions>
|
||||||
{
|
{
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 是否启用规范化文档
|
/// 是否启用规范化文档
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
341
src/Admin/ThingsGateway.Furion/App/Options/MiniRunOptions.cs
Normal file
341
src/Admin/ThingsGateway.Furion/App/Options/MiniRunOptions.cs
Normal file
@@ -0,0 +1,341 @@
|
|||||||
|
// ------------------------------------------------------------------------
|
||||||
|
// 版权信息
|
||||||
|
// 版权归百小僧及百签科技(广东)有限公司所有。
|
||||||
|
// 所有权利保留。
|
||||||
|
// 官方网站:https://baiqian.com
|
||||||
|
//
|
||||||
|
// 许可证信息
|
||||||
|
// 项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。
|
||||||
|
// 许可证的完整文本可以在源代码树根目录中的 LICENSE-APACHE 和 LICENSE-MIT 文件中找到。
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
|
||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
|
||||||
|
using ThingsGateway;
|
||||||
|
|
||||||
|
namespace System;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <see cref="WebApplication"/> 方式配置选项
|
||||||
|
/// </summary>
|
||||||
|
[SuppressSniffer]
|
||||||
|
public sealed class MiniRunOptions : IRunOptions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 内部构造函数
|
||||||
|
/// </summary>
|
||||||
|
internal MiniRunOptions()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 默认配置
|
||||||
|
/// </summary>
|
||||||
|
public static MiniRunOptions Default { get; } = new MiniRunOptions();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 默认配置(带启动参数)
|
||||||
|
/// </summary>
|
||||||
|
public static MiniRunOptions Main(string[] args)
|
||||||
|
{
|
||||||
|
return Default.WithArgs(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 默认配置(静默启动)
|
||||||
|
/// </summary>
|
||||||
|
public static MiniRunOptions DefaultSilence { get; } = new MiniRunOptions().Silence();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 默认配置(静默启动 + 启动参数)
|
||||||
|
/// </summary>
|
||||||
|
public static MiniRunOptions MainSilence(string[] args)
|
||||||
|
{
|
||||||
|
return DefaultSilence.WithArgs(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 配置 <see cref="WebApplicationOptions"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="options"></param>
|
||||||
|
/// <returns><see cref="MiniRunOptions"/></returns>
|
||||||
|
public MiniRunOptions ConfigureOptions(WebApplicationOptions options)
|
||||||
|
{
|
||||||
|
Options = options;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 配置 <see cref="IWebHostBuilder"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="configureAction"></param>
|
||||||
|
/// <returns><see cref="MiniRunOptions"/></returns>
|
||||||
|
public MiniRunOptions ConfigureBuilder(Action<IWebHostBuilder> configureAction)
|
||||||
|
{
|
||||||
|
ActionBuilder = configureAction;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 配置 <see cref="IHostBuilder"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="configureAction"></param>
|
||||||
|
/// <returns><see cref="MiniRunOptions"/></returns>
|
||||||
|
public MiniRunOptions ConfigureFirstActionBuilder(Action<IHostBuilder> configureAction)
|
||||||
|
{
|
||||||
|
FirstActionBuilder = configureAction;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 配置 <see cref="IServiceCollection"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="configureAction"></param>
|
||||||
|
/// <returns><see cref="MiniRunOptions"/></returns>
|
||||||
|
public MiniRunOptions ConfigureServices(Action<IServiceCollection> configureAction)
|
||||||
|
{
|
||||||
|
ActionServices = configureAction;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 配置 <see cref="InjectOptions"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="configureAction"></param>
|
||||||
|
/// <returns><see cref="MiniRunOptions"/></returns>
|
||||||
|
public MiniRunOptions ConfigureInject(Action<IWebHostBuilder, InjectOptions> configureAction)
|
||||||
|
{
|
||||||
|
ActionInject = configureAction;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 配置 <see cref="WebApplication"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="configureAction">配置委托</param>
|
||||||
|
/// <returns><see cref="MiniRunOptions"/></returns>
|
||||||
|
public MiniRunOptions Configure(Action<IHost> configureAction)
|
||||||
|
{
|
||||||
|
ActionConfigure = configureAction;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 配置 <see cref="ConfigurationManager"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="configureAction">配置委托</param>
|
||||||
|
/// <returns><see cref="MiniRunOptions"/></returns>
|
||||||
|
public MiniRunOptions ConfigureConfiguration(Action<IHostEnvironment, IConfiguration> configureAction)
|
||||||
|
{
|
||||||
|
ActionConfigurationManager = configureAction;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 添加应用服务组件
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TComponent">组件类型</typeparam>
|
||||||
|
/// <returns></returns>
|
||||||
|
public MiniRunOptions AddComponent<TComponent>()
|
||||||
|
where TComponent : class, IServiceComponent, new()
|
||||||
|
{
|
||||||
|
ServiceComponents.Add(typeof(TComponent), null);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 添加应用服务组件
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TComponent">组件类型</typeparam>
|
||||||
|
/// <typeparam name="TComponentOptions"></typeparam>
|
||||||
|
/// <param name="options">组件参数</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public MiniRunOptions AddComponent<TComponent, TComponentOptions>(TComponentOptions options)
|
||||||
|
where TComponent : class, IServiceComponent, new()
|
||||||
|
{
|
||||||
|
ServiceComponents.Add(typeof(TComponent), options);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 添加应用服务组件
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="componentType">组件类型</param>
|
||||||
|
/// <param name="options">组件参数</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public MiniRunOptions AddComponent(Type componentType, object options)
|
||||||
|
{
|
||||||
|
ServiceComponents.Add(componentType, options);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 添加应用中间件组件
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TComponent">组件类型</typeparam>
|
||||||
|
/// <returns></returns>
|
||||||
|
public MiniRunOptions UseComponent<TComponent>()
|
||||||
|
where TComponent : class, IApplicationComponent, new()
|
||||||
|
{
|
||||||
|
ApplicationComponents.Add(typeof(TComponent), null);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 添加应用中间件组件
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TComponent">组件类型</typeparam>
|
||||||
|
/// <typeparam name="TComponentOptions"></typeparam>
|
||||||
|
/// <param name="options">组件参数</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public MiniRunOptions UseComponent<TComponent, TComponentOptions>(TComponentOptions options)
|
||||||
|
where TComponent : class, IApplicationComponent, new()
|
||||||
|
{
|
||||||
|
ApplicationComponents.Add(typeof(TComponent), options);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 添加应用中间件组件
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="componentType">组件类型</param>
|
||||||
|
/// <param name="options">组件参数</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public MiniRunOptions UseComponent(Type componentType, object options)
|
||||||
|
{
|
||||||
|
ApplicationComponents.Add(componentType, options);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 添加 IWebHostBuilder 组件
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TComponent">组件类型</typeparam>
|
||||||
|
/// <returns></returns>
|
||||||
|
public MiniRunOptions AddWebComponent<TComponent>()
|
||||||
|
where TComponent : class, IWebComponent, new()
|
||||||
|
{
|
||||||
|
WebComponents.Add(typeof(TComponent), null);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 添加 IWebHostBuilder 组件
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TComponent">组件类型</typeparam>
|
||||||
|
/// <typeparam name="TComponentOptions"></typeparam>
|
||||||
|
/// <param name="options">组件参数</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public MiniRunOptions AddWebComponent<TComponent, TComponentOptions>(TComponentOptions options)
|
||||||
|
where TComponent : class, IWebComponent, new()
|
||||||
|
{
|
||||||
|
WebComponents.Add(typeof(TComponent), options);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 添加 IWebHostBuilder 组件
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="componentType">组件类型</param>
|
||||||
|
/// <param name="options">组件参数</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public MiniRunOptions AddWebComponent(Type componentType, object options)
|
||||||
|
{
|
||||||
|
WebComponents.Add(componentType, options);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 标识主机静默启动
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>不阻塞程序运行</remarks>
|
||||||
|
/// <param name="silence">静默启动</param>
|
||||||
|
/// <param name="logging">静默启动日志状态,默认 false</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public MiniRunOptions Silence(bool silence = true, bool logging = false)
|
||||||
|
{
|
||||||
|
IsSilence = silence;
|
||||||
|
SilenceLogging = logging;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 设置进程启动参数
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="args">启动参数</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public MiniRunOptions WithArgs(string[] args)
|
||||||
|
{
|
||||||
|
Args = args;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <see cref="WebApplicationOptions"/>
|
||||||
|
/// </summary>
|
||||||
|
internal WebApplicationOptions Options { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 自定义 <see cref="IServiceCollection"/> 委托
|
||||||
|
/// </summary>
|
||||||
|
internal Action<IServiceCollection> ActionServices { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 自定义 <see cref="IWebHostBuilder"/> 委托
|
||||||
|
/// </summary>
|
||||||
|
internal Action<IHostBuilder> FirstActionBuilder { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 自定义 <see cref="IWebHostBuilder"/> 委托
|
||||||
|
/// </summary>
|
||||||
|
internal Action<IWebHostBuilder> ActionBuilder { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 自定义 <see cref="InjectOptions"/> 委托
|
||||||
|
/// </summary>
|
||||||
|
internal Action<IWebHostBuilder, InjectOptions> ActionInject { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 自定义 <see cref="IHost"/> 委托
|
||||||
|
/// </summary>
|
||||||
|
internal Action<IHost> ActionConfigure { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 自定义 <see cref="IConfiguration"/> 委托
|
||||||
|
/// </summary>
|
||||||
|
internal Action<IHostEnvironment, IConfiguration> ActionConfigurationManager { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 应用服务组件
|
||||||
|
/// </summary>
|
||||||
|
internal Dictionary<Type, object> ServiceComponents { get; set; } = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// IWebHostBuilder 组件
|
||||||
|
/// </summary>
|
||||||
|
internal Dictionary<Type, object> WebComponents { get; set; } = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 应用中间件组件
|
||||||
|
/// </summary>
|
||||||
|
internal Dictionary<Type, object> ApplicationComponents { get; set; } = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 静默启动
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>不阻塞程序运行</remarks>
|
||||||
|
internal bool IsSilence { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 静默启动日志状态
|
||||||
|
/// </summary>
|
||||||
|
internal bool SilenceLogging { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 命令行参数
|
||||||
|
/// </summary>
|
||||||
|
internal string[] Args { get; set; }
|
||||||
|
}
|
||||||
@@ -602,6 +602,33 @@ public static class Serve
|
|||||||
return app;
|
return app;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 启动 WebApplication 主机
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>未包含 Web 基础功能,需手动注册服务/中间件</remarks>
|
||||||
|
/// <param name="options">配置选项</param>
|
||||||
|
/// <param name="urls">默认 5000/5001 端口</param>
|
||||||
|
/// <param name="cancellationToken"></param>
|
||||||
|
/// <returns><see cref="IHost"/></returns>
|
||||||
|
public static async Task<IHost> RunAsync(MiniRunOptions options, string urls = default, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
// 构建 WebApplication 对象
|
||||||
|
BuildMiniApplication(options, urls, out var app);
|
||||||
|
|
||||||
|
// 是否静默启动
|
||||||
|
if (!options.IsSilence)
|
||||||
|
{
|
||||||
|
// 配置启动地址和端口
|
||||||
|
await app.RunAsync(cancellationToken).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await app.StartAsync(cancellationToken).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return app;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 构建 WebApplication 对象
|
/// 构建 WebApplication 对象
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -616,8 +643,8 @@ public static class Serve
|
|||||||
|
|
||||||
// 初始化 WebApplicationBuilder
|
// 初始化 WebApplicationBuilder
|
||||||
var builder = (options.Options == null
|
var builder = (options.Options == null
|
||||||
? WebApplication.CreateBuilder(args)
|
? WebApplication.CreateBuilder(args)
|
||||||
: WebApplication.CreateBuilder(options.Options));
|
: WebApplication.CreateBuilder(options.Options));
|
||||||
|
|
||||||
// 调用自定义配置服务
|
// 调用自定义配置服务
|
||||||
options?.FirstActionBuilder?.Invoke(builder);
|
options?.FirstActionBuilder?.Invoke(builder);
|
||||||
@@ -799,6 +826,132 @@ public static class Serve
|
|||||||
App.AppStartups.Clear();
|
App.AppStartups.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 构建 IHost 对象
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="options">配置选项</param>
|
||||||
|
/// <param name="urls">默认 5000/5001 端口</param>
|
||||||
|
/// <param name="app"><see cref="IHost"/></param>
|
||||||
|
public static void BuildMiniApplication(MiniRunOptions options, string urls, out IHost app)
|
||||||
|
{
|
||||||
|
// 获取命令行参数
|
||||||
|
var args = options.Args ?? Environment.GetCommandLineArgs().Skip(1).ToArray();
|
||||||
|
|
||||||
|
|
||||||
|
var builder = Host.CreateDefaultBuilder(args);
|
||||||
|
|
||||||
|
// 静默启动排除指定日志类名
|
||||||
|
if (options.IsSilence && !options.SilenceLogging)
|
||||||
|
{
|
||||||
|
builder = builder.ConfigureLogging(logging =>
|
||||||
|
{
|
||||||
|
logging.AddFilter((provider, category, logLevel) => !SilenceExcludesOfLogCategoryName.Any(u => category.StartsWith(u)));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 配置 Web 主机
|
||||||
|
builder = builder.ConfigureWebHost(webHostBuilder =>
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
// 调用自定义配置服务
|
||||||
|
options?.FirstActionBuilder?.Invoke(builder);
|
||||||
|
|
||||||
|
// 注册 WebApplicationBuilder 组件
|
||||||
|
if (options.WebComponents.Count > 0)
|
||||||
|
{
|
||||||
|
foreach (var (componentType, opt) in options.WebComponents)
|
||||||
|
{
|
||||||
|
webHostBuilder.AddWebComponent(componentType, opt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
webHostBuilder.Configure((WebHostBuilderContext app, IApplicationBuilder applicationBuilder) =>
|
||||||
|
{
|
||||||
|
|
||||||
|
// 添加自定义配置
|
||||||
|
options.ActionConfigurationManager?.Invoke(app.HostingEnvironment, app.Configuration);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
// 初始化框架
|
||||||
|
webHostBuilder.Inject(options.ActionInject);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 配置服务
|
||||||
|
if (options.ServiceComponents.Count > 0)
|
||||||
|
{
|
||||||
|
webHostBuilder = webHostBuilder.ConfigureServices(services =>
|
||||||
|
{
|
||||||
|
// 注册应用服务组件
|
||||||
|
foreach (var (componentType, opt) in options.ServiceComponents)
|
||||||
|
{
|
||||||
|
services.AddComponent(componentType, opt);
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 配置启动地址和端口
|
||||||
|
var startUrls = !string.IsNullOrWhiteSpace(urls) ? urls : webHostBuilder.GetSetting(nameof(urls));
|
||||||
|
|
||||||
|
// 自定义启动端口
|
||||||
|
if (!string.IsNullOrWhiteSpace(startUrls))
|
||||||
|
{
|
||||||
|
webHostBuilder = webHostBuilder.UseUrls(startUrls);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 调用自定义配置
|
||||||
|
options?.ActionBuilder?.Invoke(webHostBuilder);
|
||||||
|
|
||||||
|
// 配置中间件
|
||||||
|
if (options.ApplicationComponents.Count > 0)
|
||||||
|
{
|
||||||
|
webHostBuilder = webHostBuilder.Configure((context, app) =>
|
||||||
|
{
|
||||||
|
// 注册应用中间件组件
|
||||||
|
foreach (var (componentType, opt) in options.ApplicationComponents)
|
||||||
|
{
|
||||||
|
app.UseComponent(context.HostingEnvironment, componentType, opt);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
builder = builder.ConfigureServices(services =>
|
||||||
|
{
|
||||||
|
// 调用自定义配置服务
|
||||||
|
options?.ActionServices?.Invoke(services);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 构建主机
|
||||||
|
app = builder.Build();
|
||||||
|
|
||||||
|
InternalApp.RootServices ??= app.Services;
|
||||||
|
|
||||||
|
var applicationPartManager = app.Services.GetService<ApplicationPartManager>();
|
||||||
|
|
||||||
|
applicationPartManager?.ApplicationParts?.RemoveWhere(p => App.BakImageNames.Any(b => b == p.Name));
|
||||||
|
// 配置所有 Starup Configure
|
||||||
|
UseStartups(app.Services);
|
||||||
|
// 释放内存
|
||||||
|
App.AppStartups.Clear();
|
||||||
|
// 调用自定义配置
|
||||||
|
options?.ActionConfigure?.Invoke(app);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 构建 IHost 对象
|
/// 构建 IHost 对象
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -127,7 +127,8 @@ public sealed class DatabaseLogger : ILogger, IDisposable
|
|||||||
// 设置日志消息模板
|
// 设置日志消息模板
|
||||||
logMsg.Message = _options.MessageFormat != null
|
logMsg.Message = _options.MessageFormat != null
|
||||||
? _options.MessageFormat(logMsg)
|
? _options.MessageFormat(logMsg)
|
||||||
: Penetrates.OutputStandardMessage(logMsg, _options.DateFormat, withTraceId: _options.WithTraceId, withStackFrame: _options.WithStackFrame, provider: _options.FormatProvider);
|
: string.Empty;
|
||||||
|
//: Penetrates.OutputStandardMessage(logMsg, _options.DateFormat, withTraceId: _options.WithTraceId, withStackFrame: _options.WithStackFrame, provider: _options.FormatProvider);
|
||||||
|
|
||||||
// 空检查
|
// 空检查
|
||||||
if (logMsg.Message is null)
|
if (logMsg.Message is null)
|
||||||
|
|||||||
@@ -683,13 +683,20 @@ public class MachineInfo : IExtend
|
|||||||
if (dic.TryGetValue("MemTotal", out var str) && !str.IsNullOrEmpty())
|
if (dic.TryGetValue("MemTotal", out var str) && !str.IsNullOrEmpty())
|
||||||
Memory = (UInt64)str.TrimEnd(" kB").ToLong();
|
Memory = (UInt64)str.TrimEnd(" kB").ToLong();
|
||||||
|
|
||||||
|
ulong ma = 0;
|
||||||
if (dic.TryGetValue("MemAvailable", out str) && !str.IsNullOrEmpty())
|
if (dic.TryGetValue("MemAvailable", out str) && !str.IsNullOrEmpty())
|
||||||
AvailableMemory = (UInt64)str.TrimEnd(" kB").ToLong();
|
{
|
||||||
else if (dic.TryGetValue("MemFree", out str) && !str.IsNullOrEmpty())
|
ma = (UInt64)(str.TrimEnd(" kB").ToLong());
|
||||||
AvailableMemory =
|
}
|
||||||
(UInt64)(str.TrimEnd(" kB").ToLong() +
|
|
||||||
dic["Buffers"]?.TrimEnd(" kB").ToLong() ?? 0 +
|
//低于3.14内核的版本用 free+cache
|
||||||
dic["Cached"]?.TrimEnd(" kB").ToLong() ?? 0);
|
var mf = (UInt64)(dic["MemFree"]?.TrimEnd(" kB").ToLong() ?? 0);
|
||||||
|
var mc = (UInt64)(dic["Cached"]?.TrimEnd(" kB").ToLong() ?? 0);
|
||||||
|
var bf = (UInt64)(dic["Buffers"]?.TrimEnd(" kB").ToLong() ?? 0);
|
||||||
|
|
||||||
|
var free = mf + mc + bf;
|
||||||
|
|
||||||
|
AvailableMemory = ma > free ? ma : free;
|
||||||
}
|
}
|
||||||
|
|
||||||
// A2/A4温度获取,Buildroot,CPU温度和主板温度
|
// A2/A4温度获取,Buildroot,CPU温度和主板温度
|
||||||
|
|||||||
@@ -306,8 +306,19 @@ public class TimerScheduler : ILogFeature
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var func = timer.Method.As<Func<Object?, Task>>(target);
|
#if NET6_0_OR_GREATER
|
||||||
await func!(timer.State).ConfigureAwait(false);
|
if (timer.IsValueTask)
|
||||||
|
{
|
||||||
|
var func = timer.Method.As<Func<Object?, ValueTask>>(target);
|
||||||
|
await func!(timer.State).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
var func = timer.Method.As<Func<Object?, Task>>(target);
|
||||||
|
await func!(timer.State).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
catch (ThreadAbortException) { throw; }
|
catch (ThreadAbortException) { throw; }
|
||||||
catch (ThreadInterruptedException) { throw; }
|
catch (ThreadInterruptedException) { throw; }
|
||||||
|
|||||||
@@ -87,6 +87,8 @@ public class TimerX : ITimer, ITimerx, IDisposable
|
|||||||
|
|
||||||
private DateTime _AbsolutelyNext;
|
private DateTime _AbsolutelyNext;
|
||||||
private readonly Cron[]? _crons;
|
private readonly Cron[]? _crons;
|
||||||
|
|
||||||
|
internal bool IsValueTask { get; }
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
// #region 静态
|
// #region 静态
|
||||||
@@ -158,6 +160,29 @@ public class TimerX : ITimer, ITimerx, IDisposable
|
|||||||
Init(dueTime);
|
Init(dueTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if NET6_0_OR_GREATER
|
||||||
|
|
||||||
|
/// <summary>实例化一个不可重入的定时器</summary>
|
||||||
|
/// <param name="callback">委托</param>
|
||||||
|
/// <param name="state">用户数据</param>
|
||||||
|
/// <param name="dueTime">多久之后开始。毫秒</param>
|
||||||
|
/// <param name="period">间隔周期。毫秒</param>
|
||||||
|
/// <param name="scheduler">调度器</param>
|
||||||
|
public TimerX(Func<Object, ValueTask> callback, Object? state, Int32 dueTime, Int32 period, String? scheduler = null) : this(callback.Target, callback.Method, state, scheduler)
|
||||||
|
{
|
||||||
|
IsValueTask = true;
|
||||||
|
if (callback == null) throw new ArgumentNullException(nameof(callback));
|
||||||
|
if (dueTime < 0) throw new ArgumentOutOfRangeException(nameof(dueTime));
|
||||||
|
|
||||||
|
IsAsyncTask = true;
|
||||||
|
Async = true;
|
||||||
|
Period = period;
|
||||||
|
|
||||||
|
Init(dueTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
/// <summary>实例化一个绝对定时器,指定时刻执行,跟当前时间和SetNext无关</summary>
|
/// <summary>实例化一个绝对定时器,指定时刻执行,跟当前时间和SetNext无关</summary>
|
||||||
/// <param name="callback">委托</param>
|
/// <param name="callback">委托</param>
|
||||||
/// <param name="state">用户数据</param>
|
/// <param name="state">用户数据</param>
|
||||||
@@ -210,6 +235,37 @@ public class TimerX : ITimer, ITimerx, IDisposable
|
|||||||
Init(ms);
|
Init(ms);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if NET6_0_OR_GREATER
|
||||||
|
|
||||||
|
/// <summary>实例化一个绝对定时器,指定时刻执行,跟当前时间和SetNext无关</summary>
|
||||||
|
/// <param name="callback">委托</param>
|
||||||
|
/// <param name="state">用户数据</param>
|
||||||
|
/// <param name="startTime">绝对开始时间</param>
|
||||||
|
/// <param name="period">间隔周期。毫秒</param>
|
||||||
|
/// <param name="scheduler">调度器</param>
|
||||||
|
public TimerX(Func<Object, ValueTask> callback, Object? state, DateTime startTime, Int32 period, String? scheduler = null) : this(callback.Target, callback.Method, state, scheduler)
|
||||||
|
{
|
||||||
|
IsValueTask = true;
|
||||||
|
if (callback == null) throw new ArgumentNullException(nameof(callback));
|
||||||
|
if (startTime <= DateTime.MinValue) throw new ArgumentOutOfRangeException(nameof(startTime));
|
||||||
|
if (period <= 0) throw new ArgumentOutOfRangeException(nameof(period));
|
||||||
|
|
||||||
|
IsAsyncTask = true;
|
||||||
|
Async = true;
|
||||||
|
Period = period;
|
||||||
|
Absolutely = true;
|
||||||
|
|
||||||
|
//var now = DateTime.Now;
|
||||||
|
var now = Scheduler.GetNow();
|
||||||
|
var next = startTime;
|
||||||
|
while (next < now) next = next.AddMilliseconds(period);
|
||||||
|
|
||||||
|
var ms = (Int64)(next - now).TotalMilliseconds;
|
||||||
|
_AbsolutelyNext = next;
|
||||||
|
Init(ms);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
/// <summary>实例化一个Cron定时器</summary>
|
/// <summary>实例化一个Cron定时器</summary>
|
||||||
/// <param name="callback">委托</param>
|
/// <param name="callback">委托</param>
|
||||||
/// <param name="state">用户数据</param>
|
/// <param name="state">用户数据</param>
|
||||||
@@ -274,6 +330,42 @@ public class TimerX : ITimer, ITimerx, IDisposable
|
|||||||
//Init(_AbsolutelyNext = _cron.GetNext(DateTime.Now));
|
//Init(_AbsolutelyNext = _cron.GetNext(DateTime.Now));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if NET6_0_OR_GREATER
|
||||||
|
/// <summary>实例化一个Cron定时器</summary>
|
||||||
|
/// <param name="callback">委托</param>
|
||||||
|
/// <param name="state">用户数据</param>
|
||||||
|
/// <param name="cronExpression">Cron表达式。支持多个表达式,分号分隔</param>
|
||||||
|
/// <param name="scheduler">调度器</param>
|
||||||
|
public TimerX(Func<Object, ValueTask> callback, Object? state, String cronExpression, String? scheduler = null) : this(callback.Target, callback.Method, state, scheduler)
|
||||||
|
{
|
||||||
|
IsValueTask = true;
|
||||||
|
if (callback == null) throw new ArgumentNullException(nameof(callback));
|
||||||
|
if (cronExpression.IsNullOrEmpty()) throw new ArgumentNullException(nameof(cronExpression));
|
||||||
|
|
||||||
|
var list = new List<Cron>();
|
||||||
|
foreach (var item in cronExpression.Split(";"))
|
||||||
|
{
|
||||||
|
var cron = new Cron();
|
||||||
|
if (!cron.Parse(item)) throw new ArgumentException($"Invalid Cron expression[{item}]", nameof(cronExpression));
|
||||||
|
|
||||||
|
list.Add(cron);
|
||||||
|
}
|
||||||
|
_crons = list.ToArray();
|
||||||
|
|
||||||
|
IsAsyncTask = true;
|
||||||
|
Async = true;
|
||||||
|
Absolutely = true;
|
||||||
|
|
||||||
|
//var now = DateTime.Now;
|
||||||
|
var now = Scheduler.GetNow();
|
||||||
|
var next = _crons.Min(e => e.GetNext(now));
|
||||||
|
var ms = (Int64)(next - now).TotalMilliseconds;
|
||||||
|
_AbsolutelyNext = next;
|
||||||
|
Init(ms);
|
||||||
|
//Init(_AbsolutelyNext = _cron.GetNext(DateTime.Now));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
public bool Disposed { get; private set; }
|
public bool Disposed { get; private set; }
|
||||||
/// <summary>销毁定时器</summary>
|
/// <summary>销毁定时器</summary>
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
|
|||||||
@@ -8,8 +8,8 @@
|
|||||||
// QQ群:605534569
|
// QQ群:605534569
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
using ThingsGateway.Common.Extension;
|
|
||||||
using ThingsGateway.NewLife;
|
using ThingsGateway.NewLife;
|
||||||
|
using ThingsGateway.Razor.Extension;
|
||||||
|
|
||||||
namespace ThingsGateway.Razor;
|
namespace ThingsGateway.Razor;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
@namespace ThingsGateway.Razor
|
||||||
|
|
||||||
|
@if (show)
|
||||||
|
{
|
||||||
|
<Spinner class="ms-auto"></Spinner>
|
||||||
|
}
|
||||||
@@ -8,7 +8,7 @@
|
|||||||
// QQ群:605534569
|
// QQ群:605534569
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
namespace ThingsGateway.Gateway.Razor;
|
namespace ThingsGateway.Razor;
|
||||||
|
|
||||||
public partial class SpinnerComponent
|
public partial class SpinnerComponent
|
||||||
{
|
{
|
||||||
@@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
using Microsoft.JSInterop;
|
using Microsoft.JSInterop;
|
||||||
|
|
||||||
namespace ThingsGateway.Common.Extension;
|
namespace ThingsGateway.Razor.Extension;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// JSRuntime扩展方法
|
/// JSRuntime扩展方法
|
||||||
@@ -49,4 +49,28 @@ public static class JSRuntimeExtensions
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static async ValueTask<T> GetLocalStorage<T>(this IJSRuntime jsRuntime, string name)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return await jsRuntime.InvokeAsync<T>("getLocalStorage", name).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async ValueTask SetLocalStorage<T>(this IJSRuntime jsRuntime, string name, T data)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await jsRuntime.InvokeVoidAsync("setLocalStorage", name, data).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
// 设置 culture
|
|
||||||
function setCultureLocalStorage(culture) {
|
|
||||||
localStorage.setItem("culture", culture);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取 culture
|
|
||||||
function getCultureLocalStorage() {
|
|
||||||
return localStorage.getItem("culture");
|
|
||||||
}
|
|
||||||
18
src/Admin/ThingsGateway.Razor/wwwroot/js/localStorageUtil.js
Normal file
18
src/Admin/ThingsGateway.Razor/wwwroot/js/localStorageUtil.js
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
// 设置 culture
|
||||||
|
function setCultureLocalStorage(culture) {
|
||||||
|
localStorage.setItem("culture", culture);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取 culture
|
||||||
|
function getCultureLocalStorage() {
|
||||||
|
return localStorage.getItem("culture");
|
||||||
|
}
|
||||||
|
|
||||||
|
function getLocalStorage(name) {
|
||||||
|
return JSON.parse(localStorage.getItem(name)) ?? 0;
|
||||||
|
}
|
||||||
|
function setLocalStorage(name, data) {
|
||||||
|
if (localStorage) {
|
||||||
|
localStorage.setItem(name, JSON.stringify(data));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -27,16 +27,17 @@
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="T">数据类型</typeparam>
|
/// <typeparam name="T">数据类型</typeparam>
|
||||||
/// <param name="insertDatas">要插入的数据列表</param>
|
/// <param name="insertDatas">要插入的数据列表</param>
|
||||||
|
/// <param name="tableName">表名称</param>
|
||||||
/// <param name="dateFormat">日期格式字符串</param>
|
/// <param name="dateFormat">日期格式字符串</param>
|
||||||
/// <returns>插入的记录数</returns>
|
/// <returns>插入的记录数</returns>
|
||||||
public int BulkCopy<T>(IEnumerable<T> insertDatas, string dateFormat = "yyyy/M/d H:mm:ss") where T : class, new()
|
public int BulkCopy<T>(IEnumerable<T> insertDatas, string tableName = null, string dateFormat = "yyyy/M/d H:mm:ss") where T : class, new()
|
||||||
{
|
{
|
||||||
int result = 0;
|
int result = 0;
|
||||||
// 使用分页方式处理大数据量插入
|
// 使用分页方式处理大数据量插入
|
||||||
db.Utilities.PageEach(insertDatas, pageSize, pageItems =>
|
db.Utilities.PageEach(insertDatas, pageSize, pageItems =>
|
||||||
{
|
{
|
||||||
// 同步调用批量插入API并累加结果
|
// 同步调用批量插入API并累加结果
|
||||||
result += questDbRestAPI.BulkCopyAsync(pageItems, dateFormat).GetAwaiter().GetResult();
|
result += questDbRestAPI.BulkCopyAsync(pageItems, tableName, dateFormat).GetAwaiter().GetResult();
|
||||||
});
|
});
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -46,16 +47,17 @@
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="T">数据类型</typeparam>
|
/// <typeparam name="T">数据类型</typeparam>
|
||||||
/// <param name="insertDatas">要插入的数据列表</param>
|
/// <param name="insertDatas">要插入的数据列表</param>
|
||||||
|
/// <param name="tableName">表名称</param>
|
||||||
/// <param name="dateFormat">日期格式字符串</param>
|
/// <param name="dateFormat">日期格式字符串</param>
|
||||||
/// <returns>插入的记录数</returns>
|
/// <returns>插入的记录数</returns>
|
||||||
public async Task<int> BulkCopyAsync<T>(IEnumerable<T> insertDatas, string dateFormat = "yyyy/M/d H:mm:ss") where T : class, new()
|
public async Task<int> BulkCopyAsync<T>(IEnumerable<T> insertDatas, string tableName = null, string dateFormat = "yyyy/M/d H:mm:ss") where T : class, new()
|
||||||
{
|
{
|
||||||
int result = 0;
|
int result = 0;
|
||||||
// 异步分页处理大数据量插入
|
// 异步分页处理大数据量插入
|
||||||
await db.Utilities.PageEachAsync(insertDatas, pageSize, async pageItems =>
|
await db.Utilities.PageEachAsync(insertDatas, pageSize, async pageItems =>
|
||||||
{
|
{
|
||||||
// 异步调用批量插入API并累加结果
|
// 异步调用批量插入API并累加结果
|
||||||
result += await questDbRestAPI.BulkCopyAsync(pageItems, dateFormat).ConfigureAwait(false);
|
result += await questDbRestAPI.BulkCopyAsync(pageItems, tableName, dateFormat).ConfigureAwait(false);
|
||||||
}).ConfigureAwait(false);
|
}).ConfigureAwait(false);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,16 +7,12 @@ namespace ThingsGateway.SqlSugar
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 绑定RestAPI需要的信息
|
/// 绑定RestAPI需要的信息
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static void SetRestApiInfo(DbConnectionStringBuilder builder, ref string host, ref string httpPort, ref string username, ref string password)
|
public static void SetRestApiInfo(DbConnectionStringBuilder builder, ref string host, ref string username, ref string password)
|
||||||
{
|
{
|
||||||
if (builder.TryGetValue("Host", out object hostValue))
|
if (builder.TryGetValue("Host", out object hostValue))
|
||||||
{
|
{
|
||||||
host = Convert.ToString(hostValue);
|
host = Convert.ToString(hostValue);
|
||||||
}
|
}
|
||||||
if (builder.TryGetValue("HttpPort", out object httpPortValue))
|
|
||||||
{
|
|
||||||
httpPort = Convert.ToString(httpPortValue);
|
|
||||||
}
|
|
||||||
if (builder.TryGetValue("Username", out object usernameValue))
|
if (builder.TryGetValue("Username", out object usernameValue))
|
||||||
{
|
{
|
||||||
username = Convert.ToString(usernameValue);
|
username = Convert.ToString(usernameValue);
|
||||||
|
|||||||
@@ -28,16 +28,16 @@ namespace ThingsGateway.SqlSugar
|
|||||||
/// 初始化 QuestDbRestAPI 实例
|
/// 初始化 QuestDbRestAPI 实例
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="db">SqlSugar 数据库客户端</param>
|
/// <param name="db">SqlSugar 数据库客户端</param>
|
||||||
public QuestDbRestAPI(ISqlSugarClient db)
|
/// <param name="httpPort">restApi端口</param>
|
||||||
|
public QuestDbRestAPI(ISqlSugarClient db, int httpPort = 9000)
|
||||||
{
|
{
|
||||||
var builder = new DbConnectionStringBuilder();
|
var builder = new DbConnectionStringBuilder();
|
||||||
builder.ConnectionString = db.CurrentConnectionConfig.ConnectionString;
|
builder.ConnectionString = db.CurrentConnectionConfig.ConnectionString;
|
||||||
this.db = db;
|
this.db = db;
|
||||||
string httpPort = String.Empty;
|
|
||||||
string host = String.Empty;
|
string host = String.Empty;
|
||||||
string username = String.Empty;
|
string username = String.Empty;
|
||||||
string password = String.Empty;
|
string password = String.Empty;
|
||||||
QuestDbRestAPHelper.SetRestApiInfo(builder, ref host, ref httpPort, ref username, ref password);
|
QuestDbRestAPHelper.SetRestApiInfo(builder, ref host, ref username, ref password);
|
||||||
BindHost(host, httpPort, username, password);
|
BindHost(host, httpPort, username, password);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,9 +51,14 @@ namespace ThingsGateway.SqlSugar
|
|||||||
// HTTP GET 请求执行SQL
|
// HTTP GET 请求执行SQL
|
||||||
var result = string.Empty;
|
var result = string.Empty;
|
||||||
var url = $"{this.url}/exec?query={HttpUtility.UrlEncode(sql)}";
|
var url = $"{this.url}/exec?query={HttpUtility.UrlEncode(sql)}";
|
||||||
|
|
||||||
|
var request = new HttpRequestMessage(HttpMethod.Get, url);
|
||||||
if (!string.IsNullOrWhiteSpace(authorization))
|
if (!string.IsNullOrWhiteSpace(authorization))
|
||||||
client.DefaultRequestHeaders.Add("Authorization", authorization);
|
{
|
||||||
var httpResponseMessage = await client.GetAsync(url).ConfigureAwait(false);
|
request.Headers.Authorization = AuthenticationHeaderValue.Parse(authorization);
|
||||||
|
}
|
||||||
|
|
||||||
|
using var httpResponseMessage = await client.SendAsync(request).ConfigureAwait(false);
|
||||||
result = await httpResponseMessage.Content.ReadAsStringAsync().ConfigureAwait(false);
|
result = await httpResponseMessage.Content.ReadAsStringAsync().ConfigureAwait(false);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -68,34 +73,34 @@ namespace ThingsGateway.SqlSugar
|
|||||||
return ExecuteCommandAsync(sql).GetAwaiter().GetResult();
|
return ExecuteCommandAsync(sql).GetAwaiter().GetResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
///// <summary>
|
||||||
/// 异步批量插入单条数据
|
///// 异步批量插入单条数据
|
||||||
/// </summary>
|
///// </summary>
|
||||||
/// <typeparam name="T">数据类型</typeparam>
|
///// <typeparam name="T">数据类型</typeparam>
|
||||||
/// <param name="insertData">要插入的数据</param>
|
///// <param name="insertData">要插入的数据</param>
|
||||||
/// <param name="dateFormat">日期格式字符串</param>
|
///// <param name="dateFormat">日期格式字符串</param>
|
||||||
/// <returns>影响的行数</returns>
|
///// <returns>影响的行数</returns>
|
||||||
public async Task<int> BulkCopyAsync<T>(T insertData, string dateFormat = "yyyy/M/d H:mm:ss") where T : class, new()
|
//public async Task<int> BulkCopyAsync<T>(T insertData, string dateFormat = "yyyy/M/d H:mm:ss") where T : class, new()
|
||||||
{
|
//{
|
||||||
if (db.CurrentConnectionConfig.MoreSettings == null)
|
// if (db.CurrentConnectionConfig.MoreSettings == null)
|
||||||
db.CurrentConnectionConfig.MoreSettings = new ConnMoreSettings();
|
// db.CurrentConnectionConfig.MoreSettings = new ConnMoreSettings();
|
||||||
db.CurrentConnectionConfig.MoreSettings.DisableNvarchar = true;
|
// db.CurrentConnectionConfig.MoreSettings.DisableNvarchar = true;
|
||||||
var sql = db.InsertableT(insertData).ToSqlString();
|
// var sql = db.InsertableT(insertData).ToSqlString();
|
||||||
var result = await ExecuteCommandAsync(sql).ConfigureAwait(false);
|
// var result = await ExecuteCommandAsync(sql).ConfigureAwait(false);
|
||||||
return result.Contains("OK", StringComparison.OrdinalIgnoreCase) ? 1 : 0;
|
// return result.Contains("OK", StringComparison.OrdinalIgnoreCase) ? 1 : 0;
|
||||||
}
|
//}
|
||||||
|
|
||||||
/// <summary>
|
///// <summary>
|
||||||
/// 同步批量插入单条数据
|
///// 同步批量插入单条数据
|
||||||
/// </summary>
|
///// </summary>
|
||||||
/// <typeparam name="T">数据类型</typeparam>
|
///// <typeparam name="T">数据类型</typeparam>
|
||||||
/// <param name="insertData">要插入的数据</param>
|
///// <param name="insertData">要插入的数据</param>
|
||||||
/// <param name="dateFormat">日期格式字符串</param>
|
///// <param name="dateFormat">日期格式字符串</param>
|
||||||
/// <returns>影响的行数</returns>
|
///// <returns>影响的行数</returns>
|
||||||
public int BulkCopy<T>(T insertData, string dateFormat = "yyyy/M/d H:mm:ss") where T : class, new()
|
//public int BulkCopy<T>(T insertData, string dateFormat = "yyyy/M/d H:mm:ss") where T : class, new()
|
||||||
{
|
//{
|
||||||
return BulkCopyAsync(insertData, dateFormat).GetAwaiter().GetResult();
|
// return BulkCopyAsync(insertData, dateFormat).GetAwaiter().GetResult();
|
||||||
}
|
//}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 创建分页批量插入器
|
/// 创建分页批量插入器
|
||||||
@@ -115,9 +120,10 @@ namespace ThingsGateway.SqlSugar
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="T">数据类型</typeparam>
|
/// <typeparam name="T">数据类型</typeparam>
|
||||||
/// <param name="insertList">要插入的数据列表</param>
|
/// <param name="insertList">要插入的数据列表</param>
|
||||||
|
/// <param name="tableName">表名称</param>
|
||||||
/// <param name="dateFormat">日期格式字符串</param>
|
/// <param name="dateFormat">日期格式字符串</param>
|
||||||
/// <returns>插入的记录数</returns>
|
/// <returns>插入的记录数</returns>
|
||||||
public async Task<int> BulkCopyAsync<T>(List<T> insertList, string dateFormat = "yyyy/M/d H:mm:ss") where T : class, new()
|
public async Task<int> BulkCopyAsync<T>(List<T> insertList, string tableName = null, string dateFormat = "yyyy/M/d H:mm:ss") where T : class, new()
|
||||||
{
|
{
|
||||||
var result = 0;
|
var result = 0;
|
||||||
var fileName = $"{Guid.NewGuid()}.csv";
|
var fileName = $"{Guid.NewGuid()}.csv";
|
||||||
@@ -127,12 +133,12 @@ namespace ThingsGateway.SqlSugar
|
|||||||
// 准备多部分表单数据
|
// 准备多部分表单数据
|
||||||
var boundary = "---------------" + DateTime.Now.Ticks.ToString("x");
|
var boundary = "---------------" + DateTime.Now.Ticks.ToString("x");
|
||||||
var list = new List<Hashtable>();
|
var list = new List<Hashtable>();
|
||||||
var name = db.EntityMaintenance.GetEntityInfo<T>().DbTableName;
|
tableName ??= db.EntityMaintenance.GetEntityInfo<T>().DbTableName;
|
||||||
|
|
||||||
// 获取或创建列信息缓存
|
// 获取或创建列信息缓存
|
||||||
var key = "QuestDbBulkCopy" + typeof(T).FullName + typeof(T).GetHashCode();
|
var key = "QuestDbBulkCopy" + typeof(T).FullName + typeof(T).GetHashCode();
|
||||||
var columns = ReflectionInoCacheService.Instance.GetOrCreate(key, () =>
|
var columns = ReflectionInoCacheService.Instance.GetOrCreate(key, () =>
|
||||||
db.CopyNew().DbMaintenance.GetColumnInfosByTableName(name));
|
db.CopyNew().DbMaintenance.GetColumnInfosByTableName(tableName));
|
||||||
|
|
||||||
// 构建schema信息
|
// 构建schema信息
|
||||||
columns.ForEach(d =>
|
columns.ForEach(d =>
|
||||||
@@ -170,8 +176,8 @@ namespace ThingsGateway.SqlSugar
|
|||||||
// 准备HTTP请求内容
|
// 准备HTTP请求内容
|
||||||
using var httpContent = new MultipartFormDataContent(boundary);
|
using var httpContent = new MultipartFormDataContent(boundary);
|
||||||
using var fileStream = File.OpenRead(filePath);
|
using var fileStream = File.OpenRead(filePath);
|
||||||
if (!string.IsNullOrWhiteSpace(this.authorization))
|
//if (!string.IsNullOrWhiteSpace(this.authorization))
|
||||||
client.DefaultRequestHeaders.Add("Authorization", this.authorization);
|
// client.DefaultRequestHeaders.Add("Authorization", this.authorization);
|
||||||
httpContent.Add(new StringContent(schema), "schema");
|
httpContent.Add(new StringContent(schema), "schema");
|
||||||
var streamContent = new StreamContent(fileStream);
|
var streamContent = new StreamContent(fileStream);
|
||||||
streamContent.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
|
streamContent.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
|
||||||
@@ -183,8 +189,8 @@ namespace ThingsGateway.SqlSugar
|
|||||||
"multipart/form-data; boundary=" + boundary);
|
"multipart/form-data; boundary=" + boundary);
|
||||||
|
|
||||||
// 发送请求并处理响应
|
// 发送请求并处理响应
|
||||||
var httpResponseMessage =
|
using var httpResponseMessage =
|
||||||
await Post(client, name, httpContent).ConfigureAwait(false);
|
await Post(client, tableName, httpContent).ConfigureAwait(false);
|
||||||
var readAsStringAsync = await httpResponseMessage.Content.ReadAsStringAsync().ConfigureAwait(false);
|
var readAsStringAsync = await httpResponseMessage.Content.ReadAsStringAsync().ConfigureAwait(false);
|
||||||
var splitByLine = QuestDbRestAPHelper.SplitByLine(readAsStringAsync);
|
var splitByLine = QuestDbRestAPHelper.SplitByLine(readAsStringAsync);
|
||||||
|
|
||||||
@@ -266,11 +272,12 @@ namespace ThingsGateway.SqlSugar
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="T">数据类型</typeparam>
|
/// <typeparam name="T">数据类型</typeparam>
|
||||||
/// <param name="insertList">要插入的数据列表</param>
|
/// <param name="insertList">要插入的数据列表</param>
|
||||||
|
/// <param name="tableName">表名称</param>
|
||||||
/// <param name="dateFormat">日期格式字符串</param>
|
/// <param name="dateFormat">日期格式字符串</param>
|
||||||
/// <returns>插入的记录数</returns>
|
/// <returns>插入的记录数</returns>
|
||||||
public int BulkCopy<T>(List<T> insertList, string dateFormat = "yyyy/M/d H:mm:ss") where T : class, new()
|
public int BulkCopy<T>(List<T> insertList, string tableName = null, string dateFormat = "yyyy/M/d H:mm:ss") where T : class, new()
|
||||||
{
|
{
|
||||||
return BulkCopyAsync(insertList, dateFormat).GetAwaiter().GetResult();
|
return BulkCopyAsync(insertList, tableName, dateFormat).GetAwaiter().GetResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -280,7 +287,7 @@ namespace ThingsGateway.SqlSugar
|
|||||||
/// <param name="httpPort">HTTP端口</param>
|
/// <param name="httpPort">HTTP端口</param>
|
||||||
/// <param name="username">用户名</param>
|
/// <param name="username">用户名</param>
|
||||||
/// <param name="password">密码</param>
|
/// <param name="password">密码</param>
|
||||||
private void BindHost(string host, string httpPort, string username, string password)
|
private void BindHost(string host, int httpPort, string username, string password)
|
||||||
{
|
{
|
||||||
url = host;
|
url = host;
|
||||||
if (url.EndsWith('/'))
|
if (url.EndsWith('/'))
|
||||||
|
|||||||
@@ -2,9 +2,9 @@
|
|||||||
{
|
{
|
||||||
public static class QuestDbSqlSugarClientExtensions
|
public static class QuestDbSqlSugarClientExtensions
|
||||||
{
|
{
|
||||||
public static QuestDbRestAPI RestApi(this ISqlSugarClient db)
|
public static QuestDbRestAPI RestApi(this ISqlSugarClient db, int httpPort = 9000)
|
||||||
{
|
{
|
||||||
return new QuestDbRestAPI(db);
|
return new QuestDbRestAPI(db, httpPort);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -111,7 +111,7 @@ namespace ThingsGateway.SqlSugar
|
|||||||
/// <param name="context">SqlSugar提供者</param>
|
/// <param name="context">SqlSugar提供者</param>
|
||||||
/// <param name="dataRecord">数据记录器</param>
|
/// <param name="dataRecord">数据记录器</param>
|
||||||
/// <param name="fieldNames">字段名列表</param>
|
/// <param name="fieldNames">字段名列表</param>
|
||||||
public IDataReaderEntityBuilder(SqlSugarProvider context, IDataRecord dataRecord, List<string> fieldNames)
|
public IDataReaderEntityBuilder(SqlSugarProvider context, IDataRecord dataRecord, IEnumerable<string> fieldNames)
|
||||||
{
|
{
|
||||||
this.Context = context;
|
this.Context = context;
|
||||||
this.DataRecord = dataRecord;
|
this.DataRecord = dataRecord;
|
||||||
|
|||||||
@@ -679,7 +679,7 @@ namespace ThingsGateway.SqlSugar
|
|||||||
IDataReaderEntityBuilder<T> entytyList = this.Context.Utilities.GetReflectionInoCacheInstance().GetOrCreate(cacheKey, () =>
|
IDataReaderEntityBuilder<T> entytyList = this.Context.Utilities.GetReflectionInoCacheInstance().GetOrCreate(cacheKey, () =>
|
||||||
{
|
{
|
||||||
var cacheResult = new IDataReaderEntityBuilder<T>(this.Context, dr,
|
var cacheResult = new IDataReaderEntityBuilder<T>(this.Context, dr,
|
||||||
columns.Select(it => it.Item1).ToList()).CreateBuilder(typeof(T));
|
columns.Select(it => it.Item1)).CreateBuilder(typeof(T));
|
||||||
return cacheResult;
|
return cacheResult;
|
||||||
});
|
});
|
||||||
using (dr)
|
using (dr)
|
||||||
@@ -706,7 +706,7 @@ namespace ThingsGateway.SqlSugar
|
|||||||
IDataReaderEntityBuilder<T> entytyList = this.Context.Utilities.GetReflectionInoCacheInstance().GetOrCreate(cacheKey, () =>
|
IDataReaderEntityBuilder<T> entytyList = this.Context.Utilities.GetReflectionInoCacheInstance().GetOrCreate(cacheKey, () =>
|
||||||
{
|
{
|
||||||
var cacheResult = new IDataReaderEntityBuilder<T>(this.Context, dr,
|
var cacheResult = new IDataReaderEntityBuilder<T>(this.Context, dr,
|
||||||
columns.Select(it => it.Item1).ToList()).CreateBuilder(typeof(T));
|
columns.Select(it => it.Item1)).CreateBuilder(typeof(T));
|
||||||
return cacheResult;
|
return cacheResult;
|
||||||
});
|
});
|
||||||
if (cancellationToken.IsCancellationRequested) yield break;
|
if (cancellationToken.IsCancellationRequested) yield break;
|
||||||
@@ -743,7 +743,7 @@ namespace ThingsGateway.SqlSugar
|
|||||||
IDataReaderEntityBuilder<T> entytyList = this.Context.Utilities.GetReflectionInoCacheInstance().GetOrCreate(cacheKey, () =>
|
IDataReaderEntityBuilder<T> entytyList = this.Context.Utilities.GetReflectionInoCacheInstance().GetOrCreate(cacheKey, () =>
|
||||||
{
|
{
|
||||||
var cacheResult = new IDataReaderEntityBuilder<T>(this.Context, dr,
|
var cacheResult = new IDataReaderEntityBuilder<T>(this.Context, dr,
|
||||||
columns.Select(it => it.Item1).ToList()).CreateBuilder(typeof(T));
|
columns.Select(it => it.Item1)).CreateBuilder(typeof(T));
|
||||||
return cacheResult;
|
return cacheResult;
|
||||||
});
|
});
|
||||||
using (dr)
|
using (dr)
|
||||||
@@ -775,7 +775,7 @@ namespace ThingsGateway.SqlSugar
|
|||||||
IDataReaderEntityBuilder<T> entytyList = this.Context.Utilities.GetReflectionInoCacheInstance().GetOrCreate(cacheKey, () =>
|
IDataReaderEntityBuilder<T> entytyList = this.Context.Utilities.GetReflectionInoCacheInstance().GetOrCreate(cacheKey, () =>
|
||||||
{
|
{
|
||||||
var cacheResult = new IDataReaderEntityBuilder<T>(this.Context, dr,
|
var cacheResult = new IDataReaderEntityBuilder<T>(this.Context, dr,
|
||||||
columns.Select(it => it.Item1).ToList()).CreateBuilder(typeof(T));
|
columns.Select(it => it.Item1)).CreateBuilder(typeof(T));
|
||||||
return cacheResult;
|
return cacheResult;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,6 @@
|
|||||||
V Get<V>(string key);
|
V Get<V>(string key);
|
||||||
IEnumerable<string> GetAllKey<V>();
|
IEnumerable<string> GetAllKey<V>();
|
||||||
void Remove<V>(string key);
|
void Remove<V>(string key);
|
||||||
V GetOrCreate<V>(string cacheKey, Func<V> create, int cacheDurationInSeconds = int.MaxValue);
|
V GetOrCreate<V>(string cacheKey, Func<V> create, int cacheDurationInSeconds = 3600);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -31,7 +31,7 @@ namespace ThingsGateway.SqlSugar
|
|||||||
return ReflectionInoCore<V>.GetInstance().GetAllKey();
|
return ReflectionInoCore<V>.GetInstance().GetAllKey();
|
||||||
}
|
}
|
||||||
|
|
||||||
public V GetOrCreate<V>(string cacheKey, Func<V> create, int cacheDurationInSeconds = int.MaxValue)
|
public V GetOrCreate<V>(string cacheKey, Func<V> create, int cacheDurationInSeconds = 3600)
|
||||||
{
|
{
|
||||||
return ReflectionInoCore<V>.GetInstance().GetOrCreate(cacheKey, create);
|
return ReflectionInoCore<V>.GetInstance().GetOrCreate(cacheKey, create);
|
||||||
}
|
}
|
||||||
@@ -43,10 +43,13 @@ namespace ThingsGateway.SqlSugar
|
|||||||
}
|
}
|
||||||
public class ReflectionInoCore<V>
|
public class ReflectionInoCore<V>
|
||||||
{
|
{
|
||||||
private MemoryCache InstanceCache => MemoryCache.Instance;
|
private MemoryCache InstanceCache = new MemoryCache() { Expire = 180 };
|
||||||
private static ReflectionInoCore<V> _instance = null;
|
private static ReflectionInoCore<V> _instance = null;
|
||||||
private static readonly object _instanceLock = new object();
|
private static readonly object _instanceLock = new object();
|
||||||
private ReflectionInoCore() { }
|
private ReflectionInoCore()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public V this[string key]
|
public V this[string key]
|
||||||
{
|
{
|
||||||
@@ -86,7 +89,7 @@ namespace ThingsGateway.SqlSugar
|
|||||||
|
|
||||||
public void Add(string key, V value, int cacheDurationInSeconds)
|
public void Add(string key, V value, int cacheDurationInSeconds)
|
||||||
{
|
{
|
||||||
Check.ThrowNotSupportedException("ReflectionInoCache.Add(string key, V value, int cacheDurationInSeconds)");
|
this.InstanceCache.Add<V>(key, value, cacheDurationInSeconds);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Remove(string key)
|
public void Remove(string key)
|
||||||
@@ -107,9 +110,10 @@ namespace ThingsGateway.SqlSugar
|
|||||||
return this.InstanceCache.Keys;
|
return this.InstanceCache.Keys;
|
||||||
}
|
}
|
||||||
|
|
||||||
public V GetOrCreate(string cacheKey, Func<V> create)
|
public V GetOrCreate(string cacheKey, Func<V> create, int expire = 3600)
|
||||||
{
|
{
|
||||||
return InstanceCache.GetOrAdd<V>(cacheKey, (a) => create());
|
return InstanceCache.GetOrAdd<V>(cacheKey, (a) =>
|
||||||
|
create(), expire);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public static class ReflectionInoHelper
|
public static class ReflectionInoHelper
|
||||||
|
|||||||
@@ -447,6 +447,28 @@ namespace ThingsGateway.SqlSugar
|
|||||||
}
|
}
|
||||||
|
|
||||||
public override List<DbColumnInfo> GetColumnInfosByTableName(string tableName, bool isCache = true)
|
public override List<DbColumnInfo> GetColumnInfosByTableName(string tableName, bool isCache = true)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(tableName)) return new List<DbColumnInfo>();
|
||||||
|
string cacheKey = "QuestDB.GetColumnInfosByTableName." + this.SqlBuilder.GetNoTranslationColumnName(tableName).ToLower() + this.Context.CurrentConnectionConfig.ConfigId;
|
||||||
|
cacheKey = GetCacheKey(cacheKey);
|
||||||
|
|
||||||
|
if (isCache)
|
||||||
|
{
|
||||||
|
|
||||||
|
return this.Context.Utilities.GetReflectionInoCacheInstance().GetOrCreate(cacheKey, () =>
|
||||||
|
{
|
||||||
|
return GetColInfo(tableName);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return GetColInfo(tableName);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<DbColumnInfo> GetColInfo(string tableName)
|
||||||
{
|
{
|
||||||
var sql = String.Format(GetColumnInfosByTableNameSql, tableName);
|
var sql = String.Format(GetColumnInfosByTableNameSql, tableName);
|
||||||
List<DbColumnInfo> result = new List<DbColumnInfo>();
|
List<DbColumnInfo> result = new List<DbColumnInfo>();
|
||||||
|
|||||||
@@ -406,22 +406,24 @@ AND sql LIKE '%" + tableName + "%'");
|
|||||||
public override bool CreateDatabase(string databaseName, string databaseDirectory = null)
|
public override bool CreateDatabase(string databaseName, string databaseDirectory = null)
|
||||||
{
|
{
|
||||||
var connString = this.Context.CurrentConnectionConfig.ConnectionString;
|
var connString = this.Context.CurrentConnectionConfig.ConnectionString;
|
||||||
var path = Regex.Match(connString, @"[a-z,A-Z]\:\\.+\\").Value;
|
|
||||||
if (path.IsNullOrEmpty())
|
|
||||||
|
// 提取 Data Source=xxx(不管是绝对还是相对路径)
|
||||||
|
var match = Regex.Match(connString, @"(?i)Data\s+Source\s*=\s*(.+?)(;|$)");
|
||||||
|
if (match.Success)
|
||||||
{
|
{
|
||||||
path = Regex.Match(connString, @"\/.+\/").Value;
|
var filePath = match.Groups[1].Value.Trim(); // => ./DB/data.sqlite
|
||||||
}
|
var folderPath = Path.GetDirectoryName(filePath); // => ./DB
|
||||||
if (path.IsNullOrEmpty())
|
|
||||||
{
|
if (!folderPath.IsNullOrEmpty())
|
||||||
path = Regex.Match(connString, @"[a-z,A-Z]\:\\").Value;
|
|
||||||
}
|
|
||||||
if (!path.IsNullOrEmpty())
|
|
||||||
{
|
|
||||||
if (!FileHelper.IsExistDirectory(path))
|
|
||||||
{
|
{
|
||||||
FileHelper.CreateDirectory(path);
|
if (!FileHelper.IsExistDirectory(folderPath))
|
||||||
|
{
|
||||||
|
FileHelper.CreateDirectory(folderPath);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.Context.Ado.Connection.Open();
|
this.Context.Ado.Connection.Open();
|
||||||
this.Context.Ado.Connection.Close();
|
this.Context.Ado.Connection.Close();
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -717,8 +717,32 @@ namespace ThingsGateway.SqlSugar
|
|||||||
/// <returns>列信息列表</returns>
|
/// <returns>列信息列表</returns>
|
||||||
public override List<DbColumnInfo> GetColumnInfosByTableName(string tableName, bool isCache = true)
|
public override List<DbColumnInfo> GetColumnInfosByTableName(string tableName, bool isCache = true)
|
||||||
{
|
{
|
||||||
var sql = $"select * from {this.SqlBuilder.GetTranslationColumnName(tableName)} where 1=2 ";
|
|
||||||
|
if (string.IsNullOrEmpty(tableName)) return new List<DbColumnInfo>();
|
||||||
|
string cacheKey = "TDengine.GetColumnInfosByTableName." + this.SqlBuilder.GetNoTranslationColumnName(tableName).ToLower() + this.Context.CurrentConnectionConfig.ConfigId;
|
||||||
|
cacheKey = GetCacheKey(cacheKey);
|
||||||
|
|
||||||
|
if (isCache)
|
||||||
|
{
|
||||||
|
|
||||||
|
return this.Context.Utilities.GetReflectionInoCacheInstance().GetOrCreate(cacheKey, () =>
|
||||||
|
{
|
||||||
|
return GetColInfo(tableName);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return GetColInfo(tableName);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<DbColumnInfo> GetColInfo(string tableName)
|
||||||
|
{
|
||||||
List<DbColumnInfo> result = new List<DbColumnInfo>();
|
List<DbColumnInfo> result = new List<DbColumnInfo>();
|
||||||
|
|
||||||
|
var sql = $"select * from {this.SqlBuilder.GetTranslationColumnName(tableName)} where 1=2 ";
|
||||||
DataTable dt = null;
|
DataTable dt = null;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,14 +1,15 @@
|
|||||||
<Project>
|
<Project>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<PluginVersion>10.10.1</PluginVersion>
|
<PluginVersion>10.10.12</PluginVersion>
|
||||||
<ProPluginVersion>10.10.1</ProPluginVersion>
|
<ProPluginVersion>10.10.12</ProPluginVersion>
|
||||||
<DefaultVersion>10.10.1</DefaultVersion>
|
<DefaultVersion>10.10.15</DefaultVersion>
|
||||||
<AuthenticationVersion>2.9.29</AuthenticationVersion>
|
<AuthenticationVersion>10.10.1</AuthenticationVersion>
|
||||||
<SourceGeneratorVersion>10.9.29</SourceGeneratorVersion>
|
<SourceGeneratorVersion>10.10.1</SourceGeneratorVersion>
|
||||||
<NET8Version>8.0.18</NET8Version>
|
<NET8Version>8.0.19</NET8Version>
|
||||||
<NET9Version>9.0.7</NET9Version>
|
<NET9Version>9.0.8</NET9Version>
|
||||||
<SatelliteResourceLanguages>zh-Hans;en-US</SatelliteResourceLanguages>
|
<SatelliteResourceLanguages>zh-Hans;en-US</SatelliteResourceLanguages>
|
||||||
|
<IsTrimmable>false</IsTrimmable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<Project>
|
<Project>
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>net462;netstandard2.0;net6.0;</TargetFrameworks>
|
<TargetFrameworks>net462;netstandard2.0;net6.0;net8.0</TargetFrameworks>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -77,7 +77,8 @@ public partial class LogConsole : IDisposable
|
|||||||
|
|
||||||
[Inject]
|
[Inject]
|
||||||
private ToastService ToastService { get; set; }
|
private ToastService ToastService { get; set; }
|
||||||
|
[Inject]
|
||||||
|
ITextFileReadService TextFileReadService { get; set; }
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
Disposed = true;
|
Disposed = true;
|
||||||
@@ -94,7 +95,7 @@ public partial class LogConsole : IDisposable
|
|||||||
|
|
||||||
if (LogPath != null)
|
if (LogPath != null)
|
||||||
{
|
{
|
||||||
var files = TextFileReader.GetFiles(LogPath);
|
var files = await TextFileReadService.GetLogFilesAsync(LogPath);
|
||||||
if (!files.IsSuccess)
|
if (!files.IsSuccess)
|
||||||
{
|
{
|
||||||
Messages = new List<LogMessage>();
|
Messages = new List<LogMessage>();
|
||||||
@@ -105,7 +106,7 @@ public partial class LogConsole : IDisposable
|
|||||||
await Task.Run(async () =>
|
await Task.Run(async () =>
|
||||||
{
|
{
|
||||||
Stopwatch sw = Stopwatch.StartNew();
|
Stopwatch sw = Stopwatch.StartNew();
|
||||||
var result = TextFileReader.LastLog(files.Content.FirstOrDefault());
|
var result = await TextFileReadService.LastLogDataAsync(files.Content.FirstOrDefault());
|
||||||
if (result.IsSuccess)
|
if (result.IsSuccess)
|
||||||
{
|
{
|
||||||
Messages = result.Content.Where(a => a.LogLevel >= LogLevel).Select(a => new LogMessage((int)a.LogLevel, $"{a.LogTime} - {a.Message}{(a.ExceptionString.IsNullOrWhiteSpace() ? null : $"{Environment.NewLine}{a.ExceptionString}")}")).ToList();
|
Messages = result.Content.Where(a => a.LogLevel >= LogLevel).Select(a => new LogMessage((int)a.LogLevel, $"{a.LogTime} - {a.Message}{(a.ExceptionString.IsNullOrWhiteSpace() ? null : $"{Environment.NewLine}{a.ExceptionString}")}")).ToList();
|
||||||
@@ -143,7 +144,7 @@ public partial class LogConsole : IDisposable
|
|||||||
{
|
{
|
||||||
if (LogPath != null)
|
if (LogPath != null)
|
||||||
{
|
{
|
||||||
var files = TextFileReader.GetFiles(LogPath);
|
var files = await TextFileReadService.GetLogFilesAsync(LogPath);
|
||||||
if (files.IsSuccess)
|
if (files.IsSuccess)
|
||||||
{
|
{
|
||||||
foreach (var item in files.Content)
|
foreach (var item in files.Content)
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ public class PlatformService : IPlatformService
|
|||||||
|
|
||||||
public async Task OnLogExport(string logPath)
|
public async Task OnLogExport(string logPath)
|
||||||
{
|
{
|
||||||
var files = TextFileReader.GetFiles(logPath);
|
var files = TextFileReader.GetLogFilesAsync(logPath);
|
||||||
if (!files.IsSuccess)
|
if (!files.IsSuccess)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -33,5 +33,6 @@ public class Startup : AppStartup
|
|||||||
}
|
}
|
||||||
|
|
||||||
services.AddScoped<IPlatformService, PlatformService>();
|
services.AddScoped<IPlatformService, PlatformService>();
|
||||||
|
services.AddSingleton<ITextFileReadService, TextFileReadService>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ public class OtherChannel : SetupConfigObject, IClientChannel
|
|||||||
public OtherChannel(IChannelOptions channelOptions)
|
public OtherChannel(IChannelOptions channelOptions)
|
||||||
{
|
{
|
||||||
ChannelOptions = channelOptions;
|
ChannelOptions = channelOptions;
|
||||||
|
ResetSign();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override TouchSocketConfig Config => base.Config ?? ChannelOptions.Config;
|
public override TouchSocketConfig Config => base.Config ?? ChannelOptions.Config;
|
||||||
@@ -39,7 +40,7 @@ public class OtherChannel : SetupConfigObject, IClientChannel
|
|||||||
pool?.SafeDispose();
|
pool?.SafeDispose();
|
||||||
}
|
}
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public ChannelReceivedEventHandler ChannelReceived { get; set; } = new();
|
public ChannelReceivedEventHandler ChannelReceived { get; } = new();
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public IChannelOptions ChannelOptions { get; }
|
public IChannelOptions ChannelOptions { get; }
|
||||||
@@ -51,16 +52,16 @@ public class OtherChannel : SetupConfigObject, IClientChannel
|
|||||||
public ConcurrentList<IDevice> Collects { get; } = new();
|
public ConcurrentList<IDevice> Collects { get; } = new();
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public ChannelEventHandler Started { get; set; } = new();
|
public ChannelEventHandler Started { get; } = new();
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public ChannelEventHandler Starting { get; set; } = new();
|
public ChannelEventHandler Starting { get; } = new();
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public ChannelEventHandler Stoped { get; set; } = new();
|
public ChannelEventHandler Stoped { get; } = new();
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public ChannelEventHandler Stoping { get; set; } = new();
|
public ChannelEventHandler Stoping { get; } = new();
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 等待池
|
/// 等待池
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ public class SerialPortChannel : SerialPortClient, IClientChannel
|
|||||||
public SerialPortChannel(IChannelOptions channelOptions)
|
public SerialPortChannel(IChannelOptions channelOptions)
|
||||||
{
|
{
|
||||||
ChannelOptions = channelOptions;
|
ChannelOptions = channelOptions;
|
||||||
|
ResetSign();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override TouchSocketConfig Config => base.Config ?? ChannelOptions.Config;
|
public override TouchSocketConfig Config => base.Config ?? ChannelOptions.Config;
|
||||||
@@ -36,7 +37,7 @@ public class SerialPortChannel : SerialPortClient, IClientChannel
|
|||||||
pool?.SafeDispose();
|
pool?.SafeDispose();
|
||||||
}
|
}
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public ChannelReceivedEventHandler ChannelReceived { get; set; } = new();
|
public ChannelReceivedEventHandler ChannelReceived { get; } = new();
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public IChannelOptions ChannelOptions { get; }
|
public IChannelOptions ChannelOptions { get; }
|
||||||
@@ -51,16 +52,16 @@ public class SerialPortChannel : SerialPortClient, IClientChannel
|
|||||||
public DataHandlingAdapter ReadOnlyDataHandlingAdapter => ProtectedDataHandlingAdapter;
|
public DataHandlingAdapter ReadOnlyDataHandlingAdapter => ProtectedDataHandlingAdapter;
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public ChannelEventHandler Started { get; set; } = new();
|
public ChannelEventHandler Started { get; } = new();
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public ChannelEventHandler Starting { get; set; } = new();
|
public ChannelEventHandler Starting { get; } = new();
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public ChannelEventHandler Stoped { get; set; } = new();
|
public ChannelEventHandler Stoped { get; } = new();
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public ChannelEventHandler Stoping { get; set; } = new();
|
public ChannelEventHandler Stoping { get; } = new();
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 等待池
|
/// 等待池
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ public class TcpClientChannel : TcpClient, IClientChannel
|
|||||||
public TcpClientChannel(IChannelOptions channelOptions)
|
public TcpClientChannel(IChannelOptions channelOptions)
|
||||||
{
|
{
|
||||||
ChannelOptions = channelOptions;
|
ChannelOptions = channelOptions;
|
||||||
|
ResetSign();
|
||||||
}
|
}
|
||||||
public override TouchSocketConfig Config => base.Config ?? ChannelOptions.Config;
|
public override TouchSocketConfig Config => base.Config ?? ChannelOptions.Config;
|
||||||
public void ResetSign(int minSign = 0, int maxSign = ushort.MaxValue)
|
public void ResetSign(int minSign = 0, int maxSign = ushort.MaxValue)
|
||||||
|
|||||||
@@ -134,6 +134,7 @@ public abstract class TcpServiceChannelBase<TClient> : TcpService<TClient>, ITcp
|
|||||||
|
|
||||||
protected override void SafetyDispose(bool disposing)
|
protected override void SafetyDispose(bool disposing)
|
||||||
{
|
{
|
||||||
|
m_transport?.SafeCancel();
|
||||||
m_transport?.SafeDispose();
|
m_transport?.SafeDispose();
|
||||||
base.SafetyDispose(disposing);
|
base.SafetyDispose(disposing);
|
||||||
}
|
}
|
||||||
@@ -179,7 +180,7 @@ public class TcpServiceChannel<TClient> : TcpServiceChannelBase<TClient>, IChann
|
|||||||
public override TouchSocketConfig Config => base.Config ?? ChannelOptions.Config;
|
public override TouchSocketConfig Config => base.Config ?? ChannelOptions.Config;
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public ChannelReceivedEventHandler ChannelReceived { get; set; } = new();
|
public ChannelReceivedEventHandler ChannelReceived { get; } = new();
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public IChannelOptions ChannelOptions { get; }
|
public IChannelOptions ChannelOptions { get; }
|
||||||
@@ -191,15 +192,15 @@ public class TcpServiceChannel<TClient> : TcpServiceChannelBase<TClient>, IChann
|
|||||||
public bool Online => ServerState == ServerState.Running;
|
public bool Online => ServerState == ServerState.Running;
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public ChannelEventHandler Started { get; set; } = new();
|
public ChannelEventHandler Started { get; } = new();
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public ChannelEventHandler Starting { get; set; } = new();
|
public ChannelEventHandler Starting { get; } = new();
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public ChannelEventHandler Stoped { get; set; } = new();
|
public ChannelEventHandler Stoped { get; } = new();
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public ChannelEventHandler Stoping { get; set; } = new();
|
public ChannelEventHandler Stoping { get; } = new();
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public Task<Result> CloseAsync(string msg, CancellationToken token)
|
public Task<Result> CloseAsync(string msg, CancellationToken token)
|
||||||
@@ -227,8 +228,8 @@ public class TcpServiceChannel<TClient> : TcpServiceChannelBase<TClient>, IChann
|
|||||||
data.ResetSign(MinSign, MaxSign);
|
data.ResetSign(MinSign, MaxSign);
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
public int MaxSign { get; set; }
|
public int MaxSign { get; private set; } = 0;
|
||||||
public int MinSign { get; set; }
|
public int MinSign { get; private set; } = ushort.MaxValue;
|
||||||
public void ResetSign(int minSign = 0, int maxSign = ushort.MaxValue)
|
public void ResetSign(int minSign = 0, int maxSign = ushort.MaxValue)
|
||||||
{
|
{
|
||||||
MinSign = minSign;
|
MinSign = minSign;
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ public class TcpSessionClientChannel : TcpSessionClient, IClientChannel
|
|||||||
pool?.SafeDispose();
|
pool?.SafeDispose();
|
||||||
}
|
}
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public ChannelReceivedEventHandler ChannelReceived { get; set; } = new();
|
public ChannelReceivedEventHandler ChannelReceived { get; } = new();
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public IChannelOptions ChannelOptions { get; internal set; }
|
public IChannelOptions ChannelOptions { get; internal set; }
|
||||||
@@ -47,15 +47,15 @@ public class TcpSessionClientChannel : TcpSessionClient, IClientChannel
|
|||||||
public DataHandlingAdapter ReadOnlyDataHandlingAdapter => DataHandlingAdapter;
|
public DataHandlingAdapter ReadOnlyDataHandlingAdapter => DataHandlingAdapter;
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public ChannelEventHandler Started { get; set; } = new();
|
public ChannelEventHandler Started { get; } = new();
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public ChannelEventHandler Starting { get; set; } = new();
|
public ChannelEventHandler Starting { get; } = new();
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public ChannelEventHandler Stoped { get; set; } = new();
|
public ChannelEventHandler Stoped { get; } = new();
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public ChannelEventHandler Stoping { get; set; } = new();
|
public ChannelEventHandler Stoping { get; } = new();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 等待池
|
/// 等待池
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ public class UdpSessionChannel : UdpSession, IClientChannel
|
|||||||
public UdpSessionChannel(IChannelOptions channelOptions)
|
public UdpSessionChannel(IChannelOptions channelOptions)
|
||||||
{
|
{
|
||||||
ChannelOptions = channelOptions;
|
ChannelOptions = channelOptions;
|
||||||
|
ResetSign();
|
||||||
}
|
}
|
||||||
public override TouchSocketConfig Config => base.Config ?? ChannelOptions.Config;
|
public override TouchSocketConfig Config => base.Config ?? ChannelOptions.Config;
|
||||||
|
|
||||||
@@ -37,7 +38,7 @@ public class UdpSessionChannel : UdpSession, IClientChannel
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public ChannelReceivedEventHandler ChannelReceived { get; set; } = new();
|
public ChannelReceivedEventHandler ChannelReceived { get; } = new();
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public IChannelOptions ChannelOptions { get; }
|
public IChannelOptions ChannelOptions { get; }
|
||||||
@@ -55,15 +56,15 @@ public class UdpSessionChannel : UdpSession, IClientChannel
|
|||||||
public DataHandlingAdapter ReadOnlyDataHandlingAdapter => DataHandlingAdapter;
|
public DataHandlingAdapter ReadOnlyDataHandlingAdapter => DataHandlingAdapter;
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public ChannelEventHandler Started { get; set; } = new();
|
public ChannelEventHandler Started { get; } = new();
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public ChannelEventHandler Starting { get; set; } = new();
|
public ChannelEventHandler Starting { get; } = new();
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public ChannelEventHandler Stoped { get; set; } = new();
|
public ChannelEventHandler Stoped { get; } = new();
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public ChannelEventHandler Stoping { get; set; } = new();
|
public ChannelEventHandler Stoping { get; } = new();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 等待池
|
/// 等待池
|
||||||
@@ -204,6 +205,7 @@ public class UdpSessionChannel : UdpSession, IClientChannel
|
|||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
protected override void SafetyDispose(bool disposing)
|
protected override void SafetyDispose(bool disposing)
|
||||||
{
|
{
|
||||||
|
m_transport?.SafeCancel();
|
||||||
m_transport?.SafeDispose();
|
m_transport?.SafeDispose();
|
||||||
WaitHandlePool.SafeDispose();
|
WaitHandlePool.SafeDispose();
|
||||||
base.SafetyDispose(disposing);
|
base.SafetyDispose(disposing);
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ namespace ThingsGateway.Foundation;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 协议基类
|
/// 协议基类
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract class DeviceBase : DisposableObject, IDevice
|
public abstract class DeviceBase : AsyncAndSyncDisposableObject, IDevice
|
||||||
{
|
{
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public IChannel Channel { get; private set; }
|
public IChannel Channel { get; private set; }
|
||||||
@@ -553,11 +553,11 @@ public abstract class DeviceBase : DisposableObject, IDevice
|
|||||||
WaitLock? waitLock = null;
|
WaitLock? waitLock = null;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
await BefortSendAsync(clientChannel, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
var dtuId = this is IDtu dtu1 ? dtu1.DtuId : null;
|
var dtuId = this is IDtu dtu1 ? dtu1.DtuId : null;
|
||||||
waitLock = GetWaitLock(clientChannel, dtuId);
|
waitLock = GetWaitLock(clientChannel, dtuId);
|
||||||
|
|
||||||
await BefortSendAsync(clientChannel, cancellationToken).ConfigureAwait(false);
|
|
||||||
|
|
||||||
await waitLock.WaitAsync(cancellationToken).ConfigureAwait(false);
|
await waitLock.WaitAsync(cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
EndPoint? endPoint = GetUdpEndpoint(dtuId);
|
EndPoint? endPoint = GetUdpEndpoint(dtuId);
|
||||||
@@ -585,13 +585,6 @@ public abstract class DeviceBase : DisposableObject, IDevice
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
if (cancellationToken.IsCancellationRequested)
|
|
||||||
{
|
|
||||||
if (!this.DisposedValue)
|
|
||||||
{
|
|
||||||
await Task.Delay(timeout, Channel.ClosedToken).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return new MessageBase(ex);
|
return new MessageBase(ex);
|
||||||
}
|
}
|
||||||
var result = waitData.Check();
|
var result = waitData.Check();
|
||||||
@@ -1041,6 +1034,56 @@ public abstract class DeviceBase : DisposableObject, IDevice
|
|||||||
base.Dispose(disposing);
|
base.Dispose(disposing);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
protected override async Task DisposeAsync(bool disposing)
|
||||||
|
{
|
||||||
|
if (Channel != null)
|
||||||
|
{
|
||||||
|
Channel.Starting.Remove(ChannelStarting);
|
||||||
|
Channel.Stoped.Remove(ChannelStoped);
|
||||||
|
Channel.Started.Remove(ChannelStarted);
|
||||||
|
Channel.Stoping.Remove(ChannelStoping);
|
||||||
|
Channel.ChannelReceived.Remove(ChannelReceived);
|
||||||
|
|
||||||
|
if (Channel.Collects.Count == 1)
|
||||||
|
{
|
||||||
|
if (Channel is ITcpServiceChannel tcpServiceChannel)
|
||||||
|
{
|
||||||
|
tcpServiceChannel.Clients.ForEach(a => a.WaitHandlePool.SafeDispose());
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
//只关闭,不释放
|
||||||
|
await Channel.CloseAsync().ConfigureAwait(false);
|
||||||
|
if (Channel is IClientChannel client)
|
||||||
|
{
|
||||||
|
client.WaitHandlePool.SafeDispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logger?.LogWarning(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (Channel is ITcpServiceChannel tcpServiceChannel && this is IDtu dtu)
|
||||||
|
{
|
||||||
|
if (tcpServiceChannel.TryGetClient($"ID={dtu.DtuId}", out var client))
|
||||||
|
{
|
||||||
|
client.WaitHandlePool?.SafeDispose();
|
||||||
|
await client.CloseAsync().ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Channel.Collects.Remove(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
_deviceLogger?.TryDispose();
|
||||||
|
base.Dispose(disposing);
|
||||||
|
}
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public virtual Action<IPluginManager> ConfigurePlugins(TouchSocketConfig config)
|
public virtual Action<IPluginManager> ConfigurePlugins(TouchSocketConfig config)
|
||||||
{
|
{
|
||||||
@@ -1058,4 +1101,5 @@ public abstract class DeviceBase : DisposableObject, IDevice
|
|||||||
return a => { };
|
return a => { };
|
||||||
}
|
}
|
||||||
public abstract ValueTask<OperResult<ReadOnlyMemory<byte>>> ReadAsync(object state, CancellationToken cancellationToken = default);
|
public abstract ValueTask<OperResult<ReadOnlyMemory<byte>>> ReadAsync(object state, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ namespace ThingsGateway.Foundation;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 协议设备接口
|
/// 协议设备接口
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface IDevice : IDisposable, IDisposableObject
|
public interface IDevice : IDisposable, IDisposableObject, IAsyncDisposable
|
||||||
{
|
{
|
||||||
#region 属性
|
#region 属性
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
// QQ群:605534569
|
// QQ群:605534569
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
using System.Buffers;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace ThingsGateway.Foundation;
|
namespace ThingsGateway.Foundation;
|
||||||
@@ -235,5 +236,28 @@ public static class ByteBlockExtension
|
|||||||
}
|
}
|
||||||
#endregion AsSegment
|
#endregion AsSegment
|
||||||
|
|
||||||
|
#region ToString
|
||||||
|
|
||||||
|
|
||||||
|
public static string ToString<TByteBlock>(this TByteBlock byteBlock, long offset, long length) where TByteBlock : IBytesReader
|
||||||
|
{
|
||||||
|
return byteBlock.TotalSequence.Slice(offset, length).ToString(Encoding.UTF8);
|
||||||
|
}
|
||||||
|
public static string ToString(this ReadOnlySequence<byte> byteBlock, Encoding encoding)
|
||||||
|
{
|
||||||
|
# if NET6_0_OR_GREATER
|
||||||
|
return encoding.GetString(byteBlock);
|
||||||
|
#else
|
||||||
|
using ContiguousMemoryBuffer contiguousMemoryBuffer = new(byteBlock);
|
||||||
|
return contiguousMemoryBuffer.Memory.Span.ToString(encoding);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public static string ToString<TByteBlock>(this TByteBlock byteBlock, long offset) where TByteBlock : IBytesReader
|
||||||
|
{
|
||||||
|
return ToString(byteBlock, offset, byteBlock.BytesRead + byteBlock.BytesRemaining - offset);
|
||||||
|
|
||||||
|
}
|
||||||
|
#endregion ToString
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
// ------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://thingsgateway.cn/
|
||||||
|
// QQ群:605534569
|
||||||
|
// ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
namespace ThingsGateway;
|
||||||
|
|
||||||
|
|
||||||
|
public static class DisposableExtensions
|
||||||
|
{
|
||||||
|
#region IDisposable
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 安全性释放(不用判断对象是否为空)。不会抛出任何异常。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dis"></param>
|
||||||
|
/// <returns>释放状态,当对象为<see langword="null"/>,或者已被释放时,均会返回<see cref="Result.Success"/>,只有实际在释放时遇到异常时,才显示其他状态。</returns>
|
||||||
|
public static async Task<Result> SafeDisposeAsync(this IAsyncDisposable dis)
|
||||||
|
{
|
||||||
|
if (dis == default)
|
||||||
|
{
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await dis.DisposeAsync().ConfigureAwait(false);
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return Result.FromException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion IDisposable
|
||||||
|
|
||||||
|
|
||||||
|
#if NET8_0_OR_GREATER
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 安全地取消 <see cref="CancellationTokenSource"/>,并返回操作结果。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="tokenSource">要取消的 <see cref="CancellationTokenSource"/>。</param>
|
||||||
|
/// <returns>一个 <see cref="Result"/> 对象,表示操作的结果。</returns>
|
||||||
|
public static async Task<Result> SafeCancelAsync(this CancellationTokenSource tokenSource)
|
||||||
|
{
|
||||||
|
if (tokenSource is null)
|
||||||
|
{
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await tokenSource.CancelAsync().ConfigureAwait(false);
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
catch (ObjectDisposedException)
|
||||||
|
{
|
||||||
|
return Result.Disposed;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return Result.FromException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
}
|
||||||
@@ -9,6 +9,7 @@
|
|||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
namespace ThingsGateway.Foundation;
|
namespace ThingsGateway.Foundation;
|
||||||
|
#pragma warning disable CA1851
|
||||||
|
|
||||||
public static class PackHelpers
|
public static class PackHelpers
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ public static class TextFileReader
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="directoryPath">目录路径</param>
|
/// <param name="directoryPath">目录路径</param>
|
||||||
/// <returns>包含文件信息的列表</returns>
|
/// <returns>包含文件信息的列表</returns>
|
||||||
public static OperResult<List<string>> GetFiles(string directoryPath)
|
public static OperResult<List<string>> GetLogFilesAsync(string directoryPath)
|
||||||
{
|
{
|
||||||
OperResult<List<string>> result = new(); // 初始化结果对象
|
OperResult<List<string>> result = new(); // 初始化结果对象
|
||||||
// 检查目录是否存在
|
// 检查目录是否存在
|
||||||
@@ -91,7 +91,7 @@ public static class TextFileReader
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static OperResult<List<LogData>> LastLog(string file, int lineCount = 200)
|
public static OperResult<List<LogData>> LastLogDataAsync(string file, int lineCount = 200)
|
||||||
{
|
{
|
||||||
if (!File.Exists(file))
|
if (!File.Exists(file))
|
||||||
return new OperResult<List<LogData>>("The file path is invalid");
|
return new OperResult<List<LogData>>("The file path is invalid");
|
||||||
@@ -104,7 +104,7 @@ public static class TextFileReader
|
|||||||
{
|
{
|
||||||
var fileInfo = new FileInfo(file);
|
var fileInfo = new FileInfo(file);
|
||||||
var length = fileInfo.Length;
|
var length = fileInfo.Length;
|
||||||
var cacheKey = $"{nameof(TextFileReader)}_{nameof(LastLog)}_{file})";
|
var cacheKey = $"{nameof(TextFileReader)}_{nameof(LastLogDataAsync)}_{file})";
|
||||||
if (_cache.TryGetValue<LogDataCache>(cacheKey, out var cachedData))
|
if (_cache.TryGetValue<LogDataCache>(cacheKey, out var cachedData))
|
||||||
{
|
{
|
||||||
if (cachedData != null && cachedData.Length == length)
|
if (cachedData != null && cachedData.Length == length)
|
||||||
|
|||||||
@@ -15,6 +15,27 @@ namespace ThingsGateway.Foundation;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static class OperResultExtension
|
public static class OperResultExtension
|
||||||
{
|
{
|
||||||
|
|
||||||
|
public static OperResult<object> GetOperResult(this IOperResult data)
|
||||||
|
{
|
||||||
|
OperResult<object> result = new(data);
|
||||||
|
var operResultType = typeof(IOperResult<>);
|
||||||
|
var interfaceType = data.GetType().GetInterfaces()
|
||||||
|
.FirstOrDefault(x => x.IsGenericType && x.GetGenericTypeDefinition() == operResultType);
|
||||||
|
|
||||||
|
if (interfaceType != null)
|
||||||
|
{
|
||||||
|
var contentProperty = interfaceType.GetProperty("Content");
|
||||||
|
if (contentProperty != null)
|
||||||
|
{
|
||||||
|
result.Content = contentProperty.GetValue(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 转换对应类型
|
/// 转换对应类型
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://thingsgateway.cn/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
namespace ThingsGateway.Foundation;
|
||||||
|
|
||||||
|
public interface ITextFileReadService
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 获取指定目录下所有文件信息
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="directoryPath">目录路径</param>
|
||||||
|
/// <returns>包含文件信息的列表</returns>
|
||||||
|
public Task<OperResult<List<string>>> GetLogFilesAsync(string directoryPath);
|
||||||
|
|
||||||
|
public Task<OperResult<List<LogData>>> LastLogDataAsync(string file, int lineCount = 200);
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://thingsgateway.cn/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
namespace ThingsGateway.Foundation;
|
||||||
|
|
||||||
|
public class TextFileReadService : ITextFileReadService
|
||||||
|
{
|
||||||
|
public Task<OperResult<List<string>>> GetLogFilesAsync(string directoryPath) => Task.FromResult(TextFileReader.GetLogFilesAsync(directoryPath));
|
||||||
|
|
||||||
|
public Task<OperResult<List<LogData>>> LastLogDataAsync(string file, int lineCount = 200) => Task.FromResult(TextFileReader.LastLogDataAsync(file, lineCount));
|
||||||
|
}
|
||||||
@@ -0,0 +1,109 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权(除特别声明或在XREF结尾的命名空间的代码)归作者本人若汝棋茗所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权
|
||||||
|
// CSDN博客:https://blog.csdn.net/qq_40374647
|
||||||
|
// 哔哩哔哩视频:https://space.bilibili.com/94253567
|
||||||
|
// Gitee源代码仓库:https://gitee.com/RRQM_Home
|
||||||
|
// Github源代码仓库:https://github.com/RRQM
|
||||||
|
// API首页:https://touchsocket.net/
|
||||||
|
// 交流QQ群:234762506
|
||||||
|
// 感谢您的下载和使用
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
namespace ThingsGateway.Foundation;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 具有释放的对象。内部实现了<see cref="GC.SuppressFinalize(object)"/>,但不包括析构函数相关。
|
||||||
|
/// </summary>
|
||||||
|
public abstract partial class AsyncAndSyncDisposableObject :
|
||||||
|
IDisposableObject,
|
||||||
|
IAsyncDisposable
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 判断当前对象是否已经被释放。
|
||||||
|
/// 如果已经被释放,则抛出<see cref="ObjectDisposedException"/>异常。
|
||||||
|
/// </summary>
|
||||||
|
/// <exception cref="ObjectDisposedException">当对象已经被释放时抛出此异常</exception>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
protected void ThrowIfDisposed()
|
||||||
|
{
|
||||||
|
// 检查对象是否已经被释放
|
||||||
|
if (this.m_disposedValue)
|
||||||
|
{
|
||||||
|
// 如果对象已被释放,抛出ObjectDisposedException异常
|
||||||
|
throw new ObjectDisposedException($"The object instance with type {this.GetType().FullName} has been released");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int m_count = 0;
|
||||||
|
private int m_asyncCount = 0;
|
||||||
|
/// <summary>
|
||||||
|
/// 判断是否已释放。
|
||||||
|
/// </summary>
|
||||||
|
private volatile bool m_disposedValue;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public bool DisposedValue => this.m_disposedValue;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 处置资源
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="disposing">一个值,表示是否释放托管资源</param>
|
||||||
|
protected virtual void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
// 标记当前对象为已处置状态
|
||||||
|
this.m_disposedValue = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 释放资源。内部已经处理了<see cref="GC.SuppressFinalize(object)"/>
|
||||||
|
/// </summary>
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (this.DisposedValue)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Interlocked.Increment(ref this.m_count) == 1)
|
||||||
|
{
|
||||||
|
this.Dispose(disposing: true);
|
||||||
|
}
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 处置资源
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="disposing">一个值,表示是否释放托管资源</param>
|
||||||
|
protected virtual Task DisposeAsync(bool disposing)
|
||||||
|
{
|
||||||
|
// 标记当前对象为已处置状态
|
||||||
|
this.m_disposedValue = true;
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 释放资源。内部已经处理了<see cref="GC.SuppressFinalize(object)"/>
|
||||||
|
/// </summary>
|
||||||
|
public async ValueTask DisposeAsync()
|
||||||
|
{
|
||||||
|
if (this.DisposedValue)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//if (Interlocked.Increment(ref this.m_count) == 1)
|
||||||
|
//{
|
||||||
|
// this.Dispose(disposing: true);
|
||||||
|
//}
|
||||||
|
|
||||||
|
if (Interlocked.Increment(ref this.m_asyncCount) == 1)
|
||||||
|
{
|
||||||
|
await this.DisposeAsync(disposing: true).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,108 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权(除特别声明或在XREF结尾的命名空间的代码)归作者本人若汝棋茗所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权
|
||||||
|
// CSDN博客:https://blog.csdn.net/qq_40374647
|
||||||
|
// 哔哩哔哩视频:https://space.bilibili.com/94253567
|
||||||
|
// Gitee源代码仓库:https://gitee.com/RRQM_Home
|
||||||
|
// Github源代码仓库:https://github.com/RRQM
|
||||||
|
// API首页:https://touchsocket.net/
|
||||||
|
// 交流QQ群:234762506
|
||||||
|
// 感谢您的下载和使用
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
namespace ThingsGateway.Foundation;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 具有释放的对象。内部实现了<see cref="GC.SuppressFinalize(object)"/>,但不包括析构函数相关。
|
||||||
|
/// </summary>
|
||||||
|
public abstract partial class AsyncDisposableObject :
|
||||||
|
//IDisposableObject,
|
||||||
|
IAsyncDisposable
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 判断当前对象是否已经被释放。
|
||||||
|
/// 如果已经被释放,则抛出<see cref="ObjectDisposedException"/>异常。
|
||||||
|
/// </summary>
|
||||||
|
/// <exception cref="ObjectDisposedException">当对象已经被释放时抛出此异常</exception>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
protected void ThrowIfDisposed()
|
||||||
|
{
|
||||||
|
// 检查对象是否已经被释放
|
||||||
|
if (this.m_disposedValue)
|
||||||
|
{
|
||||||
|
// 如果对象已被释放,抛出ObjectDisposedException异常
|
||||||
|
throw new ObjectDisposedException($"The object instance with type {this.GetType().FullName} has been released");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int m_asyncCount = 0;
|
||||||
|
/// <summary>
|
||||||
|
/// 判断是否已释放。
|
||||||
|
/// </summary>
|
||||||
|
private volatile bool m_disposedValue;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public bool DisposedValue => this.m_disposedValue;
|
||||||
|
|
||||||
|
///// <summary>
|
||||||
|
///// 处置资源
|
||||||
|
///// </summary>
|
||||||
|
///// <param name="disposing">一个值,表示是否释放托管资源</param>
|
||||||
|
//protected virtual void Dispose(bool disposing)
|
||||||
|
//{
|
||||||
|
// // 标记当前对象为已处置状态
|
||||||
|
// this.m_disposedValue = true;
|
||||||
|
//}
|
||||||
|
|
||||||
|
///// <summary>
|
||||||
|
///// 释放资源。内部已经处理了<see cref="GC.SuppressFinalize(object)"/>
|
||||||
|
///// </summary>
|
||||||
|
//public void Dispose()
|
||||||
|
//{
|
||||||
|
// if (this.DisposedValue)
|
||||||
|
// {
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (Interlocked.Increment(ref this.m_count) == 1)
|
||||||
|
// {
|
||||||
|
// this.Dispose(disposing: true);
|
||||||
|
// }
|
||||||
|
// GC.SuppressFinalize(this);
|
||||||
|
//}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 处置资源
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="disposing">一个值,表示是否释放托管资源</param>
|
||||||
|
protected virtual Task DisposeAsync(bool disposing)
|
||||||
|
{
|
||||||
|
// 标记当前对象为已处置状态
|
||||||
|
this.m_disposedValue = true;
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 释放资源。内部已经处理了<see cref="GC.SuppressFinalize(object)"/>
|
||||||
|
/// </summary>
|
||||||
|
public async ValueTask DisposeAsync()
|
||||||
|
{
|
||||||
|
if (this.DisposedValue)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//if (Interlocked.Increment(ref this.m_count) == 1)
|
||||||
|
//{
|
||||||
|
// this.Dispose(disposing: true);
|
||||||
|
//}
|
||||||
|
|
||||||
|
if (Interlocked.Increment(ref this.m_asyncCount) == 1)
|
||||||
|
{
|
||||||
|
await this.DisposeAsync(disposing: true).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -33,6 +33,8 @@ public class AsyncReadWriteLock
|
|||||||
{
|
{
|
||||||
Interlocked.Increment(ref _readerCount);
|
Interlocked.Increment(ref _readerCount);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// 第一个读者需要获取写入锁,防止写操作
|
// 第一个读者需要获取写入锁,防止写操作
|
||||||
await _readerLock.WaitOneAsync(cancellationToken).ConfigureAwait(false);
|
await _readerLock.WaitOneAsync(cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
@@ -54,42 +56,47 @@ public class AsyncReadWriteLock
|
|||||||
{
|
{
|
||||||
var cancellationTokenSource = _cancellationTokenSource;
|
var cancellationTokenSource = _cancellationTokenSource;
|
||||||
_cancellationTokenSource = new();
|
_cancellationTokenSource = new();
|
||||||
await cancellationTokenSource.CancelAsync().ConfigureAwait(false); // 取消读取
|
await cancellationTokenSource.SafeCancelAsync().ConfigureAwait(false); // 取消读取
|
||||||
cancellationTokenSource.SafeDispose();
|
cancellationTokenSource.SafeDispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Writer(this);
|
return new Writer(this);
|
||||||
}
|
}
|
||||||
|
private object lockObject = new();
|
||||||
private void ReleaseWriter()
|
private void ReleaseWriter()
|
||||||
{
|
{
|
||||||
|
|
||||||
var writerCount = Interlocked.Decrement(ref _writerCount);
|
var writerCount = Interlocked.Decrement(ref _writerCount);
|
||||||
|
|
||||||
|
// 每次释放写时,总是唤醒至少一个读
|
||||||
|
_readerLock.Set();
|
||||||
|
|
||||||
if (writerCount == 0)
|
if (writerCount == 0)
|
||||||
{
|
{
|
||||||
var resetEvent = _readerLock;
|
var resetEvent = _readerLock;
|
||||||
_readerLock = new(false);
|
//_readerLock = new(false);
|
||||||
Interlocked.Exchange(ref _writeSinceLastReadCount, 0);
|
Interlocked.Exchange(ref _writeSinceLastReadCount, 0);
|
||||||
resetEvent.SetAll();
|
resetEvent.SetAll();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
lock (lockObject)
|
||||||
// 读写占空比, 用于控制写操作与读操作的比率。该比率 n 次写入操作会执行一次读取操作。即使在应用程序执行大量的连续写入操作时,也必须确保足够的读取数据处理时间。相对于更加均衡的读写数据流而言,该特点使得外部写入可连续无顾忌操作
|
|
||||||
|
|
||||||
if (_writeReadRatio > 0)
|
|
||||||
{
|
{
|
||||||
if (Interlocked.Read(ref _readerCount) > 0)
|
|
||||||
|
// 读写占空比, 用于控制写操作与读操作的比率。该比率 n 次写入操作会执行一次读取操作。即使在应用程序执行大量的连续写入操作时,也必须确保足够的读取数据处理时间。相对于更加均衡的读写数据流而言,该特点使得外部写入可连续无顾忌操作
|
||||||
|
if (_writeReadRatio > 0)
|
||||||
{
|
{
|
||||||
var count = Interlocked.Increment(ref _writeSinceLastReadCount);
|
var count = Interlocked.Increment(ref _writeSinceLastReadCount);
|
||||||
if (count >= _writeReadRatio)
|
if (count >= _writeReadRatio)
|
||||||
{
|
{
|
||||||
Interlocked.Exchange(ref _writeSinceLastReadCount, 0);
|
Interlocked.Exchange(ref _writeSinceLastReadCount, 0);
|
||||||
_readerLock.Set();
|
//_readerLock.Set();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
else
|
||||||
else
|
{
|
||||||
{
|
//_readerLock.Set();
|
||||||
_readerLock.Set();
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,57 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://thingsgateway.cn/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
namespace ThingsGateway.Gateway.Application;
|
||||||
|
|
||||||
|
public class LinkedCancellationTokenSourceCache : IDisposable
|
||||||
|
{
|
||||||
|
private CancellationTokenSource? _cachedCts;
|
||||||
|
private CancellationToken _token1;
|
||||||
|
private CancellationToken _token2;
|
||||||
|
private readonly object _lock = new();
|
||||||
|
~LinkedCancellationTokenSourceCache()
|
||||||
|
{
|
||||||
|
Dispose();
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 获取一个 CancellationTokenSource,它是由两个 token 链接而成的。
|
||||||
|
/// 会尝试复用之前缓存的 CTS,前提是两个 token 仍然相同且未取消。
|
||||||
|
/// </summary>
|
||||||
|
public CancellationTokenSource GetLinkedTokenSource(CancellationToken token1, CancellationToken token2)
|
||||||
|
{
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
// 如果缓存的 CTS 已经取消或 Dispose,或者 token 不同,重新创建
|
||||||
|
if (_cachedCts?.IsCancellationRequested != false ||
|
||||||
|
!_token1.Equals(token1) || !_token2.Equals(token2))
|
||||||
|
{
|
||||||
|
_cachedCts?.Dispose();
|
||||||
|
|
||||||
|
_cachedCts = CancellationTokenSource.CreateLinkedTokenSource(token1, token2);
|
||||||
|
_token1 = token1;
|
||||||
|
_token2 = token2;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _cachedCts;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
_cachedCts?.Dispose();
|
||||||
|
_cachedCts = null!;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -10,6 +10,7 @@ public class CronScheduledTask : DisposeBase, IScheduledTask
|
|||||||
private int _interval10MS = 10;
|
private int _interval10MS = 10;
|
||||||
private string _interval;
|
private string _interval;
|
||||||
private readonly Func<object?, CancellationToken, Task> _taskFunc;
|
private readonly Func<object?, CancellationToken, Task> _taskFunc;
|
||||||
|
private readonly Func<object?, CancellationToken, ValueTask> _valueTaskFunc;
|
||||||
private readonly Action<object?, CancellationToken> _taskAction;
|
private readonly Action<object?, CancellationToken> _taskAction;
|
||||||
private readonly CancellationToken _token;
|
private readonly CancellationToken _token;
|
||||||
private TimerX? _timer;
|
private TimerX? _timer;
|
||||||
@@ -28,6 +29,17 @@ public class CronScheduledTask : DisposeBase, IScheduledTask
|
|||||||
_taskFunc = taskFunc;
|
_taskFunc = taskFunc;
|
||||||
_token = token;
|
_token = token;
|
||||||
}
|
}
|
||||||
|
public CronScheduledTask(string interval, Func<object?, CancellationToken, ValueTask> taskFunc, object? state, ILog log, CancellationToken token)
|
||||||
|
{
|
||||||
|
_interval = interval;
|
||||||
|
LogMessage = log;
|
||||||
|
_state = state;
|
||||||
|
_valueTaskFunc = taskFunc;
|
||||||
|
_token = token;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public CronScheduledTask(string interval, Action<object?, CancellationToken> taskAction, object? state, ILog log, CancellationToken token)
|
public CronScheduledTask(string interval, Action<object?, CancellationToken> taskAction, object? state, ILog log, CancellationToken token)
|
||||||
{
|
{
|
||||||
_interval = interval;
|
_interval = interval;
|
||||||
@@ -51,14 +63,14 @@ public class CronScheduledTask : DisposeBase, IScheduledTask
|
|||||||
if (Check()) return;
|
if (Check()) return;
|
||||||
if (_taskAction != null)
|
if (_taskAction != null)
|
||||||
_timer = new TimerX(TimerCallback, _state, _interval, nameof(IScheduledTask)) { Async = true };
|
_timer = new TimerX(TimerCallback, _state, _interval, nameof(IScheduledTask)) { Async = true };
|
||||||
else if (_taskFunc != null)
|
else if (_taskFunc != null || _valueTaskFunc != null)
|
||||||
_timer = new TimerX(TimerCallbackAsync, _state, _interval, nameof(IScheduledTask)) { Async = true };
|
_timer = new TimerX(TimerCallbackAsync, _state, _interval, nameof(IScheduledTask)) { Async = true };
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task TimerCallbackAsync(object? state)
|
private async ValueTask TimerCallbackAsync(object? state)
|
||||||
{
|
{
|
||||||
if (Check()) return;
|
if (Check()) return;
|
||||||
if (_taskFunc == null)
|
if (_taskFunc == null && _valueTaskFunc == null)
|
||||||
{
|
{
|
||||||
Dispose();
|
Dispose();
|
||||||
return;
|
return;
|
||||||
@@ -74,7 +86,10 @@ public class CronScheduledTask : DisposeBase, IScheduledTask
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await _taskFunc(state, _token).ConfigureAwait(false);
|
if (_taskFunc != null)
|
||||||
|
await _taskFunc(state, _token).ConfigureAwait(false);
|
||||||
|
else if (_valueTaskFunc != null)
|
||||||
|
await _valueTaskFunc(state, _token).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
catch (OperationCanceledException)
|
catch (OperationCanceledException)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ public class ScheduledAsyncTask : DisposeBase, IScheduledTask, IScheduledIntInte
|
|||||||
private int _interval10MS = 10;
|
private int _interval10MS = 10;
|
||||||
public int IntervalMS { get; }
|
public int IntervalMS { get; }
|
||||||
private readonly Func<object?, CancellationToken, Task> _taskFunc;
|
private readonly Func<object?, CancellationToken, Task> _taskFunc;
|
||||||
|
private readonly Func<object?, CancellationToken, ValueTask> _valueTaskFunc;
|
||||||
private readonly CancellationToken _token;
|
private readonly CancellationToken _token;
|
||||||
private TimerX? _timer;
|
private TimerX? _timer;
|
||||||
private object? _state;
|
private object? _state;
|
||||||
@@ -26,6 +27,14 @@ public class ScheduledAsyncTask : DisposeBase, IScheduledTask, IScheduledIntInte
|
|||||||
_taskFunc = taskFunc;
|
_taskFunc = taskFunc;
|
||||||
_token = token;
|
_token = token;
|
||||||
}
|
}
|
||||||
|
public ScheduledAsyncTask(int interval, Func<object?, CancellationToken, ValueTask> taskFunc, object? state, ILog log, CancellationToken token)
|
||||||
|
{
|
||||||
|
IntervalMS = interval;
|
||||||
|
LogMessage = log;
|
||||||
|
_state = state;
|
||||||
|
_valueTaskFunc = taskFunc;
|
||||||
|
_token = token;
|
||||||
|
}
|
||||||
private bool Check()
|
private bool Check()
|
||||||
{
|
{
|
||||||
if (_token.IsCancellationRequested)
|
if (_token.IsCancellationRequested)
|
||||||
@@ -42,11 +51,17 @@ public class ScheduledAsyncTask : DisposeBase, IScheduledTask, IScheduledIntInte
|
|||||||
_timer = new TimerX(DoAsync, _state, IntervalMS, IntervalMS, nameof(IScheduledTask)) { Async = true };
|
_timer = new TimerX(DoAsync, _state, IntervalMS, IntervalMS, nameof(IScheduledTask)) { Async = true };
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task DoAsync(object? state)
|
private async ValueTask DoAsync(object? state)
|
||||||
{
|
{
|
||||||
if (Check())
|
if (Check())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (_taskFunc == null && _valueTaskFunc == null)
|
||||||
|
{
|
||||||
|
Dispose();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Interlocked.Increment(ref _pendingTriggers);
|
Interlocked.Increment(ref _pendingTriggers);
|
||||||
|
|
||||||
if (Interlocked.Exchange(ref _isRunning, 1) == 1)
|
if (Interlocked.Exchange(ref _isRunning, 1) == 1)
|
||||||
@@ -55,9 +70,13 @@ public class ScheduledAsyncTask : DisposeBase, IScheduledTask, IScheduledIntInte
|
|||||||
// 减少一个触发次数
|
// 减少一个触发次数
|
||||||
Interlocked.Decrement(ref _pendingTriggers);
|
Interlocked.Decrement(ref _pendingTriggers);
|
||||||
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await _taskFunc(state, _token).ConfigureAwait(false);
|
if (_taskFunc != null)
|
||||||
|
await _taskFunc(state, _token).ConfigureAwait(false);
|
||||||
|
else if (_valueTaskFunc != null)
|
||||||
|
await _valueTaskFunc(state, _token).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
catch (OperationCanceledException)
|
catch (OperationCanceledException)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -14,6 +14,18 @@ public static class ScheduledTaskHelper
|
|||||||
return new CronScheduledTask(interval, func, state, log, cancellationToken);
|
return new CronScheduledTask(interval, func, state, log, cancellationToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public static IScheduledTask GetTask(string interval, Func<object?, CancellationToken, ValueTask> func, object? state, TouchSocket.Core.ILog log, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
if (int.TryParse(interval, out int intervalV))
|
||||||
|
{
|
||||||
|
var intervalMilliseconds = intervalV < 10 ? 10 : intervalV;
|
||||||
|
return new ScheduledAsyncTask(intervalMilliseconds, func, state, log, cancellationToken);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return new CronScheduledTask(interval, func, state, log, cancellationToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
public static IScheduledTask GetTask(string interval, Action<object?, CancellationToken> action, object? state, TouchSocket.Core.ILog log, CancellationToken cancellationToken)
|
public static IScheduledTask GetTask(string interval, Action<object?, CancellationToken> action, object? state, TouchSocket.Core.ILog log, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (int.TryParse(interval, out int intervalV))
|
if (int.TryParse(interval, out int intervalV))
|
||||||
|
|||||||
@@ -35,6 +35,12 @@ public static class ExportString
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static string VariableName => Localizer["VariableName"];
|
public static string VariableName => Localizer["VariableName"];
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 变量报警表名称
|
||||||
|
/// </summary>
|
||||||
|
public static string AlarmName => Localizer["AlarmName"];
|
||||||
|
|
||||||
public static IStringLocalizer localizer;
|
public static IStringLocalizer localizer;
|
||||||
public static IStringLocalizer Localizer
|
public static IStringLocalizer Localizer
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -16,20 +16,20 @@ namespace ThingsGateway.Gateway.Application;
|
|||||||
[ThingsGateway.DependencyInjection.SuppressSniffer]
|
[ThingsGateway.DependencyInjection.SuppressSniffer]
|
||||||
public static class ThingsGatewayCacheConst
|
public static class ThingsGatewayCacheConst
|
||||||
{
|
{
|
||||||
/// <summary>
|
///// <summary>
|
||||||
/// 通道
|
///// 通道
|
||||||
/// </summary>
|
///// </summary>
|
||||||
public const string Cache_Channel = $"{Cache_Prefix}Cache_Channel:List";
|
//public const string Cache_Channel = $"{Cache_Prefix}Cache_Channel:List";
|
||||||
|
|
||||||
/// <summary>
|
///// <summary>
|
||||||
/// device
|
///// device
|
||||||
/// </summary>
|
///// </summary>
|
||||||
public const string Cache_Device = $"{Cache_Prefix}Cache_Device:List";
|
//public const string Cache_Device = $"{Cache_Prefix}Cache_Device:List";
|
||||||
|
|
||||||
/// <summary>
|
///// <summary>
|
||||||
/// variable
|
///// variable
|
||||||
/// </summary>
|
///// </summary>
|
||||||
public const string Cache_Variable = $"{Cache_Prefix}Cache_Variable:IdNameList";
|
//public const string Cache_Variable = $"{Cache_Prefix}Cache_Variable:IdNameList";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 前缀
|
/// 前缀
|
||||||
|
|||||||
@@ -1,43 +0,0 @@
|
|||||||
//------------------------------------------------------------------------------
|
|
||||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
|
||||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
|
||||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
|
||||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
|
||||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
|
||||||
// 使用文档:https://thingsgateway.cn/
|
|
||||||
// QQ群:605534569
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
|
|
||||||
using RouteAttribute = Microsoft.AspNetCore.Mvc.RouteAttribute;
|
|
||||||
|
|
||||||
namespace ThingsGateway.Management;
|
|
||||||
|
|
||||||
[ApiDescriptionSettings("ThingsGateway.OpenApi", Order = 200)]
|
|
||||||
[Route("openApi/autoUpdate")]
|
|
||||||
[RolePermission]
|
|
||||||
[RequestAudit]
|
|
||||||
[ApiController]
|
|
||||||
[Authorize(AuthenticationSchemes = "Bearer")]
|
|
||||||
public class AutoUpdateController : ControllerBase
|
|
||||||
{
|
|
||||||
private IUpdateZipFileHostedService _updateZipFileService;
|
|
||||||
public AutoUpdateController(IUpdateZipFileHostedService updateZipFileService)
|
|
||||||
{
|
|
||||||
_updateZipFileService = updateZipFileService;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 检查更新
|
|
||||||
/// </summary>
|
|
||||||
/// <returns></returns>
|
|
||||||
[HttpPost("update")]
|
|
||||||
public async Task Update()
|
|
||||||
{
|
|
||||||
var data = await _updateZipFileService.GetList().ConfigureAwait(false);
|
|
||||||
if (data.Count != 0)
|
|
||||||
await _updateZipFileService.Update(data.OrderByDescending(a => a.Version).FirstOrDefault()).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -15,13 +15,10 @@ using Microsoft.AspNetCore.Http;
|
|||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using System.ComponentModel.DataAnnotations;
|
|
||||||
using System.IO.Ports;
|
|
||||||
|
|
||||||
using ThingsGateway.FriendlyException;
|
using ThingsGateway.FriendlyException;
|
||||||
|
|
||||||
using TouchSocket.Core;
|
using TouchSocket.Rpc;
|
||||||
using TouchSocket.Sockets;
|
|
||||||
|
|
||||||
namespace ThingsGateway.Gateway.Application;
|
namespace ThingsGateway.Gateway.Application;
|
||||||
|
|
||||||
@@ -34,7 +31,9 @@ namespace ThingsGateway.Gateway.Application;
|
|||||||
[RequestAudit]
|
[RequestAudit]
|
||||||
[ApiController]
|
[ApiController]
|
||||||
[Authorize(AuthenticationSchemes = "Bearer")]
|
[Authorize(AuthenticationSchemes = "Bearer")]
|
||||||
public class ControlController : ControllerBase
|
[TouchSocket.WebApi.Router("/miniapi/[api]/[action]")]
|
||||||
|
[TouchSocket.WebApi.EnableCors("cors")]
|
||||||
|
public class ControlController : ControllerBase, IRpcServer
|
||||||
{
|
{
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -43,6 +42,7 @@ public class ControlController : ControllerBase
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpPost("removeAllCache")]
|
[HttpPost("removeAllCache")]
|
||||||
[DisplayName("清空全部缓存")]
|
[DisplayName("清空全部缓存")]
|
||||||
|
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
|
||||||
public void RemoveAllCache()
|
public void RemoveAllCache()
|
||||||
{
|
{
|
||||||
App.CacheService.Clear();
|
App.CacheService.Clear();
|
||||||
@@ -54,6 +54,7 @@ public class ControlController : ControllerBase
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpPost("removeCache")]
|
[HttpPost("removeCache")]
|
||||||
[DisplayName("删除通道/设备缓存")]
|
[DisplayName("删除通道/设备缓存")]
|
||||||
|
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
|
||||||
public void RemoveCache()
|
public void RemoveCache()
|
||||||
{
|
{
|
||||||
App.GetService<IDeviceService>().DeleteDeviceFromCache();
|
App.GetService<IDeviceService>().DeleteDeviceFromCache();
|
||||||
@@ -66,6 +67,7 @@ public class ControlController : ControllerBase
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpPost("pauseBusinessThread")]
|
[HttpPost("pauseBusinessThread")]
|
||||||
[DisplayName("控制设备线程启停")]
|
[DisplayName("控制设备线程启停")]
|
||||||
|
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
|
||||||
public async Task PauseDeviceThreadAsync(long id, bool pause)
|
public async Task PauseDeviceThreadAsync(long id, bool pause)
|
||||||
{
|
{
|
||||||
if (GlobalData.IdDevices.TryGetValue(id, out var device))
|
if (GlobalData.IdDevices.TryGetValue(id, out var device))
|
||||||
@@ -85,6 +87,7 @@ public class ControlController : ControllerBase
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpPost("restartScopeThread")]
|
[HttpPost("restartScopeThread")]
|
||||||
[DisplayName("重启当前机构线程")]
|
[DisplayName("重启当前机构线程")]
|
||||||
|
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
|
||||||
public async Task RestartScopeThread()
|
public async Task RestartScopeThread()
|
||||||
{
|
{
|
||||||
var data = await GlobalData.GetCurrentUserChannels().ConfigureAwait(false);
|
var data = await GlobalData.GetCurrentUserChannels().ConfigureAwait(false);
|
||||||
@@ -97,6 +100,7 @@ public class ControlController : ControllerBase
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpPost("restartAllThread")]
|
[HttpPost("restartAllThread")]
|
||||||
[DisplayName("重启全部线程")]
|
[DisplayName("重启全部线程")]
|
||||||
|
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
|
||||||
public async Task RestartAllThread()
|
public async Task RestartAllThread()
|
||||||
{
|
{
|
||||||
await GlobalData.ChannelRuntimeService.RestartChannelAsync(GlobalData.IdChannels.Values).ConfigureAwait(false);
|
await GlobalData.ChannelRuntimeService.RestartChannelAsync(GlobalData.IdChannels.Values).ConfigureAwait(false);
|
||||||
@@ -108,6 +112,7 @@ public class ControlController : ControllerBase
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpPost("restartThread")]
|
[HttpPost("restartThread")]
|
||||||
[DisplayName("重启设备线程")]
|
[DisplayName("重启设备线程")]
|
||||||
|
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
|
||||||
public async Task RestartDeviceThreadAsync(long deviceId)
|
public async Task RestartDeviceThreadAsync(long deviceId)
|
||||||
{
|
{
|
||||||
if (GlobalData.IdDevices.TryGetValue(deviceId, out var deviceRuntime))
|
if (GlobalData.IdDevices.TryGetValue(deviceId, out var deviceRuntime))
|
||||||
@@ -126,7 +131,8 @@ public class ControlController : ControllerBase
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
[HttpPost("writeVariables")]
|
[HttpPost("writeVariables")]
|
||||||
[DisplayName("写入变量")]
|
[DisplayName("写入变量")]
|
||||||
public async Task<Dictionary<string, Dictionary<string, OperResult>>> WriteVariablesAsync([FromBody] Dictionary<string, Dictionary<string, string>> deviceDatas)
|
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
|
||||||
|
public async Task<Dictionary<string, Dictionary<string, OperResult>>> WriteVariablesAsync([FromBody][TouchSocket.WebApi.FromBody] Dictionary<string, Dictionary<string, string>> deviceDatas)
|
||||||
{
|
{
|
||||||
foreach (var deviceData in deviceDatas)
|
foreach (var deviceData in deviceDatas)
|
||||||
{
|
{
|
||||||
@@ -145,9 +151,10 @@ public class ControlController : ControllerBase
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
[HttpPost("batchSaveChannel")]
|
[HttpPost("batchSaveChannel")]
|
||||||
[DisplayName("保存通道")]
|
[DisplayName("保存通道")]
|
||||||
public Task<bool> BatchSaveChannelAsync([FromBody] List<ChannelInput> channels, ItemChangedType type, bool restart = true)
|
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
|
||||||
|
public Task<bool> BatchSaveChannelAsync([FromBody][TouchSocket.WebApi.FromBody] List<Channel> channels, ItemChangedType type, bool restart = true)
|
||||||
{
|
{
|
||||||
return GlobalData.ChannelRuntimeService.BatchSaveChannelAsync(channels.AdaptListChannel(), type, restart);
|
return GlobalData.ChannelRuntimeService.BatchSaveChannelAsync(channels, type, restart);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -155,9 +162,10 @@ public class ControlController : ControllerBase
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
[HttpPost("batchSaveDevice")]
|
[HttpPost("batchSaveDevice")]
|
||||||
[DisplayName("保存设备")]
|
[DisplayName("保存设备")]
|
||||||
public Task<bool> BatchSaveDeviceAsync([FromBody] List<DeviceInput> devices, ItemChangedType type, bool restart = true)
|
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
|
||||||
|
public Task<bool> BatchSaveDeviceAsync([FromBody][TouchSocket.WebApi.FromBody] List<Device> devices, ItemChangedType type, bool restart = true)
|
||||||
{
|
{
|
||||||
return GlobalData.DeviceRuntimeService.BatchSaveDeviceAsync(devices.AdaptListDevice(), type, restart);
|
return GlobalData.DeviceRuntimeService.BatchSaveDeviceAsync(devices, type, restart);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -165,9 +173,10 @@ public class ControlController : ControllerBase
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
[HttpPost("batchSaveVariable")]
|
[HttpPost("batchSaveVariable")]
|
||||||
[DisplayName("保存变量")]
|
[DisplayName("保存变量")]
|
||||||
public Task<bool> BatchSaveVariableAsync([FromBody] List<VariableInput> variables, ItemChangedType type, bool restart = true)
|
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
|
||||||
|
public Task<bool> BatchSaveVariableAsync([FromBody][TouchSocket.WebApi.FromBody] List<Variable> variables, ItemChangedType type, bool restart = true)
|
||||||
{
|
{
|
||||||
return GlobalData.VariableRuntimeService.BatchSaveVariableAsync(variables.AdaptListVariable(), type, restart, default);
|
return GlobalData.VariableRuntimeService.BatchSaveVariableAsync(variables, type, restart, default);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -175,7 +184,8 @@ public class ControlController : ControllerBase
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
[HttpPost("deleteChannel")]
|
[HttpPost("deleteChannel")]
|
||||||
[DisplayName("删除通道")]
|
[DisplayName("删除通道")]
|
||||||
public Task<bool> DeleteChannelAsync([FromBody] List<long> ids, bool restart = true)
|
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
|
||||||
|
public Task<bool> DeleteChannelAsync([FromBody][TouchSocket.WebApi.FromBody] List<long> ids, bool restart = true)
|
||||||
{
|
{
|
||||||
if (ids == null || ids.Count == 0) ids = GlobalData.IdChannels.Keys.ToList();
|
if (ids == null || ids.Count == 0) ids = GlobalData.IdChannels.Keys.ToList();
|
||||||
return GlobalData.ChannelRuntimeService.DeleteChannelAsync(ids, restart, default);
|
return GlobalData.ChannelRuntimeService.DeleteChannelAsync(ids, restart, default);
|
||||||
@@ -186,7 +196,8 @@ public class ControlController : ControllerBase
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
[HttpPost("deleteDevice")]
|
[HttpPost("deleteDevice")]
|
||||||
[DisplayName("删除设备")]
|
[DisplayName("删除设备")]
|
||||||
public Task<bool> DeleteDeviceAsync([FromBody] List<long> ids, bool restart = true)
|
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
|
||||||
|
public Task<bool> DeleteDeviceAsync([FromBody][TouchSocket.WebApi.FromBody] List<long> ids, bool restart = true)
|
||||||
{
|
{
|
||||||
if (ids == null || ids.Count == 0) ids = GlobalData.IdDevices.Keys.ToList();
|
if (ids == null || ids.Count == 0) ids = GlobalData.IdDevices.Keys.ToList();
|
||||||
return GlobalData.DeviceRuntimeService.DeleteDeviceAsync(ids, restart, default);
|
return GlobalData.DeviceRuntimeService.DeleteDeviceAsync(ids, restart, default);
|
||||||
@@ -197,7 +208,8 @@ public class ControlController : ControllerBase
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
[HttpPost("deleteVariable")]
|
[HttpPost("deleteVariable")]
|
||||||
[DisplayName("删除变量")]
|
[DisplayName("删除变量")]
|
||||||
public Task<bool> DeleteVariableAsync([FromBody] List<long> ids, bool restart = true)
|
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
|
||||||
|
public Task<bool> DeleteVariableAsync([FromBody][TouchSocket.WebApi.FromBody] List<long> ids, bool restart = true)
|
||||||
{
|
{
|
||||||
if (ids == null || ids.Count == 0) ids = GlobalData.IdVariables.Keys.ToList();
|
if (ids == null || ids.Count == 0) ids = GlobalData.IdVariables.Keys.ToList();
|
||||||
return GlobalData.VariableRuntimeService.DeleteVariableAsync(ids, restart, default);
|
return GlobalData.VariableRuntimeService.DeleteVariableAsync(ids, restart, default);
|
||||||
@@ -208,6 +220,7 @@ public class ControlController : ControllerBase
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
[HttpPost("insertTestData")]
|
[HttpPost("insertTestData")]
|
||||||
[DisplayName("增加测试数据")]
|
[DisplayName("增加测试数据")]
|
||||||
|
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
|
||||||
public Task InsertTestDataAsync(int testVariableCount, int testDeviceCount, string slaveUrl, bool businessEnable, bool restart = true)
|
public Task InsertTestDataAsync(int testVariableCount, int testDeviceCount, string slaveUrl, bool businessEnable, bool restart = true)
|
||||||
{
|
{
|
||||||
return GlobalData.VariableRuntimeService.InsertTestDataAsync(testVariableCount, testDeviceCount, slaveUrl, businessEnable, restart, default);
|
return GlobalData.VariableRuntimeService.InsertTestDataAsync(testVariableCount, testDeviceCount, slaveUrl, businessEnable, restart, default);
|
||||||
@@ -220,6 +233,7 @@ public class ControlController : ControllerBase
|
|||||||
[HttpPost("checkRealAlarm")]
|
[HttpPost("checkRealAlarm")]
|
||||||
[RequestAudit]
|
[RequestAudit]
|
||||||
[DisplayName("确认实时报警")]
|
[DisplayName("确认实时报警")]
|
||||||
|
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
|
||||||
public async Task CheckRealAlarm(long variableId)
|
public async Task CheckRealAlarm(long variableId)
|
||||||
{
|
{
|
||||||
if (GlobalData.ReadOnlyRealAlarmIdVariables.TryGetValue(variableId, out var variable))
|
if (GlobalData.ReadOnlyRealAlarmIdVariables.TryGetValue(variableId, out var variable))
|
||||||
@@ -229,552 +243,3 @@ public class ControlController : ControllerBase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public class ChannelInput
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 主键Id
|
|
||||||
/// </summary>
|
|
||||||
public virtual long Id { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 通道名称
|
|
||||||
/// </summary>
|
|
||||||
[Required]
|
|
||||||
public virtual string Name { get; set; }
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public virtual ChannelTypeEnum ChannelType { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 插件名称
|
|
||||||
/// </summary>
|
|
||||||
[Required]
|
|
||||||
public virtual string PluginName { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 使能
|
|
||||||
/// </summary>
|
|
||||||
public virtual bool Enable { get; set; } = true;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// LogLevel
|
|
||||||
/// </summary>
|
|
||||||
public LogLevel LogLevel { get; set; } = LogLevel.Info;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 远程地址,可由<see cref="IPHost"/> 与 <see cref="string"/> 相互转化
|
|
||||||
/// </summary>
|
|
||||||
[UriValidation]
|
|
||||||
public virtual string RemoteUrl { get; set; } = "127.0.0.1:502";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 本地地址,可由<see cref="IPHost.IPHost(string)"/>与<see href="IPHost.ToString()"/>相互转化
|
|
||||||
/// </summary>
|
|
||||||
[UriValidation]
|
|
||||||
public virtual string BindUrl { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// COM
|
|
||||||
/// </summary>
|
|
||||||
public virtual string PortName { get; set; } = "COM1";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 波特率
|
|
||||||
/// </summary>
|
|
||||||
public virtual int BaudRate { get; set; } = 9600;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 数据位
|
|
||||||
/// </summary>
|
|
||||||
public virtual int DataBits { get; set; } = 8;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 校验位
|
|
||||||
/// </summary>
|
|
||||||
public virtual Parity Parity { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 停止位
|
|
||||||
/// </summary>
|
|
||||||
public virtual StopBits StopBits { get; set; } = StopBits.One;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// DtrEnable
|
|
||||||
/// </summary>
|
|
||||||
public virtual bool DtrEnable { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// RtsEnable
|
|
||||||
/// </summary>
|
|
||||||
public virtual bool RtsEnable { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// StreamAsync
|
|
||||||
/// </summary>
|
|
||||||
public virtual bool StreamAsync { get; set; } = false;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 缓存超时
|
|
||||||
/// </summary>
|
|
||||||
[MinValue(100)]
|
|
||||||
public virtual int CacheTimeout { get; set; } = 500;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 连接超时
|
|
||||||
/// </summary>
|
|
||||||
[MinValue(100)]
|
|
||||||
public virtual ushort ConnectTimeout { get; set; } = 3000;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 最大并发数
|
|
||||||
/// </summary>
|
|
||||||
[MinValue(1)]
|
|
||||||
public virtual int MaxConcurrentCount { get; set; } = 1;
|
|
||||||
|
|
||||||
public virtual int MaxClientCount { get; set; } = 10000;
|
|
||||||
public virtual int CheckClearTime { get; set; } = 120000;
|
|
||||||
public virtual string Heartbeat { get; set; } = "Heartbeat";
|
|
||||||
|
|
||||||
#region dtu终端
|
|
||||||
|
|
||||||
public virtual int HeartbeatTime { get; set; } = 60000;
|
|
||||||
public virtual string DtuId { get; set; }
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
public virtual DtuSeviceType DtuSeviceType { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class DeviceInput : IValidatableObject
|
|
||||||
{
|
|
||||||
public long Id { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 名称
|
|
||||||
/// </summary>
|
|
||||||
[Required]
|
|
||||||
[RegularExpression(@"^[^.]*$", ErrorMessage = "The field {0} cannot contain a dot ('.')")]
|
|
||||||
public virtual string Name { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 描述
|
|
||||||
/// </summary>
|
|
||||||
public string? Description { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 通道
|
|
||||||
/// </summary>
|
|
||||||
[MinValue(1)]
|
|
||||||
[Required]
|
|
||||||
public virtual long ChannelId { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 默认执行间隔,支持corn表达式
|
|
||||||
/// </summary>
|
|
||||||
public virtual string IntervalTime { get; set; } = "1000";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 设备使能
|
|
||||||
/// </summary>
|
|
||||||
public virtual bool Enable { get; set; } = true;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// LogLevel
|
|
||||||
/// </summary>
|
|
||||||
public virtual TouchSocket.Core.LogLevel LogLevel { get; set; } = TouchSocket.Core.LogLevel.Info;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 设备属性Json
|
|
||||||
/// </summary>
|
|
||||||
public Dictionary<string, string>? DevicePropertys { get; set; } = new();
|
|
||||||
|
|
||||||
#region 冗余配置
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 启用冗余
|
|
||||||
/// </summary>
|
|
||||||
public bool RedundantEnable { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 冗余设备Id,只能选择相同驱动
|
|
||||||
/// </summary>
|
|
||||||
public long? RedundantDeviceId { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 冗余模式
|
|
||||||
/// </summary>
|
|
||||||
public virtual RedundantSwitchTypeEnum RedundantSwitchType { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 冗余扫描间隔
|
|
||||||
/// </summary>
|
|
||||||
[MinValue(30000)]
|
|
||||||
public virtual int RedundantScanIntervalTime { get; set; } = 30000;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 冗余切换判断脚本,返回true则切换冗余设备
|
|
||||||
/// </summary>
|
|
||||||
public virtual string RedundantScript { get; set; }
|
|
||||||
|
|
||||||
#endregion 冗余配置
|
|
||||||
|
|
||||||
#region 备用字段
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 自定义
|
|
||||||
/// </summary>
|
|
||||||
public string? Remark1 { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 自定义
|
|
||||||
/// </summary>
|
|
||||||
public string? Remark2 { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 自定义
|
|
||||||
/// </summary>
|
|
||||||
public string? Remark3 { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 自定义
|
|
||||||
/// </summary>
|
|
||||||
public string? Remark4 { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 自定义
|
|
||||||
/// </summary>
|
|
||||||
public string? Remark5 { get; set; }
|
|
||||||
|
|
||||||
#endregion 备用字段
|
|
||||||
|
|
||||||
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
|
|
||||||
{
|
|
||||||
if (RedundantEnable && RedundantDeviceId == null)
|
|
||||||
{
|
|
||||||
yield return new ValidationResult("When enable redundancy, you must select a redundant device.", new[] { nameof(RedundantEnable), nameof(RedundantDeviceId) });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class VariableInput : IValidatableObject
|
|
||||||
{
|
|
||||||
public virtual long Id { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 设备
|
|
||||||
/// </summary>
|
|
||||||
[Required]
|
|
||||||
public virtual long DeviceId { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 变量名称
|
|
||||||
/// </summary>
|
|
||||||
[Required]
|
|
||||||
public virtual string Name { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 描述
|
|
||||||
/// </summary>
|
|
||||||
public string? Description { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 单位
|
|
||||||
/// </summary>
|
|
||||||
public virtual string? Unit { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 间隔时间
|
|
||||||
/// </summary>
|
|
||||||
public virtual string? IntervalTime { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 变量地址,可能带有额外的信息,比如<see cref="DataFormatEnum"/> ,以;分割
|
|
||||||
/// </summary>
|
|
||||||
public string? RegisterAddress { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 数组长度
|
|
||||||
/// </summary>
|
|
||||||
public int? ArrayLength { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 其他方法,若不为空,此时RegisterAddress为方法参数
|
|
||||||
/// </summary>
|
|
||||||
public string? OtherMethod { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 使能
|
|
||||||
/// </summary>
|
|
||||||
public virtual bool Enable { get; set; } = true;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 读写权限
|
|
||||||
/// </summary>
|
|
||||||
public virtual ProtectTypeEnum ProtectType { get; set; } = ProtectTypeEnum.ReadWrite;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 数据类型
|
|
||||||
/// </summary>
|
|
||||||
public virtual DataTypeEnum DataType { get; set; } = DataTypeEnum.Int16;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 读取表达式
|
|
||||||
/// </summary>
|
|
||||||
public virtual string? ReadExpressions { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 写入表达式
|
|
||||||
/// </summary>
|
|
||||||
public virtual string? WriteExpressions { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 是否允许远程Rpc写入
|
|
||||||
/// </summary>
|
|
||||||
public virtual bool RpcWriteEnable { get; set; } = true;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 初始值
|
|
||||||
/// </summary>
|
|
||||||
public object? InitValue
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return _value;
|
|
||||||
}
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (value != null)
|
|
||||||
_value = value?.ToString()?.GetJTokenFromString();
|
|
||||||
else
|
|
||||||
_value = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private object? _value;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 保存初始值
|
|
||||||
/// </summary>
|
|
||||||
public virtual bool SaveValue { get; set; } = false;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 变量额外属性Json
|
|
||||||
/// </summary>
|
|
||||||
public Dictionary<long, Dictionary<string, string>>? VariablePropertys { get; set; }
|
|
||||||
|
|
||||||
#region 报警
|
|
||||||
/// <summary>
|
|
||||||
/// 报警延时
|
|
||||||
/// </summary>
|
|
||||||
public int AlarmDelay { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 布尔开报警使能
|
|
||||||
/// </summary>
|
|
||||||
public bool BoolOpenAlarmEnable { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 布尔开报警约束
|
|
||||||
/// </summary>
|
|
||||||
public string? BoolOpenRestrainExpressions { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 布尔开报警文本
|
|
||||||
/// </summary>
|
|
||||||
public string? BoolOpenAlarmText { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 布尔关报警使能
|
|
||||||
/// </summary>
|
|
||||||
public bool BoolCloseAlarmEnable { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 布尔关报警约束
|
|
||||||
/// </summary>
|
|
||||||
public string? BoolCloseRestrainExpressions { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 布尔关报警文本
|
|
||||||
/// </summary>
|
|
||||||
public string? BoolCloseAlarmText { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 高报使能
|
|
||||||
/// </summary>
|
|
||||||
public bool HAlarmEnable { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 高报约束
|
|
||||||
/// </summary>
|
|
||||||
public string? HRestrainExpressions { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 高报文本
|
|
||||||
/// </summary>
|
|
||||||
public string? HAlarmText { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 高限值
|
|
||||||
/// </summary>
|
|
||||||
public double? HAlarmCode { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 高高报使能
|
|
||||||
/// </summary>
|
|
||||||
public bool HHAlarmEnable { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 高高报约束
|
|
||||||
/// </summary>
|
|
||||||
public string? HHRestrainExpressions { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 高高报文本
|
|
||||||
/// </summary>
|
|
||||||
public string? HHAlarmText { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 高高限值
|
|
||||||
/// </summary>
|
|
||||||
public double? HHAlarmCode { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 低报使能
|
|
||||||
/// </summary>
|
|
||||||
public bool LAlarmEnable { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 低报约束
|
|
||||||
/// </summary>
|
|
||||||
public string? LRestrainExpressions { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 低报文本
|
|
||||||
/// </summary>
|
|
||||||
public string? LAlarmText { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 低限值
|
|
||||||
/// </summary>
|
|
||||||
public double? LAlarmCode { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 低低报使能
|
|
||||||
/// </summary>
|
|
||||||
public bool LLAlarmEnable { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 低低报约束
|
|
||||||
/// </summary>
|
|
||||||
public string? LLRestrainExpressions { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 低低报文本
|
|
||||||
/// </summary>
|
|
||||||
public string? LLAlarmText { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 低低限值
|
|
||||||
/// </summary>
|
|
||||||
public double? LLAlarmCode { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 自定义报警使能
|
|
||||||
/// </summary>
|
|
||||||
public bool CustomAlarmEnable { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 自定义报警条件约束
|
|
||||||
/// </summary>
|
|
||||||
public string? CustomRestrainExpressions { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 自定义文本
|
|
||||||
/// </summary>
|
|
||||||
public string? CustomAlarmText { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 自定义报警条件
|
|
||||||
/// </summary>
|
|
||||||
public string? CustomAlarmCode { get; set; }
|
|
||||||
|
|
||||||
#endregion 报警
|
|
||||||
|
|
||||||
#region 备用字段
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 自定义
|
|
||||||
/// </summary>
|
|
||||||
public string? Remark1 { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 自定义
|
|
||||||
/// </summary>
|
|
||||||
public string? Remark2 { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 自定义
|
|
||||||
/// </summary>
|
|
||||||
public string? Remark3 { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 自定义
|
|
||||||
/// </summary>
|
|
||||||
public string? Remark4 { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 自定义
|
|
||||||
/// </summary>
|
|
||||||
public string? Remark5 { get; set; }
|
|
||||||
|
|
||||||
#endregion 备用字段
|
|
||||||
|
|
||||||
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(RegisterAddress) && string.IsNullOrEmpty(OtherMethod))
|
|
||||||
{
|
|
||||||
yield return new ValidationResult("Both RegisterAddress and OtherMethod cannot be empty or null.", new[] { nameof(RegisterAddress), nameof(OtherMethod) });
|
|
||||||
}
|
|
||||||
if (HHAlarmEnable && HHAlarmCode == null)
|
|
||||||
{
|
|
||||||
yield return new ValidationResult("HHAlarmCode cannot be null when HHAlarmEnable is true", new[] { nameof(HHAlarmCode) });
|
|
||||||
}
|
|
||||||
if (HAlarmEnable && HAlarmCode == null)
|
|
||||||
{
|
|
||||||
yield return new ValidationResult("HAlarmCode cannot be null when HAlarmEnable is true", new[] { nameof(HAlarmCode) });
|
|
||||||
}
|
|
||||||
if (LAlarmEnable && LAlarmCode == null)
|
|
||||||
{
|
|
||||||
yield return new ValidationResult("LAlarmCode cannot be null when LAlarmEnable is true", new[] { nameof(LAlarmCode) });
|
|
||||||
}
|
|
||||||
if (LLAlarmEnable && LLAlarmCode == null)
|
|
||||||
{
|
|
||||||
yield return new ValidationResult("LLAlarmCode cannot be null when LLAlarmEnable is true", new[] { nameof(LLAlarmCode) });
|
|
||||||
}
|
|
||||||
|
|
||||||
if (HHAlarmEnable && HAlarmEnable && HHAlarmCode <= HAlarmCode)
|
|
||||||
{
|
|
||||||
yield return new ValidationResult("HHAlarmCode must be greater than HAlarmCode", new[] { nameof(HHAlarmCode), nameof(HAlarmCode) });
|
|
||||||
}
|
|
||||||
if (HAlarmEnable && LAlarmEnable && HAlarmCode <= LAlarmCode)
|
|
||||||
{
|
|
||||||
yield return new ValidationResult("HAlarmCode must be greater than LAlarmCode", new[] { nameof(HAlarmCode), nameof(LAlarmCode) });
|
|
||||||
}
|
|
||||||
if (LAlarmEnable && LLAlarmEnable && LAlarmCode <= LLAlarmCode)
|
|
||||||
{
|
|
||||||
yield return new ValidationResult("LAlarmCode must be greater than LLAlarmCode", new[] { nameof(LAlarmCode), nameof(LLAlarmCode) });
|
|
||||||
}
|
|
||||||
|
|
||||||
if (HHAlarmEnable && LAlarmEnable && HHAlarmCode <= LAlarmCode)
|
|
||||||
{
|
|
||||||
yield return new ValidationResult("HHAlarmCode should be greater than or less than LAlarmCode", new[] { nameof(HHAlarmCode), nameof(LAlarmCode) });
|
|
||||||
}
|
|
||||||
if (HHAlarmEnable && LLAlarmEnable && HHAlarmCode <= LLAlarmCode)
|
|
||||||
{
|
|
||||||
yield return new ValidationResult("HHAlarmCode should be greater than or less than LLAlarmCode", new[] { nameof(HHAlarmCode), nameof(LLAlarmCode) });
|
|
||||||
}
|
|
||||||
if (HAlarmEnable && LLAlarmEnable && HAlarmCode <= LLAlarmCode)
|
|
||||||
{
|
|
||||||
yield return new ValidationResult("HAlarmCode should be greater than or less than LLAlarmCode", new[] { nameof(HAlarmCode), nameof(LLAlarmCode) });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -14,6 +14,8 @@ using Microsoft.AspNetCore.Mvc;
|
|||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
|
|
||||||
using ThingsGateway.NewLife.Extension;
|
using ThingsGateway.NewLife.Extension;
|
||||||
|
|
||||||
|
using TouchSocket.Rpc;
|
||||||
namespace ThingsGateway.Gateway.Application;
|
namespace ThingsGateway.Gateway.Application;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -25,7 +27,9 @@ namespace ThingsGateway.Gateway.Application;
|
|||||||
[ApiController]
|
[ApiController]
|
||||||
[RolePermission]
|
[RolePermission]
|
||||||
[Authorize(AuthenticationSchemes = "Bearer")]
|
[Authorize(AuthenticationSchemes = "Bearer")]
|
||||||
public class RuntimeInfoController : ControllerBase
|
[TouchSocket.WebApi.Router("/miniapi/[api]/[action]")]
|
||||||
|
[TouchSocket.WebApi.EnableCors("cors")]
|
||||||
|
public class RuntimeInfoController : ControllerBase, IRpcServer
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取冗余状态
|
/// 获取冗余状态
|
||||||
@@ -33,6 +37,7 @@ public class RuntimeInfoController : ControllerBase
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpGet("redundancyStatus")]
|
[HttpGet("redundancyStatus")]
|
||||||
[DisplayName("获取冗余状态")]
|
[DisplayName("获取冗余状态")]
|
||||||
|
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Get)]
|
||||||
public bool GetRedundancyStatus()
|
public bool GetRedundancyStatus()
|
||||||
{
|
{
|
||||||
return GlobalData.StartCollectChannelEnable;
|
return GlobalData.StartCollectChannelEnable;
|
||||||
@@ -44,7 +49,8 @@ public class RuntimeInfoController : ControllerBase
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpGet("channelList")]
|
[HttpGet("channelList")]
|
||||||
[DisplayName("获取通道信息")]
|
[DisplayName("获取通道信息")]
|
||||||
public async Task<SqlSugarPagedList<ChannelRuntime>> GetChannelListAsync([FromQuery] ChannelPageInput input)
|
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
|
||||||
|
public async Task<SqlSugarPagedList<ChannelRuntime>> GetChannelListAsync([FromQuery][TouchSocket.WebApi.FromBody] ChannelPageInput input)
|
||||||
{
|
{
|
||||||
var channelRuntimes = await GlobalData.GetCurrentUserChannels().ConfigureAwait(false);
|
var channelRuntimes = await GlobalData.GetCurrentUserChannels().ConfigureAwait(false);
|
||||||
|
|
||||||
@@ -62,7 +68,8 @@ public class RuntimeInfoController : ControllerBase
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpGet("deviceList")]
|
[HttpGet("deviceList")]
|
||||||
[DisplayName("获取设备信息")]
|
[DisplayName("获取设备信息")]
|
||||||
public async Task<SqlSugarPagedList<DeviceRuntime>> GetDeviceListAsync([FromQuery] DevicePageInput input)
|
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
|
||||||
|
public async Task<SqlSugarPagedList<DeviceRuntime>> GetDeviceListAsync([FromQuery][TouchSocket.WebApi.FromBody] DevicePageInput input)
|
||||||
{
|
{
|
||||||
var deviceRuntimes = await GlobalData.GetCurrentUserDevices().ConfigureAwait(false);
|
var deviceRuntimes = await GlobalData.GetCurrentUserDevices().ConfigureAwait(false);
|
||||||
var data = deviceRuntimes
|
var data = deviceRuntimes
|
||||||
@@ -80,9 +87,10 @@ public class RuntimeInfoController : ControllerBase
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpGet("realAlarmList")]
|
[HttpGet("realAlarmList")]
|
||||||
[DisplayName("获取实时报警变量信息")]
|
[DisplayName("获取实时报警变量信息")]
|
||||||
public async Task<SqlSugarPagedList<AlarmVariable>> GetRealAlarmList([FromQuery] AlarmVariablePageInput input)
|
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
|
||||||
|
public async Task<SqlSugarPagedList<AlarmVariable>> GetRealAlarmList([FromQuery][TouchSocket.WebApi.FromBody] AlarmVariablePageInput input)
|
||||||
{
|
{
|
||||||
var realAlarmVariables = await GlobalData.GetCurrentUserRealAlarmVariables().ConfigureAwait(false);
|
var realAlarmVariables = await GlobalData.GetCurrentUserRealAlarmVariablesAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
var data = realAlarmVariables
|
var data = realAlarmVariables
|
||||||
.WhereIF(!input.RegisterAddress.IsNullOrEmpty(), a => a.RegisterAddress == input.RegisterAddress)
|
.WhereIF(!input.RegisterAddress.IsNullOrEmpty(), a => a.RegisterAddress == input.RegisterAddress)
|
||||||
@@ -98,7 +106,8 @@ public class RuntimeInfoController : ControllerBase
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpGet("variableList")]
|
[HttpGet("variableList")]
|
||||||
[DisplayName("获取变量信息")]
|
[DisplayName("获取变量信息")]
|
||||||
public async Task<SqlSugarPagedList<VariableRuntime>> GetVariableList([FromQuery] VariablePageInput input)
|
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
|
||||||
|
public async Task<SqlSugarPagedList<VariableRuntime>> GetVariableList([FromQuery][TouchSocket.WebApi.FromBody] VariablePageInput input)
|
||||||
{
|
{
|
||||||
var variables = await GlobalData.GetCurrentUserIdVariables().ConfigureAwait(false);
|
var variables = await GlobalData.GetCurrentUserIdVariables().ConfigureAwait(false);
|
||||||
var data = variables
|
var data = variables
|
||||||
@@ -117,6 +126,7 @@ public class RuntimeInfoController : ControllerBase
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
[HttpGet("getPluginPropertys")]
|
[HttpGet("getPluginPropertys")]
|
||||||
[DisplayName("获取默认插件属性")]
|
[DisplayName("获取默认插件属性")]
|
||||||
|
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Get)]
|
||||||
public Dictionary<string, string> GetPluginPropertys(string pluginName)
|
public Dictionary<string, string> GetPluginPropertys(string pluginName)
|
||||||
{
|
{
|
||||||
var data = GlobalData.PluginService.GetDriverPropertyTypes(pluginName);
|
var data = GlobalData.PluginService.GetDriverPropertyTypes(pluginName);
|
||||||
@@ -131,10 +141,11 @@ public class RuntimeInfoController : ControllerBase
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
[HttpGet("getPluginInfos")]
|
[HttpGet("getPluginInfos")]
|
||||||
[DisplayName("获取插件")]
|
[DisplayName("获取插件")]
|
||||||
public SqlSugarPagedList<PluginInfo> GetPluginInfos([FromQuery] PluginInfoPageInput input)
|
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
|
||||||
|
public SqlSugarPagedList<PluginInfo> GetPluginInfos([FromQuery][TouchSocket.WebApi.FromBody] PluginInfoPageInput input)
|
||||||
{
|
{
|
||||||
//指定关键词搜索为插件FullName
|
//指定关键词搜索为插件FullName
|
||||||
return GlobalData.PluginService.GetList().WhereIF(!input.Name.IsNullOrWhiteSpace(), a => a.Name == input.Name)
|
return (GlobalData.PluginService.GetPluginList()).WhereIF(!input.Name.IsNullOrWhiteSpace(), a => a.Name == input.Name)
|
||||||
.ToPagedList(input);
|
.ToPagedList(input);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,14 +11,19 @@
|
|||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
namespace ThingsGateway.Admin.Application;
|
using TouchSocket.Rpc;
|
||||||
|
|
||||||
|
namespace ThingsGateway.Gateway.Application;
|
||||||
|
|
||||||
[Route("api/[controller]/[action]")]
|
[Route("api/[controller]/[action]")]
|
||||||
[AllowAnonymous]
|
[AllowAnonymous]
|
||||||
[ApiController]
|
[ApiController]
|
||||||
public class TestController : ControllerBase
|
[TouchSocket.WebApi.Router("/miniapi/[api]/[action]")]
|
||||||
|
[TouchSocket.WebApi.EnableCors("cors")]
|
||||||
|
public class TestController : ControllerBase, IRpcServer
|
||||||
{
|
{
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
|
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Get)]
|
||||||
public void Test()
|
public void Test()
|
||||||
{
|
{
|
||||||
GC.Collect();
|
GC.Collect();
|
||||||
@@ -59,10 +59,10 @@ public abstract class BusinessBaseWithCacheAlarm : BusinessBaseWithCache
|
|||||||
|
|
||||||
await base.InitChannelAsync(channel, cancellationToken).ConfigureAwait(false);
|
await base.InitChannelAsync(channel, cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
protected override void Dispose(bool disposing)
|
protected override Task DisposeAsync(bool disposing)
|
||||||
{
|
{
|
||||||
GlobalData.AlarmChangedEvent -= AlarmValueChange;
|
GlobalData.AlarmChangedEvent -= AlarmValueChange;
|
||||||
base.Dispose(disposing);
|
return base.DisposeAsync(disposing);
|
||||||
}
|
}
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 当报警值发生变化时触发此事件处理方法。该方法内部会检查是否需要进行报警上传,如果需要,则调用 <see cref="AlarmChange(AlarmVariable)"/> 方法。
|
/// 当报警值发生变化时触发此事件处理方法。该方法内部会检查是否需要进行报警上传,如果需要,则调用 <see cref="AlarmChange(AlarmVariable)"/> 方法。
|
||||||
|
|||||||
@@ -130,7 +130,7 @@ public abstract class BusinessBaseWithCacheInterval : BusinessBaseWithCache
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 释放资源方法
|
/// 释放资源方法
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected override void Dispose(bool disposing)
|
protected override Task DisposeAsync(bool disposing)
|
||||||
{
|
{
|
||||||
// 解绑事件
|
// 解绑事件
|
||||||
GlobalData.AlarmChangedEvent -= AlarmValueChange;
|
GlobalData.AlarmChangedEvent -= AlarmValueChange;
|
||||||
@@ -142,7 +142,7 @@ public abstract class BusinessBaseWithCacheInterval : BusinessBaseWithCache
|
|||||||
_memoryDevModelQueue.Clear();
|
_memoryDevModelQueue.Clear();
|
||||||
_memoryVarModelQueue.Clear();
|
_memoryVarModelQueue.Clear();
|
||||||
_memoryVarModelsQueue.Clear();
|
_memoryVarModelsQueue.Clear();
|
||||||
base.Dispose(disposing);
|
return base.DisposeAsync(disposing);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -21,6 +21,8 @@ public interface IDBHistoryAlarm
|
|||||||
string? Description { get; set; }
|
string? Description { get; set; }
|
||||||
string DeviceName { get; set; }
|
string DeviceName { get; set; }
|
||||||
DateTime EventTime { get; set; }
|
DateTime EventTime { get; set; }
|
||||||
|
DateTime FinishTime { get; set; }
|
||||||
|
DateTime ConfirmTime { get; set; }
|
||||||
EventTypeEnum EventType { get; set; }
|
EventTypeEnum EventType { get; set; }
|
||||||
string Name { get; set; }
|
string Name { get; set; }
|
||||||
string RegisterAddress { get; set; }
|
string RegisterAddress { get; set; }
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ public class CacheDBItem<T> : IPrimaryIdEntity
|
|||||||
}
|
}
|
||||||
|
|
||||||
[SugarColumn(IsPrimaryKey = true)]
|
[SugarColumn(IsPrimaryKey = true)]
|
||||||
|
[System.ComponentModel.DataAnnotations.Key]
|
||||||
public long Id { get; set; }
|
public long Id { get; set; }
|
||||||
|
|
||||||
[SugarColumn(IsJson = true, ColumnDataType = "TEXT")]
|
[SugarColumn(IsJson = true, ColumnDataType = "TEXT")]
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ namespace ThingsGateway.Gateway.Application;
|
|||||||
/// 采集插件,继承实现不同PLC通讯
|
/// 采集插件,继承实现不同PLC通讯
|
||||||
/// <para></para>
|
/// <para></para>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract class CollectBase : DriverBase, IRpcDriver
|
public abstract partial class CollectBase : DriverBase, IRpcDriver
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 插件配置项
|
/// 插件配置项
|
||||||
@@ -278,11 +278,9 @@ public abstract class CollectBase : DriverBase, IRpcDriver
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#region private
|
|
||||||
|
|
||||||
#region 执行方法
|
#region 执行方法
|
||||||
|
async ValueTask ReadVariableMed(object? state, CancellationToken cancellationToken)
|
||||||
async Task ReadVariableMed(object? state, CancellationToken cancellationToken)
|
|
||||||
{
|
{
|
||||||
if (state is not VariableMethod readVariableMethods) return;
|
if (state is not VariableMethod readVariableMethods) return;
|
||||||
if (Pause)
|
if (Pause)
|
||||||
@@ -348,33 +346,33 @@ public abstract class CollectBase : DriverBase, IRpcDriver
|
|||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
private readonly LinkedCancellationTokenSourceCache _linkedCtsCache = new();
|
||||||
|
|
||||||
#region 执行默认读取
|
#region 执行默认读取
|
||||||
|
async ValueTask ReadVariableSource(object? state, CancellationToken cancellationToken)
|
||||||
async Task ReadVariableSource(object? state, CancellationToken cancellationToken)
|
|
||||||
{
|
{
|
||||||
|
|
||||||
if (state is not VariableSourceRead variableSourceRead) return;
|
if (state is not VariableSourceRead variableSourceRead) return;
|
||||||
|
|
||||||
if (Pause) return;
|
if (Pause) return;
|
||||||
if (cancellationToken.IsCancellationRequested) return;
|
if (cancellationToken.IsCancellationRequested) return;
|
||||||
|
|
||||||
var readErrorCount = 0;
|
|
||||||
|
|
||||||
var readToken = await ReadWriteLock.ReaderLockAsync(cancellationToken).ConfigureAwait(false);
|
var readToken = await ReadWriteLock.ReaderLockAsync(cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
if (readToken.IsCancellationRequested)
|
if (readToken.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
await ReadVariableSource(state, cancellationToken).ConfigureAwait(false);
|
await ReadVariableSource(state, cancellationToken).ConfigureAwait(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
using var allTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, readToken);
|
var allTokenSource = _linkedCtsCache.GetLinkedTokenSource(cancellationToken, readToken);
|
||||||
var allToken = allTokenSource.Token;
|
var allToken = allTokenSource.Token;
|
||||||
|
|
||||||
//if (LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Trace)
|
//if (LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Trace)
|
||||||
// LogMessage?.Trace(string.Format("{0} - Collecting [{1} - {2}]", DeviceName, variableSourceRead?.RegisterAddress, variableSourceRead?.Length));
|
// LogMessage?.Trace(string.Format("{0} - Collecting [{1} - {2}]", DeviceName, variableSourceRead?.RegisterAddress, variableSourceRead?.Length));
|
||||||
var readResult = await ReadSourceAsync(variableSourceRead, allToken).ConfigureAwait(false);
|
var readResult = await ReadSourceAsync(variableSourceRead, allToken).ConfigureAwait(false);
|
||||||
|
|
||||||
|
var readErrorCount = 0;
|
||||||
|
|
||||||
// 读取失败时重试一定次数
|
// 读取失败时重试一定次数
|
||||||
while (!readResult.IsSuccess && readErrorCount < CollectProperties.RetryCount)
|
while (!readResult.IsSuccess && readErrorCount < CollectProperties.RetryCount)
|
||||||
{
|
{
|
||||||
@@ -434,13 +432,15 @@ public abstract class CollectBase : DriverBase, IRpcDriver
|
|||||||
variableSourceRead.LastErrorMessage = readResult.ErrorMessage;
|
variableSourceRead.LastErrorMessage = readResult.ErrorMessage;
|
||||||
CurrentDevice.SetDeviceStatus(TimerX.Now, null, readResult.ErrorMessage);
|
CurrentDevice.SetDeviceStatus(TimerX.Now, null, readResult.ErrorMessage);
|
||||||
var time = DateTime.Now;
|
var time = DateTime.Now;
|
||||||
variableSourceRead.VariableRuntimes.ForEach(a => a.SetValue(null, time, isOnline: false));
|
foreach (var item in variableSourceRead.VariableRuntimes)
|
||||||
|
{
|
||||||
|
item.SetValue(null, time, isOnline: false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
protected virtual Task TestOnline(object? state, CancellationToken cancellationToken)
|
protected virtual Task TestOnline(object? state, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
@@ -688,19 +688,8 @@ public abstract class CollectBase : DriverBase, IRpcDriver
|
|||||||
{
|
{
|
||||||
// 调用方法并获取结果
|
// 调用方法并获取结果
|
||||||
var data = await variableMethod.InvokeMethodAsync(this, value, cancellationToken).ConfigureAwait(false);
|
var data = await variableMethod.InvokeMethodAsync(this, value, cancellationToken).ConfigureAwait(false);
|
||||||
result = new(data);
|
|
||||||
var operResultType = typeof(IOperResult<>);
|
|
||||||
var interfaceType = data.GetType().GetInterfaces()
|
|
||||||
.FirstOrDefault(x => x.IsGenericType && x.GetGenericTypeDefinition() == operResultType);
|
|
||||||
|
|
||||||
if (interfaceType != null)
|
result = data.GetOperResult();
|
||||||
{
|
|
||||||
var contentProperty = interfaceType.GetProperty("Content");
|
|
||||||
if (contentProperty != null)
|
|
||||||
{
|
|
||||||
result.Content = contentProperty.GetValue(data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 如果方法有返回值,并且是读取操作
|
// 如果方法有返回值,并且是读取操作
|
||||||
if (method.HasReturn && isRead)
|
if (method.HasReturn && isRead)
|
||||||
@@ -731,5 +720,12 @@ public abstract class CollectBase : DriverBase, IRpcDriver
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#endregion 写入方法
|
#endregion 写入方法
|
||||||
|
|
||||||
|
protected override Task DisposeAsync(bool disposing)
|
||||||
|
{
|
||||||
|
_linkedCtsCache?.SafeDispose();
|
||||||
|
return base.DisposeAsync(disposing);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,10 +47,11 @@ public abstract class CollectFoundationBase : CollectBase
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
protected override void Dispose(bool disposing)
|
protected override async Task DisposeAsync(bool disposing)
|
||||||
{
|
{
|
||||||
FoundationDevice?.Dispose();
|
if (FoundationDevice != null)
|
||||||
base.Dispose(disposing);
|
await FoundationDevice.SafeDisposeAsync().ConfigureAwait(false);
|
||||||
|
await base.DisposeAsync(disposing).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 开始通讯执行的方法
|
/// 开始通讯执行的方法
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ public abstract class CollectPropertyBase : DriverPropertyBase
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 读写占空比
|
/// 读写占空比
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[MinValue(1)]
|
||||||
public virtual int DutyCycle { get; set; } = 3;
|
public virtual int DutyCycle { get; set; } = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,6 +53,7 @@ public abstract class CollectPropertyRetryBase : CollectPropertyBase
|
|||||||
public override int RetryCount { get; set; } = 3;
|
public override int RetryCount { get; set; } = 3;
|
||||||
|
|
||||||
[DynamicProperty(Remark = "n 次写入操作会执行一次读取")]
|
[DynamicProperty(Remark = "n 次写入操作会执行一次读取")]
|
||||||
|
[MinValue(1)]
|
||||||
public override int DutyCycle { get; set; } = 3;
|
public override int DutyCycle { get; set; } = 3;
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -26,11 +26,12 @@ namespace ThingsGateway.Gateway.Application;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 插件基类
|
/// 插件基类
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract class DriverBase : DisposableObject, IDriver
|
public abstract class DriverBase : AsyncDisposableObject, IDriver
|
||||||
{
|
{
|
||||||
/// <inheritdoc cref="DriverBase"/>
|
/// <inheritdoc cref="DriverBase"/>
|
||||||
public DriverBase()
|
public DriverBase()
|
||||||
{
|
{
|
||||||
|
|
||||||
Localizer = App.CreateLocalizerByType(typeof(DriverBase))!;
|
Localizer = App.CreateLocalizerByType(typeof(DriverBase))!;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,8 +40,7 @@ public abstract class DriverBase : DisposableObject, IDriver
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 当前设备
|
/// 当前设备
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public DeviceRuntime? CurrentDevice => WeakReferenceCurrentDevice?.TryGetTarget(out var target) == true ? target : null;
|
public DeviceRuntime? CurrentDevice { get; private set; }
|
||||||
private WeakReference<DeviceRuntime> WeakReferenceCurrentDevice { get; set; }
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 当前设备Id
|
/// 当前设备Id
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -208,7 +208,7 @@ public abstract class DriverBase : DisposableObject, IDriver
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
internal void InitDevice(DeviceRuntime device)
|
internal void InitDevice(DeviceRuntime device)
|
||||||
{
|
{
|
||||||
WeakReferenceCurrentDevice = new WeakReference<DeviceRuntime>(device);
|
CurrentDevice = device;
|
||||||
|
|
||||||
_logger = App.RootServices.GetService<Microsoft.Extensions.Logging.ILoggerFactory>().CreateLogger($"Driver[{CurrentDevice.Name}]");
|
_logger = App.RootServices.GetService<Microsoft.Extensions.Logging.ILoggerFactory>().CreateLogger($"Driver[{CurrentDevice.Name}]");
|
||||||
|
|
||||||
@@ -313,38 +313,45 @@ public abstract class DriverBase : DisposableObject, IDriver
|
|||||||
|
|
||||||
protected abstract List<IScheduledTask> ProtectedGetTasks(CancellationToken cancellationToken);
|
protected abstract List<IScheduledTask> ProtectedGetTasks(CancellationToken cancellationToken);
|
||||||
|
|
||||||
protected object stopLock = new();
|
protected WaitLock stopLock = new(nameof(DriverBase));
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 已停止任务,释放插件
|
/// 已停止任务,释放插件
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal virtual void Stop()
|
internal virtual async Task StopAsync()
|
||||||
{
|
{
|
||||||
if (!DisposedValue)
|
if (!DisposedValue)
|
||||||
{
|
{
|
||||||
lock (stopLock)
|
await stopLock.WaitAsync().ConfigureAwait(false);
|
||||||
|
try
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
if (!DisposedValue)
|
if (!DisposedValue)
|
||||||
{
|
{
|
||||||
try
|
|
||||||
{
|
|
||||||
// 执行资源释放操作
|
|
||||||
Dispose();
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
// 记录 Dispose 方法执行失败的错误信息
|
|
||||||
LogMessage?.LogError(ex, "Dispose");
|
|
||||||
}
|
|
||||||
// 记录设备线程已停止的信息
|
// 记录设备线程已停止的信息
|
||||||
LogMessage?.LogInformation(string.Format(AppResource.DeviceTaskStop, DeviceName));
|
LogMessage?.LogInformation(string.Format(AppResource.DeviceTaskStop, DeviceName));
|
||||||
|
|
||||||
|
// 执行资源释放操作
|
||||||
|
await this.SafeDisposeAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
// 记录 Dispose 方法执行失败的错误信息
|
||||||
|
LogMessage?.LogError(ex, "Dispose");
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
stopLock.Release();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Dispose(bool disposing)
|
protected override async Task DisposeAsync(bool disposing)
|
||||||
{
|
{
|
||||||
base.Dispose(disposing);
|
await base.DisposeAsync(disposing).ConfigureAwait(false);
|
||||||
if (TaskSchedulerLoop != null)
|
if (TaskSchedulerLoop != null)
|
||||||
{
|
{
|
||||||
lock (TaskSchedulerLoop)
|
lock (TaskSchedulerLoop)
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ using TouchSocket.Core;
|
|||||||
|
|
||||||
namespace ThingsGateway.Gateway.Application
|
namespace ThingsGateway.Gateway.Application
|
||||||
{
|
{
|
||||||
public interface IDriver : IDisposable
|
public interface IDriver : IAsyncDisposable
|
||||||
{
|
{
|
||||||
bool DisposedValue { get; }
|
bool DisposedValue { get; }
|
||||||
ChannelRuntime CurrentChannel { get; }
|
ChannelRuntime CurrentChannel { get; }
|
||||||
|
|||||||
@@ -0,0 +1,284 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://thingsgateway.cn/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
using BootstrapBlazor.Components;
|
||||||
|
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
|
namespace ThingsGateway.Gateway.Application;
|
||||||
|
|
||||||
|
public class AlarmPropertys : IValidatableObject
|
||||||
|
{
|
||||||
|
private int alarmDelay;
|
||||||
|
private int alarmLevel;
|
||||||
|
|
||||||
|
private decimal hAlarmCode = 50;
|
||||||
|
private decimal lAlarmCode = 10;
|
||||||
|
private decimal hHAlarmCode = 90;
|
||||||
|
private decimal lLAlarmCode = 0;
|
||||||
|
|
||||||
|
private bool boolOpenAlarmEnable;
|
||||||
|
private bool boolCloseAlarmEnable;
|
||||||
|
private bool hAlarmEnable;
|
||||||
|
private bool hHAlarmEnable;
|
||||||
|
private bool lLAlarmEnable;
|
||||||
|
private bool lAlarmEnable;
|
||||||
|
private bool customAlarmEnable;
|
||||||
|
|
||||||
|
private string boolOpenRestrainExpressions;
|
||||||
|
private string boolOpenAlarmText;
|
||||||
|
private string boolCloseRestrainExpressions;
|
||||||
|
private string boolCloseAlarmText;
|
||||||
|
private string hRestrainExpressions;
|
||||||
|
private string hAlarmText;
|
||||||
|
private string hHRestrainExpressions;
|
||||||
|
private string hHAlarmText;
|
||||||
|
private string lRestrainExpressions;
|
||||||
|
private string lAlarmText;
|
||||||
|
|
||||||
|
private string lLRestrainExpressions;
|
||||||
|
private string lLAlarmText;
|
||||||
|
private string customRestrainExpressions;
|
||||||
|
private string customAlarmText;
|
||||||
|
private string customAlarmCode;
|
||||||
|
|
||||||
|
#region 报警
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 报警等级
|
||||||
|
/// </summary>
|
||||||
|
[SugarColumn(ColumnDescription = "报警等级")]
|
||||||
|
[AutoGenerateColumn(Visible = false, Filterable = true, Sortable = true)]
|
||||||
|
public int AlarmLevel { get => alarmLevel; set => alarmLevel = value; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 报警延时
|
||||||
|
/// </summary>
|
||||||
|
[SugarColumn(ColumnDescription = "报警延时")]
|
||||||
|
[AutoGenerateColumn(Visible = false, Filterable = true, Sortable = true)]
|
||||||
|
public int AlarmDelay { get => alarmDelay; set => alarmDelay = value; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 布尔开报警使能
|
||||||
|
/// </summary>
|
||||||
|
[SugarColumn(ColumnDescription = "布尔开报警使能")]
|
||||||
|
[AutoGenerateColumn(Visible = false, Filterable = true, Sortable = true)]
|
||||||
|
public bool BoolOpenAlarmEnable { get => boolOpenAlarmEnable; set => boolOpenAlarmEnable = value; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 布尔开报警约束
|
||||||
|
/// </summary>
|
||||||
|
[SugarColumn(ColumnDescription = "布尔开报警约束", IsNullable = true)]
|
||||||
|
[AutoGenerateColumn(Visible = false, Filterable = true, Sortable = true)]
|
||||||
|
public string BoolOpenRestrainExpressions { get => boolOpenRestrainExpressions; set => boolOpenRestrainExpressions = value; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 布尔开报警文本
|
||||||
|
/// </summary>
|
||||||
|
[SugarColumn(ColumnDescription = "布尔开报警文本", IsNullable = true)]
|
||||||
|
[AutoGenerateColumn(Visible = false, Filterable = true, Sortable = true)]
|
||||||
|
public string BoolOpenAlarmText { get => boolOpenAlarmText; set => boolOpenAlarmText = value; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 布尔关报警使能
|
||||||
|
/// </summary>
|
||||||
|
[SugarColumn(ColumnDescription = "布尔关报警使能")]
|
||||||
|
[AutoGenerateColumn(Visible = false, Filterable = true, Sortable = true)]
|
||||||
|
public bool BoolCloseAlarmEnable { get => boolCloseAlarmEnable; set => boolCloseAlarmEnable = value; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 布尔关报警约束
|
||||||
|
/// </summary>
|
||||||
|
[SugarColumn(ColumnDescription = "布尔关报警约束", IsNullable = true)]
|
||||||
|
[AutoGenerateColumn(Visible = false, Filterable = true, Sortable = true)]
|
||||||
|
public string BoolCloseRestrainExpressions { get => boolCloseRestrainExpressions; set => boolCloseRestrainExpressions = value; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 布尔关报警文本
|
||||||
|
/// </summary>
|
||||||
|
[SugarColumn(ColumnDescription = "布尔关报警文本", IsNullable = true)]
|
||||||
|
[AutoGenerateColumn(Visible = false, Filterable = true, Sortable = true)]
|
||||||
|
public string BoolCloseAlarmText { get => boolCloseAlarmText; set => boolCloseAlarmText = value; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 高报使能
|
||||||
|
/// </summary>
|
||||||
|
[SugarColumn(ColumnDescription = "高报使能")]
|
||||||
|
[AutoGenerateColumn(Visible = false, Filterable = true, Sortable = true)]
|
||||||
|
public bool HAlarmEnable { get => hAlarmEnable; set => hAlarmEnable = value; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 高报约束
|
||||||
|
/// </summary>
|
||||||
|
[SugarColumn(ColumnDescription = "高报约束", IsNullable = true)]
|
||||||
|
[AutoGenerateColumn(Visible = false, Filterable = true, Sortable = true)]
|
||||||
|
public string HRestrainExpressions { get => hRestrainExpressions; set => hRestrainExpressions = value; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 高报文本
|
||||||
|
/// </summary>
|
||||||
|
[SugarColumn(ColumnDescription = "高报文本", IsNullable = true)]
|
||||||
|
[AutoGenerateColumn(Visible = false, Filterable = true, Sortable = true)]
|
||||||
|
public string HAlarmText { get => hAlarmText; set => hAlarmText = value; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 高限值
|
||||||
|
/// </summary>
|
||||||
|
[SugarColumn(ColumnDescription = "高限值", IsNullable = true)]
|
||||||
|
[AutoGenerateColumn(Visible = false, Filterable = true, Sortable = true)]
|
||||||
|
public decimal HAlarmCode { get => hAlarmCode; set => hAlarmCode = value; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 高高报使能
|
||||||
|
/// </summary>
|
||||||
|
[SugarColumn(ColumnDescription = "高高报使能")]
|
||||||
|
[AutoGenerateColumn(Visible = false, Filterable = true, Sortable = true)]
|
||||||
|
public bool HHAlarmEnable { get => hHAlarmEnable; set => hHAlarmEnable = value; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 高高报约束
|
||||||
|
/// </summary>
|
||||||
|
[SugarColumn(ColumnDescription = "高高报约束", IsNullable = true)]
|
||||||
|
[AutoGenerateColumn(Visible = false, Filterable = true, Sortable = true)]
|
||||||
|
public string HHRestrainExpressions { get => hHRestrainExpressions; set => hHRestrainExpressions = value; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 高高报文本
|
||||||
|
/// </summary>
|
||||||
|
[SugarColumn(ColumnDescription = "高高报文本", IsNullable = true)]
|
||||||
|
[AutoGenerateColumn(Visible = false, Filterable = true, Sortable = true)]
|
||||||
|
public string HHAlarmText { get => hHAlarmText; set => hHAlarmText = value; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 高高限值
|
||||||
|
/// </summary>
|
||||||
|
[SugarColumn(ColumnDescription = "高高限值", IsNullable = true)]
|
||||||
|
[AutoGenerateColumn(Visible = false, Filterable = true, Sortable = true)]
|
||||||
|
public decimal HHAlarmCode { get => hHAlarmCode; set => hHAlarmCode = value; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 低报使能
|
||||||
|
/// </summary>
|
||||||
|
[SugarColumn(ColumnDescription = "低报使能")]
|
||||||
|
[AutoGenerateColumn(Visible = false, Filterable = true, Sortable = true)]
|
||||||
|
public bool LAlarmEnable { get => lAlarmEnable; set => lAlarmEnable = value; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 低报约束
|
||||||
|
/// </summary>
|
||||||
|
[SugarColumn(ColumnDescription = "低报约束", IsNullable = true)]
|
||||||
|
[AutoGenerateColumn(Visible = false, Filterable = true, Sortable = true)]
|
||||||
|
public string LRestrainExpressions { get => lRestrainExpressions; set => lRestrainExpressions = value; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 低报文本
|
||||||
|
/// </summary>
|
||||||
|
[SugarColumn(ColumnDescription = "低报文本", IsNullable = true)]
|
||||||
|
[AutoGenerateColumn(Visible = false, Filterable = true, Sortable = true)]
|
||||||
|
public string LAlarmText { get => lAlarmText; set => lAlarmText = value; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 低限值
|
||||||
|
/// </summary>
|
||||||
|
[SugarColumn(ColumnDescription = "低限值", IsNullable = true)]
|
||||||
|
[AutoGenerateColumn(Visible = false, Filterable = true, Sortable = true)]
|
||||||
|
public decimal LAlarmCode { get => lAlarmCode; set => lAlarmCode = value; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 低低报使能
|
||||||
|
/// </summary>
|
||||||
|
[SugarColumn(ColumnDescription = "低低报使能")]
|
||||||
|
[AutoGenerateColumn(Visible = false, Filterable = true, Sortable = true)]
|
||||||
|
public bool LLAlarmEnable { get => lLAlarmEnable; set => lLAlarmEnable = value; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 低低报约束
|
||||||
|
/// </summary>
|
||||||
|
[SugarColumn(ColumnDescription = "低低报约束", IsNullable = true)]
|
||||||
|
[AutoGenerateColumn(Visible = false, Filterable = true, Sortable = true)]
|
||||||
|
public string LLRestrainExpressions { get => lLRestrainExpressions; set => lLRestrainExpressions = value; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 低低报文本
|
||||||
|
/// </summary>
|
||||||
|
[SugarColumn(ColumnDescription = "低低报文本", IsNullable = true)]
|
||||||
|
[AutoGenerateColumn(Visible = false, Filterable = true, Sortable = true)]
|
||||||
|
public string LLAlarmText { get => lLAlarmText; set => lLAlarmText = value; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 低低限值
|
||||||
|
/// </summary>
|
||||||
|
[SugarColumn(ColumnDescription = "低低限值", IsNullable = true)]
|
||||||
|
[AutoGenerateColumn(Visible = false, Filterable = true, Sortable = true)]
|
||||||
|
public decimal LLAlarmCode { get => lLAlarmCode; set => lLAlarmCode = value; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 自定义报警使能
|
||||||
|
/// </summary>
|
||||||
|
[SugarColumn(ColumnDescription = "自定义报警使能")]
|
||||||
|
[AutoGenerateColumn(Visible = false, Filterable = true, Sortable = true)]
|
||||||
|
public bool CustomAlarmEnable { get => customAlarmEnable; set => customAlarmEnable = value; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 自定义报警条件约束
|
||||||
|
/// </summary>
|
||||||
|
[SugarColumn(ColumnDescription = "自定义报警条件约束", IsNullable = true)]
|
||||||
|
[AutoGenerateColumn(Visible = false, Filterable = true, Sortable = true)]
|
||||||
|
public string CustomRestrainExpressions { get => customRestrainExpressions; set => customRestrainExpressions = value; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 自定义文本
|
||||||
|
/// </summary>
|
||||||
|
[SugarColumn(ColumnDescription = "自定义文本", IsNullable = true)]
|
||||||
|
[AutoGenerateColumn(Visible = false, Filterable = true, Sortable = true)]
|
||||||
|
public string CustomAlarmText { get => customAlarmText; set => customAlarmText = value; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 自定义报警条件
|
||||||
|
/// </summary>
|
||||||
|
[SugarColumn(ColumnDescription = "自定义报警条件", IsNullable = true)]
|
||||||
|
[AutoGenerateColumn(Visible = false, Filterable = true, Sortable = true)]
|
||||||
|
public string CustomAlarmCode { get => customAlarmCode; set => customAlarmCode = value; }
|
||||||
|
|
||||||
|
#endregion 报警
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (HHAlarmEnable && HAlarmEnable && HHAlarmCode <= HAlarmCode)
|
||||||
|
{
|
||||||
|
yield return new ValidationResult("HHAlarmCode must be greater than HAlarmCode", new[] { nameof(HHAlarmCode), nameof(HAlarmCode) });
|
||||||
|
}
|
||||||
|
if (HAlarmEnable && LAlarmEnable && HAlarmCode <= LAlarmCode)
|
||||||
|
{
|
||||||
|
yield return new ValidationResult("HAlarmCode must be greater than LAlarmCode", new[] { nameof(HAlarmCode), nameof(LAlarmCode) });
|
||||||
|
}
|
||||||
|
if (LAlarmEnable && LLAlarmEnable && LAlarmCode <= LLAlarmCode)
|
||||||
|
{
|
||||||
|
yield return new ValidationResult("LAlarmCode must be greater than LLAlarmCode", new[] { nameof(LAlarmCode), nameof(LLAlarmCode) });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (HHAlarmEnable && LAlarmEnable && HHAlarmCode <= LAlarmCode)
|
||||||
|
{
|
||||||
|
yield return new ValidationResult("HHAlarmCode should be greater than or less than LAlarmCode", new[] { nameof(HHAlarmCode), nameof(LAlarmCode) });
|
||||||
|
}
|
||||||
|
if (HHAlarmEnable && LLAlarmEnable && HHAlarmCode <= LLAlarmCode)
|
||||||
|
{
|
||||||
|
yield return new ValidationResult("HHAlarmCode should be greater than or less than LLAlarmCode", new[] { nameof(HHAlarmCode), nameof(LLAlarmCode) });
|
||||||
|
}
|
||||||
|
if (HAlarmEnable && LLAlarmEnable && HAlarmCode <= LLAlarmCode)
|
||||||
|
{
|
||||||
|
yield return new ValidationResult("HAlarmCode should be greater than or less than LLAlarmCode", new[] { nameof(HAlarmCode), nameof(LLAlarmCode) });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,8 +17,10 @@ namespace ThingsGateway.Gateway.Application;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 后台日志表
|
/// 后台日志表
|
||||||
///</summary>
|
///</summary>
|
||||||
|
#if !Management
|
||||||
[SugarTable("backend_log", TableDescription = "后台日志表")]
|
[SugarTable("backend_log", TableDescription = "后台日志表")]
|
||||||
[Tenant(SqlSugarConst.DB_Log)]
|
[Tenant(SqlSugarConst.DB_Log)]
|
||||||
|
#endif
|
||||||
public class BackendLog : PrimaryIdEntity
|
public class BackendLog : PrimaryIdEntity
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -17,13 +17,16 @@ using TouchSocket.Core;
|
|||||||
using TouchSocket.Sockets;
|
using TouchSocket.Sockets;
|
||||||
|
|
||||||
namespace ThingsGateway.Gateway.Application;
|
namespace ThingsGateway.Gateway.Application;
|
||||||
|
#pragma warning disable CS0649
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 通道表
|
/// 通道表
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
#if !Management
|
||||||
[SugarTable("channel", TableDescription = "通道表")]
|
[SugarTable("channel", TableDescription = "通道表")]
|
||||||
[Tenant(SqlSugarConst.DB_Custom)]
|
[Tenant(SqlSugarConst.DB_Custom)]
|
||||||
[SugarIndex("unique_channel_name", nameof(Channel.Name), OrderByType.Asc, true)]
|
[SugarIndex("unique_channel_name", nameof(Channel.Name), OrderByType.Asc, true)]
|
||||||
|
#endif
|
||||||
public class Channel : ChannelOptionsBase, IPrimaryIdEntity, IBaseDataEntity, IBaseEntity
|
public class Channel : ChannelOptionsBase, IPrimaryIdEntity, IBaseDataEntity, IBaseEntity
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -32,6 +35,7 @@ public class Channel : ChannelOptionsBase, IPrimaryIdEntity, IBaseDataEntity, IB
|
|||||||
[SugarColumn(ColumnDescription = "Id", IsPrimaryKey = true)]
|
[SugarColumn(ColumnDescription = "Id", IsPrimaryKey = true)]
|
||||||
[IgnoreExcel]
|
[IgnoreExcel]
|
||||||
[AutoGenerateColumn(Visible = false, IsVisibleWhenEdit = false, IsVisibleWhenAdd = false, Sortable = true, DefaultSort = true, DefaultSortOrder = SortOrder.Asc)]
|
[AutoGenerateColumn(Visible = false, IsVisibleWhenEdit = false, IsVisibleWhenAdd = false, Sortable = true, DefaultSort = true, DefaultSortOrder = SortOrder.Asc)]
|
||||||
|
[System.ComponentModel.DataAnnotations.Key]
|
||||||
public virtual long Id { get; set; }
|
public virtual long Id { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -17,13 +17,16 @@ using System.ComponentModel.DataAnnotations;
|
|||||||
using ThingsGateway.NewLife.Extension;
|
using ThingsGateway.NewLife.Extension;
|
||||||
|
|
||||||
namespace ThingsGateway.Gateway.Application;
|
namespace ThingsGateway.Gateway.Application;
|
||||||
|
#pragma warning disable CS0649
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 设备表
|
/// 设备表
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
#if !Management
|
||||||
[SugarTable("device", TableDescription = "设备表")]
|
[SugarTable("device", TableDescription = "设备表")]
|
||||||
[Tenant(SqlSugarConst.DB_Custom)]
|
[Tenant(SqlSugarConst.DB_Custom)]
|
||||||
[SugarIndex("unique_device_name", nameof(Device.Name), OrderByType.Asc, true)]
|
[SugarIndex("unique_device_name", nameof(Device.Name), OrderByType.Asc, true)]
|
||||||
|
#endif
|
||||||
public class Device : BaseDataEntity, IValidatableObject
|
public class Device : BaseDataEntity, IValidatableObject
|
||||||
{
|
{
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
|
|||||||
@@ -15,8 +15,10 @@ namespace ThingsGateway.Gateway.Application;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Rpc写入日志
|
/// Rpc写入日志
|
||||||
///</summary>
|
///</summary>
|
||||||
|
#if !Management
|
||||||
[SugarTable("rpc_log", TableDescription = "RPC操作日志")]
|
[SugarTable("rpc_log", TableDescription = "RPC操作日志")]
|
||||||
[Tenant(SqlSugarConst.DB_Log)]
|
[Tenant(SqlSugarConst.DB_Log)]
|
||||||
|
#endif
|
||||||
public class RpcLog : PrimaryIdEntity
|
public class RpcLog : PrimaryIdEntity
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -16,14 +16,17 @@ using System.Collections.Concurrent;
|
|||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
namespace ThingsGateway.Gateway.Application;
|
namespace ThingsGateway.Gateway.Application;
|
||||||
|
#pragma warning disable CS0649
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 设备变量表
|
/// 设备变量表
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
#if !Management
|
||||||
[SugarTable("variable", TableDescription = "设备变量表")]
|
[SugarTable("variable", TableDescription = "设备变量表")]
|
||||||
[Tenant(SqlSugarConst.DB_Custom)]
|
[Tenant(SqlSugarConst.DB_Custom)]
|
||||||
[SugarIndex("index_device", nameof(Variable.DeviceId), OrderByType.Asc)]
|
[SugarIndex("index_device", nameof(Variable.DeviceId), OrderByType.Asc)]
|
||||||
[SugarIndex("unique_deviceid_variable_name", nameof(Variable.Name), OrderByType.Asc, nameof(Variable.DeviceId), OrderByType.Asc, true)]
|
[SugarIndex("unique_deviceid_variable_name", nameof(Variable.Name), OrderByType.Asc, nameof(Variable.DeviceId), OrderByType.Asc, true)]
|
||||||
|
#endif
|
||||||
public class Variable : BaseDataEntity, IValidatableObject
|
public class Variable : BaseDataEntity, IValidatableObject
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -31,6 +34,7 @@ public class Variable : BaseDataEntity, IValidatableObject
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
[SugarColumn(ColumnDescription = "Id", IsPrimaryKey = true)]
|
[SugarColumn(ColumnDescription = "Id", IsPrimaryKey = true)]
|
||||||
[AutoGenerateColumn(Visible = false, IsVisibleWhenEdit = false, IsVisibleWhenAdd = false, Sortable = true, DefaultSort = true, DefaultSortOrder = SortOrder.Asc)]
|
[AutoGenerateColumn(Visible = false, IsVisibleWhenEdit = false, IsVisibleWhenAdd = false, Sortable = true, DefaultSort = true, DefaultSortOrder = SortOrder.Asc)]
|
||||||
|
[System.ComponentModel.DataAnnotations.Key]
|
||||||
public override long Id { get; set; }
|
public override long Id { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -39,13 +43,9 @@ public class Variable : BaseDataEntity, IValidatableObject
|
|||||||
[System.Text.Json.Serialization.JsonIgnore]
|
[System.Text.Json.Serialization.JsonIgnore]
|
||||||
[Newtonsoft.Json.JsonIgnore]
|
[Newtonsoft.Json.JsonIgnore]
|
||||||
internal long Row;
|
internal long Row;
|
||||||
private double hAlarmCode = 50;
|
|
||||||
private double lAlarmCode = 10;
|
|
||||||
private double hHAlarmCode = 90;
|
|
||||||
private double lLAlarmCode = 0;
|
|
||||||
private long deviceId;
|
private long deviceId;
|
||||||
private int? arrayLength;
|
private int? arrayLength;
|
||||||
private int alarmDelay;
|
|
||||||
private ProtectTypeEnum protectType = ProtectTypeEnum.ReadWrite;
|
private ProtectTypeEnum protectType = ProtectTypeEnum.ReadWrite;
|
||||||
private DataTypeEnum dataType = DataTypeEnum.Int16;
|
private DataTypeEnum dataType = DataTypeEnum.Int16;
|
||||||
|
|
||||||
@@ -59,13 +59,6 @@ public class Variable : BaseDataEntity, IValidatableObject
|
|||||||
public bool DynamicVariable;
|
public bool DynamicVariable;
|
||||||
private bool rpcWriteEnable = true;
|
private bool rpcWriteEnable = true;
|
||||||
private bool saveValue = false;
|
private bool saveValue = false;
|
||||||
private bool boolOpenAlarmEnable;
|
|
||||||
private bool boolCloseAlarmEnable;
|
|
||||||
private bool hAlarmEnable;
|
|
||||||
private bool hHAlarmEnable;
|
|
||||||
private bool lLAlarmEnable;
|
|
||||||
private bool lAlarmEnable;
|
|
||||||
private bool customAlarmEnable;
|
|
||||||
private bool businessGroupUpdateTrigger = true;
|
private bool businessGroupUpdateTrigger = true;
|
||||||
private bool rpcWriteCheck;
|
private bool rpcWriteCheck;
|
||||||
|
|
||||||
@@ -80,29 +73,17 @@ public class Variable : BaseDataEntity, IValidatableObject
|
|||||||
private string otherMethod;
|
private string otherMethod;
|
||||||
private string readExpressions;
|
private string readExpressions;
|
||||||
private string writeExpressions;
|
private string writeExpressions;
|
||||||
private string boolOpenRestrainExpressions;
|
|
||||||
private string boolOpenAlarmText;
|
|
||||||
private string boolCloseRestrainExpressions;
|
|
||||||
private string boolCloseAlarmText;
|
|
||||||
private string hRestrainExpressions;
|
|
||||||
private string hAlarmText;
|
|
||||||
private Dictionary<long, Dictionary<string, string>>? variablePropertys;
|
|
||||||
private string hHRestrainExpressions;
|
|
||||||
private string hHAlarmText;
|
|
||||||
private string lRestrainExpressions;
|
|
||||||
private string lAlarmText;
|
|
||||||
|
|
||||||
private string lLRestrainExpressions;
|
private Dictionary<long, Dictionary<string, string>>? variablePropertys;
|
||||||
private string lLAlarmText;
|
|
||||||
private string customRestrainExpressions;
|
|
||||||
private string customAlarmText;
|
|
||||||
private string customAlarmCode;
|
|
||||||
private string remark1;
|
private string remark1;
|
||||||
private string remark2;
|
private string remark2;
|
||||||
private string remark3;
|
private string remark3;
|
||||||
private string remark4;
|
private string remark4;
|
||||||
private string remark5;
|
private string remark5;
|
||||||
|
|
||||||
|
[MapperIgnore]
|
||||||
|
public ValidateForm AlarmPropertysValidateForm;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 变量额外属性Json
|
/// 变量额外属性Json
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -271,197 +252,15 @@ public class Variable : BaseDataEntity, IValidatableObject
|
|||||||
[AutoGenerateColumn(Ignore = true)]
|
[AutoGenerateColumn(Ignore = true)]
|
||||||
public Dictionary<long, Dictionary<string, string>>? VariablePropertys { get => variablePropertys; set => variablePropertys = value; }
|
public Dictionary<long, Dictionary<string, string>>? VariablePropertys { get => variablePropertys; set => variablePropertys = value; }
|
||||||
|
|
||||||
#region 报警
|
|
||||||
/// <summary>
|
|
||||||
/// 报警延时
|
|
||||||
/// </summary>
|
|
||||||
[SugarColumn(ColumnDescription = "报警延时")]
|
|
||||||
[AutoGenerateColumn(Visible = false, Filterable = true, Sortable = true)]
|
|
||||||
public int AlarmDelay { get => alarmDelay; set => alarmDelay = value; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 布尔开报警使能
|
/// 变量报警属性Json
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[SugarColumn(ColumnDescription = "布尔开报警使能")]
|
[SugarColumn(IsJson = true, ColumnDataType = StaticConfig.CodeFirst_BigString, ColumnDescription = "报警属性Json", IsNullable = true)]
|
||||||
[AutoGenerateColumn(Visible = false, Filterable = true, Sortable = true)]
|
[IgnoreExcel]
|
||||||
public bool BoolOpenAlarmEnable { get => boolOpenAlarmEnable; set => boolOpenAlarmEnable = value; }
|
[AutoGenerateColumn(Ignore = true)]
|
||||||
|
public AlarmPropertys? AlarmPropertys { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 布尔开报警约束
|
|
||||||
/// </summary>
|
|
||||||
[SugarColumn(ColumnDescription = "布尔开报警约束", IsNullable = true)]
|
|
||||||
[AutoGenerateColumn(Visible = false, Filterable = true, Sortable = true)]
|
|
||||||
public string BoolOpenRestrainExpressions { get => boolOpenRestrainExpressions; set => boolOpenRestrainExpressions = value; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 布尔开报警文本
|
|
||||||
/// </summary>
|
|
||||||
[SugarColumn(ColumnDescription = "布尔开报警文本", IsNullable = true)]
|
|
||||||
[AutoGenerateColumn(Visible = false, Filterable = true, Sortable = true)]
|
|
||||||
public string BoolOpenAlarmText { get => boolOpenAlarmText; set => boolOpenAlarmText = value; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 布尔关报警使能
|
|
||||||
/// </summary>
|
|
||||||
[SugarColumn(ColumnDescription = "布尔关报警使能")]
|
|
||||||
[AutoGenerateColumn(Visible = false, Filterable = true, Sortable = true)]
|
|
||||||
public bool BoolCloseAlarmEnable { get => boolCloseAlarmEnable; set => boolCloseAlarmEnable = value; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 布尔关报警约束
|
|
||||||
/// </summary>
|
|
||||||
[SugarColumn(ColumnDescription = "布尔关报警约束", IsNullable = true)]
|
|
||||||
[AutoGenerateColumn(Visible = false, Filterable = true, Sortable = true)]
|
|
||||||
public string BoolCloseRestrainExpressions { get => boolCloseRestrainExpressions; set => boolCloseRestrainExpressions = value; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 布尔关报警文本
|
|
||||||
/// </summary>
|
|
||||||
[SugarColumn(ColumnDescription = "布尔关报警文本", IsNullable = true)]
|
|
||||||
[AutoGenerateColumn(Visible = false, Filterable = true, Sortable = true)]
|
|
||||||
public string BoolCloseAlarmText { get => boolCloseAlarmText; set => boolCloseAlarmText = value; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 高报使能
|
|
||||||
/// </summary>
|
|
||||||
[SugarColumn(ColumnDescription = "高报使能")]
|
|
||||||
[AutoGenerateColumn(Visible = false, Filterable = true, Sortable = true)]
|
|
||||||
public bool HAlarmEnable { get => hAlarmEnable; set => hAlarmEnable = value; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 高报约束
|
|
||||||
/// </summary>
|
|
||||||
[SugarColumn(ColumnDescription = "高报约束", IsNullable = true)]
|
|
||||||
[AutoGenerateColumn(Visible = false, Filterable = true, Sortable = true)]
|
|
||||||
public string HRestrainExpressions { get => hRestrainExpressions; set => hRestrainExpressions = value; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 高报文本
|
|
||||||
/// </summary>
|
|
||||||
[SugarColumn(ColumnDescription = "高报文本", IsNullable = true)]
|
|
||||||
[AutoGenerateColumn(Visible = false, Filterable = true, Sortable = true)]
|
|
||||||
public string HAlarmText { get => hAlarmText; set => hAlarmText = value; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 高限值
|
|
||||||
/// </summary>
|
|
||||||
[SugarColumn(ColumnDescription = "高限值", IsNullable = true)]
|
|
||||||
[AutoGenerateColumn(Visible = false, Filterable = true, Sortable = true)]
|
|
||||||
public double HAlarmCode { get => hAlarmCode; set => hAlarmCode = value; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 高高报使能
|
|
||||||
/// </summary>
|
|
||||||
[SugarColumn(ColumnDescription = "高高报使能")]
|
|
||||||
[AutoGenerateColumn(Visible = false, Filterable = true, Sortable = true)]
|
|
||||||
public bool HHAlarmEnable { get => hHAlarmEnable; set => hHAlarmEnable = value; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 高高报约束
|
|
||||||
/// </summary>
|
|
||||||
[SugarColumn(ColumnDescription = "高高报约束", IsNullable = true)]
|
|
||||||
[AutoGenerateColumn(Visible = false, Filterable = true, Sortable = true)]
|
|
||||||
public string HHRestrainExpressions { get => hHRestrainExpressions; set => hHRestrainExpressions = value; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 高高报文本
|
|
||||||
/// </summary>
|
|
||||||
[SugarColumn(ColumnDescription = "高高报文本", IsNullable = true)]
|
|
||||||
[AutoGenerateColumn(Visible = false, Filterable = true, Sortable = true)]
|
|
||||||
public string HHAlarmText { get => hHAlarmText; set => hHAlarmText = value; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 高高限值
|
|
||||||
/// </summary>
|
|
||||||
[SugarColumn(ColumnDescription = "高高限值", IsNullable = true)]
|
|
||||||
[AutoGenerateColumn(Visible = false, Filterable = true, Sortable = true)]
|
|
||||||
public double HHAlarmCode { get => hHAlarmCode; set => hHAlarmCode = value; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 低报使能
|
|
||||||
/// </summary>
|
|
||||||
[SugarColumn(ColumnDescription = "低报使能")]
|
|
||||||
[AutoGenerateColumn(Visible = false, Filterable = true, Sortable = true)]
|
|
||||||
public bool LAlarmEnable { get => lAlarmEnable; set => lAlarmEnable = value; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 低报约束
|
|
||||||
/// </summary>
|
|
||||||
[SugarColumn(ColumnDescription = "低报约束", IsNullable = true)]
|
|
||||||
[AutoGenerateColumn(Visible = false, Filterable = true, Sortable = true)]
|
|
||||||
public string LRestrainExpressions { get => lRestrainExpressions; set => lRestrainExpressions = value; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 低报文本
|
|
||||||
/// </summary>
|
|
||||||
[SugarColumn(ColumnDescription = "低报文本", IsNullable = true)]
|
|
||||||
[AutoGenerateColumn(Visible = false, Filterable = true, Sortable = true)]
|
|
||||||
public string LAlarmText { get => lAlarmText; set => lAlarmText = value; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 低限值
|
|
||||||
/// </summary>
|
|
||||||
[SugarColumn(ColumnDescription = "低限值", IsNullable = true)]
|
|
||||||
[AutoGenerateColumn(Visible = false, Filterable = true, Sortable = true)]
|
|
||||||
public double LAlarmCode { get => lAlarmCode; set => lAlarmCode = value; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 低低报使能
|
|
||||||
/// </summary>
|
|
||||||
[SugarColumn(ColumnDescription = "低低报使能")]
|
|
||||||
[AutoGenerateColumn(Visible = false, Filterable = true, Sortable = true)]
|
|
||||||
public bool LLAlarmEnable { get => lLAlarmEnable; set => lLAlarmEnable = value; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 低低报约束
|
|
||||||
/// </summary>
|
|
||||||
[SugarColumn(ColumnDescription = "低低报约束", IsNullable = true)]
|
|
||||||
[AutoGenerateColumn(Visible = false, Filterable = true, Sortable = true)]
|
|
||||||
public string LLRestrainExpressions { get => lLRestrainExpressions; set => lLRestrainExpressions = value; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 低低报文本
|
|
||||||
/// </summary>
|
|
||||||
[SugarColumn(ColumnDescription = "低低报文本", IsNullable = true)]
|
|
||||||
[AutoGenerateColumn(Visible = false, Filterable = true, Sortable = true)]
|
|
||||||
public string LLAlarmText { get => lLAlarmText; set => lLAlarmText = value; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 低低限值
|
|
||||||
/// </summary>
|
|
||||||
[SugarColumn(ColumnDescription = "低低限值", IsNullable = true)]
|
|
||||||
[AutoGenerateColumn(Visible = false, Filterable = true, Sortable = true)]
|
|
||||||
public double LLAlarmCode { get => lLAlarmCode; set => lLAlarmCode = value; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 自定义报警使能
|
|
||||||
/// </summary>
|
|
||||||
[SugarColumn(ColumnDescription = "自定义报警使能")]
|
|
||||||
[AutoGenerateColumn(Visible = false, Filterable = true, Sortable = true)]
|
|
||||||
public bool CustomAlarmEnable { get => customAlarmEnable; set => customAlarmEnable = value; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 自定义报警条件约束
|
|
||||||
/// </summary>
|
|
||||||
[SugarColumn(ColumnDescription = "自定义报警条件约束", IsNullable = true)]
|
|
||||||
[AutoGenerateColumn(Visible = false, Filterable = true, Sortable = true)]
|
|
||||||
public string CustomRestrainExpressions { get => customRestrainExpressions; set => customRestrainExpressions = value; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 自定义文本
|
|
||||||
/// </summary>
|
|
||||||
[SugarColumn(ColumnDescription = "自定义文本", IsNullable = true)]
|
|
||||||
[AutoGenerateColumn(Visible = false, Filterable = true, Sortable = true)]
|
|
||||||
public string CustomAlarmText { get => customAlarmText; set => customAlarmText = value; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 自定义报警条件
|
|
||||||
/// </summary>
|
|
||||||
[SugarColumn(ColumnDescription = "自定义报警条件", IsNullable = true)]
|
|
||||||
[AutoGenerateColumn(Visible = false, Filterable = true, Sortable = true)]
|
|
||||||
public string CustomAlarmCode { get => customAlarmCode; set => customAlarmCode = value; }
|
|
||||||
|
|
||||||
#endregion 报警
|
|
||||||
|
|
||||||
#region 备用字段
|
#region 备用字段
|
||||||
|
|
||||||
@@ -509,30 +308,6 @@ public class Variable : BaseDataEntity, IValidatableObject
|
|||||||
yield return new ValidationResult("Both RegisterAddress and OtherMethod cannot be empty or null.", new[] { nameof(OtherMethod), nameof(RegisterAddress) });
|
yield return new ValidationResult("Both RegisterAddress and OtherMethod cannot be empty or null.", new[] { nameof(OtherMethod), nameof(RegisterAddress) });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (HHAlarmEnable && HAlarmEnable && HHAlarmCode <= HAlarmCode)
|
|
||||||
{
|
|
||||||
yield return new ValidationResult("HHAlarmCode must be greater than HAlarmCode", new[] { nameof(HHAlarmCode), nameof(HAlarmCode) });
|
|
||||||
}
|
|
||||||
if (HAlarmEnable && LAlarmEnable && HAlarmCode <= LAlarmCode)
|
|
||||||
{
|
|
||||||
yield return new ValidationResult("HAlarmCode must be greater than LAlarmCode", new[] { nameof(HAlarmCode), nameof(LAlarmCode) });
|
|
||||||
}
|
|
||||||
if (LAlarmEnable && LLAlarmEnable && LAlarmCode <= LLAlarmCode)
|
|
||||||
{
|
|
||||||
yield return new ValidationResult("LAlarmCode must be greater than LLAlarmCode", new[] { nameof(LAlarmCode), nameof(LLAlarmCode) });
|
|
||||||
}
|
|
||||||
|
|
||||||
if (HHAlarmEnable && LAlarmEnable && HHAlarmCode <= LAlarmCode)
|
|
||||||
{
|
|
||||||
yield return new ValidationResult("HHAlarmCode should be greater than or less than LAlarmCode", new[] { nameof(HHAlarmCode), nameof(LAlarmCode) });
|
|
||||||
}
|
|
||||||
if (HHAlarmEnable && LLAlarmEnable && HHAlarmCode <= LLAlarmCode)
|
|
||||||
{
|
|
||||||
yield return new ValidationResult("HHAlarmCode should be greater than or less than LLAlarmCode", new[] { nameof(HHAlarmCode), nameof(LLAlarmCode) });
|
|
||||||
}
|
|
||||||
if (HAlarmEnable && LLAlarmEnable && HAlarmCode <= LLAlarmCode)
|
|
||||||
{
|
|
||||||
yield return new ValidationResult("HAlarmCode should be greater than or less than LLAlarmCode", new[] { nameof(HAlarmCode), nameof(LLAlarmCode) });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,4 +39,16 @@ public enum EventTypeEnum
|
|||||||
/// 准备恢复
|
/// 准备恢复
|
||||||
/// </summary>
|
/// </summary>
|
||||||
PrepareFinish,
|
PrepareFinish,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 报警确认并恢复
|
||||||
|
/// </summary>
|
||||||
|
ConfirmAndFinish,
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 重启默认
|
||||||
|
/// </summary>
|
||||||
|
Restart,
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,6 @@
|
|||||||
// QQ群:605534569
|
// QQ群:605534569
|
||||||
// ------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------
|
||||||
|
|
||||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
|
||||||
using Microsoft.Extensions.Hosting;
|
using Microsoft.Extensions.Hosting;
|
||||||
|
|
||||||
namespace Microsoft.Extensions.DependencyInjection;
|
namespace Microsoft.Extensions.DependencyInjection;
|
||||||
@@ -19,30 +18,19 @@ namespace Microsoft.Extensions.DependencyInjection;
|
|||||||
[ThingsGateway.DependencyInjection.SuppressSniffer]
|
[ThingsGateway.DependencyInjection.SuppressSniffer]
|
||||||
public static class ServiceCollectionHostedServiceExtensions
|
public static class ServiceCollectionHostedServiceExtensions
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// Add an <see cref="IHostedService"/> registration for the given type.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="THostedService">An <see cref="IHostedService"/> to register.</typeparam>
|
|
||||||
/// <param name="services">The <see cref="IServiceCollection"/> to register with.</param>
|
|
||||||
/// <returns>The original <see cref="IServiceCollection"/>.</returns>
|
|
||||||
public static IServiceCollection AddGatewayHostedService<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] THostedService>(this IServiceCollection services)
|
|
||||||
where THostedService : class, IHostedService
|
|
||||||
{
|
|
||||||
services.AddSingleton<THostedService>();
|
|
||||||
services.TryAddEnumerable(ServiceDescriptor.Singleton<IHostedService, THostedService>(seriveProvider => seriveProvider.GetService<THostedService>()));
|
|
||||||
|
|
||||||
return services;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Add an <see cref="IHostedService"/> registration for the given type.
|
/// Add an <see cref="IHostedService"/> registration for the given type.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static IServiceCollection AddGatewayHostedService<TService, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] THostedService>(this IServiceCollection services)
|
public static IServiceCollection AddGatewayHostedService<TService, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] THostedService>(this IServiceCollection services)
|
||||||
where TService : class, IHostedService
|
where TService : class
|
||||||
where THostedService : class, IHostedService, TService
|
where THostedService : class, IHostedService, TService
|
||||||
{
|
{
|
||||||
services.AddSingleton(typeof(TService), typeof(THostedService));
|
|
||||||
services.TryAddEnumerable(ServiceDescriptor.Singleton<IHostedService, TService>(seriveProvider => seriveProvider.GetService<TService>()));
|
services.AddSingleton<THostedService>();
|
||||||
|
services.AddHostedService<THostedService>(a => a.GetService<THostedService>());
|
||||||
|
services.AddSingleton<TService>(a => a.GetService<THostedService>());
|
||||||
|
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,58 +0,0 @@
|
|||||||
//------------------------------------------------------------------------------
|
|
||||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
|
||||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
|
||||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
|
||||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
|
||||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
|
||||||
// 使用文档:https://thingsgateway.cn/
|
|
||||||
// QQ群:605534569
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
namespace ThingsGateway.Gateway.Application
|
|
||||||
{
|
|
||||||
internal static class USheetDataHelpers
|
|
||||||
{
|
|
||||||
public static USheetDatas GetUSheetDatas(Dictionary<string, object> data)
|
|
||||||
{
|
|
||||||
var uSheetDatas = new USheetDatas();
|
|
||||||
|
|
||||||
foreach (var a in data)
|
|
||||||
{
|
|
||||||
var value = (a.Value as IEnumerable<Dictionary<string, object>>).ToList();
|
|
||||||
|
|
||||||
var uSheetData = new USheetData();
|
|
||||||
uSheetData.id = a.Key;
|
|
||||||
uSheetData.name = a.Key;
|
|
||||||
|
|
||||||
for (int row1 = 0; row1 < value.Count; row1++)
|
|
||||||
{
|
|
||||||
if (row1 == 0)
|
|
||||||
{
|
|
||||||
Dictionary<int, USheetCelldata> usheetColldata = new();
|
|
||||||
int col = 0;
|
|
||||||
foreach (var colData in value[row1])
|
|
||||||
{
|
|
||||||
usheetColldata.Add(col, new USheetCelldata() { v = colData.Key });
|
|
||||||
col++;
|
|
||||||
}
|
|
||||||
uSheetData.cellData.Add(row1, usheetColldata);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
Dictionary<int, USheetCelldata> usheetColldata = new();
|
|
||||||
int col = 0;
|
|
||||||
foreach (var colData in value[row1])
|
|
||||||
{
|
|
||||||
usheetColldata.Add(col, new USheetCelldata() { v = colData.Value });
|
|
||||||
col++;
|
|
||||||
}
|
|
||||||
uSheetData.cellData.Add(row1 + 1, usheetColldata);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
uSheetData.rowCount = uSheetData.cellData.Count + 100;
|
|
||||||
uSheetData.columnCount = uSheetData.cellData.FirstOrDefault().Value?.Count ?? 0;
|
|
||||||
uSheetDatas.sheets.Add(a.Key, uSheetData);
|
|
||||||
}
|
|
||||||
return uSheetDatas;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -83,7 +83,7 @@ public static class GlobalData
|
|||||||
.WhereIf(dataScope?.Count == 0, u => u.Value.CreateUserId == UserManager.UserId).Select(a => a.Value);
|
.WhereIf(dataScope?.Count == 0, u => u.Value.CreateUserId == UserManager.UserId).Select(a => a.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<IEnumerable<AlarmVariable>> GetCurrentUserRealAlarmVariables()
|
public static async Task<IEnumerable<AlarmVariable>> GetCurrentUserRealAlarmVariablesAsync()
|
||||||
{
|
{
|
||||||
var dataScope = await GlobalData.SysUserService.GetCurrentUserDataScopeAsync().ConfigureAwait(false);
|
var dataScope = await GlobalData.SysUserService.GetCurrentUserDataScopeAsync().ConfigureAwait(false);
|
||||||
return RealAlarmIdVariables.WhereIf(dataScope != null && dataScope?.Count > 0, u => dataScope.Contains(u.Value.CreateOrgId))//在指定机构列表查询
|
return RealAlarmIdVariables.WhereIf(dataScope != null && dataScope?.Count > 0, u => dataScope.Contains(u.Value.CreateOrgId))//在指定机构列表查询
|
||||||
@@ -177,6 +177,15 @@ public static class GlobalData
|
|||||||
}
|
}
|
||||||
return GlobalData.ChannelThreadManage.DeviceThreadManages.TryGetValue(deviceRuntime.ChannelId, out deviceThreadManage);
|
return GlobalData.ChannelThreadManage.DeviceThreadManages.TryGetValue(deviceRuntime.ChannelId, out deviceThreadManage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static IChannelThreadManage GetChannelThreadManage(ChannelRuntime channelRuntime)
|
||||||
|
{
|
||||||
|
if (channelRuntime.DeviceThreadManage?.ChannelThreadManage != null)
|
||||||
|
return channelRuntime.DeviceThreadManage.ChannelThreadManage;
|
||||||
|
else
|
||||||
|
return GlobalData.ChannelThreadManage;
|
||||||
|
}
|
||||||
|
|
||||||
public static Dictionary<IDeviceThreadManage, List<DeviceRuntime>> GetDeviceThreadManages(IEnumerable<DeviceRuntime> deviceRuntimes)
|
public static Dictionary<IDeviceThreadManage, List<DeviceRuntime>> GetDeviceThreadManages(IEnumerable<DeviceRuntime> deviceRuntimes)
|
||||||
{
|
{
|
||||||
Dictionary<IDeviceThreadManage, List<DeviceRuntime>> deviceThreadManages = new();
|
Dictionary<IDeviceThreadManage, List<DeviceRuntime>> deviceThreadManages = new();
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ public class LogJob : IJob
|
|||||||
await DeleteRpcLog(rpcLogDaysdaysAgo, stoppingToken).ConfigureAwait(false);
|
await DeleteRpcLog(rpcLogDaysdaysAgo, stoppingToken).ConfigureAwait(false);
|
||||||
await DeleteBackendLog(backendLogdaysAgo, stoppingToken).ConfigureAwait(false);
|
await DeleteBackendLog(backendLogdaysAgo, stoppingToken).ConfigureAwait(false);
|
||||||
await DeleteTextLog(stoppingToken).ConfigureAwait(false);
|
await DeleteTextLog(stoppingToken).ConfigureAwait(false);
|
||||||
await DeleteLocalDB(stoppingToken).ConfigureAwait(false);
|
DeleteLocalDB(stoppingToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task DeleteRpcLog(int daysAgo, CancellationToken stoppingToken)
|
private static async Task DeleteRpcLog(int daysAgo, CancellationToken stoppingToken)
|
||||||
@@ -49,8 +49,8 @@ public class LogJob : IJob
|
|||||||
//网关通道日志以通道id命名
|
//网关通道日志以通道id命名
|
||||||
var channelService = App.RootServices.GetService<IChannelService>();
|
var channelService = App.RootServices.GetService<IChannelService>();
|
||||||
var deviceService = App.RootServices.GetService<IDeviceService>();
|
var deviceService = App.RootServices.GetService<IDeviceService>();
|
||||||
var channelNames = (await channelService.GetAllAsync().ConfigureAwait(false)).Select(a => a.Name.ToString()).ToHashSet();
|
var channelNames = (GlobalData.Channels.Keys).ToHashSet();
|
||||||
var deviceNames = (await deviceService.GetAllAsync().ConfigureAwait(false)).Select(a => a.Name.ToString()).ToHashSet();
|
var deviceNames = (GlobalData.Devices.Keys).ToHashSet();
|
||||||
var channelBaseDir = LoggerExtensions.GetChannelLogBasePath();
|
var channelBaseDir = LoggerExtensions.GetChannelLogBasePath();
|
||||||
Directory.CreateDirectory(channelBaseDir);
|
Directory.CreateDirectory(channelBaseDir);
|
||||||
var deviceBaseDir = LoggerExtensions.GetDeviceLogBasePath();
|
var deviceBaseDir = LoggerExtensions.GetDeviceLogBasePath();
|
||||||
@@ -112,10 +112,9 @@ public class LogJob : IJob
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task DeleteLocalDB(CancellationToken stoppingToken)
|
public void DeleteLocalDB(CancellationToken stoppingToken)
|
||||||
{
|
{
|
||||||
var deviceService = App.RootServices.GetService<IDeviceService>();
|
var data = (GlobalData.Devices.Keys).ToHashSet();
|
||||||
var data = (await deviceService.GetAllAsync().ConfigureAwait(false)).Select(a => a.Name).ToHashSet();
|
|
||||||
var dir = CacheDBUtil.GetCacheFileBasePath();
|
var dir = CacheDBUtil.GetCacheFileBasePath();
|
||||||
string[] dirs = Directory.GetDirectories(dir);
|
string[] dirs = Directory.GetDirectories(dir);
|
||||||
foreach (var item in dirs)
|
foreach (var item in dirs)
|
||||||
|
|||||||
@@ -1,6 +1,32 @@
|
|||||||
{
|
{
|
||||||
|
"ThingsGateway.Management.Application.ExportString": {
|
||||||
|
|
||||||
"ThingsGateway.Gateway.Application.DefaultDiagram": {
|
"ManagementConfigName": "ManagementConfigName"
|
||||||
|
},
|
||||||
|
"ThingsGateway.Management.Application.ManagementConfig": {
|
||||||
|
|
||||||
|
"Name": "Name",
|
||||||
|
"ServerUri": "ServerUri",
|
||||||
|
"Enable": "Enable",
|
||||||
|
"IsServer": "IsServer",
|
||||||
|
"VerifyToken": "VerifyToken",
|
||||||
|
"HeartbeatInterval": "HeartbeatInterval",
|
||||||
|
|
||||||
|
"ImportNullError": "Unable to recognize"
|
||||||
|
},
|
||||||
|
|
||||||
|
"ThingsGateway.Management.Application.UpdateZipFile": {
|
||||||
|
"AppName": "AppName",
|
||||||
|
"Architecture": "Architecture",
|
||||||
|
"DotNetVersion": "DotNetVersion",
|
||||||
|
"FilePath": "FilePath",
|
||||||
|
"FileSize": "FileSize",
|
||||||
|
"MinimumCompatibleVersion": "MinimumCompatibleVersion",
|
||||||
|
"OSPlatform": "OSPlatform",
|
||||||
|
"Version": "Version"
|
||||||
|
},
|
||||||
|
|
||||||
|
"ThingsGateway.Gateway.Application.INode": {
|
||||||
|
|
||||||
"Actuator": "Actuator",
|
"Actuator": "Actuator",
|
||||||
"AlarmChangedTriggerNode": "AlarmStateTrigger",
|
"AlarmChangedTriggerNode": "AlarmStateTrigger",
|
||||||
@@ -53,11 +79,7 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
"ThingsGateway.Management.AutoUpdateController": {
|
"ThingsGateway.Gateway.Application.RedundancyHostedService": {
|
||||||
"AutoUpdateController": "AutoUpdate",
|
|
||||||
"Update": "Update"
|
|
||||||
},
|
|
||||||
"ThingsGateway.Management.RedundancyHostedService": {
|
|
||||||
"ErrorSynchronizingData": "Synchronize data to standby site error",
|
"ErrorSynchronizingData": "Synchronize data to standby site error",
|
||||||
"RedundancyDisable": "Redundant gateway site not enabled",
|
"RedundancyDisable": "Redundant gateway site not enabled",
|
||||||
"RedundancyDup": "Redundant station settings duplicated",
|
"RedundancyDup": "Redundant station settings duplicated",
|
||||||
@@ -67,10 +89,10 @@
|
|||||||
"SwitchNormalState": "Local machine (primary site) will switch to normal state",
|
"SwitchNormalState": "Local machine (primary site) will switch to normal state",
|
||||||
"SwitchSlaveState": "Master site has recovered, local machine (standby) will switch to standby state"
|
"SwitchSlaveState": "Master site has recovered, local machine (standby) will switch to standby state"
|
||||||
},
|
},
|
||||||
"ThingsGateway.Management.RedundancyOptions": {
|
"ThingsGateway.Gateway.Application.RedundancyOptions": {
|
||||||
"Confirm": "Confirm switching to redundant state",
|
"Confirm": "Confirm switching to redundant state",
|
||||||
"Enable": "Enable Dual-Machine Redundancy",
|
"Enable": "Enable Dual-Machine Redundancy",
|
||||||
"ForcedSync": "Forced Synchronous",
|
"RedundancyForcedSync": "Forced Synchronous",
|
||||||
"ForcedSyncWarning": "Forcing synchronization will generate database configuration information.Are you sure you want to continue?",
|
"ForcedSyncWarning": "Forcing synchronization will generate database configuration information.Are you sure you want to continue?",
|
||||||
"HeartbeatInterval": "Heartbeat Interval",
|
"HeartbeatInterval": "Heartbeat Interval",
|
||||||
"IsMaster": "IsMaster",
|
"IsMaster": "IsMaster",
|
||||||
@@ -86,13 +108,13 @@
|
|||||||
"SyncInterval": "Data Synchronization Interval",
|
"SyncInterval": "Data Synchronization Interval",
|
||||||
"VerifyToken": "Verification Token"
|
"VerifyToken": "Verification Token"
|
||||||
},
|
},
|
||||||
"ThingsGateway.Management.RedundancyService": {
|
"ThingsGateway.Gateway.Application.RedundancyService": {
|
||||||
"EditRedundancyOption": "EditRedundancyOption"
|
"EditRedundancyOption": "EditRedundancyOption"
|
||||||
},
|
},
|
||||||
"ThingsGateway.Management.UpdateZipFileHostedService": {
|
"ThingsGateway.Gateway.Application.UpdateZipFileService": {
|
||||||
"Update": "New version detected"
|
"Update": "New version detected"
|
||||||
},
|
},
|
||||||
"ThingsGateway.Upgrade.UpdateZipFile": {
|
"ThingsGateway.Gateway.Application.UpdateZipFile": {
|
||||||
"AppName": "AppName",
|
"AppName": "AppName",
|
||||||
"Architecture": "Architecture",
|
"Architecture": "Architecture",
|
||||||
"DotNetVersion": "DotNetVersion",
|
"DotNetVersion": "DotNetVersion",
|
||||||
@@ -106,6 +128,7 @@
|
|||||||
"ThingsGateway.Gateway.Application.AlarmVariable": {
|
"ThingsGateway.Gateway.Application.AlarmVariable": {
|
||||||
"AlarmCode": "AlarmCode",
|
"AlarmCode": "AlarmCode",
|
||||||
"AlarmDelay": "AlarmDelay",
|
"AlarmDelay": "AlarmDelay",
|
||||||
|
"AlarmLevel": "AlarmLevel",
|
||||||
"AlarmEnable": "AlarmEnable",
|
"AlarmEnable": "AlarmEnable",
|
||||||
"AlarmLimit": "AlarmLimit",
|
"AlarmLimit": "AlarmLimit",
|
||||||
"AlarmText": "AlarmText",
|
"AlarmText": "AlarmText",
|
||||||
@@ -132,6 +155,8 @@
|
|||||||
"DeviceName": "DeviceName",
|
"DeviceName": "DeviceName",
|
||||||
"Enable": "Enable",
|
"Enable": "Enable",
|
||||||
"EventTime": "EventTime",
|
"EventTime": "EventTime",
|
||||||
|
"ConfirmTime": "ConfirmTime",
|
||||||
|
"FinishTime": "FinishTime",
|
||||||
"EventType": "EventType",
|
"EventType": "EventType",
|
||||||
"HAlarmCode": "HAlarmCode",
|
"HAlarmCode": "HAlarmCode",
|
||||||
"HAlarmEnable": "HAlarmEnable",
|
"HAlarmEnable": "HAlarmEnable",
|
||||||
@@ -375,6 +400,7 @@
|
|||||||
"BusinessDeviceName": "BusinessDevice",
|
"BusinessDeviceName": "BusinessDevice",
|
||||||
"ChannelName": "Channel",
|
"ChannelName": "Channel",
|
||||||
"DeviceName": "Device",
|
"DeviceName": "Device",
|
||||||
|
"AlarmName": "Alarm",
|
||||||
"RedundantDeviceName": "Redundant Device",
|
"RedundantDeviceName": "Redundant Device",
|
||||||
"VariableName": "Variable"
|
"VariableName": "Variable"
|
||||||
},
|
},
|
||||||
@@ -436,24 +462,16 @@
|
|||||||
},
|
},
|
||||||
"ThingsGateway.Gateway.Application.Variable": {
|
"ThingsGateway.Gateway.Application.Variable": {
|
||||||
"AddressOrOtherMethodNotNull": "Variable address or special method cannot be empty at the same time",
|
"AddressOrOtherMethodNotNull": "Variable address or special method cannot be empty at the same time",
|
||||||
"AlarmDelay": "AlarmDelay",
|
|
||||||
"ArrayLength": "ArrayLength",
|
"ArrayLength": "ArrayLength",
|
||||||
"BoolCloseAlarmEnable": "BoolCloseAlarmEnable",
|
|
||||||
"BoolCloseAlarmText": "BoolCloseAlarmText",
|
|
||||||
"BoolCloseRestrainExpressions": "BoolCloseRestrainExpressions",
|
|
||||||
"BoolOpenAlarmEnable": "BoolOpenAlarmEnable",
|
|
||||||
"BoolOpenAlarmText": "BoolOpenAlarmText",
|
|
||||||
"BoolOpenRestrainExpressions": "BoolOpenRestrainExpressions",
|
|
||||||
"BusinessGroup": "BusinessGroup",
|
"BusinessGroup": "BusinessGroup",
|
||||||
"BusinessGroupUpdateTrigger": "BusinessGroupUpdateTrigger",
|
"BusinessGroupUpdateTrigger": "BusinessGroupUpdateTrigger",
|
||||||
"RpcWriteCheck": "RpcWriteCheck",
|
"RpcWriteCheck": "RpcWriteCheck",
|
||||||
"ClearVariable": "Clear Variable",
|
"ClearVariable": "Clear Variable",
|
||||||
"CollectGroup": "CollectGroup",
|
"CollectGroup": "CollectGroup",
|
||||||
"CopyVariable": "Copy Variable",
|
"CopyVariable": "Copy Variable",
|
||||||
"CustomAlarmCode": "CustomAlarmCode",
|
|
||||||
"CustomAlarmEnable": "CustomAlarmEnable",
|
|
||||||
"CustomAlarmText": "CustomAlarmText",
|
|
||||||
"CustomRestrainExpressions": "CustomRestrainExpressions",
|
|
||||||
"DataType": "DataType",
|
"DataType": "DataType",
|
||||||
"DeleteVariable": "Delete Variable",
|
"DeleteVariable": "Delete Variable",
|
||||||
"Description": "Description",
|
"Description": "Description",
|
||||||
@@ -464,26 +482,14 @@
|
|||||||
"DynamicVariable": "DynamicVariable",
|
"DynamicVariable": "DynamicVariable",
|
||||||
"Enable": "Enable",
|
"Enable": "Enable",
|
||||||
"ExportVariable": "Export Variable",
|
"ExportVariable": "Export Variable",
|
||||||
"HAlarmCode": "HAlarmCode",
|
|
||||||
"HAlarmEnable": "HAlarmEnable",
|
|
||||||
"HAlarmText": "HAlarmText",
|
|
||||||
"HHAlarmCode": "HHAlarmCode",
|
|
||||||
"HHAlarmEnable": "HHAlarmEnable",
|
|
||||||
"HHAlarmText": "HHAlarmText",
|
|
||||||
"HHRestrainExpressions": "HHRestrainExpressions",
|
|
||||||
"HRestrainExpressions": "HRestrainExpressions",
|
|
||||||
"ImportVariable": "Import Variable",
|
"ImportVariable": "Import Variable",
|
||||||
"InitValue": "InitValue",
|
"InitValue": "InitValue",
|
||||||
"IntervalTime": "IntervalTime",
|
"IntervalTime": "IntervalTime",
|
||||||
"IntervalTime.MinValue": "{0} value is too small",
|
"IntervalTime.MinValue": "{0} value is too small",
|
||||||
"LAlarmCode": "LAlarmCode",
|
|
||||||
"LAlarmEnable": "LAlarmEnable",
|
|
||||||
"LAlarmText": "LAlarmText",
|
|
||||||
"LLAlarmCode": "LLAlarmCode",
|
|
||||||
"LLAlarmEnable": "LLAlarmEnable",
|
|
||||||
"LLAlarmText": "LLAlarmText",
|
|
||||||
"LLRestrainExpressions": "LLRestrainExpressions",
|
|
||||||
"LRestrainExpressions": "LRestrainExpressions",
|
|
||||||
"Name": "Name",
|
"Name": "Name",
|
||||||
"Name.Required": "{0} cannot be empty",
|
"Name.Required": "{0} cannot be empty",
|
||||||
"NameDump": "Duplicate variable name {0}",
|
"NameDump": "Duplicate variable name {0}",
|
||||||
@@ -505,6 +511,38 @@
|
|||||||
"VariableNotNull": "Variable name does not exist",
|
"VariableNotNull": "Variable name does not exist",
|
||||||
"WriteExpressions": "WriteExpressions"
|
"WriteExpressions": "WriteExpressions"
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"ThingsGateway.Gateway.Application.AlarmPropertys": {
|
||||||
|
|
||||||
|
"AlarmDelay": "AlarmDelay",
|
||||||
|
"AlarmLevel": "AlarmLevel",
|
||||||
|
"BoolCloseAlarmEnable": "BoolCloseAlarmEnable",
|
||||||
|
"BoolCloseAlarmText": "BoolCloseAlarmText",
|
||||||
|
"BoolCloseRestrainExpressions": "BoolCloseRestrainExpressions",
|
||||||
|
"BoolOpenAlarmEnable": "BoolOpenAlarmEnable",
|
||||||
|
"BoolOpenAlarmText": "BoolOpenAlarmText",
|
||||||
|
"BoolOpenRestrainExpressions": "BoolOpenRestrainExpressions",
|
||||||
|
"CustomAlarmCode": "CustomAlarmCode",
|
||||||
|
"CustomAlarmEnable": "CustomAlarmEnable",
|
||||||
|
"CustomAlarmText": "CustomAlarmText",
|
||||||
|
"CustomRestrainExpressions": "CustomRestrainExpressions",
|
||||||
|
"HAlarmCode": "HAlarmCode",
|
||||||
|
"HAlarmEnable": "HAlarmEnable",
|
||||||
|
"HAlarmText": "HAlarmText",
|
||||||
|
"HHAlarmCode": "HHAlarmCode",
|
||||||
|
"HHAlarmEnable": "HHAlarmEnable",
|
||||||
|
"HHAlarmText": "HHAlarmText",
|
||||||
|
"HHRestrainExpressions": "HHRestrainExpressions",
|
||||||
|
"HRestrainExpressions": "HRestrainExpressions",
|
||||||
|
"LAlarmCode": "LAlarmCode",
|
||||||
|
"LAlarmEnable": "LAlarmEnable",
|
||||||
|
"LAlarmText": "LAlarmText",
|
||||||
|
"LLAlarmCode": "LLAlarmCode",
|
||||||
|
"LLAlarmEnable": "LLAlarmEnable",
|
||||||
|
"LLAlarmText": "LLAlarmText",
|
||||||
|
"LLRestrainExpressions": "LLRestrainExpressions",
|
||||||
|
"LRestrainExpressions": "LRestrainExpressions"
|
||||||
|
},
|
||||||
"ThingsGateway.Gateway.Application.VariableRuntime": {
|
"ThingsGateway.Gateway.Application.VariableRuntime": {
|
||||||
"AlarmCode": "AlarmCode",
|
"AlarmCode": "AlarmCode",
|
||||||
"AlarmEnable": "AlarmEnable",
|
"AlarmEnable": "AlarmEnable",
|
||||||
@@ -519,6 +557,8 @@
|
|||||||
"DeviceName": "DeviceName",
|
"DeviceName": "DeviceName",
|
||||||
"DynamicVariable": "DynamicVariable",
|
"DynamicVariable": "DynamicVariable",
|
||||||
"EventTime": "EventTime",
|
"EventTime": "EventTime",
|
||||||
|
"ConfirmTime": "ConfirmTime",
|
||||||
|
"FinishTime": "FinishTime",
|
||||||
"EventType": "EventType",
|
"EventType": "EventType",
|
||||||
"IntervalTime.MinValue": "{0} value is too small",
|
"IntervalTime.MinValue": "{0} value is too small",
|
||||||
"IsOnline": "IsOnline",
|
"IsOnline": "IsOnline",
|
||||||
|
|||||||
@@ -1,6 +1,34 @@
|
|||||||
{
|
{
|
||||||
|
"ThingsGateway.Management.Application.ExportString": {
|
||||||
|
|
||||||
"ThingsGateway.Gateway.Application.DefaultDiagram": {
|
"ManagementConfigName": "通讯配置"
|
||||||
|
},
|
||||||
|
"ThingsGateway.Management.Application.ManagementConfig": {
|
||||||
|
|
||||||
|
"Name": "名称",
|
||||||
|
"ServerUri": "服务端Url",
|
||||||
|
"Enable": "启用",
|
||||||
|
"IsServer": "服务端",
|
||||||
|
"VerifyToken": "验证令牌",
|
||||||
|
"HeartbeatInterval": "心跳间隔",
|
||||||
|
|
||||||
|
"ImportNullError": "无法识别"
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
"ThingsGateway.Management.Application.UpdateZipFile": {
|
||||||
|
"AppName": "名称",
|
||||||
|
"Architecture": "架构",
|
||||||
|
"DotNetVersion": ".net版本",
|
||||||
|
"FilePath": "文件路径",
|
||||||
|
"FileSize": "文件大小",
|
||||||
|
"MinimumCompatibleVersion": "最小兼容版本",
|
||||||
|
"OSPlatform": "系统版本",
|
||||||
|
"Version": "版本"
|
||||||
|
},
|
||||||
|
|
||||||
|
"ThingsGateway.Gateway.Application.INode": {
|
||||||
|
|
||||||
"Actuator": "执行",
|
"Actuator": "执行",
|
||||||
"AlarmChangedTriggerNode": "报警状态触发器",
|
"AlarmChangedTriggerNode": "报警状态触发器",
|
||||||
@@ -51,11 +79,8 @@
|
|||||||
"RulesId": "名称"
|
"RulesId": "名称"
|
||||||
},
|
},
|
||||||
|
|
||||||
"ThingsGateway.Management.AutoUpdateController": {
|
|
||||||
"AutoUpdateController": "程序更新",
|
"ThingsGateway.Gateway.Application.RedundancyHostedService": {
|
||||||
"Update": "更新"
|
|
||||||
},
|
|
||||||
"ThingsGateway.Management.RedundancyHostedService": {
|
|
||||||
"ErrorSynchronizingData": "同步数据到从站错误",
|
"ErrorSynchronizingData": "同步数据到从站错误",
|
||||||
"RedundancyDisable": "不启用网关冗余站点",
|
"RedundancyDisable": "不启用网关冗余站点",
|
||||||
"RedundancyDup": "主备站设置重复",
|
"RedundancyDup": "主备站设置重复",
|
||||||
@@ -65,10 +90,10 @@
|
|||||||
"SwitchNormalState": "本机(主站)将切换到正常状态",
|
"SwitchNormalState": "本机(主站)将切换到正常状态",
|
||||||
"SwitchSlaveState": "主站已恢复,本机(从站)将切换到备用状态"
|
"SwitchSlaveState": "主站已恢复,本机(从站)将切换到备用状态"
|
||||||
},
|
},
|
||||||
"ThingsGateway.Management.RedundancyOptions": {
|
"ThingsGateway.Gateway.Application.RedundancyOptions": {
|
||||||
"Confirm": "确认切换冗余状态",
|
"Confirm": "确认切换冗余状态",
|
||||||
"Enable": "启用双机冗余",
|
"Enable": "启用双机冗余",
|
||||||
"ForcedSync": "强制同步",
|
"RedundancyForcedSync": "强制同步",
|
||||||
"ForcedSyncWarning": "强制同步会生成数据库配置信息,是否继续?",
|
"ForcedSyncWarning": "强制同步会生成数据库配置信息,是否继续?",
|
||||||
"HeartbeatInterval": "心跳间隔",
|
"HeartbeatInterval": "心跳间隔",
|
||||||
"IsMaster": "是否为主站",
|
"IsMaster": "是否为主站",
|
||||||
@@ -82,15 +107,15 @@
|
|||||||
"Status": "当前站点状态",
|
"Status": "当前站点状态",
|
||||||
"Switch": "切换",
|
"Switch": "切换",
|
||||||
"SyncInterval": "数据同步间隔",
|
"SyncInterval": "数据同步间隔",
|
||||||
"VerifyToken": "Token"
|
"VerifyToken": "验证令牌"
|
||||||
},
|
},
|
||||||
"ThingsGateway.Management.RedundancyService": {
|
"ThingsGateway.Gateway.Application.RedundancyService": {
|
||||||
"EditRedundancyOption": "修改网关冗余配置"
|
"EditRedundancyOption": "修改网关冗余配置"
|
||||||
},
|
},
|
||||||
"ThingsGateway.Management.UpdateZipFileHostedService": {
|
"ThingsGateway.Gateway.Application.UpdateZipFileService": {
|
||||||
"Update": "检测到新版本"
|
"Update": "检测到新版本"
|
||||||
},
|
},
|
||||||
"ThingsGateway.Upgrade.UpdateZipFile": {
|
"ThingsGateway.Gateway.Application.UpdateZipFile": {
|
||||||
"AppName": "名称",
|
"AppName": "名称",
|
||||||
"Architecture": "架构",
|
"Architecture": "架构",
|
||||||
"DotNetVersion": ".net版本",
|
"DotNetVersion": ".net版本",
|
||||||
@@ -105,6 +130,7 @@
|
|||||||
"ThingsGateway.Gateway.Application.AlarmVariable": {
|
"ThingsGateway.Gateway.Application.AlarmVariable": {
|
||||||
"AlarmCode": "报警值",
|
"AlarmCode": "报警值",
|
||||||
"AlarmDelay": "报警延时",
|
"AlarmDelay": "报警延时",
|
||||||
|
"AlarmLevel": "报警等级",
|
||||||
"AlarmEnable": "报警使能",
|
"AlarmEnable": "报警使能",
|
||||||
"AlarmLimit": "报警限值",
|
"AlarmLimit": "报警限值",
|
||||||
"AlarmText": "报警文本",
|
"AlarmText": "报警文本",
|
||||||
@@ -131,6 +157,8 @@
|
|||||||
"DeviceName": "设备名称",
|
"DeviceName": "设备名称",
|
||||||
"Enable": "变量使能",
|
"Enable": "变量使能",
|
||||||
"EventTime": "事件时间",
|
"EventTime": "事件时间",
|
||||||
|
"ConfirmTime": "确认时间",
|
||||||
|
"FinishTime": "恢复时间",
|
||||||
"EventType": "事件类型",
|
"EventType": "事件类型",
|
||||||
"HAlarmCode": "高限值",
|
"HAlarmCode": "高限值",
|
||||||
"HAlarmEnable": "高报使能",
|
"HAlarmEnable": "高报使能",
|
||||||
@@ -376,6 +404,7 @@
|
|||||||
"BusinessDeviceName": "业务设备",
|
"BusinessDeviceName": "业务设备",
|
||||||
"ChannelName": "通道",
|
"ChannelName": "通道",
|
||||||
"DeviceName": "设备",
|
"DeviceName": "设备",
|
||||||
|
"AlarmName": "报警",
|
||||||
"RedundantDeviceName": "冗余设备",
|
"RedundantDeviceName": "冗余设备",
|
||||||
"VariableName": "变量"
|
"VariableName": "变量"
|
||||||
},
|
},
|
||||||
@@ -438,6 +467,7 @@
|
|||||||
"ThingsGateway.Gateway.Application.Variable": {
|
"ThingsGateway.Gateway.Application.Variable": {
|
||||||
"AddressOrOtherMethodNotNull": " 变量地址或特殊方法不能同时为空 ",
|
"AddressOrOtherMethodNotNull": " 变量地址或特殊方法不能同时为空 ",
|
||||||
"AlarmDelay": "报警延时",
|
"AlarmDelay": "报警延时",
|
||||||
|
"AlarmLevel": "报警等级",
|
||||||
"ArrayLength": "数组长度",
|
"ArrayLength": "数组长度",
|
||||||
"BoolCloseAlarmEnable": "布尔关报警使能",
|
"BoolCloseAlarmEnable": "布尔关报警使能",
|
||||||
"BoolCloseAlarmText": "布尔关报警文本",
|
"BoolCloseAlarmText": "布尔关报警文本",
|
||||||
@@ -506,6 +536,40 @@
|
|||||||
"VariableNotNull": "变量名称不存在",
|
"VariableNotNull": "变量名称不存在",
|
||||||
"WriteExpressions": "写入表达式"
|
"WriteExpressions": "写入表达式"
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"ThingsGateway.Gateway.Application.AlarmPropertys": {
|
||||||
|
|
||||||
|
"AlarmDelay": "报警延时",
|
||||||
|
"AlarmLevel": "报警等级",
|
||||||
|
"BoolCloseAlarmEnable": "布尔关报警使能",
|
||||||
|
"BoolCloseAlarmText": "布尔关报警文本",
|
||||||
|
"BoolCloseRestrainExpressions": "布尔关报警约束",
|
||||||
|
"BoolOpenAlarmEnable": "布尔开报警使能",
|
||||||
|
"BoolOpenAlarmText": "布尔开报警文本",
|
||||||
|
"BoolOpenRestrainExpressions": "布尔开报警约束",
|
||||||
|
"CustomAlarmCode": "自定义报警限值",
|
||||||
|
"CustomAlarmEnable": "自定义报警使能",
|
||||||
|
"CustomAlarmText": "自定义报警文本",
|
||||||
|
"CustomRestrainExpressions": "自定义报警约束",
|
||||||
|
"HAlarmCode": "高限值",
|
||||||
|
"HAlarmEnable": "高报使能",
|
||||||
|
"HAlarmText": "高报文本",
|
||||||
|
"HHAlarmCode": "高高限值",
|
||||||
|
"HHAlarmEnable": "高高报使能",
|
||||||
|
"HHAlarmText": "高高报文本",
|
||||||
|
"HHRestrainExpressions": "高高报约束",
|
||||||
|
"HRestrainExpressions": "高报约束",
|
||||||
|
"LAlarmCode": "低限值",
|
||||||
|
"LAlarmEnable": "低报使能",
|
||||||
|
"LAlarmText": "低报文本",
|
||||||
|
"LLAlarmCode": "低低限值",
|
||||||
|
"LLAlarmEnable": "低低报使能",
|
||||||
|
"LLAlarmText": "低低报文本",
|
||||||
|
"LLRestrainExpressions": "低低报约束",
|
||||||
|
"LRestrainExpressions": "低报约束"
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
"ThingsGateway.Gateway.Application.VariableRuntime": {
|
"ThingsGateway.Gateway.Application.VariableRuntime": {
|
||||||
"AlarmCode": "报警值",
|
"AlarmCode": "报警值",
|
||||||
"AlarmEnable": "报警使能",
|
"AlarmEnable": "报警使能",
|
||||||
@@ -520,6 +584,8 @@
|
|||||||
"DeviceName": "设备名称",
|
"DeviceName": "设备名称",
|
||||||
"DynamicVariable": "动态变量",
|
"DynamicVariable": "动态变量",
|
||||||
"EventTime": "事件时间",
|
"EventTime": "事件时间",
|
||||||
|
"ConfirmTime": "确认时间",
|
||||||
|
"FinishTime": "恢复时间",
|
||||||
"EventType": "事件类型",
|
"EventType": "事件类型",
|
||||||
"IntervalTime.MinValue": " {0} 值太小",
|
"IntervalTime.MinValue": " {0} 值太小",
|
||||||
"IsOnline": "在线",
|
"IsOnline": "在线",
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user