mirror of
https://gitee.com/ThingsGateway/ThingsGateway.git
synced 2025-10-21 03:01:28 +08:00
Compare commits
4 Commits
10.10.14.0
...
10.10.16.0
Author | SHA1 | Date | |
---|---|---|---|
![]() |
280366e1b2 | ||
![]() |
6660ce3e34 | ||
![]() |
7499162c1a | ||
![]() |
40208a5cd6 |
@@ -123,8 +123,8 @@ public class BlazorAppContext
|
||||
}
|
||||
}
|
||||
var ownMenus = OwnMenus.Where(a => a.Module == CurrentModuleId);
|
||||
OwnMenuItems = ResourceUtil.BuildMenuTrees(ownMenus).ToList();
|
||||
AllOwnMenuItems = ResourceUtil.BuildMenuTrees(OwnMenus).ToList();
|
||||
OwnMenuItems = AdminResourceUtil.BuildMenuTrees(ownMenus).ToList();
|
||||
AllOwnMenuItems = AdminResourceUtil.BuildMenuTrees(OwnMenus).ToList();
|
||||
OwnSameLevelMenuItems = ownMenus.Where(a => !a.Href.IsNullOrWhiteSpace()).Select(item => new MenuItem()
|
||||
{
|
||||
Match = item.NavLinkMatch ?? Microsoft.AspNetCore.Components.Routing.NavLinkMatch.All,
|
||||
|
@@ -29,7 +29,7 @@ public partial class EditPagePolicy
|
||||
|
||||
protected override Task OnParametersSetAsync()
|
||||
{
|
||||
ShortcutsTreeViewItems = ResourceUtil.BuildTreeItemList(AppContext.OwnMenus.WhereIf(!ShortcutsSearchText.IsNullOrEmpty(), a => a.Title.Contains(ShortcutsSearchText)), Model.Shortcuts, null);
|
||||
ShortcutsTreeViewItems = AdminResourceUtil.BuildTreeItemList(AppContext.OwnMenus.WhereIf(!ShortcutsSearchText.IsNullOrEmpty(), a => a.Title.Contains(ShortcutsSearchText)), Model.Shortcuts, null);
|
||||
return base.OnParametersSetAsync();
|
||||
}
|
||||
|
||||
@@ -48,6 +48,6 @@ public partial class EditPagePolicy
|
||||
{
|
||||
await Task.CompletedTask;
|
||||
ShortcutsSearchText = searchText;
|
||||
return ResourceUtil.BuildTreeItemList(AppContext.OwnMenus.WhereIf(!ShortcutsSearchText.IsNullOrEmpty(), a => a.Title.Contains(ShortcutsSearchText)), Model.Shortcuts, null);
|
||||
return AdminResourceUtil.BuildTreeItemList(AppContext.OwnMenus.WhereIf(!ShortcutsSearchText.IsNullOrEmpty(), a => a.Title.Contains(ShortcutsSearchText)), Model.Shortcuts, null);
|
||||
}
|
||||
}
|
||||
|
@@ -41,7 +41,7 @@ public partial class MenuChoiceDialog
|
||||
var all = (await SysResourceService.GetAllAsync());
|
||||
var items = all.Where(a => a.Category == ResourceCategoryEnum.Menu && a.Module == ModuleId);
|
||||
ModuleTitle = all.FirstOrDefault(a => a.Id == ModuleId)?.Title;
|
||||
Items = ResourceUtil.BuildTreeItemList(items, new List<long> { Value }, RenderTreeItem);
|
||||
Items = AdminResourceUtil.BuildTreeItemList(items, new List<long> { Value }, RenderTreeItem);
|
||||
await base.OnParametersSetAsync();
|
||||
}
|
||||
|
||||
|
@@ -39,8 +39,8 @@ public partial class SysResourcePage
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
ModuleSelectedItems = ResourceUtil.BuildModuleSelectList((await SysResourceService.GetAllAsync())).ToList();
|
||||
MenuItems = ResourceUtil.BuildMenuSelectList((await SysResourceService.GetAllAsync())).Concat(new List<SelectedItem>() { new("0", AdminLocalizer["Root"]) }).ToList();
|
||||
ModuleSelectedItems = AdminResourceUtil.BuildModuleSelectList((await SysResourceService.GetAllAsync())).ToList();
|
||||
MenuItems = AdminResourceUtil.BuildMenuSelectList((await SysResourceService.GetAllAsync())).Concat(new List<SelectedItem>() { new("0", AdminLocalizer["Root"]) }).ToList();
|
||||
|
||||
await base.OnParametersSetAsync();
|
||||
}
|
||||
@@ -49,7 +49,7 @@ public partial class SysResourcePage
|
||||
|
||||
private async Task<QueryData<SysResource>> OnQueryAsync(QueryPageOptions options)
|
||||
{
|
||||
MenuTreeItems = new List<TreeViewItem<SysResource>>() { new TreeViewItem<SysResource>(new SysResource()) { Text = AdminLocalizer["Root"] } }.Concat(ResourceUtil.BuildTreeItemList((await SysResourceService.GetAllAsync()).Where(a => a.Module == CustomerSearchModel.Module), new(), null)).ToList();
|
||||
MenuTreeItems = new List<TreeViewItem<SysResource>>() { new TreeViewItem<SysResource>(new SysResource()) { Text = AdminLocalizer["Root"] } }.Concat(AdminResourceUtil.BuildTreeItemList((await SysResourceService.GetAllAsync()).Where(a => a.Module == CustomerSearchModel.Module), new(), null)).ToList();
|
||||
|
||||
var data = await SysResourceService.PageAsync(options, CustomerSearchModel);
|
||||
return data;
|
||||
@@ -136,14 +136,14 @@ public partial class SysResourcePage
|
||||
private async Task<IEnumerable<TableTreeNode<SysResource>>> OnTreeExpand(SysResource menu)
|
||||
{
|
||||
var sysResources = await SysResourceService.GetAllAsync();
|
||||
var result = ResourceUtil.BuildTableTrees(sysResources, menu.Id);
|
||||
var result = AdminResourceUtil.BuildTableTrees(sysResources, menu.Id);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static async Task<IEnumerable<TableTreeNode<SysResource>>> TreeNodeConverter(IEnumerable<SysResource> items)
|
||||
{
|
||||
await Task.CompletedTask;
|
||||
var result = ResourceUtil.BuildTableTrees(items, 0);
|
||||
var result = AdminResourceUtil.BuildTableTrees(items, 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@@ -35,7 +35,7 @@ public partial class GrantResourceDialog
|
||||
{
|
||||
var items = (await SysResourceService.GetAllAsync()).Where(a => a.Category != ResourceCategoryEnum.Module).OrderBy(a => a.Module).ThenBy(a => a.Id).ToList();
|
||||
|
||||
Items = ResourceUtil.BuildTreeItemList(items, Value, RenderTreeItem);
|
||||
Items = AdminResourceUtil.BuildTreeItemList(items, Value, RenderTreeItem);
|
||||
ModuleList = (await SysResourceService.GetAllAsync()).Where(a => a.Category == ResourceCategoryEnum.Module).ToList();
|
||||
await base.OnInitializedAsync();
|
||||
}
|
||||
|
@@ -35,7 +35,7 @@ public partial class SysUserEdit
|
||||
BoolItems = LocalizerUtil.GetBoolItems(Model.GetType(), nameof(Model.Status));
|
||||
var items = await SysPositionService.SelectorAsync(new PositionSelectorInput());
|
||||
Items = PositionUtil.BuildCascaderItemList(items);
|
||||
ModuleSelectedItems = ResourceUtil.BuildModuleSelectList((await SysResourceService.GetAllAsync())).ToList();
|
||||
ModuleSelectedItems = AdminResourceUtil.BuildModuleSelectList((await SysResourceService.GetAllAsync())).ToList();
|
||||
await InvokeAsync(StateHasChanged);
|
||||
await base.OnInitializedAsync();
|
||||
}
|
||||
|
@@ -14,7 +14,7 @@ namespace ThingsGateway.Admin.Razor;
|
||||
|
||||
/// <inheritdoc/>
|
||||
[ThingsGateway.DependencyInjection.SuppressSniffer]
|
||||
public static class ResourceUtil
|
||||
public static class AdminResourceUtil
|
||||
{
|
||||
/// <summary>
|
||||
/// 构造选择项,ID/TITLE
|
@@ -39,7 +39,7 @@
|
||||
<BlazorReconnector @rendermode="new InteractiveServerRenderMode(false)" />
|
||||
|
||||
<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>
|
||||
<!-- PWA Service Worker -->
|
||||
<script type="text/javascript">'serviceWorker' in navigator && navigator.serviceWorker.register('./service-worker.js')</script>
|
||||
|
@@ -45,7 +45,7 @@
|
||||
</app>
|
||||
|
||||
<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>
|
||||
|
||||
<!-- PWA Service Worker -->
|
||||
|
@@ -53,6 +53,8 @@ public static class QueryPageOptionsExtensions
|
||||
return datas;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static IEnumerable<T> GetQuery<T>(this IEnumerable<T> query, QueryPageOptions option, Func<IEnumerable<T>, IEnumerable<T>>? queryFunc = null, FilterKeyValueAction where = null)
|
||||
{
|
||||
if (queryFunc != null)
|
||||
@@ -135,4 +137,24 @@ public static class QueryPageOptionsExtensions
|
||||
ret.Items = items.ToList();
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据查询条件返回QueryData
|
||||
/// </summary>
|
||||
public static QueryData<SelectedItem> GetQueryData<T>(this IEnumerable<T> datas, VirtualizeQueryOption option, Func<IEnumerable<T>, IEnumerable<SelectedItem>> func, FilterKeyValueAction where = null)
|
||||
{
|
||||
var ret = new QueryData<SelectedItem>()
|
||||
{
|
||||
IsSorted = false,
|
||||
IsFiltered = false,
|
||||
IsAdvanceSearch = false,
|
||||
IsSearch = !option.SearchText.IsNullOrWhiteSpace()
|
||||
};
|
||||
|
||||
var items = datas.Skip((option.StartIndex)).Take(option.Count);
|
||||
ret.TotalCount = datas.Count();
|
||||
|
||||
ret.Items = func(items).ToList();
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
@@ -8,8 +8,8 @@
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using ThingsGateway.Common.Extension;
|
||||
using ThingsGateway.NewLife;
|
||||
using ThingsGateway.Razor.Extension;
|
||||
|
||||
namespace ThingsGateway.Razor;
|
||||
|
||||
|
@@ -5,7 +5,7 @@
|
||||
<Step @ref="@step" IsVertical="true">
|
||||
<StepItem Text=@Localizer["First"] Title=@Localizer["Upload"]>
|
||||
<InputUpload ShowDeleteButton="true" @bind-Value=_importFile Accept=".xlsx"></InputUpload>
|
||||
<Button class="mt-2" IsAsync OnClick="() => DeviceImport(_importFile)">@Localizer["Validate"]</Button>
|
||||
<PopConfirmButton IsAsync Color=Color.Warning class="mt-2" OnConfirm=@(SaveDeviceImport)>@Localizer["Import"]</PopConfirmButton>
|
||||
</StepItem>
|
||||
<StepItem Text=@Localizer["Second"] Title=@Localizer["ValidateText"]>
|
||||
|
||||
@@ -41,16 +41,12 @@
|
||||
|
||||
}
|
||||
|
||||
<PopConfirmButton IsAsync IsDisabled=@_importPreviews.Any(it => it.Value.HasError) Color=Color.Warning class="mt-2" OnConfirm=@(SaveDeviceImport)>@Localizer["Import"]</PopConfirmButton>
|
||||
<Button class="mt-2" IsAsync OnClick="() => DeviceImport()">@RazorLocalizer["Close"]</Button>
|
||||
|
||||
@*
|
||||
<Button IsAsync class="mt-2" IsDisabled=@_importPreviews.Any(it => it.Value.HasError) OnClick="() => step.Next()">@Localizer["Next"]</Button> *@
|
||||
|
||||
</div>
|
||||
</StepItem>
|
||||
@* <StepItem Text=@Localizer["Third"] Title=@Localizer["Import"]>
|
||||
<PopConfirmButton IsAsync Color=Color.Warning class="mt-2" OnConfirm=@(SaveDeviceImport)>@Localizer["Import"]</PopConfirmButton>
|
||||
</StepItem> *@
|
||||
|
||||
</Step>
|
||||
@code {
|
||||
[NotNull]
|
||||
|
@@ -24,18 +24,17 @@ public partial class ImportExcel
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
[EditorRequired]
|
||||
public Func<Dictionary<string, ImportPreviewOutputBase>, Task> Import { get; set; }
|
||||
public Func<IBrowserFile, Task<Dictionary<string, ImportPreviewOutputBase>>> Import { get; set; }
|
||||
|
||||
[Inject]
|
||||
[NotNull]
|
||||
private IStringLocalizer<ImportExcel>? Localizer { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 预览
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
[EditorRequired]
|
||||
public Func<IBrowserFile, Task<Dictionary<string, ImportPreviewOutputBase>>> Preview { get; set; }
|
||||
[Inject]
|
||||
[NotNull]
|
||||
private IStringLocalizer<ThingsGateway.Razor._Imports>? RazorLocalizer { get; set; }
|
||||
|
||||
|
||||
|
||||
[Inject]
|
||||
[NotNull]
|
||||
@@ -47,13 +46,17 @@ public partial class ImportExcel
|
||||
[CascadingParameter]
|
||||
private Func<Task>? OnCloseAsync { get; set; }
|
||||
|
||||
private async Task DeviceImport(IBrowserFile file)
|
||||
private async Task DeviceImport()
|
||||
{
|
||||
try
|
||||
{
|
||||
_importPreviews.Clear();
|
||||
_importPreviews = await Preview.Invoke(file);
|
||||
await step.Next();
|
||||
await InvokeAsync(async () =>
|
||||
{
|
||||
if (OnCloseAsync != null)
|
||||
await OnCloseAsync();
|
||||
await ToastService.Default();
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -67,16 +70,12 @@ public partial class ImportExcel
|
||||
{
|
||||
await Task.Run(async () =>
|
||||
{
|
||||
await Import.Invoke(_importPreviews);
|
||||
_importPreviews = await Import.Invoke(_importFile);
|
||||
_importFile = null;
|
||||
|
||||
await InvokeAsync(async () =>
|
||||
{
|
||||
if (OnCloseAsync != null)
|
||||
await OnCloseAsync();
|
||||
await ToastService.Default();
|
||||
});
|
||||
});
|
||||
await step.Next();
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
@@ -0,0 +1,58 @@
|
||||
@using ThingsGateway.Extension
|
||||
@namespace ThingsGateway.Razor
|
||||
<Button OnClick="() => step.Reset()">@Localizer["Reset"]</Button>
|
||||
<h6 class="my-3 green--text">@Localizer["Tip"] </h6>
|
||||
<Step @ref="@step" IsVertical="true">
|
||||
<StepItem Text=@Localizer["First"] Title=@Localizer["Upload"]>
|
||||
<InputUpload ShowDeleteButton="true" @bind-Value=_importFile Accept=".xlsx"></InputUpload>
|
||||
<Button class="mt-2" IsAsync OnClick="() => DeviceImport(_importFile)">@Localizer["Validate"]</Button>
|
||||
</StepItem>
|
||||
<StepItem Text=@Localizer["Second"] Title=@Localizer["ValidateText"]>
|
||||
|
||||
<div class="overflow-y-auto">
|
||||
|
||||
@foreach (var item in _importPreviews)
|
||||
{
|
||||
<div class="mt-2">
|
||||
@(
|
||||
Localizer["UploadCount", item.Key, item.Value.DataCount]
|
||||
)
|
||||
</div>
|
||||
<div class=@((item.Value.HasError ? "my-2 red--text" : "my-2 green--text"))>
|
||||
@(
|
||||
(item.Value.HasError ? "Error" : "Success")
|
||||
)
|
||||
</div>
|
||||
if (item.Value.HasError)
|
||||
{
|
||||
<div style="height:300px;" class="overflow-y-scroll">
|
||||
<Virtualize Items="item.Value.Results.Where(a => !a.Success).OrderBy(a => a.Row).ToList()" Context="item1" ItemSize="60" OverscanCount=2>
|
||||
<ItemContent>
|
||||
<div class="row g-0">
|
||||
<span class="col mx-2">@item1.Row</span>
|
||||
<span class=@((item1.Success ? "green--text col-auto" : "red--text col-auto"))>
|
||||
<strong>@item1.ErrorMessage</strong>
|
||||
</span>
|
||||
</div>
|
||||
</ItemContent>
|
||||
</Virtualize>
|
||||
</div>
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
<PopConfirmButton IsAsync IsDisabled=@_importPreviews.Any(it => it.Value.HasError) Color=Color.Warning class="mt-2" OnConfirm=@(SaveDeviceImport)>@Localizer["Import"]</PopConfirmButton>
|
||||
|
||||
@*
|
||||
<Button IsAsync class="mt-2" IsDisabled=@_importPreviews.Any(it => it.Value.HasError) OnClick="() => step.Next()">@Localizer["Next"]</Button> *@
|
||||
|
||||
</div>
|
||||
</StepItem>
|
||||
@* <StepItem Text=@Localizer["Third"] Title=@Localizer["Import"]>
|
||||
<PopConfirmButton IsAsync Color=Color.Warning class="mt-2" OnConfirm=@(SaveDeviceImport)>@Localizer["Import"]</PopConfirmButton>
|
||||
</StepItem> *@
|
||||
</Step>
|
||||
@code {
|
||||
[NotNull]
|
||||
Step? step { get; set; }
|
||||
}
|
@@ -0,0 +1,86 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://thingsgateway.cn/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using Microsoft.AspNetCore.Components.Forms;
|
||||
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace ThingsGateway.Razor;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public partial class ImportExcelConfirm
|
||||
{
|
||||
private Dictionary<string, ImportPreviewOutputBase> _importPreviews = new();
|
||||
|
||||
/// <summary>
|
||||
/// 导入
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
[EditorRequired]
|
||||
public Func<Dictionary<string, ImportPreviewOutputBase>, Task> Import { get; set; }
|
||||
|
||||
[Inject]
|
||||
[NotNull]
|
||||
private IStringLocalizer<ImportExcel>? Localizer { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 预览
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
[EditorRequired]
|
||||
public Func<IBrowserFile, Task<Dictionary<string, ImportPreviewOutputBase>>> Preview { get; set; }
|
||||
|
||||
[Inject]
|
||||
[NotNull]
|
||||
private ToastService? ToastService { get; set; }
|
||||
|
||||
[Required]
|
||||
private IBrowserFile _importFile { get; set; }
|
||||
|
||||
[CascadingParameter]
|
||||
private Func<Task>? OnCloseAsync { get; set; }
|
||||
|
||||
private async Task DeviceImport(IBrowserFile file)
|
||||
{
|
||||
try
|
||||
{
|
||||
_importPreviews.Clear();
|
||||
_importPreviews = await Preview.Invoke(file);
|
||||
await step.Next();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await ToastService.Warn(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task SaveDeviceImport()
|
||||
{
|
||||
try
|
||||
{
|
||||
await Task.Run(async () =>
|
||||
{
|
||||
await Import.Invoke(_importPreviews);
|
||||
_importFile = null;
|
||||
|
||||
await InvokeAsync(async () =>
|
||||
{
|
||||
if (OnCloseAsync != null)
|
||||
await OnCloseAsync();
|
||||
await ToastService.Default();
|
||||
});
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await InvokeAsync(async () => await ToastService.Warn(ex));
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,9 @@
|
||||
::deep .avatar {
|
||||
border-radius: 1.5rem;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
background-color: var(--bs-red);
|
||||
color: #fff;
|
||||
flex: 0 0 auto;
|
||||
font-size: 1rem;
|
||||
}
|
@@ -10,7 +10,7 @@
|
||||
|
||||
using Microsoft.JSInterop;
|
||||
|
||||
namespace ThingsGateway.Common.Extension;
|
||||
namespace ThingsGateway.Razor.Extension;
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@@ -60,14 +60,14 @@
|
||||
},
|
||||
"ThingsGateway.Razor.ImportExcel": {
|
||||
"First": "Step 1",
|
||||
"Import": "Import",
|
||||
"Import": "If there are no errors during verification, it will be directly imported into the database",
|
||||
"Next": "Next",
|
||||
"Reset": "Reset",
|
||||
"Second": "Step 2",
|
||||
"Third": "Step 3",
|
||||
"Tip": "When the data volume is large (more than 200,000), the import may take more than 1 minute, please be patient",
|
||||
"Upload": "Upload File",
|
||||
"UploadCount": " Table {0}, expecting to import {1} records",
|
||||
"UploadCount": " Table {0}, import {1} records",
|
||||
"Validate": "Validate",
|
||||
"ValidateText": "Validation Content"
|
||||
},
|
||||
|
@@ -60,14 +60,14 @@
|
||||
},
|
||||
"ThingsGateway.Razor.ImportExcel": {
|
||||
"First": "第一步",
|
||||
"Import": "导入",
|
||||
"Import": "若验证无错误,将直接导入数据库",
|
||||
"Next": "下一步",
|
||||
"Reset": "重置",
|
||||
"Second": "第二步",
|
||||
"Third": "第三",
|
||||
"Tip": "数据量较大时(大于20万),所需导入时间可能超过1分钟,请耐心等待",
|
||||
"Upload": "上传文件",
|
||||
"UploadCount": " 表 {0},预计导入 {1} 条数据",
|
||||
"UploadCount": " 表 {0},导入 {1} 条数据",
|
||||
"Validate": "验证",
|
||||
"ValidateText": "验证内容"
|
||||
},
|
||||
|
@@ -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));
|
||||
}
|
||||
}
|
@@ -1,9 +1,9 @@
|
||||
<Project>
|
||||
|
||||
<PropertyGroup>
|
||||
<PluginVersion>10.10.11</PluginVersion>
|
||||
<ProPluginVersion>10.10.11</ProPluginVersion>
|
||||
<DefaultVersion>10.10.14</DefaultVersion>
|
||||
<PluginVersion>10.10.16</PluginVersion>
|
||||
<ProPluginVersion>10.10.16</ProPluginVersion>
|
||||
<DefaultVersion>10.10.16</DefaultVersion>
|
||||
<AuthenticationVersion>10.10.1</AuthenticationVersion>
|
||||
<SourceGeneratorVersion>10.10.1</SourceGeneratorVersion>
|
||||
<NET8Version>8.0.19</NET8Version>
|
||||
|
@@ -95,7 +95,7 @@ public partial class LogConsole : IDisposable
|
||||
|
||||
if (LogPath != null)
|
||||
{
|
||||
var files = await TextFileReadService.GetLogFiles(LogPath);
|
||||
var files = await TextFileReadService.GetLogFilesAsync(LogPath);
|
||||
if (!files.IsSuccess)
|
||||
{
|
||||
Messages = new List<LogMessage>();
|
||||
@@ -106,7 +106,7 @@ public partial class LogConsole : IDisposable
|
||||
await Task.Run(async () =>
|
||||
{
|
||||
Stopwatch sw = Stopwatch.StartNew();
|
||||
var result = await TextFileReadService.LastLogData(files.Content.FirstOrDefault());
|
||||
var result = await TextFileReadService.LastLogDataAsync(files.Content.FirstOrDefault());
|
||||
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();
|
||||
@@ -144,7 +144,7 @@ public partial class LogConsole : IDisposable
|
||||
{
|
||||
if (LogPath != null)
|
||||
{
|
||||
var files = await TextFileReadService.GetLogFiles(LogPath);
|
||||
var files = await TextFileReadService.GetLogFilesAsync(LogPath);
|
||||
if (files.IsSuccess)
|
||||
{
|
||||
foreach (var item in files.Content)
|
||||
|
@@ -26,7 +26,7 @@ public class PlatformService : IPlatformService
|
||||
|
||||
public async Task OnLogExport(string logPath)
|
||||
{
|
||||
var files = TextFileReader.GetLogFiles(logPath);
|
||||
var files = TextFileReader.GetLogFilesAsync(logPath);
|
||||
if (!files.IsSuccess)
|
||||
{
|
||||
return;
|
||||
|
@@ -59,7 +59,7 @@ public static class TextFileReader
|
||||
/// </summary>
|
||||
/// <param name="directoryPath">目录路径</param>
|
||||
/// <returns>包含文件信息的列表</returns>
|
||||
public static OperResult<List<string>> GetLogFiles(string directoryPath)
|
||||
public static OperResult<List<string>> GetLogFilesAsync(string directoryPath)
|
||||
{
|
||||
OperResult<List<string>> result = new(); // 初始化结果对象
|
||||
// 检查目录是否存在
|
||||
@@ -91,7 +91,7 @@ public static class TextFileReader
|
||||
return result;
|
||||
}
|
||||
|
||||
public static OperResult<List<LogData>> LastLogData(string file, int lineCount = 200)
|
||||
public static OperResult<List<LogData>> LastLogDataAsync(string file, int lineCount = 200)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return new OperResult<List<LogData>>("The file path is invalid");
|
||||
@@ -104,7 +104,7 @@ public static class TextFileReader
|
||||
{
|
||||
var fileInfo = new FileInfo(file);
|
||||
var length = fileInfo.Length;
|
||||
var cacheKey = $"{nameof(TextFileReader)}_{nameof(LastLogData)}_{file})";
|
||||
var cacheKey = $"{nameof(TextFileReader)}_{nameof(LastLogDataAsync)}_{file})";
|
||||
if (_cache.TryGetValue<LogDataCache>(cacheKey, out var cachedData))
|
||||
{
|
||||
if (cachedData != null && cachedData.Length == length)
|
||||
|
@@ -18,7 +18,7 @@ public interface ITextFileReadService
|
||||
/// </summary>
|
||||
/// <param name="directoryPath">目录路径</param>
|
||||
/// <returns>包含文件信息的列表</returns>
|
||||
public Task<OperResult<List<string>>> GetLogFiles(string directoryPath);
|
||||
public Task<OperResult<List<string>>> GetLogFilesAsync(string directoryPath);
|
||||
|
||||
public Task<OperResult<List<LogData>>> LastLogData(string file, int lineCount = 200);
|
||||
public Task<OperResult<List<LogData>>> LastLogDataAsync(string file, int lineCount = 200);
|
||||
}
|
||||
|
@@ -14,7 +14,7 @@ namespace ThingsGateway.Foundation;
|
||||
|
||||
public class TextFileReadService : ITextFileReadService
|
||||
{
|
||||
public Task<OperResult<List<string>>> GetLogFiles(string directoryPath) => Task.FromResult(TextFileReader.GetLogFiles(directoryPath));
|
||||
public Task<OperResult<List<string>>> GetLogFilesAsync(string directoryPath) => Task.FromResult(TextFileReader.GetLogFilesAsync(directoryPath));
|
||||
|
||||
public Task<OperResult<List<LogData>>> LastLogData(string file, int lineCount = 200) => Task.FromResult(TextFileReader.LastLogData(file, lineCount));
|
||||
public Task<OperResult<List<LogData>>> LastLogDataAsync(string file, int lineCount = 200) => Task.FromResult(TextFileReader.LastLogDataAsync(file, lineCount));
|
||||
}
|
||||
|
@@ -12,13 +12,15 @@ using TouchSocket.Core;
|
||||
|
||||
namespace ThingsGateway.Gateway.Application;
|
||||
|
||||
public class AsyncReadWriteLock
|
||||
public class AsyncReadWriteLock : IAsyncDisposable
|
||||
{
|
||||
private readonly int _writeReadRatio = 3; // 写3次会允许1次读,但写入也不会被阻止,具体协议取决于插件协议实现
|
||||
public AsyncReadWriteLock(int writeReadRatio)
|
||||
public AsyncReadWriteLock(int writeReadRatio, bool writePriority)
|
||||
{
|
||||
_writeReadRatio = writeReadRatio;
|
||||
_writePriority = writePriority;
|
||||
}
|
||||
private bool _writePriority;
|
||||
private AsyncAutoResetEvent _readerLock = new AsyncAutoResetEvent(false); // 控制读计数
|
||||
private long _writerCount = 0; // 当前活跃的写线程数
|
||||
private long _readerCount = 0; // 当前被阻塞的读线程数
|
||||
@@ -33,6 +35,8 @@ public class AsyncReadWriteLock
|
||||
{
|
||||
Interlocked.Increment(ref _readerCount);
|
||||
|
||||
|
||||
|
||||
// 第一个读者需要获取写入锁,防止写操作
|
||||
await _readerLock.WaitOneAsync(cancellationToken).ConfigureAwait(false);
|
||||
|
||||
@@ -52,10 +56,13 @@ public class AsyncReadWriteLock
|
||||
|
||||
if (Interlocked.Increment(ref _writerCount) == 1)
|
||||
{
|
||||
var cancellationTokenSource = _cancellationTokenSource;
|
||||
_cancellationTokenSource = new();
|
||||
await cancellationTokenSource.SafeCancelAsync().ConfigureAwait(false); // 取消读取
|
||||
cancellationTokenSource.SafeDispose();
|
||||
if (_writePriority)
|
||||
{
|
||||
var cancellationTokenSource = _cancellationTokenSource;
|
||||
_cancellationTokenSource = new();
|
||||
await cancellationTokenSource.SafeCancelAsync().ConfigureAwait(false); // 取消读取
|
||||
cancellationTokenSource.SafeDispose();
|
||||
}
|
||||
}
|
||||
|
||||
return new Writer(this);
|
||||
@@ -63,11 +70,16 @@ public class AsyncReadWriteLock
|
||||
private object lockObject = new();
|
||||
private void ReleaseWriter()
|
||||
{
|
||||
|
||||
var writerCount = Interlocked.Decrement(ref _writerCount);
|
||||
|
||||
// 每次释放写时,总是唤醒至少一个读
|
||||
_readerLock.Set();
|
||||
|
||||
if (writerCount == 0)
|
||||
{
|
||||
var resetEvent = _readerLock;
|
||||
_readerLock = new(false);
|
||||
//_readerLock = new(false);
|
||||
Interlocked.Exchange(ref _writeSinceLastReadCount, 0);
|
||||
resetEvent.SetAll();
|
||||
}
|
||||
@@ -83,18 +95,28 @@ public class AsyncReadWriteLock
|
||||
if (count >= _writeReadRatio)
|
||||
{
|
||||
Interlocked.Exchange(ref _writeSinceLastReadCount, 0);
|
||||
_readerLock.Set();
|
||||
//_readerLock.Set();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_readerLock.Set();
|
||||
//_readerLock.Set();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
if (_cancellationTokenSource != null)
|
||||
{
|
||||
await _cancellationTokenSource.SafeCancelAsync().ConfigureAwait(false);
|
||||
_cancellationTokenSource.SafeDispose();
|
||||
}
|
||||
_readerLock.SetAll();
|
||||
}
|
||||
|
||||
private int _writeSinceLastReadCount = 0;
|
||||
private struct Writer : IDisposable
|
||||
{
|
||||
|
@@ -12,7 +12,7 @@ using BootstrapBlazor.Components;
|
||||
|
||||
namespace ThingsGateway.Gateway.Application;
|
||||
|
||||
public class ExportFilter
|
||||
public class GatewayExportFilter
|
||||
{
|
||||
public FilterKeyValueAction FilterKeyValueAction { get; set; }
|
||||
public string? PluginName { get; set; }
|
@@ -10,7 +10,7 @@
|
||||
|
||||
namespace ThingsGateway.Gateway.Application;
|
||||
|
||||
public static class ExportString
|
||||
public static class GatewayExportString
|
||||
{
|
||||
/// <summary>
|
||||
/// 通道名称
|
||||
@@ -47,7 +47,7 @@ public static class ExportString
|
||||
get
|
||||
{
|
||||
if (localizer == null)
|
||||
localizer = App.CreateLocalizerByType(typeof(ExportString));
|
||||
localizer = App.CreateLocalizerByType(typeof(GatewayExportString));
|
||||
return localizer;
|
||||
}
|
||||
}
|
@@ -45,7 +45,7 @@ public class GatewayExportController : ControllerBase
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpPost("device")]
|
||||
public async Task<IActionResult> DownloadDeviceAsync([FromBody] ExportFilter input)
|
||||
public async Task<IActionResult> DownloadDeviceAsync([FromBody] GatewayExportFilter input)
|
||||
{
|
||||
input.QueryPageOptions.IsPage = false;
|
||||
input.QueryPageOptions.IsVirtualScroll = false;
|
||||
@@ -58,7 +58,7 @@ public class GatewayExportController : ControllerBase
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpPost("channel")]
|
||||
public async Task<IActionResult> DownloadChannelAsync([FromBody] ExportFilter input)
|
||||
public async Task<IActionResult> DownloadChannelAsync([FromBody] GatewayExportFilter input)
|
||||
{
|
||||
input.QueryPageOptions.IsPage = false;
|
||||
input.QueryPageOptions.IsVirtualScroll = false;
|
||||
@@ -72,7 +72,7 @@ public class GatewayExportController : ControllerBase
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpPost("variable")]
|
||||
public async Task<IActionResult> DownloadVariableAsync([FromBody] ExportFilter input)
|
||||
public async Task<IActionResult> DownloadVariableAsync([FromBody] GatewayExportFilter input)
|
||||
{
|
||||
input.QueryPageOptions.IsPage = false;
|
||||
input.QueryPageOptions.IsVirtualScroll = false;
|
||||
|
@@ -90,7 +90,7 @@ public class RuntimeInfoController : ControllerBase, IRpcServer
|
||||
[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
|
||||
.WhereIF(!input.RegisterAddress.IsNullOrEmpty(), a => a.RegisterAddress == input.RegisterAddress)
|
||||
@@ -145,7 +145,7 @@ public class RuntimeInfoController : ControllerBase, IRpcServer
|
||||
public SqlSugarPagedList<PluginInfo> GetPluginInfos([FromQuery][TouchSocket.WebApi.FromBody] PluginInfoPageInput input)
|
||||
{
|
||||
//指定关键词搜索为插件FullName
|
||||
return (GlobalData.PluginService.GetPluginListSync()).WhereIF(!input.Name.IsNullOrWhiteSpace(), a => a.Name == input.Name)
|
||||
return (GlobalData.PluginService.GetPluginList()).WhereIF(!input.Name.IsNullOrWhiteSpace(), a => a.Name == input.Name)
|
||||
.ToPagedList(input);
|
||||
}
|
||||
}
|
||||
|
@@ -11,14 +11,19 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace ThingsGateway.Admin.Application;
|
||||
using TouchSocket.Rpc;
|
||||
|
||||
namespace ThingsGateway.Gateway.Application;
|
||||
|
||||
[Route("api/[controller]/[action]")]
|
||||
[AllowAnonymous]
|
||||
[ApiController]
|
||||
public class TestController : ControllerBase
|
||||
[TouchSocket.WebApi.Router("/miniapi/[api]/[action]")]
|
||||
[TouchSocket.WebApi.EnableCors("cors")]
|
||||
public class TestController : ControllerBase, IRpcServer
|
||||
{
|
||||
[HttpGet]
|
||||
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Get)]
|
||||
public void Test()
|
||||
{
|
||||
GC.Collect();
|
@@ -195,7 +195,7 @@ public abstract partial class CollectBase : DriverBase, IRpcDriver
|
||||
// 从插件服务中获取当前设备关联的驱动方法信息列表
|
||||
DriverMethodInfos = GlobalData.PluginService.GetDriverMethodInfos(device.PluginName, this);
|
||||
|
||||
ReadWriteLock = new(CollectProperties.DutyCycle);
|
||||
ReadWriteLock = new(CollectProperties.DutyCycle, CollectProperties.WritePriority);
|
||||
}
|
||||
|
||||
public virtual string GetAddressDescription()
|
||||
@@ -723,9 +723,11 @@ public abstract partial class CollectBase : DriverBase, IRpcDriver
|
||||
|
||||
#endregion 写入方法
|
||||
|
||||
protected override Task DisposeAsync(bool disposing)
|
||||
protected override async Task DisposeAsync(bool disposing)
|
||||
{
|
||||
_linkedCtsCache?.SafeDispose();
|
||||
return base.DisposeAsync(disposing);
|
||||
if (ReadWriteLock != null)
|
||||
await ReadWriteLock.SafeDisposeAsync().ConfigureAwait(false);
|
||||
await base.DisposeAsync(disposing).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
@@ -37,6 +37,12 @@ public abstract class CollectPropertyBase : DriverPropertyBase
|
||||
/// </summary>
|
||||
[MinValue(1)]
|
||||
public virtual int DutyCycle { get; set; } = 3;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 写优先,写入时强制读取消操作
|
||||
/// </summary>
|
||||
public virtual bool WritePriority { get; set; } = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -56,4 +62,6 @@ public abstract class CollectPropertyRetryBase : CollectPropertyBase
|
||||
[MinValue(1)]
|
||||
public override int DutyCycle { get; set; } = 3;
|
||||
|
||||
[DynamicProperty(Remark = "写优先,写入时强制读取消操作")]
|
||||
public override bool WritePriority { get; set; } = false;
|
||||
}
|
@@ -17,7 +17,6 @@ using TouchSocket.Core;
|
||||
using TouchSocket.Sockets;
|
||||
|
||||
namespace ThingsGateway.Gateway.Application;
|
||||
#pragma warning disable CS0649
|
||||
|
||||
/// <summary>
|
||||
/// 通道表
|
||||
|
@@ -17,7 +17,6 @@ using System.ComponentModel.DataAnnotations;
|
||||
using ThingsGateway.NewLife.Extension;
|
||||
|
||||
namespace ThingsGateway.Gateway.Application;
|
||||
#pragma warning disable CS0649
|
||||
|
||||
/// <summary>
|
||||
/// 设备表
|
||||
@@ -172,6 +171,7 @@ public class Device : BaseDataEntity, IValidatableObject
|
||||
|
||||
#endregion 备用字段
|
||||
|
||||
#if !Management
|
||||
/// <summary>
|
||||
/// 导入验证专用
|
||||
/// </summary>
|
||||
@@ -186,6 +186,8 @@ public class Device : BaseDataEntity, IValidatableObject
|
||||
[Newtonsoft.Json.JsonIgnore]
|
||||
[MapperIgnore]
|
||||
public ModelValueValidateForm? ModelValueValidateForm;
|
||||
#endif
|
||||
|
||||
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
|
||||
{
|
||||
if (RedundantEnable && RedundantDeviceId == null)
|
||||
|
@@ -16,7 +16,6 @@ using System.Collections.Concurrent;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace ThingsGateway.Gateway.Application;
|
||||
#pragma warning disable CS0649
|
||||
|
||||
/// <summary>
|
||||
/// 设备变量表
|
||||
|
@@ -83,7 +83,7 @@ public static class GlobalData
|
||||
.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);
|
||||
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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
Dictionary<IDeviceThreadManage, List<DeviceRuntime>> deviceThreadManages = new();
|
||||
|
@@ -1,12 +1,20 @@
|
||||
{
|
||||
"ThingsGateway.Management.Application._Imports": {
|
||||
"Restart": "Restart",
|
||||
"Upgrade": "Upgrade"
|
||||
"ThingsGateway.Management.Application.ManagementExportString": {
|
||||
|
||||
"ManagementConfigName": "ManagementConfigName"
|
||||
},
|
||||
"ThingsGateway.Management.Application.SaveUpdateZipFile": {
|
||||
"DownTemplate": "Download Template",
|
||||
"SaveUpdateZipFile": "Upload Version Package"
|
||||
"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",
|
||||
@@ -313,7 +321,8 @@
|
||||
},
|
||||
"ThingsGateway.Gateway.Application.CollectPropertyRetryBase": {
|
||||
"RetryCount": "RetryCount",
|
||||
"DutyCycle": "DutyCycle"
|
||||
"DutyCycle": "DutyCycle",
|
||||
"WritePriority": "WritePriority"
|
||||
},
|
||||
"ThingsGateway.Gateway.Application.ControlController": {
|
||||
"BatchSaveChannelAsync": "BatchSaveChannel",
|
||||
@@ -388,7 +397,7 @@
|
||||
"ExpireTime": "ExpireTime {0}",
|
||||
"Unauthorized": "Unauthorized"
|
||||
},
|
||||
"ThingsGateway.Gateway.Application.ExportString": {
|
||||
"ThingsGateway.Gateway.Application.GatewayExportString": {
|
||||
"BusinessDeviceName": "BusinessDevice",
|
||||
"ChannelName": "Channel",
|
||||
"DeviceName": "Device",
|
||||
|
@@ -1,12 +1,22 @@
|
||||
{
|
||||
"ThingsGateway.Management.Application._Imports": {
|
||||
"Restart": "重启",
|
||||
"Upgrade": "更新"
|
||||
"ThingsGateway.Management.Application.ManagementExportString": {
|
||||
|
||||
"ManagementConfigName": "通讯配置"
|
||||
},
|
||||
"ThingsGateway.Management.Application.SaveUpdateZipFile": {
|
||||
"DownTemplate": "下载模板",
|
||||
"SaveUpdateZipFile": "上传版本包"
|
||||
"ThingsGateway.Management.Application.ManagementConfig": {
|
||||
|
||||
"Name": "名称",
|
||||
"ServerUri": "服务端Url",
|
||||
"Enable": "启用",
|
||||
"IsServer": "服务端",
|
||||
"VerifyToken": "验证令牌",
|
||||
"HeartbeatInterval": "心跳间隔",
|
||||
|
||||
"ImportNullError": "无法识别"
|
||||
|
||||
},
|
||||
|
||||
|
||||
"ThingsGateway.Management.Application.UpdateZipFile": {
|
||||
"AppName": "名称",
|
||||
"Architecture": "架构",
|
||||
@@ -97,7 +107,7 @@
|
||||
"Status": "当前站点状态",
|
||||
"Switch": "切换",
|
||||
"SyncInterval": "数据同步间隔",
|
||||
"VerifyToken": "Token"
|
||||
"VerifyToken": "验证令牌"
|
||||
},
|
||||
"ThingsGateway.Gateway.Application.RedundancyService": {
|
||||
"EditRedundancyOption": "修改网关冗余配置"
|
||||
@@ -313,7 +323,8 @@
|
||||
},
|
||||
"ThingsGateway.Gateway.Application.CollectPropertyRetryBase": {
|
||||
"RetryCount": "失败重试次数",
|
||||
"DutyCycle": "占空比"
|
||||
"DutyCycle": "占空比",
|
||||
"WritePriority": "写优先"
|
||||
},
|
||||
"ThingsGateway.Gateway.Application.ControlController": {
|
||||
"BatchSaveChannelAsync": "保存通道",
|
||||
@@ -390,7 +401,7 @@
|
||||
"ExpireTime": "过期时间 {0}",
|
||||
"Unauthorized": "未授权"
|
||||
},
|
||||
"ThingsGateway.Gateway.Application.ExportString": {
|
||||
"ThingsGateway.Gateway.Application.GatewayExportString": {
|
||||
"BusinessDeviceName": "业务设备",
|
||||
"ChannelName": "通道",
|
||||
"DeviceName": "设备",
|
||||
@@ -557,7 +568,7 @@
|
||||
"LLAlarmText": "低低报文本",
|
||||
"LLRestrainExpressions": "低低报约束",
|
||||
"LRestrainExpressions": "低报约束"
|
||||
|
||||
|
||||
},
|
||||
|
||||
"ThingsGateway.Gateway.Application.VariableRuntime": {
|
||||
|
@@ -83,5 +83,8 @@ public partial class AlarmRuntimePropertys
|
||||
public DateTime EventTime { get; set; } = DateTime.UnixEpoch.ToLocalTime();
|
||||
|
||||
internal object AlarmLockObject = new();
|
||||
|
||||
#if !Management
|
||||
internal bool AlarmConfirm;
|
||||
#endif
|
||||
}
|
||||
|
@@ -8,7 +8,8 @@
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace ThingsGateway.Gateway.Razor;
|
||||
|
||||
namespace ThingsGateway.Gateway.Application;
|
||||
public enum ChannelDevicePluginTypeEnum
|
||||
{
|
||||
PluginType,
|
@@ -23,27 +23,15 @@ namespace ThingsGateway.Gateway.Application;
|
||||
/// <summary>
|
||||
/// 业务设备运行状态
|
||||
/// </summary>
|
||||
public class ChannelRuntime : Channel, IChannelOptions, IDisposable
|
||||
public class ChannelRuntime : Channel
|
||||
#if !Management
|
||||
,
|
||||
IChannelOptions,
|
||||
IDisposable
|
||||
#endif
|
||||
{
|
||||
/// <summary>
|
||||
/// 插件信息
|
||||
/// </summary>
|
||||
[System.Text.Json.Serialization.JsonIgnore]
|
||||
[Newtonsoft.Json.JsonIgnore]
|
||||
[MapperIgnore]
|
||||
[AutoGenerateColumn(Ignore = true)]
|
||||
public PluginInfo? PluginInfo { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否采集
|
||||
/// </summary>
|
||||
public PluginTypeEnum? PluginType => PluginInfo?.PluginType;
|
||||
|
||||
/// <summary>
|
||||
/// 是否采集
|
||||
/// </summary>
|
||||
[AutoGenerateColumn(Ignore = true)]
|
||||
public bool? IsCollect => PluginInfo == null ? null : PluginInfo?.PluginType == PluginTypeEnum.Collect;
|
||||
#if !Management
|
||||
|
||||
[System.Text.Json.Serialization.JsonIgnore]
|
||||
[Newtonsoft.Json.JsonIgnore]
|
||||
@@ -105,19 +93,72 @@ public class ChannelRuntime : Channel, IChannelOptions, IDisposable
|
||||
[Newtonsoft.Json.JsonIgnore]
|
||||
public int? DeviceRuntimeCount => DeviceRuntimes?.Count;
|
||||
|
||||
public bool Started => DeviceThreadManage != null;
|
||||
#else
|
||||
|
||||
|
||||
|
||||
/// <inheritdoc/>
|
||||
[MinValue(1)]
|
||||
public override int MaxConcurrentCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 设备数量
|
||||
/// </summary>
|
||||
public int? DeviceRuntimeCount { get; set; }
|
||||
|
||||
public bool Started { get; set; }
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 插件信息
|
||||
/// </summary>
|
||||
[AutoGenerateColumn(Ignore = true)]
|
||||
public PluginInfo? PluginInfo { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否采集
|
||||
/// </summary>
|
||||
public PluginTypeEnum? PluginType => PluginInfo?.PluginType;
|
||||
|
||||
/// <summary>
|
||||
/// 是否采集
|
||||
/// </summary>
|
||||
[AutoGenerateColumn(Ignore = true)]
|
||||
public bool? IsCollect => PluginInfo == null ? null : PluginInfo?.PluginType == PluginTypeEnum.Collect;
|
||||
|
||||
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
if (ChannelType == ChannelTypeEnum.Other)
|
||||
{
|
||||
return Name;
|
||||
}
|
||||
return $"{Name}[{base.ToString()}]";
|
||||
}
|
||||
|
||||
[AutoGenerateColumn(Ignore = true)]
|
||||
public string LogPath => Name.GetChannelLogPath();
|
||||
|
||||
|
||||
#if !Management
|
||||
[System.Text.Json.Serialization.JsonIgnore]
|
||||
[Newtonsoft.Json.JsonIgnore]
|
||||
[MapperIgnore]
|
||||
[AutoGenerateColumn(Ignore = true)]
|
||||
public IDeviceThreadManage? DeviceThreadManage { get; internal set; }
|
||||
|
||||
[AutoGenerateColumn(Ignore = true)]
|
||||
public string LogPath => Name.GetChannelLogPath();
|
||||
|
||||
|
||||
|
||||
|
||||
public void Init()
|
||||
{
|
||||
// 通过插件名称获取插件信息
|
||||
PluginInfo = GlobalData.PluginService.GetPluginListSync().FirstOrDefault(A => A.FullName == PluginName);
|
||||
PluginInfo = GlobalData.PluginService.GetPluginList().FirstOrDefault(A => A.FullName == PluginName);
|
||||
|
||||
GlobalData.IdChannels.TryRemove(Id, out _);
|
||||
GlobalData.Channels.TryRemove(Name, out _);
|
||||
@@ -135,14 +176,8 @@ public class ChannelRuntime : Channel, IChannelOptions, IDisposable
|
||||
DeviceThreadManage = null;
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
public override string ToString()
|
||||
{
|
||||
if (ChannelType == ChannelTypeEnum.Other)
|
||||
{
|
||||
return Name;
|
||||
}
|
||||
return $"{Name}[{base.ToString()}]";
|
||||
}
|
||||
|
||||
|
||||
|
||||
public IChannel GetChannel(TouchSocketConfig config)
|
||||
{
|
||||
@@ -192,4 +227,7 @@ public class ChannelRuntime : Channel, IChannelOptions, IDisposable
|
||||
return ichannel;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
}
|
||||
|
@@ -23,12 +23,17 @@ namespace ThingsGateway.Gateway.Application;
|
||||
/// <summary>
|
||||
/// 业务设备运行状态
|
||||
/// </summary>
|
||||
public class DeviceRuntime : Device, IDisposable
|
||||
public class DeviceRuntime : Device
|
||||
#if !Management
|
||||
, IDisposable
|
||||
#endif
|
||||
{
|
||||
protected volatile DeviceStatusEnum _deviceStatus = DeviceStatusEnum.Default;
|
||||
|
||||
private string? _lastErrorMessage;
|
||||
|
||||
private readonly object _lockObject = new object();
|
||||
|
||||
/// <summary>
|
||||
/// 设备活跃时间
|
||||
/// </summary>
|
||||
@@ -67,11 +72,15 @@ public class DeviceRuntime : Device, IDisposable
|
||||
[AutoGenerateColumn(Ignore = true)]
|
||||
public string LogPath => Name.GetDeviceLogPath();
|
||||
|
||||
|
||||
#if !Management
|
||||
|
||||
[System.Text.Json.Serialization.JsonIgnore]
|
||||
[Newtonsoft.Json.JsonIgnore]
|
||||
[MapperIgnore]
|
||||
public DateTime DeviceStatusChangeTime = DateTime.UnixEpoch.ToLocalTime();
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 设备状态
|
||||
/// </summary>
|
||||
@@ -98,6 +107,58 @@ public class DeviceRuntime : Device, IDisposable
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 设备变量数量
|
||||
/// </summary>
|
||||
public int DeviceVariableCount { get => Driver == null ? VariableRuntimes?.Count ?? 0 : Driver.IdVariableRuntimes.Count; }
|
||||
|
||||
/// <summary>
|
||||
/// 设备读取打包数量
|
||||
/// </summary>
|
||||
public int SourceVariableCount => VariableSourceReads?.Count ?? 0;
|
||||
|
||||
#else
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 设备状态
|
||||
/// </summary>
|
||||
public virtual DeviceStatusEnum DeviceStatus
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!Pause)
|
||||
return _deviceStatus;
|
||||
else
|
||||
return DeviceStatusEnum.Pause;
|
||||
}
|
||||
set
|
||||
{
|
||||
lock (_lockObject)
|
||||
{
|
||||
if (_deviceStatus != value)
|
||||
{
|
||||
_deviceStatus = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 设备变量数量
|
||||
/// </summary>
|
||||
public int DeviceVariableCount { get; set; }
|
||||
/// <summary>
|
||||
/// 设备读取打包数量
|
||||
/// </summary>
|
||||
public int SourceVariableCount { get; set; }
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// 暂停
|
||||
/// </summary>
|
||||
@@ -130,12 +191,17 @@ public class DeviceRuntime : Device, IDisposable
|
||||
/// </summary>
|
||||
public RedundantTypeEnum? RedundantType { get; set; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// 设备变量数量
|
||||
/// </summary>
|
||||
public int DeviceVariableCount { get => Driver == null ? VariableRuntimes?.Count ?? 0 : Driver.IdVariableRuntimes.Count; }
|
||||
|
||||
#region 采集
|
||||
|
||||
/// <summary>
|
||||
/// 特殊方法数量
|
||||
/// </summary>
|
||||
public int MethodVariableCount { get; set; }
|
||||
|
||||
|
||||
|
||||
|
||||
#if !Management
|
||||
|
||||
/// <summary>
|
||||
/// 设备变量
|
||||
@@ -154,11 +220,6 @@ public class DeviceRuntime : Device, IDisposable
|
||||
[AutoGenerateColumn(Ignore = true)]
|
||||
internal ConcurrentDictionary<string, VariableRuntime>? VariableRuntimes { get; } = new(Environment.ProcessorCount, 1000);
|
||||
|
||||
/// <summary>
|
||||
/// 特殊方法数量
|
||||
/// </summary>
|
||||
public int MethodVariableCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 特殊方法变量
|
||||
/// </summary>
|
||||
@@ -168,11 +229,6 @@ public class DeviceRuntime : Device, IDisposable
|
||||
[AutoGenerateColumn(Ignore = true)]
|
||||
public List<VariableMethod>? ReadVariableMethods { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 设备读取打包数量
|
||||
/// </summary>
|
||||
public int SourceVariableCount => VariableSourceReads?.Count ?? 0;
|
||||
|
||||
/// <summary>
|
||||
/// 打包变量
|
||||
/// </summary>
|
||||
@@ -191,10 +247,11 @@ public class DeviceRuntime : Device, IDisposable
|
||||
[AutoGenerateColumn(Ignore = true)]
|
||||
public List<VariableScriptRead>? VariableScriptReads { get; set; }
|
||||
|
||||
public volatile bool CheckEnable;
|
||||
private readonly object _lockObject = new object();
|
||||
#endif
|
||||
|
||||
#endregion 采集
|
||||
|
||||
|
||||
#if !Management
|
||||
|
||||
/// <summary>
|
||||
/// 传入设备的状态信息
|
||||
@@ -218,6 +275,7 @@ public class DeviceRuntime : Device, IDisposable
|
||||
LastErrorMessage = lastErrorMessage;
|
||||
}
|
||||
|
||||
|
||||
[System.Text.Json.Serialization.JsonIgnore]
|
||||
[Newtonsoft.Json.JsonIgnore]
|
||||
[MapperIgnore]
|
||||
@@ -230,6 +288,7 @@ public class DeviceRuntime : Device, IDisposable
|
||||
[AutoGenerateColumn(Ignore = true)]
|
||||
public IRpcDriver? RpcDriver { get; set; }
|
||||
|
||||
|
||||
[System.Text.Json.Serialization.JsonIgnore]
|
||||
[Newtonsoft.Json.JsonIgnore]
|
||||
[AutoGenerateColumn(Ignore = true)]
|
||||
@@ -263,4 +322,7 @@ public class DeviceRuntime : Device, IDisposable
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
}
|
||||
|
@@ -17,12 +17,13 @@ namespace ThingsGateway.Gateway.Application;
|
||||
/// </summary>
|
||||
public class PluginInfo
|
||||
{
|
||||
#if !Management
|
||||
/// <summary>
|
||||
/// 插件文件名称.插件类型名称
|
||||
/// </summary>
|
||||
[AutoGenerateColumn(Ignore = true)]
|
||||
public List<PluginInfo>? Children { get; set; } = new();
|
||||
|
||||
#endif
|
||||
/// <summary>
|
||||
/// 插件文件名称.插件类型名称
|
||||
/// </summary>
|
||||
@@ -70,5 +71,6 @@ public class PluginInfo
|
||||
/// </summary>
|
||||
[IgnoreExcel]
|
||||
[SugarColumn(IsIgnore = true)]
|
||||
[AutoGenerateColumn(Ignore = true)]
|
||||
public string Directory { get; set; }
|
||||
}
|
||||
|
@@ -8,9 +8,15 @@
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using BootstrapBlazor.Components;
|
||||
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
using Riok.Mapperly.Abstractions;
|
||||
|
||||
#if !Management
|
||||
using ThingsGateway.Gateway.Application.Extensions;
|
||||
#endif
|
||||
using ThingsGateway.NewLife.DictionaryExtensions;
|
||||
using ThingsGateway.NewLife.Json.Extension;
|
||||
|
||||
@@ -19,8 +25,194 @@ namespace ThingsGateway.Gateway.Application;
|
||||
/// <summary>
|
||||
/// 变量运行态
|
||||
/// </summary>
|
||||
public partial class VariableRuntime : Variable, IVariable, IDisposable
|
||||
public partial class VariableRuntime : Variable
|
||||
#if !Management
|
||||
,
|
||||
IVariable,
|
||||
IDisposable
|
||||
#endif
|
||||
{
|
||||
[AutoGenerateColumn(Visible = false)]
|
||||
public bool ValueInited { get => _valueInited; set => _valueInited = value; }
|
||||
|
||||
#region 属性
|
||||
|
||||
/// <summary>
|
||||
/// 这个参数值由自动打包方法写入<see cref="IDevice.LoadSourceRead{T}(IEnumerable{IVariable}, int, string)"/>
|
||||
/// </summary>
|
||||
[AutoGenerateColumn(Visible = false)]
|
||||
public int Index { get => index; set => index = value; }
|
||||
|
||||
/// <summary>
|
||||
/// 变化时间
|
||||
/// </summary>
|
||||
[AutoGenerateColumn(Visible = true, Filterable = true, Sortable = true, Order = 5)]
|
||||
public DateTime ChangeTime { get => changeTime; set => changeTime = value; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 采集时间
|
||||
/// </summary>
|
||||
[AutoGenerateColumn(Visible = true, Filterable = true, Sortable = true, Order = 5)]
|
||||
public DateTime CollectTime { get => collectTime; set => collectTime = value; }
|
||||
|
||||
[SugarColumn(ColumnDescription = "排序码", IsNullable = true)]
|
||||
[AutoGenerateColumn(Visible = false, DefaultSort = false, Sortable = true)]
|
||||
[IgnoreExcel]
|
||||
public override int SortCode { get => sortCode; set => sortCode = value; }
|
||||
|
||||
/// <summary>
|
||||
/// 上次值
|
||||
/// </summary>
|
||||
[AutoGenerateColumn(Visible = false, Order = 6)]
|
||||
public object LastSetValue { get => lastSetValue; set => lastSetValue = value; }
|
||||
|
||||
/// <summary>
|
||||
/// 原始值
|
||||
/// </summary>
|
||||
[AutoGenerateColumn(Visible = false, Order = 6)]
|
||||
public object RawValue { get => rawValue; set => rawValue = value; }
|
||||
|
||||
|
||||
#if !Management
|
||||
|
||||
/// <summary>
|
||||
/// 所在采集设备
|
||||
/// </summary>
|
||||
[Newtonsoft.Json.JsonIgnore]
|
||||
[System.Text.Json.Serialization.JsonIgnore]
|
||||
[AutoGenerateColumn(Ignore = true)]
|
||||
public DeviceRuntime? DeviceRuntime { get => deviceRuntime; set => deviceRuntime = value; }
|
||||
|
||||
/// <summary>
|
||||
/// VariableSource
|
||||
/// </summary>
|
||||
[Newtonsoft.Json.JsonIgnore]
|
||||
[System.Text.Json.Serialization.JsonIgnore]
|
||||
[MapperIgnore]
|
||||
[AutoGenerateColumn(Ignore = true)]
|
||||
public IVariableSource? VariableSource { get => variableSource; set => variableSource = value; }
|
||||
|
||||
/// <summary>
|
||||
/// VariableMethod
|
||||
/// </summary>
|
||||
[Newtonsoft.Json.JsonIgnore]
|
||||
[System.Text.Json.Serialization.JsonIgnore]
|
||||
[MapperIgnore]
|
||||
[AutoGenerateColumn(Ignore = true)]
|
||||
public VariableMethod? VariableMethod { get => variableMethod; set => variableMethod = value; }
|
||||
|
||||
/// <summary>
|
||||
/// 这个参数值由自动打包方法写入<see cref="IDevice.LoadSourceRead{T}(IEnumerable{IVariable}, int, string)"/>
|
||||
/// </summary>
|
||||
[Newtonsoft.Json.JsonIgnore]
|
||||
[System.Text.Json.Serialization.JsonIgnore]
|
||||
[AutoGenerateColumn(Ignore = true)]
|
||||
public IThingsGatewayBitConverter? ThingsGatewayBitConverter { get => thingsGatewayBitConverter; set => thingsGatewayBitConverter = value; }
|
||||
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// 是否在线
|
||||
/// </summary>
|
||||
[AutoGenerateColumn(Visible = true, Filterable = true, Sortable = true, Order = 5)]
|
||||
public bool IsOnline
|
||||
{
|
||||
get
|
||||
{
|
||||
return _isOnline;
|
||||
}
|
||||
private set
|
||||
{
|
||||
if (IsOnline != value)
|
||||
{
|
||||
_isOnlineChanged = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
_isOnlineChanged = false;
|
||||
}
|
||||
_isOnline = value;
|
||||
}
|
||||
}
|
||||
|
||||
#if !Management
|
||||
/// <summary>
|
||||
/// 设备名称
|
||||
/// </summary>
|
||||
[AutoGenerateColumn(Visible = true, Filterable = true, Sortable = true, Order = 4)]
|
||||
public string DeviceName => DeviceRuntime?.Name;
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc/>
|
||||
/// </summary>
|
||||
[AutoGenerateColumn(Visible = true, Filterable = true, Sortable = true, Order = 5)]
|
||||
public string LastErrorMessage
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_isOnline == false)
|
||||
return _lastErrorMessage ?? VariableSource?.LastErrorMessage ?? VariableMethod?.LastErrorMessage;
|
||||
else
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 实时值类型
|
||||
/// </summary>
|
||||
[AutoGenerateColumn(Visible = true, Order = 6)]
|
||||
public string RuntimeType => Value?.GetType()?.ToString();
|
||||
|
||||
#else
|
||||
|
||||
/// <summary>
|
||||
/// 设备名称
|
||||
/// </summary>
|
||||
[AutoGenerateColumn(Visible = true, Filterable = true, Sortable = true, Order = 4)]
|
||||
public string DeviceName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc/>
|
||||
/// </summary>
|
||||
[AutoGenerateColumn(Visible = true, Filterable = true, Sortable = true, Order = 5)]
|
||||
public string LastErrorMessage { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 实时值类型
|
||||
/// </summary>
|
||||
[AutoGenerateColumn(Visible = true, Order = 6)]
|
||||
public string RuntimeType { get; set; }
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 实时值
|
||||
/// </summary>
|
||||
[AutoGenerateColumn(Visible = true, Order = 6)]
|
||||
public object Value { get => _value; set => _value = value; }
|
||||
|
||||
/// <summary>
|
||||
/// 报警使能
|
||||
/// </summary>
|
||||
[AutoGenerateColumn(Visible = false, Filterable = true, Sortable = true)]
|
||||
public bool AlarmEnable
|
||||
{
|
||||
get
|
||||
{
|
||||
return AlarmPropertys != null && (AlarmPropertys.LAlarmEnable || AlarmPropertys.LLAlarmEnable || AlarmPropertys.HAlarmEnable || AlarmPropertys.HHAlarmEnable || AlarmPropertys.BoolOpenAlarmEnable || AlarmPropertys.BoolCloseAlarmEnable || AlarmPropertys.CustomAlarmEnable);
|
||||
}
|
||||
}
|
||||
|
||||
[IgnoreExcel]
|
||||
[AutoGenerateColumn(Ignore = true)]
|
||||
public AlarmRuntimePropertys? AlarmRuntimePropertys { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
private int index;
|
||||
@@ -30,18 +222,24 @@ public partial class VariableRuntime : Variable, IVariable, IDisposable
|
||||
private DateTime collectTime = DateTime.UnixEpoch.ToLocalTime();
|
||||
|
||||
private bool _isOnline;
|
||||
#pragma warning disable CS0414
|
||||
private bool _isOnlineChanged;
|
||||
#pragma warning restore CS0414
|
||||
private bool _valueInited;
|
||||
|
||||
private string _lastErrorMessage;
|
||||
private object _value;
|
||||
private object lastSetValue;
|
||||
private object rawValue;
|
||||
#if !Management
|
||||
#pragma warning disable CS0649
|
||||
private string _lastErrorMessage;
|
||||
#pragma warning restore CS0649
|
||||
private DeviceRuntime? deviceRuntime;
|
||||
private IVariableSource? variableSource;
|
||||
private VariableMethod? variableMethod;
|
||||
private IThingsGatewayBitConverter? thingsGatewayBitConverter;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 设置变量值与时间/质量戳
|
||||
/// </summary>
|
||||
@@ -224,4 +422,10 @@ public partial class VariableRuntime : Variable, IVariable, IDisposable
|
||||
{
|
||||
_lastErrorMessage = lastErrorMessage;
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
@@ -1,173 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://thingsgateway.cn/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using BootstrapBlazor.Components;
|
||||
|
||||
using Riok.Mapperly.Abstractions;
|
||||
|
||||
namespace ThingsGateway.Gateway.Application;
|
||||
|
||||
/// <summary>
|
||||
/// 变量运行态
|
||||
/// </summary>
|
||||
public partial class VariableRuntime : Variable, IVariable, IDisposable
|
||||
{
|
||||
|
||||
[AutoGenerateColumn(Visible = false)]
|
||||
public bool ValueInited { get => _valueInited; set => _valueInited = value; }
|
||||
|
||||
#region 属性
|
||||
|
||||
/// <summary>
|
||||
/// 这个参数值由自动打包方法写入<see cref="IDevice.LoadSourceRead{T}(IEnumerable{IVariable}, int, string)"/>
|
||||
/// </summary>
|
||||
[AutoGenerateColumn(Visible = false)]
|
||||
public int Index { get => index; set => index = value; }
|
||||
|
||||
/// <summary>
|
||||
/// 变化时间
|
||||
/// </summary>
|
||||
[AutoGenerateColumn(Visible = true, Filterable = true, Sortable = true, Order = 5)]
|
||||
public DateTime ChangeTime { get => changeTime; set => changeTime = value; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 采集时间
|
||||
/// </summary>
|
||||
[AutoGenerateColumn(Visible = true, Filterable = true, Sortable = true, Order = 5)]
|
||||
public DateTime CollectTime { get => collectTime; set => collectTime = value; }
|
||||
|
||||
[SugarColumn(ColumnDescription = "排序码", IsNullable = true)]
|
||||
[AutoGenerateColumn(Visible = false, DefaultSort = false, Sortable = true)]
|
||||
[IgnoreExcel]
|
||||
public override int SortCode { get => sortCode; set => sortCode = value; }
|
||||
|
||||
/// <summary>
|
||||
/// 上次值
|
||||
/// </summary>
|
||||
[AutoGenerateColumn(Visible = false, Order = 6)]
|
||||
public object LastSetValue { get => lastSetValue; set => lastSetValue = value; }
|
||||
|
||||
/// <summary>
|
||||
/// 原始值
|
||||
/// </summary>
|
||||
[AutoGenerateColumn(Visible = false, Order = 6)]
|
||||
public object RawValue { get => rawValue; set => rawValue = value; }
|
||||
|
||||
/// <summary>
|
||||
/// 所在采集设备
|
||||
/// </summary>
|
||||
[Newtonsoft.Json.JsonIgnore]
|
||||
[System.Text.Json.Serialization.JsonIgnore]
|
||||
[AutoGenerateColumn(Ignore = true)]
|
||||
public DeviceRuntime? DeviceRuntime { get => deviceRuntime; set => deviceRuntime = value; }
|
||||
|
||||
/// <summary>
|
||||
/// VariableSource
|
||||
/// </summary>
|
||||
[Newtonsoft.Json.JsonIgnore]
|
||||
[System.Text.Json.Serialization.JsonIgnore]
|
||||
[MapperIgnore]
|
||||
[AutoGenerateColumn(Ignore = true)]
|
||||
public IVariableSource? VariableSource { get => variableSource; set => variableSource = value; }
|
||||
|
||||
/// <summary>
|
||||
/// VariableMethod
|
||||
/// </summary>
|
||||
[Newtonsoft.Json.JsonIgnore]
|
||||
[System.Text.Json.Serialization.JsonIgnore]
|
||||
[MapperIgnore]
|
||||
[AutoGenerateColumn(Ignore = true)]
|
||||
public VariableMethod? VariableMethod { get => variableMethod; set => variableMethod = value; }
|
||||
|
||||
/// <summary>
|
||||
/// 这个参数值由自动打包方法写入<see cref="IDevice.LoadSourceRead{T}(IEnumerable{IVariable}, int, string)"/>
|
||||
/// </summary>
|
||||
[Newtonsoft.Json.JsonIgnore]
|
||||
[System.Text.Json.Serialization.JsonIgnore]
|
||||
[AutoGenerateColumn(Ignore = true)]
|
||||
public IThingsGatewayBitConverter? ThingsGatewayBitConverter { get => thingsGatewayBitConverter; set => thingsGatewayBitConverter = value; }
|
||||
|
||||
/// <summary>
|
||||
/// 设备名称
|
||||
/// </summary>
|
||||
[AutoGenerateColumn(Visible = true, Filterable = true, Sortable = true, Order = 4)]
|
||||
public string DeviceName => DeviceRuntime?.Name;
|
||||
|
||||
/// <summary>
|
||||
/// 是否在线
|
||||
/// </summary>
|
||||
[AutoGenerateColumn(Visible = true, Filterable = true, Sortable = true, Order = 5)]
|
||||
public bool IsOnline
|
||||
{
|
||||
get
|
||||
{
|
||||
return _isOnline;
|
||||
}
|
||||
private set
|
||||
{
|
||||
if (IsOnline != value)
|
||||
{
|
||||
_isOnlineChanged = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
_isOnlineChanged = false;
|
||||
}
|
||||
_isOnline = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc/>
|
||||
/// </summary>
|
||||
[AutoGenerateColumn(Visible = true, Filterable = true, Sortable = true, Order = 5)]
|
||||
public string LastErrorMessage
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_isOnline == false)
|
||||
return _lastErrorMessage ?? VariableSource?.LastErrorMessage ?? VariableMethod?.LastErrorMessage;
|
||||
else
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 实时值类型
|
||||
/// </summary>
|
||||
[AutoGenerateColumn(Visible = true, Order = 6)]
|
||||
public string RuntimeType => Value?.GetType()?.ToString();
|
||||
|
||||
/// <summary>
|
||||
/// 实时值
|
||||
/// </summary>
|
||||
[AutoGenerateColumn(Visible = true, Order = 6)]
|
||||
public object Value { get => _value; set => _value = value; }
|
||||
|
||||
/// <summary>
|
||||
/// 报警使能
|
||||
/// </summary>
|
||||
[AutoGenerateColumn(Visible = false, Filterable = true, Sortable = true)]
|
||||
public bool AlarmEnable
|
||||
{
|
||||
get
|
||||
{
|
||||
return AlarmPropertys != null && (AlarmPropertys.LAlarmEnable || AlarmPropertys.LLAlarmEnable || AlarmPropertys.HAlarmEnable || AlarmPropertys.HHAlarmEnable || AlarmPropertys.BoolOpenAlarmEnable || AlarmPropertys.BoolCloseAlarmEnable || AlarmPropertys.CustomAlarmEnable);
|
||||
}
|
||||
}
|
||||
|
||||
[IgnoreExcel]
|
||||
[AutoGenerateColumn(Ignore = true)]
|
||||
public AlarmRuntimePropertys? AlarmRuntimePropertys { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
@@ -17,6 +17,6 @@ namespace ThingsGateway.Gateway.Application
|
||||
public interface IRealAlarmService
|
||||
{
|
||||
[DmtpRpc]
|
||||
Task<IEnumerable<AlarmVariable>> GetCurrentUserRealAlarmVariables();
|
||||
Task<IEnumerable<AlarmVariable>> GetCurrentUserRealAlarmVariablesAsync();
|
||||
}
|
||||
}
|
@@ -15,9 +15,9 @@ namespace ThingsGateway.Gateway.Application;
|
||||
/// </summary>
|
||||
internal sealed class RealAlarmService : IRealAlarmService
|
||||
{
|
||||
public Task<IEnumerable<AlarmVariable>> GetCurrentUserRealAlarmVariables()
|
||||
public Task<IEnumerable<AlarmVariable>> GetCurrentUserRealAlarmVariablesAsync()
|
||||
{
|
||||
return GlobalData.GetCurrentUserRealAlarmVariables();
|
||||
return GlobalData.GetCurrentUserRealAlarmVariablesAsync();
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -19,13 +19,99 @@ namespace ThingsGateway.Gateway.Application;
|
||||
|
||||
public class ChannelRuntimeService : IChannelRuntimeService
|
||||
{
|
||||
private ILogger _logger;
|
||||
public ChannelRuntimeService(ILogger<ChannelRuntimeService> logger)
|
||||
private Microsoft.Extensions.Logging.ILogger _logger;
|
||||
public ChannelRuntimeService(Microsoft.Extensions.Logging.ILogger<ChannelRuntimeService> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
private WaitLock WaitLock { get; set; } = new WaitLock(nameof(ChannelRuntimeService));
|
||||
|
||||
|
||||
|
||||
public async Task<QueryData<SelectedItem>> OnChannelSelectedItemQueryAsync(VirtualizeQueryOption option)
|
||||
{
|
||||
var channels = await GlobalData.GetCurrentUserChannels().ConfigureAwait(false);
|
||||
var _channelItems = channels.GetQueryData(option, GatewayResourceUtil.BuildChannelSelectList);
|
||||
return _channelItems;
|
||||
}
|
||||
|
||||
|
||||
public Task<TouchSocket.Core.LogLevel> ChannelLogLevelAsync(long id)
|
||||
{
|
||||
GlobalData.IdChannels.TryGetValue(id, out var ChannelRuntime);
|
||||
var data = ChannelRuntime?.DeviceThreadManage?.LogMessage?.LogLevel ?? TouchSocket.Core.LogLevel.Trace;
|
||||
return Task.FromResult(data);
|
||||
}
|
||||
|
||||
public async Task RestartChannelAsync(long channelId)
|
||||
{
|
||||
GlobalData.IdChannels.TryGetValue(channelId, out var channelRuntime);
|
||||
await GlobalData.GetChannelThreadManage(channelRuntime).RestartChannelAsync(channelRuntime).ConfigureAwait(false);
|
||||
|
||||
}
|
||||
|
||||
public async Task SetChannelLogLevelAsync(long id, TouchSocket.Core.LogLevel logLevel)
|
||||
{
|
||||
if (GlobalData.IdChannels.TryGetValue(id, out var ChannelRuntime))
|
||||
{
|
||||
if (ChannelRuntime.DeviceThreadManage != null)
|
||||
{
|
||||
await ChannelRuntime.DeviceThreadManage.SetLogAsync(logLevel).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public async Task CopyChannelAsync(int CopyCount, string CopyChannelNamePrefix, int CopyChannelNameSuffixNumber, string CopyDeviceNamePrefix, int CopyDeviceNameSuffixNumber, long channelId, bool AutoRestartThread)
|
||||
{
|
||||
if (!GlobalData.IdChannels.TryGetValue(channelId, out var channelRuntime))
|
||||
{
|
||||
return;
|
||||
}
|
||||
Dictionary<Device, List<Variable>> deviceDict = new();
|
||||
Channel Model = channelRuntime.AdaptChannel();
|
||||
Model.Id = 0;
|
||||
|
||||
var Devices = channelRuntime.ReadDeviceRuntimes.ToDictionary(a => a.Value.AdaptDevice(), a => a.Value.ReadOnlyVariableRuntimes.Select(a => a.Value).AdaptListVariable());
|
||||
|
||||
List<Channel> channels = new();
|
||||
Dictionary<Device, List<Variable>> devices = new();
|
||||
for (int i = 0; i < CopyCount; i++)
|
||||
{
|
||||
Channel channel = Model.AdaptChannel();
|
||||
channel.Id = CommonUtils.GetSingleId();
|
||||
channel.Name = $"{CopyChannelNamePrefix}{CopyChannelNameSuffixNumber + i}";
|
||||
|
||||
int index = 0;
|
||||
foreach (var item in Devices)
|
||||
{
|
||||
Device device = item.Key.AdaptDevice();
|
||||
device.Id = CommonUtils.GetSingleId();
|
||||
device.Name = $"{channel.Name}_{CopyDeviceNamePrefix}{CopyDeviceNameSuffixNumber + (index++)}";
|
||||
device.ChannelId = channel.Id;
|
||||
List<Variable> variables = new();
|
||||
|
||||
foreach (var variable in item.Value)
|
||||
{
|
||||
Variable v = variable.AdaptVariable();
|
||||
v.Id = CommonUtils.GetSingleId();
|
||||
v.DeviceId = device.Id;
|
||||
variables.Add(v);
|
||||
}
|
||||
devices.Add(device, variables);
|
||||
}
|
||||
|
||||
channels.Add(channel);
|
||||
}
|
||||
|
||||
await GlobalData.ChannelRuntimeService.CopyAsync(channels, devices, AutoRestartThread, default).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public async Task<bool> CopyAsync(List<Channel> models, Dictionary<Device, List<Variable>> devices, bool restart, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
@@ -122,7 +208,7 @@ public class ChannelRuntimeService : IChannelRuntimeService
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<bool> BatchEditAsync(IEnumerable<Channel> models, Channel oldModel, Channel model, bool restart)
|
||||
public async Task<bool> BatchEditChannelAsync(IEnumerable<Channel> models, Channel oldModel, Channel model, bool restart)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -177,11 +263,101 @@ public class ChannelRuntimeService : IChannelRuntimeService
|
||||
|
||||
public Task<Dictionary<string, ImportPreviewOutputBase>> PreviewAsync(IBrowserFile browserFile) => GlobalData.ChannelService.PreviewAsync(browserFile);
|
||||
|
||||
public Task<Dictionary<string, object>> ExportChannelAsync(ExportFilter exportFilter) => GlobalData.ChannelService.ExportChannelAsync(exportFilter);
|
||||
public Task<Dictionary<string, object>> ExportChannelAsync(GatewayExportFilter exportFilter) => GlobalData.ChannelService.ExportChannelAsync(exportFilter);
|
||||
|
||||
public Task<MemoryStream> ExportMemoryStream(IEnumerable<Channel> data) =>
|
||||
GlobalData.ChannelService.ExportMemoryStream(data);
|
||||
|
||||
public async Task<Dictionary<string, ImportPreviewOutputBase>> ImportChannelAsync(USheetDatas input, bool restart)
|
||||
{
|
||||
try
|
||||
{
|
||||
await WaitLock.WaitAsync().ConfigureAwait(false);
|
||||
|
||||
|
||||
var data = await ChannelServiceHelpers.ImportAsync(input).ConfigureAwait(false);
|
||||
|
||||
if (data.Any(a => a.Value.HasError)) return data;
|
||||
|
||||
ChannelServiceHelpers.GetImportChannelData(data, out var upData, out var insertData);
|
||||
|
||||
var result = await GlobalData.ChannelService.ImportChannelAsync(upData, insertData).ConfigureAwait(false);
|
||||
|
||||
var newChannelRuntimes = await RuntimeServiceHelper.GetNewChannelRuntimesAsync(result).ConfigureAwait(false);
|
||||
|
||||
RuntimeServiceHelper.Init(newChannelRuntimes);
|
||||
|
||||
//根据条件重启通道线程
|
||||
if (restart)
|
||||
await GlobalData.ChannelThreadManage.RestartChannelAsync(newChannelRuntimes).ConfigureAwait(false);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
finally
|
||||
{
|
||||
WaitLock.Release();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<Dictionary<string, ImportPreviewOutputBase>> ImportChannelAsync(string filePath, bool restart)
|
||||
{
|
||||
try
|
||||
{
|
||||
await WaitLock.WaitAsync().ConfigureAwait(false);
|
||||
|
||||
var data = await GlobalData.ChannelService.PreviewAsync(filePath).ConfigureAwait(false);
|
||||
|
||||
if (data.Any(a => a.Value.HasError)) return data;
|
||||
var result = await GlobalData.ChannelService.ImportChannelAsync(data).ConfigureAwait(false);
|
||||
|
||||
var newChannelRuntimes = await RuntimeServiceHelper.GetNewChannelRuntimesAsync(result).ConfigureAwait(false);
|
||||
|
||||
RuntimeServiceHelper.Init(newChannelRuntimes);
|
||||
|
||||
//根据条件重启通道线程
|
||||
if (restart)
|
||||
await GlobalData.ChannelThreadManage.RestartChannelAsync(newChannelRuntimes).ConfigureAwait(false);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
finally
|
||||
{
|
||||
WaitLock.Release();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public async Task<Dictionary<string, ImportPreviewOutputBase>> ImportChannelAsync(IBrowserFile file, bool restart)
|
||||
{
|
||||
try
|
||||
{
|
||||
await WaitLock.WaitAsync().ConfigureAwait(false);
|
||||
|
||||
var data = await GlobalData.ChannelService.PreviewAsync(file).ConfigureAwait(false);
|
||||
|
||||
if (data.Any(a => a.Value.HasError)) return data;
|
||||
var result = await GlobalData.ChannelService.ImportChannelAsync(data).ConfigureAwait(false);
|
||||
|
||||
var newChannelRuntimes = await RuntimeServiceHelper.GetNewChannelRuntimesAsync(result).ConfigureAwait(false);
|
||||
|
||||
RuntimeServiceHelper.Init(newChannelRuntimes);
|
||||
|
||||
//根据条件重启通道线程
|
||||
if (restart)
|
||||
await GlobalData.ChannelThreadManage.RestartChannelAsync(newChannelRuntimes).ConfigureAwait(false);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
finally
|
||||
{
|
||||
WaitLock.Release();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public async Task ImportChannelAsync(Dictionary<string, ImportPreviewOutputBase> input, bool restart)
|
||||
{
|
||||
try
|
||||
@@ -204,6 +380,31 @@ public class ChannelRuntimeService : IChannelRuntimeService
|
||||
WaitLock.Release();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task ImportChannelAsync(List<Channel> upData, List<Channel> insertData, bool restart)
|
||||
{
|
||||
|
||||
try
|
||||
{
|
||||
await WaitLock.WaitAsync().ConfigureAwait(false);
|
||||
|
||||
var result = await GlobalData.ChannelService.ImportChannelAsync(upData, insertData).ConfigureAwait(false);
|
||||
|
||||
var newChannelRuntimes = await RuntimeServiceHelper.GetNewChannelRuntimesAsync(result).ConfigureAwait(false);
|
||||
|
||||
RuntimeServiceHelper.Init(newChannelRuntimes);
|
||||
|
||||
//根据条件重启通道线程
|
||||
if (restart)
|
||||
await GlobalData.ChannelThreadManage.RestartChannelAsync(newChannelRuntimes).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
finally
|
||||
{
|
||||
WaitLock.Release();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<bool> SaveChannelAsync(Channel input, ItemChangedType type, bool restart)
|
||||
{
|
||||
try
|
||||
|
@@ -247,7 +247,7 @@ internal sealed class ChannelService : BaseService<Channel>, IChannelService
|
||||
/// 报表查询
|
||||
/// </summary>
|
||||
/// <param name="exportFilter">查询条件</param>
|
||||
public async Task<QueryData<Channel>> PageAsync(ExportFilter exportFilter)
|
||||
public async Task<QueryData<Channel>> PageAsync(GatewayExportFilter exportFilter)
|
||||
{
|
||||
var whereQuery = await GetWhereQueryFunc(exportFilter).ConfigureAwait(false);
|
||||
|
||||
@@ -255,12 +255,12 @@ internal sealed class ChannelService : BaseService<Channel>, IChannelService
|
||||
, exportFilter.FilterKeyValueAction).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private async Task<Func<ISugarQueryable<Channel>, ISugarQueryable<Channel>>> GetWhereQueryFunc(ExportFilter exportFilter)
|
||||
private async Task<Func<ISugarQueryable<Channel>, ISugarQueryable<Channel>>> GetWhereQueryFunc(GatewayExportFilter exportFilter)
|
||||
{
|
||||
HashSet<long>? channel = null;
|
||||
if (exportFilter.PluginType != null)
|
||||
{
|
||||
var pluginInfo = GlobalData.PluginService.GetPluginListSync(exportFilter.PluginType).Select(a => a.FullName).ToHashSet();
|
||||
var pluginInfo = GlobalData.PluginService.GetPluginList(exportFilter.PluginType).Select(a => a.FullName).ToHashSet();
|
||||
channel = GlobalData.IdChannels.Where(a => pluginInfo.Contains(a.Value.PluginName)).Select(a => a.Value.Id).ToHashSet();
|
||||
}
|
||||
var dataScope = await GlobalData.SysUserService.GetCurrentUserDataScopeAsync().ConfigureAwait(false);
|
||||
@@ -331,15 +331,15 @@ internal sealed class ChannelService : BaseService<Channel>, IChannelService
|
||||
|
||||
/// <inheritdoc/>
|
||||
[OperDesc("ExportChannel", isRecordPar: false, localizerType: typeof(Channel))]
|
||||
public async Task<Dictionary<string, object>> ExportChannelAsync(ExportFilter exportFilter)
|
||||
public async Task<Dictionary<string, object>> ExportChannelAsync(GatewayExportFilter exportFilter)
|
||||
{
|
||||
var channels = await GetEnumerableData(exportFilter).ConfigureAwait(false);
|
||||
var rows = ChannelServiceHelpers.ExportRows(channels); // IEnumerable 延迟执行
|
||||
var sheets = ChannelServiceHelpers.WrapAsSheet(ExportString.ChannelName, rows);
|
||||
var sheets = ChannelServiceHelpers.WrapAsSheet(GatewayExportString.ChannelName, rows);
|
||||
return sheets;
|
||||
}
|
||||
|
||||
private async Task<IAsyncEnumerable<Channel>> GetEnumerableData(ExportFilter exportFilter)
|
||||
private async Task<IAsyncEnumerable<Channel>> GetEnumerableData(GatewayExportFilter exportFilter)
|
||||
{
|
||||
var db = GetDB();
|
||||
var whereQuery = await GetWhereQueryFunc(exportFilter).ConfigureAwait(false);
|
||||
@@ -354,7 +354,7 @@ internal sealed class ChannelService : BaseService<Channel>, IChannelService
|
||||
public async Task<MemoryStream> ExportMemoryStream(IEnumerable<Channel> channels)
|
||||
{
|
||||
var rows = ChannelServiceHelpers.ExportRows(channels); // IEnumerable 延迟执行
|
||||
var sheets = ChannelServiceHelpers.WrapAsSheet(ExportString.ChannelName, rows);
|
||||
var sheets = ChannelServiceHelpers.WrapAsSheet(GatewayExportString.ChannelName, rows);
|
||||
var memoryStream = new MemoryStream();
|
||||
await memoryStream.SaveAsAsync(sheets).ConfigureAwait(false);
|
||||
memoryStream.Seek(0, SeekOrigin.Begin);
|
||||
@@ -365,23 +365,19 @@ internal sealed class ChannelService : BaseService<Channel>, IChannelService
|
||||
|
||||
#region 导入
|
||||
|
||||
|
||||
/// <inheritdoc/>
|
||||
[OperDesc("ImportChannel", isRecordPar: false, localizerType: typeof(Channel))]
|
||||
public async Task<HashSet<long>> ImportChannelAsync(Dictionary<string, ImportPreviewOutputBase> input)
|
||||
public Task<HashSet<long>> ImportChannelAsync(Dictionary<string, ImportPreviewOutputBase> input)
|
||||
{
|
||||
List<Channel>? channels = new List<Channel>();
|
||||
foreach (var item in input)
|
||||
{
|
||||
if (item.Key == ExportString.ChannelName)
|
||||
{
|
||||
var channelImports = ((ImportPreviewListOutput<Channel>)item.Value).Data;
|
||||
channels = channelImports;
|
||||
break;
|
||||
}
|
||||
}
|
||||
var upData = channels.Where(a => a.IsUp).ToList();
|
||||
var insertData = channels.Where(a => !a.IsUp).ToList();
|
||||
ChannelServiceHelpers.GetImportChannelData(input, out var upData, out var insertData);
|
||||
return ImportChannelAsync(upData, insertData);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public async Task<HashSet<long>> ImportChannelAsync(List<Channel> upData, List<Channel> insertData)
|
||||
{
|
||||
ManageHelper.CheckChannelCount(insertData.Count);
|
||||
|
||||
using var db = GetDB();
|
||||
@@ -396,13 +392,23 @@ internal sealed class ChannelService : BaseService<Channel>, IChannelService
|
||||
await db.BulkUpdateAsync(upData, 10000).ConfigureAwait(false);
|
||||
}
|
||||
DeleteChannelFromCache();
|
||||
return channels.Select(a => a.Id).ToHashSet();
|
||||
return upData.Select(a => a.Id).Concat(insertData.Select(a => a.Id)).ToHashSet();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task<Dictionary<string, ImportPreviewOutputBase>> PreviewAsync(IBrowserFile browserFile)
|
||||
{
|
||||
var path = await browserFile.StorageLocal().ConfigureAwait(false);
|
||||
return await PreviewAsync(path).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task<Dictionary<string, ImportPreviewOutputBase>> PreviewAsync(string path)
|
||||
{
|
||||
try
|
||||
{
|
||||
var dataScope = await GlobalData.SysUserService.GetCurrentUserDataScopeAsync().ConfigureAwait(false);
|
||||
@@ -431,7 +437,7 @@ internal sealed class ChannelService : BaseService<Channel>, IChannelService
|
||||
{
|
||||
#region sheet
|
||||
|
||||
if (sheetName == ExportString.ChannelName)
|
||||
if (sheetName == GatewayExportString.ChannelName)
|
||||
{
|
||||
int row = 1;
|
||||
ImportPreviewListOutput<Channel> importPreviewOutput = new();
|
||||
@@ -441,7 +447,7 @@ internal sealed class ChannelService : BaseService<Channel>, IChannelService
|
||||
// 获取目标类型的所有属性,并根据是否需要过滤 IgnoreExcelAttribute 进行筛选
|
||||
var channelProperties = type.GetRuntimeProperties().Where(a => (a.GetCustomAttribute<IgnoreExcelAttribute>() == null) && a.CanWrite)
|
||||
.ToDictionary(a => type.GetPropertyDisplayName(a.Name), a => (a, a.IsNullableType()));
|
||||
|
||||
string unportNull = App.CreateLocalizerByType(typeof(Channel))["ImportNullError"];
|
||||
rows.ForEach(item =>
|
||||
{
|
||||
try
|
||||
@@ -450,7 +456,7 @@ internal sealed class ChannelService : BaseService<Channel>, IChannelService
|
||||
if (channel == null)
|
||||
{
|
||||
importPreviewOutput.HasError = true;
|
||||
importPreviewOutput.Results.Add((Interlocked.Increment(ref row), false, Localizer["ImportNullError"]));
|
||||
importPreviewOutput.Results.Add((Interlocked.Increment(ref row), false, unportNull));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@@ -16,55 +16,26 @@ using ThingsGateway.Common.Extension;
|
||||
|
||||
namespace ThingsGateway.Gateway.Application;
|
||||
|
||||
public static class ChannelServiceHelpers
|
||||
public static partial class ChannelServiceHelpers
|
||||
{
|
||||
public static USheetDatas ExportChannel(IEnumerable<Channel> channels)
|
||||
|
||||
public static void GetImportChannelData(Dictionary<string, ImportPreviewOutputBase> input, out List<Channel> upData, out List<Channel> insertData)
|
||||
{
|
||||
var rows = ExportRows(channels); // IEnumerable 延迟执行
|
||||
var sheets = WrapAsSheet(ExportString.ChannelName, rows);
|
||||
return USheetDataHelpers.GetUSheetDatas(sheets);
|
||||
}
|
||||
|
||||
internal static IEnumerable<Dictionary<string, object>> ExportRows(IEnumerable<Channel>? data)
|
||||
{
|
||||
if (data == null)
|
||||
yield break;
|
||||
|
||||
#region 列名称
|
||||
|
||||
var type = typeof(Channel);
|
||||
var propertyInfos = type.GetRuntimeProperties().Where(a => a.GetCustomAttribute<IgnoreExcelAttribute>(false) == null)
|
||||
.OrderBy(
|
||||
a =>
|
||||
{
|
||||
var order = a.GetCustomAttribute<AutoGenerateColumnAttribute>()?.Order ?? int.MaxValue;
|
||||
if (order < 0)
|
||||
{
|
||||
order = order + 10000000;
|
||||
}
|
||||
else if (order == 0)
|
||||
{
|
||||
order = 10000000;
|
||||
}
|
||||
return order;
|
||||
}
|
||||
)
|
||||
;
|
||||
|
||||
#endregion 列名称
|
||||
|
||||
foreach (var device in data)
|
||||
List<Channel>? channels = new List<Channel>();
|
||||
foreach (var item in input)
|
||||
{
|
||||
Dictionary<string, object> row = new();
|
||||
foreach (var prop in propertyInfos)
|
||||
if (item.Key == GatewayExportString.ChannelName)
|
||||
{
|
||||
var desc = type.GetPropertyDisplayName(prop.Name);
|
||||
row.Add(desc ?? prop.Name, prop.GetValue(device)?.ToString());
|
||||
var channelImports = ((ImportPreviewListOutput<Channel>)item.Value).Data;
|
||||
channels = channelImports;
|
||||
break;
|
||||
}
|
||||
yield return row;
|
||||
}
|
||||
upData = channels.Where(a => a.IsUp).ToList();
|
||||
insertData = channels.Where(a => !a.IsUp).ToList();
|
||||
}
|
||||
|
||||
|
||||
internal static async IAsyncEnumerable<Dictionary<string, object>> ExportRows(IAsyncEnumerable<Channel>? data)
|
||||
{
|
||||
if (data == null)
|
||||
@@ -108,13 +79,6 @@ public static class ChannelServiceHelpers
|
||||
}
|
||||
}
|
||||
|
||||
internal static Dictionary<string, object> WrapAsSheet(string sheetName, IEnumerable<IDictionary<string, object>> rows)
|
||||
{
|
||||
return new Dictionary<string, object>
|
||||
{
|
||||
[sheetName] = rows
|
||||
};
|
||||
}
|
||||
|
||||
internal static Dictionary<string, object> WrapAsSheet(string sheetName, IAsyncEnumerable<IDictionary<string, object>> rows)
|
||||
{
|
||||
|
@@ -0,0 +1,75 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://thingsgateway.cn/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using BootstrapBlazor.Components;
|
||||
|
||||
using System.Reflection;
|
||||
|
||||
using ThingsGateway.Common.Extension;
|
||||
|
||||
namespace ThingsGateway.Gateway.Application;
|
||||
|
||||
public static partial class ChannelServiceHelpers
|
||||
{
|
||||
public static USheetDatas ExportChannel(IEnumerable<Channel> channels)
|
||||
{
|
||||
var rows = ExportRows(channels); // IEnumerable 延迟执行
|
||||
var sheets = WrapAsSheet(GatewayExportString.ChannelName, rows);
|
||||
return USheetDataHelpers.GetUSheetDatas(sheets);
|
||||
}
|
||||
internal static Dictionary<string, object> WrapAsSheet(string sheetName, IEnumerable<IDictionary<string, object>> rows)
|
||||
{
|
||||
return new Dictionary<string, object>
|
||||
{
|
||||
[sheetName] = rows
|
||||
};
|
||||
}
|
||||
|
||||
internal static IEnumerable<Dictionary<string, object>> ExportRows(IEnumerable<Channel>? data)
|
||||
{
|
||||
if (data == null)
|
||||
yield break;
|
||||
|
||||
#region 列名称
|
||||
|
||||
var type = typeof(Channel);
|
||||
var propertyInfos = type.GetRuntimeProperties().Where(a => a.GetCustomAttribute<IgnoreExcelAttribute>(false) == null)
|
||||
.OrderBy(
|
||||
a =>
|
||||
{
|
||||
var order = a.GetCustomAttribute<AutoGenerateColumnAttribute>()?.Order ?? int.MaxValue;
|
||||
if (order < 0)
|
||||
{
|
||||
order = order + 10000000;
|
||||
}
|
||||
else if (order == 0)
|
||||
{
|
||||
order = 10000000;
|
||||
}
|
||||
return order;
|
||||
}
|
||||
)
|
||||
;
|
||||
|
||||
#endregion 列名称
|
||||
|
||||
foreach (var device in data)
|
||||
{
|
||||
Dictionary<string, object> row = new();
|
||||
foreach (var prop in propertyInfos)
|
||||
{
|
||||
var desc = type.GetPropertyDisplayName(prop.Name);
|
||||
row.Add(desc ?? prop.Name, prop.GetValue(device)?.ToString());
|
||||
}
|
||||
yield return row;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,64 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://thingsgateway.cn/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
using BootstrapBlazor.Components;
|
||||
|
||||
using Microsoft.AspNetCore.Components.Forms;
|
||||
|
||||
namespace ThingsGateway.Gateway.Application
|
||||
{
|
||||
public interface IChannelPageService
|
||||
{
|
||||
Task RestartChannelAsync(long channelId);
|
||||
|
||||
Task<TouchSocket.Core.LogLevel> ChannelLogLevelAsync(long id);
|
||||
Task SetChannelLogLevelAsync(long id, TouchSocket.Core.LogLevel logLevel);
|
||||
Task CopyChannelAsync(int CopyCount, string CopyChannelNamePrefix, int CopyChannelNameSuffixNumber, string CopyDeviceNamePrefix, int CopyDeviceNameSuffixNumber, long channelId, bool AutoRestartThread);
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 保存通道
|
||||
/// </summary>
|
||||
/// <param name="input">通道对象</param>
|
||||
/// <param name="type">保存类型</param>
|
||||
/// <param name="restart">重启</param>
|
||||
Task<bool> SaveChannelAsync(Channel input, ItemChangedType type, bool restart);
|
||||
|
||||
/// <summary>
|
||||
/// 批量修改
|
||||
/// </summary>
|
||||
/// <param name="models">列表</param>
|
||||
/// <param name="oldModel">旧数据</param>
|
||||
/// <param name="model">新数据</param>
|
||||
/// <param name="restart">重启</param>
|
||||
/// <returns></returns>
|
||||
Task<bool> BatchEditChannelAsync(IEnumerable<Channel> models, Channel oldModel, Channel model, bool restart);
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 删除通道
|
||||
/// </summary>
|
||||
Task<bool> DeleteChannelAsync(IEnumerable<long> ids, bool restart, CancellationToken cancellationToken);
|
||||
|
||||
Task ImportChannelAsync(List<Channel> upData, List<Channel> insertData, bool restart);
|
||||
Task<Dictionary<string, ImportPreviewOutputBase>> ImportChannelAsync(USheetDatas input, bool restart);
|
||||
Task<Dictionary<string, ImportPreviewOutputBase>> ImportChannelAsync(string filePath, bool restart);
|
||||
Task<Dictionary<string, ImportPreviewOutputBase>> ImportChannelAsync(IBrowserFile file, bool restart);
|
||||
|
||||
|
||||
|
||||
Task<QueryData<SelectedItem>> OnChannelSelectedItemQueryAsync(VirtualizeQueryOption option);
|
||||
|
||||
}
|
||||
}
|
@@ -14,15 +14,8 @@ using Microsoft.AspNetCore.Components.Forms;
|
||||
|
||||
namespace ThingsGateway.Gateway.Application;
|
||||
|
||||
public interface IChannelRuntimeService
|
||||
public interface IChannelRuntimeService : IChannelPageService
|
||||
{
|
||||
/// <summary>
|
||||
/// 保存通道
|
||||
/// </summary>
|
||||
/// <param name="input">通道对象</param>
|
||||
/// <param name="type">保存类型</param>
|
||||
/// <param name="restart">重启</param>
|
||||
Task<bool> SaveChannelAsync(Channel input, ItemChangedType type, bool restart);
|
||||
|
||||
/// <summary>
|
||||
/// 保存通道
|
||||
@@ -32,30 +25,21 @@ public interface IChannelRuntimeService
|
||||
/// <param name="restart">重启</param>
|
||||
Task<bool> BatchSaveChannelAsync(List<Channel> input, ItemChangedType type, bool restart);
|
||||
|
||||
/// <summary>
|
||||
/// 批量修改
|
||||
/// </summary>
|
||||
/// <param name="models">列表</param>
|
||||
/// <param name="oldModel">旧数据</param>
|
||||
/// <param name="model">新数据</param>
|
||||
/// <param name="restart">重启</param>
|
||||
/// <returns></returns>
|
||||
Task<bool> BatchEditAsync(IEnumerable<Channel> models, Channel oldModel, Channel model, bool restart);
|
||||
|
||||
/// <summary>
|
||||
/// 删除通道
|
||||
/// </summary>
|
||||
Task<bool> DeleteChannelAsync(IEnumerable<long> ids, bool restart, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// 导入通道数据
|
||||
/// </summary>
|
||||
Task ImportChannelAsync(Dictionary<string, ImportPreviewOutputBase> input, bool restart);
|
||||
Task<Dictionary<string, object>> ExportChannelAsync(ExportFilter exportFilter);
|
||||
|
||||
|
||||
Task<Dictionary<string, object>> ExportChannelAsync(GatewayExportFilter exportFilter);
|
||||
Task<Dictionary<string, ImportPreviewOutputBase>> PreviewAsync(IBrowserFile browserFile);
|
||||
Task<MemoryStream> ExportMemoryStream(IEnumerable<Channel> data);
|
||||
Task RestartChannelAsync(IEnumerable<ChannelRuntime> oldChannelRuntimes);
|
||||
Task<bool> CopyAsync(List<Channel> models, Dictionary<Device, List<Variable>> devices, bool restart, CancellationToken cancellationToken);
|
||||
Task<bool> UpdateAsync(List<Channel> models, List<Device> devices, List<Variable> variables, bool restart, CancellationToken cancellationToken);
|
||||
Task<bool> InsertAsync(List<Channel> models, List<Device> devices, List<Variable> variables, bool restart, CancellationToken cancellationToken);
|
||||
|
||||
|
||||
|
||||
}
|
@@ -46,7 +46,7 @@ internal interface IChannelService
|
||||
/// 导出通道为文件流结果
|
||||
/// </summary>
|
||||
/// <returns>文件流结果</returns>
|
||||
Task<Dictionary<string, object>> ExportChannelAsync(ExportFilter exportFilter);
|
||||
Task<Dictionary<string, object>> ExportChannelAsync(GatewayExportFilter exportFilter);
|
||||
|
||||
/// <summary>
|
||||
/// 导出通道为内存流
|
||||
@@ -71,7 +71,7 @@ internal interface IChannelService
|
||||
/// 报表查询
|
||||
/// </summary>
|
||||
/// <param name="exportFilter">查询条件</param>
|
||||
Task<QueryData<Channel>> PageAsync(ExportFilter exportFilter);
|
||||
Task<QueryData<Channel>> PageAsync(GatewayExportFilter exportFilter);
|
||||
|
||||
/// <summary>
|
||||
/// 预览导入数据
|
||||
@@ -102,4 +102,7 @@ internal interface IChannelService
|
||||
Task UpdateLogAsync(long channelId, TouchSocket.Core.LogLevel logLevel);
|
||||
Task<bool> InsertAsync(List<Channel> models, List<Device> devices, List<Variable> variables);
|
||||
Task<bool> UpdateAsync(List<Channel> models, List<Device> devices, List<Variable> variables);
|
||||
Task<HashSet<long>> ImportChannelAsync(List<Channel> upData, List<Channel> insertData);
|
||||
|
||||
Task<Dictionary<string, ImportPreviewOutputBase>> PreviewAsync(string path);
|
||||
}
|
||||
|
@@ -29,6 +29,46 @@ public class DeviceRuntimeService : IDeviceRuntimeService
|
||||
|
||||
private WaitLock WaitLock { get; set; } = new WaitLock(nameof(DeviceRuntimeService));
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public async Task CopyDeviceAsync(int CopyCount, string CopyDeviceNamePrefix, int CopyDeviceNameSuffixNumber, long deviceId, bool AutoRestartThread)
|
||||
{
|
||||
if (!GlobalData.IdDevices.TryGetValue(deviceId, out var deviceRuntime))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Device Model = deviceRuntime.AdaptDevice();
|
||||
Model.Id = 0;
|
||||
var Variables = deviceRuntime.ReadOnlyVariableRuntimes.Select(a => a.Value).AdaptListVariable();
|
||||
|
||||
|
||||
Dictionary<Device, List<Variable>> devices = new();
|
||||
for (int i = 0; i < CopyCount; i++)
|
||||
{
|
||||
Device device = Model.AdaptDevice();
|
||||
device.Id = CommonUtils.GetSingleId();
|
||||
device.Name = $"{CopyDeviceNamePrefix}{CopyDeviceNameSuffixNumber + i}";
|
||||
List<Variable> variables = new();
|
||||
|
||||
foreach (var item in Variables)
|
||||
{
|
||||
Variable v = item.AdaptVariable();
|
||||
v.Id = CommonUtils.GetSingleId();
|
||||
v.DeviceId = device.Id;
|
||||
variables.Add(v);
|
||||
}
|
||||
devices.Add(device, variables);
|
||||
}
|
||||
|
||||
await GlobalData.DeviceRuntimeService.CopyAsync(devices, AutoRestartThread, default).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public async Task<bool> CopyAsync(Dictionary<Device, List<Variable>> devices, bool restart, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
@@ -57,6 +97,10 @@ public class DeviceRuntimeService : IDeviceRuntimeService
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public async Task<bool> BatchEditAsync(IEnumerable<Device> models, Device oldModel, Device model, bool restart)
|
||||
{
|
||||
try
|
||||
@@ -122,7 +166,7 @@ public class DeviceRuntimeService : IDeviceRuntimeService
|
||||
}
|
||||
}
|
||||
|
||||
public Task<Dictionary<string, object>> ExportDeviceAsync(ExportFilter exportFilter) => GlobalData.DeviceService.ExportDeviceAsync(exportFilter);
|
||||
public Task<Dictionary<string, object>> ExportDeviceAsync(GatewayExportFilter exportFilter) => GlobalData.DeviceService.ExportDeviceAsync(exportFilter);
|
||||
public Task<Dictionary<string, ImportPreviewOutputBase>> PreviewAsync(IBrowserFile browserFile) => GlobalData.DeviceService.PreviewAsync(browserFile);
|
||||
public Task<MemoryStream> ExportMemoryStream(List<Device> data, string channelName, string plugin) =>
|
||||
GlobalData.DeviceService.ExportMemoryStream(data, channelName, plugin);
|
||||
|
@@ -200,14 +200,14 @@ internal sealed class DeviceService : BaseService<Device>, IDeviceService
|
||||
/// 报表查询
|
||||
/// </summary>
|
||||
/// <param name="exportFilter">查询条件</param>
|
||||
public async Task<QueryData<Device>> PageAsync(ExportFilter exportFilter)
|
||||
public async Task<QueryData<Device>> PageAsync(GatewayExportFilter exportFilter)
|
||||
{
|
||||
var whereQuery = await GetWhereQueryFunc(exportFilter).ConfigureAwait(false);
|
||||
|
||||
return await QueryAsync(exportFilter.QueryPageOptions, whereQuery
|
||||
, exportFilter.FilterKeyValueAction).ConfigureAwait(false);
|
||||
}
|
||||
private async Task<Func<ISugarQueryable<Device>, ISugarQueryable<Device>>> GetWhereQueryFunc(ExportFilter exportFilter)
|
||||
private async Task<Func<ISugarQueryable<Device>, ISugarQueryable<Device>>> GetWhereQueryFunc(GatewayExportFilter exportFilter)
|
||||
{
|
||||
HashSet<long>? channel = null;
|
||||
if (!exportFilter.PluginName.IsNullOrWhiteSpace())
|
||||
@@ -216,7 +216,7 @@ internal sealed class DeviceService : BaseService<Device>, IDeviceService
|
||||
}
|
||||
if (exportFilter.PluginType != null)
|
||||
{
|
||||
var pluginInfo = GlobalData.PluginService.GetPluginListSync(exportFilter.PluginType).Select(a => a.FullName).ToHashSet();
|
||||
var pluginInfo = GlobalData.PluginService.GetPluginList(exportFilter.PluginType).Select(a => a.FullName).ToHashSet();
|
||||
channel = (GlobalData.IdChannels).Where(a => pluginInfo.Contains(a.Value.PluginName)).Select(a => a.Value.Id).ToHashSet();
|
||||
}
|
||||
var dataScope = await GlobalData.SysUserService.GetCurrentUserDataScopeAsync().ConfigureAwait(false);
|
||||
@@ -229,7 +229,7 @@ internal sealed class DeviceService : BaseService<Device>, IDeviceService
|
||||
return whereQuery;
|
||||
}
|
||||
|
||||
private async Task<Func<IEnumerable<Device>, IEnumerable<Device>>> GetWhereEnumerableFunc(ExportFilter exportFilter)
|
||||
private async Task<Func<IEnumerable<Device>, IEnumerable<Device>>> GetWhereEnumerableFunc(GatewayExportFilter exportFilter)
|
||||
{
|
||||
HashSet<long>? channel = null;
|
||||
if (!exportFilter.PluginName.IsNullOrWhiteSpace())
|
||||
@@ -238,7 +238,7 @@ internal sealed class DeviceService : BaseService<Device>, IDeviceService
|
||||
}
|
||||
if (exportFilter.PluginType != null)
|
||||
{
|
||||
var pluginInfo = GlobalData.PluginService.GetPluginListSync(exportFilter.PluginType).Select(a => a.FullName).ToHashSet();
|
||||
var pluginInfo = GlobalData.PluginService.GetPluginList(exportFilter.PluginType).Select(a => a.FullName).ToHashSet();
|
||||
channel = (GlobalData.IdChannels).Where(a => pluginInfo.Contains(a.Value.PluginName)).Select(a => a.Value.Id).ToHashSet();
|
||||
}
|
||||
var dataScope = await GlobalData.SysUserService.GetCurrentUserDataScopeAsync().ConfigureAwait(false);
|
||||
@@ -302,7 +302,7 @@ internal sealed class DeviceService : BaseService<Device>, IDeviceService
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[OperDesc("ExportDevice", isRecordPar: false, localizerType: typeof(Device))]
|
||||
public async Task<Dictionary<string, object>> ExportDeviceAsync(ExportFilter exportFilter)
|
||||
public async Task<Dictionary<string, object>> ExportDeviceAsync(GatewayExportFilter exportFilter)
|
||||
{
|
||||
//导出
|
||||
var devices = await GetAsyncEnumerableData(exportFilter).ConfigureAwait(false);
|
||||
@@ -321,12 +321,12 @@ internal sealed class DeviceService : BaseService<Device>, IDeviceService
|
||||
|
||||
return sheets;
|
||||
}
|
||||
private async Task<IAsyncEnumerable<Device>> GetAsyncEnumerableData(ExportFilter exportFilter)
|
||||
private async Task<IAsyncEnumerable<Device>> GetAsyncEnumerableData(GatewayExportFilter exportFilter)
|
||||
{
|
||||
var whereQuery = await GetEnumerableData(exportFilter).ConfigureAwait(false);
|
||||
return whereQuery.ToAsyncEnumerable();
|
||||
}
|
||||
private async Task<ISugarQueryable<Device>> GetEnumerableData(ExportFilter exportFilter)
|
||||
private async Task<ISugarQueryable<Device>> GetEnumerableData(GatewayExportFilter exportFilter)
|
||||
{
|
||||
var db = GetDB();
|
||||
var whereQuery = await GetWhereQueryFunc(exportFilter).ConfigureAwait(false);
|
||||
@@ -367,7 +367,7 @@ internal sealed class DeviceService : BaseService<Device>, IDeviceService
|
||||
IEnumerable<Device>? devices = new List<Device>();
|
||||
foreach (var item in input)
|
||||
{
|
||||
if (item.Key == ExportString.DeviceName)
|
||||
if (item.Key == GatewayExportString.DeviceName)
|
||||
{
|
||||
var deviceImports = ((ImportPreviewOutput<Device>)item.Value).Data;
|
||||
devices = deviceImports.Select(a => a.Value);
|
||||
@@ -417,7 +417,7 @@ internal sealed class DeviceService : BaseService<Device>, IDeviceService
|
||||
ImportPreviewOutput<Device> deviceImportPreview = new();
|
||||
|
||||
// 获取所有驱动程序,并将驱动程序名称作为键构建字典
|
||||
var driverPluginNameDict = _pluginService.GetPluginListSync().DistinctBy(a => a.Name).ToDictionary(a => a.Name);
|
||||
var driverPluginNameDict = _pluginService.GetPluginList().DistinctBy(a => a.Name).ToDictionary(a => a.Name);
|
||||
ConcurrentDictionary<string, (Type, Dictionary<string, PropertyInfo>, Dictionary<string, PropertyInfo>)> propertysDict = new();
|
||||
foreach (var sheetName in sheetNames)
|
||||
{
|
||||
@@ -439,8 +439,14 @@ internal sealed class DeviceService : BaseService<Device>, IDeviceService
|
||||
public void SetDeviceData(HashSet<long>? dataScope, IReadOnlyDictionary<string, DeviceRuntime> deviceDicts, IReadOnlyDictionary<string, ChannelRuntime> channelDicts, Dictionary<string, ImportPreviewOutputBase> ImportPreviews, ref ImportPreviewOutput<Device> deviceImportPreview, Dictionary<string, PluginInfo> driverPluginNameDict, ConcurrentDictionary<string, (Type, Dictionary<string, PropertyInfo>, Dictionary<string, PropertyInfo>)> propertysDict, string sheetName, IEnumerable<IDictionary<string, object>> rows)
|
||||
{
|
||||
#region 采集设备sheet
|
||||
string ImportNullError = Localizer["ImportNullError"];
|
||||
string RedundantDeviceError = Localizer["RedundantDeviceError"];
|
||||
string ChannelError = Localizer["ChannelError"];
|
||||
|
||||
if (sheetName == ExportString.DeviceName)
|
||||
string PluginNotNull = Localizer["PluginNotNull"];
|
||||
string DeviceNotNull = Localizer["DeviceNotNull"];
|
||||
|
||||
if (sheetName == GatewayExportString.DeviceName)
|
||||
{
|
||||
// 初始化行数
|
||||
int row = 1;
|
||||
@@ -459,6 +465,8 @@ internal sealed class DeviceService : BaseService<Device>, IDeviceService
|
||||
var deviceProperties = type.GetRuntimeProperties().Where(a => (a.GetCustomAttribute<IgnoreExcelAttribute>() == null) && a.CanWrite)
|
||||
.ToDictionary(a => type.GetPropertyDisplayName(a.Name), a => (a, a.IsNullableType()));
|
||||
|
||||
|
||||
|
||||
// 遍历每一行数据
|
||||
rows.ForEach(item =>
|
||||
{
|
||||
@@ -471,13 +479,13 @@ internal sealed class DeviceService : BaseService<Device>, IDeviceService
|
||||
if (device == null)
|
||||
{
|
||||
importPreviewOutput.HasError = true;
|
||||
importPreviewOutput.Results.Add((Interlocked.Increment(ref row), false, Localizer["ImportNullError"]));
|
||||
importPreviewOutput.Results.Add((Interlocked.Increment(ref row), false, ImportNullError));
|
||||
return;
|
||||
}
|
||||
|
||||
// 转换冗余设备名称
|
||||
var hasRedundant = item.TryGetValue(ExportString.RedundantDeviceName, out var redundantObj);
|
||||
var hasChannel = item.TryGetValue(ExportString.ChannelName, out var channelObj);
|
||||
var hasRedundant = item.TryGetValue(GatewayExportString.RedundantDeviceName, out var redundantObj);
|
||||
var hasChannel = item.TryGetValue(GatewayExportString.ChannelName, out var channelObj);
|
||||
|
||||
// 设备ID、冗余设备ID都需要手动补录
|
||||
if (hasRedundant && redundantObj != null)
|
||||
@@ -488,7 +496,7 @@ internal sealed class DeviceService : BaseService<Device>, IDeviceService
|
||||
{
|
||||
// 如果找不到对应的冗余设备,则添加错误信息到导入预览结果并返回
|
||||
importPreviewOutput.HasError = true;
|
||||
importPreviewOutput.Results.Add((Interlocked.Increment(ref row), false, Localizer["RedundantDeviceError"]));
|
||||
importPreviewOutput.Results.Add((Interlocked.Increment(ref row), false, RedundantDeviceError));
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -498,7 +506,7 @@ internal sealed class DeviceService : BaseService<Device>, IDeviceService
|
||||
if (device.RedundantEnable)
|
||||
{
|
||||
importPreviewOutput.HasError = true;
|
||||
importPreviewOutput.Results.Add((Interlocked.Increment(ref row), false, Localizer["RedundantDeviceError"]));
|
||||
importPreviewOutput.Results.Add((Interlocked.Increment(ref row), false, RedundantDeviceError));
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -512,7 +520,7 @@ internal sealed class DeviceService : BaseService<Device>, IDeviceService
|
||||
{
|
||||
// 如果找不到对应的通道信息,则添加错误信息到导入预览结果并返回
|
||||
importPreviewOutput.HasError = true;
|
||||
importPreviewOutput.Results.Add((Interlocked.Increment(ref row), false, Localizer["ChannelError"]));
|
||||
importPreviewOutput.Results.Add((Interlocked.Increment(ref row), false, ChannelError));
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -520,7 +528,7 @@ internal sealed class DeviceService : BaseService<Device>, IDeviceService
|
||||
{
|
||||
// 如果未提供通道信息,则添加错误信息到导入预览结果并返回
|
||||
importPreviewOutput.HasError = true;
|
||||
importPreviewOutput.Results.Add((Interlocked.Increment(ref row), false, Localizer["ChannelError"]));
|
||||
importPreviewOutput.Results.Add((Interlocked.Increment(ref row), false, ChannelError));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -653,20 +661,20 @@ internal sealed class DeviceService : BaseService<Device>, IDeviceService
|
||||
if (propertys.Item1 == null)
|
||||
{
|
||||
importPreviewOutput.HasError = true;
|
||||
importPreviewOutput.Results.Add((Interlocked.Increment(ref row), false, Localizer["PluginNotNull"]));
|
||||
importPreviewOutput.Results.Add((Interlocked.Increment(ref row), false, PluginNotNull));
|
||||
continue;
|
||||
}
|
||||
|
||||
// 获取设备名称
|
||||
if (!item.TryGetValue(ExportString.DeviceName, out var deviceName))
|
||||
if (!item.TryGetValue(GatewayExportString.DeviceName, out var deviceName))
|
||||
{
|
||||
importPreviewOutput.HasError = true;
|
||||
importPreviewOutput.Results.Add((Interlocked.Increment(ref row), false, Localizer["DeviceNotNull"]));
|
||||
importPreviewOutput.Results.Add((Interlocked.Increment(ref row), false, DeviceNotNull));
|
||||
continue;
|
||||
}
|
||||
|
||||
// 转化插件名称
|
||||
var value = item[ExportString.DeviceName]?.ToString();
|
||||
var value = item[GatewayExportString.DeviceName]?.ToString();
|
||||
|
||||
// 检查设备名称是否存在于设备导入预览数据中,如果不存在,则添加错误信息到导入预览结果并继续下一轮循环
|
||||
var hasDevice = deviceImportPreview.Data.ContainsKey(value);
|
||||
@@ -684,7 +692,7 @@ internal sealed class DeviceService : BaseService<Device>, IDeviceService
|
||||
if (pluginProp == null)
|
||||
{
|
||||
importPreviewOutput.HasError = true;
|
||||
importPreviewOutput.Results.Add((Interlocked.Increment(ref row), false, Localizer["ImportNullError"]));
|
||||
importPreviewOutput.Results.Add((Interlocked.Increment(ref row), false, ImportNullError));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@@ -44,7 +44,7 @@ HashSet<string> pluginSheetNames,
|
||||
data = new List<Device>();
|
||||
|
||||
var result = new Dictionary<string, object>();
|
||||
result.Add(ExportString.DeviceName, GetDeviceSheets(data, deviceDicts, channelDicts, channelName));
|
||||
result.Add(GatewayExportString.DeviceName, GetDeviceSheets(data, deviceDicts, channelDicts, channelName));
|
||||
ConcurrentDictionary<string, (object, Dictionary<string, PropertyInfo>)> propertysDict = new();
|
||||
|
||||
foreach (var plugin in pluginSheetNames)
|
||||
@@ -69,7 +69,7 @@ string? channelName = null)
|
||||
return new();
|
||||
|
||||
var result = new Dictionary<string, object>();
|
||||
result.Add(ExportString.DeviceName, GetDeviceSheets(data1, deviceDicts, channelDicts, channelName));
|
||||
result.Add(GatewayExportString.DeviceName, GetDeviceSheets(data1, deviceDicts, channelDicts, channelName));
|
||||
ConcurrentDictionary<string, (object, Dictionary<string, PropertyInfo>)> propertysDict = new();
|
||||
|
||||
foreach (var plugin in pluginSheetNames)
|
||||
@@ -209,7 +209,7 @@ string? channelName)
|
||||
deviceDicts.TryGetValue(device.RedundantDeviceId ?? 0, out var redundantDevice);
|
||||
channelDicts.TryGetValue(device.ChannelId, out var channel);
|
||||
|
||||
devExport.Add(ExportString.ChannelName, channel?.Name ?? channelName);
|
||||
devExport.Add(GatewayExportString.ChannelName, channel?.Name ?? channelName);
|
||||
|
||||
foreach (var item in propertyInfos)
|
||||
{
|
||||
@@ -220,7 +220,7 @@ string? channelName)
|
||||
}
|
||||
|
||||
//设备实体没有包含冗余设备名称,手动插入
|
||||
devExport.Add(ExportString.RedundantDeviceName, redundantDevice?.Name);
|
||||
devExport.Add(GatewayExportString.RedundantDeviceName, redundantDevice?.Name);
|
||||
return devExport;
|
||||
}
|
||||
|
||||
@@ -250,7 +250,7 @@ string? channelName)
|
||||
if (propertys.Item2.Count > 0)
|
||||
{
|
||||
//没有包含设备名称,手动插入
|
||||
driverInfo.Add(ExportString.DeviceName, device.Name);
|
||||
driverInfo.Add(GatewayExportString.DeviceName, device.Name);
|
||||
}
|
||||
//根据插件的配置属性项生成列,从数据库中获取值或者获取属性默认值
|
||||
foreach (var item in propertys.Item2)
|
||||
@@ -288,10 +288,10 @@ string? channelName)
|
||||
ImportPreviewOutput<Device> deviceImportPreview = new();
|
||||
|
||||
// 获取所有驱动程序,并将驱动程序的完整名称作为键构建字典
|
||||
var driverPluginFullNameDict = GlobalData.PluginService.GetPluginListSync().ToDictionary(a => a.FullName);
|
||||
var driverPluginFullNameDict = GlobalData.PluginService.GetPluginList().ToDictionary(a => a.FullName);
|
||||
|
||||
// 获取所有驱动程序,并将驱动程序名称作为键构建字典
|
||||
var driverPluginNameDict = GlobalData.PluginService.GetPluginListSync().DistinctBy(a => a.Name).ToDictionary(a => a.Name);
|
||||
var driverPluginNameDict = GlobalData.PluginService.GetPluginList().DistinctBy(a => a.Name).ToDictionary(a => a.Name);
|
||||
ConcurrentDictionary<string, (Type, Dictionary<string, PropertyInfo>, Dictionary<string, PropertyInfo>)> propertysDict = new();
|
||||
|
||||
var sheetNames = uSheetDatas.sheets.Keys.ToList();
|
||||
|
@@ -0,0 +1,17 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://thingsgateway.cn/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace ThingsGateway.Gateway.Application
|
||||
{
|
||||
public interface IDevicePageService
|
||||
{
|
||||
Task CopyDeviceAsync(int CopyCount, string CopyDeviceNamePrefix, int CopyDeviceNameSuffixNumber, long deviceId, bool AutoRestartThread);
|
||||
}
|
||||
}
|
@@ -14,12 +14,12 @@ using Microsoft.AspNetCore.Components.Forms;
|
||||
|
||||
namespace ThingsGateway.Gateway.Application
|
||||
{
|
||||
public interface IDeviceRuntimeService
|
||||
public interface IDeviceRuntimeService : IDevicePageService
|
||||
{
|
||||
Task<bool> BatchEditAsync(IEnumerable<Device> models, Device oldModel, Device model, bool restart);
|
||||
Task<bool> CopyAsync(Dictionary<Device, List<Variable>> devices, bool restart, CancellationToken cancellationToken);
|
||||
Task<bool> DeleteDeviceAsync(IEnumerable<long> ids, bool restart, CancellationToken cancellationToken);
|
||||
Task<Dictionary<string, object>> ExportDeviceAsync(ExportFilter exportFilter);
|
||||
Task<Dictionary<string, object>> ExportDeviceAsync(GatewayExportFilter exportFilter);
|
||||
Task<MemoryStream> ExportMemoryStream(List<Device> data, string channelName, string plugin);
|
||||
Task ImportDeviceAsync(Dictionary<string, ImportPreviewOutputBase> input, bool restart);
|
||||
Task<Dictionary<string, ImportPreviewOutputBase>> PreviewAsync(IBrowserFile browserFile);
|
||||
|
@@ -59,7 +59,7 @@ internal interface IDeviceService
|
||||
/// 导出设备信息到文件流。
|
||||
/// </summary>
|
||||
/// <returns>导出的文件流</returns>
|
||||
Task<Dictionary<string, object>> ExportDeviceAsync(ExportFilter exportFilter);
|
||||
Task<Dictionary<string, object>> ExportDeviceAsync(GatewayExportFilter exportFilter);
|
||||
|
||||
/// <summary>
|
||||
/// 导出设备信息到内存流。
|
||||
@@ -88,7 +88,7 @@ internal interface IDeviceService
|
||||
/// </summary>
|
||||
/// <param name="exportFilter">查询条件</param>
|
||||
/// <returns>查询结果</returns>
|
||||
Task<QueryData<Device>> PageAsync(ExportFilter exportFilter);
|
||||
Task<QueryData<Device>> PageAsync(GatewayExportFilter exportFilter);
|
||||
|
||||
/// <summary>
|
||||
/// 预览导入设备信息。
|
||||
|
@@ -21,7 +21,7 @@ internal sealed class BackendLogService : BaseService<BackendLog>, IBackendLogSe
|
||||
/// <summary>
|
||||
/// 最新十条
|
||||
/// </summary>
|
||||
public async Task<List<BackendLog>> GetNewBackendLog()
|
||||
public async Task<List<BackendLog>> GetNewBackendLogAsync()
|
||||
{
|
||||
using var db = GetDB();
|
||||
var data = await db.Queryable<BackendLog>().OrderByDescending(a => a.LogTime).Take(10).ToListAsync().ConfigureAwait(false);
|
||||
|
@@ -26,7 +26,7 @@ public interface IBackendLogService
|
||||
/// 获取最新的十条 BackendLog 记录
|
||||
/// </summary>
|
||||
/// <returns>最新的十条记录</returns>
|
||||
Task<List<BackendLog>> GetNewBackendLog();
|
||||
Task<List<BackendLog>> GetNewBackendLogAsync();
|
||||
|
||||
/// <summary>
|
||||
/// 分页查询 BackendLog 数据
|
||||
|
@@ -26,7 +26,7 @@ public interface IRpcLogService
|
||||
/// 获取最新的十条 RpcLog 记录
|
||||
/// </summary>
|
||||
/// <returns>最新的十条记录</returns>
|
||||
Task<List<RpcLog>> GetNewRpcLog();
|
||||
Task<List<RpcLog>> GetNewRpcLogAsync();
|
||||
|
||||
/// <summary>
|
||||
/// 分页查询 RpcLog 数据
|
||||
|
@@ -21,7 +21,7 @@ internal sealed class RpcLogService : BaseService<RpcLog>, IRpcLogService
|
||||
/// <summary>
|
||||
/// 最新十条
|
||||
/// </summary>
|
||||
public async Task<List<RpcLog>> GetNewRpcLog()
|
||||
public async Task<List<RpcLog>> GetNewRpcLogAsync()
|
||||
{
|
||||
using var db = GetDB();
|
||||
var data = await db.Queryable<RpcLog>().OrderByDescending(a => a.LogTime).Take(10).ToListAsync().ConfigureAwait(false);
|
||||
|
@@ -15,21 +15,21 @@ namespace ThingsGateway.Gateway.Application;
|
||||
|
||||
internal sealed class AuthenticationService : IAuthenticationService
|
||||
{
|
||||
public Task<string> UUID() => Task.FromResult(ProAuthentication.UUID);
|
||||
public Task<AuthorizeInfo> TryGetAuthorizeInfo()
|
||||
public Task<string> UUIDAsync() => Task.FromResult(ProAuthentication.UUID);
|
||||
public Task<AuthorizeInfo> TryGetAuthorizeInfoAsync()
|
||||
{
|
||||
ProAuthentication.TryGetAuthorizeInfo(out var authorizeInfo);
|
||||
return Task.FromResult(authorizeInfo);
|
||||
}
|
||||
|
||||
public Task<AuthorizeInfo> TryAuthorize(string password)
|
||||
public Task<AuthorizeInfo> TryAuthorizeAsync(string password)
|
||||
{
|
||||
ProAuthentication.TryAuthorize(password, out var authorizeInfo);
|
||||
return Task.FromResult(authorizeInfo);
|
||||
|
||||
}
|
||||
|
||||
public Task UnAuthorize()
|
||||
public Task UnAuthorizeAsync()
|
||||
{
|
||||
ProAuthentication.UnAuthorize();
|
||||
return Task.CompletedTask;
|
||||
|
@@ -14,8 +14,8 @@ namespace ThingsGateway.Gateway.Application;
|
||||
|
||||
public interface IAuthenticationService
|
||||
{
|
||||
Task<string> UUID();
|
||||
Task<AuthorizeInfo> TryAuthorize(string password);
|
||||
Task<AuthorizeInfo> TryGetAuthorizeInfo();
|
||||
Task UnAuthorize();
|
||||
Task<string> UUIDAsync();
|
||||
Task<AuthorizeInfo> TryAuthorizeAsync(string password);
|
||||
Task<AuthorizeInfo> TryGetAuthorizeInfoAsync();
|
||||
Task UnAuthorizeAsync();
|
||||
}
|
||||
|
@@ -16,11 +16,11 @@ internal sealed class ChannelEnableService : IChannelEnableService
|
||||
/// <summary>
|
||||
/// 采集通道是否可用
|
||||
/// </summary>
|
||||
public Task<bool> StartCollectChannelEnable() => Task.FromResult(GlobalData.StartCollectChannelEnable);
|
||||
public Task<bool> StartCollectChannelEnableAsync() => Task.FromResult(GlobalData.StartCollectChannelEnable);
|
||||
|
||||
/// <summary>
|
||||
/// 业务通道是否可用
|
||||
/// </summary>
|
||||
public Task<bool> StartBusinessChannelEnable() => Task.FromResult(GlobalData.StartBusinessChannelEnable);
|
||||
public Task<bool> StartBusinessChannelEnableAsync() => Task.FromResult(GlobalData.StartBusinessChannelEnable);
|
||||
|
||||
}
|
||||
|
@@ -0,0 +1,55 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://thingsgateway.cn/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using BootstrapBlazor.Components;
|
||||
|
||||
using ThingsGateway.Extension.Generic;
|
||||
using ThingsGateway.NewLife.Extension;
|
||||
|
||||
namespace ThingsGateway.Gateway.Application;
|
||||
|
||||
public class GlobalDataService : IGlobalDataService
|
||||
{
|
||||
public async Task<IEnumerable<SelectedItem>> GetCurrentUserDeviceSelectedItemsAsync(string searchText, int startIndex, int count)
|
||||
{
|
||||
var devices = await GlobalData.GetCurrentUserDevices().ConfigureAwait(false);
|
||||
var items = devices.WhereIf(!searchText.IsNullOrWhiteSpace(), a => a.Name.Contains(searchText)).Skip(startIndex).Take(count)
|
||||
.Select(a => new SelectedItem(a.Name, a.Name));
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
public Task<QueryData<SelectedItem>> GetCurrentUserDeviceVariableSelectedItemsAsync(string deviceText, string searchText, int startIndex, int count)
|
||||
{
|
||||
var ret = new QueryData<SelectedItem>()
|
||||
{
|
||||
IsSorted = false,
|
||||
IsFiltered = false,
|
||||
IsAdvanceSearch = false,
|
||||
IsSearch = !searchText.IsNullOrWhiteSpace()
|
||||
};
|
||||
|
||||
if ((!deviceText.IsNullOrWhiteSpace()) && GlobalData.ReadOnlyDevices.TryGetValue(deviceText, out var device))
|
||||
{
|
||||
var items = device.ReadOnlyVariableRuntimes.WhereIf(!searchText.IsNullOrWhiteSpace(), a => a.Value.Name.Contains(searchText)).Select(a => a.Value).Skip(startIndex).Take(count)
|
||||
.Select(a => new SelectedItem(a.Name, a.Name)).ToList();
|
||||
|
||||
ret.TotalCount = items.Count;
|
||||
ret.Items = items;
|
||||
return Task.FromResult(ret);
|
||||
}
|
||||
else
|
||||
{
|
||||
ret.TotalCount = 0;
|
||||
ret.Items = new List<SelectedItem>();
|
||||
return Task.FromResult(ret);
|
||||
}
|
||||
}
|
||||
}
|
@@ -15,10 +15,10 @@ public interface IChannelEnableService
|
||||
/// <summary>
|
||||
/// 采集通道是否可用
|
||||
/// </summary>
|
||||
public Task<bool> StartCollectChannelEnable();
|
||||
public Task<bool> StartCollectChannelEnableAsync();
|
||||
|
||||
/// <summary>
|
||||
/// 业务通道是否可用
|
||||
/// </summary>
|
||||
public Task<bool> StartBusinessChannelEnable();
|
||||
public Task<bool> StartBusinessChannelEnableAsync();
|
||||
}
|
||||
|
@@ -0,0 +1,19 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://thingsgateway.cn/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using BootstrapBlazor.Components;
|
||||
|
||||
namespace ThingsGateway.Gateway.Application;
|
||||
|
||||
public interface IGlobalDataService
|
||||
{
|
||||
Task<IEnumerable<SelectedItem>> GetCurrentUserDeviceSelectedItemsAsync(string searchText, int startIndex, int count);
|
||||
Task<QueryData<SelectedItem>> GetCurrentUserDeviceVariableSelectedItemsAsync(string deviceText, string searchText, int startIndex, int count);
|
||||
}
|
@@ -23,13 +23,13 @@ namespace ThingsGateway.Gateway.Application;
|
||||
public interface IManagementRpcServer : IRpcServer
|
||||
{
|
||||
[DmtpRpc]
|
||||
Task<Dictionary<string, Dictionary<string, OperResult<object>>>> Rpc(ICallContext callContext, Dictionary<string, Dictionary<string, string>> deviceDatas);
|
||||
Task<Dictionary<string, Dictionary<string, OperResult<object>>>> RpcAsync(ICallContext callContext, Dictionary<string, Dictionary<string, string>> deviceDatas);
|
||||
|
||||
|
||||
[DmtpRpc]
|
||||
Task DeleteBackendLogAsync();
|
||||
[DmtpRpc]
|
||||
Task<List<BackendLog>> GetNewBackendLog();
|
||||
Task<List<BackendLog>> GetNewBackendLogAsync();
|
||||
[DmtpRpc]
|
||||
Task<QueryData<BackendLog>> BackendLogPageAsync(QueryPageOptions option);
|
||||
[DmtpRpc]
|
||||
@@ -50,7 +50,7 @@ public interface IManagementRpcServer : IRpcServer
|
||||
/// </summary>
|
||||
/// <returns>最新的十条记录</returns>
|
||||
[DmtpRpc]
|
||||
Task<List<RpcLog>> GetNewRpcLog();
|
||||
Task<List<RpcLog>> GetNewRpcLogAsync();
|
||||
|
||||
/// <summary>
|
||||
/// 分页查询 RpcLog 数据
|
||||
@@ -69,21 +69,21 @@ public interface IManagementRpcServer : IRpcServer
|
||||
Task<List<RpcLogDayStatisticsOutput>> RpcLogStatisticsByDayAsync(int day);
|
||||
|
||||
[DmtpRpc]
|
||||
Task RestartServer();
|
||||
Task RestartServerAsync();
|
||||
|
||||
[DmtpRpc]
|
||||
Task<string> UUID();
|
||||
Task<string> UUIDAsync();
|
||||
|
||||
[DmtpRpc]
|
||||
Task<AuthorizeInfo> TryAuthorize(string password);
|
||||
Task<AuthorizeInfo> TryAuthorizeAsync(string password);
|
||||
[DmtpRpc]
|
||||
Task<AuthorizeInfo> TryGetAuthorizeInfo();
|
||||
Task<AuthorizeInfo> TryGetAuthorizeInfoAsync();
|
||||
[DmtpRpc]
|
||||
Task UnAuthorize();
|
||||
Task UnAuthorizeAsync();
|
||||
[DmtpRpc]
|
||||
Task<bool> StartBusinessChannelEnable();
|
||||
Task<bool> StartBusinessChannelEnableAsync();
|
||||
[DmtpRpc]
|
||||
Task<bool> StartCollectChannelEnable();
|
||||
Task<bool> StartCollectChannelEnableAsync();
|
||||
|
||||
|
||||
[DmtpRpc]
|
||||
@@ -94,11 +94,11 @@ public interface IManagementRpcServer : IRpcServer
|
||||
Task RedundancyForcedSync();
|
||||
|
||||
[DmtpRpc]
|
||||
public Task<TouchSocket.Core.LogLevel> RedundancyLogLevel();
|
||||
public Task<TouchSocket.Core.LogLevel> RedundancyLogLevelAsync();
|
||||
[DmtpRpc]
|
||||
public Task SetRedundancyLogLevel(TouchSocket.Core.LogLevel logLevel);
|
||||
public Task SetRedundancyLogLevelAsync(TouchSocket.Core.LogLevel logLevel);
|
||||
[DmtpRpc]
|
||||
public Task<string> RedundancyLogPath();
|
||||
public Task<string> RedundancyLogPathAsync();
|
||||
|
||||
/// <summary>
|
||||
/// 修改冗余设置
|
||||
@@ -112,9 +112,9 @@ public interface IManagementRpcServer : IRpcServer
|
||||
[DmtpRpc]
|
||||
Task<RedundancyOptions> GetRedundancyAsync();
|
||||
[DmtpRpc]
|
||||
Task<OperResult<List<string>>> GetLogFiles(string directoryPath);
|
||||
Task<OperResult<List<string>>> GetLogFilesAsync(string directoryPath);
|
||||
[DmtpRpc]
|
||||
Task<OperResult<List<LogData>>> LastLogData(string file, int lineCount = 200);
|
||||
Task<OperResult<List<LogData>>> LastLogDataAsync(string file, int lineCount = 200);
|
||||
|
||||
|
||||
|
||||
@@ -127,19 +127,19 @@ public interface IManagementRpcServer : IRpcServer
|
||||
/// <param name="pluginType"></param>
|
||||
/// <returns></returns>
|
||||
[DmtpRpc]
|
||||
Task<List<PluginInfo>> GetPluginListAsync(PluginTypeEnum? pluginType = null);
|
||||
Task<List<PluginInfo>> GetPluginsAsync(PluginTypeEnum? pluginType = null);
|
||||
|
||||
/// <summary>
|
||||
/// 分页显示插件
|
||||
/// </summary>
|
||||
[DmtpRpc]
|
||||
public Task<QueryData<PluginInfo>> PluginPage(QueryPageOptions options, PluginTypeEnum? pluginTypeEnum = null);
|
||||
public Task<QueryData<PluginInfo>> PluginPageAsync(QueryPageOptions options, PluginTypeEnum? pluginTypeEnum = null);
|
||||
|
||||
/// <summary>
|
||||
/// 重载插件
|
||||
/// </summary>
|
||||
[DmtpRpc]
|
||||
Task ReloadPlugin();
|
||||
Task ReloadPluginAsync();
|
||||
|
||||
|
||||
/// <summary>
|
||||
@@ -148,10 +148,68 @@ public interface IManagementRpcServer : IRpcServer
|
||||
/// <param name="plugin"></param>
|
||||
/// <returns></returns>
|
||||
[DmtpRpc]
|
||||
Task SavePluginByPath(PluginAddPathInput plugin);
|
||||
Task SavePluginByPathAsync(PluginAddPathInput plugin);
|
||||
|
||||
|
||||
[DmtpRpc]
|
||||
Task<IEnumerable<AlarmVariable>> GetCurrentUserRealAlarmVariables();
|
||||
Task<IEnumerable<AlarmVariable>> GetCurrentUserRealAlarmVariablesAsync();
|
||||
|
||||
|
||||
|
||||
[DmtpRpc]
|
||||
Task<IEnumerable<SelectedItem>> GetCurrentUserDeviceSelectedItemsAsync(string searchText, int startIndex, int count);
|
||||
[DmtpRpc]
|
||||
Task<QueryData<SelectedItem>> GetCurrentUserDeviceVariableSelectedItemsAsync(string deviceText, string searchText, int startIndex, int count);
|
||||
|
||||
|
||||
[DmtpRpc]
|
||||
Task<TouchSocket.Core.LogLevel> RulesLogLevelAsync(long rulesId);
|
||||
[DmtpRpc]
|
||||
Task SetRulesLogLevelAsync(long rulesId, TouchSocket.Core.LogLevel logLevel);
|
||||
[DmtpRpc]
|
||||
Task<string> RulesLogPathAsync(long rulesId);
|
||||
[DmtpRpc]
|
||||
Task<Rules> GetRuleRuntimesAsync(long rulesId);
|
||||
[DmtpRpc]
|
||||
Task DeleteRuleRuntimesAsync(List<long> ids);
|
||||
[DmtpRpc]
|
||||
Task EditRuleRuntimesAsync(Rules rules);
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 清除所有规则
|
||||
/// </summary>
|
||||
[DmtpRpc]
|
||||
Task ClearRulesAsync();
|
||||
|
||||
/// <summary>
|
||||
/// 删除规则
|
||||
/// </summary>
|
||||
/// <param name="ids">待删除规则的ID列表</param>
|
||||
[DmtpRpc]
|
||||
Task<bool> DeleteRulesAsync(List<long> ids);
|
||||
|
||||
/// <summary>
|
||||
/// 从缓存/数据库获取全部信息
|
||||
/// </summary>
|
||||
/// <returns>规则列表</returns>
|
||||
[DmtpRpc]
|
||||
Task<List<Rules>> GetAllAsync();
|
||||
|
||||
/// <summary>
|
||||
/// 报表查询
|
||||
/// </summary>
|
||||
/// <param name="option">查询条件</param>
|
||||
/// <param name="filterKeyValueAction">查询条件</param>
|
||||
[DmtpRpc]
|
||||
Task<QueryData<Rules>> RulesPageAsync(QueryPageOptions option, FilterKeyValueAction filterKeyValueAction = null);
|
||||
|
||||
/// <summary>
|
||||
/// 保存规则
|
||||
/// </summary>
|
||||
/// <param name="input">规则对象</param>
|
||||
/// <param name="type">保存类型</param>
|
||||
[DmtpRpc]
|
||||
Task<bool> SaveRulesAsync(Rules input, ItemChangedType type);
|
||||
}
|
@@ -29,14 +29,14 @@ public partial class ManagementRpcServer : IRpcServer, IManagementRpcServer, IBa
|
||||
public Task<List<BackendLogDayStatisticsOutput>> BackendLogStatisticsByDayAsync(int day) => App.GetService<IBackendLogService>().BackendLogStatisticsByDayAsync(day);
|
||||
|
||||
[DmtpRpc]
|
||||
public Task<List<BackendLog>> GetNewBackendLog() => App.GetService<IBackendLogService>().GetNewBackendLog();
|
||||
public Task<List<BackendLog>> GetNewBackendLogAsync() => App.GetService<IBackendLogService>().GetNewBackendLogAsync();
|
||||
|
||||
[DmtpRpc]
|
||||
public Task<QueryData<BackendLog>> BackendLogPageAsync(QueryPageOptions option) => App.GetService<IBackendLogService>().BackendLogPageAsync(option);
|
||||
|
||||
|
||||
[DmtpRpc]
|
||||
public async Task<Dictionary<string, Dictionary<string, OperResult<object>>>> Rpc(ICallContext callContext, Dictionary<string, Dictionary<string, string>> deviceDatas)
|
||||
public async Task<Dictionary<string, Dictionary<string, OperResult<object>>>> RpcAsync(ICallContext callContext, Dictionary<string, Dictionary<string, string>> deviceDatas)
|
||||
{
|
||||
var data = await GlobalData.RpcService.InvokeDeviceMethodAsync($"Management[{(callContext.Caller is ITcpSession tcpSession ? tcpSession.GetIPPort() : string.Empty)}]", deviceDatas, callContext.Token).ConfigureAwait(false);
|
||||
|
||||
@@ -45,26 +45,26 @@ public partial class ManagementRpcServer : IRpcServer, IManagementRpcServer, IBa
|
||||
|
||||
public Task DeleteRpcLogAsync() => App.GetService<IRpcLogService>().DeleteRpcLogAsync();
|
||||
|
||||
public Task<List<RpcLog>> GetNewRpcLog() => App.GetService<IRpcLogService>().GetNewRpcLog();
|
||||
public Task<List<RpcLog>> GetNewRpcLogAsync() => App.GetService<IRpcLogService>().GetNewRpcLogAsync();
|
||||
|
||||
public Task<QueryData<RpcLog>> RpcLogPageAsync(QueryPageOptions option) => App.GetService<IRpcLogService>().RpcLogPageAsync(option);
|
||||
|
||||
public Task<List<RpcLogDayStatisticsOutput>> RpcLogStatisticsByDayAsync(int day) => App.GetService<IRpcLogService>().RpcLogStatisticsByDayAsync(day);
|
||||
|
||||
public Task RestartServer() => App.GetService<IRestartService>().RestartServer();
|
||||
public Task RestartServerAsync() => App.GetService<IRestartService>().RestartServerAsync();
|
||||
|
||||
public Task<string> UUID() => App.GetService<IAuthenticationService>().UUID();
|
||||
public Task<string> UUIDAsync() => App.GetService<IAuthenticationService>().UUIDAsync();
|
||||
|
||||
public Task<AuthorizeInfo> TryAuthorize(string password) => App.GetService<IAuthenticationService>().TryAuthorize(password);
|
||||
public Task<AuthorizeInfo> TryAuthorizeAsync(string password) => App.GetService<IAuthenticationService>().TryAuthorizeAsync(password);
|
||||
|
||||
public Task<AuthorizeInfo> TryGetAuthorizeInfo() => App.GetService<IAuthenticationService>().TryGetAuthorizeInfo();
|
||||
public Task<AuthorizeInfo> TryGetAuthorizeInfoAsync() => App.GetService<IAuthenticationService>().TryGetAuthorizeInfoAsync();
|
||||
|
||||
public Task UnAuthorize() => App.GetService<IAuthenticationService>().UnAuthorize();
|
||||
public Task UnAuthorizeAsync() => App.GetService<IAuthenticationService>().UnAuthorizeAsync();
|
||||
|
||||
public Task<bool> StartBusinessChannelEnable() => App.GetService<IChannelEnableService>().StartBusinessChannelEnable();
|
||||
public Task<bool> StartBusinessChannelEnableAsync() => App.GetService<IChannelEnableService>().StartBusinessChannelEnableAsync();
|
||||
|
||||
|
||||
public Task<bool> StartCollectChannelEnable() => App.GetService<IChannelEnableService>().StartCollectChannelEnable();
|
||||
public Task<bool> StartCollectChannelEnableAsync() => App.GetService<IChannelEnableService>().StartCollectChannelEnableAsync();
|
||||
|
||||
public Task StartRedundancyTaskAsync() => App.GetService<IRedundancyHostedService>().StartRedundancyTaskAsync();
|
||||
|
||||
@@ -77,26 +77,54 @@ public partial class ManagementRpcServer : IRpcServer, IManagementRpcServer, IBa
|
||||
|
||||
public Task<RedundancyOptions> GetRedundancyAsync() => App.GetService<IRedundancyService>().GetRedundancyAsync();
|
||||
|
||||
public Task<LogLevel> RedundancyLogLevel() => App.GetService<IRedundancyHostedService>().RedundancyLogLevel();
|
||||
public Task<LogLevel> RedundancyLogLevelAsync() => App.GetService<IRedundancyHostedService>().RedundancyLogLevelAsync();
|
||||
|
||||
|
||||
public Task SetRedundancyLogLevel(LogLevel logLevel) => App.GetService<IRedundancyHostedService>().SetRedundancyLogLevel(logLevel);
|
||||
public Task SetRedundancyLogLevelAsync(LogLevel logLevel) => App.GetService<IRedundancyHostedService>().SetRedundancyLogLevelAsync(logLevel);
|
||||
|
||||
public Task<string> RedundancyLogPath() => App.GetService<IRedundancyHostedService>().RedundancyLogPath();
|
||||
public Task<string> RedundancyLogPathAsync() => App.GetService<IRedundancyHostedService>().RedundancyLogPathAsync();
|
||||
|
||||
public Task<OperResult<List<string>>> GetLogFiles(string directoryPath) => App.GetService<ITextFileReadService>().GetLogFiles(directoryPath);
|
||||
public Task<OperResult<List<string>>> GetLogFilesAsync(string directoryPath) => App.GetService<ITextFileReadService>().GetLogFilesAsync(directoryPath);
|
||||
|
||||
public Task<OperResult<List<LogData>>> LastLogData(string file, int lineCount = 200) => App.GetService<ITextFileReadService>().LastLogData(file, lineCount);
|
||||
public Task<OperResult<List<LogData>>> LastLogDataAsync(string file, int lineCount = 200) => App.GetService<ITextFileReadService>().LastLogDataAsync(file, lineCount);
|
||||
|
||||
public Task<List<PluginInfo>> GetPluginListAsync(PluginTypeEnum? pluginType = null) => App.GetService<IPluginPageService>().GetPluginListAsync(pluginType);
|
||||
public Task<List<PluginInfo>> GetPluginsAsync(PluginTypeEnum? pluginType = null) => App.GetService<IPluginPageService>().GetPluginsAsync(pluginType);
|
||||
|
||||
public Task<QueryData<PluginInfo>> PluginPage(QueryPageOptions options, PluginTypeEnum? pluginTypeEnum = null) => App.GetService<IPluginPageService>().PluginPage(options, pluginTypeEnum);
|
||||
public Task<QueryData<PluginInfo>> PluginPageAsync(QueryPageOptions options, PluginTypeEnum? pluginTypeEnum = null) => App.GetService<IPluginPageService>().PluginPageAsync(options, pluginTypeEnum);
|
||||
|
||||
public Task ReloadPlugin() => App.GetService<IPluginPageService>().ReloadPlugin();
|
||||
public Task ReloadPluginAsync() => App.GetService<IPluginPageService>().ReloadPluginAsync();
|
||||
|
||||
|
||||
|
||||
public Task SavePluginByPath(PluginAddPathInput plugin) => App.GetService<IPluginPageService>().SavePluginByPath(plugin);
|
||||
public Task SavePluginByPathAsync(PluginAddPathInput plugin) => App.GetService<IPluginPageService>().SavePluginByPathAsync(plugin);
|
||||
|
||||
public Task<IEnumerable<AlarmVariable>> GetCurrentUserRealAlarmVariablesAsync() => App.GetService<IRealAlarmService>().GetCurrentUserRealAlarmVariablesAsync();
|
||||
|
||||
public Task<IEnumerable<SelectedItem>> GetCurrentUserDeviceSelectedItemsAsync(string searchText, int startIndex, int count) => App.GetService<IGlobalDataService>().GetCurrentUserDeviceSelectedItemsAsync(searchText, startIndex, count);
|
||||
|
||||
public Task<QueryData<SelectedItem>> GetCurrentUserDeviceVariableSelectedItemsAsync(string deviceText, string searchText, int startIndex, int count) => App.GetService<IGlobalDataService>().GetCurrentUserDeviceVariableSelectedItemsAsync(deviceText, searchText, startIndex, count);
|
||||
|
||||
public Task<TouchSocket.Core.LogLevel> RulesLogLevelAsync(long rulesId) => App.GetService<IRulesEngineHostedService>().RulesLogLevelAsync(rulesId);
|
||||
public Task SetRulesLogLevelAsync(long rulesId, TouchSocket.Core.LogLevel logLevel) => App.GetService<IRulesEngineHostedService>().SetRulesLogLevelAsync(rulesId, logLevel);
|
||||
public Task<string> RulesLogPathAsync(long rulesId) => App.GetService<IRulesEngineHostedService>().RulesLogPathAsync(rulesId);
|
||||
public Task<Rules> GetRuleRuntimesAsync(long rulesId) => App.GetService<IRulesEngineHostedService>().GetRuleRuntimesAsync(rulesId);
|
||||
|
||||
|
||||
public Task DeleteRuleRuntimesAsync(List<long> ids) => App.GetService<IRulesEngineHostedService>().DeleteRuleRuntimesAsync(ids);
|
||||
|
||||
public Task EditRuleRuntimesAsync(Rules rules) => App.GetService<IRulesEngineHostedService>().EditRuleRuntimesAsync(rules);
|
||||
|
||||
public Task ClearRulesAsync() => App.GetService<IRulesService>().ClearRulesAsync();
|
||||
|
||||
|
||||
public Task<bool> DeleteRulesAsync(List<long> ids) => App.GetService<IRulesService>().DeleteRulesAsync(ids);
|
||||
|
||||
public Task<List<Rules>> GetAllAsync() => App.GetService<IRulesService>().GetAllAsync();
|
||||
|
||||
|
||||
public Task<QueryData<Rules>> RulesPageAsync(QueryPageOptions option, FilterKeyValueAction filterKeyValueAction = null) => App.GetService<IRulesService>().RulesPageAsync(option, filterKeyValueAction);
|
||||
|
||||
|
||||
public Task<bool> SaveRulesAsync(Rules input, ItemChangedType type) => App.GetService<IRulesService>().SaveRulesAsync(input, type);
|
||||
|
||||
public Task<IEnumerable<AlarmVariable>> GetCurrentUserRealAlarmVariables() => App.GetService<IRealAlarmService>().GetCurrentUserRealAlarmVariables();
|
||||
}
|
||||
|
@@ -11,6 +11,8 @@
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
using ThingsGateway.NewLife.Json.Extension;
|
||||
|
||||
using TouchSocket.Core;
|
||||
using TouchSocket.Dmtp;
|
||||
using TouchSocket.Dmtp.FileTransfer;
|
||||
@@ -45,7 +47,7 @@ public partial class ManagementTask : AsyncDisposableObject
|
||||
{
|
||||
_logger?.Log_Out(logLevel, source, message, exception);
|
||||
}
|
||||
|
||||
private bool success = true;
|
||||
public async Task StartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
if (!_managementOptions.Enable) return;
|
||||
@@ -63,10 +65,14 @@ public partial class ManagementTask : AsyncDisposableObject
|
||||
try
|
||||
{
|
||||
await EnsureChannelOpenAsync(cancellationToken).ConfigureAwait(false);
|
||||
success = true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogMessage?.LogWarning(ex, "Start");
|
||||
if (success)
|
||||
LogMessage?.LogWarning(ex, "Start");
|
||||
|
||||
success = false;
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -107,6 +113,11 @@ public partial class ManagementTask : AsyncDisposableObject
|
||||
{
|
||||
b.UseSystemTextJson(json =>
|
||||
{
|
||||
json.Converters.Add(new ByteArrayToNumberArrayConverterSystemTextJson());
|
||||
json.Converters.Add(new JTokenSystemTextJsonConverter());
|
||||
json.Converters.Add(new JValueSystemTextJsonConverter());
|
||||
json.Converters.Add(new JObjectSystemTextJsonConverter());
|
||||
json.Converters.Add(new JArraySystemTextJsonConverter());
|
||||
});
|
||||
});
|
||||
|
||||
@@ -162,6 +173,11 @@ public partial class ManagementTask : AsyncDisposableObject
|
||||
{
|
||||
b.UseSystemTextJson(json =>
|
||||
{
|
||||
json.Converters.Add(new ByteArrayToNumberArrayConverterSystemTextJson());
|
||||
json.Converters.Add(new JTokenSystemTextJsonConverter());
|
||||
json.Converters.Add(new JValueSystemTextJsonConverter());
|
||||
json.Converters.Add(new JObjectSystemTextJsonConverter());
|
||||
json.Converters.Add(new JArraySystemTextJsonConverter());
|
||||
});
|
||||
});
|
||||
|
||||
|
@@ -16,7 +16,7 @@ public interface IRedundancyHostedService
|
||||
Task StopRedundancyTaskAsync();
|
||||
Task RedundancyForcedSync();
|
||||
|
||||
public Task<TouchSocket.Core.LogLevel> RedundancyLogLevel();
|
||||
public Task SetRedundancyLogLevel(TouchSocket.Core.LogLevel logLevel);
|
||||
public Task<string> RedundancyLogPath();
|
||||
public Task<TouchSocket.Core.LogLevel> RedundancyLogLevelAsync();
|
||||
public Task SetRedundancyLogLevelAsync(TouchSocket.Core.LogLevel logLevel);
|
||||
public Task<string> RedundancyLogPathAsync();
|
||||
}
|
||||
|
@@ -17,7 +17,7 @@ namespace ThingsGateway.Gateway.Application;
|
||||
internal interface IRedundantRpcServer : IRpcServer
|
||||
{
|
||||
[DmtpRpc]
|
||||
Task<Dictionary<string, Dictionary<string, OperResult<object>>>> Rpc(ICallContext callContext, Dictionary<string, Dictionary<string, string>> deviceDatas);
|
||||
Task<Dictionary<string, Dictionary<string, OperResult<object>>>> RpcAsync(ICallContext callContext, Dictionary<string, Dictionary<string, string>> deviceDatas);
|
||||
|
||||
[DmtpRpc]
|
||||
Task SyncData(List<Channel> channels, List<Device> devices, List<Variable> variables);
|
||||
|
@@ -42,18 +42,18 @@ internal sealed class RedundancyHostedService : BackgroundService, IRedundancyHo
|
||||
await base.StopAsync(cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public Task<TouchSocket.Core.LogLevel> RedundancyLogLevel()
|
||||
public Task<TouchSocket.Core.LogLevel> RedundancyLogLevelAsync()
|
||||
{
|
||||
return Task.FromResult(RedundancyTask.TextLogger.LogLevel);
|
||||
}
|
||||
|
||||
public Task SetRedundancyLogLevel(TouchSocket.Core.LogLevel logLevel)
|
||||
public Task SetRedundancyLogLevelAsync(TouchSocket.Core.LogLevel logLevel)
|
||||
{
|
||||
RedundancyTask.TextLogger.LogLevel = logLevel;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task<string> RedundancyLogPath()
|
||||
public Task<string> RedundancyLogPathAsync()
|
||||
{
|
||||
return Task.FromResult(RedundancyTask.LogPath);
|
||||
}
|
||||
|
@@ -15,6 +15,7 @@ using Newtonsoft.Json.Linq;
|
||||
|
||||
using ThingsGateway.Extension.Generic;
|
||||
using ThingsGateway.NewLife;
|
||||
using ThingsGateway.NewLife.Json.Extension;
|
||||
|
||||
using TouchSocket.Core;
|
||||
using TouchSocket.Dmtp;
|
||||
@@ -349,6 +350,11 @@ internal sealed class RedundancyTask : IRpcDriver, IAsyncDisposable
|
||||
{
|
||||
b.UseSystemTextJson(json =>
|
||||
{
|
||||
json.Converters.Add(new ByteArrayToNumberArrayConverterSystemTextJson());
|
||||
json.Converters.Add(new JTokenSystemTextJsonConverter());
|
||||
json.Converters.Add(new JValueSystemTextJsonConverter());
|
||||
json.Converters.Add(new JObjectSystemTextJsonConverter());
|
||||
json.Converters.Add(new JArraySystemTextJsonConverter());
|
||||
});
|
||||
});
|
||||
a.UseDmtpHeartbeat()//使用Dmtp心跳
|
||||
@@ -388,6 +394,11 @@ internal sealed class RedundancyTask : IRpcDriver, IAsyncDisposable
|
||||
{
|
||||
b.UseSystemTextJson(json =>
|
||||
{
|
||||
json.Converters.Add(new ByteArrayToNumberArrayConverterSystemTextJson());
|
||||
json.Converters.Add(new JTokenSystemTextJsonConverter());
|
||||
json.Converters.Add(new JValueSystemTextJsonConverter());
|
||||
json.Converters.Add(new JObjectSystemTextJsonConverter());
|
||||
json.Converters.Add(new JArraySystemTextJsonConverter());
|
||||
});
|
||||
});
|
||||
a.UseDmtpHeartbeat()//使用Dmtp心跳
|
||||
@@ -520,10 +531,10 @@ internal sealed class RedundancyTask : IRpcDriver, IAsyncDisposable
|
||||
/// <returns>写入操作的结果字典</returns>
|
||||
public async ValueTask<Dictionary<string, Dictionary<string, IOperResult>>> InvokeMethodAsync(Dictionary<VariableRuntime, JToken> writeInfoLists, CancellationToken cancellationToken)
|
||||
{
|
||||
return (await Rpc(writeInfoLists, cancellationToken).ConfigureAwait(false)).ToDictionary(a => a.Key, a => a.Value.ToDictionary(b => b.Key, b => (IOperResult)b.Value));
|
||||
return (await RpcAsync(writeInfoLists, cancellationToken).ConfigureAwait(false)).ToDictionary(a => a.Key, a => a.Value.ToDictionary(b => b.Key, b => (IOperResult)b.Value));
|
||||
}
|
||||
|
||||
private async ValueTask<Dictionary<string, Dictionary<string, OperResult<object>>>> Rpc(Dictionary<VariableRuntime, JToken> writeInfoLists, CancellationToken cancellationToken)
|
||||
private async ValueTask<Dictionary<string, Dictionary<string, OperResult<object>>>> RpcAsync(Dictionary<VariableRuntime, JToken> writeInfoLists, CancellationToken cancellationToken)
|
||||
{
|
||||
var dataResult = new Dictionary<string, Dictionary<string, OperResult<object>>>();
|
||||
|
||||
@@ -660,7 +671,7 @@ internal sealed class RedundancyTask : IRpcDriver, IAsyncDisposable
|
||||
/// <returns>写入操作的结果字典</returns>
|
||||
public async ValueTask<Dictionary<string, Dictionary<string, IOperResult>>> InVokeWriteAsync(Dictionary<VariableRuntime, JToken> writeInfoLists, CancellationToken cancellationToken)
|
||||
{
|
||||
return (await Rpc(writeInfoLists, cancellationToken).ConfigureAwait(false)).ToDictionary(a => a.Key, a => a.Value.ToDictionary(b => b.Key, b => (IOperResult)b.Value));
|
||||
return (await RpcAsync(writeInfoLists, cancellationToken).ConfigureAwait(false)).ToDictionary(a => a.Key, a => a.Value.ToDictionary(b => b.Key, b => (IOperResult)b.Value));
|
||||
}
|
||||
private async Task EnsureChannelOpenAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
|
@@ -145,7 +145,7 @@ internal sealed partial class RedundantRpcServer : SingletonRpcServer, IRedundan
|
||||
}
|
||||
|
||||
[DmtpRpc]
|
||||
public async Task<Dictionary<string, Dictionary<string, OperResult<object>>>> Rpc(ICallContext callContext, Dictionary<string, Dictionary<string, string>> deviceDatas)
|
||||
public async Task<Dictionary<string, Dictionary<string, OperResult<object>>>> RpcAsync(ICallContext callContext, Dictionary<string, Dictionary<string, string>> deviceDatas)
|
||||
{
|
||||
var data = await GlobalData.RpcService.InvokeDeviceMethodAsync($"Redundant[{(callContext.Caller is ITcpSession tcpSession ? tcpSession.GetIPPort() : string.Empty)}]", deviceDatas, callContext.Token).ConfigureAwait(false);
|
||||
|
||||
|
@@ -12,5 +12,5 @@ namespace ThingsGateway.Gateway.Application;
|
||||
|
||||
public interface IRestartService
|
||||
{
|
||||
Task RestartServer();
|
||||
Task RestartServerAsync();
|
||||
}
|
||||
|
@@ -14,7 +14,7 @@ namespace ThingsGateway.Gateway.Application;
|
||||
internal sealed class RestartService : IRestartService
|
||||
{
|
||||
|
||||
public Task RestartServer()
|
||||
public Task RestartServerAsync()
|
||||
{
|
||||
RestartServerHelper.RestartServer();
|
||||
return Task.CompletedTask;
|
||||
|
@@ -8,8 +8,10 @@
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
|
||||
using TouchSocket.Core;
|
||||
@@ -86,6 +88,7 @@ public partial class WebApiTask : AsyncDisposableObject
|
||||
{
|
||||
store.RegisterServer<ControlController>();
|
||||
store.RegisterServer<RuntimeInfoController>();
|
||||
store.RegisterServer<TestController>();
|
||||
});
|
||||
|
||||
//添加跨域服务
|
||||
@@ -109,7 +112,8 @@ public partial class WebApiTask : AsyncDisposableObject
|
||||
|
||||
a.UseWebApi();
|
||||
|
||||
a.UseSwagger();
|
||||
if (App.WebHostEnvironment.IsDevelopment() || Debugger.IsAttached)
|
||||
a.UseSwagger();
|
||||
|
||||
a.UseDefaultHttpServicePlugin();
|
||||
});
|
||||
|
@@ -24,17 +24,17 @@ public interface IPluginPageService
|
||||
/// </summary>
|
||||
/// <param name="pluginType"></param>
|
||||
/// <returns></returns>
|
||||
Task<List<PluginInfo>> GetPluginListAsync(PluginTypeEnum? pluginType = null);
|
||||
Task<List<PluginInfo>> GetPluginsAsync(PluginTypeEnum? pluginType = null);
|
||||
|
||||
/// <summary>
|
||||
/// 分页显示插件
|
||||
/// </summary>
|
||||
public Task<QueryData<PluginInfo>> PluginPage(QueryPageOptions options, PluginTypeEnum? pluginTypeEnum = null);
|
||||
public Task<QueryData<PluginInfo>> PluginPageAsync(QueryPageOptions options, PluginTypeEnum? pluginTypeEnum = null);
|
||||
|
||||
/// <summary>
|
||||
/// 重载插件
|
||||
/// </summary>
|
||||
Task ReloadPlugin();
|
||||
Task ReloadPluginAsync();
|
||||
|
||||
///// <summary>
|
||||
///// 添加插件
|
||||
@@ -48,5 +48,5 @@ public interface IPluginPageService
|
||||
/// </summary>
|
||||
/// <param name="plugin"></param>
|
||||
/// <returns></returns>
|
||||
Task SavePluginByPath(PluginAddPathInput plugin);
|
||||
Task SavePluginByPathAsync(PluginAddPathInput plugin);
|
||||
}
|
||||
|
@@ -26,7 +26,7 @@ public interface IPluginService : IPluginPageService
|
||||
/// </summary>
|
||||
/// <param name="pluginType"></param>
|
||||
/// <returns></returns>
|
||||
List<PluginInfo> GetPluginListSync(PluginTypeEnum? pluginType = null);
|
||||
List<PluginInfo> GetPluginList(PluginTypeEnum? pluginType = null);
|
||||
|
||||
|
||||
/// <summary>
|
||||
|
@@ -73,7 +73,7 @@ public static class PluginInfoUtil
|
||||
}
|
||||
|
||||
|
||||
await pluginPageService.SavePluginByPath(pluginAddPathInput).ConfigureAwait(false);
|
||||
await pluginPageService.SavePluginByPathAsync(pluginAddPathInput).ConfigureAwait(false);
|
||||
|
||||
|
||||
}
|
||||
|
@@ -35,7 +35,7 @@ internal sealed class PluginService : IPluginService
|
||||
/// </summary>
|
||||
public const string DirName = "GatewayPlugins";
|
||||
|
||||
private const string CacheKeyGetPluginOutputs = $"ThingsGateway.Gateway.Application.{nameof(PluginService)}{nameof(GetPluginListSync)}";
|
||||
private const string CacheKeyGetPluginOutputs = $"ThingsGateway.Gateway.Application.{nameof(PluginService)}{nameof(GetPluginList)}";
|
||||
private const string SaveEx = ".save";
|
||||
private const string DelEx = ".del";
|
||||
|
||||
@@ -259,7 +259,7 @@ internal sealed class PluginService : IPluginService
|
||||
/// </summary>
|
||||
/// <param name="pluginType">要筛选的插件类型,可选参数</param>
|
||||
/// <returns>符合条件的插件列表</returns>
|
||||
public Task<List<PluginInfo>> GetPluginListAsync(PluginTypeEnum? pluginType = null)
|
||||
public Task<List<PluginInfo>> GetPluginsAsync(PluginTypeEnum? pluginType = null)
|
||||
{
|
||||
// 获取完整的插件列表
|
||||
var pluginList = PrivateGetList();
|
||||
@@ -281,7 +281,7 @@ internal sealed class PluginService : IPluginService
|
||||
/// </summary>
|
||||
/// <param name="pluginType">要筛选的插件类型,可选参数</param>
|
||||
/// <returns>符合条件的插件列表</returns>
|
||||
public List<PluginInfo> GetPluginListSync(PluginTypeEnum? pluginType = null)
|
||||
public List<PluginInfo> GetPluginList(PluginTypeEnum? pluginType = null)
|
||||
{
|
||||
// 获取完整的插件列表
|
||||
var pluginList = PrivateGetList();
|
||||
@@ -330,17 +330,17 @@ internal sealed class PluginService : IPluginService
|
||||
/// <summary>
|
||||
/// 分页显示插件
|
||||
/// </summary>
|
||||
public async Task<QueryData<PluginInfo>> PluginPage(QueryPageOptions options, PluginTypeEnum? pluginType = null)
|
||||
public async Task<QueryData<PluginInfo>> PluginPageAsync(QueryPageOptions options, PluginTypeEnum? pluginType = null)
|
||||
{
|
||||
//指定关键词搜索为插件FullName
|
||||
var query = (await GetPluginListAsync(pluginType).ConfigureAwait(false)).WhereIf(!options.SearchText.IsNullOrWhiteSpace(), a => a.FullName.Contains(options.SearchText)).GetQueryData(options);
|
||||
var query = (await GetPluginsAsync(pluginType).ConfigureAwait(false)).WhereIf(!options.SearchText.IsNullOrWhiteSpace(), a => a.FullName.Contains(options.SearchText)).GetQueryData(options);
|
||||
return query;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 移除全部插件
|
||||
/// </summary>
|
||||
public async Task ReloadPlugin()
|
||||
public async Task ReloadPluginAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -368,7 +368,7 @@ internal sealed class PluginService : IPluginService
|
||||
/// </summary>
|
||||
/// <param name="pluginAddPathInput">要保存的插件信息。</param>
|
||||
[OperDesc("SavePlugin", isRecordPar: false, localizerType: typeof(PluginAddInput))]
|
||||
public async Task SavePluginByPath(PluginAddPathInput pluginAddPathInput)
|
||||
public async Task SavePluginByPathAsync(PluginAddPathInput pluginAddPathInput)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -747,7 +747,7 @@ internal sealed class PluginService : IPluginService
|
||||
if (data == null)
|
||||
{
|
||||
var pluginInfos = GetPluginOutputs();
|
||||
App.CacheService.Set(CacheKeyGetPluginOutputs, pluginInfos);
|
||||
App.CacheService.Set(CacheKeyGetPluginOutputs, pluginInfos.ToList());
|
||||
return pluginInfos;
|
||||
}
|
||||
|
||||
|
@@ -31,7 +31,7 @@ public class Rules : BaseDataEntity
|
||||
[SugarColumn(IsJson = true, ColumnDataType = StaticConfig.CodeFirst_BigString, ColumnDescription = "RulesJson", IsNullable = true)]
|
||||
[IgnoreExcel]
|
||||
[AutoGenerateColumn(Ignore = true)]
|
||||
public RulesJson RulesJson { get; set; } = new();
|
||||
public RulesJson RulesJson { get; set; }
|
||||
}
|
||||
public class RulesJson
|
||||
{
|
||||
|
@@ -59,6 +59,9 @@ public static class RuleHelpers
|
||||
|
||||
public static NodeModel GetNodeModel(string key, string id, Point point)
|
||||
{
|
||||
#if Management
|
||||
key = key.Replace(",ThingsGateway.Gateway.Application", ",ThingsGateway.Management.Application");
|
||||
#endif
|
||||
var type = Type.GetType(key.Contains('.') ? key : $"ThingsGateway.Gateway.Application.{key}");
|
||||
if (type != null)
|
||||
{
|
||||
@@ -120,7 +123,14 @@ public static class RuleHelpers
|
||||
{
|
||||
NodeJson nodeJson = new();
|
||||
rules.NodeJsons.Add(nodeJson);
|
||||
#if Management
|
||||
|
||||
nodeJson.DraggedType = $"{item.GetType().FullName},ThingsGateway.Gateway.Application";
|
||||
#else
|
||||
|
||||
nodeJson.DraggedType = $"{item.GetType().FullName},{item.GetType().Assembly.GetName().Name}";
|
||||
|
||||
#endif
|
||||
nodeJson.Point = item.Position;
|
||||
nodeJson.Id = item.Id;
|
||||
foreach (var keyValuePair in GetModelValue(item))
|
||||
|
@@ -2,6 +2,13 @@
|
||||
|
||||
public interface INode
|
||||
{
|
||||
#if !Management
|
||||
public const string ModuleBasePath = "_content/ThingsGateway.Gateway.Razor/";
|
||||
public const string FileModulePath = "ThingsGateway.Gateway.Razor";
|
||||
#else
|
||||
public const string ModuleBasePath = "_content/ThingsGateway.Management.Razor/";
|
||||
public const string FileModulePath = "ThingsGateway.Management.Razor";
|
||||
#endif
|
||||
public TouchSocket.Core.ILog Logger { get; set; }
|
||||
string RulesEngineName { get; set; }
|
||||
}
|
||||
|
@@ -6,7 +6,7 @@ using TouchSocket.Core;
|
||||
|
||||
namespace ThingsGateway.Gateway.Application;
|
||||
|
||||
[CategoryNode(Category = "Actuator", ImgUrl = "_content/ThingsGateway.Gateway.Razor/img/CSharpScript.svg", Desc = nameof(ExecuteScriptNode), LocalizerType = typeof(ThingsGateway.Gateway.Application.INode), WidgetType = "ThingsGateway.Gateway.Razor.CSharpScriptWidget,ThingsGateway.Gateway.Razor")]
|
||||
[CategoryNode(Category = "Actuator", ImgUrl = $"{INode.ModuleBasePath}img/CSharpScript.svg", Desc = nameof(ExecuteScriptNode), LocalizerType = typeof(ThingsGateway.Gateway.Application.INode), WidgetType = $"ThingsGateway.Gateway.Razor.CSharpScriptWidget,{INode.FileModulePath}")]
|
||||
public class ExecuteScriptNode : TextNode, IActuatorNode, IExexcuteExpressionsBase, IDisposable
|
||||
{
|
||||
public ExecuteScriptNode(string id, Point? position = null) : base(id, position)
|
||||
|
@@ -5,7 +5,7 @@ using TouchSocket.Core;
|
||||
|
||||
namespace ThingsGateway.Gateway.Application;
|
||||
|
||||
[CategoryNode(Category = "Actuator", ImgUrl = "_content/ThingsGateway.Gateway.Razor/img/Rpc.svg", Desc = nameof(VariableRpcNode), LocalizerType = typeof(ThingsGateway.Gateway.Application.INode), WidgetType = "ThingsGateway.Gateway.Razor.VariableWidget,ThingsGateway.Gateway.Razor")]
|
||||
[CategoryNode(Category = "Actuator", ImgUrl = $"{INode.ModuleBasePath}img/Rpc.svg", Desc = nameof(VariableRpcNode), LocalizerType = typeof(ThingsGateway.Gateway.Application.INode), WidgetType = $"ThingsGateway.Gateway.Razor.VariableWidget,{INode.FileModulePath}")]
|
||||
public class VariableRpcNode : VariableNode, IActuatorNode
|
||||
{
|
||||
public VariableRpcNode(string id, Point? position = null) : base(id, position)
|
||||
|
@@ -9,7 +9,7 @@ using TouchSocket.Core;
|
||||
|
||||
namespace ThingsGateway.Gateway.Application;
|
||||
|
||||
[CategoryNode(Category = "Expression", ImgUrl = "_content/ThingsGateway.Gateway.Razor/img/CSharpScript.svg", Desc = nameof(ConditionNode), LocalizerType = typeof(ThingsGateway.Gateway.Application.INode), WidgetType = "ThingsGateway.Gateway.Razor.CSharpScriptWidget,ThingsGateway.Gateway.Razor")]
|
||||
[CategoryNode(Category = "Expression", ImgUrl = $"{INode.ModuleBasePath}img/CSharpScript.svg", Desc = nameof(ConditionNode), LocalizerType = typeof(ThingsGateway.Gateway.Application.INode), WidgetType = $"ThingsGateway.Gateway.Razor.CSharpScriptWidget,{INode.FileModulePath}")]
|
||||
public class ConditionNode : TextNode, IConditionNode
|
||||
{
|
||||
public ConditionNode(string id, Point? position = null) : base(id, position)
|
||||
|
@@ -9,7 +9,7 @@ using TouchSocket.Core;
|
||||
|
||||
namespace ThingsGateway.Gateway.Application;
|
||||
|
||||
[CategoryNode(Category = "Expression", ImgUrl = "_content/ThingsGateway.Gateway.Razor/img/CSharpScript.svg", Desc = nameof(DataNode), LocalizerType = typeof(ThingsGateway.Gateway.Application.INode), WidgetType = "ThingsGateway.Gateway.Razor.CSharpScriptWidget,ThingsGateway.Gateway.Razor")]
|
||||
[CategoryNode(Category = "Expression", ImgUrl = $"{INode.ModuleBasePath}img/CSharpScript.svg", Desc = nameof(DataNode), LocalizerType = typeof(ThingsGateway.Gateway.Application.INode), WidgetType = $"ThingsGateway.Gateway.Razor.CSharpScriptWidget,{INode.FileModulePath}")]
|
||||
public class DataNode : TextNode, IExpressionNode
|
||||
{
|
||||
public DataNode(string id, Point? position = null) : base(id, position)
|
||||
|
@@ -5,7 +5,7 @@ using TouchSocket.Core;
|
||||
|
||||
namespace ThingsGateway.Gateway.Application;
|
||||
|
||||
[CategoryNode(Category = "Expression", ImgUrl = "_content/ThingsGateway.Gateway.Razor/img/Delay.svg", Desc = nameof(DelayNode), LocalizerType = typeof(ThingsGateway.Gateway.Application.INode), WidgetType = "ThingsGateway.Gateway.Razor.NumberWidget,ThingsGateway.Gateway.Razor")]
|
||||
[CategoryNode(Category = "Expression", ImgUrl = $"{INode.ModuleBasePath}img/Delay.svg", Desc = nameof(DelayNode), LocalizerType = typeof(ThingsGateway.Gateway.Application.INode), WidgetType = $"ThingsGateway.Gateway.Razor.NumberWidget,{INode.FileModulePath}")]
|
||||
public class DelayNode : NumberNode, IExpressionNode
|
||||
{
|
||||
public DelayNode(string id, Point? position = null) : base(id, position) { Title = "DelayNode"; Placeholder = "DelayNode.Placeholder"; }
|
||||
|
@@ -3,7 +3,7 @@ using ThingsGateway.Blazor.Diagrams.Core.Geometry;
|
||||
|
||||
namespace ThingsGateway.Gateway.Application;
|
||||
|
||||
[CategoryNode(Category = "Start/End", ImgUrl = "_content/ThingsGateway.Gateway.Razor/img/Start.svg", Desc = nameof(StartNode), LocalizerType = typeof(ThingsGateway.Gateway.Application.INode), WidgetType = "ThingsGateway.Gateway.Razor.DefaultWidget,ThingsGateway.Gateway.Razor")]
|
||||
[CategoryNode(Category = "Start/End", ImgUrl = $"{INode.ModuleBasePath}img/Start.svg", Desc = nameof(StartNode), LocalizerType = typeof(ThingsGateway.Gateway.Application.INode), WidgetType = $"ThingsGateway.Gateway.Razor.DefaultWidget,{INode.FileModulePath}")]
|
||||
public class StartNode : BaseNode, IStartNode
|
||||
{
|
||||
public StartNode(string id, Point? position = null) : base(id, position)
|
||||
|
@@ -8,7 +8,7 @@ using TouchSocket.Core;
|
||||
|
||||
namespace ThingsGateway.Gateway.Application;
|
||||
|
||||
[CategoryNode(Category = "Trigger", ImgUrl = "_content/ThingsGateway.Gateway.Razor/img/ValueChanged.svg", Desc = nameof(AlarmChangedTriggerNode), LocalizerType = typeof(ThingsGateway.Gateway.Application.INode), WidgetType = "ThingsGateway.Gateway.Razor.VariableWidget,ThingsGateway.Gateway.Razor")]
|
||||
[CategoryNode(Category = "Trigger", ImgUrl = $"{INode.ModuleBasePath}img/ValueChanged.svg", Desc = nameof(AlarmChangedTriggerNode), LocalizerType = typeof(ThingsGateway.Gateway.Application.INode), WidgetType = $"ThingsGateway.Gateway.Razor.VariableWidget,{INode.FileModulePath}")]
|
||||
public class AlarmChangedTriggerNode : VariableNode, ITriggerNode, IDisposable
|
||||
{
|
||||
public AlarmChangedTriggerNode(string id, Point? position = null) : base(id, position) { Title = "AlarmChangedTriggerNode"; }
|
||||
|
@@ -8,7 +8,7 @@ using TouchSocket.Core;
|
||||
|
||||
namespace ThingsGateway.Gateway.Application;
|
||||
|
||||
[CategoryNode(Category = "Trigger", ImgUrl = "_content/ThingsGateway.Gateway.Razor/img/ValueChanged.svg", Desc = nameof(DeviceChangedTriggerNode), LocalizerType = typeof(ThingsGateway.Gateway.Application.INode), WidgetType = "ThingsGateway.Gateway.Razor.DeviceWidget,ThingsGateway.Gateway.Razor")]
|
||||
[CategoryNode(Category = "Trigger", ImgUrl = $"{INode.ModuleBasePath}img/ValueChanged.svg", Desc = nameof(DeviceChangedTriggerNode), LocalizerType = typeof(ThingsGateway.Gateway.Application.INode), WidgetType = $"ThingsGateway.Gateway.Razor.DeviceWidget,{INode.FileModulePath}")]
|
||||
public class DeviceChangedTriggerNode : TextNode, ITriggerNode, IDisposable
|
||||
{
|
||||
public DeviceChangedTriggerNode(string id, Point? position = null) : base(id, position) { Title = "DeviceChangedTriggerNode"; Placeholder = "Device.Placeholder"; }
|
||||
|
@@ -5,7 +5,7 @@ using TouchSocket.Core;
|
||||
|
||||
namespace ThingsGateway.Gateway.Application;
|
||||
|
||||
[CategoryNode(Category = "Trigger", ImgUrl = "_content/ThingsGateway.Gateway.Razor/img/TimeInterval.svg", Desc = nameof(TimeIntervalTriggerNode), LocalizerType = typeof(ThingsGateway.Gateway.Application.INode), WidgetType = "ThingsGateway.Gateway.Razor.TextWidget,ThingsGateway.Gateway.Razor")]
|
||||
[CategoryNode(Category = "Trigger", ImgUrl = $"{INode.ModuleBasePath}img/TimeInterval.svg", Desc = nameof(TimeIntervalTriggerNode), LocalizerType = typeof(ThingsGateway.Gateway.Application.INode), WidgetType = $"ThingsGateway.Gateway.Razor.TextWidget,{INode.FileModulePath}")]
|
||||
public class TimeIntervalTriggerNode : TextNode, ITriggerNode, IDisposable
|
||||
{
|
||||
~TimeIntervalTriggerNode()
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user