mirror of
https://gitee.com/ThingsGateway/ThingsGateway.git
synced 2025-10-20 10:50:48 +08:00
!71 适配远程客户端
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -364,6 +364,7 @@ FodyWeavers.xsd
|
||||
|
||||
/src/*Pro*/
|
||||
/src/*Pro*
|
||||
/src/**/*Pro*
|
||||
/src/*pro*
|
||||
/src/*pro*/
|
||||
/src/ThingsGateway.Server/Configuration/GiteeOAuthSettings.json
|
||||
|
@@ -11,6 +11,7 @@
|
||||
<IncludeBuildOutput>false</IncludeBuildOutput>
|
||||
<!-- 避免 DLL 被打包到 lib/ -->
|
||||
<EnableSourceGenerator>true</EnableSourceGenerator>
|
||||
|
||||
<!-- 可选 -->
|
||||
|
||||
|
||||
|
@@ -39,7 +39,7 @@ public class FileController : ControllerBase
|
||||
var filePath = Path.Combine(wwwroot, fileName);
|
||||
// 防止路径穿越攻击
|
||||
#pragma warning disable CA3003
|
||||
if (!filePath.StartsWith(wwwroot, StringComparison.OrdinalIgnoreCase) || !System.IO.File.Exists(filePath))
|
||||
if (filePath.Contains("..") || !System.IO.File.Exists(filePath))
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
@@ -49,6 +49,6 @@ public class FileController : ControllerBase
|
||||
|
||||
Response.Headers.Append("Access-Control-Expose-Headers", "Content-Disposition");
|
||||
|
||||
return File(fileStream, "application/octet-stream", (fileName.Replace('/', '_')));
|
||||
return File(fileStream, "application/octet-stream", (Path.GetFileName(filePath).Replace('/', '_')));
|
||||
}
|
||||
}
|
||||
|
@@ -21,16 +21,7 @@
|
||||
"UserNoModule": "This account has not been assigned a module. Please contact the administrator",
|
||||
"UserNull": "User {0} does not exist"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.BaseDataEntity": {
|
||||
"CreateOrgId": "CreateOrgId"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.BaseEntity": {
|
||||
"CreateTime": "CreateTime",
|
||||
"CreateUser": "CreateUser",
|
||||
"SortCode": "SortCode",
|
||||
"UpdateTime": "UpdateTime",
|
||||
"UpdateUser": "UpdateUser"
|
||||
},
|
||||
|
||||
"ThingsGateway.Admin.Application.BlazorAuthenticationHandler": {
|
||||
"UserExpire": "User expired, please login again"
|
||||
},
|
||||
|
@@ -21,16 +21,7 @@
|
||||
"UserNoModule": "该账号未分配模块,请联系管理员",
|
||||
"UserNull": "用户 {0} 不存在"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.BaseDataEntity": {
|
||||
"CreateOrgId": "创建机构Id"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.BaseEntity": {
|
||||
"CreateTime": "创建时间",
|
||||
"CreateUser": "创建人",
|
||||
"SortCode": "排序",
|
||||
"UpdateTime": "更新时间",
|
||||
"UpdateUser": "更新人"
|
||||
},
|
||||
|
||||
"ThingsGateway.Admin.Application.BlazorAuthenticationHandler": {
|
||||
"UserExpire": "用户登录已过期,请重新登录"
|
||||
},
|
||||
|
@@ -5,6 +5,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<GenerateDocumentationFile>True</GenerateDocumentationFile>
|
||||
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net8.0;net9.0;</TargetFrameworks>
|
||||
|
@@ -30,7 +30,7 @@ public class BlazorAppContext
|
||||
/// <summary>
|
||||
/// 全部菜单
|
||||
/// </summary>
|
||||
public IEnumerable<SysResource> AllMenus { get; private set; }
|
||||
public List<SysResource> AllMenus { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 当前用户
|
||||
@@ -42,22 +42,22 @@ public class BlazorAppContext
|
||||
/// <summary>
|
||||
/// 用户个人菜单
|
||||
/// </summary>
|
||||
public IEnumerable<MenuItem> OwnMenuItems { get; private set; }
|
||||
public List<MenuItem> OwnMenuItems { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 不同模块的菜单
|
||||
/// </summary>
|
||||
public IEnumerable<MenuItem> AllOwnMenuItems { get; private set; }
|
||||
public List<MenuItem> AllOwnMenuItems { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 用户个人菜单,多个模块
|
||||
/// </summary>
|
||||
public IEnumerable<SysResource> OwnMenus { get; private set; }
|
||||
public List<SysResource> OwnMenus { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 用户个人菜单,非树形
|
||||
/// </summary>
|
||||
public IEnumerable<MenuItem> OwnSameLevelMenuItems { get; private set; }
|
||||
public List<MenuItem> OwnSameLevelMenuItems { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 个人工作台
|
||||
@@ -67,9 +67,9 @@ public class BlazorAppContext
|
||||
/// <summary>
|
||||
/// 用户个人快捷方式菜单
|
||||
/// </summary>
|
||||
public IEnumerable<SysResource> UserWorkbenchOutputs { get; private set; }
|
||||
public List<SysResource> UserWorkbenchOutputs { get; private set; }
|
||||
|
||||
public IEnumerable<SysResource> AllResource { get; private set; }
|
||||
public List<SysResource> AllResource { get; private set; }
|
||||
|
||||
private ISysResourceService ResourceService { get; }
|
||||
private ISysUserService SysUserService { get; }
|
||||
@@ -93,7 +93,7 @@ public class BlazorAppContext
|
||||
AllResource = sysResources;
|
||||
var ids = CurrentUser.ModuleList.Select(a => a.Id).ToHashSet();
|
||||
CurrentUser.ModuleList = AllResource.Where(a => ids.Contains(a.Id)).OrderBy(a => a.SortCode).ToList();
|
||||
AllMenus = AllResource.Where(a => a.Category == ResourceCategoryEnum.Menu);
|
||||
AllMenus = AllResource.Where(a => a.Category == ResourceCategoryEnum.Menu).ToList();
|
||||
|
||||
if (moduleId == null)
|
||||
{
|
||||
@@ -132,8 +132,8 @@ public class BlazorAppContext
|
||||
Icon = item.Icon,
|
||||
Url = item.Href,
|
||||
Target = item.Target.ToString(),
|
||||
});
|
||||
UserWorkbenchOutputs = AllMenus.Where(it => UserWorkBench.Shortcuts.Contains(it.Id));
|
||||
}).ToList();
|
||||
UserWorkbenchOutputs = AllMenus.Where(it => UserWorkBench.Shortcuts.Contains(it.Id)).ToList();
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -18,6 +18,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net8.0;net9.0</TargetFrameworks>
|
||||
|
||||
<!--<UseRazorSourceGenerator>false</UseRazorSourceGenerator>-->
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
|
@@ -5,7 +5,8 @@
|
||||
#推送:docker push registry.cn-shenzhen.aliyuncs.com/thingsgateway/thingsgateway
|
||||
|
||||
#aspnetcore9.0环境
|
||||
FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS base
|
||||
#FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS base
|
||||
FROM mcr.microsoft.com/dotnet/aspnet:8.0-noble AS base
|
||||
COPY . /app
|
||||
WORKDIR /app
|
||||
#默认web
|
||||
@@ -13,6 +14,8 @@ EXPOSE 5000
|
||||
|
||||
# 添加时区环境变量,亚洲,上海
|
||||
ENV TimeZone=Asia/Shanghai
|
||||
# 转发头
|
||||
ENV ASPNETCORE_FORWARDEDHEADERS_ENABLED=true
|
||||
# 使用软连接,并且将时区配置覆盖/etc/timezone
|
||||
RUN ln -snf /usr/share/zoneinfo/$TimeZone /etc/localtime && echo $TimeZone > /etc/timezone
|
||||
|
||||
|
@@ -5,7 +5,8 @@
|
||||
#推送:docker push registry.cn-shenzhen.aliyuncs.com/thingsgateway/thingsgateway_arm64
|
||||
|
||||
#aspnetcore9.0环境
|
||||
FROM mcr.microsoft.com/dotnet/aspnet:9.0-alpine-arm64v8 AS base
|
||||
#FROM mcr.microsoft.com/dotnet/aspnet:9.0-alpine-arm64v8 AS base
|
||||
FROM mcr.microsoft.com/dotnet/aspnet:8.0-noble-arm64v8 AS base
|
||||
COPY . /app
|
||||
WORKDIR /app
|
||||
#默认web
|
||||
@@ -13,6 +14,8 @@ EXPOSE 5000
|
||||
|
||||
# 添加时区环境变量,亚洲,上海
|
||||
ENV TimeZone=Asia/Shanghai
|
||||
# 转发头
|
||||
ENV ASPNETCORE_FORWARDEDHEADERS_ENABLED=true
|
||||
# 使用软连接,并且将时区配置覆盖/etc/timezone
|
||||
RUN ln -snf /usr/share/zoneinfo/$TimeZone /etc/localtime && echo $TimeZone > /etc/timezone
|
||||
|
||||
|
@@ -5,6 +5,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net8.0;net9.0;</TargetFrameworks>
|
||||
|
||||
</PropertyGroup>
|
||||
|
||||
<!--<Import Project="Admin.targets" Condition=" '$(Configuration)' != 'Debug' " />-->
|
||||
|
@@ -5,6 +5,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<GenerateDocumentationFile>True</GenerateDocumentationFile>
|
||||
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net8.0;net9.0;</TargetFrameworks>
|
||||
|
13
src/Admin/ThingsGateway.DB/Locales/en-US.json
Normal file
13
src/Admin/ThingsGateway.DB/Locales/en-US.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
|
||||
"ThingsGateway.Admin.Application.BaseDataEntity": {
|
||||
"CreateOrgId": "CreateOrgId"
|
||||
},
|
||||
"ThingsGateway.Admin.Application.BaseEntity": {
|
||||
"CreateTime": "CreateTime",
|
||||
"CreateUser": "CreateUser",
|
||||
"SortCode": "SortCode",
|
||||
"UpdateTime": "UpdateTime",
|
||||
"UpdateUser": "UpdateUser"
|
||||
}
|
||||
}
|
13
src/Admin/ThingsGateway.DB/Locales/zh-CN.json
Normal file
13
src/Admin/ThingsGateway.DB/Locales/zh-CN.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
|
||||
"ThingsGateway.DB.BaseDataEntity": {
|
||||
"CreateOrgId": "创建机构Id"
|
||||
},
|
||||
"ThingsGateway.DB.BaseEntity": {
|
||||
"CreateTime": "创建时间",
|
||||
"CreateUser": "创建人",
|
||||
"SortCode": "排序",
|
||||
"UpdateTime": "更新时间",
|
||||
"UpdateUser": "更新人"
|
||||
}
|
||||
}
|
@@ -5,6 +5,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<GenerateDocumentationFile>True</GenerateDocumentationFile>
|
||||
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net8.0;net9.0;</TargetFrameworks>
|
||||
@@ -18,6 +19,12 @@
|
||||
<None Remove="..\..\..\README.zh-CN.md" Pack="false" PackagePath="\" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Locales\en-US.json" />
|
||||
<EmbeddedResource Include="Locales\zh-CN.json" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<!--<PackageReference Include="ThingsGateway.Razor" Version="$(SourceGeneratorVersion)" />-->
|
||||
<!--<ProjectReference Include="..\ThingsGateway.Razor\ThingsGateway.Razor.csproj" />-->
|
||||
|
@@ -554,10 +554,9 @@ public static class App
|
||||
{
|
||||
types = ass.GetTypes();
|
||||
}
|
||||
catch
|
||||
catch (Exception ex)
|
||||
{
|
||||
XTrace.Log.Warn($"Error load `{ass.FullName}` assembly.");
|
||||
Console.WriteLine($"Error load `{ass.FullName}` assembly.");
|
||||
XTrace.Log.Warn($"Error load `{ass.FullName}` assembly. : {ex.Message}");
|
||||
}
|
||||
|
||||
return types.Where(u => u.IsPublic && !u.IsDefined(typeof(SuppressSnifferAttribute), false));
|
||||
|
@@ -5,6 +5,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net8.0;net9.0;</TargetFrameworks>
|
||||
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
|
@@ -12,6 +12,7 @@
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<SignAssembly>True</SignAssembly>
|
||||
<AssemblyOriginatorKeyFile>newlife.snk</AssemblyOriginatorKeyFile>
|
||||
|
||||
|
||||
|
||||
</PropertyGroup>
|
||||
|
@@ -4,6 +4,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net8.0;net9.0;</TargetFrameworks>
|
||||
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@@ -28,7 +28,7 @@ public partial class ImportExcelConfirm
|
||||
|
||||
[Inject]
|
||||
[NotNull]
|
||||
private IStringLocalizer<ImportExcel>? Localizer { get; set; }
|
||||
private IStringLocalizer<ImportExcelConfirm>? Localizer { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 预览
|
||||
|
@@ -25,7 +25,8 @@
|
||||
"Success": "Success",
|
||||
"TablesExportButtonExcelText": "Export Excel",
|
||||
"TablesImportButtonExcelText": "Import Excel",
|
||||
"True": "Yes"
|
||||
"True": "Yes",
|
||||
"Info": "Info"
|
||||
},
|
||||
"ThingsGateway.Razor.About": {
|
||||
"Community": "Community",
|
||||
@@ -71,6 +72,19 @@
|
||||
"Validate": "Validate",
|
||||
"ValidateText": "Validation Content"
|
||||
},
|
||||
"ThingsGateway.Razor.ImportExcelConfirm": {
|
||||
"First": "Step 1",
|
||||
"Import": "Import",
|
||||
"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}, import {1} records",
|
||||
"Validate": "Validate",
|
||||
"ValidateText": "Validation Content"
|
||||
},
|
||||
"ThingsGateway.Razor.MenuIconList": {
|
||||
"ChoiceIcon": "Icon for Selection"
|
||||
}
|
||||
|
@@ -25,7 +25,8 @@
|
||||
"Success": "成功",
|
||||
"TablesExportButtonExcelText": "导出Excel",
|
||||
"TablesImportButtonExcelText": "导入Excel",
|
||||
"True": "是"
|
||||
"True": "是",
|
||||
"Info": "详情"
|
||||
},
|
||||
"ThingsGateway.Razor.About": {
|
||||
"Community": "社区",
|
||||
@@ -71,6 +72,19 @@
|
||||
"Validate": "验证",
|
||||
"ValidateText": "验证内容"
|
||||
},
|
||||
"ThingsGateway.Razor.ImportExcelConfirm": {
|
||||
"First": "第一步",
|
||||
"Import": "导入",
|
||||
"Next": "下一步",
|
||||
"Reset": "重置",
|
||||
"Second": "第二步",
|
||||
"Third": "第三",
|
||||
"Tip": "数据量较大时(大于20万),所需导入时间可能超过1分钟,请耐心等待",
|
||||
"Upload": "上传文件",
|
||||
"UploadCount": " 表 {0},导入 {1} 条数据",
|
||||
"Validate": "验证",
|
||||
"ValidateText": "验证内容"
|
||||
},
|
||||
"ThingsGateway.Razor.MenuIconList": {
|
||||
"ChoiceIcon": "选择图标"
|
||||
}
|
||||
|
@@ -3,6 +3,7 @@
|
||||
<Import Project="..\..\PackNuget.props" />
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net8.0</TargetFrameworks>
|
||||
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BootstrapBlazor.FontAwesome" Version="9.1.0" />
|
||||
|
@@ -132,35 +132,43 @@ namespace ThingsGateway.SqlSugar
|
||||
{
|
||||
// 准备多部分表单数据
|
||||
var boundary = "---------------" + DateTime.Now.Ticks.ToString("x");
|
||||
var list = new List<Hashtable>();
|
||||
|
||||
tableName ??= db.EntityMaintenance.GetEntityInfo<T>().DbTableName;
|
||||
|
||||
// 获取或创建列信息缓存
|
||||
var key = "QuestDbBulkCopy" + typeof(T).FullName + typeof(T).GetHashCode();
|
||||
var columns = ReflectionInoCacheService.Instance.GetOrCreate(key, () =>
|
||||
db.CopyNew().DbMaintenance.GetColumnInfosByTableName(tableName));
|
||||
|
||||
// 构建schema信息
|
||||
columns.ForEach(d =>
|
||||
var list = ReflectionInoCacheService.Instance.GetOrCreate($"{key}{dateFormat}List<Hashtable>", () =>
|
||||
{
|
||||
if (d.DataType == "TIMESTAMP")
|
||||
var list = new List<Hashtable>();
|
||||
|
||||
// 构建schema信息
|
||||
columns.ForEach(d =>
|
||||
{
|
||||
list.Add(new Hashtable()
|
||||
if (d.DataType == "TIMESTAMP")
|
||||
{
|
||||
list.Add(new Hashtable()
|
||||
{
|
||||
{ "name", d.DbColumnName },
|
||||
{ "type", d.DataType },
|
||||
{ "pattern", dateFormat}
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
list.Add(new Hashtable()
|
||||
}
|
||||
else
|
||||
{
|
||||
list.Add(new Hashtable()
|
||||
{
|
||||
{ "name", d.DbColumnName },
|
||||
{ "type", d.DataType }
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return list;
|
||||
}
|
||||
);
|
||||
|
||||
var schema = JsonConvert.SerializeObject(list);
|
||||
|
||||
// 写入CSV文件
|
||||
|
@@ -5,6 +5,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<GenerateDocumentationFile>True</GenerateDocumentationFile>
|
||||
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net8.0;net9.0;</TargetFrameworks>
|
||||
@@ -31,7 +32,7 @@
|
||||
<PackageReference Include="CsvHelper" Version="33.1.0" />
|
||||
<PackageReference Include="TDengine.Connector" Version="3.1.7" />
|
||||
<PackageReference Include="Oracle.ManagedDataAccess.Core" Version="23.9.1" />
|
||||
<PackageReference Include="Oscar.Data.SqlClient" Version="4.2.21" />
|
||||
<PackageReference Include="Oscar.Data.SqlClient" Version="4.2.22" />
|
||||
<PackageReference Include="System.Data.Common" Version="4.3.0" />
|
||||
<PackageReference Include="Microsoft.Data.SqlClient" Version="6.1.0" />
|
||||
<PackageReference Include="System.Reflection.Emit.Lightweight" Version="4.7.0" />
|
||||
|
@@ -1,9 +1,9 @@
|
||||
<Project>
|
||||
|
||||
<PropertyGroup>
|
||||
<PluginVersion>10.10.16</PluginVersion>
|
||||
<ProPluginVersion>10.10.16</ProPluginVersion>
|
||||
<DefaultVersion>10.10.16</DefaultVersion>
|
||||
<PluginVersion>10.10.18</PluginVersion>
|
||||
<ProPluginVersion>10.10.18</ProPluginVersion>
|
||||
<DefaultVersion>10.10.18</DefaultVersion>
|
||||
<AuthenticationVersion>10.10.1</AuthenticationVersion>
|
||||
<SourceGeneratorVersion>10.10.1</SourceGeneratorVersion>
|
||||
<NET8Version>8.0.19</NET8Version>
|
||||
|
@@ -4,10 +4,11 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>netstandard2.0;</TargetFrameworks>
|
||||
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CS-Script" Version="4.10.1" />
|
||||
<PackageReference Include="CS-Script" Version="4.11.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@@ -23,7 +23,7 @@
|
||||
<EditorItem @bind-Field=Model.EncodingName>
|
||||
<EditTemplate Context="value">
|
||||
<div class="col-12 col-sm-4">
|
||||
<Select @bind-Value=value.EncodingName Items="EncodingItems" />
|
||||
<Select @bind-Value=value.EncodingName Items="EncodingItems" IsClearable/>
|
||||
</div>
|
||||
</EditTemplate>
|
||||
</EditorItem>
|
||||
|
@@ -31,6 +31,6 @@ public partial class ConverterConfigComponent : ComponentBase
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
BoolItems = LocalizerUtil.GetBoolItems(Model.GetType(), nameof(Model.VariableStringLength), true);
|
||||
EncodingItems = new List<SelectedItem>() { new SelectedItem("", "none") }.Concat(Encoding.GetEncodings().Select(a => new SelectedItem(a.CodePage.ToString(), a.DisplayName))).ToList();
|
||||
EncodingItems = Encoding.GetEncodings().Select(a => new SelectedItem(a.CodePage.ToString(), a.DisplayName)).ToList();
|
||||
}
|
||||
}
|
||||
|
@@ -40,7 +40,7 @@ public class PlatformService : IPlatformService
|
||||
await using var jSObject = await JSRuntime.InvokeAsync<IJSObjectReference>("import", $"{WebsiteConst.DefaultResourceUrl}js/downloadFile.js");
|
||||
var path = Path.GetRelativePath("wwwroot", item);
|
||||
string fileName = DateTime.Now.ToFileDateTimeFormat();
|
||||
await jSObject.InvokeVoidAsync("blazor_downloadFile", url, fileName, new { FileName = path });
|
||||
await jSObject.InvokeAsync<bool>("blazor_downloadFile", url, fileName, new { FileName = path });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -4,6 +4,7 @@
|
||||
<Import Project="..\..\PackNuget.props" />
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net8.0;</TargetFrameworks>
|
||||
|
||||
<!--<UseRazorSourceGenerator>false</UseRazorSourceGenerator>-->
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
|
@@ -5,6 +5,7 @@
|
||||
<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
|
||||
<NoPackageAnalysis>true</NoPackageAnalysis>
|
||||
<SignAssembly>false</SignAssembly>
|
||||
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@@ -5,6 +5,7 @@
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>netstandard2.0;</TargetFrameworks>
|
||||
<Version>$(SourceGeneratorVersion)</Version>
|
||||
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@@ -6,6 +6,7 @@
|
||||
<PropertyGroup>
|
||||
<Description>工业设备通讯协议-基础类库</Description>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@@ -4,6 +4,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net8.0;net9.0</TargetFrameworks>
|
||||
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@@ -4,6 +4,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net8.0;net9.0</TargetFrameworks>
|
||||
|
||||
<!--<UseRazorSourceGenerator>false</UseRazorSourceGenerator>-->
|
||||
</PropertyGroup>
|
||||
|
||||
|
@@ -18,7 +18,7 @@ namespace BootstrapBlazor.Components;
|
||||
/// <param name="fieldName">字段名称</param>
|
||||
/// <param name="fieldType">字段类型</param>
|
||||
/// <param name="fieldText">显示文字</param>
|
||||
internal sealed class InternalTableColumn(string fieldName, Type fieldType, string? fieldText = null) : IEditorItem, ITableColumn
|
||||
public sealed class DefaultTableColumn(string fieldName, Type fieldType, string? fieldText = null) : IEditorItem, ITableColumn
|
||||
{
|
||||
public IEnumerable<KeyValuePair<string, object>>? ComponentParameters { get; set; }
|
||||
public Type? ComponentType { get; set; }
|
@@ -176,7 +176,7 @@ public class ControlController : ControllerBase, IRpcServer
|
||||
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
|
||||
public Task<bool> BatchSaveVariableAsync([FromBody][TouchSocket.WebApi.FromBody] List<Variable> variables, ItemChangedType type, bool restart = true)
|
||||
{
|
||||
return GlobalData.VariableRuntimeService.BatchSaveVariableAsync(variables, type, restart, default);
|
||||
return GlobalData.VariableRuntimeService.BatchSaveVariableAsync(variables, type, restart);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -188,7 +188,7 @@ public class ControlController : ControllerBase, IRpcServer
|
||||
public Task<bool> DeleteChannelAsync([FromBody][TouchSocket.WebApi.FromBody] List<long> ids, bool restart = true)
|
||||
{
|
||||
if (ids == null || ids.Count == 0) ids = GlobalData.IdChannels.Keys.ToList();
|
||||
return GlobalData.ChannelRuntimeService.DeleteChannelAsync(ids, restart, default);
|
||||
return GlobalData.ChannelRuntimeService.DeleteChannelAsync(ids, restart);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -200,7 +200,7 @@ public class ControlController : ControllerBase, IRpcServer
|
||||
public Task<bool> DeleteDeviceAsync([FromBody][TouchSocket.WebApi.FromBody] List<long> ids, bool restart = true)
|
||||
{
|
||||
if (ids == null || ids.Count == 0) ids = GlobalData.IdDevices.Keys.ToList();
|
||||
return GlobalData.DeviceRuntimeService.DeleteDeviceAsync(ids, restart, default);
|
||||
return GlobalData.DeviceRuntimeService.DeleteDeviceAsync(ids, restart);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -212,7 +212,7 @@ public class ControlController : ControllerBase, IRpcServer
|
||||
public Task<bool> DeleteVariableAsync([FromBody][TouchSocket.WebApi.FromBody] List<long> ids, bool restart = true)
|
||||
{
|
||||
if (ids == null || ids.Count == 0) ids = GlobalData.IdVariables.Keys.ToList();
|
||||
return GlobalData.VariableRuntimeService.DeleteVariableAsync(ids, restart, default);
|
||||
return GlobalData.VariableRuntimeService.DeleteVariableAsync(ids, restart);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -223,7 +223,7 @@ public class ControlController : ControllerBase, IRpcServer
|
||||
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
|
||||
public Task InsertTestDataAsync(int testVariableCount, int testDeviceCount, string slaveUrl, bool businessEnable, bool restart = true)
|
||||
{
|
||||
return GlobalData.VariableRuntimeService.InsertTestDataAsync(testVariableCount, testDeviceCount, slaveUrl, businessEnable, restart, default);
|
||||
return GlobalData.VariableRuntimeService.InsertTestDataAsync(testVariableCount, testDeviceCount, slaveUrl, businessEnable, restart);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@@ -57,6 +57,9 @@ public abstract class BusinessBase : DriverBase
|
||||
/// </summary>
|
||||
protected Dictionary<string, List<VariableRuntime>> VariableRuntimeGroups { get; set; } = new();
|
||||
|
||||
|
||||
#if !Management
|
||||
|
||||
public override Task AfterVariablesChangedAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
LogMessage?.LogInformation("Refresh variable");
|
||||
@@ -117,6 +120,8 @@ public abstract class BusinessBase : DriverBase
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 间隔执行
|
||||
/// </summary>
|
||||
@@ -134,4 +139,5 @@ public abstract class BusinessBase : DriverBase
|
||||
CurrentDevice?.SetDeviceStatus(TimerX.Now, true);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
@@ -20,6 +20,12 @@ namespace ThingsGateway.Gateway.Application;
|
||||
/// </summary>
|
||||
public abstract class BusinessBaseWithCache : BusinessBase
|
||||
{
|
||||
|
||||
protected sealed override BusinessPropertyBase _businessPropertyBase => _businessPropertyWithCache;
|
||||
|
||||
protected abstract BusinessPropertyWithCache _businessPropertyWithCache { get; }
|
||||
|
||||
#if !Management
|
||||
#region 条件
|
||||
|
||||
protected abstract bool AlarmModelEnable { get; }
|
||||
@@ -537,9 +543,7 @@ public abstract class BusinessBaseWithCache : BusinessBase
|
||||
private CacheDB DBCacheVar;
|
||||
private CacheDB DBCacheVars;
|
||||
|
||||
protected sealed override BusinessPropertyBase _businessPropertyBase => _businessPropertyWithCache;
|
||||
|
||||
protected abstract BusinessPropertyWithCache _businessPropertyWithCache { get; }
|
||||
|
||||
protected object cacheLock = new();
|
||||
|
||||
@@ -976,4 +980,6 @@ public abstract class BusinessBaseWithCache : BusinessBase
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#endif
|
||||
}
|
||||
|
@@ -19,6 +19,7 @@ namespace ThingsGateway.Gateway.Application;
|
||||
/// </summary>
|
||||
public abstract class BusinessBaseWithCacheAlarm : BusinessBaseWithCache
|
||||
{
|
||||
#if !Management
|
||||
protected override bool AlarmModelEnable => true;
|
||||
|
||||
protected override bool DevModelEnable => false;
|
||||
@@ -104,4 +105,6 @@ public abstract class BusinessBaseWithCacheAlarm : BusinessBaseWithCache
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
@@ -29,6 +29,9 @@ public abstract class BusinessBaseWithCacheInterval : BusinessBaseWithCache
|
||||
/// </summary>
|
||||
protected abstract BusinessPropertyWithCacheInterval _businessPropertyWithCacheInterval { get; }
|
||||
|
||||
#if !Management
|
||||
|
||||
|
||||
protected internal override async Task InitChannelAsync(IChannel? channel, CancellationToken cancellationToken)
|
||||
{
|
||||
if (AlarmModelEnable)
|
||||
@@ -340,5 +343,5 @@ public abstract class BusinessBaseWithCacheInterval : BusinessBaseWithCache
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
@@ -26,6 +26,8 @@ public abstract partial class BusinessBaseWithCacheIntervalScript : BusinessBase
|
||||
|
||||
protected abstract BusinessPropertyWithCacheIntervalScript _businessPropertyWithCacheIntervalScript { get; }
|
||||
|
||||
#if !Management
|
||||
|
||||
public virtual string[] Match(string input)
|
||||
{
|
||||
// 生成缓存键,以确保缓存的唯一性
|
||||
@@ -347,4 +349,7 @@ public abstract partial class BusinessBaseWithCacheIntervalScript : BusinessBase
|
||||
[GeneratedRegex(@"\$\{(.+?)\}")]
|
||||
private static partial Regex TopicRegex();
|
||||
#endregion 封装方法
|
||||
|
||||
|
||||
#endif
|
||||
}
|
||||
|
@@ -15,9 +15,12 @@ namespace ThingsGateway.Gateway.Application;
|
||||
/// </summary>
|
||||
public abstract partial class BusinessBaseWithCacheIntervalScriptAll : BusinessBaseWithCacheIntervalScript
|
||||
{
|
||||
#if !Management
|
||||
protected override bool AlarmModelEnable => true;
|
||||
|
||||
protected override bool DevModelEnable => true;
|
||||
|
||||
protected override bool VarModelEnable => true;
|
||||
|
||||
#endif
|
||||
}
|
||||
|
@@ -15,6 +15,7 @@ namespace ThingsGateway.Gateway.Application;
|
||||
/// </summary>
|
||||
public abstract class BusinessBaseWithCacheIntervalVariable : BusinessBaseWithCacheInterval
|
||||
{
|
||||
#if !Management
|
||||
protected override bool AlarmModelEnable => false;
|
||||
|
||||
protected override bool DevModelEnable => false;
|
||||
@@ -29,5 +30,5 @@ public abstract class BusinessBaseWithCacheIntervalVariable : BusinessBaseWithCa
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
@@ -18,6 +18,7 @@ namespace ThingsGateway.Gateway.Application;
|
||||
/// </summary>
|
||||
public static class BusinessDatabaseUtil
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// 获取数据库链接
|
||||
/// </summary>
|
||||
@@ -45,6 +46,7 @@ public static class BusinessDatabaseUtil
|
||||
return sqlSugarClient;
|
||||
}
|
||||
|
||||
#if !Management
|
||||
/// <summary>
|
||||
/// 按条件获取DB插件中的全部历史报警(不分页)
|
||||
/// </summary>
|
||||
@@ -144,4 +146,6 @@ public static class BusinessDatabaseUtil
|
||||
return new("GetDBHistoryValues Fail", ex);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
@@ -16,7 +16,9 @@ using System.Collections.Concurrent;
|
||||
|
||||
using ThingsGateway.Common.Extension;
|
||||
using ThingsGateway.Extension.Generic;
|
||||
#if !Management
|
||||
using ThingsGateway.Gateway.Application.Extensions;
|
||||
#endif
|
||||
using ThingsGateway.NewLife.Json.Extension;
|
||||
using ThingsGateway.NewLife.Threading;
|
||||
|
||||
@@ -29,20 +31,31 @@ namespace ThingsGateway.Gateway.Application;
|
||||
/// 采集插件,继承实现不同PLC通讯
|
||||
/// <para></para>
|
||||
/// </summary>
|
||||
public abstract partial class CollectBase : DriverBase, IRpcDriver
|
||||
public abstract partial class CollectBase : DriverBase
|
||||
#if !Management
|
||||
, IRpcDriver
|
||||
#endif
|
||||
{
|
||||
/// <summary>
|
||||
/// 插件配置项
|
||||
/// </summary>
|
||||
public abstract CollectPropertyBase CollectProperties { get; }
|
||||
|
||||
|
||||
public sealed override object DriverProperties => CollectProperties;
|
||||
|
||||
public virtual string GetAddressDescription()
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
#if !Management
|
||||
|
||||
/// <summary>
|
||||
/// 特殊方法
|
||||
/// </summary>
|
||||
public List<DriverMethodInfo>? DriverMethodInfos { get; private set; }
|
||||
|
||||
public sealed override object DriverProperties => CollectProperties;
|
||||
|
||||
public override async Task AfterVariablesChangedAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
LogMessage?.LogInformation("Refresh variable");
|
||||
@@ -198,10 +211,7 @@ public abstract partial class CollectBase : DriverBase, IRpcDriver
|
||||
ReadWriteLock = new(CollectProperties.DutyCycle, CollectProperties.WritePriority);
|
||||
}
|
||||
|
||||
public virtual string GetAddressDescription()
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
protected virtual bool VariableSourceReadsEnable => true;
|
||||
|
||||
protected List<IScheduledTask> VariableTasks = new List<IScheduledTask>();
|
||||
@@ -468,6 +478,7 @@ public abstract partial class CollectBase : DriverBase, IRpcDriver
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 连读打包,返回实际通讯包信息<see cref="VariableSourceRead"/>
|
||||
/// <br></br>每个驱动打包方法不一样,所以需要实现这个接口
|
||||
@@ -730,4 +741,6 @@ public abstract partial class CollectBase : DriverBase, IRpcDriver
|
||||
await ReadWriteLock.SafeDisposeAsync().ConfigureAwait(false);
|
||||
await base.DisposeAsync(disposing).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
@@ -33,13 +33,7 @@ public abstract class CollectFoundationBase : CollectBase
|
||||
/// </summary>
|
||||
public virtual IDevice? FoundationDevice { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否连接成功
|
||||
/// </summary>
|
||||
public override bool IsConnected()
|
||||
{
|
||||
return FoundationDevice?.OnLine == true;
|
||||
}
|
||||
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
@@ -53,6 +47,24 @@ public abstract class CollectFoundationBase : CollectBase
|
||||
await FoundationDevice.SafeDisposeAsync().ConfigureAwait(false);
|
||||
await base.DisposeAsync(disposing).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public override string GetAddressDescription()
|
||||
{
|
||||
return FoundationDevice?.GetAddressDescription();
|
||||
}
|
||||
#if !Management
|
||||
|
||||
/// <summary>
|
||||
/// 是否连接成功
|
||||
/// </summary>
|
||||
public override bool IsConnected()
|
||||
{
|
||||
return FoundationDevice?.OnLine == true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 开始通讯执行的方法
|
||||
/// </summary>
|
||||
@@ -66,10 +78,6 @@ public abstract class CollectFoundationBase : CollectBase
|
||||
}
|
||||
}
|
||||
|
||||
public override string GetAddressDescription()
|
||||
{
|
||||
return FoundationDevice?.GetAddressDescription();
|
||||
}
|
||||
|
||||
protected override async Task TestOnline(object? state, CancellationToken cancellationToken)
|
||||
{
|
||||
@@ -216,4 +224,7 @@ public abstract class CollectFoundationBase : CollectBase
|
||||
// 返回包含操作结果的字典
|
||||
return new Dictionary<string, OperResult>(operResults);
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
}
|
||||
|
@@ -37,20 +37,6 @@ public abstract class DriverBase : AsyncDisposableObject, IDriver
|
||||
|
||||
#region 属性
|
||||
|
||||
/// <summary>
|
||||
/// 当前设备
|
||||
/// </summary>
|
||||
public DeviceRuntime? CurrentDevice { get; private set; }
|
||||
/// <summary>
|
||||
/// 当前设备Id
|
||||
/// </summary>
|
||||
public long DeviceId => CurrentDevice?.Id ?? 0;
|
||||
|
||||
/// <summary>
|
||||
/// 当前设备名称
|
||||
/// </summary>
|
||||
public string? DeviceName => CurrentDevice?.Name;
|
||||
|
||||
/// <summary>
|
||||
/// 调试UI Type,如果不存在,返回null
|
||||
/// </summary>
|
||||
@@ -76,25 +62,8 @@ public abstract class DriverBase : AsyncDisposableObject, IDriver
|
||||
/// </summary>
|
||||
public abstract object DriverProperties { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否执行了Start方法
|
||||
/// </summary>
|
||||
public bool IsStarted { get; protected set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// 是否初始化成功,失败时不再执行,等待检测重启
|
||||
/// </summary>
|
||||
public bool IsInitSuccess { get; internal set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// 是否采集插件
|
||||
/// </summary>
|
||||
public virtual bool? IsCollectDevice => CurrentDevice?.IsCollect;
|
||||
|
||||
/// <summary>
|
||||
/// 暂停
|
||||
/// </summary>
|
||||
public bool Pause => CurrentDevice?.Pause == true;
|
||||
|
||||
private List<IEditorItem> pluginPropertyEditorItems;
|
||||
public List<IEditorItem> PluginPropertyEditorItems
|
||||
@@ -112,6 +81,82 @@ public abstract class DriverBase : AsyncDisposableObject, IDriver
|
||||
private IStringLocalizer Localizer { get; }
|
||||
|
||||
#endregion 属性
|
||||
|
||||
|
||||
public virtual bool GetAuthentication(out DateTime? expireTime)
|
||||
{
|
||||
expireTime = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
public string GetAuthString()
|
||||
{
|
||||
if (PluginServiceUtil.IsEducation(GetType()))
|
||||
{
|
||||
StringBuilder stringBuilder = new();
|
||||
var ret = GetAuthentication(out var expireTime);
|
||||
if (ret)
|
||||
{
|
||||
stringBuilder.Append(Localizer["Authorized"]);
|
||||
}
|
||||
else
|
||||
{
|
||||
stringBuilder.Append(Localizer["Unauthorized"]);
|
||||
}
|
||||
|
||||
stringBuilder.Append(" ");
|
||||
if (expireTime.HasValue && (DateTime.Now - expireTime.Value).TotalHours > -72)
|
||||
{
|
||||
stringBuilder.Append(',');
|
||||
stringBuilder.Append(Localizer["ExpireTime", expireTime.Value.ToString("yyyy-MM-dd HH")]);
|
||||
}
|
||||
|
||||
return stringBuilder.ToString();
|
||||
}
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#if !Management
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 是否执行了Start方法
|
||||
/// </summary>
|
||||
public bool IsStarted { get; protected set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// 是否初始化成功,失败时不再执行,等待检测重启
|
||||
/// </summary>
|
||||
public bool IsInitSuccess { get; internal set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// 是否采集插件
|
||||
/// </summary>
|
||||
public virtual bool? IsCollectDevice => CurrentDevice?.IsCollect;
|
||||
|
||||
/// <summary>
|
||||
/// 当前设备
|
||||
/// </summary>
|
||||
public DeviceRuntime? CurrentDevice { get; private set; }
|
||||
/// <summary>
|
||||
/// 当前设备Id
|
||||
/// </summary>
|
||||
public long DeviceId => CurrentDevice?.Id ?? 0;
|
||||
|
||||
/// <summary>
|
||||
/// 当前设备名称
|
||||
/// </summary>
|
||||
public string? DeviceName => CurrentDevice?.Name;
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 暂停
|
||||
/// </summary>
|
||||
public bool Pause => CurrentDevice?.Pause == true;
|
||||
|
||||
protected object pauseLock = new object();
|
||||
/// <summary>
|
||||
/// 暂停
|
||||
@@ -378,38 +423,6 @@ public abstract class DriverBase : AsyncDisposableObject, IDriver
|
||||
|
||||
#region 插件重写
|
||||
|
||||
public virtual bool GetAuthentication(out DateTime? expireTime)
|
||||
{
|
||||
expireTime = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
public string GetAuthString()
|
||||
{
|
||||
if (PluginServiceUtil.IsEducation(GetType()))
|
||||
{
|
||||
StringBuilder stringBuilder = new();
|
||||
var ret = GetAuthentication(out var expireTime);
|
||||
if (ret)
|
||||
{
|
||||
stringBuilder.Append(Localizer["Authorized"]);
|
||||
}
|
||||
else
|
||||
{
|
||||
stringBuilder.Append(Localizer["Unauthorized"]);
|
||||
}
|
||||
|
||||
stringBuilder.Append(" ");
|
||||
if (expireTime.HasValue && (DateTime.Now - expireTime.Value).TotalHours > -72)
|
||||
{
|
||||
stringBuilder.Append(',');
|
||||
stringBuilder.Append(Localizer["ExpireTime", expireTime.Value.ToString("yyyy-MM-dd HH")]);
|
||||
}
|
||||
|
||||
return stringBuilder.ToString();
|
||||
}
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 开始通讯执行的方法
|
||||
@@ -456,4 +469,7 @@ public abstract class DriverBase : AsyncDisposableObject, IDriver
|
||||
public abstract Task AfterVariablesChangedAsync(CancellationToken cancellationToken);
|
||||
|
||||
#endregion 插件重写
|
||||
|
||||
|
||||
#endif
|
||||
}
|
||||
|
@@ -37,6 +37,7 @@ public static class DynamicModelExtension
|
||||
}
|
||||
}
|
||||
|
||||
#if !Management
|
||||
/// <summary>
|
||||
/// 获取变量的业务属性值
|
||||
/// </summary>
|
||||
@@ -79,6 +80,8 @@ public static class DynamicModelExtension
|
||||
return null; // 未找到对应的业务设备Id,返回null
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
public static IEnumerable<IGrouping<object[], T>> GroupByKeys<T>(this IEnumerable<T> values, params string[] keys)
|
||||
{
|
||||
// 获取动态对象集合中指定键的属性信息
|
||||
|
@@ -16,25 +16,31 @@ namespace ThingsGateway.Gateway.Application
|
||||
{
|
||||
public interface IDriver : IAsyncDisposable
|
||||
{
|
||||
bool DisposedValue { get; }
|
||||
ChannelRuntime CurrentChannel { get; }
|
||||
DeviceRuntime? CurrentDevice { get; }
|
||||
long DeviceId { get; }
|
||||
string? DeviceName { get; }
|
||||
|
||||
Type DriverDebugUIType { get; }
|
||||
object DriverProperties { get; }
|
||||
|
||||
Type DriverPropertyUIType { get; }
|
||||
Type DriverUIType { get; }
|
||||
Type DriverVariableAddressUIType { get; }
|
||||
List<IEditorItem> PluginPropertyEditorItems { get; }
|
||||
|
||||
#if !Management
|
||||
|
||||
bool? IsCollectDevice { get; }
|
||||
bool DisposedValue { get; }
|
||||
ChannelRuntime CurrentChannel { get; }
|
||||
DeviceRuntime? CurrentDevice { get; }
|
||||
long DeviceId { get; }
|
||||
string? DeviceName { get; }
|
||||
|
||||
|
||||
bool IsInitSuccess { get; }
|
||||
bool IsStarted { get; }
|
||||
bool Pause { get; }
|
||||
LoggerGroup LogMessage { get; }
|
||||
string LogPath { get; }
|
||||
bool Pause { get; }
|
||||
string PluginDirectory { get; }
|
||||
List<IEditorItem> PluginPropertyEditorItems { get; }
|
||||
Dictionary<long, VariableRuntime> IdVariableRuntimes { get; }
|
||||
IDeviceThreadManage DeviceThreadManage { get; }
|
||||
|
||||
@@ -42,6 +48,10 @@ namespace ThingsGateway.Gateway.Application
|
||||
void PauseThread(bool pause);
|
||||
Task SetLogAsync(LogLevel? logLevel = null, bool upDataBase = true);
|
||||
Task AfterVariablesChangedAsync(CancellationToken cancellationToken);
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
string GetAuthString();
|
||||
bool GetAuthentication(out DateTime? expireTime);
|
||||
}
|
||||
|
@@ -179,6 +179,9 @@ public class Device : BaseDataEntity, IValidatableObject
|
||||
[Newtonsoft.Json.JsonIgnore]
|
||||
internal bool IsUp;
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// 额外属性
|
||||
/// </summary>
|
||||
@@ -186,7 +189,6 @@ public class Device : BaseDataEntity, IValidatableObject
|
||||
[Newtonsoft.Json.JsonIgnore]
|
||||
[MapperIgnore]
|
||||
public ModelValueValidateForm? ModelValueValidateForm;
|
||||
#endif
|
||||
|
||||
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
|
||||
{
|
||||
|
@@ -80,6 +80,8 @@ public class Variable : BaseDataEntity, IValidatableObject
|
||||
private string remark4;
|
||||
private string remark5;
|
||||
|
||||
[System.Text.Json.Serialization.JsonIgnore]
|
||||
[Newtonsoft.Json.JsonIgnore]
|
||||
[MapperIgnore]
|
||||
public ValidateForm AlarmPropertysValidateForm;
|
||||
|
||||
|
@@ -341,6 +341,7 @@
|
||||
"WriteVariablesAsync": "Write variables"
|
||||
},
|
||||
"ThingsGateway.Gateway.Application.Device": {
|
||||
"Pause": "Pause",
|
||||
"ChannelError": "Channel error",
|
||||
"ChannelId": "Channel",
|
||||
"ChannelId.MinValue": "{0} cannot be empty",
|
||||
|
@@ -343,6 +343,7 @@
|
||||
"WriteVariablesAsync": "写入变量"
|
||||
},
|
||||
"ThingsGateway.Gateway.Application.Device": {
|
||||
"Pause": "暂停",
|
||||
"ChannelError": "通道错误",
|
||||
"ChannelId": "通道",
|
||||
"ChannelId.MinValue": " {0} 不可为空",
|
||||
|
@@ -93,6 +93,7 @@ public class ChannelRuntime : Channel
|
||||
[Newtonsoft.Json.JsonIgnore]
|
||||
public int? DeviceRuntimeCount => DeviceRuntimes?.Count;
|
||||
|
||||
[AutoGenerateColumn(Ignore = true)]
|
||||
public bool Started => DeviceThreadManage != null;
|
||||
#else
|
||||
|
||||
@@ -107,6 +108,7 @@ public class ChannelRuntime : Channel
|
||||
/// </summary>
|
||||
public int? DeviceRuntimeCount { get; set; }
|
||||
|
||||
[AutoGenerateColumn(Ignore = true)]
|
||||
public bool Started { get; set; }
|
||||
#endif
|
||||
|
||||
|
@@ -39,6 +39,20 @@ public class DeviceRuntime : Device
|
||||
/// </summary>
|
||||
public DateTime ActiveTime { get; set; } = DateTime.UnixEpoch.ToLocalTime();
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 是否采集
|
||||
/// </summary>
|
||||
[AutoGenerateColumn(Ignore = true)]
|
||||
public bool? IsCollect => PluginType == null ? null : PluginType == PluginTypeEnum.Collect;
|
||||
|
||||
|
||||
[AutoGenerateColumn(Ignore = true)]
|
||||
public string LogPath => Name.GetDeviceLogPath();
|
||||
|
||||
#if !Management
|
||||
|
||||
/// <summary>
|
||||
/// 插件名称
|
||||
/// </summary>
|
||||
@@ -50,12 +64,11 @@ public class DeviceRuntime : Device
|
||||
[AutoGenerateColumn(Ignore = true)]
|
||||
public virtual PluginTypeEnum? PluginType => ChannelRuntime?.PluginInfo?.PluginType;
|
||||
|
||||
/// <summary>
|
||||
/// 是否采集
|
||||
/// </summary>
|
||||
[AutoGenerateColumn(Ignore = true)]
|
||||
public bool? IsCollect => PluginType == null ? null : PluginType == PluginTypeEnum.Collect;
|
||||
|
||||
/// <summary>
|
||||
/// 通道名称
|
||||
/// </summary>
|
||||
public string? ChannelName => ChannelRuntime?.Name;
|
||||
/// <summary>
|
||||
/// 通道
|
||||
/// </summary>
|
||||
@@ -65,15 +78,9 @@ public class DeviceRuntime : Device
|
||||
[AutoGenerateColumn(Ignore = true)]
|
||||
public ChannelRuntime? ChannelRuntime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 通道名称
|
||||
/// </summary>
|
||||
public string? ChannelName => ChannelRuntime?.Name;
|
||||
|
||||
[AutoGenerateColumn(Ignore = true)]
|
||||
public string LogPath => Name.GetDeviceLogPath();
|
||||
|
||||
|
||||
#if !Management
|
||||
public bool Started => Driver?.DeviceThreadManage != null;
|
||||
|
||||
[System.Text.Json.Serialization.JsonIgnore]
|
||||
[Newtonsoft.Json.JsonIgnore]
|
||||
@@ -122,6 +129,27 @@ public class DeviceRuntime : Device
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 插件名称
|
||||
/// </summary>
|
||||
public virtual string PluginName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 插件名称
|
||||
/// </summary>
|
||||
[AutoGenerateColumn(Ignore = true)]
|
||||
public virtual PluginTypeEnum? PluginType { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 通道名称
|
||||
/// </summary>
|
||||
public string? ChannelName { get; set; }
|
||||
|
||||
[AutoGenerateColumn(Ignore = true)]
|
||||
public bool Started { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 设备状态
|
||||
/// </summary>
|
||||
@@ -175,8 +203,13 @@ public class DeviceRuntime : Device
|
||||
}
|
||||
set
|
||||
{
|
||||
#if !Management
|
||||
if (!value.IsNullOrWhiteSpace())
|
||||
_lastErrorMessage = TimerX.Now.ToDefaultDateTimeFormat() + " - " + value;
|
||||
#else
|
||||
if (!value.IsNullOrWhiteSpace())
|
||||
_lastErrorMessage = value;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -32,7 +32,7 @@ public partial class VariableRuntime : Variable
|
||||
IDisposable
|
||||
#endif
|
||||
{
|
||||
[AutoGenerateColumn(Visible = false)]
|
||||
[AutoGenerateColumn(Ignore = true)]
|
||||
public bool ValueInited { get => _valueInited; set => _valueInited = value; }
|
||||
|
||||
#region 属性
|
||||
@@ -112,6 +112,11 @@ public partial class VariableRuntime : Variable
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#if !Management
|
||||
private bool _isOnline;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 是否在线
|
||||
/// </summary>
|
||||
@@ -136,7 +141,6 @@ public partial class VariableRuntime : Variable
|
||||
}
|
||||
}
|
||||
|
||||
#if !Management
|
||||
/// <summary>
|
||||
/// 设备名称
|
||||
/// </summary>
|
||||
@@ -167,6 +171,14 @@ public partial class VariableRuntime : Variable
|
||||
|
||||
#else
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 是否在线
|
||||
/// </summary>
|
||||
[AutoGenerateColumn(Visible = true, Filterable = true, Sortable = true, Order = 5)]
|
||||
public bool IsOnline{get;set;}
|
||||
|
||||
/// <summary>
|
||||
/// 设备名称
|
||||
/// </summary>
|
||||
@@ -221,7 +233,6 @@ public partial class VariableRuntime : Variable
|
||||
|
||||
private DateTime collectTime = DateTime.UnixEpoch.ToLocalTime();
|
||||
|
||||
private bool _isOnline;
|
||||
#pragma warning disable CS0414
|
||||
private bool _isOnlineChanged;
|
||||
#pragma warning restore CS0414
|
||||
|
@@ -13,7 +13,9 @@ using BootstrapBlazor.Components;
|
||||
using Microsoft.AspNetCore.Components.Forms;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
using ThingsGateway.Extension.Generic;
|
||||
using ThingsGateway.NewLife;
|
||||
using ThingsGateway.NewLife.Extension;
|
||||
|
||||
namespace ThingsGateway.Gateway.Application;
|
||||
|
||||
@@ -26,12 +28,47 @@ public class ChannelRuntimeService : IChannelRuntimeService
|
||||
}
|
||||
private WaitLock WaitLock { get; set; } = new WaitLock(nameof(ChannelRuntimeService));
|
||||
|
||||
public Task<string> GetChannelNameAsync(long channelId)
|
||||
{
|
||||
return Task.FromResult(GlobalData.ReadOnlyIdChannels.TryGetValue(channelId, out var channelRuntime) ? channelRuntime.Name : string.Empty);
|
||||
}
|
||||
|
||||
public Task<QueryData<ChannelRuntime>> OnChannelQueryAsync(QueryPageOptions options)
|
||||
{
|
||||
var data = GlobalData.IdChannels.Select(a => a.Value)
|
||||
.WhereIf(!options.SearchText.IsNullOrWhiteSpace(), a => a.Name.Contains(options.SearchText))
|
||||
.GetQueryData(options);
|
||||
return Task.FromResult(data);
|
||||
}
|
||||
|
||||
public Task<List<Channel>> GetChannelListAsync(QueryPageOptions options, int max = 0)
|
||||
{
|
||||
var models = GlobalData.IdChannels.Select(a => a.Value)
|
||||
.WhereIf(!options.SearchText.IsNullOrWhiteSpace(), a => a.Name.Contains(options.SearchText))
|
||||
.GetData(options, out var total).Cast<Channel>().ToList();
|
||||
|
||||
if (max > 0 && models.Count > max)
|
||||
{
|
||||
throw new("online Excel max data count 2000");
|
||||
}
|
||||
return Task.FromResult(models);
|
||||
}
|
||||
|
||||
public Task<USheetDatas> ExportChannelAsync(List<Channel> channels)
|
||||
{
|
||||
return Task.FromResult(ChannelServiceHelpers.ExportChannel(channels));
|
||||
}
|
||||
public Task<string> GetPluginNameAsync(long channelId)
|
||||
{
|
||||
var pluginName = GlobalData.ReadOnlyIdChannels.TryGetValue(channelId, out var channel) ? channel.PluginName : string.Empty;
|
||||
return Task.FromResult(pluginName);
|
||||
}
|
||||
|
||||
|
||||
public async Task<QueryData<SelectedItem>> OnChannelSelectedItemQueryAsync(VirtualizeQueryOption option)
|
||||
{
|
||||
var channels = await GlobalData.GetCurrentUserChannels().ConfigureAwait(false);
|
||||
var _channelItems = channels.GetQueryData(option, GatewayResourceUtil.BuildChannelSelectList);
|
||||
var _channelItems = channels.WhereIf(!option.SearchText.IsNullOrWhiteSpace(), a => a.Name.Contains(option.SearchText)).GetQueryData(option, GatewayResourceUtil.BuildChannelSelectList);
|
||||
return _channelItems;
|
||||
}
|
||||
|
||||
@@ -106,17 +143,17 @@ public class ChannelRuntimeService : IChannelRuntimeService
|
||||
channels.Add(channel);
|
||||
}
|
||||
|
||||
await GlobalData.ChannelRuntimeService.CopyAsync(channels, devices, AutoRestartThread, default).ConfigureAwait(false);
|
||||
await GlobalData.ChannelRuntimeService.CopyAsync(channels, devices, AutoRestartThread).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public async Task<bool> CopyAsync(List<Channel> models, Dictionary<Device, List<Variable>> devices, bool restart, CancellationToken cancellationToken)
|
||||
public async Task<bool> CopyAsync(List<Channel> models, Dictionary<Device, List<Variable>> devices, bool restart)
|
||||
{
|
||||
try
|
||||
{
|
||||
await WaitLock.WaitAsync(cancellationToken).ConfigureAwait(false);
|
||||
await WaitLock.WaitAsync().ConfigureAwait(false);
|
||||
|
||||
var result = await GlobalData.ChannelService.CopyAsync(models, devices).ConfigureAwait(false);
|
||||
var ids = models.Select(a => a.Id).ToHashSet();
|
||||
@@ -133,7 +170,7 @@ public class ChannelRuntimeService : IChannelRuntimeService
|
||||
{
|
||||
await GlobalData.ChannelThreadManage.RestartChannelAsync(newChannelRuntimes).ConfigureAwait(false);
|
||||
|
||||
await RuntimeServiceHelper.ChangedDriverAsync(_logger, cancellationToken).ConfigureAwait(false);
|
||||
await RuntimeServiceHelper.ChangedDriverAsync(_logger).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -144,11 +181,11 @@ public class ChannelRuntimeService : IChannelRuntimeService
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<bool> InsertAsync(List<Channel> models, List<Device> devices, List<Variable> variables, bool restart, CancellationToken cancellationToken)
|
||||
public async Task<bool> InsertAsync(List<Channel> models, List<Device> devices, List<Variable> variables, bool restart)
|
||||
{
|
||||
try
|
||||
{
|
||||
await WaitLock.WaitAsync(cancellationToken).ConfigureAwait(false);
|
||||
await WaitLock.WaitAsync().ConfigureAwait(false);
|
||||
|
||||
var result = await GlobalData.ChannelService.InsertAsync(models, devices, variables).ConfigureAwait(false);
|
||||
var ids = models.Select(a => a.Id).ToHashSet();
|
||||
@@ -165,7 +202,7 @@ public class ChannelRuntimeService : IChannelRuntimeService
|
||||
{
|
||||
await GlobalData.ChannelThreadManage.RestartChannelAsync(newChannelRuntimes).ConfigureAwait(false);
|
||||
|
||||
await RuntimeServiceHelper.ChangedDriverAsync(_logger, cancellationToken).ConfigureAwait(false);
|
||||
await RuntimeServiceHelper.ChangedDriverAsync(_logger).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -176,11 +213,11 @@ public class ChannelRuntimeService : IChannelRuntimeService
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<bool> UpdateAsync(List<Channel> models, List<Device> devices, List<Variable> variables, bool restart, CancellationToken cancellationToken)
|
||||
public async Task<bool> UpdateAsync(List<Channel> models, List<Device> devices, List<Variable> variables, bool restart)
|
||||
{
|
||||
try
|
||||
{
|
||||
await WaitLock.WaitAsync(cancellationToken).ConfigureAwait(false);
|
||||
await WaitLock.WaitAsync().ConfigureAwait(false);
|
||||
|
||||
var result = await GlobalData.ChannelService.UpdateAsync(models, devices, variables).ConfigureAwait(false);
|
||||
var ids = models.Select(a => a.Id).ToHashSet();
|
||||
@@ -197,7 +234,7 @@ public class ChannelRuntimeService : IChannelRuntimeService
|
||||
{
|
||||
await GlobalData.ChannelThreadManage.RestartChannelAsync(newChannelRuntimes).ConfigureAwait(false);
|
||||
|
||||
await RuntimeServiceHelper.ChangedDriverAsync(_logger, cancellationToken).ConfigureAwait(false);
|
||||
await RuntimeServiceHelper.ChangedDriverAsync(_logger).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -208,7 +245,7 @@ public class ChannelRuntimeService : IChannelRuntimeService
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<bool> BatchEditChannelAsync(IEnumerable<Channel> models, Channel oldModel, Channel model, bool restart)
|
||||
public async Task<bool> BatchEditChannelAsync(List<Channel> models, Channel oldModel, Channel model, bool restart)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -234,11 +271,11 @@ public class ChannelRuntimeService : IChannelRuntimeService
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<bool> DeleteChannelAsync(IEnumerable<long> ids, bool restart, CancellationToken cancellationToken)
|
||||
public async Task<bool> DeleteChannelAsync(List<long> ids, bool restart)
|
||||
{
|
||||
try
|
||||
{
|
||||
await WaitLock.WaitAsync(cancellationToken).ConfigureAwait(false);
|
||||
await WaitLock.WaitAsync().ConfigureAwait(false);
|
||||
|
||||
var array = ids.ToArray();
|
||||
var result = await GlobalData.ChannelService.DeleteChannelAsync(array).ConfigureAwait(false);
|
||||
@@ -250,7 +287,7 @@ public class ChannelRuntimeService : IChannelRuntimeService
|
||||
{
|
||||
await GlobalData.ChannelThreadManage.RemoveChannelAsync(array).ConfigureAwait(false);
|
||||
|
||||
await RuntimeServiceHelper.ChangedDriverAsync(changedDriver, _logger, cancellationToken).ConfigureAwait(false);
|
||||
await RuntimeServiceHelper.ChangedDriverAsync(changedDriver, _logger).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -260,7 +297,10 @@ public class ChannelRuntimeService : IChannelRuntimeService
|
||||
WaitLock.Release();
|
||||
}
|
||||
}
|
||||
|
||||
public Task<bool> ClearChannelAsync(bool restart)
|
||||
{
|
||||
return DeleteChannelAsync(GlobalData.IdChannels.Keys.ToList(), restart);
|
||||
}
|
||||
public Task<Dictionary<string, ImportPreviewOutputBase>> PreviewAsync(IBrowserFile browserFile) => GlobalData.ChannelService.PreviewAsync(browserFile);
|
||||
|
||||
public Task<Dictionary<string, object>> ExportChannelAsync(GatewayExportFilter exportFilter) => GlobalData.ChannelService.ExportChannelAsync(exportFilter);
|
||||
@@ -268,7 +308,7 @@ public class ChannelRuntimeService : IChannelRuntimeService
|
||||
public Task<MemoryStream> ExportMemoryStream(IEnumerable<Channel> data) =>
|
||||
GlobalData.ChannelService.ExportMemoryStream(data);
|
||||
|
||||
public async Task<Dictionary<string, ImportPreviewOutputBase>> ImportChannelAsync(USheetDatas input, bool restart)
|
||||
public async Task<Dictionary<string, ImportPreviewOutputBase>> ImportChannelUSheetDatasAsync(USheetDatas input, bool restart)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -279,9 +319,7 @@ public class ChannelRuntimeService : IChannelRuntimeService
|
||||
|
||||
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 result = await GlobalData.ChannelService.ImportChannelAsync(data).ConfigureAwait(false);
|
||||
|
||||
var newChannelRuntimes = await RuntimeServiceHelper.GetNewChannelRuntimesAsync(result).ConfigureAwait(false);
|
||||
|
||||
@@ -300,7 +338,7 @@ public class ChannelRuntimeService : IChannelRuntimeService
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<Dictionary<string, ImportPreviewOutputBase>> ImportChannelAsync(string filePath, bool restart)
|
||||
public async Task<Dictionary<string, ImportPreviewOutputBase>> ImportChannelFileAsync(string filePath, bool restart)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -480,4 +518,11 @@ public class ChannelRuntimeService : IChannelRuntimeService
|
||||
WaitLock.Release();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<string> ExportChannelFileAsync(GatewayExportFilter exportFilter)
|
||||
{
|
||||
var sheets = await GlobalData.ChannelService.ExportChannelAsync(exportFilter).ConfigureAwait(false);
|
||||
return await App.GetService<IImportExportService>().CreateFileAsync<Channel>(sheets, "Channel", false).ConfigureAwait(false);
|
||||
|
||||
}
|
||||
}
|
@@ -370,7 +370,18 @@ internal sealed class ChannelService : BaseService<Channel>, IChannelService
|
||||
[OperDesc("ImportChannel", isRecordPar: false, localizerType: typeof(Channel))]
|
||||
public Task<HashSet<long>> ImportChannelAsync(Dictionary<string, ImportPreviewOutputBase> input)
|
||||
{
|
||||
ChannelServiceHelpers.GetImportChannelData(input, out var upData, out var insertData);
|
||||
List<Channel>? channels = new List<Channel>();
|
||||
foreach (var item in input)
|
||||
{
|
||||
if (item.Key == GatewayExportString.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();
|
||||
return ImportChannelAsync(upData, insertData);
|
||||
}
|
||||
|
||||
|
@@ -19,21 +19,7 @@ namespace ThingsGateway.Gateway.Application;
|
||||
public static partial class ChannelServiceHelpers
|
||||
{
|
||||
|
||||
public static void GetImportChannelData(Dictionary<string, ImportPreviewOutputBase> input, out List<Channel> upData, out List<Channel> insertData)
|
||||
{
|
||||
List<Channel>? channels = new List<Channel>();
|
||||
foreach (var item in input)
|
||||
{
|
||||
if (item.Key == GatewayExportString.ChannelName)
|
||||
{
|
||||
var channelImports = ((ImportPreviewListOutput<Channel>)item.Value).Data;
|
||||
channels = channelImports;
|
||||
break;
|
||||
}
|
||||
}
|
||||
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)
|
||||
|
@@ -17,13 +17,17 @@ namespace ThingsGateway.Gateway.Application
|
||||
{
|
||||
public interface IChannelPageService
|
||||
{
|
||||
Task<string> GetPluginNameAsync(long channelId);
|
||||
|
||||
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);
|
||||
|
||||
|
||||
Task<QueryData<ChannelRuntime>> OnChannelQueryAsync(QueryPageOptions options);
|
||||
Task<List<Channel>> GetChannelListAsync(QueryPageOptions options, int max = 0);
|
||||
Task<Dictionary<string, ImportPreviewOutputBase>> ImportChannelAsync(IBrowserFile file, bool restart);
|
||||
|
||||
/// <summary>
|
||||
/// 保存通道
|
||||
@@ -41,24 +45,27 @@ namespace ThingsGateway.Gateway.Application
|
||||
/// <param name="model">新数据</param>
|
||||
/// <param name="restart">重启</param>
|
||||
/// <returns></returns>
|
||||
Task<bool> BatchEditChannelAsync(IEnumerable<Channel> models, Channel oldModel, Channel model, bool restart);
|
||||
|
||||
|
||||
|
||||
Task<bool> BatchEditChannelAsync(List<Channel> models, Channel oldModel, Channel model, bool restart);
|
||||
|
||||
/// <summary>
|
||||
/// 删除通道
|
||||
/// </summary>
|
||||
Task<bool> DeleteChannelAsync(IEnumerable<long> ids, bool restart, CancellationToken cancellationToken);
|
||||
|
||||
Task<bool> DeleteChannelAsync(List<long> ids, bool restart);
|
||||
/// <summary>
|
||||
/// 删除通道
|
||||
/// </summary>
|
||||
Task<bool> ClearChannelAsync(bool restart);
|
||||
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<Dictionary<string, ImportPreviewOutputBase>> ImportChannelUSheetDatasAsync(USheetDatas input, bool restart);
|
||||
Task<Dictionary<string, ImportPreviewOutputBase>> ImportChannelFileAsync(string filePath, bool restart);
|
||||
|
||||
|
||||
Task<USheetDatas> ExportChannelAsync(List<Channel> channels);
|
||||
|
||||
Task<string> ExportChannelFileAsync(GatewayExportFilter exportFilter);
|
||||
|
||||
|
||||
Task<QueryData<SelectedItem>> OnChannelSelectedItemQueryAsync(VirtualizeQueryOption option);
|
||||
|
||||
Task<string> GetChannelNameAsync(long channelId);
|
||||
}
|
||||
}
|
@@ -16,7 +16,6 @@ namespace ThingsGateway.Gateway.Application;
|
||||
|
||||
public interface IChannelRuntimeService : IChannelPageService
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// 保存通道
|
||||
/// </summary>
|
||||
@@ -36,9 +35,9 @@ public interface IChannelRuntimeService : IChannelPageService
|
||||
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);
|
||||
Task<bool> CopyAsync(List<Channel> models, Dictionary<Device, List<Variable>> devices, bool restart);
|
||||
Task<bool> UpdateAsync(List<Channel> models, List<Device> devices, List<Variable> variables, bool restart);
|
||||
Task<bool> InsertAsync(List<Channel> models, List<Device> devices, List<Variable> variables, bool restart);
|
||||
|
||||
|
||||
|
||||
|
@@ -13,9 +13,11 @@ using BootstrapBlazor.Components;
|
||||
using Microsoft.AspNetCore.Components.Forms;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
using ThingsGateway.Extension.Generic;
|
||||
using ThingsGateway.NewLife;
|
||||
using ThingsGateway.NewLife.Collections;
|
||||
using ThingsGateway.NewLife.DictionaryExtensions;
|
||||
using ThingsGateway.NewLife.Extension;
|
||||
|
||||
namespace ThingsGateway.Gateway.Application;
|
||||
|
||||
@@ -27,8 +29,121 @@ public class DeviceRuntimeService : IDeviceRuntimeService
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public Task<Dictionary<long, Tuple<string, string>>> GetDeviceIdNamesAsync()
|
||||
{
|
||||
|
||||
return Task.FromResult(GlobalData.ReadOnlyIdDevices.ToDictionary(a => a.Key, a => Tuple.Create(a.Value.Name, a.Value.PluginName)));
|
||||
}
|
||||
|
||||
public async Task<List<SelectedItem>> GetDeviceItemsAsync(bool isCollect)
|
||||
{
|
||||
var devices = await GlobalData.GetCurrentUserDevices().ConfigureAwait(false);
|
||||
return devices.Where(a => a.IsCollect == isCollect).BuildDeviceSelectList().ToList();
|
||||
}
|
||||
public Task<string> GetDevicePluginNameAsync(long id)
|
||||
{
|
||||
return Task.FromResult(GlobalData.ReadOnlyIdDevices.TryGetValue(id, out var deviceRuntime) ? deviceRuntime.PluginName : string.Empty);
|
||||
}
|
||||
public Task<string> GetDeviceNameAsync(long redundantDeviceId)
|
||||
{
|
||||
return Task.FromResult(GlobalData.ReadOnlyIdDevices.TryGetValue(redundantDeviceId, out var deviceRuntime) ? deviceRuntime.Name : string.Empty);
|
||||
}
|
||||
|
||||
public Task<bool> IsRedundantDeviceAsync(long id)
|
||||
{
|
||||
return Task.FromResult(GlobalData.IsRedundant(id));
|
||||
}
|
||||
|
||||
public Task<QueryData<DeviceRuntime>> OnDeviceQueryAsync(QueryPageOptions options)
|
||||
{
|
||||
var data = GlobalData.IdDevices.Select(a => a.Value)
|
||||
.WhereIf(!options.SearchText.IsNullOrWhiteSpace(), a => a.Name.Contains(options.SearchText))
|
||||
.GetQueryData(options);
|
||||
return Task.FromResult(data);
|
||||
}
|
||||
public Task<List<Device>> GetDeviceListAsync(QueryPageOptions options, int max = 0)
|
||||
{
|
||||
var models = GlobalData.IdDevices.Select(a => a.Value)
|
||||
.WhereIf(!options.SearchText.IsNullOrWhiteSpace(), a => a.Name.Contains(options.SearchText))
|
||||
.GetData(options, out var total).Cast<Device>().ToList();
|
||||
|
||||
if (max > 0 && models.Count > max)
|
||||
{
|
||||
throw new("online Excel max data count 2000");
|
||||
}
|
||||
return Task.FromResult(models);
|
||||
}
|
||||
|
||||
public Task<bool> ClearDeviceAsync(bool restart)
|
||||
{
|
||||
return DeleteDeviceAsync(GlobalData.IdChannels.Keys.ToList(), restart);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public async Task DeviceRedundantThreadAsync(long id)
|
||||
{
|
||||
if (GlobalData.IdDevices.TryGetValue(id, out var deviceRuntime) && GlobalData.TryGetDeviceThreadManage(deviceRuntime, out var deviceThreadManage))
|
||||
{
|
||||
await deviceThreadManage.DeviceRedundantThreadAsync(id, default).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
public async Task RestartDeviceAsync(long id, bool deleteCache)
|
||||
{
|
||||
if (GlobalData.IdDevices.TryGetValue(id, out var deviceRuntime) && GlobalData.TryGetDeviceThreadManage(deviceRuntime, out var deviceThreadManage))
|
||||
{
|
||||
await deviceThreadManage.RestartDeviceAsync(deviceRuntime, deleteCache).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
public Task PauseThreadAsync(long id)
|
||||
{
|
||||
if (GlobalData.IdDevices.TryGetValue(id, out var deviceRuntime))
|
||||
{
|
||||
deviceRuntime.Driver?.PauseThread(!deviceRuntime.Pause);
|
||||
}
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task<USheetDatas> ExportDeviceAsync(List<Device> devices)
|
||||
{
|
||||
return Task.FromResult(DeviceServiceHelpers.ExportDevice(devices));
|
||||
}
|
||||
|
||||
|
||||
private WaitLock WaitLock { get; set; } = new WaitLock(nameof(DeviceRuntimeService));
|
||||
|
||||
public async Task<QueryData<SelectedItem>> OnRedundantDevicesQueryAsync(VirtualizeQueryOption option, long deviceId, long channelId)
|
||||
{
|
||||
|
||||
var devices = await GlobalData.GetCurrentUserDevices().ConfigureAwait(false);
|
||||
var pluginName = GlobalData.ReadOnlyIdChannels.TryGetValue(channelId, out var channel) ? channel.PluginName : string.Empty;
|
||||
var ret = devices.WhereIf(!option.SearchText.IsNullOrWhiteSpace(), a => a.Name.Contains(option.SearchText))
|
||||
.Where(a => a.PluginName == pluginName && a.Id != deviceId).GetQueryData(option, GatewayResourceUtil.BuildDeviceSelectList
|
||||
);
|
||||
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
public Task<TouchSocket.Core.LogLevel> DeviceLogLevelAsync(long id)
|
||||
{
|
||||
GlobalData.IdDevices.TryGetValue(id, out var DeviceRuntime);
|
||||
var data = DeviceRuntime?.Driver?.LogMessage?.LogLevel ?? TouchSocket.Core.LogLevel.Trace;
|
||||
return Task.FromResult(data);
|
||||
}
|
||||
|
||||
|
||||
public async Task SetDeviceLogLevelAsync(long id, TouchSocket.Core.LogLevel logLevel)
|
||||
{
|
||||
if (GlobalData.IdDevices.TryGetValue(id, out var DeviceRuntime))
|
||||
{
|
||||
if (DeviceRuntime.Driver != null)
|
||||
{
|
||||
await DeviceRuntime.Driver.SetLogAsync(logLevel).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -63,17 +178,17 @@ public class DeviceRuntimeService : IDeviceRuntimeService
|
||||
devices.Add(device, variables);
|
||||
}
|
||||
|
||||
await GlobalData.DeviceRuntimeService.CopyAsync(devices, AutoRestartThread, default).ConfigureAwait(false);
|
||||
await GlobalData.DeviceRuntimeService.CopyAsync(devices, AutoRestartThread).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public async Task<bool> CopyAsync(Dictionary<Device, List<Variable>> devices, bool restart, CancellationToken cancellationToken)
|
||||
public async Task<bool> CopyAsync(Dictionary<Device, List<Variable>> devices, bool restart)
|
||||
{
|
||||
try
|
||||
{
|
||||
await WaitLock.WaitAsync(cancellationToken).ConfigureAwait(false);
|
||||
await WaitLock.WaitAsync().ConfigureAwait(false);
|
||||
|
||||
var result = await GlobalData.DeviceService.CopyAsync(devices).ConfigureAwait(false);
|
||||
|
||||
@@ -86,7 +201,7 @@ public class DeviceRuntimeService : IDeviceRuntimeService
|
||||
if (restart)
|
||||
{
|
||||
await RuntimeServiceHelper.RestartDeviceAsync(newDeviceRuntimes).ConfigureAwait(false);
|
||||
await RuntimeServiceHelper.ChangedDriverAsync(_logger, cancellationToken).ConfigureAwait(false);
|
||||
await RuntimeServiceHelper.ChangedDriverAsync(_logger).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -101,7 +216,7 @@ public class DeviceRuntimeService : IDeviceRuntimeService
|
||||
|
||||
|
||||
|
||||
public async Task<bool> BatchEditAsync(IEnumerable<Device> models, Device oldModel, Device model, bool restart)
|
||||
public async Task<bool> BatchEditDeviceAsync(List<Device> models, Device oldModel, Device model, bool restart)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -136,11 +251,11 @@ public class DeviceRuntimeService : IDeviceRuntimeService
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<bool> DeleteDeviceAsync(IEnumerable<long> ids, bool restart, CancellationToken cancellationToken)
|
||||
public async Task<bool> DeleteDeviceAsync(List<long> ids, bool restart)
|
||||
{
|
||||
try
|
||||
{
|
||||
await WaitLock.WaitAsync(cancellationToken).ConfigureAwait(false);
|
||||
await WaitLock.WaitAsync().ConfigureAwait(false);
|
||||
|
||||
var devids = ids.ToHashSet();
|
||||
|
||||
@@ -155,7 +270,7 @@ public class DeviceRuntimeService : IDeviceRuntimeService
|
||||
{
|
||||
await RuntimeServiceHelper.RemoveDeviceAsync(deviceRuntimes).ConfigureAwait(false);
|
||||
|
||||
await RuntimeServiceHelper.ChangedDriverAsync(changedDriver, _logger, cancellationToken).ConfigureAwait(false);
|
||||
await RuntimeServiceHelper.ChangedDriverAsync(changedDriver, _logger).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -202,6 +317,119 @@ public class DeviceRuntimeService : IDeviceRuntimeService
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<Dictionary<string, ImportPreviewOutputBase>> ImportDeviceAsync(IBrowserFile file, bool restart)
|
||||
{
|
||||
try
|
||||
{
|
||||
await WaitLock.WaitAsync().ConfigureAwait(false);
|
||||
|
||||
var data = await GlobalData.DeviceService.PreviewAsync(file).ConfigureAwait(false);
|
||||
|
||||
if (data.Any(a => a.Value.HasError)) return data;
|
||||
|
||||
var deviceids = await GlobalData.DeviceService.ImportDeviceAsync(data).ConfigureAwait(false);
|
||||
|
||||
var newDeviceRuntimes = await RuntimeServiceHelper.GetNewDeviceRuntimesAsync(deviceids).ConfigureAwait(false);
|
||||
|
||||
if (restart)
|
||||
{
|
||||
var newDeciceIds = newDeviceRuntimes.Select(a => a.Id).ToHashSet();
|
||||
await RuntimeServiceHelper.RemoveDeviceAsync(newDeciceIds).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
//批量修改之后,需要重新加载通道
|
||||
RuntimeServiceHelper.Init(newDeviceRuntimes);
|
||||
|
||||
//根据条件重启通道线程
|
||||
if (restart)
|
||||
{
|
||||
await RuntimeServiceHelper.RestartDeviceAsync(newDeviceRuntimes).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
finally
|
||||
{
|
||||
WaitLock.Release();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<Dictionary<string, ImportPreviewOutputBase>> ImportDeviceUSheetDatasAsync(USheetDatas input, bool restart)
|
||||
{
|
||||
try
|
||||
{
|
||||
await WaitLock.WaitAsync().ConfigureAwait(false);
|
||||
|
||||
|
||||
var data = await DeviceServiceHelpers.ImportAsync(input).ConfigureAwait(false);
|
||||
|
||||
if (data.Any(a => a.Value.HasError)) return data;
|
||||
|
||||
|
||||
var deviceids = await GlobalData.DeviceService.ImportDeviceAsync(data).ConfigureAwait(false);
|
||||
|
||||
var newDeviceRuntimes = await RuntimeServiceHelper.GetNewDeviceRuntimesAsync(deviceids).ConfigureAwait(false);
|
||||
|
||||
if (restart)
|
||||
{
|
||||
var newDeciceIds = newDeviceRuntimes.Select(a => a.Id).ToHashSet();
|
||||
await RuntimeServiceHelper.RemoveDeviceAsync(newDeciceIds).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
//批量修改之后,需要重新加载通道
|
||||
RuntimeServiceHelper.Init(newDeviceRuntimes);
|
||||
|
||||
//根据条件重启通道线程
|
||||
if (restart)
|
||||
{
|
||||
await RuntimeServiceHelper.RestartDeviceAsync(newDeviceRuntimes).ConfigureAwait(false);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
finally
|
||||
{
|
||||
WaitLock.Release();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<Dictionary<string, ImportPreviewOutputBase>> ImportDeviceFileAsync(string filePath, bool restart)
|
||||
{
|
||||
try
|
||||
{
|
||||
await WaitLock.WaitAsync().ConfigureAwait(false);
|
||||
|
||||
var data = await GlobalData.DeviceService.PreviewAsync(filePath).ConfigureAwait(false);
|
||||
|
||||
if (data.Any(a => a.Value.HasError)) return data;
|
||||
|
||||
var deviceids = await GlobalData.DeviceService.ImportDeviceAsync(data).ConfigureAwait(false);
|
||||
|
||||
var newDeviceRuntimes = await RuntimeServiceHelper.GetNewDeviceRuntimesAsync(deviceids).ConfigureAwait(false);
|
||||
|
||||
if (restart)
|
||||
{
|
||||
var newDeciceIds = newDeviceRuntimes.Select(a => a.Id).ToHashSet();
|
||||
await RuntimeServiceHelper.RemoveDeviceAsync(newDeciceIds).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
//批量修改之后,需要重新加载通道
|
||||
RuntimeServiceHelper.Init(newDeviceRuntimes);
|
||||
|
||||
//根据条件重启通道线程
|
||||
if (restart)
|
||||
{
|
||||
await RuntimeServiceHelper.RestartDeviceAsync(newDeviceRuntimes).ConfigureAwait(false);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
finally
|
||||
{
|
||||
WaitLock.Release();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public async Task<bool> SaveDeviceAsync(Device input, ItemChangedType type, bool restart)
|
||||
{
|
||||
try
|
||||
@@ -253,4 +481,13 @@ public class DeviceRuntimeService : IDeviceRuntimeService
|
||||
WaitLock.Release();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<string> ExportDeviceFileAsync(GatewayExportFilter exportFilter)
|
||||
{
|
||||
var sheets = await GlobalData.DeviceService.ExportDeviceAsync(exportFilter).ConfigureAwait(false);
|
||||
return await App.GetService<IImportExportService>().CreateFileAsync<Device>(sheets, "Device", false).ConfigureAwait(false);
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
@@ -362,9 +362,10 @@ internal sealed class DeviceService : BaseService<Device>, IDeviceService
|
||||
|
||||
/// <inheritdoc/>
|
||||
[OperDesc("ImportDevice", isRecordPar: false, localizerType: typeof(Device))]
|
||||
public async Task<HashSet<long>> ImportDeviceAsync(Dictionary<string, ImportPreviewOutputBase> input)
|
||||
public Task<HashSet<long>> ImportDeviceAsync(Dictionary<string, ImportPreviewOutputBase> input)
|
||||
{
|
||||
IEnumerable<Device>? devices = new List<Device>();
|
||||
|
||||
IEnumerable<Device> devices = new List<Device>();
|
||||
foreach (var item in input)
|
||||
{
|
||||
if (item.Key == GatewayExportString.DeviceName)
|
||||
@@ -376,6 +377,13 @@ internal sealed class DeviceService : BaseService<Device>, IDeviceService
|
||||
}
|
||||
var upData = devices.Where(a => a.IsUp).ToList();
|
||||
var insertData = devices.Where(a => !a.IsUp).ToList();
|
||||
return ImportDeviceAsync(upData, insertData);
|
||||
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
[OperDesc("ImportDevice", isRecordPar: false, localizerType: typeof(Device))]
|
||||
public async Task<HashSet<long>> ImportDeviceAsync(List<Device> upData, List<Device> insertData)
|
||||
{
|
||||
|
||||
ManageHelper.CheckDeviceCount(insertData.Count);
|
||||
|
||||
@@ -391,14 +399,21 @@ internal sealed class DeviceService : BaseService<Device>, IDeviceService
|
||||
await db.BulkUpdateAsync(upData, 10000).ConfigureAwait(false);
|
||||
}
|
||||
DeleteDeviceFromCache();
|
||||
return devices.Select(a => a.Id).ToHashSet();
|
||||
return upData.Select(a => a.Id).Concat(insertData.Select(a => a.Id)).ToHashSet();
|
||||
}
|
||||
|
||||
|
||||
|
||||
public async Task<Dictionary<string, ImportPreviewOutputBase>> PreviewAsync(IBrowserFile browserFile)
|
||||
{
|
||||
var path = await browserFile.StorageLocal().ConfigureAwait(false); // 上传文件并获取文件路径
|
||||
var dataScope = await GlobalData.SysUserService.GetCurrentUserDataScopeAsync().ConfigureAwait(false);
|
||||
|
||||
return await PreviewAsync(path).ConfigureAwait(false);
|
||||
|
||||
}
|
||||
public async Task<Dictionary<string, ImportPreviewOutputBase>> PreviewAsync(string path)
|
||||
{
|
||||
var dataScope = await GlobalData.SysUserService.GetCurrentUserDataScopeAsync().ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
// 获取 Excel 文件中所有工作表的名称
|
||||
@@ -434,8 +449,8 @@ internal sealed class DeviceService : BaseService<Device>, IDeviceService
|
||||
{
|
||||
FileUtility.Delete(path);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
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
|
||||
|
@@ -13,49 +13,13 @@ using BootstrapBlazor.Components;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Reflection;
|
||||
|
||||
using ThingsGateway.Common.Extension;
|
||||
|
||||
namespace ThingsGateway.Gateway.Application;
|
||||
|
||||
public static class DeviceServiceHelpers
|
||||
public static partial class DeviceServiceHelpers
|
||||
{
|
||||
public static USheetDatas ExportDevice(IEnumerable<Device> models)
|
||||
{
|
||||
var deviceDicts = GlobalData.IdDevices;
|
||||
var channelDicts = GlobalData.IdChannels;
|
||||
var pluginSheetNames = models.Select(a => a.ChannelId).Select(a =>
|
||||
{
|
||||
channelDicts.TryGetValue(a, out var channel);
|
||||
var pluginKey = channel?.PluginName;
|
||||
return pluginKey;
|
||||
}).ToHashSet();
|
||||
var data = ExportSheets(models, deviceDicts, channelDicts, pluginSheetNames); // IEnumerable 延迟执行
|
||||
return USheetDataHelpers.GetUSheetDatas(data);
|
||||
}
|
||||
|
||||
public static Dictionary<string, object> ExportSheets(
|
||||
IEnumerable<Device>? data,
|
||||
IReadOnlyDictionary<long, DeviceRuntime>? deviceDicts,
|
||||
IReadOnlyDictionary<long, ChannelRuntime> channelDicts,
|
||||
HashSet<string> pluginSheetNames,
|
||||
string? channelName = null)
|
||||
{
|
||||
if (data?.Any() != true)
|
||||
data = new List<Device>();
|
||||
|
||||
var result = new Dictionary<string, object>();
|
||||
result.Add(GatewayExportString.DeviceName, GetDeviceSheets(data, deviceDicts, channelDicts, channelName));
|
||||
ConcurrentDictionary<string, (object, Dictionary<string, PropertyInfo>)> propertysDict = new();
|
||||
|
||||
foreach (var plugin in pluginSheetNames)
|
||||
{
|
||||
var filtered = FilterPluginDevices(data, plugin, channelDicts);
|
||||
var filtResult = PluginInfoUtil.GetFileNameAndTypeName(plugin);
|
||||
var pluginSheets = GetPluginSheets(filtered, propertysDict, plugin);
|
||||
result.Add(filtResult.TypeName, pluginSheets);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static Dictionary<string, object> ExportSheets(
|
||||
IAsyncEnumerable<Device>? data1,
|
||||
@@ -99,61 +63,8 @@ string? channelName = null)
|
||||
}
|
||||
});
|
||||
}
|
||||
static IEnumerable<Device> FilterPluginDevices(IEnumerable<Device> data, string plugin, IReadOnlyDictionary<long, ChannelRuntime> channelDicts)
|
||||
{
|
||||
return data.Where(device =>
|
||||
{
|
||||
if (channelDicts.TryGetValue(device.ChannelId, out var channel))
|
||||
{
|
||||
if (channel.PluginName == plugin)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static IEnumerable<Dictionary<string, object>> GetDeviceSheets(
|
||||
IEnumerable<Device> data,
|
||||
IReadOnlyDictionary<long, DeviceRuntime>? deviceDicts,
|
||||
IReadOnlyDictionary<long, ChannelRuntime> channelDicts,
|
||||
string? channelName)
|
||||
{
|
||||
var type = typeof(Device);
|
||||
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 += 10000000;
|
||||
else if (order == 0) order = 10000000;
|
||||
return order;
|
||||
});
|
||||
|
||||
foreach (var device in data)
|
||||
{
|
||||
yield return GetDeviceRows(device, propertyInfos, type, deviceDicts, channelDicts, channelName);
|
||||
}
|
||||
}
|
||||
|
||||
static IEnumerable<Dictionary<string, object>> GetPluginSheets(
|
||||
IEnumerable<Device> data,
|
||||
ConcurrentDictionary<string, (object, Dictionary<string, PropertyInfo>)> propertysDict,
|
||||
string? plugin)
|
||||
{
|
||||
foreach (var device in data)
|
||||
{
|
||||
var row = GetPluginRows(device, plugin, propertysDict);
|
||||
if (row != null)
|
||||
{
|
||||
yield return row;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static async IAsyncEnumerable<Dictionary<string, object>> GetDeviceSheets(
|
||||
IAsyncEnumerable<Device> data,
|
||||
@@ -197,80 +108,6 @@ IReadOnlyDictionary<long, DeviceRuntime>? deviceDicts,
|
||||
}
|
||||
}
|
||||
|
||||
static Dictionary<string, object> GetDeviceRows(
|
||||
Device device,
|
||||
IEnumerable<PropertyInfo>? propertyInfos,
|
||||
Type type,
|
||||
IReadOnlyDictionary<long, DeviceRuntime>? deviceDicts,
|
||||
IReadOnlyDictionary<long, ChannelRuntime>? channelDicts,
|
||||
string? channelName)
|
||||
{
|
||||
Dictionary<string, object> devExport = new();
|
||||
deviceDicts.TryGetValue(device.RedundantDeviceId ?? 0, out var redundantDevice);
|
||||
channelDicts.TryGetValue(device.ChannelId, out var channel);
|
||||
|
||||
devExport.Add(GatewayExportString.ChannelName, channel?.Name ?? channelName);
|
||||
|
||||
foreach (var item in propertyInfos)
|
||||
{
|
||||
//描述
|
||||
var desc = type.GetPropertyDisplayName(item.Name);
|
||||
//数据源增加
|
||||
devExport.Add(desc ?? item.Name, item.GetValue(device)?.ToString());
|
||||
}
|
||||
|
||||
//设备实体没有包含冗余设备名称,手动插入
|
||||
devExport.Add(GatewayExportString.RedundantDeviceName, redundantDevice?.Name);
|
||||
return devExport;
|
||||
}
|
||||
|
||||
static Dictionary<string, object> GetPluginRows(Device device, string? plugin, ConcurrentDictionary<string, (object, Dictionary<string, PropertyInfo>)> propertysDict)
|
||||
{
|
||||
Dictionary<string, object> driverInfo = new();
|
||||
var propDict = device.DevicePropertys;
|
||||
if (!propertysDict.TryGetValue(plugin, out var propertys))
|
||||
{
|
||||
try
|
||||
{
|
||||
var driverProperties = GlobalData.PluginService.GetDriver(plugin).DriverProperties;
|
||||
propertys.Item1 = driverProperties;
|
||||
var driverPropertyType = driverProperties.GetType();
|
||||
propertys.Item2 = driverPropertyType.GetRuntimeProperties()
|
||||
.Where(a => a.GetCustomAttribute<DynamicPropertyAttribute>() != null)
|
||||
.ToDictionary(a => driverPropertyType.GetPropertyDisplayName(a.Name, a => a.GetCustomAttribute<DynamicPropertyAttribute>(true)?.Description), a => a);
|
||||
propertysDict.TryAdd(plugin, propertys);
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
if (propertys.Item2 != null)
|
||||
{
|
||||
if (propertys.Item2.Count > 0)
|
||||
{
|
||||
//没有包含设备名称,手动插入
|
||||
driverInfo.Add(GatewayExportString.DeviceName, device.Name);
|
||||
}
|
||||
//根据插件的配置属性项生成列,从数据库中获取值或者获取属性默认值
|
||||
foreach (var item in propertys.Item2)
|
||||
{
|
||||
if (propDict.TryGetValue(item.Value.Name, out var dependencyProperty))
|
||||
{
|
||||
driverInfo.Add(item.Key, dependencyProperty);
|
||||
}
|
||||
else
|
||||
{
|
||||
//添加对应属性数据
|
||||
driverInfo.Add(item.Key, ThingsGatewayStringConverter.Default.Serialize(null, item.Value.GetValue(propertys.Item1)));
|
||||
}
|
||||
}
|
||||
|
||||
if (driverInfo.Count > 0)
|
||||
return driverInfo;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static async Task<Dictionary<string, ImportPreviewOutputBase>> ImportAsync(USheetDatas uSheetDatas)
|
||||
{
|
||||
|
@@ -0,0 +1,196 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://thingsgateway.cn/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using BootstrapBlazor.Components;
|
||||
|
||||
using System.Collections.Concurrent;
|
||||
using System.Reflection;
|
||||
|
||||
using ThingsGateway.Common.Extension;
|
||||
|
||||
namespace ThingsGateway.Gateway.Application;
|
||||
|
||||
public static partial class DeviceServiceHelpers
|
||||
{
|
||||
public static USheetDatas ExportDevice(IEnumerable<Device> models)
|
||||
{
|
||||
var deviceDicts = GlobalData.IdDevices;
|
||||
var channelDicts = GlobalData.IdChannels;
|
||||
var pluginSheetNames = models.Select(a => a.ChannelId).Select(a =>
|
||||
{
|
||||
channelDicts.TryGetValue(a, out var channel);
|
||||
var pluginKey = channel?.PluginName;
|
||||
return pluginKey;
|
||||
}).ToHashSet();
|
||||
var data = ExportSheets(models, deviceDicts, channelDicts, pluginSheetNames); // IEnumerable 延迟执行
|
||||
return USheetDataHelpers.GetUSheetDatas(data);
|
||||
}
|
||||
|
||||
public static Dictionary<string, object> ExportSheets(
|
||||
IEnumerable<Device>? data,
|
||||
IReadOnlyDictionary<long, DeviceRuntime>? deviceDicts,
|
||||
IReadOnlyDictionary<long, ChannelRuntime> channelDicts,
|
||||
HashSet<string> pluginSheetNames,
|
||||
string? channelName = null)
|
||||
{
|
||||
if (data?.Any() != true)
|
||||
data = new List<Device>();
|
||||
|
||||
var result = new Dictionary<string, object>();
|
||||
result.Add(GatewayExportString.DeviceName, GetDeviceSheets(data, deviceDicts, channelDicts, channelName));
|
||||
ConcurrentDictionary<string, (object, Dictionary<string, PropertyInfo>)> propertysDict = new();
|
||||
|
||||
foreach (var plugin in pluginSheetNames)
|
||||
{
|
||||
var filtered = FilterPluginDevices(data, plugin, channelDicts);
|
||||
var filtResult = PluginInfoUtil.GetFileNameAndTypeName(plugin);
|
||||
var pluginSheets = GetPluginSheets(filtered, propertysDict, plugin);
|
||||
result.Add(filtResult.TypeName, pluginSheets);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
static IEnumerable<Dictionary<string, object>> GetDeviceSheets(
|
||||
IEnumerable<Device> data,
|
||||
IReadOnlyDictionary<long, DeviceRuntime>? deviceDicts,
|
||||
IReadOnlyDictionary<long, ChannelRuntime> channelDicts,
|
||||
string? channelName)
|
||||
{
|
||||
var type = typeof(Device);
|
||||
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 += 10000000;
|
||||
else if (order == 0) order = 10000000;
|
||||
return order;
|
||||
});
|
||||
|
||||
foreach (var device in data)
|
||||
{
|
||||
yield return GetDeviceRows(device, propertyInfos, type, deviceDicts, channelDicts, channelName);
|
||||
}
|
||||
}
|
||||
static IEnumerable<Device> FilterPluginDevices(IEnumerable<Device> data, string plugin, IReadOnlyDictionary<long, ChannelRuntime> channelDicts)
|
||||
{
|
||||
return data.Where(device =>
|
||||
{
|
||||
if (channelDicts.TryGetValue(device.ChannelId, out var channel))
|
||||
{
|
||||
if (channel.PluginName == plugin)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static Dictionary<string, object> GetDeviceRows(
|
||||
Device device,
|
||||
IEnumerable<PropertyInfo>? propertyInfos,
|
||||
Type type,
|
||||
IReadOnlyDictionary<long, DeviceRuntime>? deviceDicts,
|
||||
IReadOnlyDictionary<long, ChannelRuntime>? channelDicts,
|
||||
string? channelName)
|
||||
{
|
||||
Dictionary<string, object> devExport = new();
|
||||
deviceDicts.TryGetValue(device.RedundantDeviceId ?? 0, out var redundantDevice);
|
||||
channelDicts.TryGetValue(device.ChannelId, out var channel);
|
||||
|
||||
devExport.Add(GatewayExportString.ChannelName, channel?.Name ?? channelName);
|
||||
|
||||
foreach (var item in propertyInfos)
|
||||
{
|
||||
//描述
|
||||
var desc = type.GetPropertyDisplayName(item.Name);
|
||||
//数据源增加
|
||||
devExport.Add(desc ?? item.Name, item.GetValue(device)?.ToString());
|
||||
}
|
||||
|
||||
//设备实体没有包含冗余设备名称,手动插入
|
||||
devExport.Add(GatewayExportString.RedundantDeviceName, redundantDevice?.Name);
|
||||
return devExport;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
static IEnumerable<Dictionary<string, object>> GetPluginSheets(
|
||||
IEnumerable<Device> data,
|
||||
ConcurrentDictionary<string, (object, Dictionary<string, PropertyInfo>)> propertysDict,
|
||||
string? plugin)
|
||||
{
|
||||
foreach (var device in data)
|
||||
{
|
||||
var row = GetPluginRows(device, plugin, propertysDict);
|
||||
if (row != null)
|
||||
{
|
||||
yield return row;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static Dictionary<string, object> GetPluginRows(Device device, string? plugin, ConcurrentDictionary<string, (object, Dictionary<string, PropertyInfo>)> propertysDict)
|
||||
{
|
||||
Dictionary<string, object> driverInfo = new();
|
||||
var propDict = device.DevicePropertys;
|
||||
if (!propertysDict.TryGetValue(plugin, out var propertys))
|
||||
{
|
||||
try
|
||||
{
|
||||
var driverProperties = GlobalData.PluginService.GetDriver(plugin).DriverProperties;
|
||||
propertys.Item1 = driverProperties;
|
||||
var driverPropertyType = driverProperties.GetType();
|
||||
propertys.Item2 = driverPropertyType.GetRuntimeProperties()
|
||||
.Where(a => a.GetCustomAttribute<DynamicPropertyAttribute>() != null)
|
||||
.ToDictionary(a => driverPropertyType.GetPropertyDisplayName(a.Name, a => a.GetCustomAttribute<DynamicPropertyAttribute>(true)?.Description), a => a);
|
||||
propertysDict.TryAdd(plugin, propertys);
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
if (propertys.Item2 != null)
|
||||
{
|
||||
if (propertys.Item2.Count > 0)
|
||||
{
|
||||
//没有包含设备名称,手动插入
|
||||
driverInfo.Add(GatewayExportString.DeviceName, device.Name);
|
||||
}
|
||||
//根据插件的配置属性项生成列,从数据库中获取值或者获取属性默认值
|
||||
foreach (var item in propertys.Item2)
|
||||
{
|
||||
if (propDict.TryGetValue(item.Value.Name, out var dependencyProperty))
|
||||
{
|
||||
driverInfo.Add(item.Key, dependencyProperty);
|
||||
}
|
||||
else
|
||||
{
|
||||
//添加对应属性数据
|
||||
driverInfo.Add(item.Key, ThingsGatewayStringConverter.Default.Serialize(null, item.Value.GetValue(propertys.Item1)));
|
||||
}
|
||||
}
|
||||
|
||||
if (driverInfo.Count > 0)
|
||||
return driverInfo;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
@@ -8,10 +8,43 @@
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using BootstrapBlazor.Components;
|
||||
|
||||
using Microsoft.AspNetCore.Components.Forms;
|
||||
|
||||
using TouchSocket.Core;
|
||||
|
||||
namespace ThingsGateway.Gateway.Application
|
||||
{
|
||||
public interface IDevicePageService
|
||||
{
|
||||
Task SetDeviceLogLevelAsync(long id, TouchSocket.Core.LogLevel logLevel);
|
||||
Task<Dictionary<string, ImportPreviewOutputBase>> ImportDeviceAsync(IBrowserFile file, bool restart);
|
||||
|
||||
Task CopyDeviceAsync(int CopyCount, string CopyDeviceNamePrefix, int CopyDeviceNameSuffixNumber, long deviceId, bool AutoRestartThread);
|
||||
Task<LogLevel> DeviceLogLevelAsync(long id);
|
||||
Task<bool> BatchEditDeviceAsync(List<Device> models, Device oldModel, Device model, bool restart);
|
||||
Task<bool> SaveDeviceAsync(Device input, ItemChangedType type, bool restart);
|
||||
Task<bool> DeleteDeviceAsync(List<long> ids, bool restart);
|
||||
Task<Dictionary<string, ImportPreviewOutputBase>> ImportDeviceUSheetDatasAsync(USheetDatas input, bool restart);
|
||||
Task<USheetDatas> ExportDeviceAsync(List<Device> devices);
|
||||
|
||||
Task<string> ExportDeviceFileAsync(GatewayExportFilter exportFilter);
|
||||
|
||||
|
||||
Task<QueryData<SelectedItem>> OnRedundantDevicesQueryAsync(VirtualizeQueryOption option, long deviceId, long channelId);
|
||||
Task<Dictionary<string, ImportPreviewOutputBase>> ImportDeviceFileAsync(string filePath, bool restart);
|
||||
Task DeviceRedundantThreadAsync(long id);
|
||||
Task RestartDeviceAsync(long id, bool deleteCache);
|
||||
Task PauseThreadAsync(long id);
|
||||
Task<QueryData<DeviceRuntime>> OnDeviceQueryAsync(QueryPageOptions options);
|
||||
Task<List<Device>> GetDeviceListAsync(QueryPageOptions option, int v);
|
||||
Task<bool> ClearDeviceAsync(bool restart);
|
||||
Task<bool> IsRedundantDeviceAsync(long id);
|
||||
Task<string> GetDeviceNameAsync(long redundantDeviceId);
|
||||
Task<List<SelectedItem>> GetDeviceItemsAsync(bool isCollect);
|
||||
Task<string> GetDevicePluginNameAsync(long id);
|
||||
|
||||
Task<Dictionary<long, Tuple<string, string>>> GetDeviceIdNamesAsync();
|
||||
}
|
||||
}
|
@@ -16,14 +16,13 @@ namespace ThingsGateway.Gateway.Application
|
||||
{
|
||||
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<bool> CopyAsync(Dictionary<Device, List<Variable>> devices, bool restart);
|
||||
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);
|
||||
Task<bool> SaveDeviceAsync(Device input, ItemChangedType type, bool restart);
|
||||
Task<bool> BatchSaveDeviceAsync(List<Device> input, ItemChangedType type, bool restart);
|
||||
Task<Dictionary<string, ImportPreviewOutputBase>> PreviewAsync(IBrowserFile browserFile);
|
||||
}
|
||||
}
|
@@ -119,4 +119,6 @@ internal interface IDeviceService
|
||||
/// 保存是否输出日志和日志等级
|
||||
/// </summary>
|
||||
Task UpdateLogAsync(long deviceId, TouchSocket.Core.LogLevel logLevel);
|
||||
Task<HashSet<long>> ImportDeviceAsync(List<Device> upData, List<Device> insertData);
|
||||
Task<Dictionary<string, ImportPreviewOutputBase>> PreviewAsync(string path);
|
||||
}
|
||||
|
@@ -94,11 +94,11 @@ public interface IManagementRpcServer : IRpcServer
|
||||
Task RedundancyForcedSync();
|
||||
|
||||
[DmtpRpc]
|
||||
public Task<TouchSocket.Core.LogLevel> RedundancyLogLevelAsync();
|
||||
Task<TouchSocket.Core.LogLevel> RedundancyLogLevelAsync();
|
||||
[DmtpRpc]
|
||||
public Task SetRedundancyLogLevelAsync(TouchSocket.Core.LogLevel logLevel);
|
||||
Task SetRedundancyLogLevelAsync(TouchSocket.Core.LogLevel logLevel);
|
||||
[DmtpRpc]
|
||||
public Task<string> RedundancyLogPathAsync();
|
||||
Task<string> RedundancyLogPathAsync();
|
||||
|
||||
/// <summary>
|
||||
/// 修改冗余设置
|
||||
@@ -133,7 +133,7 @@ public interface IManagementRpcServer : IRpcServer
|
||||
/// 分页显示插件
|
||||
/// </summary>
|
||||
[DmtpRpc]
|
||||
public Task<QueryData<PluginInfo>> PluginPageAsync(QueryPageOptions options, PluginTypeEnum? pluginTypeEnum = null);
|
||||
Task<QueryData<PluginInfo>> PluginPageAsync(QueryPageOptions options, PluginTypeEnum? pluginTypeEnum = null);
|
||||
|
||||
/// <summary>
|
||||
/// 重载插件
|
||||
@@ -212,4 +212,159 @@ public interface IManagementRpcServer : IRpcServer
|
||||
/// <param name="type">保存类型</param>
|
||||
[DmtpRpc]
|
||||
Task<bool> SaveRulesAsync(Rules input, ItemChangedType type);
|
||||
|
||||
|
||||
|
||||
|
||||
[DmtpRpc]
|
||||
Task<string> GetPluginNameAsync(long channelId);
|
||||
|
||||
[DmtpRpc]
|
||||
Task RestartChannelAsync(long channelId);
|
||||
|
||||
[DmtpRpc]
|
||||
Task<TouchSocket.Core.LogLevel> ChannelLogLevelAsync(long id);
|
||||
[DmtpRpc]
|
||||
Task SetChannelLogLevelAsync(long id, TouchSocket.Core.LogLevel logLevel);
|
||||
[DmtpRpc]
|
||||
Task CopyChannelAsync(int CopyCount, string CopyChannelNamePrefix, int CopyChannelNameSuffixNumber, string CopyDeviceNamePrefix, int CopyDeviceNameSuffixNumber, long channelId, bool AutoRestartThread);
|
||||
|
||||
[DmtpRpc]
|
||||
Task<QueryData<ChannelRuntime>> OnChannelQueryAsync(QueryPageOptions options);
|
||||
[DmtpRpc]
|
||||
Task<List<Channel>> GetChannelListAsync(QueryPageOptions options, int max = 0);
|
||||
|
||||
[DmtpRpc]
|
||||
Task<bool> SaveChannelAsync(Channel input, ItemChangedType type, bool restart);
|
||||
|
||||
[DmtpRpc]
|
||||
Task<bool> BatchEditChannelAsync(List<Channel> models, Channel oldModel, Channel model, bool restart);
|
||||
|
||||
[DmtpRpc]
|
||||
Task<bool> DeleteChannelAsync(List<long> ids, bool restart);
|
||||
|
||||
[DmtpRpc]
|
||||
Task<bool> ClearChannelAsync(bool restart);
|
||||
[DmtpRpc]
|
||||
Task ImportChannelAsync(List<Channel> upData, List<Channel> insertData, bool restart);
|
||||
|
||||
[DmtpRpc]
|
||||
Task<Dictionary<string, ImportPreviewOutputBase>> ImportChannelUSheetDatasAsync(USheetDatas input, bool restart);
|
||||
|
||||
[DmtpRpc]
|
||||
Task<Dictionary<string, ImportPreviewOutputBase>> ImportChannelFileAsync(string filePath, bool restart);
|
||||
|
||||
[DmtpRpc]
|
||||
Task<USheetDatas> ExportChannelAsync(List<Channel> channels);
|
||||
|
||||
[DmtpRpc]
|
||||
Task<string> ExportChannelFileAsync(GatewayExportFilter exportFilter);
|
||||
|
||||
|
||||
[DmtpRpc]
|
||||
Task<QueryData<SelectedItem>> OnChannelSelectedItemQueryAsync(VirtualizeQueryOption option);
|
||||
|
||||
|
||||
[DmtpRpc]
|
||||
Task<string> GetChannelNameAsync(long channelId);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
[DmtpRpc]
|
||||
Task SetDeviceLogLevelAsync(long id, TouchSocket.Core.LogLevel logLevel);
|
||||
|
||||
[DmtpRpc]
|
||||
Task CopyDeviceAsync(int CopyCount, string CopyDeviceNamePrefix, int CopyDeviceNameSuffixNumber, long deviceId, bool AutoRestartThread);
|
||||
[DmtpRpc]
|
||||
Task<TouchSocket.Core.LogLevel> DeviceLogLevelAsync(long id);
|
||||
[DmtpRpc]
|
||||
Task<bool> BatchEditDeviceAsync(List<Device> models, Device oldModel, Device model, bool restart);
|
||||
[DmtpRpc]
|
||||
Task<bool> SaveDeviceAsync(Device input, ItemChangedType type, bool restart);
|
||||
[DmtpRpc]
|
||||
Task<bool> DeleteDeviceAsync(List<long> ids, bool restart);
|
||||
[DmtpRpc]
|
||||
Task<Dictionary<string, ImportPreviewOutputBase>> ImportDeviceUSheetDatasAsync(USheetDatas input, bool restart);
|
||||
[DmtpRpc]
|
||||
Task<USheetDatas> ExportDeviceAsync(List<Device> devices);
|
||||
|
||||
[DmtpRpc]
|
||||
Task<string> ExportDeviceFileAsync(GatewayExportFilter exportFilter);
|
||||
|
||||
|
||||
[DmtpRpc]
|
||||
Task<QueryData<SelectedItem>> OnRedundantDevicesQueryAsync(VirtualizeQueryOption option, long deviceId, long channelId);
|
||||
[DmtpRpc]
|
||||
Task<Dictionary<string, ImportPreviewOutputBase>> ImportDeviceFileAsync(string filePath, bool restart);
|
||||
[DmtpRpc]
|
||||
Task DeviceRedundantThreadAsync(long id);
|
||||
[DmtpRpc]
|
||||
Task RestartDeviceAsync(long id, bool deleteCache);
|
||||
[DmtpRpc]
|
||||
Task PauseThreadAsync(long id);
|
||||
[DmtpRpc]
|
||||
Task<QueryData<DeviceRuntime>> OnDeviceQueryAsync(QueryPageOptions options);
|
||||
[DmtpRpc]
|
||||
Task<List<Device>> GetDeviceListAsync(QueryPageOptions option, int v);
|
||||
[DmtpRpc]
|
||||
Task<bool> ClearDeviceAsync(bool restart);
|
||||
[DmtpRpc]
|
||||
Task<bool> IsRedundantDeviceAsync(long id);
|
||||
[DmtpRpc]
|
||||
Task<string> GetDeviceNameAsync(long redundantDeviceId);
|
||||
|
||||
|
||||
|
||||
[DmtpRpc]
|
||||
Task<List<SelectedItem>> GetDeviceItemsAsync(bool isCollect);
|
||||
|
||||
[DmtpRpc]
|
||||
Task<string> GetDevicePluginNameAsync(long id);
|
||||
|
||||
[DmtpRpc]
|
||||
Task<bool> BatchEditVariableAsync(List<Variable> models, Variable oldModel, Variable model, bool restart);
|
||||
|
||||
[DmtpRpc]
|
||||
Task<bool> DeleteVariableAsync(List<long> ids, bool restart);
|
||||
|
||||
[DmtpRpc]
|
||||
Task<bool> ClearVariableAsync(bool restart);
|
||||
|
||||
[DmtpRpc]
|
||||
Task InsertTestDataAsync(int testVariableCount, int testDeviceCount, string slaveUrl, bool businessEnable, bool restart);
|
||||
|
||||
[DmtpRpc]
|
||||
Task<bool> BatchSaveVariableAsync(List<Variable> input, ItemChangedType type, bool restart);
|
||||
|
||||
[DmtpRpc]
|
||||
Task<bool> SaveVariableAsync(Variable input, ItemChangedType type, bool restart);
|
||||
|
||||
[DmtpRpc]
|
||||
Task CopyVariableAsync(List<Variable> Model, int CopyCount, string CopyVariableNamePrefix, int CopyVariableNameSuffixNumber, bool AutoRestartThread);
|
||||
|
||||
[DmtpRpc]
|
||||
Task<QueryData<VariableRuntime>> OnVariableQueryAsync(QueryPageOptions options);
|
||||
[DmtpRpc]
|
||||
Task<string> ExportVariableFileAsync(GatewayExportFilter exportFilter);
|
||||
|
||||
[DmtpRpc]
|
||||
Task<List<Variable>> GetVariableListAsync(QueryPageOptions option, int v);
|
||||
|
||||
[DmtpRpc]
|
||||
Task<USheetDatas> ExportVariableAsync(List<Variable> models, string? sortName, SortOrder sortOrder);
|
||||
|
||||
[DmtpRpc]
|
||||
Task<Dictionary<string, ImportPreviewOutputBase>> ImportVariableUSheetDatasAsync(USheetDatas data, bool restart);
|
||||
|
||||
[DmtpRpc]
|
||||
Task<OperResult<object>> OnWriteVariableAsync(long id, string writeData);
|
||||
|
||||
|
||||
[DmtpRpc]
|
||||
Task<Dictionary<string, ImportPreviewOutputBase>> ImportVariableFileAsync(string filePath, bool restart);
|
||||
[DmtpRpc]
|
||||
Task<Dictionary<long, Tuple<string, string>>> GetDeviceIdNamesAsync();
|
||||
|
||||
}
|
@@ -30,8 +30,8 @@ internal sealed class ManagementHostedService : BackgroundService
|
||||
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||
{
|
||||
await Task.Yield();
|
||||
await RemoteClientManagementTask.StartAsync(stoppingToken).ConfigureAwait(false);
|
||||
await RemoteServerManagementTask.StartAsync(stoppingToken).ConfigureAwait(false);
|
||||
_ = RemoteClientManagementTask.StartAsync(stoppingToken);
|
||||
_ = RemoteServerManagementTask.StartAsync(stoppingToken);
|
||||
}
|
||||
|
||||
public override async Task StopAsync(CancellationToken cancellationToken)
|
||||
|
@@ -10,6 +10,8 @@
|
||||
|
||||
using BootstrapBlazor.Components;
|
||||
|
||||
using Microsoft.AspNetCore.Components.Forms;
|
||||
|
||||
using ThingsGateway.Authentication;
|
||||
|
||||
using TouchSocket.Core;
|
||||
@@ -19,10 +21,9 @@ using TouchSocket.Sockets;
|
||||
|
||||
namespace ThingsGateway.Gateway.Application;
|
||||
|
||||
public partial class ManagementRpcServer : IRpcServer, IManagementRpcServer, IBackendLogService, IRpcLogService, IRestartService, IAuthenticationService, IChannelEnableService, IRedundancyHostedService, IRedundancyService, ITextFileReadService, IPluginPageService, IRealAlarmService
|
||||
public partial class ManagementRpcServer : IRpcServer, IManagementRpcServer, IBackendLogService, IRpcLogService, IRestartService, IAuthenticationService, IChannelEnableService, IRedundancyHostedService, IRedundancyService, ITextFileReadService, IPluginPageService, IRealAlarmService, IChannelPageService, IDevicePageService, IVariablePageService
|
||||
{
|
||||
|
||||
|
||||
[DmtpRpc]
|
||||
public Task DeleteBackendLogAsync() => App.GetService<IBackendLogService>().DeleteBackendLogAsync();
|
||||
[DmtpRpc]
|
||||
@@ -127,4 +128,173 @@ public partial class ManagementRpcServer : IRpcServer, IManagementRpcServer, IBa
|
||||
|
||||
public Task<bool> SaveRulesAsync(Rules input, ItemChangedType type) => App.GetService<IRulesService>().SaveRulesAsync(input, type);
|
||||
|
||||
public Task<string> GetPluginNameAsync(long channelId) => App.GetService<IChannelPageService>().GetPluginNameAsync(channelId);
|
||||
|
||||
public Task RestartChannelAsync(long channelId) =>
|
||||
App.GetService<IChannelPageService>().RestartChannelAsync(channelId);
|
||||
|
||||
public Task<LogLevel> ChannelLogLevelAsync(long id) =>
|
||||
App.GetService<IChannelPageService>().ChannelLogLevelAsync(id);
|
||||
|
||||
public Task SetChannelLogLevelAsync(long id, LogLevel logLevel) =>
|
||||
App.GetService<IChannelPageService>().SetChannelLogLevelAsync(id, logLevel);
|
||||
|
||||
public Task CopyChannelAsync(int copyCount, string copyChannelNamePrefix, int copyChannelNameSuffixNumber,
|
||||
string copyDeviceNamePrefix, int copyDeviceNameSuffixNumber, long channelId, bool restart) =>
|
||||
App.GetService<IChannelPageService>().CopyChannelAsync(copyCount, copyChannelNamePrefix, copyChannelNameSuffixNumber,
|
||||
copyDeviceNamePrefix, copyDeviceNameSuffixNumber, channelId, restart);
|
||||
|
||||
public Task<QueryData<ChannelRuntime>> OnChannelQueryAsync(QueryPageOptions options) =>
|
||||
App.GetService<IChannelPageService>().OnChannelQueryAsync(options);
|
||||
|
||||
public Task<List<Channel>> GetChannelListAsync(QueryPageOptions options, int max = 0) =>
|
||||
App.GetService<IChannelPageService>().GetChannelListAsync(options, max);
|
||||
|
||||
public Task<bool> SaveChannelAsync(Channel input, ItemChangedType type, bool restart) =>
|
||||
App.GetService<IChannelPageService>().SaveChannelAsync(input, type, restart);
|
||||
|
||||
public Task<bool> BatchEditChannelAsync(List<Channel> models, Channel oldModel, Channel model, bool restart) =>
|
||||
App.GetService<IChannelPageService>().BatchEditChannelAsync(models, oldModel, model, restart);
|
||||
|
||||
public Task<bool> DeleteChannelAsync(List<long> ids, bool restart) =>
|
||||
App.GetService<IChannelPageService>().DeleteChannelAsync(ids, restart);
|
||||
|
||||
public Task<bool> ClearChannelAsync(bool restart) =>
|
||||
App.GetService<IChannelPageService>().ClearChannelAsync(restart);
|
||||
|
||||
public Task ImportChannelAsync(List<Channel> upData, List<Channel> insertData, bool restart) =>
|
||||
App.GetService<IChannelPageService>().ImportChannelAsync(upData, insertData, restart);
|
||||
|
||||
public Task<Dictionary<string, ImportPreviewOutputBase>> ImportChannelUSheetDatasAsync(USheetDatas input, bool restart) =>
|
||||
App.GetService<IChannelPageService>().ImportChannelUSheetDatasAsync(input, restart);
|
||||
|
||||
public Task<Dictionary<string, ImportPreviewOutputBase>> ImportChannelFileAsync(string filePath, bool restart) =>
|
||||
App.GetService<IChannelPageService>().ImportChannelFileAsync(filePath, restart);
|
||||
|
||||
public Task<USheetDatas> ExportChannelAsync(List<Channel> channels) =>
|
||||
App.GetService<IChannelPageService>().ExportChannelAsync(channels);
|
||||
|
||||
public Task<QueryData<SelectedItem>> OnChannelSelectedItemQueryAsync(VirtualizeQueryOption option) =>
|
||||
App.GetService<IChannelPageService>().OnChannelSelectedItemQueryAsync(option);
|
||||
|
||||
public Task<Dictionary<string, ImportPreviewOutputBase>> ImportChannelAsync(IBrowserFile file, bool restart) =>
|
||||
App.GetService<IChannelPageService>().ImportChannelAsync(file, restart);
|
||||
|
||||
public Task<string> ExportChannelFileAsync(GatewayExportFilter exportFilter) =>
|
||||
App.GetService<IChannelPageService>().ExportChannelFileAsync(exportFilter);
|
||||
|
||||
public Task<string> GetChannelNameAsync(long id) =>
|
||||
App.GetService<IChannelPageService>().GetChannelNameAsync(id);
|
||||
|
||||
public Task SetDeviceLogLevelAsync(long id, LogLevel logLevel) =>
|
||||
App.GetService<IDevicePageService>().SetDeviceLogLevelAsync(id, logLevel);
|
||||
|
||||
public Task CopyDeviceAsync(int CopyCount, string CopyDeviceNamePrefix, int CopyDeviceNameSuffixNumber, long deviceId, bool AutoRestartThread) =>
|
||||
App.GetService<IDevicePageService>().CopyDeviceAsync(CopyCount, CopyDeviceNamePrefix, CopyDeviceNameSuffixNumber, deviceId, AutoRestartThread);
|
||||
|
||||
public Task<LogLevel> DeviceLogLevelAsync(long id) =>
|
||||
App.GetService<IDevicePageService>().DeviceLogLevelAsync(id);
|
||||
|
||||
public Task<bool> BatchEditDeviceAsync(List<Device> models, Device oldModel, Device model, bool restart) =>
|
||||
App.GetService<IDevicePageService>().BatchEditDeviceAsync(models, oldModel, model, restart);
|
||||
|
||||
public Task<bool> SaveDeviceAsync(Device input, ItemChangedType type, bool restart) =>
|
||||
App.GetService<IDevicePageService>().SaveDeviceAsync(input, type, restart);
|
||||
|
||||
public Task<bool> DeleteDeviceAsync(List<long> ids, bool restart) =>
|
||||
App.GetService<IDevicePageService>().DeleteDeviceAsync(ids, restart);
|
||||
|
||||
public Task<Dictionary<string, ImportPreviewOutputBase>> ImportDeviceUSheetDatasAsync(USheetDatas input, bool restart) =>
|
||||
App.GetService<IDevicePageService>().ImportDeviceUSheetDatasAsync(input, restart);
|
||||
|
||||
public Task<USheetDatas> ExportDeviceAsync(List<Device> devices) =>
|
||||
App.GetService<IDevicePageService>().ExportDeviceAsync(devices);
|
||||
|
||||
public Task<string> ExportDeviceFileAsync(GatewayExportFilter exportFilter) =>
|
||||
App.GetService<IDevicePageService>().ExportDeviceFileAsync(exportFilter);
|
||||
|
||||
public Task<QueryData<SelectedItem>> OnRedundantDevicesQueryAsync(VirtualizeQueryOption option, long deviceId, long channelId) =>
|
||||
App.GetService<IDevicePageService>().OnRedundantDevicesQueryAsync(option, deviceId, channelId);
|
||||
|
||||
public Task<Dictionary<string, ImportPreviewOutputBase>> ImportDeviceFileAsync(string filePath, bool restart) =>
|
||||
App.GetService<IDevicePageService>().ImportDeviceFileAsync(filePath, restart);
|
||||
|
||||
public Task DeviceRedundantThreadAsync(long id) =>
|
||||
App.GetService<IDevicePageService>().DeviceRedundantThreadAsync(id);
|
||||
|
||||
public Task RestartDeviceAsync(long id, bool deleteCache) =>
|
||||
App.GetService<IDevicePageService>().RestartDeviceAsync(id, deleteCache);
|
||||
|
||||
public Task PauseThreadAsync(long id) =>
|
||||
App.GetService<IDevicePageService>().PauseThreadAsync(id);
|
||||
|
||||
public Task<QueryData<DeviceRuntime>> OnDeviceQueryAsync(QueryPageOptions options) =>
|
||||
App.GetService<IDevicePageService>().OnDeviceQueryAsync(options);
|
||||
|
||||
public Task<List<Device>> GetDeviceListAsync(QueryPageOptions option, int v) =>
|
||||
App.GetService<IDevicePageService>().GetDeviceListAsync(option, v);
|
||||
|
||||
public Task<bool> ClearDeviceAsync(bool restart) =>
|
||||
App.GetService<IDevicePageService>().ClearDeviceAsync(restart);
|
||||
|
||||
public Task<bool> IsRedundantDeviceAsync(long id) =>
|
||||
App.GetService<IDevicePageService>().IsRedundantDeviceAsync(id);
|
||||
|
||||
public Task<string> GetDeviceNameAsync(long redundantDeviceId) =>
|
||||
App.GetService<IDevicePageService>().GetDeviceNameAsync(redundantDeviceId);
|
||||
|
||||
public Task<Dictionary<string, ImportPreviewOutputBase>> ImportDeviceAsync(IBrowserFile file, bool restart) =>
|
||||
App.GetService<IDevicePageService>().ImportDeviceAsync(file, restart);
|
||||
|
||||
public Task<List<SelectedItem>> GetDeviceItemsAsync(bool isCollect) =>
|
||||
App.GetService<IDevicePageService>().GetDeviceItemsAsync(isCollect);
|
||||
|
||||
public Task<string> GetDevicePluginNameAsync(long id) =>
|
||||
App.GetService<IDevicePageService>().GetDevicePluginNameAsync(id);
|
||||
|
||||
public Task<bool> BatchEditVariableAsync(List<Variable> models, Variable oldModel, Variable model, bool restart) =>
|
||||
App.GetService<IVariablePageService>().BatchEditVariableAsync(models, oldModel, model, restart);
|
||||
|
||||
public Task<bool> DeleteVariableAsync(List<long> ids, bool restart) =>
|
||||
App.GetService<IVariablePageService>().DeleteVariableAsync(ids, restart);
|
||||
|
||||
public Task<bool> ClearVariableAsync(bool restart) =>
|
||||
App.GetService<IVariablePageService>().ClearVariableAsync(restart);
|
||||
|
||||
public Task InsertTestDataAsync(int testVariableCount, int testDeviceCount, string slaveUrl, bool businessEnable, bool restart) =>
|
||||
App.GetService<IVariablePageService>().InsertTestDataAsync(testVariableCount, testDeviceCount, slaveUrl, businessEnable, restart);
|
||||
|
||||
public Task<bool> BatchSaveVariableAsync(List<Variable> input, ItemChangedType type, bool restart) =>
|
||||
App.GetService<IVariablePageService>().BatchSaveVariableAsync(input, type, restart);
|
||||
|
||||
public Task<bool> SaveVariableAsync(Variable input, ItemChangedType type, bool restart) =>
|
||||
App.GetService<IVariablePageService>().SaveVariableAsync(input, type, restart);
|
||||
|
||||
public Task CopyVariableAsync(List<Variable> model, int copyCount, string copyVariableNamePrefix, int copyVariableNameSuffixNumber, bool restart) =>
|
||||
App.GetService<IVariablePageService>().CopyVariableAsync(model, copyCount, copyVariableNamePrefix, copyVariableNameSuffixNumber, restart);
|
||||
|
||||
public Task<QueryData<VariableRuntime>> OnVariableQueryAsync(QueryPageOptions options) =>
|
||||
App.GetService<IVariablePageService>().OnVariableQueryAsync(options);
|
||||
|
||||
public Task<List<Variable>> GetVariableListAsync(QueryPageOptions option, int v) =>
|
||||
App.GetService<IVariablePageService>().GetVariableListAsync(option, v);
|
||||
|
||||
public Task<USheetDatas> ExportVariableAsync(List<Variable> models, string? sortName, SortOrder sortOrder) =>
|
||||
App.GetService<IVariablePageService>().ExportVariableAsync(models, sortName, sortOrder);
|
||||
|
||||
public Task<Dictionary<string, ImportPreviewOutputBase>> ImportVariableUSheetDatasAsync(USheetDatas data, bool restart) =>
|
||||
App.GetService<IVariablePageService>().ImportVariableUSheetDatasAsync(data, restart);
|
||||
|
||||
public Task<OperResult<object>> OnWriteVariableAsync(long id, string writeData) =>
|
||||
App.GetService<IVariablePageService>().OnWriteVariableAsync(id, writeData);
|
||||
|
||||
public Task<Dictionary<string, ImportPreviewOutputBase>> ImportVariableAsync(IBrowserFile a, bool restart) =>
|
||||
App.GetService<IVariablePageService>().ImportVariableAsync(a, restart);
|
||||
|
||||
public Task<Dictionary<string, ImportPreviewOutputBase>> ImportVariableFileAsync(string filePath, bool restart) =>
|
||||
App.GetService<IVariablePageService>().ImportVariableFileAsync(filePath, restart);
|
||||
|
||||
public Task<string> ExportVariableFileAsync(GatewayExportFilter exportFilter) => App.GetService<IVariablePageService>().ExportVariableFileAsync(exportFilter);
|
||||
|
||||
public Task<Dictionary<long, Tuple<string, string>>> GetDeviceIdNamesAsync() => App.GetService<IDevicePageService>().GetDeviceIdNamesAsync();
|
||||
}
|
||||
|
@@ -0,0 +1,68 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://thingsgateway.cn/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using Microsoft.AspNetCore.Components.Forms;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace ThingsGateway.Gateway.Application;
|
||||
public class UpdateZipFileAddInput1
|
||||
{
|
||||
/// <summary>
|
||||
/// zip包
|
||||
/// </summary>
|
||||
[Required]
|
||||
public IFormFile ZipFile { get; set; }
|
||||
/// <summary>
|
||||
/// json
|
||||
/// </summary>
|
||||
[Required]
|
||||
public IFormFile JsonFile { get; set; }
|
||||
}
|
||||
|
||||
public class UpdateZipFileAddInput
|
||||
{
|
||||
/// <summary>
|
||||
/// zip包
|
||||
/// </summary>
|
||||
[Required]
|
||||
public IBrowserFile ZipFile { get; set; }
|
||||
/// <summary>
|
||||
/// json
|
||||
/// </summary>
|
||||
[Required]
|
||||
public IBrowserFile JsonFile { get; set; }
|
||||
}
|
||||
public class UpdateZipFileInput
|
||||
{
|
||||
/// <summary>
|
||||
/// APP名称
|
||||
/// </summary>
|
||||
public string AppName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 版本
|
||||
/// </summary>
|
||||
public Version Version { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// .net版本
|
||||
/// </summary>
|
||||
public Version DotNetVersion { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 系统版本
|
||||
/// </summary>
|
||||
public string OSPlatform { get; set; }
|
||||
|
||||
public Architecture Architecture { get; set; }
|
||||
}
|
@@ -20,5 +20,9 @@ namespace ThingsGateway.Gateway.Application;
|
||||
public interface IUpgradeRpcServer : IRpcServer
|
||||
{
|
||||
[DmtpRpc]
|
||||
Task Upgrade(ICallContext callContext, List<UpdateZipFile> updateZipFiles);
|
||||
Task UpgradeAsync(ICallContext callContext, UpdateZipFile updateZipFile);
|
||||
|
||||
[DmtpRpc]
|
||||
Task<UpdateZipFileInput> GetUpdateZipFileInputAsync(ICallContext callContext);
|
||||
|
||||
}
|
@@ -8,6 +8,9 @@
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
using ThingsGateway.NewLife;
|
||||
|
||||
using TouchSocket.Dmtp;
|
||||
@@ -20,10 +23,24 @@ public partial class UpgradeRpcServer : IRpcServer, IUpgradeRpcServer
|
||||
{
|
||||
|
||||
[DmtpRpc]
|
||||
public async Task Upgrade(ICallContext callContext, List<UpdateZipFile> updateZipFiles)
|
||||
public async Task UpgradeAsync(ICallContext callContext, UpdateZipFile updateZipFile)
|
||||
{
|
||||
if (updateZipFiles?.Count > 0 && callContext.Caller is IDmtpActorObject dmtpActorObject)
|
||||
await Update(dmtpActorObject.DmtpActor, updateZipFiles.OrderByDescending(a => a.Version).FirstOrDefault()).ConfigureAwait(false);
|
||||
if (callContext.Caller is IDmtpActorObject dmtpActorObject)
|
||||
await Update(dmtpActorObject.DmtpActor, updateZipFile).ConfigureAwait(false);
|
||||
}
|
||||
[DmtpRpc]
|
||||
public Task<UpdateZipFileInput> GetUpdateZipFileInputAsync(ICallContext callContext)
|
||||
{
|
||||
return Task.FromResult(new UpdateZipFileInput()
|
||||
{
|
||||
Version = Assembly.GetEntryAssembly().GetName().Version,
|
||||
DotNetVersion = Environment.Version,
|
||||
OSPlatform = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "Windows" :
|
||||
RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ? "Linux" :
|
||||
RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? "OSX" : "Unknown",
|
||||
Architecture = RuntimeInformation.ProcessArchitecture,
|
||||
AppName = "ThingsGateway"
|
||||
});
|
||||
}
|
||||
|
||||
public static async Task Update(IDmtpActor dmtpActor, UpdateZipFile updateZipFile, Func<Task<bool>> check = null)
|
||||
@@ -65,7 +82,6 @@ public partial class UpgradeRpcServer : IRpcServer, IUpgradeRpcServer
|
||||
|
||||
|
||||
|
||||
|
||||
private static readonly WaitLock WaitLock = new(nameof(ManagementTask));
|
||||
private static readonly WaitLock UpdateWaitLock = new(nameof(ManagementTask));
|
||||
}
|
||||
|
@@ -138,8 +138,8 @@ internal sealed partial class RedundantRpcServer : SingletonRpcServer, IRedundan
|
||||
}
|
||||
}
|
||||
|
||||
await GlobalData.ChannelRuntimeService.InsertAsync(addChannels, addDevices, addVariables, true, default).ConfigureAwait(false);
|
||||
await GlobalData.ChannelRuntimeService.UpdateAsync(upChannels, upDevices, upVariables, true, default).ConfigureAwait(false);
|
||||
await GlobalData.ChannelRuntimeService.InsertAsync(addChannels, addDevices, addVariables, true).ConfigureAwait(false);
|
||||
await GlobalData.ChannelRuntimeService.UpdateAsync(upChannels, upDevices, upVariables, true).ConfigureAwait(false);
|
||||
|
||||
RedundancyTask.LogMessage?.LogTrace($"Sync data success");
|
||||
}
|
||||
|
@@ -15,19 +15,22 @@ namespace ThingsGateway.Gateway.Application;
|
||||
/// <summary>
|
||||
/// 驱动插件服务
|
||||
/// </summary>
|
||||
public interface IPluginService : IPluginPageService
|
||||
public interface IPluginService
|
||||
#if !Management
|
||||
: IPluginPageService
|
||||
#endif
|
||||
{
|
||||
Type GetDebugUI(string pluginName);
|
||||
Type GetAddressUI(string pluginName);
|
||||
|
||||
|
||||
#if !Management
|
||||
/// <summary>
|
||||
/// 根据插件类型获取信息
|
||||
/// </summary>
|
||||
/// <param name="pluginType"></param>
|
||||
/// <returns></returns>
|
||||
List<PluginInfo> GetPluginList(PluginTypeEnum? pluginType = null);
|
||||
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// 根据插件全名称构建插件实例
|
||||
|
@@ -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(GetPluginList)}";
|
||||
private const string CacheKeyGetPluginOutputs = $"ThingsGateway.Gateway.Application.{nameof(PluginService)}GetList";
|
||||
private const string SaveEx = ".save";
|
||||
private const string DelEx = ".del";
|
||||
|
||||
@@ -166,7 +166,7 @@ internal sealed class PluginService : IPluginService
|
||||
{
|
||||
|
||||
{
|
||||
string cacheKey = $"{nameof(PluginService)}_{nameof(GetDriverMethodInfos)}_{CultureInfo.CurrentUICulture.Name}";
|
||||
string cacheKey = $"{nameof(PluginService)}_{nameof(GetDriverMethodInfos)}_{System.Globalization.CultureInfo.CurrentUICulture.Name}";
|
||||
// 如果未提供驱动基类对象,则尝试根据插件名称获取驱动对象
|
||||
driver ??= GetDriver(pluginName); // 如果未提供驱动对象,则根据插件名称获取驱动对象
|
||||
|
||||
@@ -222,7 +222,7 @@ internal sealed class PluginService : IPluginService
|
||||
{
|
||||
|
||||
{
|
||||
string cacheKey = $"{nameof(PluginService)}_{nameof(GetDriverPropertyTypes)}_{CultureInfo.CurrentUICulture.Name}";
|
||||
string cacheKey = $"{nameof(PluginService)}_{nameof(GetDriverPropertyTypes)}_{System.Globalization.CultureInfo.CurrentUICulture.Name}";
|
||||
|
||||
driver ??= GetDriver(pluginName); // 如果 driver 为 null, 获取驱动实例
|
||||
// 检查插件名称是否为空或空字符串
|
||||
@@ -253,7 +253,7 @@ internal sealed class PluginService : IPluginService
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if !Management
|
||||
/// <summary>
|
||||
/// 获取插件信息的方法,可以根据插件类型筛选插件列表。
|
||||
/// </summary>
|
||||
@@ -297,6 +297,9 @@ internal sealed class PluginService : IPluginService
|
||||
|
||||
return filteredPlugins;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// 获取变量的属性类型
|
||||
/// </summary>
|
||||
@@ -304,7 +307,7 @@ internal sealed class PluginService : IPluginService
|
||||
{
|
||||
|
||||
{
|
||||
string cacheKey = $"{nameof(PluginService)}_{nameof(GetVariablePropertyTypes)}_{CultureInfo.CurrentUICulture.Name}";
|
||||
string cacheKey = $"{nameof(PluginService)}_{nameof(GetVariablePropertyTypes)}_{System.Globalization.CultureInfo.CurrentUICulture.Name}";
|
||||
businessBase ??= (BusinessBase)GetDriver(pluginName); // 如果 driver 为 null, 获取驱动实例
|
||||
|
||||
var data = App.CacheService.HashGetAll<List<IEditorItem>>(cacheKey);
|
||||
@@ -327,6 +330,7 @@ internal sealed class PluginService : IPluginService
|
||||
}
|
||||
}
|
||||
|
||||
#if !Management
|
||||
/// <summary>
|
||||
/// 分页显示插件
|
||||
/// </summary>
|
||||
@@ -336,6 +340,7 @@ internal sealed class PluginService : IPluginService
|
||||
var query = (await GetPluginsAsync(pluginType).ConfigureAwait(false)).WhereIf(!options.SearchText.IsNullOrWhiteSpace(), a => a.FullName.Contains(options.SearchText)).GetQueryData(options);
|
||||
return query;
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// 移除全部插件
|
||||
@@ -729,7 +734,7 @@ internal sealed class PluginService : IPluginService
|
||||
}
|
||||
return assembly;
|
||||
}
|
||||
|
||||
#if !Management
|
||||
/// <summary>
|
||||
/// 获取全部插件信息
|
||||
/// </summary>
|
||||
@@ -848,7 +853,7 @@ internal sealed class PluginService : IPluginService
|
||||
return plugins.DistinctBy(a => a.FullName).OrderBy(a => a.EducationPlugin);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
//public Task SavePlugin(PluginAddInput plugin)
|
||||
//{
|
||||
// return PluginInfoUtil.SavePlugin(plugin);
|
||||
|
@@ -44,7 +44,7 @@ public static class PluginServiceUtil
|
||||
if (classAttribute == null) continue;//删除不需要显示的属性
|
||||
|
||||
var displayName = classAttribute.Description ?? BootstrapBlazor.Components.Utility.GetDisplayName(type, prop.Name);
|
||||
InternalTableColumn tc = new InternalTableColumn(prop.Name, prop.PropertyType, displayName);
|
||||
DefaultTableColumn tc = new DefaultTableColumn(prop.Name, prop.PropertyType, displayName);
|
||||
if (autoGenerateColumnAttribute != null)
|
||||
IEditItemExtensions.CopyValue(tc, autoGenerateColumnAttribute);
|
||||
tc.ComponentParameters ??= new Dictionary<string, object>();
|
||||
@@ -74,6 +74,7 @@ public static class PluginServiceUtil
|
||||
return type.GetRuntimeProperties().Any(a => a.GetCustomAttribute<DynamicPropertyAttribute>(false) != null);
|
||||
}
|
||||
|
||||
#if !Management
|
||||
/// <summary>
|
||||
/// 插件是否支持平台
|
||||
/// </summary>
|
||||
@@ -86,6 +87,7 @@ public static class PluginServiceUtil
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// 插件是否专业版
|
||||
|
@@ -257,7 +257,7 @@ internal static class RuntimeServiceHelper
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task ChangedDriverAsync(ILogger logger, CancellationToken cancellationToken)
|
||||
public static async Task ChangedDriverAsync(ILogger logger)
|
||||
{
|
||||
var channelDevice = GlobalData.IdDevices.Where(a => a.Value.Driver?.DriverProperties is IBusinessPropertyAllVariableBase property && property.IsAllVariable).Select(a => a.Value).ToArray();
|
||||
|
||||
@@ -271,9 +271,9 @@ internal static class RuntimeServiceHelper
|
||||
{
|
||||
logger.LogWarning(ex, "VariablesChanged");
|
||||
}
|
||||
}, cancellationToken).ConfigureAwait(false);
|
||||
}).ConfigureAwait(false);
|
||||
}
|
||||
public static async Task ChangedDriverAsync(ConcurrentHashSet<IDriver> changedDriver, ILogger logger, CancellationToken cancellationToken)
|
||||
public static async Task ChangedDriverAsync(ConcurrentHashSet<IDriver> changedDriver, ILogger logger)
|
||||
{
|
||||
var drivers = GlobalData.IdDevices.Where(a => a.Value.Driver?.DriverProperties is IBusinessPropertyAllVariableBase property && property.IsAllVariable).Select(a => a.Value.Driver);
|
||||
|
||||
@@ -288,7 +288,7 @@ internal static class RuntimeServiceHelper
|
||||
{
|
||||
logger.LogWarning(ex, "VariablesChanged");
|
||||
}
|
||||
}, cancellationToken).ConfigureAwait(false);
|
||||
}).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public static void AddBusinessChangedDriver(HashSet<long> variableIds, ConcurrentHashSet<IDriver> changedDriver)
|
||||
|
@@ -0,0 +1,39 @@
|
||||
// ------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人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 IVariablePageService
|
||||
{
|
||||
Task<bool> BatchEditVariableAsync(List<Variable> models, Variable oldModel, Variable model, bool restart);
|
||||
Task<bool> DeleteVariableAsync(List<long> ids, bool restart);
|
||||
Task<bool> ClearVariableAsync(bool restart);
|
||||
Task InsertTestDataAsync(int testVariableCount, int testDeviceCount, string slaveUrl, bool businessEnable, bool restart);
|
||||
|
||||
Task<bool> BatchSaveVariableAsync(List<Variable> input, ItemChangedType type, bool restart);
|
||||
|
||||
Task<bool> SaveVariableAsync(Variable input, ItemChangedType type, bool restart);
|
||||
Task CopyVariableAsync(List<Variable> Model, int CopyCount, string CopyVariableNamePrefix, int CopyVariableNameSuffixNumber, bool AutoRestartThread);
|
||||
Task<QueryData<VariableRuntime>> OnVariableQueryAsync(QueryPageOptions options);
|
||||
Task<List<Variable>> GetVariableListAsync(QueryPageOptions option, int v);
|
||||
Task<USheetDatas> ExportVariableAsync(List<Variable> models, string? sortName, SortOrder sortOrder);
|
||||
Task<Dictionary<string, ImportPreviewOutputBase>> ImportVariableUSheetDatasAsync(USheetDatas data, bool restart);
|
||||
|
||||
Task<string> ExportVariableFileAsync(GatewayExportFilter exportFilter);
|
||||
|
||||
Task<OperResult<object>> OnWriteVariableAsync(long id, string writeData);
|
||||
Task<Dictionary<string, ImportPreviewOutputBase>> ImportVariableAsync(IBrowserFile a, bool restart);
|
||||
Task<Dictionary<string, ImportPreviewOutputBase>> ImportVariableFileAsync(string filePath, bool restart);
|
||||
}
|
||||
}
|
@@ -8,28 +8,18 @@
|
||||
// QQ群:605534569
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
using BootstrapBlazor.Components;
|
||||
|
||||
using Microsoft.AspNetCore.Components.Forms;
|
||||
|
||||
namespace ThingsGateway.Gateway.Application
|
||||
{
|
||||
public interface IVariableRuntimeService
|
||||
public interface IVariableRuntimeService : IVariablePageService
|
||||
{
|
||||
Task<bool> BatchEditAsync(IEnumerable<Variable> models, Variable oldModel, Variable model, bool restart, CancellationToken cancellationToken);
|
||||
Task<bool> DeleteVariableAsync(IEnumerable<long> ids, bool restart, CancellationToken cancellationToken);
|
||||
Task<bool> ClearVariableAsync(bool restart, CancellationToken cancellationToken);
|
||||
Task<Dictionary<string, object>> ExportVariableAsync(GatewayExportFilter exportFilter);
|
||||
|
||||
Task ImportVariableAsync(Dictionary<string, ImportPreviewOutputBase> input, bool restart, CancellationToken cancellationToken);
|
||||
Task InsertTestDataAsync(int testVariableCount, int testDeviceCount, string slaveUrl, bool businessEnable, bool restart, CancellationToken cancellationToken);
|
||||
|
||||
Task<bool> BatchSaveVariableAsync(List<Variable> input, ItemChangedType type, bool restart, CancellationToken cancellationToken);
|
||||
Task ImportVariableAsync(Dictionary<string, ImportPreviewOutputBase> input, bool restart);
|
||||
|
||||
Task<Dictionary<string, ImportPreviewOutputBase>> PreviewAsync(IBrowserFile browserFile);
|
||||
|
||||
Task<bool> SaveVariableAsync(Variable input, ItemChangedType type, bool restart, CancellationToken cancellationToken);
|
||||
|
||||
Task<MemoryStream> ExportMemoryStream(List<Variable> data, string devName);
|
||||
}
|
||||
}
|
@@ -108,4 +108,6 @@ internal interface IVariableService
|
||||
void DeleteVariableCache();
|
||||
ImportPreviewOutput<Dictionary<string, Variable>> SetVariableData(HashSet<long>? dataScope, IReadOnlyDictionary<string, DeviceRuntime> deviceDicts, Dictionary<string, ImportPreviewOutputBase> ImportPreviews, ImportPreviewOutput<Dictionary<string, Variable>> deviceImportPreview, Dictionary<string, PluginInfo> driverPluginNameDict, ConcurrentDictionary<string, (Type, Dictionary<string, PropertyInfo>, Dictionary<string, PropertyInfo>)> propertysDict, string sheetName, IEnumerable<IDictionary<string, object>> rows);
|
||||
List<VariableRuntime> GetAllVariableRuntime();
|
||||
Task<Dictionary<string, ImportPreviewOutputBase>> PreviewAsync(string filePath);
|
||||
Task<HashSet<long>> ImportVariableAsync(List<Variable> upData, List<Variable> insertData);
|
||||
}
|
||||
|
@@ -13,7 +13,9 @@ using BootstrapBlazor.Components;
|
||||
using Microsoft.AspNetCore.Components.Forms;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
using ThingsGateway.Extension.Generic;
|
||||
using ThingsGateway.NewLife.Collections;
|
||||
using ThingsGateway.NewLife.Extension;
|
||||
|
||||
namespace ThingsGateway.Gateway.Application;
|
||||
|
||||
@@ -26,7 +28,68 @@ public class VariableRuntimeService : IVariableRuntimeService
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task<bool> BatchSaveVariableAsync(List<Variable> input, ItemChangedType type, bool restart, CancellationToken cancellationToken)
|
||||
|
||||
public Task<QueryData<VariableRuntime>> OnVariableQueryAsync(QueryPageOptions options)
|
||||
{
|
||||
var data = GlobalData.IdVariables.Select(a => a.Value)
|
||||
.WhereIf(!options.SearchText.IsNullOrWhiteSpace(), a => a.Name.Contains(options.SearchText))
|
||||
.GetQueryData(options);
|
||||
return Task.FromResult(data);
|
||||
}
|
||||
|
||||
public Task<List<Variable>> GetVariableListAsync(QueryPageOptions option, int max)
|
||||
{
|
||||
var models = GlobalData.IdVariables.Select(a => a.Value)
|
||||
.WhereIf(!option.SearchText.IsNullOrWhiteSpace(), a => a.Name.Contains(option.SearchText))
|
||||
.GetData(option, out var total).Cast<Variable>().ToList();
|
||||
|
||||
if (max > 0 && models.Count > max)
|
||||
{
|
||||
throw new("online Excel max data count 2000");
|
||||
}
|
||||
return Task.FromResult(models);
|
||||
|
||||
}
|
||||
|
||||
public Task<USheetDatas> ExportVariableAsync(List<Variable> models, string? sortName, SortOrder sortOrder)
|
||||
{
|
||||
return Task.FromResult(VariableServiceHelpers.ExportVariable(models, sortName, sortOrder));
|
||||
}
|
||||
|
||||
|
||||
|
||||
public async Task<OperResult<object>> OnWriteVariableAsync(long id, string writeData)
|
||||
{
|
||||
if (GlobalData.IdVariables.TryGetValue(id, out var variableRuntime))
|
||||
{
|
||||
var data = await variableRuntime.RpcAsync(writeData).ConfigureAwait(false);
|
||||
return data.GetOperResult();
|
||||
}
|
||||
return new OperResult<object>($"Variable with ID {id} not found.");
|
||||
}
|
||||
|
||||
|
||||
|
||||
public async Task CopyVariableAsync(List<Variable> Model, int CopyCount, string CopyVariableNamePrefix, int CopyVariableNameSuffixNumber, bool AutoRestartThread)
|
||||
{
|
||||
|
||||
List<Variable> variables = new();
|
||||
for (int i = 0; i < CopyCount; i++)
|
||||
{
|
||||
var variable = Model.AdaptListVariable();
|
||||
foreach (var item in variable)
|
||||
{
|
||||
item.Id = CommonUtils.GetSingleId();
|
||||
item.Name = $"{CopyVariableNamePrefix}{CopyVariableNameSuffixNumber + i}";
|
||||
variables.Add(item);
|
||||
}
|
||||
}
|
||||
await BatchSaveVariableAsync(variables, ItemChangedType.Add, AutoRestartThread).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public async Task<bool> BatchSaveVariableAsync(List<Variable> input, ItemChangedType type, bool restart)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -46,7 +109,7 @@ public class VariableRuntimeService : IVariableRuntimeService
|
||||
if (restart)
|
||||
{
|
||||
//根据条件重启通道线程
|
||||
await RuntimeServiceHelper.ChangedDriverAsync(changedDriver, _logger, cancellationToken).ConfigureAwait(false);
|
||||
await RuntimeServiceHelper.ChangedDriverAsync(changedDriver, _logger).ConfigureAwait(false);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -56,7 +119,7 @@ public class VariableRuntimeService : IVariableRuntimeService
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<bool> BatchEditAsync(IEnumerable<Variable> models, Variable oldModel, Variable model, bool restart, CancellationToken cancellationToken)
|
||||
public async Task<bool> BatchEditVariableAsync(List<Variable> models, Variable oldModel, Variable model, bool restart)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -67,7 +130,7 @@ public class VariableRuntimeService : IVariableRuntimeService
|
||||
using var db = DbContext.GetDB<Variable>();
|
||||
var ids = models.Select(a => a.Id).ToHashSet();
|
||||
|
||||
var newVariableRuntimes = (await db.Queryable<Variable>().Where(a => ids.Contains(a.Id)).ToListAsync(cancellationToken).ConfigureAwait(false)).AdaptListVariableRuntime();
|
||||
var newVariableRuntimes = (await db.Queryable<Variable>().Where(a => ids.Contains(a.Id)).ToListAsync().ConfigureAwait(false)).AdaptListVariableRuntime();
|
||||
|
||||
var variableIds = newVariableRuntimes.Select(a => a.Id).ToHashSet();
|
||||
|
||||
@@ -80,7 +143,7 @@ public class VariableRuntimeService : IVariableRuntimeService
|
||||
if (restart)
|
||||
{
|
||||
//根据条件重启通道线程
|
||||
await RuntimeServiceHelper.ChangedDriverAsync(changedDriver, _logger, cancellationToken).ConfigureAwait(false);
|
||||
await RuntimeServiceHelper.ChangedDriverAsync(changedDriver, _logger).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -91,7 +154,7 @@ public class VariableRuntimeService : IVariableRuntimeService
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<bool> DeleteVariableAsync(IEnumerable<long> ids, bool restart, CancellationToken cancellationToken)
|
||||
public async Task<bool> DeleteVariableAsync(List<long> ids, bool restart)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -108,7 +171,7 @@ public class VariableRuntimeService : IVariableRuntimeService
|
||||
|
||||
if (restart)
|
||||
{
|
||||
await RuntimeServiceHelper.ChangedDriverAsync(changedDriver, _logger, cancellationToken).ConfigureAwait(false);
|
||||
await RuntimeServiceHelper.ChangedDriverAsync(changedDriver, _logger).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -119,7 +182,7 @@ public class VariableRuntimeService : IVariableRuntimeService
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<bool> ClearVariableAsync(bool restart, CancellationToken cancellationToken)
|
||||
public async Task<bool> ClearVariableAsync(bool restart)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -134,7 +197,7 @@ public class VariableRuntimeService : IVariableRuntimeService
|
||||
|
||||
if (restart)
|
||||
{
|
||||
await RuntimeServiceHelper.ChangedDriverAsync(changedDriver, _logger, cancellationToken).ConfigureAwait(false);
|
||||
await RuntimeServiceHelper.ChangedDriverAsync(changedDriver, _logger).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -147,7 +210,7 @@ public class VariableRuntimeService : IVariableRuntimeService
|
||||
|
||||
public Task<Dictionary<string, object>> ExportVariableAsync(GatewayExportFilter exportFilter) => GlobalData.VariableService.ExportVariableAsync(exportFilter);
|
||||
|
||||
public async Task ImportVariableAsync(Dictionary<string, ImportPreviewOutputBase> input, bool restart, CancellationToken cancellationToken)
|
||||
public async Task ImportVariableAsync(Dictionary<string, ImportPreviewOutputBase> input, bool restart)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -156,7 +219,7 @@ public class VariableRuntimeService : IVariableRuntimeService
|
||||
var result = await GlobalData.VariableService.ImportVariableAsync(input).ConfigureAwait(false);
|
||||
|
||||
using var db = DbContext.GetDB<Variable>();
|
||||
var newVariableRuntimes = (await db.Queryable<Variable>().Where(a => result.Contains(a.Id)).ToListAsync(cancellationToken).ConfigureAwait(false)).AdaptListVariableRuntime();
|
||||
var newVariableRuntimes = (await db.Queryable<Variable>().Where(a => result.Contains(a.Id)).ToListAsync().ConfigureAwait(false)).AdaptListVariableRuntime();
|
||||
|
||||
var variableIds = newVariableRuntimes.Select(a => a.Id).ToHashSet();
|
||||
|
||||
@@ -168,7 +231,7 @@ public class VariableRuntimeService : IVariableRuntimeService
|
||||
if (restart)
|
||||
{
|
||||
//根据条件重启通道线程
|
||||
await RuntimeServiceHelper.ChangedDriverAsync(changedDriver, _logger, cancellationToken).ConfigureAwait(false);
|
||||
await RuntimeServiceHelper.ChangedDriverAsync(changedDriver, _logger).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -177,8 +240,114 @@ public class VariableRuntimeService : IVariableRuntimeService
|
||||
//WaitLock.Release();
|
||||
}
|
||||
}
|
||||
public async Task<Dictionary<string, ImportPreviewOutputBase>> ImportVariableUSheetDatasAsync(USheetDatas uSheetDatas, bool restart)
|
||||
{
|
||||
try
|
||||
{
|
||||
// await WaitLock.WaitAsync().ConfigureAwait(false);
|
||||
|
||||
public async Task InsertTestDataAsync(int testVariableCount, int testDeviceCount, string slaveUrl, bool businessEnable, bool restart, CancellationToken cancellationToken)
|
||||
var data = await VariableServiceHelpers.ImportAsync(uSheetDatas).ConfigureAwait(false);
|
||||
|
||||
if (data.Any(a => a.Value.HasError)) return data;
|
||||
|
||||
var result = await GlobalData.VariableService.ImportVariableAsync((Dictionary<string, ImportPreviewOutputBase>)data).ConfigureAwait(false);
|
||||
|
||||
using var db = DbContext.GetDB<Variable>();
|
||||
var newVariableRuntimes = (await db.Queryable<Variable>().Where(a => result.Contains(a.Id)).ToListAsync().ConfigureAwait(false)).AdaptListVariableRuntime();
|
||||
|
||||
var variableIds = newVariableRuntimes.Select(a => a.Id).ToHashSet();
|
||||
|
||||
ConcurrentHashSet<IDriver> changedDriver = new();
|
||||
RuntimeServiceHelper.VariableRuntimesDispose(variableIds);
|
||||
RuntimeServiceHelper.AddCollectChangedDriver(newVariableRuntimes, changedDriver);
|
||||
RuntimeServiceHelper.AddBusinessChangedDriver(variableIds, changedDriver);
|
||||
|
||||
if (restart)
|
||||
{
|
||||
//根据条件重启通道线程
|
||||
await RuntimeServiceHelper.ChangedDriverAsync(changedDriver, _logger).ConfigureAwait(false);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
finally
|
||||
{
|
||||
//WaitLock.Release();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
public async Task<Dictionary<string, ImportPreviewOutputBase>> ImportVariableAsync(IBrowserFile file, bool restart)
|
||||
{
|
||||
try
|
||||
{
|
||||
// await WaitLock.WaitAsync().ConfigureAwait(false);
|
||||
|
||||
var data = await GlobalData.VariableService.PreviewAsync(file).ConfigureAwait(false);
|
||||
|
||||
if (data.Any(a => a.Value.HasError)) return data;
|
||||
|
||||
var result = await GlobalData.VariableService.ImportVariableAsync(data).ConfigureAwait(false);
|
||||
|
||||
using var db = DbContext.GetDB<Variable>();
|
||||
var newVariableRuntimes = (await db.Queryable<Variable>().Where(a => result.Contains(a.Id)).ToListAsync().ConfigureAwait(false)).AdaptListVariableRuntime();
|
||||
|
||||
var variableIds = newVariableRuntimes.Select(a => a.Id).ToHashSet();
|
||||
|
||||
ConcurrentHashSet<IDriver> changedDriver = new();
|
||||
RuntimeServiceHelper.VariableRuntimesDispose(variableIds);
|
||||
RuntimeServiceHelper.AddCollectChangedDriver(newVariableRuntimes, changedDriver);
|
||||
RuntimeServiceHelper.AddBusinessChangedDriver(variableIds, changedDriver);
|
||||
|
||||
if (restart)
|
||||
{
|
||||
//根据条件重启通道线程
|
||||
await RuntimeServiceHelper.ChangedDriverAsync(changedDriver, _logger).ConfigureAwait(false);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
finally
|
||||
{
|
||||
//WaitLock.Release();
|
||||
}
|
||||
}
|
||||
public async Task<Dictionary<string, ImportPreviewOutputBase>> ImportVariableFileAsync(string filePath, bool restart)
|
||||
{
|
||||
try
|
||||
{
|
||||
// await WaitLock.WaitAsync().ConfigureAwait(false);
|
||||
|
||||
var data = await GlobalData.VariableService.PreviewAsync(filePath).ConfigureAwait(false);
|
||||
|
||||
if (data.Any(a => a.Value.HasError)) return data;
|
||||
|
||||
var result = await GlobalData.VariableService.ImportVariableAsync(data).ConfigureAwait(false);
|
||||
|
||||
using var db = DbContext.GetDB<Variable>();
|
||||
var newVariableRuntimes = (await db.Queryable<Variable>().Where(a => result.Contains(a.Id)).ToListAsync().ConfigureAwait(false)).AdaptListVariableRuntime();
|
||||
|
||||
var variableIds = newVariableRuntimes.Select(a => a.Id).ToHashSet();
|
||||
|
||||
ConcurrentHashSet<IDriver> changedDriver = new();
|
||||
RuntimeServiceHelper.VariableRuntimesDispose(variableIds);
|
||||
RuntimeServiceHelper.AddCollectChangedDriver(newVariableRuntimes, changedDriver);
|
||||
RuntimeServiceHelper.AddBusinessChangedDriver(variableIds, changedDriver);
|
||||
|
||||
if (restart)
|
||||
{
|
||||
//根据条件重启通道线程
|
||||
await RuntimeServiceHelper.ChangedDriverAsync(changedDriver, _logger).ConfigureAwait(false);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
finally
|
||||
{
|
||||
//WaitLock.Release();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public async Task InsertTestDataAsync(int testVariableCount, int testDeviceCount, string slaveUrl, bool businessEnable, bool restart)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -207,7 +376,7 @@ public class VariableRuntimeService : IVariableRuntimeService
|
||||
{
|
||||
await GlobalData.ChannelThreadManage.RestartChannelAsync(newChannelRuntimes).ConfigureAwait(false);
|
||||
|
||||
await RuntimeServiceHelper.ChangedDriverAsync(_logger, cancellationToken).ConfigureAwait(false);
|
||||
await RuntimeServiceHelper.ChangedDriverAsync(_logger).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -222,7 +391,7 @@ public class VariableRuntimeService : IVariableRuntimeService
|
||||
return GlobalData.VariableService.PreviewAsync(browserFile);
|
||||
}
|
||||
|
||||
public async Task<bool> SaveVariableAsync(Variable input, ItemChangedType type, bool restart, CancellationToken cancellationToken)
|
||||
public async Task<bool> SaveVariableAsync(Variable input, ItemChangedType type, bool restart)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -231,7 +400,7 @@ public class VariableRuntimeService : IVariableRuntimeService
|
||||
var result = await GlobalData.VariableService.SaveVariableAsync(input, type).ConfigureAwait(false);
|
||||
|
||||
using var db = DbContext.GetDB<Variable>();
|
||||
var newVariableRuntimes = (await db.Queryable<Variable>().Where(a => a.Id == input.Id).ToListAsync(cancellationToken).ConfigureAwait(false)).AdaptListVariableRuntime();
|
||||
var newVariableRuntimes = (await db.Queryable<Variable>().Where(a => a.Id == input.Id).ToListAsync().ConfigureAwait(false)).AdaptListVariableRuntime();
|
||||
|
||||
var variableIds = newVariableRuntimes.Select(a => a.Id).ToHashSet();
|
||||
|
||||
@@ -244,7 +413,7 @@ public class VariableRuntimeService : IVariableRuntimeService
|
||||
if (restart)
|
||||
{
|
||||
//根据条件重启通道线程
|
||||
await RuntimeServiceHelper.ChangedDriverAsync(changedDriver, _logger, cancellationToken).ConfigureAwait(false);
|
||||
await RuntimeServiceHelper.ChangedDriverAsync(changedDriver, _logger).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -257,4 +426,9 @@ public class VariableRuntimeService : IVariableRuntimeService
|
||||
|
||||
public Task<MemoryStream> ExportMemoryStream(List<Variable> data, string deviceName) => GlobalData.VariableService.ExportMemoryStream(data, deviceName);
|
||||
|
||||
public async Task<string> ExportVariableFileAsync(GatewayExportFilter exportFilter)
|
||||
{
|
||||
var sheets = await GlobalData.VariableService.ExportVariableAsync(exportFilter).ConfigureAwait(false);
|
||||
return await App.GetService<IImportExportService>().CreateFileAsync<Variable>(sheets, "Variable", false).ConfigureAwait(false);
|
||||
}
|
||||
}
|
@@ -558,6 +558,12 @@ internal sealed class VariableService : BaseService<Variable>, IVariableService
|
||||
}
|
||||
var upData = variables.Where(a => a.IsUp).ToList();
|
||||
var insertData = variables.Where(a => !a.IsUp).ToList();
|
||||
return await ImportVariableAsync(upData, insertData).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[OperDesc("ImportVariable", isRecordPar: false, localizerType: typeof(Variable))]
|
||||
public async Task<HashSet<long>> ImportVariableAsync(List<Variable> upData, List<Variable> insertData)
|
||||
{
|
||||
ManageHelper.CheckVariableCount(insertData.Count);
|
||||
using var db = GetDB();
|
||||
if (GlobalData.HardwareJob.HardwareInfo.MachineInfo.AvailableMemory > 2 * 1024 * 1024)
|
||||
@@ -571,13 +577,19 @@ internal sealed class VariableService : BaseService<Variable>, IVariableService
|
||||
await db.BulkUpdateAsync(upData, 10000).ConfigureAwait(false);
|
||||
}
|
||||
DeleteVariableCache();
|
||||
return variables.Select(a => a.Id).ToHashSet();
|
||||
return upData.Select(a => a.Id).Concat(insertData.Select(a => a.Id)).ToHashSet();
|
||||
}
|
||||
|
||||
public async Task<Dictionary<string, ImportPreviewOutputBase>> PreviewAsync(IBrowserFile browserFile)
|
||||
{
|
||||
var path = await browserFile.StorageLocal().ConfigureAwait(false); // 上传文件并获取文件路径
|
||||
|
||||
return await PreviewAsync(path).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task<Dictionary<string, ImportPreviewOutputBase>> PreviewAsync(string path)
|
||||
{
|
||||
// 上传文件并获取文件路径
|
||||
var path = await browserFile.StorageLocal().ConfigureAwait(false);
|
||||
var dataScope = await GlobalData.SysUserService.GetCurrentUserDataScopeAsync().ConfigureAwait(false);
|
||||
|
||||
try
|
||||
|
@@ -76,8 +76,10 @@ public class Startup : AppStartup
|
||||
services.AddSingleton<IChannelRuntimeService, ChannelRuntimeService>();
|
||||
services.AddSingleton<IVariableService, VariableService>();
|
||||
services.AddSingleton<IVariableRuntimeService, VariableRuntimeService>();
|
||||
services.AddSingleton<IVariablePageService>(a => a.GetService<IVariableRuntimeService>());
|
||||
services.AddSingleton<IDeviceService, DeviceService>();
|
||||
services.AddSingleton<IDeviceRuntimeService, DeviceRuntimeService>();
|
||||
services.AddSingleton<IDevicePageService>(a => a.GetService<IDeviceRuntimeService>());
|
||||
services.AddSingleton<IPluginService, PluginService>();
|
||||
services.AddSingleton<IPluginPageService>(a => a.GetService<IPluginService>());
|
||||
|
||||
|
@@ -4,6 +4,7 @@
|
||||
<Import Project="..\..\PackNuget.props" />
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net8.0;</TargetFrameworks>
|
||||
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Portable.BouncyCastle" Version="1.9.0" />
|
||||
|
@@ -59,7 +59,7 @@ public static class GatewayResourceUtil
|
||||
).ToList();
|
||||
return data;
|
||||
}
|
||||
|
||||
#if !Management
|
||||
/// <summary>
|
||||
/// 构建树节点,传入的列表已经是树结构
|
||||
/// </summary>
|
||||
@@ -81,168 +81,5 @@ public static class GatewayResourceUtil
|
||||
}
|
||||
return trees;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 构建树节点,传入的列表已经是树结构
|
||||
/// </summary>
|
||||
public static List<TreeViewItem<ChannelDeviceTreeItem>> BuildTreeItemList(this IEnumerable<ChannelRuntime> channelRuntimes, List<ChannelDeviceTreeItem> selectedItems, Microsoft.AspNetCore.Components.RenderFragment<ChannelDeviceTreeItem> render = null, TreeViewItem<ChannelDeviceTreeItem>? parent = null, List<TreeViewItem<ChannelDeviceTreeItem>> items = null)
|
||||
{
|
||||
if (channelRuntimes == null) return null;
|
||||
items ??= new();
|
||||
var trees = new List<TreeViewItem<ChannelDeviceTreeItem>>();
|
||||
|
||||
//筛选插件名称
|
||||
#pragma warning disable CA1851
|
||||
foreach (var pluginName in channelRuntimes.Select(a => a.PluginName).ToHashSet())
|
||||
{
|
||||
var pluginItemValue = new ChannelDeviceTreeItem() { ChannelDevicePluginType = ChannelDevicePluginTypeEnum.PluginName, PluginName = pluginName };
|
||||
var pluginItem = items.FirstOrDefault(a => a.Value.Equals(pluginItemValue));
|
||||
|
||||
if (pluginItem == null)
|
||||
{
|
||||
pluginItem = new TreeViewItem<ChannelDeviceTreeItem>(pluginItemValue)
|
||||
{
|
||||
Text = PluginInfoUtil.GetFileNameAndTypeName(pluginName).TypeName,
|
||||
IsExpand = true,
|
||||
Parent = parent,
|
||||
};
|
||||
}
|
||||
|
||||
var channelOldItems = pluginItem.Items.ToList();
|
||||
pluginItem.Items.Clear();
|
||||
foreach (var channelRuntime in channelRuntimes.Where(a => a.PluginName == pluginName))
|
||||
{
|
||||
var channelRuntimeTreeItem = new ChannelDeviceTreeItem() { ChannelDevicePluginType = ChannelDevicePluginTypeEnum.Channel, ChannelRuntimeId = channelRuntime.Id, Id = channelRuntime.Id };
|
||||
|
||||
var channelTreeItemItem = channelOldItems.FirstOrDefault(a => a.Value.Equals(channelRuntimeTreeItem));
|
||||
|
||||
if (channelTreeItemItem == null)
|
||||
{
|
||||
channelTreeItemItem = new TreeViewItem<ChannelDeviceTreeItem>(channelRuntimeTreeItem)
|
||||
{
|
||||
Text = channelRuntime.ToString(),
|
||||
Parent = pluginItem,
|
||||
IsExpand = true,
|
||||
IsActive = selectedItems.Contains(channelRuntimeTreeItem),
|
||||
Template = render,
|
||||
};
|
||||
}
|
||||
|
||||
var deviceOldItems = channelTreeItemItem.Items.ToList();
|
||||
channelTreeItemItem.Items.Clear();
|
||||
|
||||
foreach (var keyValue in channelRuntime.ReadDeviceRuntimes.OrderBy(a => a.Value.DeviceStatus))
|
||||
{
|
||||
var deviceRuntime = keyValue.Value;
|
||||
var deviceRuntimeTreeItem = new ChannelDeviceTreeItem() { ChannelDevicePluginType = ChannelDevicePluginTypeEnum.Device, DeviceRuntimeId = deviceRuntime.Id, Id = deviceRuntime.Id };
|
||||
|
||||
var deviceTreeItemItem = deviceOldItems.FirstOrDefault(a => a.Value.Equals(deviceRuntimeTreeItem));
|
||||
|
||||
if (deviceTreeItemItem == null)
|
||||
{
|
||||
deviceTreeItemItem = new TreeViewItem<ChannelDeviceTreeItem>(deviceRuntimeTreeItem)
|
||||
{
|
||||
Text = keyValue.Value.Name,
|
||||
Parent = pluginItem,
|
||||
IsExpand = false,
|
||||
IsActive = selectedItems.Contains(deviceRuntimeTreeItem),
|
||||
Template = render,
|
||||
};
|
||||
}
|
||||
|
||||
channelTreeItemItem.Items.Add(deviceTreeItemItem);
|
||||
}
|
||||
|
||||
channelTreeItemItem.Items = channelTreeItemItem.Items.OrderBy(a => a.Value.Id).ToList();
|
||||
pluginItem.Items.Add(channelTreeItemItem);
|
||||
}
|
||||
pluginItem.Items = pluginItem.Items.OrderBy(a => a.Value.Id).ToList();
|
||||
trees.Add(pluginItem);
|
||||
}
|
||||
#pragma warning restore CA1851
|
||||
|
||||
return trees;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 构建树节点,传入的列表已经是树结构
|
||||
/// </summary>
|
||||
public static List<TreeViewItem<ChannelDeviceTreeItem>> BuildTreeItemList(this Dictionary<ChannelRuntime, List<DeviceRuntime>> dict, List<ChannelDeviceTreeItem> selectedItems, Microsoft.AspNetCore.Components.RenderFragment<ChannelDeviceTreeItem> render = null, TreeViewItem<ChannelDeviceTreeItem>? parent = null, List<TreeViewItem<ChannelDeviceTreeItem>> items = null)
|
||||
{
|
||||
if (dict == null) return null;
|
||||
items ??= new();
|
||||
var trees = new List<TreeViewItem<ChannelDeviceTreeItem>>();
|
||||
|
||||
//筛选插件名称
|
||||
|
||||
foreach (var pluginName in dict.Select(a => a.Key.PluginName).ToHashSet())
|
||||
{
|
||||
var pluginItemValue = new ChannelDeviceTreeItem() { ChannelDevicePluginType = ChannelDevicePluginTypeEnum.PluginName, PluginName = pluginName };
|
||||
|
||||
var pluginItem = items.FirstOrDefault(a => a.Value.Equals(pluginItemValue));
|
||||
|
||||
if (pluginItem == null)
|
||||
{
|
||||
pluginItem = new TreeViewItem<ChannelDeviceTreeItem>(pluginItemValue)
|
||||
{
|
||||
Text = PluginInfoUtil.GetFileNameAndTypeName(pluginName).TypeName,
|
||||
IsExpand = true,
|
||||
Parent = parent,
|
||||
};
|
||||
}
|
||||
var channelOldItems = pluginItem.Items.ToList();
|
||||
pluginItem.Items.Clear();
|
||||
|
||||
foreach (var channelRuntime in dict.Where(a => a.Key.PluginName == pluginName))
|
||||
{
|
||||
var channelRuntimeTreeItem = new ChannelDeviceTreeItem() { ChannelDevicePluginType = ChannelDevicePluginTypeEnum.Channel, ChannelRuntimeId = channelRuntime.Key.Id, Id = channelRuntime.Key.Id };
|
||||
|
||||
var channelTreeItemItem = channelOldItems.FirstOrDefault(a => a.Value.Equals(channelRuntimeTreeItem));
|
||||
|
||||
if (channelTreeItemItem == null)
|
||||
{
|
||||
channelTreeItemItem = new TreeViewItem<ChannelDeviceTreeItem>(channelRuntimeTreeItem)
|
||||
{
|
||||
Text = channelRuntime.ToString(),
|
||||
Parent = pluginItem,
|
||||
IsExpand = true,
|
||||
IsActive = selectedItems.Contains(channelRuntimeTreeItem),
|
||||
Template = render,
|
||||
};
|
||||
}
|
||||
|
||||
var deviceOldItems = channelTreeItemItem.Items.ToList();
|
||||
channelTreeItemItem.Items.Clear();
|
||||
|
||||
foreach (var deviceRuntime in channelRuntime.Value.OrderBy(a => a.DeviceStatus))
|
||||
{
|
||||
var deviceRuntimeTreeItem = new ChannelDeviceTreeItem() { ChannelDevicePluginType = ChannelDevicePluginTypeEnum.Device, DeviceRuntimeId = deviceRuntime.Id, Id = deviceRuntime.Id };
|
||||
|
||||
var deviceTreeItemItem = deviceOldItems.FirstOrDefault(a => a.Value.Equals(deviceRuntimeTreeItem));
|
||||
|
||||
if (deviceTreeItemItem == null)
|
||||
{
|
||||
deviceTreeItemItem = new TreeViewItem<ChannelDeviceTreeItem>(deviceRuntimeTreeItem)
|
||||
{
|
||||
Text = deviceRuntime.Name,
|
||||
Parent = pluginItem,
|
||||
IsExpand = false,
|
||||
IsActive = selectedItems.Contains(deviceRuntimeTreeItem),
|
||||
Template = render,
|
||||
};
|
||||
}
|
||||
|
||||
channelTreeItemItem.Items.Add(deviceTreeItemItem);
|
||||
}
|
||||
|
||||
channelTreeItemItem.Items = channelTreeItemItem.Items.OrderBy(a => a.Value.Id).ToList();
|
||||
pluginItem.Items.Add(channelTreeItemItem);
|
||||
}
|
||||
|
||||
pluginItem.Items = pluginItem.Items.OrderBy(a => a.Value.Id).ToList();
|
||||
trees.Add(pluginItem);
|
||||
}
|
||||
|
||||
return trees;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
@@ -62,13 +62,13 @@ public partial class QuickActions
|
||||
[Parameter]
|
||||
public EventCallback<bool> AutoRestartThreadChanged { get; set; }
|
||||
|
||||
private async Task OnAutoRestartThreadChanged(bool autoRestartThread)
|
||||
private async Task OnAutoRestartThreadChanged(bool restart)
|
||||
{
|
||||
AutoRestartThread = autoRestartThread;
|
||||
AutoRestartThread = restart;
|
||||
if (Module != null)
|
||||
await Module!.InvokeVoidAsync("saveAutoRestartThread", autoRestartThread);
|
||||
await Module!.InvokeVoidAsync("saveAutoRestartThread", restart);
|
||||
if (AutoRestartThreadChanged.HasDelegate)
|
||||
await AutoRestartThreadChanged.InvokeAsync(autoRestartThread);
|
||||
await AutoRestartThreadChanged.InvokeAsync(restart);
|
||||
}
|
||||
|
||||
private List<SelectedItem> AutoRestartThreadBoolItems;
|
||||
@@ -84,8 +84,8 @@ public partial class QuickActions
|
||||
|
||||
protected override async Task InvokeInitAsync()
|
||||
{
|
||||
var autoRestartThread = await Module!.InvokeAsync<bool>("getAutoRestartThread");
|
||||
await OnAutoRestartThreadChanged(autoRestartThread);
|
||||
var restart = await Module!.InvokeAsync<bool>("getAutoRestartThread");
|
||||
await OnAutoRestartThreadChanged(restart);
|
||||
}
|
||||
#endregion
|
||||
private async Task ToggleOpen()
|
||||
|
@@ -8,11 +8,11 @@
|
||||
themeList.classList.toggle('is-open')
|
||||
}
|
||||
export function getAutoRestartThread() {
|
||||
return JSON.parse(localStorage.getItem('autoRestartThread'))??true;
|
||||
return JSON.parse(localStorage.getItem('restart'))??true;
|
||||
}
|
||||
|
||||
export function saveAutoRestartThread(autoRestartThread) {
|
||||
export function saveAutoRestartThread(restart) {
|
||||
if (localStorage) {
|
||||
localStorage.setItem('autoRestartThread', JSON.stringify(autoRestartThread));
|
||||
localStorage.setItem('restart', JSON.stringify(restart));
|
||||
}
|
||||
}
|
@@ -1,4 +1,7 @@
|
||||
{
|
||||
"ThingsGateway.Management.Razor.GatewayUpdateZipFilePage": {
|
||||
"Upgrade": "Upgrade"
|
||||
},
|
||||
"ThingsGateway.Management.Razor.ManagementTree": {
|
||||
"AddManagementConfig": "Add",
|
||||
"ExcelManagementConfig": "Excel",
|
||||
@@ -20,8 +23,11 @@
|
||||
"SystemConfigPage": "SystemConfig",
|
||||
"PluginPage": "Plugin",
|
||||
"RealAlarmPage": "RealAlarm",
|
||||
"RulesPage": "Rules"
|
||||
|
||||
"RulesPage": "Rules",
|
||||
"ChannelTable": "Channel",
|
||||
"DeviceTable": "Device",
|
||||
"VariableRuntimeInfo": "Variable",
|
||||
"GatewayUpdateZipFilePage": "Version"
|
||||
},
|
||||
"ThingsGateway.Management.Razor.SaveUpdateZipFile": {
|
||||
"DownTemplate": "Download Template",
|
||||
|
@@ -1,4 +1,7 @@
|
||||
{
|
||||
"ThingsGateway.Management.Razor.GatewayUpdateZipFilePage": {
|
||||
"Upgrade": "升级"
|
||||
},
|
||||
"ThingsGateway.Management.Razor.ManagementTree": {
|
||||
"AddManagementConfig": "添加",
|
||||
"ExcelManagementConfig": "在线excel编辑",
|
||||
@@ -20,7 +23,11 @@
|
||||
"SystemConfigPage": "系统管理",
|
||||
"PluginPage": "插件管理",
|
||||
"RealAlarmPage": "实时报警",
|
||||
"RulesPage": "规则引擎"
|
||||
"RulesPage": "规则引擎",
|
||||
"ChannelTable": "通道信息",
|
||||
"DeviceTable": "设备信息",
|
||||
"VariableRuntimeInfo": "变量信息",
|
||||
"GatewayUpdateZipFilePage": "版本管理"
|
||||
},
|
||||
"ThingsGateway.Management.Razor.SaveUpdateZipFile": {
|
||||
"DownTemplate": "下载模板",
|
||||
|
@@ -4,7 +4,7 @@ namespace ThingsGateway.Gateway.Razor
|
||||
{
|
||||
public static class TreeViewItemMapper
|
||||
{
|
||||
public static global::System.Collections.Generic.List<global::BootstrapBlazor.Components.TreeViewItem<global::ThingsGateway.Gateway.Application.ChannelDeviceTreeItem>> AdaptListTreeViewItemChannelDeviceTreeItem(this global::System.Collections.Generic.List<global::BootstrapBlazor.Components.TreeViewItem<global::ThingsGateway.Gateway.Application.ChannelDeviceTreeItem>> src)
|
||||
public static global::System.Collections.Generic.List<global::BootstrapBlazor.Components.TreeViewItem<ChannelDeviceTreeItem>> AdaptListTreeViewItemChannelDeviceTreeItem(this global::System.Collections.Generic.List<global::BootstrapBlazor.Components.TreeViewItem<ChannelDeviceTreeItem>> src)
|
||||
{
|
||||
NormalizeItems(src);
|
||||
return src.ToList();
|
||||
|
@@ -5,7 +5,7 @@
|
||||
|
||||
<ChannelRuntimeInfo1 ChannelRuntime="ChannelRuntime" />
|
||||
|
||||
<LogConsole HeightString="calc(100% - 270px)" LogLevel=@(LogLevel)
|
||||
<LogConsole HeightString="@Height" LogLevel=@(LogLevel)
|
||||
LogLevelChanged="async(logLevel)=>
|
||||
{
|
||||
LogLevel = logLevel;
|
||||
|
@@ -12,6 +12,12 @@ namespace ThingsGateway.Gateway.Razor;
|
||||
|
||||
public partial class ChannelRuntimeInfo
|
||||
{
|
||||
#if !Management
|
||||
private string Height { get; set; } = "calc(100% - 270px)";
|
||||
#else
|
||||
private string Height { get; set; } = "calc(100% - 330px)";
|
||||
|
||||
#endif
|
||||
[Parameter, EditorRequired]
|
||||
public ChannelRuntime ChannelRuntime { get; set; }
|
||||
|
||||
|
@@ -5,6 +5,9 @@
|
||||
@using ThingsGateway.Gateway.Application
|
||||
@inherits ComponentDefault
|
||||
|
||||
@{
|
||||
#if Management
|
||||
}
|
||||
<AdminTable @ref=table BeforeShowEditDialogCallback="BeforeShowEditDialogCallback"
|
||||
TItem="ChannelRuntime"
|
||||
EditDialogSize="Size.ExtraLarge"
|
||||
@@ -60,6 +63,118 @@
|
||||
|
||||
</TableColumns>
|
||||
|
||||
|
||||
<EditTemplate Context="context">
|
||||
<ChannelEditComponent Model=@(context) ValidateEnable=false BatchEditEnable=false PluginType="null"></ChannelEditComponent>
|
||||
</EditTemplate>
|
||||
|
||||
|
||||
<RowButtonTemplate>
|
||||
|
||||
<TableCellButton IsShow=@(AuthorizeButton(AdminOperConst.Edit)) Size="Size.ExtraSmall" Color="Color.Warning" Icon="fa-solid fa-bars" Text="@RazorLocalizer["Info"]" OnClick="() => DrawerServiceShowChannelRuntimeInfo(context)" />
|
||||
</RowButtonTemplate>
|
||||
|
||||
|
||||
<ExportButtonDropdownTemplate Context="ExportContext">
|
||||
@if ((AuthorizeButton("导出")))
|
||||
{
|
||||
|
||||
<Button class="dropdown-item" OnClick="() => ExcelExportAsync(ExportContext, true)" IsKeepDisabled=@(!AuthorizeButton("导出"))>
|
||||
<i class="fas fa-file-export"></i>
|
||||
<span>@RazorLocalizer["ExportAll"]</span>
|
||||
</Button>
|
||||
<Button class="dropdown-item" OnClick="() => ExcelExportAsync(ExportContext)" IsKeepDisabled=@(!AuthorizeButton("导出"))>
|
||||
<i class="fas fa-file-export"></i>
|
||||
<span>@RazorLocalizer["TablesExportButtonExcelText"]</span>
|
||||
</Button>
|
||||
}
|
||||
@if ((AuthorizeButton("导入")))
|
||||
{
|
||||
<Button class="dropdown-item" OnClick="() => ExcelImportAsync(ExportContext)" IsKeepDisabled=@(!AuthorizeButton("导入"))>
|
||||
<i class="fas fa-file-import"></i>
|
||||
<span>@RazorLocalizer["TablesImportButtonExcelText"]</span>
|
||||
</Button>
|
||||
<Button class="dropdown-item" OnClick="() => ExcelChannelAsync(ExportContext)" IsKeepDisabled=@(!AuthorizeButton("导入"))>
|
||||
<i class="fas fa-file-import"></i>
|
||||
<span>@GatewayLocalizer["ExcelChannel"]</span>
|
||||
</Button>
|
||||
}
|
||||
</ExportButtonDropdownTemplate>
|
||||
<TableToolbarTemplate>
|
||||
|
||||
<TableToolbarButton TItem="ChannelRuntime" IsDisabled=@(!AuthorizeButton(AdminOperConst.Add))
|
||||
Color=Color.Success Text="@RazorLocalizer["Copy"]"
|
||||
OnClickCallback=@(Copy) />
|
||||
|
||||
<TableToolbarButton TItem="ChannelRuntime" IsDisabled=@(!AuthorizeButton(AdminOperConst.Edit))
|
||||
Color=Color.Info Text="@RazorLocalizer["BatchEdit"]"
|
||||
OnClickCallback=@(BatchEdit) />
|
||||
|
||||
<TableToolbarPopConfirmButton TItem="ChannelRuntime"
|
||||
Color=Color.Warning Text="@RazorLocalizer["Clear"]" IsDisabled=@(!AuthorizeButton(AdminOperConst.Delete))
|
||||
IsAsync OnConfirm=@(ClearAsync) />
|
||||
|
||||
</TableToolbarTemplate>
|
||||
</AdminTable>
|
||||
|
||||
|
||||
@{#else}
|
||||
<AdminTable @ref=table BeforeShowEditDialogCallback="BeforeShowEditDialogCallback"
|
||||
TItem="ChannelRuntime"
|
||||
EditDialogSize="Size.ExtraLarge"
|
||||
AutoGenerateColumns="true"
|
||||
ShowAdvancedSearch=false
|
||||
ScrollingDialogContent=false
|
||||
AllowResizing="true"
|
||||
OnAdd="OnAdd"
|
||||
IsFixedHeader=true
|
||||
IsMultipleSelect=true
|
||||
SearchMode=SearchMode.Top
|
||||
ShowExtendButtons=true
|
||||
ShowToolbar="true"
|
||||
ShowExportButton
|
||||
ShowDefaultButtons=true
|
||||
ShowSearch=false
|
||||
ExtendButtonColumnWidth=220
|
||||
OnSaveAsync="Save"
|
||||
OnDeleteAsync="Delete"
|
||||
OnQueryAsync="OnQueryAsync"
|
||||
IsPagination=true>
|
||||
<TableColumns>
|
||||
<TableColumn @bind-Field="@context.Name" ShowTips=true Filterable=true Sortable=true Visible=true>
|
||||
|
||||
<Template Context="value">
|
||||
@value.Row?.ToString()
|
||||
</Template>
|
||||
|
||||
</TableColumn>
|
||||
<TableColumn @bind-Field="@context.PluginName" ShowTips=true Filterable=true Sortable=true Visible=true />
|
||||
<TableColumn @bind-Field="@context.Enable" Filterable=true Sortable=true Visible="true" />
|
||||
<TableColumn @bind-Field="@context.LogLevel" Filterable=true Sortable=true Visible="false" />
|
||||
<TableColumn @bind-Field="@context.ChannelType" Filterable=true Sortable=true Visible="false" />
|
||||
|
||||
<TableColumn Field="@context.CacheTimeout" FieldExpression=@(() => context.CacheTimeout) ShowTips=true Filterable=true Sortable=true Visible=false />
|
||||
<TableColumn Field="@context.CheckClearTime" FieldExpression=@(() => context.CheckClearTime) ShowTips=true Filterable=true Sortable=true Visible=false />
|
||||
<TableColumn Field="@context.ConnectTimeout" FieldExpression=@(() => context.ConnectTimeout) ShowTips=true Filterable=true Sortable=true Visible=false />
|
||||
<TableColumn Field="@context.Heartbeat" FieldExpression=@(() => context.Heartbeat) ShowTips=true Filterable=true Sortable=true Visible=false />
|
||||
<TableColumn Field="@context.HeartbeatTime" FieldExpression=@(() => context.HeartbeatTime) ShowTips=true Filterable=true Sortable=true Visible=false />
|
||||
<TableColumn Field="@context.DtuSeviceType" FieldExpression=@(() => context.DtuSeviceType) ShowTips=true Filterable=true Sortable=true Visible=false />
|
||||
<TableColumn Field="@context.DtuId" FieldExpression=@(() => context.DtuId) ShowTips=true Filterable=true Sortable=true Visible=false />
|
||||
|
||||
<TableColumn Field="@context.PluginType" FieldExpression=@(() => context.PluginType) ShowTips=true Filterable=true Sortable=true Visible=false />
|
||||
<TableColumn Field="@context.PortName" FieldExpression=@(() => context.PortName) ShowTips=true Filterable=true Sortable=true Visible=false />
|
||||
<TableColumn Field="@context.RemoteUrl" FieldExpression=@(() => context.RemoteUrl) ShowTips=true Filterable=true Sortable=true Visible=false />
|
||||
<TableColumn Field="@context.BindUrl" FieldExpression=@(() => context.BindUrl) ShowTips=true Filterable=true Sortable=true Visible=false />
|
||||
<TableColumn Field="@context.MaxClientCount" FieldExpression=@(() => context.MaxClientCount) ShowTips=true Filterable=true Sortable=true Visible=false />
|
||||
<TableColumn Field="@context.MaxConcurrentCount" FieldExpression=@(() => context.MaxConcurrentCount) ShowTips=true Filterable=true Sortable=true Visible=false />
|
||||
<TableColumn Field="@context.DtuIdHex" FieldExpression=@(() => context.DtuIdHex) ShowTips=true Filterable=true Sortable=true Visible=false />
|
||||
<TableColumn Field="@context.HeartbeatHex" FieldExpression=@(() => context.HeartbeatHex) ShowTips=true Filterable=true Sortable=true Visible=false />
|
||||
|
||||
<TableColumn @bind-Field="@context.Id" Filterable=true Sortable=true Visible="false" DefaultSort=true DefaultSortOrder="SortOrder.Asc" />
|
||||
|
||||
</TableColumns>
|
||||
|
||||
|
||||
<EditTemplate Context="context">
|
||||
<ChannelEditComponent Model=@(context) ValidateEnable=false BatchEditEnable=false PluginType="ChannelDeviceHelpers.GetPluginType(SelectModel)"></ChannelEditComponent>
|
||||
</EditTemplate>
|
||||
@@ -108,6 +223,8 @@
|
||||
</TableToolbarTemplate>
|
||||
</AdminTable>
|
||||
|
||||
@{#endif}
|
||||
|
||||
@code {
|
||||
AdminTable<ChannelRuntime> table;
|
||||
}
|
@@ -20,15 +20,53 @@ namespace ThingsGateway.Gateway.Razor;
|
||||
|
||||
public partial class ChannelTable : IDisposable
|
||||
{
|
||||
|
||||
#if !Management
|
||||
[Parameter]
|
||||
public ChannelDeviceTreeItem SelectModel { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public IEnumerable<ChannelRuntime>? Items { get; set; } = Enumerable.Empty<ChannelRuntime>();
|
||||
|
||||
private IEnumerable<ChannelRuntime>? _previousItemsRef;
|
||||
protected override void OnParametersSet()
|
||||
{
|
||||
if (!ReferenceEquals(_previousItemsRef, Items))
|
||||
{
|
||||
_previousItemsRef = Items;
|
||||
Refresh();
|
||||
}
|
||||
base.OnParametersSet();
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
private async Task DrawerServiceShowChannelRuntimeInfo(ChannelRuntime channelRuntime) => await DrawerService.Show(new DrawerOption()
|
||||
{
|
||||
Class = "h-100",
|
||||
Width = "80%",
|
||||
Placement = Placement.Right,
|
||||
ChildContent = BootstrapDynamicComponent.CreateComponent<ChannelRuntimeInfo>(new Dictionary<string, object?>
|
||||
{
|
||||
{nameof(ChannelRuntimeInfo.ChannelRuntime), channelRuntime }
|
||||
}).Render(),
|
||||
ShowBackdrop = true,
|
||||
AllowResize = true,
|
||||
IsBackdrop = true
|
||||
});
|
||||
#endif
|
||||
|
||||
[Inject]
|
||||
DrawerService DrawerService { get; set; }
|
||||
private static void BeforeShowEditDialogCallback(ITableEditDialogOption<ChannelRuntime> tableEditDialogOption)
|
||||
{
|
||||
tableEditDialogOption.Model = tableEditDialogOption.Model.AdaptChannelRuntime();
|
||||
}
|
||||
|
||||
|
||||
|
||||
public bool Disposed { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public IEnumerable<ChannelRuntime>? Items { get; set; } = Enumerable.Empty<ChannelRuntime>();
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
@@ -44,16 +82,7 @@ public partial class ChannelTable : IDisposable
|
||||
}
|
||||
|
||||
private SmartTriggerScheduler scheduler;
|
||||
private IEnumerable<ChannelRuntime>? _previousItemsRef;
|
||||
protected override void OnParametersSet()
|
||||
{
|
||||
if (!ReferenceEquals(_previousItemsRef, Items))
|
||||
{
|
||||
_previousItemsRef = Items;
|
||||
Refresh();
|
||||
}
|
||||
base.OnParametersSet();
|
||||
}
|
||||
|
||||
private void Refresh()
|
||||
{
|
||||
scheduler.Trigger();
|
||||
@@ -71,7 +100,11 @@ public partial class ChannelTable : IDisposable
|
||||
{
|
||||
try
|
||||
{
|
||||
#if Management
|
||||
Refresh();
|
||||
#else
|
||||
await InvokeAsync(StateHasChanged);
|
||||
#endif
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -89,16 +122,23 @@ public partial class ChannelTable : IDisposable
|
||||
private QueryPageOptions _option = new();
|
||||
private Task<QueryData<ChannelRuntime>> OnQueryAsync(QueryPageOptions options)
|
||||
{
|
||||
#if Management
|
||||
var data = ChannelPageService.OnChannelQueryAsync(options);
|
||||
|
||||
_option = options;
|
||||
return data;
|
||||
#else
|
||||
var data = Items
|
||||
.WhereIf(!options.SearchText.IsNullOrWhiteSpace(), a => a.Name.Contains(options.SearchText))
|
||||
.GetQueryData(options);
|
||||
_option = options;
|
||||
return Task.FromResult(data);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
#endregion 查询
|
||||
|
||||
#region 编辑
|
||||
|
||||
[Inject]
|
||||
IChannelPageService ChannelPageService { get; set; }
|
||||
@@ -138,13 +178,14 @@ public partial class ChannelTable : IDisposable
|
||||
|
||||
private async Task BatchEdit(IEnumerable<Channel> changedModels)
|
||||
{
|
||||
var oldModel = changedModels.FirstOrDefault();//默认值显示第一个
|
||||
var datas = changedModels.ToList();
|
||||
var oldModel = datas.FirstOrDefault();//默认值显示第一个
|
||||
if (oldModel == null)
|
||||
{
|
||||
await ToastService.Warning(null, RazorLocalizer["PleaseSelect"]);
|
||||
return;
|
||||
}
|
||||
changedModels = changedModels.AdaptListChannel();
|
||||
datas = datas.AdaptListChannel();
|
||||
oldModel = oldModel.AdaptChannel();
|
||||
var oneModel = oldModel.AdaptChannel();//默认值显示第一个
|
||||
|
||||
@@ -162,7 +203,7 @@ public partial class ChannelTable : IDisposable
|
||||
{
|
||||
{nameof(ChannelEditComponent.OnValidSubmit), async () =>
|
||||
{
|
||||
await Task.Run(() => ChannelPageService.BatchEditChannelAsync(changedModels, oldModel, oneModel,AutoRestartThread));
|
||||
await Task.Run(() => ChannelPageService.BatchEditChannelAsync(datas, oldModel, oneModel,AutoRestartThread));
|
||||
|
||||
await InvokeAsync(table.QueryAsync);
|
||||
} },
|
||||
@@ -179,7 +220,7 @@ public partial class ChannelTable : IDisposable
|
||||
{
|
||||
try
|
||||
{
|
||||
return await Task.Run(async () => await ChannelPageService.DeleteChannelAsync(channels.Select(a => a.Id), AutoRestartThread, default));
|
||||
return await Task.Run(async () => await ChannelPageService.DeleteChannelAsync(channels.Select(a => a.Id).ToList(), AutoRestartThread));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -206,7 +247,11 @@ public partial class ChannelTable : IDisposable
|
||||
|
||||
private Task<ChannelRuntime> OnAdd()
|
||||
{
|
||||
#if !Management
|
||||
return Task.FromResult(ChannelDeviceHelpers.GetChannelModel(ItemChangedType.Add, SelectModel).AdaptChannelRuntime());
|
||||
#else
|
||||
return Task.FromResult(new ChannelRuntime());
|
||||
#endif
|
||||
}
|
||||
|
||||
#region 导出
|
||||
@@ -224,6 +269,7 @@ public partial class ChannelTable : IDisposable
|
||||
}
|
||||
else
|
||||
{
|
||||
#if !Management
|
||||
switch (SelectModel.ChannelDevicePluginType)
|
||||
{
|
||||
|
||||
@@ -241,6 +287,9 @@ public partial class ChannelTable : IDisposable
|
||||
|
||||
break;
|
||||
}
|
||||
#else
|
||||
ret = await GatewayExportService.OnChannelExport(new() { QueryPageOptions = _option });
|
||||
#endif
|
||||
}
|
||||
|
||||
// 返回 true 时自动弹出提示框
|
||||
@@ -262,14 +311,21 @@ public partial class ChannelTable : IDisposable
|
||||
|
||||
var option = _option;
|
||||
option.IsPage = false;
|
||||
#if !Management
|
||||
var models = Items
|
||||
.WhereIf(!option.SearchText.IsNullOrWhiteSpace(), a => a.Name.Contains(option.SearchText)).GetData(option, out var total).ToList();
|
||||
if (models.Count > 50000)
|
||||
.WhereIf(!option.SearchText.IsNullOrWhiteSpace(), a => a.Name.Contains(option.SearchText)).GetData(option, out var total).Cast<Channel>().ToList();
|
||||
|
||||
#else
|
||||
|
||||
var models = await ChannelPageService.GetChannelListAsync(option, 2000);
|
||||
|
||||
#endif
|
||||
if (models.Count > 2000)
|
||||
{
|
||||
await ToastService.Warning("online Excel max data count 50000");
|
||||
await ToastService.Warning("online Excel max data count 2000");
|
||||
return;
|
||||
}
|
||||
var uSheetDatas = ChannelServiceHelpers.ExportChannel(models);
|
||||
var uSheetDatas = await ChannelPageService.ExportChannelAsync(models);
|
||||
|
||||
op.Component = BootstrapDynamicComponent.CreateComponent<USheet>(new Dictionary<string, object?>
|
||||
{
|
||||
@@ -279,7 +335,7 @@ public partial class ChannelTable : IDisposable
|
||||
{
|
||||
await Task.Run(async ()=>
|
||||
{
|
||||
var importData=await ChannelPageService.ImportChannelAsync(data,AutoRestartThread);
|
||||
var importData=await ChannelPageService.ImportChannelUSheetDatasAsync(data,AutoRestartThread);
|
||||
|
||||
})
|
||||
;
|
||||
@@ -343,7 +399,11 @@ finally
|
||||
{
|
||||
await Task.Run(async () =>
|
||||
{
|
||||
await ChannelPageService.DeleteChannelAsync(Items.Select(a => a.Id), AutoRestartThread, default);
|
||||
#if !Management
|
||||
await ChannelPageService.DeleteChannelAsync(Items.Select(a => a.Id).ToList(), AutoRestartThread);
|
||||
#else
|
||||
await ChannelPageService.ClearChannelAsync(AutoRestartThread);
|
||||
#endif
|
||||
await InvokeAsync(async () =>
|
||||
{
|
||||
await ToastService.Default();
|
||||
@@ -360,11 +420,10 @@ finally
|
||||
|
||||
[Parameter]
|
||||
public bool AutoRestartThread { get; set; }
|
||||
[Parameter]
|
||||
public ChannelDeviceTreeItem SelectModel { get; set; }
|
||||
|
||||
|
||||
[Inject]
|
||||
[NotNull]
|
||||
public IStringLocalizer<ThingsGateway.Gateway.Razor._Imports>? GatewayLocalizer { get; set; }
|
||||
#endregion
|
||||
|
||||
}
|
||||
|
@@ -14,7 +14,7 @@
|
||||
<span style="color:var(--bs-body-color);opacity: 0.5;" class="text-h6 mb-2">@GatewayLocalizer["DeviceList"]</span>
|
||||
|
||||
<ContextMenuZone>
|
||||
<TreeView TItem="ChannelDeviceTreeItem" Items="Items" ShowIcon="false" ShowSearch IsAccordion=false IsVirtualize="true" OnTreeItemClick="OnTreeItemClick" OnSearchAsync="OnClickSearch" ModelEqualityComparer=ModelEqualityComparer ShowToolbar="true">
|
||||
<TreeView TItem="ChannelDeviceTreeItem" Items="Items" ShowIcon="false" ShowSearch IsAccordion=false IsVirtualize="true" OnTreeItemClick="OnTreeItemClick" OnSearchAsync="OnClickSearch" ModelEqualityComparer=ModelEqualityComparer ShowToolbar="true" >
|
||||
<ToolbarTemplate>
|
||||
|
||||
<div class="tree-node-toolbar-edit" @onclick:preventDefault @onclick:stopPropagation>
|
||||
|
@@ -241,7 +241,7 @@ public partial class ChannelDeviceTree : IDisposable
|
||||
{nameof(ChannelEditComponent.OnValidSubmit), async () =>
|
||||
{
|
||||
Spinner.SetRun(true);
|
||||
await Task.Run(() => GlobalData.ChannelRuntimeService.BatchEditChannelAsync(changedModels, oldModel, oneModel,AutoRestartThread));
|
||||
await Task.Run(() => GlobalData.ChannelRuntimeService.BatchEditChannelAsync(changedModels.ToList(), oldModel, oneModel,AutoRestartThread));
|
||||
|
||||
//await Notify();
|
||||
await InvokeAsync(() => Spinner.SetRun(false));
|
||||
@@ -397,7 +397,7 @@ finally
|
||||
{
|
||||
Spinner.SetRun(true);
|
||||
|
||||
await Task.Run(() => GlobalData.ChannelRuntimeService.DeleteChannelAsync(modelIds.Select(a => a.Id), AutoRestartThread, default));
|
||||
await Task.Run(() => GlobalData.ChannelRuntimeService.DeleteChannelAsync(modelIds.Select(a => a.Id).ToList(), AutoRestartThread));
|
||||
//await Notify();
|
||||
await InvokeAsync(() => Spinner.SetRun(false));
|
||||
}
|
||||
@@ -433,7 +433,7 @@ finally
|
||||
Spinner.SetRun(true);
|
||||
|
||||
var key = await GlobalData.GetCurrentUserChannels().ConfigureAwait(false);
|
||||
await Task.Run(() => GlobalData.ChannelRuntimeService.DeleteChannelAsync(key.Select(a => a.Id), AutoRestartThread, default));
|
||||
await Task.Run(() => GlobalData.ChannelRuntimeService.DeleteChannelAsync(key.Select(a => a.Id).ToList(), AutoRestartThread));
|
||||
//await Notify();
|
||||
await InvokeAsync(() => Spinner.SetRun(false));
|
||||
}
|
||||
@@ -718,7 +718,7 @@ EventCallback.Factory.Create<MouseEventArgs>(this, async e =>
|
||||
{nameof(DeviceEditComponent.OnValidSubmit), async () =>
|
||||
{
|
||||
await InvokeAsync( () => Spinner.SetRun(true));
|
||||
await Task.Run(() =>GlobalData.DeviceRuntimeService.BatchEditAsync(changedModels,oldModel,oneModel,AutoRestartThread));
|
||||
await Task.Run(() =>GlobalData.DeviceRuntimeService.BatchEditDeviceAsync(changedModels.ToList(),oldModel,oneModel,AutoRestartThread));
|
||||
//await Notify();
|
||||
await InvokeAsync(() => Spinner.SetRun(false));
|
||||
}},
|
||||
@@ -876,7 +876,7 @@ EventCallback.Factory.Create<MouseEventArgs>(this, async e =>
|
||||
{
|
||||
Spinner.SetRun(true);
|
||||
|
||||
await Task.Run(() => GlobalData.DeviceRuntimeService.DeleteDeviceAsync(modelIds.Select(a => a.Id), AutoRestartThread, default));
|
||||
await Task.Run(() => GlobalData.DeviceRuntimeService.DeleteDeviceAsync(modelIds.Select(a => a.Id).ToList(), AutoRestartThread));
|
||||
//await Notify();
|
||||
await InvokeAsync(() => Spinner.SetRun(false));
|
||||
}
|
||||
@@ -914,7 +914,7 @@ EventCallback.Factory.Create<MouseEventArgs>(this, async e =>
|
||||
|
||||
var data = await GlobalData.GetCurrentUserDevices().ConfigureAwait(false);
|
||||
|
||||
await Task.Run(() => GlobalData.DeviceRuntimeService.DeleteDeviceAsync(data.Select(a => a.Id), AutoRestartThread, default));
|
||||
await Task.Run(() => GlobalData.DeviceRuntimeService.DeleteDeviceAsync(data.Select(a => a.Id).ToList(), AutoRestartThread));
|
||||
//await Notify();
|
||||
await InvokeAsync(() => Spinner.SetRun(false));
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user