Compare commits

..

68 Commits
7.2.0.60 ... v7

Author SHA1 Message Date
2248356998 qq.com
7590ba4ad5 更新插件 2025-01-06 20:57:36 +08:00
Diego
39ce18f8bb 更新readme 2025-01-06 16:06:01 +08:00
Diego
777a4f9d3f 更新插件 2025-01-06 14:29:51 +08:00
Diego
b91a938787 更新构建脚本 2025-01-06 14:02:41 +08:00
Diego
c903abdb1b 更新admin 2025-01-06 11:24:49 +08:00
Diego
3279bc0580 更新构建脚本 2025-01-06 11:12:04 +08:00
Diego
bd32c9f49e 更新插件 2025-01-06 10:28:14 +08:00
Diego
5507088e3d 更新插件 2025-01-06 09:48:55 +08:00
2248356998 qq.com
37ad48ae4a 7.2.3.6
更新依赖包
2025-01-05 23:46:31 +08:00
2248356998 qq.com
120c6e6d87 7.2.3.5
opcuaclient: 心跳机制异常
2025-01-05 00:46:52 +08:00
2248356998 qq.com
866e907b7e 同步 2025-01-02 20:47:47 +08:00
2248356998 qq.com
2bd8c2cb10 更新mc1e插件 2025-01-02 20:45:19 +08:00
Diego
c6c9919178 更新依赖包 2025-01-02 16:45:31 +08:00
Diego
575bf9d1e0 update Version MSBuildProperty 2025-01-02 13:56:38 +08:00
Diego
82a56e0285 更新构建脚本 2025-01-02 12:56:52 +08:00
Diego
25d5f7c132 设定动态编译缓存目录 2025-01-02 12:27:57 +08:00
Diego2098
a3e9ecf30f !56 恢复插件隔离
* 恢复插件域
2025-01-02 04:03:14 +00:00
2248356998 qq.com
ec5ff0a07f 7.2.1.3
更新mqttnet版本
更新admin
2025-01-01 16:25:19 +08:00
Diego
be836d30c5 更新admin 2024-12-30 12:11:08 +08:00
2248356998 qq.com
42b1529a5f 添加Release解决方案 2024-12-28 10:35:02 +08:00
2248356998 qq.com
47708c4807 添加Release解决方案 2024-12-28 10:30:17 +08:00
2248356998 qq.com
d2a51e004c 7.2.0.99
规则引擎:自定义脚本接口定义修改,增加Logger属性
数据库上传插件:回退修改,恢复string类型Value字段
2024-12-28 04:19:37 +08:00
2248356998 qq.com
ab14230101 修复通道报文日志失效 2024-12-26 21:36:09 +08:00
Diego
4bcf8c1f78 修复设备通讯变量点位 2024-12-26 15:38:19 +08:00
Diego
44d00e9da3 数据库插件实体类修改! 2024-12-26 14:45:25 +08:00
Diego
d7e6a4493c 更新依赖 2024-12-26 13:22:51 +08:00
Diego
53e89d8c54 7.2.0.95
修复 LL优先级比L低
添加报警属性验证规则
2024-12-25 12:26:06 +08:00
Diego
4497c13634 代码清理 2024-12-25 10:53:43 +08:00
Diego
4a35fade46 更新插件 2024-12-24 17:44:51 +08:00
Diego
6cba0601fd fix: 上一提交导致other通道创建错误 2024-12-24 17:21:10 +08:00
Diego
ed4332ea78 更新readme 2024-12-24 14:14:30 +08:00
Diego
aba069cec5 7.2.0.91
修复dlt645驱动
调整异步方法
2024-12-24 14:03:39 +08:00
Diego
8a82ac0a11 更新测试方法 2024-12-23 16:43:35 +08:00
Diego
1cd1456d75 7.2.0.87
更新admin
2024-12-23 15:31:16 +08:00
Diego
b791a3eb10 修复 动态插件属性 验证错误 2024-12-23 15:21:42 +08:00
2248356998 qq.com
3b22a8b170 7.2.0.85
更新依赖
2024-12-22 15:59:15 +08:00
2248356998 qq.com
419e8214ca 更新规则引擎,优化性能 2024-12-20 22:32:37 +08:00
Diego
b9f8571f0f 7.2.0.83
fix(VariableRuntime): 修改判断变量变化方法
feat(TestVariable): 修改添加测试变量UI
2024-12-20 14:28:44 +08:00
Diego
c6d9a9d7f8 修改判断变量变化方法 2024-12-20 14:27:29 +08:00
Diego
310aba6ccb 修改添加测试变量UI 2024-12-20 12:11:20 +08:00
Diego
9bd89ac4f6 添加 git 更新子模块 bat 2024-12-19 18:40:13 +08:00
Diego
ea6a51dca9 更新admin 2024-12-19 18:16:01 +08:00
Diego
e701bcc50c 调整插件基类,增加上传模式配置 2024-12-19 18:15:47 +08:00
Diego
a1abf06e75 7.2.0.78
更新opc插件
2024-12-19 16:11:13 +08:00
Diego
76ace394b0 更新opcua插件 2024-12-19 15:21:21 +08:00
Diego
4dac462f8f 更新解决方案 2024-12-19 12:21:32 +08:00
Diego
039672b1e7 更新admin 2024-12-19 12:19:55 +08:00
Diego
1b26ecbbf5 添加 admin 子模块 2024-12-19 11:54:14 +08:00
Diego2098
f4a7e96943 add Admin submodule.
Signed-off-by: Diego2098 <2248356998@qq.com>
2024-12-19 03:17:59 +00:00
Diego
a45bc2954f 移动文件夹 2024-12-19 11:16:24 +08:00
Diego
62f32467b7 移动文件夹 2024-12-19 11:15:18 +08:00
Diego
600a1bf201 7.2.0.75 2024-12-18 12:36:49 +08:00
Diego
7e196e7aa6 更新admin 2024-12-17 14:16:15 +08:00
Diego
9ad3507b66 7.2.0.72
修复变量导入错误
2024-12-17 13:51:51 +08:00
Diego
add1bdfcf6 更新admin 2024-12-17 13:26:27 +08:00
Diego
7ea8a7c079 7.2.0.70
修复初始值解析
2024-12-17 10:51:52 +08:00
Diego
28e31f5165 7.2.0.69
更新实体特性
2024-12-17 10:22:03 +08:00
Diego
10a6975c5d 7.2.0.68
更新modbusSlave插件,适配03功能码单写bit
2024-12-16 18:24:16 +08:00
Diego
b34ea87660 7.2.0.67
更新db插件,增加 单独配置实时表上传间隔时间 属性
2024-12-16 17:38:42 +08:00
Diego
86ed69c50b 7.2.0.66
更新版本
2024-12-16 11:29:52 +08:00
Diego
02e824154c 7.2.0.65
读取间隔增加cron支持
2024-12-16 11:13:23 +08:00
Matrix
dc973c8491 !55 TimeTick 新增 Cron
* TimeTick 新增 cron定时
2024-12-16 03:03:39 +00:00
2248356998 qq.com
0a28e3a8d3 7.2.0.64 2024-12-15 23:41:07 +08:00
2248356998 qq.com
7a48c260e1 更新插件 2024-12-14 15:22:19 +08:00
Diego
c55c49a3a2 添加TS自定义二进制序列化方式 2024-12-13 12:19:12 +08:00
Diego
0fc9b06d12 7.2.0.62
优化上传插件批量性能
2024-12-11 15:52:00 +08:00
Diego
d71ad5a6bf 单文件发布 2024-12-11 11:13:58 +08:00
Diego
f0b3028306 更新规则引擎 2024-12-11 09:15:27 +08:00
98 changed files with 2481 additions and 1223 deletions

4
.gitmodules vendored Normal file
View File

@@ -0,0 +1,4 @@
[submodule "Admin"]
url = https://gitee.com/ThingsGateway/BlazorAdmin
path = Admin

1
Admin Submodule

Submodule Admin added at 3b73b7283a

View File

@@ -10,6 +10,42 @@
[NuGet](https://www.nuget.org/packages?q=Tags%3A%22ThingsGateway%22)
## 源码获取/更新
### 源码克隆
注意因仓库包含子模块直接下载zip包会导致子模块丢失建议使用git clone命令
``` shell
https://gitee.com/ThingsGateway/ThingsGateway.git
```
### 源码更新
在vs中打开powerShell窗口执行以下命令或根目录下的`git_pull.bat`脚本
<img src="https://foruda.gitee.com/images/1736150639726525137/8ff84c29_7941935.png" width="400px" />
``` shell
chcp 65001
rem 更新主仓库
git pull
rem 初始化并更新所有子模块
git submodule update --init
pause
```
### 插件列表
#### 采集插件

9
git_pull.bat Normal file
View File

@@ -0,0 +1,9 @@
chcp 65001
rem 更新主仓库
git pull
rem 初始化并更新所有子模块
git submodule update --init
pause

View File

@@ -1,48 +1,49 @@
<Project>
<PropertyGroup>
<AdminVersion>7.2.0.45</AdminVersion>
<PluginVersion>9.0.2.38</PluginVersion>
<ProPluginVersion>9.0.2.65</ProPluginVersion>
</PropertyGroup>
<PropertyGroup>
<AdminVersion>7.2.0.64</AdminVersion>
<GatewayVersion>7.2.3.9</GatewayVersion>
<PluginVersion>9.0.3.10</PluginVersion>
<ProPluginVersion>9.0.3.6</ProPluginVersion>
</PropertyGroup>
<PropertyGroup>
<AnalysisModeDesign>None</AnalysisModeDesign>
<AnalysisModeDocumentation>All</AnalysisModeDocumentation>
<AnalysisModeGlobalization>None</AnalysisModeGlobalization>
<AnalysisModeInteroperability>All</AnalysisModeInteroperability>
<AnalysisModeMaintainability>All</AnalysisModeMaintainability>
<AnalysisModeNaming>None</AnalysisModeNaming>
<AnalysisModePerformance>All</AnalysisModePerformance>
<AnalysisModeSingleFile>All</AnalysisModeSingleFile>
<AnalysisModeReliability>All</AnalysisModeReliability>
<AnalysisModeSecurity>All</AnalysisModeSecurity>
<AnalysisModeUsage>None</AnalysisModeUsage>
<AnalysisModeStyle>None</AnalysisModeStyle>
<NoWarn>CS8603;CS8618;CS1591;CS8625;CS8602;CS8604;CS8600;CS8601;CS8714;CS8619;CS8629;CS8765;CS8634;CS8621;CS8767;CS8633;CS8620;CS8610;CS8631;CS8605;CS8622;CS8613;NU5100;NU5104;NU1903;NU1902;</NoWarn>
<TargetFrameworks>net9.0;net8.0;net6.0;</TargetFrameworks>
<LangVersion>12.0</LangVersion>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<Authors>Diego</Authors>
<Company>Diego</Company>
<Product>Diego</Product>
<Copyright>版权所有 © 2023-present Diego</Copyright>
<RepositoryUrl>https://gitee.com/diego2098/ThingsGateway</RepositoryUrl>
<RepositoryType>Gitee</RepositoryType>
<GenerateResxSourceIncludeDefaultValues>true</GenerateResxSourceIncludeDefaultValues>
</PropertyGroup>
<PropertyGroup>
<ItemGroup>
<None Include="$(SolutionDir)Directory.Build.props" Pack="true" PackagePath="\" />
</ItemGroup>
<AnalysisModeDesign>None</AnalysisModeDesign>
<AnalysisModeDocumentation>All</AnalysisModeDocumentation>
<AnalysisModeGlobalization>None</AnalysisModeGlobalization>
<AnalysisModeInteroperability>All</AnalysisModeInteroperability>
<AnalysisModeMaintainability>All</AnalysisModeMaintainability>
<AnalysisModeNaming>None</AnalysisModeNaming>
<AnalysisModePerformance>All</AnalysisModePerformance>
<AnalysisModeSingleFile>All</AnalysisModeSingleFile>
<AnalysisModeReliability>All</AnalysisModeReliability>
<AnalysisModeSecurity>All</AnalysisModeSecurity>
<AnalysisModeUsage>None</AnalysisModeUsage>
<AnalysisModeStyle>None</AnalysisModeStyle>
<PropertyGroup>
<DebugSymbols>True</DebugSymbols>
<DebugType>Embedded</DebugType>
<EmbedAllSources>True</EmbedAllSources>
</PropertyGroup>
<NoWarn>CS8603;CS8618;CS1591;CS8625;CS8602;CS8604;CS8600;CS8601;CS8714;CS8619;CS8629;CS8765;CS8634;CS8621;CS8767;CS8633;CS8620;CS8610;CS8631;CS8605;CS8622;CS8613;NU5100;NU5104;NU1903;NU1902;</NoWarn>
<TargetFrameworks>net9.0;net8.0;net6.0;</TargetFrameworks>
<LangVersion>12.0</LangVersion>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<Authors>Diego</Authors>
<Company>Diego</Company>
<Product>Diego</Product>
<Copyright>版权所有 © 2023-present Diego</Copyright>
<RepositoryUrl>https://gitee.com/diego2098/ThingsGateway</RepositoryUrl>
<RepositoryType>Gitee</RepositoryType>
<GenerateResxSourceIncludeDefaultValues>true</GenerateResxSourceIncludeDefaultValues>
</PropertyGroup>
<ItemGroup>
<None Include="$(SolutionDir)Directory.Build.props" Pack="true" PackagePath="\" />
</ItemGroup>
<PropertyGroup>
<DebugSymbols>True</DebugSymbols>
<DebugType>Embedded</DebugType>
<EmbedAllSources>True</EmbedAllSources>
</PropertyGroup>
</Project>

View File

@@ -134,7 +134,11 @@ public static class CSharpScriptEngineExtension
return runScript;
}
public static void SetExpire(string source, TimeSpan? timeSpan = null)
{
var field = $"{CacheKey}-{source}";
Instance.SetExpire(field, timeSpan ?? TimeSpan.FromHours(1));
}
}

View File

@@ -18,6 +18,8 @@ using ThingsGateway.NewLife;
using ThingsGateway.NewLife.Caching;
using ThingsGateway.NewLife.Threading;
using TouchSocket.Core;
namespace ThingsGateway.Gateway.Application.Extensions;
/// <summary>
@@ -25,6 +27,8 @@ namespace ThingsGateway.Gateway.Application.Extensions;
/// </summary>
public interface ReadWriteExpressions
{
public TouchSocket.Core.ILog? Logger { get; set; }
/// <summary>
/// 获取新值
/// </summary>
@@ -125,6 +129,7 @@ public static class ExpressionEvaluatorExtension
{_using}
public class Script:ReadWriteExpressions
{{
public TouchSocket.Core.ILog? Logger {{ get; set; }}
public object GetNewValue(object raw)
{{
{_body};
@@ -134,6 +139,7 @@ public static class ExpressionEvaluatorExtension
GC.Collect();
Instance.Set(field, runScript);
}
return runScript;
}
@@ -151,6 +157,21 @@ public static class ExpressionEvaluatorExtension
return value;
}
/// <summary>
/// 计算表达式:例如:(int)raw*100raw为原始值
/// </summary>
public static object GetExpressionsResult(this string expressions, object rawvalue, ILog logger)
{
if (string.IsNullOrWhiteSpace(expressions))
{
return rawvalue;
}
var readWriteExpressions = GetReadWriteExpressions(expressions);
readWriteExpressions.Logger = logger;
var value = readWriteExpressions.GetNewValue(rawvalue);
return value;
}
/// <summary>
/// 执行脚本获取返回值ReadWriteExpressions
/// </summary>
@@ -169,4 +190,9 @@ public static class ExpressionEvaluatorExtension
return runScript;
}
public static void SetExpire(string source, TimeSpan? timeSpan = null)
{
var field = $"{CacheKey}-{source}";
Instance.SetExpire(field, timeSpan ?? TimeSpan.FromHours(1));
}
}

View File

@@ -20,6 +20,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="ThingsGateway.NewLife.X" Version="$(AdminVersion)" />
<ProjectReference Include="..\ThingsGateway.Foundation\ThingsGateway.Foundation.csproj" />
</ItemGroup>
</Project>

View File

@@ -26,7 +26,7 @@
<div class="p-1">
<BootstrapInputNumber @bind-Value=@ArrayLength ShowLabel="true" />
<Select @bind-Value="@DataType" ShowLabel="true" IsPopover="true"/>
<Select @bind-Value="@DataType" ShowLabel="true" IsPopover="true" />
</div>
</div>
@@ -106,10 +106,11 @@
</div>
<div class="col-12 col-md-7 ">
@if (Plc?.Logger != null || Logger!=null)
{
<LogConsole Logger=@(Plc?.Logger??Logger) LogPath=@LogPath HeaderText=@HeaderText></LogConsole>
}
<LogConsole LogLevel=@((Plc?.Logger??Logger)?.LogLevel??TouchSocket.Core.LogLevel.Trace) LogLevelChanged="(a)=>{
var log=Plc?.Logger??Logger;
if(log!=null)
log.LogLevel=a;
}" LogPath=@LogPath HeaderText=@HeaderText></LogConsole>
</div>
</div>

View File

@@ -71,7 +71,7 @@ public partial class AdapterDebugComponent : AdapterDebugBase
{
if (Plc != null)
{
var deviceVariableSourceReads = Plc.LoadSourceRead<VariableSourceClass>(VariableRunTimes, MaxPack, 1000);
var deviceVariableSourceReads = Plc.LoadSourceRead<VariableSourceClass>(VariableRunTimes, MaxPack, "1000");
foreach (var item in deviceVariableSourceReads)
{
var result = await Plc.ReadAsync(item.RegisterAddress, item.Length);
@@ -113,13 +113,13 @@ public partial class AdapterDebugComponent : AdapterDebugBase
{
DataType=DataTypeEnum.Int16,
RegisterAddress="40001",
IntervalTime=1000,
IntervalTime="1000",
},
new VariableClass()
{
DataType=DataTypeEnum.Int32,
RegisterAddress="40011",
IntervalTime=1000,
IntervalTime="1000",
},
};

View File

@@ -115,7 +115,7 @@ public partial class ChannelDataDebugComponent : ComponentBase
{
try
{
ChannelData.CreateChannel(Model);
await ChannelData.CreateChannelAsync(Model);
if (OnConfimClick.HasDelegate)
await OnConfimClick.InvokeAsync(Model);
}

View File

@@ -10,9 +10,9 @@
<HeaderTemplate>
<div class="flex-fill">
</div>
@if (Logger!=null)
@if (LogLevelChanged.HasDelegate)
{
<Select @bind-Value="@Logger.LogLevel" IsPopover></Select>
<Select Value="@LogLevel" ValueChanged="LogLevelChanged" IsPopover></Select>
}
<Button Color="Color.None" style="color: var(--bs-card-title-color);" Icon=@(IsPause?"fa fa-play":"fa fa-pause") OnClick="Pause" />
<Button IsAsync Color="Color.None" style="color: var(--bs-card-title-color);" Icon=@("fa fa-sign-out") OnClick="HandleOnExportClick" />

View File

@@ -31,7 +31,10 @@ public partial class LogConsole : IDisposable
public bool Disposed { get; set; }
[Parameter, EditorRequired]
public ILog Logger { get; set; }
public LogLevel LogLevel { get; set; }
[Parameter]
public EventCallback<LogLevel> LogLevelChanged { get; set; }
[Parameter]
public string HeaderText { get; set; } = "Log";
@@ -96,7 +99,7 @@ public partial class LogConsole : IDisposable
var result = TextFileReader.LastLog(files.FirstOrDefault().FullName, 0);
if (result.IsSuccess)
{
Messages = result.Content.Where(a => a.LogLevel >= Logger?.LogLevel).Select(a => new LogMessage((int)a.LogLevel, $"{a.LogTime} - {a.Message}{(a.ExceptionString.IsNullOrWhiteSpace() ? null : $"{Environment.NewLine}{a.ExceptionString}")}")).ToList();
Messages = result.Content.Where(a => a.LogLevel >= LogLevel).Select(a => new LogMessage((int)a.LogLevel, $"{a.LogTime} - {a.Message}{(a.ExceptionString.IsNullOrWhiteSpace() ? null : $"{Environment.NewLine}{a.ExceptionString}")}")).ToList();
}
else
{

View File

@@ -16,7 +16,7 @@
<ItemGroup>
<PackageReference Include="ThingsGateway.Razor" Version="$(AdminVersion)" />
<ProjectReference Include="..\..\..\Admin\src\ThingsGateway.Razor\ThingsGateway.Razor.csproj" />
</ItemGroup>

View File

@@ -11,6 +11,8 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Linq.Expressions;
using ThingsGateway.Gateway.Application.Extensions;
using ThingsGateway.NewLife.Reflection;
@@ -78,6 +80,38 @@ public abstract class VariableObject
return jToken;
}
/// <summary>
/// GetBytes
/// </summary>
/// <returns></returns>
public virtual byte[] GetBytes(Expression<Func<object>> accessor)
{
if (accessor.Body == null)
{
throw new ArgumentNullException(nameof(accessor));
}
var expression = accessor.Body;
if (expression is UnaryExpression unaryExpression && unaryExpression.NodeType == ExpressionType.Convert && unaryExpression.Type == typeof(object))
{
expression = unaryExpression.Operand;
}
if (expression is not MemberExpression memberExpression)
{
throw new ArgumentException("Can only access properties");
}
// 从字典中获取与属性对应的变量信息
if (!VariableRuntimePropertyDict.TryGetValue(memberExpression.Member.Name, out var variable))
{
throw new KeyNotFoundException($"Variable for {memberExpression.Member.Name} not found.");
}
var func = accessor.Compile();
return variable.VariableClass.ThingsGatewayBitConverter.GetBytesFormData(GetExpressionsValue(func(), variable), variable.VariableClass.DataType);
}
/// <summary>
/// GetVariableClass
/// </summary>
@@ -93,7 +127,7 @@ public abstract class VariableObject
{
DataType = dataType,
RegisterAddress = pair.Value.Attribute.RegisterAddress,
IntervalTime = 1000,
IntervalTime = "1000",
};
pair.Value.VariableClass = variableClass;
variableClasss.Add(variableClass);
@@ -205,7 +239,7 @@ public abstract class VariableObject
if (DeviceVariableSourceReads == null)
{
List<VariableClass> variableClasss = GetVariableClass();
DeviceVariableSourceReads = Protocol.LoadSourceRead<VariableSourceClass>(variableClasss, MaxPack, 1000);
DeviceVariableSourceReads = Protocol.LoadSourceRead<VariableSourceClass>(variableClasss, MaxPack, "1000");
}
}
}

View File

@@ -98,7 +98,7 @@ public class ChannelData
/// 创建通道
/// </summary>
/// <param name="channelData"></param>
public static void CreateChannel(ChannelData channelData)
public static async Task CreateChannelAsync(ChannelData channelData)
{
if (channelData.Channel != null)
{
@@ -116,19 +116,19 @@ public class ChannelData
switch (channelData.ChannelType)
{
case ChannelTypeEnum.TcpClient:
channelData.Channel = channelData.TouchSocketConfig.GetTcpClientWithIPHost(channelData.RemoteUrl, channelData.BindUrl);
channelData.Channel = await channelData.TouchSocketConfig.GetTcpClientWithIPHostAsync(channelData.RemoteUrl, channelData.BindUrl).ConfigureAwait(false);
break;
case ChannelTypeEnum.TcpService:
channelData.Channel = channelData.TouchSocketConfig.GetTcpServiceWithBindIPHost(channelData.BindUrl);
channelData.Channel = await channelData.TouchSocketConfig.GetTcpServiceWithBindIPHostAsync(channelData.BindUrl).ConfigureAwait(false);
break;
case ChannelTypeEnum.SerialPort:
channelData.Channel = channelData.TouchSocketConfig.GetSerialPortWithOption(channelData.Map<SerialPortOption>());
channelData.Channel = await channelData.TouchSocketConfig.GetSerialPortWithOptionAsync(channelData.Map<SerialPortOption>()).ConfigureAwait(false);
break;
case ChannelTypeEnum.UdpSession:
channelData.Channel = channelData.TouchSocketConfig.GetUdpSessionWithIPHost(channelData.RemoteUrl, channelData.BindUrl);
channelData.Channel = await channelData.TouchSocketConfig.GetUdpSessionWithIPHostAsync(channelData.RemoteUrl, channelData.BindUrl).ConfigureAwait(false);
break;
}
}

View File

@@ -80,7 +80,7 @@ public static class ChannelConfigExtensions
/// <param name="serialPortOption">串口配置</param>
/// <returns></returns>
/// <exception cref="ArgumentNullException"></exception>
public static IChannel? GetChannel(this TouchSocketConfig config, ChannelTypeEnum channelType, string? remoteUrl = default, string? bindUrl = default, SerialPortOption? serialPortOption = default)
public static async ValueTask<IChannel?> GetChannelAsync(this TouchSocketConfig config, ChannelTypeEnum channelType, string? remoteUrl = default, string? bindUrl = default, SerialPortOption? serialPortOption = default)
{
config.ThrowIfNull(nameof(TouchSocketConfig));
channelType.ThrowIfNull(nameof(ChannelTypeEnum));
@@ -90,20 +90,20 @@ public static class ChannelConfigExtensions
{
case ChannelTypeEnum.TcpClient:
remoteUrl.ThrowIfNull(nameof(IPHost));
return config.GetTcpClientWithIPHost(remoteUrl, bindUrl);
return await config.GetTcpClientWithIPHostAsync(remoteUrl, bindUrl).ConfigureAwait(false);
case ChannelTypeEnum.TcpService:
bindUrl.ThrowIfNull(nameof(IPHost));
return config.GetTcpServiceWithBindIPHost(bindUrl);
return await config.GetTcpServiceWithBindIPHostAsync(bindUrl).ConfigureAwait(false);
case ChannelTypeEnum.SerialPort:
serialPortOption.ThrowIfNull(nameof(SerialPortOption));
return config.GetSerialPortWithOption(serialPortOption);
return await config.GetSerialPortWithOptionAsync(serialPortOption).ConfigureAwait(false);
case ChannelTypeEnum.UdpSession:
if (string.IsNullOrEmpty(remoteUrl) && string.IsNullOrEmpty(bindUrl))
throw new ArgumentNullException(nameof(IPHost));
return config.GetUdpSessionWithIPHost(remoteUrl, bindUrl);
return await config.GetUdpSessionWithIPHostAsync(remoteUrl, bindUrl).ConfigureAwait(false);
}
return default;
}
@@ -114,14 +114,14 @@ public static class ChannelConfigExtensions
/// <param name="config">配置</param>
/// <param name="serialPortOption">串口配置</param>
/// <returns></returns>
public static SerialPortChannel GetSerialPortWithOption(this TouchSocketConfig config, SerialPortOption serialPortOption)
public static async ValueTask<SerialPortChannel> GetSerialPortWithOptionAsync(this TouchSocketConfig config, SerialPortOption serialPortOption)
{
serialPortOption.ThrowIfNull(nameof(SerialPortOption));
config.SetSerialPortOption(serialPortOption);
//载入配置
SerialPortChannel serialPortChannel = new SerialPortChannel();
serialPortChannel.Setup(config);
await serialPortChannel.SetupAsync(config).ConfigureAwait(false);
return serialPortChannel;
}
@@ -134,7 +134,7 @@ public static class ChannelConfigExtensions
/// <param name="bindUrl">本地IP端口配置</param>
/// <returns></returns>
/// <exception cref="ArgumentNullException"></exception>
public static TcpClientChannel GetTcpClientWithIPHost(this TouchSocketConfig config, string remoteUrl, string? bindUrl = default)
public static async ValueTask<TcpClientChannel> GetTcpClientWithIPHostAsync(this TouchSocketConfig config, string remoteUrl, string? bindUrl = default)
{
remoteUrl.ThrowIfNull(nameof(IPHost));
config.SetRemoteIPHost(remoteUrl);
@@ -143,7 +143,7 @@ public static class ChannelConfigExtensions
//载入配置
TcpClientChannel tcpClientChannel = new TcpClientChannel();
tcpClientChannel.Setup(config);
await tcpClientChannel.SetupAsync(config).ConfigureAwait(false);
return tcpClientChannel;
}
@@ -154,7 +154,7 @@ public static class ChannelConfigExtensions
/// <param name="bindUrl">本地IP端口配置</param>
/// <returns></returns>
/// <exception cref="ArgumentNullException"></exception>
public static TcpServiceChannel GetTcpServiceWithBindIPHost(this TouchSocketConfig config, string bindUrl)
public static async ValueTask<TcpServiceChannel> GetTcpServiceWithBindIPHostAsync(this TouchSocketConfig config, string bindUrl)
{
bindUrl.ThrowIfNull(nameof(IPHost));
@@ -162,7 +162,7 @@ public static class ChannelConfigExtensions
config.SetListenIPHosts(IPHost.ParseIPHosts(urls));
//载入配置
TcpServiceChannel tcpServiceChannel = new TcpServiceChannel();
tcpServiceChannel.Setup(config);
await tcpServiceChannel.SetupAsync(config).ConfigureAwait(false);
return tcpServiceChannel;
}
@@ -174,7 +174,7 @@ public static class ChannelConfigExtensions
/// <param name="bindUrl">本地IP端口配置</param>
/// <returns></returns>
/// <exception cref="ArgumentNullException"></exception>
public static UdpSessionChannel GetUdpSessionWithIPHost(this TouchSocketConfig config, string? remoteUrl, string? bindUrl)
public static async ValueTask<UdpSessionChannel> GetUdpSessionWithIPHostAsync(this TouchSocketConfig config, string? remoteUrl, string? bindUrl)
{
if (string.IsNullOrEmpty(remoteUrl) && string.IsNullOrEmpty(bindUrl))
throw new ArgumentNullException(nameof(IPHost));
@@ -195,7 +195,7 @@ public static class ChannelConfigExtensions
config.UseUdpConnReset();
}
#endif
udpSessionChannel.Setup(config);
await udpSessionChannel.SetupAsync(config).ConfigureAwait(false);
return udpSessionChannel;
}
}

View File

@@ -36,6 +36,10 @@ public interface IChannel : ISetupConfigObject, IDisposable, IClosableClient
/// </summary>
public bool Online { get; }
/// <summary>
/// MaxSign
/// </summary>
public int MaxSign { get; set; }
/// <summary>
/// 通道启动成功后
/// </summary>

View File

@@ -36,6 +36,7 @@ public class DtuPlugin : PluginBase, ITcpReceivingPlugin
{
oldClient.TryShutdown();
await oldClient.CloseAsync().ConfigureAwait(false);
oldClient.Dispose();
}
catch
{
@@ -52,6 +53,7 @@ public class DtuPlugin : PluginBase, ITcpReceivingPlugin
{
socket.TryShutdown();
await socket.CloseAsync().ConfigureAwait(false);
socket.Dispose();
}
catch
{

View File

@@ -17,4 +17,9 @@ public interface IDtu : ITcpService
/// 心跳检测(大写16进制字符串)
/// </summary>
public string HeartbeatHexString { get; set; }
/// <summary>
/// 默认Dtu注册包,utf-8字符串
/// </summary>
public string DtuId { get; set; }
}

View File

@@ -24,6 +24,7 @@ public class SerialPortChannel : SerialPortClient, IClientChannel
{
WaitHandlePool.MaxSign = ushort.MaxValue;
}
public int MaxSign { get => WaitHandlePool.MaxSign; set => WaitHandlePool.MaxSign = value; }
/// <inheritdoc/>
public ChannelReceivedEventHandler ChannelReceived { get; set; } = new();
@@ -117,8 +118,9 @@ public class SerialPortChannel : SerialPortClient, IClientChannel
/// <inheritdoc/>
public override string? ToString()
{
if (ProtectedMainSerialPort != null)
return $"{ProtectedMainSerialPort.PortName}[{ProtectedMainSerialPort.BaudRate},{ProtectedMainSerialPort.DataBits},{ProtectedMainSerialPort.StopBits},{ProtectedMainSerialPort.Parity}]";
var port = Config?.GetValue(SerialPortConfigExtension.SerialPortOptionProperty);
if (port != null)
return $"{port.PortName}[{port.BaudRate},{port.DataBits},{port.StopBits},{port.Parity}]";
return base.ToString();
}

View File

@@ -22,6 +22,7 @@ public class TcpClientChannel : TcpClient, IClientChannel
{
WaitHandlePool.MaxSign = ushort.MaxValue;
}
public int MaxSign { get => WaitHandlePool.MaxSign; set => WaitHandlePool.MaxSign = value; }
/// <inheritdoc/>
public ChannelReceivedEventHandler ChannelReceived { get; set; } = new();

View File

@@ -188,9 +188,11 @@ public class TcpServiceChannel : TcpServiceChannelBase<TcpSessionClientChannel>,
/// <inheritdoc/>
protected override TcpSessionClientChannel NewClient()
{
return new TcpSessionClientChannel();
var data = new TcpSessionClientChannel();
data.WaitHandlePool.MaxSign = MaxSign;
return data;
}
public int MaxSign { get; set; }
/// <inheritdoc/>
protected override async Task OnTcpClosing(TcpSessionClientChannel socketClient, ClosingEventArgs e)
{

View File

@@ -20,6 +20,7 @@ public class TcpSessionClientChannel : TcpSessionClient, IClientChannel
{
WaitHandlePool.MaxSign = ushort.MaxValue;
}
public int MaxSign { get => WaitHandlePool.MaxSign; set => WaitHandlePool.MaxSign = value; }
/// <inheritdoc/>
public ChannelReceivedEventHandler ChannelReceived { get; set; } = new();
@@ -84,6 +85,11 @@ public class TcpSessionClientChannel : TcpSessionClient, IClientChannel
return EasyTask.CompletedTask;
}
public override async Task ResetIdAsync(string newId)
{
await base.ResetIdAsync(newId).ConfigureAwait(false);
}
/// <inheritdoc/>
public override string ToString()
{

View File

@@ -22,6 +22,7 @@ public class UdpSessionChannel : UdpSession, IClientChannel
{
WaitHandlePool.MaxSign = ushort.MaxValue;
}
public int MaxSign { get => WaitHandlePool.MaxSign; set => WaitHandlePool.MaxSign = value; }
/// <inheritdoc/>
public ChannelReceivedEventHandler ChannelReceived { get; set; } = new();
@@ -52,7 +53,7 @@ public class UdpSessionChannel : UdpSession, IClientChannel
/// <summary>
/// 等待池
/// </summary>
public WaitHandlePool<MessageBase> WaitHandlePool { get; } = new();
public WaitHandlePool<MessageBase> WaitHandlePool { get; set; } = new();
/// <inheritdoc/>
public WaitLock WaitLock { get; } = new WaitLock();

View File

@@ -8,6 +8,8 @@
// QQ群605534569
//------------------------------------------------------------------------------
using ThingsGateway.NewLife.Threading;
namespace ThingsGateway.Foundation;
/// <summary>
@@ -19,20 +21,26 @@ public class TimeTick
/// 时间间隔(毫秒)
/// </summary>
private readonly int intervalMilliseconds = 1000;
private readonly Cron cron;
/// <inheritdoc cref="TimeTick"/>
public TimeTick(int intervalMilliseconds = 1000)
public TimeTick(string delay)
{
if (intervalMilliseconds < 10)
intervalMilliseconds = 10;
LastTime = DateTime.Now.AddMilliseconds(-intervalMilliseconds);
this.intervalMilliseconds = intervalMilliseconds;
if (int.TryParse(delay, out intervalMilliseconds))
{
if (intervalMilliseconds < 10)
intervalMilliseconds = 10;
LastTime = DateTime.Now.AddMilliseconds(-intervalMilliseconds);
}
else
{
cron = new Cron(delay);
}
}
/// <summary>
/// 上次触发时间
/// </summary>
public DateTime LastTime { get; private set; }
public DateTime LastTime { get; private set; } = DateTime.Now;
/// <summary>
/// 是否触发时间刻度
@@ -41,15 +49,34 @@ public class TimeTick
/// <returns>是否触发时间刻度</returns>
public bool IsTickHappen(DateTime currentTime)
{
var nextTime = LastTime.AddMilliseconds(intervalMilliseconds);
var diffMilliseconds = (currentTime - nextTime).TotalMilliseconds;
if (diffMilliseconds < 0)
return false;
else if (diffMilliseconds > intervalMilliseconds)
LastTime = currentTime; //选择当前时间
DateTime nextTime = DateTime.MinValue;
if (cron == null)
{
nextTime = LastTime.AddMilliseconds(intervalMilliseconds);
var diffMilliseconds = (currentTime - nextTime).TotalMilliseconds;
if (diffMilliseconds < 0)
return false;
else if (diffMilliseconds * 2 < intervalMilliseconds)
LastTime = nextTime;
else
LastTime = nextTime;//选择当前时间
return true;
}
else
LastTime = nextTime;
return true;
{
nextTime = cron.GetNext(LastTime);
if (currentTime >= nextTime)
{
LastTime = nextTime;
return true;
}
else
{
return false;
}
}
}
/// <summary>

View File

@@ -17,6 +17,10 @@ namespace ThingsGateway.Foundation;
/// <inheritdoc/>
public static class ByteExtensions
{
public static T[] SpliceArray<T>(this T[] array, params T[] values)
{
return DataTransUtil.SpliceArray<T>(array, values);
}
/// <summary>
/// 获取byte数据类型的第offset位是否为True<br />
/// </summary>

View File

@@ -51,6 +51,16 @@ public static class LoggerExtensions
return GetDebugLogBasePath().CombinePath(channelId.ToString()).FileNameReplace();
}
/// <summary>
/// 获取DEBUG日志路径
/// </summary>
/// <param name="channelId"></param>
/// <returns></returns>
public static string GetDebugLogPath(this string channelId)
{
return GetDebugLogBasePath().CombinePath(channelId.ToString()).FileNameReplace();
}
/// <summary>
/// GetLogBasePath
/// </summary>

View File

@@ -167,8 +167,11 @@ public static class StringExtensions
objResult = value;
else if (propertyType == typeof(IPAddress))
objResult = IPAddress.Parse(value);
else if (propertyType == typeof(object))
objResult = value.GetJTokenFromString();
else if (propertyType.IsEnum)
objResult = Enum.Parse(propertyType, value);
else
{
objResult = null;

View File

@@ -116,7 +116,7 @@ public interface IProtocol : IDisposable
/// <summary>
/// 连读寄存器打包
/// </summary>
List<T> LoadSourceRead<T>(IEnumerable<IVariable> deviceVariables, int maxPack, int defaultIntervalTime) where T : IVariableSource, new();
List<T> LoadSourceRead<T>(IEnumerable<IVariable> deviceVariables, int maxPack, string defaultIntervalTime) where T : IVariableSource, new();
#endregion

View File

@@ -174,7 +174,7 @@ public abstract class ProtocolBase : DisposableObject, IProtocol
#region
/// <inheritdoc/>
public abstract List<T> LoadSourceRead<T>(IEnumerable<IVariable> deviceVariables, int maxPack, int defaultIntervalTime) where T : IVariableSource, new();
public abstract List<T> LoadSourceRead<T>(IEnumerable<IVariable> deviceVariables, int maxPack, string defaultIntervalTime) where T : IVariableSource, new();
/// <inheritdoc/>
public virtual string GetAddressDescription()

View File

@@ -10,9 +10,9 @@
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Localization.Abstractions" Version="9.0.0" />
<PackageReference Include="ThingsGateway.NewLife.X" Version="$(AdminVersion)" />
<PackageReference Include="TouchSocket" Version="3.0.7" />
<PackageReference Include="TouchSocket.SerialPorts" Version="3.0.7" />
<ProjectReference Include="..\..\..\Admin\src\ThingsGateway.NewLife.X\ThingsGateway.NewLife.X.csproj" />
<PackageReference Include="TouchSocket" Version="3.0.10" />
<PackageReference Include="TouchSocket.SerialPorts" Version="3.0.10" />
</ItemGroup>
</Project>

View File

@@ -28,15 +28,17 @@ public static class JTokenUtil
{
try
{
if (item.IsNullOrWhiteSpace())
return JValue.CreateNull();
if (bool.TryParse(item, out bool parseBool))
return new JValue(parseBool);
// 尝试解析字符串为 JToken 对象
return JToken.Parse(item);
}
catch
{
if (bool.TryParse(item, out bool parseBool))
{
return new JValue(parseBool);
}
// 解析失败时,将其转为 String 类型的 JValue
return new JValue(item);
}

View File

@@ -28,7 +28,7 @@ public interface IVariable
/// <summary>
/// 执行间隔
/// </summary>
int? IntervalTime { get; set; }
string? IntervalTime { get; set; }
/// <summary>
/// 寄存器地址

View File

@@ -31,7 +31,7 @@ public class VariableClass : IVariable
/// <summary>
/// 执行间隔
/// </summary>
public virtual int? IntervalTime { get; set; }
public virtual string? IntervalTime { get; set; }
/// <inheritdoc/>
public bool IsOnline { get; set; }

View File

@@ -1,6 +1,6 @@
<Project>
<PropertyGroup>
<Version>9.0.2.30</Version>
<Version>9.0.3.6</Version>
</PropertyGroup>
<ItemGroup>

View File

@@ -23,9 +23,9 @@ namespace ThingsGateway.Gateway.Application;
[Route("openApi/configInfo")]
[RolePermission]
[Authorize(AuthenticationSchemes = "Bearer")]
public class ConfigInfoControler : ControllerBase
public class ConfigInfoController : ControllerBase
{
public ConfigInfoControler(
public ConfigInfoController(
IChannelService channelService,
IVariableService variableService,
IDeviceService deviceService)

View File

@@ -23,10 +23,10 @@ namespace ThingsGateway.Gateway.Application;
[RolePermission]
[LoggingMonitor]
[Authorize(AuthenticationSchemes = "Bearer")]
public class ControlControler : ControllerBase
public class ControlController : ControllerBase
{
private ISysUserService _sysUserService;
public ControlControler(IRpcService rpcService, ISysUserService sysUserService)
public ControlController(IRpcService rpcService, ISysUserService sysUserService)
{
_sysUserService = sysUserService;
_rpcService = rpcService;
@@ -34,6 +34,31 @@ public class ControlControler : ControllerBase
private IRpcService _rpcService { get; set; }
/// <summary>
/// 清空全部缓存
/// </summary>
/// <returns></returns>
[HttpPost("removeAllCache")]
[DisplayName("清空全部缓存")]
public void RemoveAllCache()
{
App.CacheService.Clear();
}
/// <summary>
/// 删除通道/设备缓存
/// </summary>
/// <returns></returns>
[HttpPost("removeCache")]
[DisplayName("删除通道/设备缓存")]
public void RemoveCache()
{
App.GetService<IDeviceService>().DeleteDeviceFromCache();
App.GetService<IChannelService>().DeleteChannelFromCache();
}
/// <summary>
/// 控制业务线程启停
/// </summary>

View File

@@ -29,10 +29,10 @@ namespace ThingsGateway.Gateway.Application;
[Route("openApi/runtimeInfo")]
[RolePermission]
[Authorize(AuthenticationSchemes = "Bearer")]
public class RuntimeInfoControler : ControllerBase
public class RuntimeInfoController : ControllerBase
{
private ISysUserService _sysUserService;
public RuntimeInfoControler(ISysUserService sysUserService)
public RuntimeInfoController(ISysUserService sysUserService)
{
_sysUserService = sysUserService;
}

View File

@@ -61,8 +61,7 @@ public class Device : BaseDataEntity
/// </summary>
[SugarColumn(ColumnDescription = "默认执行间隔")]
[AutoGenerateColumn(Visible = true, Filterable = true, Sortable = true)]
[MinValue(10)]
public virtual int IntervalTime { get; set; } = 1000;
public virtual string IntervalTime { get; set; } = "1000";
/// <summary>
/// 插件名称

View File

@@ -10,6 +10,8 @@
using BootstrapBlazor.Components;
using Newtonsoft.Json.Linq;
using SqlSugar;
using System.Collections.Concurrent;
@@ -25,7 +27,7 @@ namespace ThingsGateway.Gateway.Application;
[Tenant(SqlSugarConst.DB_Custom)]
[SugarIndex("index_device", nameof(Variable.DeviceId), OrderByType.Asc)]
[SugarIndex("unique_variable_name", nameof(Variable.Name), OrderByType.Asc, true)]
public class Variable : BaseDataEntity
public class Variable : BaseDataEntity, IValidatableObject
{
/// <summary>
/// 设备
@@ -60,11 +62,11 @@ public class Variable : BaseDataEntity
public virtual string? Unit { get; set; }
/// <summary>
/// 执行间隔
/// 间隔时间
/// </summary>
[SugarColumn(ColumnDescription = "执行间隔", IsNullable = true)]
[SugarColumn(ColumnDescription = "间隔时间", IsNullable = true)]
[AutoGenerateColumn(Visible = true, Filterable = true, Sortable = true)]
public virtual int? IntervalTime { get; set; }
public virtual string? IntervalTime { get; set; }
/// <summary>
/// 变量地址,可能带有额外的信息,比如<see cref="DataFormatEnum"/> ,以;分割
@@ -122,11 +124,32 @@ public class Variable : BaseDataEntity
[AutoGenerateColumn(Visible = true, Filterable = true, Sortable = true)]
public virtual bool RpcWriteEnable { get; set; } = true;
/// <summary>
/// 初始值
/// </summary>
[SugarColumn(IsJson = true, ColumnDataType = StaticConfig.CodeFirst_BigString, ColumnDescription = "初始值", IsNullable = true)]
[AutoGenerateColumn(Visible = true, Filterable = true, Sortable = true)]
public virtual object? Value
{
get
{
return _value;
}
set
{
if (value != null)
_value = value?.ToString().GetJTokenFromString();
else
_value = null;
}
}
private object? _value;
/// <summary>
/// 保存值
/// 保存初始
/// </summary>
[SugarColumn(ColumnDescription = "保存值", IsNullable = true)]
[SugarColumn(ColumnDescription = "保存初始值", IsNullable = true)]
[AutoGenerateColumn(Visible = true, Filterable = true, Sortable = true)]
public virtual bool SaveValue { get; set; } = false;
@@ -395,6 +418,53 @@ public class Variable : BaseDataEntity
[System.Text.Json.Serialization.JsonIgnore]
[Newtonsoft.Json.JsonIgnore]
public ConcurrentDictionary<long, ModelValueValidateForm>? VariablePropertyModels;
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (HHAlarmEnable && HHAlarmCode == null)
{
yield return new ValidationResult("HHAlarmCode cannot be null when HHAlarmEnable is true", new[] { nameof(HHAlarmCode) });
}
if (HAlarmEnable && HAlarmCode == null)
{
yield return new ValidationResult("HAlarmCode cannot be null when HAlarmEnable is true", new[] { nameof(HAlarmCode) });
}
if (LAlarmEnable && LAlarmCode == null)
{
yield return new ValidationResult("LAlarmCode cannot be null when LAlarmEnable is true", new[] { nameof(LAlarmCode) });
}
if (LLAlarmEnable && LLAlarmCode == null)
{
yield return new ValidationResult("LLAlarmCode cannot be null when LLAlarmEnable is true", new[] { nameof(LLAlarmCode) });
}
if (HHAlarmEnable && HAlarmEnable && HHAlarmCode <= HAlarmCode)
{
yield return new ValidationResult("HHAlarmCode must be greater than HAlarmCode", new[] { nameof(HHAlarmCode), nameof(HAlarmCode) });
}
if (HAlarmEnable && LAlarmEnable && HAlarmCode <= LAlarmCode)
{
yield return new ValidationResult("HAlarmCode must be greater than LAlarmCode", new[] { nameof(HAlarmCode), nameof(LAlarmCode) });
}
if (LAlarmEnable && LLAlarmEnable && LAlarmCode <= LLAlarmCode)
{
yield return new ValidationResult("LAlarmCode must be greater than LLAlarmCode", new[] { nameof(LAlarmCode), nameof(LLAlarmCode) });
}
if (HHAlarmEnable && LAlarmEnable && HHAlarmCode <= LAlarmCode)
{
yield return new ValidationResult("HHAlarmCode should be greater than or less than LAlarmCode", new[] { nameof(HHAlarmCode), nameof(LAlarmCode) });
}
if (HHAlarmEnable && LLAlarmEnable && HHAlarmCode <= LLAlarmCode)
{
yield return new ValidationResult("HHAlarmCode should be greater than or less than LLAlarmCode", new[] { nameof(HHAlarmCode), nameof(LLAlarmCode) });
}
if (HAlarmEnable && LLAlarmEnable && HAlarmCode <= LLAlarmCode)
{
yield return new ValidationResult("HAlarmCode should be greater than or less than LLAlarmCode", new[] { nameof(HAlarmCode), nameof(LLAlarmCode) });
}
}
}
public class ModelValueValidateForm

View File

@@ -182,14 +182,6 @@ internal sealed class AlarmHostedService : BackgroundService, IAlarmHostedServic
return AlarmTypeEnum.H; // 返回高报警类型枚举
}
// 检查是否启用了低报警功能,并且变量的值小于低报警的限制值
if (tag.LAlarmEnable && tag.Value.ToDecimal() < tag.LAlarmCode.ToDecimal())
{
limit = tag.LAlarmCode.ToString()!; // 将报警限制值设置为低报警的限制值
expressions = tag.LRestrainExpressions!; // 获取低报警的约束表达式
text = tag.LAlarmText!; // 获取低报警时的报警文本
return AlarmTypeEnum.L; // 返回低报警类型枚举
}
// 检查是否启用了低低报警功能,并且变量的值小于低低报警的限制值
if (tag.LLAlarmEnable && tag.Value.ToDecimal() < tag.LLAlarmCode.ToDecimal())
@@ -200,6 +192,16 @@ internal sealed class AlarmHostedService : BackgroundService, IAlarmHostedServic
return AlarmTypeEnum.LL; // 返回低低报警类型枚举
}
// 检查是否启用了低报警功能,并且变量的值小于低报警的限制值
if (tag.LAlarmEnable && tag.Value.ToDecimal() < tag.LAlarmCode.ToDecimal())
{
limit = tag.LAlarmCode.ToString()!; // 将报警限制值设置为低报警的限制值
expressions = tag.LRestrainExpressions!; // 获取低报警的约束表达式
text = tag.LAlarmText!; // 获取低报警时的报警文本
return AlarmTypeEnum.L; // 返回低报警类型枚举
}
return null; // 如果不符合任何报警条件则返回null
}

View File

@@ -173,7 +173,7 @@ internal sealed class BusinessDeviceHostedService : DeviceHostedService, IBusine
{
// 创建新的设备驱动并获取对应的通道线程
DriverBase newDriverBase = dev.CreateDriver(PluginService);
var newChannelThread = GetChannelThread(newDriverBase);
var newChannelThread = await GetChannelThreadAsync(newDriverBase).ConfigureAwait(false);
// 如果找到了对应的通道线程
if (newChannelThread != null)
@@ -297,21 +297,21 @@ internal sealed class BusinessDeviceHostedService : DeviceHostedService, IBusine
_logger.LogInformation(BusinessDeviceHostedServiceLocalizer["DeviceRuntimeGeted"]);
var idSet = deviceRunTimes.Where(a => a.RedundantEnable && a.RedundantDeviceId != null).Select(a => a.RedundantDeviceId ?? 0).ToHashSet().ToDictionary(a => a);
var result = deviceRunTimes.Where(a => !idSet.ContainsKey(a.Id));
result.ParallelForEach(businessDeviceRunTime =>
{
if (!_stoppingToken.IsCancellationRequested)
{
try
{
DriverBase driverBase = businessDeviceRunTime.CreateDriver(PluginService);
GetChannelThread(driverBase);
}
catch (Exception ex)
{
_logger.LogError(ex, Localizer["InitError", businessDeviceRunTime.Name]);
}
}
});
await result.ParallelForEachAsync(async (businessDeviceRunTime, token) =>
{
if (!token.IsCancellationRequested)
{
try
{
DriverBase driverBase = businessDeviceRunTime.CreateDriver(PluginService);
await GetChannelThreadAsync(driverBase).ConfigureAwait(false);
}
catch (Exception ex)
{
_logger.LogError(ex, Localizer["InitError", businessDeviceRunTime.Name]);
}
}
}, Environment.ProcessorCount / 2 == 0 ? 1 : Environment.ProcessorCount / 2, _stoppingToken).ConfigureAwait(false);
}
for (int i = 0; i < 3; i++)
{

View File

@@ -197,7 +197,7 @@ internal sealed class CollectDeviceHostedService : DeviceHostedService, ICollect
{
// 创建新的设备驱动并获取对应的通道线程
DriverBase newDriverBase = dev.CreateDriver(PluginService);
var newChannelThread = GetChannelThread(newDriverBase);
var newChannelThread = await GetChannelThreadAsync(newDriverBase).ConfigureAwait(false);
// 如果找到了对应的通道线程
if (newChannelThread != null)
@@ -205,47 +205,9 @@ internal sealed class CollectDeviceHostedService : DeviceHostedService, ICollect
// 如果设备已更改,则执行启动前的操作
if (isChanged)
{
try
{
//添加保存数据变量读取操作
var saveVariable = dev.VariableRunTimes.Where(a => a.Value.SaveValue).ToDictionary(a => a.Value.Id, a => a.Value);
if (saveVariable.Count > 0)
{
var cacheDb = CacheDBUtil.GetCache(typeof(CacheDBItem<JToken>), nameof(VariableRunTime), nameof(VariableRunTime.SaveValue));
var varList = await cacheDb.DBProvider.Queryable<CacheDBItem<JToken>>().ToListAsync().ConfigureAwait(false);
for (int i = 0; i < varList.Count; i++)
{
var varValue = varList[i];
if (saveVariable.TryGetValue(varValue.Id, out var variable))
{
if (varValue.Value is JValue jValue)
{
variable.Value = jValue.Value;
}
else
{
variable.Value = varValue.Value;
}
}
}
cacheDb.SafeDispose();
}
}
catch (Exception ex)
{
_logger.LogWarning(ex, "SaveValue");
}
await OnCollectDeviceStarting().ConfigureAwait(false);
}
try
{
// 启动新的通道线程
@@ -323,7 +285,6 @@ internal sealed class CollectDeviceHostedService : DeviceHostedService, ICollect
await OnCollectDeviceStarting().ConfigureAwait(false);
}
await ReadValue().ConfigureAwait(false);
await StartAllChannelThreadsAsync().ConfigureAwait(false);
await OnCollectDeviceStarted().ConfigureAwait(false);
_ = Task.Run(() =>
@@ -343,52 +304,6 @@ internal sealed class CollectDeviceHostedService : DeviceHostedService, ICollect
}
}
private async Task ReadValue()
{
try
{
//添加保存数据变量读取操作
var saveVariable = GlobalData.ReadOnlyVariables.Where(a => a.Value.SaveValue).ToDictionary(a => a.Value.Id, a => a.Value);
var cacheDb = CacheDBUtil.GetCache(typeof(CacheDBItem<JToken>), nameof(VariableRunTime), nameof(VariableRunTime.SaveValue));
cacheDb.InitDb();
{
var varList = await cacheDb.DBProvider.Queryable<CacheDBItem<JToken>>().ToListAsync().ConfigureAwait(false);
List<long> ids = new List<long>();
for (int i = 0; i < varList.Count; i++)
{
var varValue = varList[i];
var has = saveVariable.Count > 0;
if (has && saveVariable.TryGetValue(varValue.Id, out var variable))
{
if (varValue.Value is JValue jValue)
{
variable.Value = jValue.Value;
}
else
{
variable.Value = varValue.Value;
}
}
else
{
ids.Add(varValue.Id);
}
}
if (ids.Count > 0)
{
await cacheDb.DBProvider.Deleteable<CacheDBItem<JToken>>(ids).ExecuteCommandAsync().ConfigureAwait(false);
}
cacheDb.SafeDispose();
}
}
catch (Exception ex)
{
_logger.LogWarning(ex, "SaveValue");
}
}
/// <inheritdoc/>
public async Task StopAsync(bool removeDevice)
{
@@ -436,22 +351,12 @@ internal sealed class CollectDeviceHostedService : DeviceHostedService, ICollect
try
{
//添加保存数据变量读取操作
var saveVariable = DriverBases.SelectMany(a => a.CurrentDevice.VariableRunTimes).Where(a => a.Value.SaveValue).Select(a =>
{
return new CacheDBItem<JToken>()
{
Id = a.Value.Id,
Value = JToken.FromObject(a.Value.Value)
};
}).ToList();
var saveVariable = DriverBases.SelectMany(a => a.CurrentDevice.VariableRunTimes).Where(a => a.Value.SaveValue).Select(a => (Variable)a.Value).ToList();
if (saveVariable.Count > 0)
{
var cacheDb = CacheDBUtil.GetCache(typeof(CacheDBItem<JToken>), nameof(VariableRunTime), nameof(VariableRunTime.SaveValue));
await cacheDb.DBProvider.Fastest<CacheDBItem<JToken>>().PageSize(100000).BulkMergeAsync(saveVariable).ConfigureAwait(false);
cacheDb.SafeDispose();
using var db = DbContext.Db.GetConnectionScopeWithAttr<Variable>().CopyNew();
var result = await db.Updateable<Variable>(saveVariable).UpdateColumns(a => a.Value).ExecuteCommandAsync().ConfigureAwait(false);
}
}
catch (Exception ex)
@@ -486,22 +391,22 @@ collectDeviceRunTimes.SelectMany(a =>
.Select(b => b.Value.WriteExpressions)))
.Distinct().ToList();
result.ParallelForEach(collectDeviceRunTime =>
{
if (!_stoppingToken.IsCancellationRequested)
{
try
{
DriverBase driverBase = collectDeviceRunTime.CreateDriver(PluginService);
GetChannelThread(driverBase);
await result.ParallelForEachAsync(async (collectDeviceRunTime, token) =>
{
if (!token.IsCancellationRequested)
{
try
{
DriverBase driverBase = collectDeviceRunTime.CreateDriver(PluginService);
await GetChannelThreadAsync(driverBase).ConfigureAwait(false);
}
catch (Exception ex)
{
_logger.LogError(ex, Localizer["InitError", collectDeviceRunTime.Name]);
}
}
});
}
catch (Exception ex)
{
_logger.LogError(ex, Localizer["InitError", collectDeviceRunTime.Name]);
}
}
}, Environment.ProcessorCount / 2 == 0 ? 1 : Environment.ProcessorCount / 2, _stoppingToken).ConfigureAwait(false);
scripts.ParallelForEach(script =>
{

View File

@@ -122,7 +122,7 @@ internal abstract class DeviceHostedService : BackgroundService, IDeviceHostedSe
//初始化
DriverBase newDriverBase = dev.CreateDriver(PluginService);
var newChannelThread = GetChannelThread(newDriverBase);
var newChannelThread = await GetChannelThreadAsync(newDriverBase).ConfigureAwait(false);
if (newChannelThread != null)
{
await StartChannelThreadAsync(newChannelThread).ConfigureAwait(false);
@@ -224,17 +224,18 @@ internal abstract class DeviceHostedService : BackgroundService, IDeviceHostedSe
});
}
private WaitLock NewChannelLock = new();
/// <summary>
/// 根据设备生成或获取通道线程管理器
/// </summary>
/// <param name="driverBase">驱动程序实例</param>
/// <returns>通道线程管理器</returns>
protected ChannelThread GetChannelThread(DriverBase driverBase)
protected async ValueTask<ChannelThread> GetChannelThreadAsync(DriverBase driverBase)
{
try
{
var channelId = driverBase.CurrentDevice.ChannelId;
lock (ChannelThreads)
await NewChannelLock.WaitAsync().ConfigureAwait(false);
{
// 尝试从现有的通道线程管理器列表中查找匹配的通道线程
var channelThread = ChannelThreads.FirstOrDefault(t => t.ChannelId == channelId);
@@ -242,22 +243,29 @@ internal abstract class DeviceHostedService : BackgroundService, IDeviceHostedSe
{
// 如果找到了匹配的通道线程,则将驱动程序添加到该线程中
channelThread.AddDriver(driverBase);
channelThread.Channel?.Setup(channelThread.FoundataionConfig.Clone());
if (channelThread.Channel != null)
{
await channelThread.Channel.SetupAsync(channelThread.FoundataionConfig?.Clone()).ConfigureAwait(false);
}
return channelThread;
}
// 如果未找到匹配的通道线程,则创建一个新的通道线程
return NewChannelThread(driverBase, channelId);
return await NewChannelThreadAsync(driverBase, channelId).ConfigureAwait(false);
}
}
catch (Exception ex)
{
driverBase.SafeDispose();
_logger.LogWarning(ex, nameof(GetChannelThread));
_logger.LogWarning(ex, nameof(GetChannelThreadAsync));
return null;
}
finally
{
NewChannelLock.Release();
}
// 创建新的通道线程的内部方法
ChannelThread NewChannelThread(DriverBase driverBase, long channelId)
async ValueTask<ChannelThread> NewChannelThreadAsync(DriverBase driverBase, long channelId)
{
// 根据通道ID获取通道信息
var channel = ChannelService.GetChannelById(channelId);
@@ -290,13 +298,19 @@ internal abstract class DeviceHostedService : BackgroundService, IDeviceHostedSe
var wts = Math.Max(ChannelThreads.Count, 10) * 10;
ThreadPool.SetMaxThreads(wts, wts);
var config = new TouchSocketConfig();
var log= new LoggerGroup() { LogLevel = TouchSocket.Core.LogLevel.Warning };
// 配置容器中注册日志记录器实例
config.ConfigureContainer(a => a.RegisterSingleton<ILog>(log));
var ichannel = await config.GetChannelAsync(channel.ChannelType, channel.RemoteUrl, channel.BindUrl, channel.Adapt<SerialPortOption>()).ConfigureAwait(false);
// 创建新的通道线程,并将驱动程序添加到其中
ChannelThread channelThread = new ChannelThread(channel, (a =>
{
return a.GetChannel(channel.ChannelType, channel.RemoteUrl, channel.BindUrl, channel.Adapt<SerialPortOption>());
}));
ChannelThread channelThread = new ChannelThread(channel, config, ichannel, log);
channelThread.AddDriver(driverBase);
channelThread.Channel?.Setup(channelThread.FoundataionConfig.Clone());
if (channelThread.Channel != null)
{
await channelThread.Channel.SetupAsync(channelThread.FoundataionConfig?.Clone()).ConfigureAwait(false);
}
ChannelThreads.Add(channelThread);
return channelThread;
}

View File

@@ -1,22 +1,26 @@
{
"ThingsGateway.Gateway.Application.BusinessUpdateEnum": {
"Change": "Change",
"Interval": "Interval",
"IntervalOrChange": "IntervalOrChange"
},
"ThingsGateway.Gateway.Application.ConfigInfoControler": {
"ConfigInfoControler": "Get configuration information",
"ThingsGateway.Gateway.Application.ConfigInfoController": {
"ConfigInfoController": "Get configuration information",
"GetChannelList": "Get channel information",
"GetCollectDeviceList": "Get device information",
"GetVariableList": "Get variable information"
},
"ThingsGateway.Gateway.Application.ControlControler": {
"ControlControler": "Device control",
"ThingsGateway.Gateway.Application.ControlController": {
"ControlController": "Device control",
"PauseCollectThread": "Control collection thread start/stop",
"PauseBusinessThread": "Control business thread start/stop",
"RestartCollectDeviceThread": "Restart collection thread",
"RestartBusinessDeviceThread": "Restart business thread",
"WriteDeviceMethods": "Write variables"
},
"ThingsGateway.Gateway.Application.RuntimeInfoControler": {
"RuntimeInfoControler": "Get runtime information",
"ThingsGateway.Gateway.Application.RuntimeInfoController": {
"RuntimeInfoController": "Get runtime information",
"GetCollectDeviceList": "Get device information",
"GetVariableList": "Get variable information",
"GetRealAlarmList": "Get real-time alarm information",
@@ -361,6 +365,7 @@
"ReadExpressions": "ReadExpressions",
"WriteExpressions": "WriteExpressions",
"RpcWriteEnable": "RpcWriteEnable",
"Value": "InitValue",
"SaveValue": "SaveValue",
"AlarmDelay": "AlarmDelay",
"BoolOpenAlarmEnable": "BoolOpenAlarmEnable",

View File

@@ -1,22 +1,25 @@
{
"ThingsGateway.Gateway.Application.ConfigInfoControler": {
"ConfigInfoControler": "获取配置信息",
"ThingsGateway.Gateway.Application.BusinessUpdateEnum": {
"Change": "变化",
"Interval": "定时",
"IntervalOrChange": "定时或变化"
},
"ThingsGateway.Gateway.Application.ConfigInfoController": {
"ConfigInfoController": "获取配置信息",
"GetChannelList": "获取通道信息",
"GetCollectDeviceList": "获取设备信息",
"GetVariableList": "获取变量信息"
},
"ThingsGateway.Gateway.Application.ControlControler": {
"ControlControler": "设备控制",
"ThingsGateway.Gateway.Application.ControlController": {
"ControlController": "设备控制",
"PauseCollectThread": "控制采集线程启停",
"PauseBusinessThread": "控制业务线程启停",
"RestartCollectDeviceThread": "重启采集线程",
"RestartBusinessDeviceThread": "重启业务线程",
"WriteDeviceMethods": "写入变量"
},
"ThingsGateway.Gateway.Application.RuntimeInfoControler": {
"RuntimeInfoControler": "获取运行态信息",
"ThingsGateway.Gateway.Application.RuntimeInfoController": {
"RuntimeInfoController": "获取运行态信息",
"GetCollectDeviceList": "获取设备信息",
"GetVariableList": "获取变量信息",
"GetRealAlarmList": "获取实时报警信息",
@@ -145,7 +148,7 @@
"ReadExpressions": "读取表达式",
"WriteExpressions": "写入表达式",
"RpcWriteEnable": "远程写入",
"SaveValue": "保存值",
"SaveValue": "保存初始值",
"AlarmDelay": "报警延时",
"BoolOpenAlarmEnable": "布尔开报警使能",
@@ -422,7 +425,8 @@
"ReadExpressions": "读取表达式",
"WriteExpressions": "写入表达式",
"RpcWriteEnable": "远程写入",
"SaveValue": "保存值",
"Value": "初始值",
"SaveValue": "保存初始值",
"AlarmDelay": "报警延时",
"BoolOpenAlarmEnable": "布尔开报警使能",

View File

@@ -1,21 +1,25 @@
{
"ThingsGateway.Gateway.Application.ConfigInfoControler": {
"ConfigInfoControler": "獲取配置資訊",
"ThingsGateway.Gateway.Application.BusinessUpdateEnum": {
"Change": "變化",
"Interval": "定時",
"IntervalOrChange": "定時或變化"
},
"ThingsGateway.Gateway.Application.ConfigInfoController": {
"ConfigInfoController": "獲取配置資訊",
"GetChannelList": "獲取通道資訊",
"GetCollectDeviceList": "獲取設備資訊",
"GetVariableList": "獲取變量資訊"
},
"ThingsGateway.Gateway.Application.ControlControler": {
"ControlControler": "設備控制",
"ThingsGateway.Gateway.Application.ControlController": {
"ControlController": "設備控制",
"PauseCollectThread": "控制採集線程啟停",
"PauseBusinessThread": "控制業務線程啟停",
"RestartCollectDeviceThread": "重启採集線程",
"RestartBusinessDeviceThread": "重启業務線程",
"WriteDeviceMethods": "寫入變量"
},
"ThingsGateway.Gateway.Application.RuntimeInfoControler": {
"RuntimeInfoControler": "獲取運行態信息",
"ThingsGateway.Gateway.Application.RuntimeInfoController": {
"RuntimeInfoController": "獲取運行態信息",
"GetCollectDeviceList": "獲取設備信息",
"GetVariableList": "獲取變量信息",
"GetRealAlarmList": "獲取實時警報信息",
@@ -131,7 +135,7 @@
"ReadExpressions": "讀取表達式",
"WriteExpressions": "寫入表達式",
"RpcWriteEnable": "遠端寫入",
"SaveValue": "保存值",
"SaveValue": "保存初始值",
"AlarmDelay": "警報延遲",
"BoolOpenAlarmEnable": "布林開警報啟用",
"BoolOpenRestrainExpressions": "布林開警報約束",
@@ -386,7 +390,8 @@
"ReadExpressions": "讀取表達式",
"WriteExpressions": "寫入表達式",
"RpcWriteEnable": "遠端寫入",
"SaveValue": "保存值",
"SaveValue": "保存初始值",
"Value": "初始值",
"AlarmDelay": "警報延遲",
"BoolOpenAlarmEnable": "布林開警報啟用",
"BoolOpenRestrainExpressions": "布林開警報約束",

View File

@@ -24,9 +24,9 @@ public class VariableMethod
private object?[]? OS;
public VariableMethod(Method method, VariableRunTime variable, int milliSeconds = 1000)
public VariableMethod(Method method, VariableRunTime variable, string delay)
{
_timeTick = new TimeTick(milliSeconds);
_timeTick = new TimeTick(delay);
MethodInfo = method;
Variable = variable;
variable.VariableMethod = this;

View File

@@ -12,6 +12,7 @@ using BootstrapBlazor.Components;
using Mapster;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using ThingsGateway.Core.Json.Extension;
@@ -44,7 +45,7 @@ public class VariableRunTime : Variable, IVariable
public override bool Enable { get; set; }
[AutoGenerateColumn(Visible = false, Filterable = true, Sortable = true)]
public override int? IntervalTime { get; set; }
public override string? IntervalTime { get; set; }
#endregion
@@ -154,7 +155,7 @@ public class VariableRunTime : Variable, IVariable
/// 实时值
/// </summary>
[AutoGenerateColumn(Visible = true, Order = 6)]
public object? Value { get => _value; internal set => _value = value; }
public override object? Value { get => _value; set => _value = value; }
/// <summary>
/// 设置变量值与时间/质量戳
@@ -182,7 +183,15 @@ public class VariableRunTime : Variable, IVariable
{
IsOnline = false;
Set(null, dateTime);
lastErrorMessage = $"{Name} Conversion expression failed{ex.Message}";
if (ex.StackTrace != null)
{
string stachTrace = string.Join(Environment.NewLine, ex.StackTrace.Split(new[] { Environment.NewLine }, StringSplitOptions.None).Take(3));
lastErrorMessage = $"{Name} Conversion expression failed{ex.Message}{Environment.NewLine}{stachTrace}";
}
else
{
lastErrorMessage = $"{Name} Conversion expression failed{ex.Message}{Environment.NewLine}";
}
return new($"{Name} Conversion expression failed", ex);
}
}
@@ -201,7 +210,10 @@ public class VariableRunTime : Variable, IVariable
bool changed = false;
if (data == null)
{
changed = (_value != null);
if (IsOnline)
{
changed = (_value != null);
}
}
else
{
@@ -259,13 +271,13 @@ public class VariableRunTime : Variable, IVariable
#region LoadSourceRead
/// <summary>
/// 这个参数值由自动打包方法写入<see cref="IProtocol.LoadSourceRead{T}(IEnumerable{IVariable}, int, int)"/>
/// 这个参数值由自动打包方法写入<see cref="IProtocol.LoadSourceRead{T}(IEnumerable{IVariable}, int, string)"/>
/// </summary>
[AutoGenerateColumn(Visible = false)]
public int Index { get; set; }
/// <summary>
/// 这个参数值由自动打包方法写入<see cref="IProtocol.LoadSourceRead{T}(IEnumerable{IVariable}, int, int)"/>
/// 这个参数值由自动打包方法写入<see cref="IProtocol.LoadSourceRead{T}(IEnumerable{IVariable}, int, string)"/>
/// </summary>
[Newtonsoft.Json.JsonIgnore]
[System.Text.Json.Serialization.JsonIgnore]

View File

@@ -81,8 +81,11 @@ public abstract class BusinessBase : DriverBase
CurrentDevice.RefreshBusinessDeviceRuntime(device.Id);
// 如果设备的采集间隔小于等于50毫秒则将其设置为50毫秒
if (device.IntervalTime <= 50)
device.IntervalTime = 50;
if (int.TryParse(device.IntervalTime, out int delay))
{
if (delay <= 50)
device.IntervalTime = "50";
}
}
/// <summary>
@@ -90,7 +93,7 @@ public abstract class BusinessBase : DriverBase
/// </summary>
protected async Task Delay(CancellationToken cancellationToken)
{
await Task.Delay(Math.Max(CurrentDevice.IntervalTime - ChannelThread.CycleInterval, ChannelThread.CycleInterval), cancellationToken).ConfigureAwait(false);
await Task.Delay(ChannelThread.CycleInterval, cancellationToken).ConfigureAwait(false);
}
protected override void Dispose(bool disposing)

View File

@@ -30,10 +30,15 @@ public abstract class BusinessBaseWithCacheAlarmModel<VarModel, DevModel, AlarmM
/// <param name="data"></param>
protected virtual void AddCache(List<CacheDBItem<AlarmModel>> data)
{
if (_businessPropertyWithCache.CacheEnable && data?.Count > 0)
{
try
{
foreach (var item in data)
{
item.Id = CommonUtils.GetSingleId();
}
var dir = CacheDBUtil.GetFilePath(CurrentDevice.Id.ToString());
var fileStart = CacheDBUtil.GetFileName($"{CurrentDevice.PluginName}_{typeof(AlarmModel).FullName}_{nameof(AlarmModel)}");
var fullName = dir.CombinePathWithOs($"{fileStart}{CacheDBUtil.EX}");

View File

@@ -30,10 +30,16 @@ public abstract class BusinessBaseWithCacheDeviceModel<VarModel, DevModel> : Bus
/// <param name="data"></param>
protected virtual void AddCache(List<CacheDBItem<DevModel>> data)
{
if (_businessPropertyWithCache.CacheEnable && data?.Count > 0)
{
try
{
foreach (var item in data)
{
item.Id = CommonUtils.GetSingleId();
}
var dir = CacheDBUtil.GetFilePath(CurrentDevice.Id.ToString());
var fileStart = CacheDBUtil.GetFileName($"{CurrentDevice.PluginName}_{typeof(DevModel).FullName}_{nameof(DevModel)}");
var fullName = dir.CombinePathWithOs($"{fileStart}{CacheDBUtil.EX}");

View File

@@ -38,6 +38,10 @@ public abstract class BusinessBaseWithCacheVariableModel<VarModel> : BusinessBas
{
try
{
foreach (var item in data)
{
item.Id = CommonUtils.GetSingleId();
}
var dir = CacheDBUtil.GetFilePath(CurrentDevice.Id.ToString());
var fileStart = CacheDBUtil.GetFileName($"{CurrentDevice.PluginName}_{typeof(VarModel).FullName}_{nameof(VarModel)}");
var fullName = dir.CombinePathWithOs($"{fileStart}{CacheDBUtil.EX}");

View File

@@ -45,8 +45,11 @@ public abstract class BusinessBaseWithCacheIntervalAlarmModel<VarModel, DevModel
}
// 如果业务属性的缓存要求的业务间隔小于等于100则设置为100
if (_businessPropertyWithCacheInterval.BusinessInterval <= 100)
_businessPropertyWithCacheInterval.BusinessInterval = 100;
if (int.TryParse(_businessPropertyWithCacheInterval.BusinessInterval, out int delay))
{
if (delay <= 100)
_businessPropertyWithCacheInterval.BusinessInterval = "100";
}
// 初始化
_exTTimerTick = new(_businessPropertyWithCacheInterval.BusinessInterval);
@@ -54,30 +57,30 @@ public abstract class BusinessBaseWithCacheIntervalAlarmModel<VarModel, DevModel
GlobalData.AlarmHostedService.OnAlarmChanged -= AlarmValueChange;
GlobalData.AlarmHostedService.OnAlarmChanged += AlarmValueChange;
// 解绑全局数据的事件
GlobalData.VariableValueChangeEvent -= VariableValueChange;
GlobalData.DeviceStatusChangeEvent -= DeviceStatusChange;
// 根据业务属性的缓存是否为间隔上传来决定事件绑定
if (!_businessPropertyWithCacheInterval.IsInterval)
if (_businessPropertyWithCacheInterval.BusinessUpdateEnum != BusinessUpdateEnum.Interval)
{
// 解绑全局数据的事件
GlobalData.VariableValueChangeEvent -= VariableValueChange;
GlobalData.DeviceStatusChangeEvent -= DeviceStatusChange;
// 绑定全局数据的事件
GlobalData.DeviceStatusChangeEvent += DeviceStatusChange;
GlobalData.VariableValueChangeEvent += VariableValueChange;
// 触发一次设备状态变化和变量值变化事件
CollectDevices.ForEach(a =>
{
if (a.Value.DeviceStatus == DeviceStatusEnum.OnLine)
DeviceStatusChange(a.Value, a.Value.Adapt<DeviceBasicData>());
});
CurrentDevice.VariableRunTimes.ForEach(a =>
{
if (a.Value.IsOnline)
VariableValueChange(a.Value, a.Value.Adapt<VariableBasicData>());
});
}
// 触发一次设备状态变化和变量值变化事件
CollectDevices.ForEach(a =>
{
if (a.Value.DeviceStatus == DeviceStatusEnum.OnLine)
DeviceStatusChange(a.Value, a.Value.Adapt<DeviceBasicData>());
});
CurrentDevice.VariableRunTimes.ForEach(a =>
{
if (a.Value.IsOnline)
VariableValueChange(a.Value, a.Value.Adapt<VariableBasicData>());
});
}
/// <summary>
@@ -98,7 +101,15 @@ public abstract class BusinessBaseWithCacheIntervalAlarmModel<VarModel, DevModel
{
// 在设备状态变化时执行的自定义逻辑
}
/// <summary>
/// 当设备状态定时变化时触发此方法。如果不需要进行设备上传,则可以忽略此方法。通常情况下,需要在此方法中执行 <see cref="BusinessBaseWithCacheDeviceModel{T,T2}.AddQueueDevModel(CacheDBItem{T2})"/> 方法。
/// </summary>
/// <param name="deviceRunTime">设备运行时信息</param>
/// <param name="deviceData">设备数据</param>
protected virtual void DeviceTimeInterval(DeviceRunTime deviceRunTime, DeviceBasicData deviceData)
{
// 在设备状态变化时执行的自定义逻辑
}
/// <summary>
/// 释放资源方法
/// </summary>
@@ -121,6 +132,8 @@ public abstract class BusinessBaseWithCacheIntervalAlarmModel<VarModel, DevModel
/// </summary>
protected virtual async Task IntervalInsert()
{
var vardatas = CurrentDevice.VariableRunTimes.Values.ToList();
var devdatas = CollectDevices.Values.ToList();
while (!DisposedValue)
{
if (CurrentDevice?.KeepRun == false)
@@ -130,16 +143,16 @@ public abstract class BusinessBaseWithCacheIntervalAlarmModel<VarModel, DevModel
}
// 如果业务属性的缓存为间隔上传,则根据定时器间隔执行相应操作
if (_businessPropertyWithCacheInterval.IsInterval)
if (_businessPropertyWithCacheInterval.BusinessUpdateEnum != BusinessUpdateEnum.Change)
{
try
{
if (_exTTimerTick.IsTickHappen())
{
// 间隔推送全部变量
foreach (var variableRuntime in CurrentDevice.VariableRunTimes)
foreach (var variableRuntime in vardatas)
{
VariableChange(variableRuntime.Value, variableRuntime.Value.Adapt<VariableBasicData>());
VariableTimeInterval(variableRuntime, variableRuntime.Adapt<VariableBasicData>());
}
}
}
@@ -152,9 +165,9 @@ public abstract class BusinessBaseWithCacheIntervalAlarmModel<VarModel, DevModel
if (_exT2TimerTick.IsTickHappen())
{
// 间隔推送全部设备
foreach (var deviceRuntime in CollectDevices)
foreach (var deviceRuntime in devdatas)
{
DeviceChange(deviceRuntime.Value, deviceRuntime.Value.Adapt<DeviceBasicData>());
DeviceTimeInterval(deviceRuntime, deviceRuntime.Adapt<DeviceBasicData>());
}
}
}
@@ -187,7 +200,15 @@ public abstract class BusinessBaseWithCacheIntervalAlarmModel<VarModel, DevModel
{
// 在变量状态变化时执行的自定义逻辑
}
/// <summary>
/// 当变量定时变化时触发此方法。如果不需要进行变量上传,则可以忽略此方法。通常情况下,需要在此方法中执行 <see cref="BusinessBaseWithCacheVariableModel{T}.AddQueueVarModel(CacheDBItem{T})"/> 方法。
/// </summary>
/// <param name="variableRunTime">变量运行时信息</param>
/// <param name="variable">变量数据</param>
protected virtual void VariableTimeInterval(VariableRunTime variableRunTime, VariableBasicData variable)
{
// 在变量状态变化时执行的自定义逻辑
}
/// <summary>
/// 当报警值发生变化时触发此事件处理方法。该方法内部会检查是否需要进行报警上传,如果需要,则调用 <see cref="AlarmChange(AlarmVariable)"/> 方法。
/// </summary>
@@ -197,7 +218,7 @@ public abstract class BusinessBaseWithCacheIntervalAlarmModel<VarModel, DevModel
if (!CurrentDevice.KeepRun)
return;
// 如果业务属性的缓存为间隔上传,则不执行后续操作
if (_businessPropertyWithCacheInterval?.IsInterval != true)
//if (_businessPropertyWithCacheInterval?.IsInterval != true)
{
// 检查当前设备的变量是否包含此报警变量,如果包含,则触发报警变量的变化处理方法
if (CurrentDevice.VariableRunTimes.ContainsKey(alarmVariable.Name))
@@ -215,7 +236,7 @@ public abstract class BusinessBaseWithCacheIntervalAlarmModel<VarModel, DevModel
if (!CurrentDevice.KeepRun)
return;
// 如果业务属性的缓存为间隔上传,则不执行后续操作
if (_businessPropertyWithCacheInterval?.IsInterval != true)
//if (_businessPropertyWithCacheInterval?.IsInterval != true)
{
// 检查当前设备的设备列表是否包含此设备,如果包含,则触发设备的状态变化处理方法
if (CollectDevices.ContainsKey(deviceData.Name))
@@ -233,7 +254,7 @@ public abstract class BusinessBaseWithCacheIntervalAlarmModel<VarModel, DevModel
if (!CurrentDevice.KeepRun)
return;
// 如果业务属性的缓存为间隔上传,则不执行后续操作
if (_businessPropertyWithCacheInterval?.IsInterval != true)
//if (_businessPropertyWithCacheInterval?.IsInterval != true)
{
// 检查当前设备的变量是否包含此变量,如果包含,则触发变量的变化处理方法
if (CurrentDevice.VariableRunTimes.ContainsKey(variable.Name))

View File

@@ -51,8 +51,11 @@ public abstract class BusinessBaseWithCacheIntervalDeviceModel<VarModel, DevMode
}
// 设置业务间隔时间的最小值为100毫秒
if (_businessPropertyWithCacheInterval.BusinessInterval <= 100)
_businessPropertyWithCacheInterval.BusinessInterval = 100;
if (int.TryParse(_businessPropertyWithCacheInterval.BusinessInterval, out int delay))
{
if (delay <= 100)
_businessPropertyWithCacheInterval.BusinessInterval = "100";
}
// 初始化设备和变量上传的定时器
_exTTimerTick = new(_businessPropertyWithCacheInterval.BusinessInterval);
@@ -63,21 +66,22 @@ public abstract class BusinessBaseWithCacheIntervalDeviceModel<VarModel, DevMode
GlobalData.DeviceStatusChangeEvent -= DeviceStatusChange;
// 如果不是间隔上传,则订阅全局变量值改变事件和设备状态改变事件,并触发一次事件处理
if (!_businessPropertyWithCacheInterval.IsInterval)
if (_businessPropertyWithCacheInterval.BusinessUpdateEnum != BusinessUpdateEnum.Interval)
{
GlobalData.DeviceStatusChangeEvent += DeviceStatusChange;
GlobalData.VariableValueChangeEvent += VariableValueChange;
CollectDevices.ForEach(a =>
{
if (a.Value.DeviceStatus == DeviceStatusEnum.OnLine)
DeviceStatusChange(a.Value, a.Value.Adapt<DeviceBasicData>());
});
CurrentDevice.VariableRunTimes.ForEach(a =>
{
if (a.Value.IsOnline)
VariableValueChange(a.Value, a.Value.Adapt<VariableBasicData>());
});
}
CollectDevices.ForEach(a =>
{
if (a.Value.DeviceStatus == DeviceStatusEnum.OnLine)
DeviceStatusChange(a.Value, a.Value.Adapt<DeviceBasicData>());
});
CurrentDevice.VariableRunTimes.ForEach(a =>
{
if (a.Value.IsOnline)
VariableValueChange(a.Value, a.Value.Adapt<VariableBasicData>());
});
}
/// <summary>
@@ -88,7 +92,15 @@ public abstract class BusinessBaseWithCacheIntervalDeviceModel<VarModel, DevMode
protected virtual void DeviceChange(DeviceRunTime deviceRunTime, DeviceBasicData deviceData)
{
}
/// <summary>
/// 当设备状态定时变化时触发此方法。如果不需要进行设备上传,则可以忽略此方法。通常情况下,需要在此方法中执行 <see cref="BusinessBaseWithCacheDeviceModel{T,T2}.AddQueueDevModel(CacheDBItem{T2})"/> 方法。
/// </summary>
/// <param name="deviceRunTime">设备运行时信息</param>
/// <param name="deviceData">设备数据</param>
protected virtual void DeviceTimeInterval(DeviceRunTime deviceRunTime, DeviceBasicData deviceData)
{
// 在设备状态变化时执行的自定义逻辑
}
/// <summary>
/// 释放资源的方法,释放插件相关资源。
/// </summary>
@@ -111,6 +123,8 @@ public abstract class BusinessBaseWithCacheIntervalDeviceModel<VarModel, DevMode
/// <returns>异步任务</returns>
protected virtual async Task IntervalInsert()
{
var vardatas = CurrentDevice.VariableRunTimes.Values.ToList();
var devdatas = CollectDevices.Values.ToList();
while (!DisposedValue)
{
if (CurrentDevice?.KeepRun == false)
@@ -120,16 +134,16 @@ public abstract class BusinessBaseWithCacheIntervalDeviceModel<VarModel, DevMode
}
// 如果是间隔上传,根据定时器触发事件上传设备和变量信息
if (_businessPropertyWithCacheInterval.IsInterval)
if (_businessPropertyWithCacheInterval.BusinessUpdateEnum != BusinessUpdateEnum.Change)
{
try
{
if (_exTTimerTick.IsTickHappen())
{
// 上传所有变量信息
foreach (var variableRuntime in CurrentDevice.VariableRunTimes)
foreach (var variableRuntime in vardatas)
{
VariableChange(variableRuntime.Value, variableRuntime.Value.Adapt<VariableData>());
VariableTimeInterval(variableRuntime, variableRuntime.Adapt<VariableBasicData>());
}
}
}
@@ -143,9 +157,9 @@ public abstract class BusinessBaseWithCacheIntervalDeviceModel<VarModel, DevMode
if (_exT2TimerTick.IsTickHappen())
{
// 上传所有设备信息
foreach (var deviceRuntime in CollectDevices)
foreach (var deviceRuntime in devdatas)
{
DeviceChange(deviceRuntime.Value, deviceRuntime.Value.Adapt<DeviceBasicData>());
DeviceTimeInterval(deviceRuntime, deviceRuntime.Adapt<DeviceBasicData>());
}
}
}
@@ -178,7 +192,15 @@ public abstract class BusinessBaseWithCacheIntervalDeviceModel<VarModel, DevMode
protected virtual void VariableChange(VariableRunTime variableRunTime, VariableData variable)
{
}
/// <summary>
/// 当变量定时变化时触发此方法。如果不需要进行变量上传,则可以忽略此方法。通常情况下,需要在此方法中执行 <see cref="BusinessBaseWithCacheVariableModel{T}.AddQueueVarModel(CacheDBItem{T})"/> 方法。
/// </summary>
/// <param name="variableRunTime">变量运行时信息</param>
/// <param name="variable">变量数据</param>
protected virtual void VariableTimeInterval(VariableRunTime variableRunTime, VariableBasicData variable)
{
// 在变量状态变化时执行的自定义逻辑
}
/// <summary>
/// 设备状态改变时的事件处理方法。
/// </summary>
@@ -191,7 +213,7 @@ public abstract class BusinessBaseWithCacheIntervalDeviceModel<VarModel, DevMode
return;
// 如果业务属性不是间隔上传,则执行设备状态改变的处理逻辑
if (_businessPropertyWithCacheInterval?.IsInterval != true)
//if (_businessPropertyWithCacheInterval?.IsInterval != true)
{
// 检查当前设备集合中是否包含该设备,并进行相应处理
if (CollectDevices.ContainsKey(deviceRunTime.Name))
@@ -211,7 +233,7 @@ public abstract class BusinessBaseWithCacheIntervalDeviceModel<VarModel, DevMode
return;
// 如果业务属性不是间隔上传,则执行变量状态改变的处理逻辑
if (_businessPropertyWithCacheInterval?.IsInterval != true)
//if (_businessPropertyWithCacheInterval?.IsInterval != true)
{
// 检查当前设备是否包含该变量,并进行相应处理
if (CurrentDevice.VariableRunTimes.ContainsKey(variableRunTime.Name))

View File

@@ -8,11 +8,15 @@
// QQ群605534569
//------------------------------------------------------------------------------
using Newtonsoft.Json;
using System.Globalization;
using System.Text.RegularExpressions;
using ThingsGateway.Core.Json.Extension;
using TouchSocket.Core;
namespace ThingsGateway.Gateway.Application;
/// <summary>
@@ -57,6 +61,84 @@ public abstract partial class BusinessBaseWithCacheIntervalScript<VarModel, DevM
}
#region
protected List<TopicArray> GetAlarmTopicArrays(IEnumerable<AlarmModel> item)
{
IEnumerable<dynamic>? data = Application.DynamicModelExtension.GetDynamicModel<AlarmModel>(item, _businessPropertyWithCacheIntervalScript.BigTextScriptAlarmModel);
List<TopicArray> topicJsonList = new List<TopicArray>();
var topics = Match(_businessPropertyWithCacheIntervalScript.AlarmTopic);
if (topics.Count > 0)
{
{
//获取分组最终结果
var groups = data.GroupByKeys(topics);
foreach (var group in groups)
{
// 上传主题
// 获取预定义的报警主题
string topic = _businessPropertyWithCacheIntervalScript.AlarmTopic;
// 将主题中的占位符替换为分组键对应的值
for (int i = 0; i < topics.Count; i++)
{
topic = topic.Replace(@"${" + topics[i] + @"}", group.Key[i]?.ToString());
}
// 上传内容
if (_businessPropertyWithCacheIntervalScript.IsAlarmList)
{
var json = Serialize(group.Select(a => a).ToList().ToList());
// 将主题和 JSON 内容添加到列表中
topicJsonList.Add(new(topic, json));
}
else
{
// 如果不是报警列表,则将每个分组元素分别转换为 JSON 字符串
foreach (var gro in group)
{
var json = Serialize(gro);
// 将主题和 JSON 内容添加到列表中
topicJsonList.Add(new(topic, json));
}
}
}
}
}
else
{
if (_businessPropertyWithCacheIntervalScript.IsAlarmList)
{
var json = Serialize(data.Select(a => a).ToList());
topicJsonList.Add(new(_businessPropertyWithCacheIntervalScript.AlarmTopic, json));
}
else
{
foreach (var group in data)
{
var json = Serialize(group);
topicJsonList.Add(new(_businessPropertyWithCacheIntervalScript.AlarmTopic, json));
}
}
}
return topicJsonList;
}
protected static ArraySegment<byte> Serialize(object value)
{
var block = new ValueByteBlock(1024 * 64);
try
{
//将数据序列化到内存块
FastBinaryFormatter.Serialize(ref block, value);
block.SeekToStart();
return block.Memory.GetArray();
}
finally
{
block.Dispose();
}
}
protected List<TopicJson> GetAlarms(IEnumerable<AlarmModel> item)
{
@@ -85,7 +167,7 @@ public abstract partial class BusinessBaseWithCacheIntervalScript<VarModel, DevM
if (_businessPropertyWithCacheIntervalScript.IsAlarmList)
{
// 如果是报警列表,则将整个分组转换为 JSON 字符串
string json = group.Select(a => a).ToJsonNetString();
string json = group.Select(a => a).ToList().ToJsonNetString();
// 将主题和 JSON 内容添加到列表中
topicJsonList.Add(new(topic, json));
}
@@ -106,7 +188,7 @@ public abstract partial class BusinessBaseWithCacheIntervalScript<VarModel, DevM
{
if (_businessPropertyWithCacheIntervalScript.IsAlarmList)
{
string json = data.Select(a => a).ToJsonNetString();
string json = data.Select(a => a).ToList().ToJsonNetString();
topicJsonList.Add(new(_businessPropertyWithCacheIntervalScript.AlarmTopic, json));
}
else
@@ -150,7 +232,7 @@ public abstract partial class BusinessBaseWithCacheIntervalScript<VarModel, DevM
if (_businessPropertyWithCacheIntervalScript.IsDeviceList)
{
// 如果是设备列表,则将整个分组转换为 JSON 字符串
string json = group.Select(a => a).ToJsonNetString();
string json = group.Select(a => a).ToList().ToJsonNetString();
// 将主题和 JSON 内容添加到列表中
topicJsonList.Add(new(topic, json));
}
@@ -172,7 +254,7 @@ public abstract partial class BusinessBaseWithCacheIntervalScript<VarModel, DevM
{
if (_businessPropertyWithCacheIntervalScript.IsDeviceList)
{
string json = data.Select(a => a).ToJsonNetString();
string json = data.Select(a => a).ToList().ToJsonNetString();
topicJsonList.Add(new(_businessPropertyWithCacheIntervalScript.DeviceTopic, json));
}
else
@@ -187,6 +269,71 @@ public abstract partial class BusinessBaseWithCacheIntervalScript<VarModel, DevM
return topicJsonList;
}
protected List<TopicArray> GetDeviceTopicArray(IEnumerable<DevModel> item)
{
IEnumerable<dynamic>? data = Application.DynamicModelExtension.GetDynamicModel<DevModel>(item, _businessPropertyWithCacheIntervalScript.BigTextScriptDeviceModel);
List<TopicArray> topicJsonList = new List<TopicArray>();
var topics = Match(_businessPropertyWithCacheIntervalScript.DeviceTopic);
if (topics.Count > 0)
{
{
//获取分组最终结果
var groups = data.GroupByKeys(topics).ToList();
if (groups.Count > 0)
{
foreach (var group in groups)
{
// 上传主题
// 获取预定义的设备主题
string topic = _businessPropertyWithCacheIntervalScript.DeviceTopic;
// 将主题中的占位符替换为分组键对应的值
for (int i = 0; i < topics.Count; i++)
{
topic = topic.Replace(@"${" + topics[i] + @"}", group.Key[i]?.ToString());
}
// 上传内容
if (_businessPropertyWithCacheIntervalScript.IsDeviceList)
{
// 如果是设备列表,则将整个分组转换为 JSON 字符串
var json = Serialize(group.Select(a => a).ToList());
// 将主题和 JSON 内容添加到列表中
topicJsonList.Add(new(topic, json));
}
else
{
// 如果不是设备列表,则将每个分组元素分别转换为 JSON 字符串
foreach (var gro in group)
{
var json = Serialize(gro);
// 将主题和 JSON 内容添加到列表中
topicJsonList.Add(new(topic, json));
}
}
}
}
}
}
else
{
if (_businessPropertyWithCacheIntervalScript.IsDeviceList)
{
var json = Serialize(data.Select(a => a).ToList());
topicJsonList.Add(new(_businessPropertyWithCacheIntervalScript.DeviceTopic, json));
}
else
{
foreach (var group in data)
{
var json = Serialize(group);
topicJsonList.Add(new(_businessPropertyWithCacheIntervalScript.DeviceTopic, json));
}
}
}
return topicJsonList;
}
protected List<TopicJson> GetVariable(IEnumerable<VarModel> item)
{
IEnumerable<dynamic>? data = Application.DynamicModelExtension.GetDynamicModel<VarModel>(item, _businessPropertyWithCacheIntervalScript.BigTextScriptVariableModel);
@@ -215,7 +362,7 @@ public abstract partial class BusinessBaseWithCacheIntervalScript<VarModel, DevM
if (_businessPropertyWithCacheIntervalScript.IsVariableList)
{
// 如果是变量列表,则将整个分组转换为 JSON 字符串
string json = group.Select(a => a).ToJsonNetString();
string json = group.Select(a => a).ToList().ToJsonNetString();
// 将主题和 JSON 内容添加到列表中
topicJsonList.Add(new(topic, json));
}
@@ -237,7 +384,7 @@ public abstract partial class BusinessBaseWithCacheIntervalScript<VarModel, DevM
{
if (_businessPropertyWithCacheIntervalScript.IsVariableList)
{
string json = data.Select(a => a).ToJsonNetString();
string json = data.Select(a => a).ToList().ToJsonNetString();
topicJsonList.Add(new(_businessPropertyWithCacheIntervalScript.VariableTopic, json));
}
else
@@ -252,5 +399,71 @@ public abstract partial class BusinessBaseWithCacheIntervalScript<VarModel, DevM
return topicJsonList;
}
protected List<TopicArray> GetVariableTopicArray(IEnumerable<VarModel> item)
{
IEnumerable<dynamic>? data = Application.DynamicModelExtension.GetDynamicModel<VarModel>(item, _businessPropertyWithCacheIntervalScript.BigTextScriptVariableModel);
List<TopicArray> topicJsonList = new List<TopicArray>();
var topics = Match(_businessPropertyWithCacheIntervalScript.VariableTopic);
if (topics.Count > 0)
{
{
//获取分组最终结果
var groups = data.GroupByKeys(topics).ToList();
if (groups.Count > 0)
{
foreach (var group in groups)
{
// 上传主题
// 获取预定义的变量主题
string topic = _businessPropertyWithCacheIntervalScript.VariableTopic;
// 将主题中的占位符替换为分组键对应的值
for (int i = 0; i < topics.Count; i++)
{
topic = topic.Replace(@"${" + topics[i] + @"}", group.Key[i]?.ToString());
}
// 上传内容
if (_businessPropertyWithCacheIntervalScript.IsVariableList)
{
// 如果是变量列表,则将整个分组转换为 JSON 字符串
var json = Serialize(group.Select(a => a).ToList());
// 将主题和 JSON 内容添加到列表中
topicJsonList.Add(new(topic, json));
}
else
{
// 如果不是变量列表,则将每个分组元素分别转换为 JSON 字符串
foreach (var gro in group)
{
var json = Serialize(gro);
// 将主题和 JSON 内容添加到列表中
topicJsonList.Add(new(topic, json));
}
}
}
}
}
}
else
{
if (_businessPropertyWithCacheIntervalScript.IsVariableList)
{
var json = Serialize(data.Select(a => a).ToList());
topicJsonList.Add(new(_businessPropertyWithCacheIntervalScript.VariableTopic, json));
}
else
{
foreach (var group in data)
{
var json = Serialize(group);
topicJsonList.Add(new(_businessPropertyWithCacheIntervalScript.VariableTopic, json));
}
}
}
return topicJsonList;
}
#endregion
}

View File

@@ -49,24 +49,28 @@ public abstract class BusinessBaseWithCacheIntervalVariableModel<T> : BusinessBa
}
// 如果业务间隔小于等于100毫秒则将业务间隔设置为100毫秒
if (_businessPropertyWithCacheInterval.BusinessInterval <= 100)
_businessPropertyWithCacheInterval.BusinessInterval = 100;
if (int.TryParse(_businessPropertyWithCacheInterval.BusinessInterval, out int delay))
{
if (delay <= 100)
_businessPropertyWithCacheInterval.BusinessInterval = "100";
}
// 初始化定时器
_exTTimerTick = new TimeTick(_businessPropertyWithCacheInterval.BusinessInterval);
// 注册变量值变化事件处理程序
GlobalData.VariableValueChangeEvent -= VariableValueChange;
if (!_businessPropertyWithCacheInterval.IsInterval)
if (_businessPropertyWithCacheInterval.BusinessUpdateEnum != BusinessUpdateEnum.Interval)
{
GlobalData.VariableValueChangeEvent += VariableValueChange;
// 触发一次变量值变化事件
CurrentDevice.VariableRunTimes.ForEach(a =>
{
if (a.Value.IsOnline)
VariableValueChange(a.Value, a.Value.Adapt<VariableData>());
});
}
// 触发一次变量值变化事件
CurrentDevice.VariableRunTimes.ForEach(a =>
{
if (a.Value.IsOnline)
VariableValueChange(a.Value, a.Value.Adapt<VariableBasicData>());
});
}
/// <summary>
@@ -86,6 +90,7 @@ public abstract class BusinessBaseWithCacheIntervalVariableModel<T> : BusinessBa
/// <returns>表示异步操作的任务</returns>
protected virtual async Task IntervalInsert()
{
var vardatas = CurrentDevice.VariableRunTimes.Values.ToList();
while (!DisposedValue)
{
if (CurrentDevice?.KeepRun == false)
@@ -94,36 +99,31 @@ public abstract class BusinessBaseWithCacheIntervalVariableModel<T> : BusinessBa
continue;
}
//间隔上传
IntervalInsertVariable();
if (_businessPropertyWithCacheInterval.BusinessUpdateEnum != BusinessUpdateEnum.Change)
{
try
{
if (_exTTimerTick.IsTickHappen())
{
//间隔推送全部变量
foreach (var variableRuntime in vardatas)
{
VariableTimeInterval(variableRuntime, variableRuntime.Adapt<VariableBasicData>());
}
}
}
catch (Exception ex)
{
LogMessage.LogWarning(ex, BusinessBaseLocalizer["IntervalInsertVariableFail"]);
}
}
await Delay(default).ConfigureAwait(false);
}
}
/// <summary>
/// 执行间隔插入变量的操作。
/// </summary>
protected virtual void IntervalInsertVariable()
{
if (_businessPropertyWithCacheInterval.IsInterval)
{
try
{
if (_exTTimerTick.IsTickHappen())
{
//间隔推送全部变量
foreach (var variableRuntime in CurrentDevice.VariableRunTimes)
{
VariableChange(variableRuntime.Value, variableRuntime.Value.Adapt<VariableData>());
}
}
}
catch (Exception ex)
{
LogMessage.LogWarning(ex, BusinessBaseLocalizer["IntervalInsertVariableFail"]);
}
}
}
/// <summary>
/// 在启动前执行的异步操作。
@@ -142,20 +142,28 @@ public abstract class BusinessBaseWithCacheIntervalVariableModel<T> : BusinessBa
/// </summary>
/// <param name="variableRunTime">变量运行时对象</param>
/// <param name="variable">变量运行时对象</param>
protected virtual void VariableChange(VariableRunTime variableRunTime, VariableData variable)
protected virtual void VariableChange(VariableRunTime variableRunTime, VariableBasicData variable)
{
}
/// <summary>
/// 当变量定时变化时触发此方法。如果不需要进行变量上传,则可以忽略此方法。通常情况下,需要在此方法中执行 <see cref="BusinessBaseWithCacheVariableModel{T}.AddQueueVarModel(CacheDBItem{T})"/> 方法。
/// </summary>
/// <param name="variableRunTime">变量运行时信息</param>
/// <param name="variable">变量数据</param>
protected virtual void VariableTimeInterval(VariableRunTime variableRunTime, VariableBasicData variable)
{
// 在变量状态变化时执行的自定义逻辑
}
/// <summary>
/// 当变量值发生变化时调用的方法。
/// </summary>
/// <param name="variableRunTime">变量运行时对象</param>
/// <param name="variable">变量数据</param>
private void VariableValueChange(VariableRunTime variableRunTime, VariableData variable)
private void VariableValueChange(VariableRunTime variableRunTime, VariableBasicData variable)
{
if (!CurrentDevice.KeepRun)
return;
if (_businessPropertyWithCacheInterval?.IsInterval != true)
//if (_businessPropertyWithCacheInterval?.IsInterval != true)
{
//筛选
if (CurrentDevice.VariableRunTimes.ContainsKey(variableRunTime.Name))

View File

@@ -10,6 +10,12 @@
namespace ThingsGateway.Gateway.Application;
public enum BusinessUpdateEnum
{
Change,
Interval,
IntervalOrChange,
}
/// <summary>
/// <inheritdoc/>
/// </summary>
@@ -19,14 +25,14 @@ public class BusinessPropertyWithCacheInterval : BusinessPropertyWithCache
public virtual bool IsAllVariable { get; set; } = false;
/// <summary>
/// 是否间隔上传
/// 上传模式
/// </summary>
[DynamicProperty]
public virtual bool IsInterval { get; set; } = false;
public virtual BusinessUpdateEnum BusinessUpdateEnum { get; set; }
/// <summary>
/// 间隔上传时间
/// </summary>
[DynamicProperty]
public virtual int BusinessInterval { get; set; } = 1000;
public virtual string BusinessInterval { get; set; } = "1000";
}

View File

@@ -0,0 +1,22 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://thingsgateway.cn/
// QQ群605534569
//------------------------------------------------------------------------------
namespace ThingsGateway.Gateway.Application;
public struct TopicArray
{
public TopicArray(string topic, ArraySegment<byte> json)
{
Topic = topic; Json = json;
}
public ArraySegment<byte> Json { get; set; }
public string Topic { get; set; }
}

View File

@@ -19,12 +19,10 @@ public class CacheDBItem<T> : IPrimaryIdEntity
{
public CacheDBItem()
{
Id = CommonUtils.GetSingleId();
}
public CacheDBItem(T value)
{
Id = CommonUtils.GetSingleId();
Value = value;
}

View File

@@ -13,10 +13,7 @@ using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json.Linq;
using System.Collections.Concurrent;
using System.Threading;
using ThingsGateway.NewLife.Extension;
@@ -112,9 +109,11 @@ public class ChannelThread
/// <summary>
/// 通道线程构造函数,用于初始化通道线程实例。
/// </summary>
/// <param name="channel">通道实例</param>
/// <param name="getChannel">获取通道的方法</param>
public ChannelThread(Channel channel, Func<TouchSocketConfig, IChannel> getChannel)
/// <param name="channel">通道</param>
/// <param name="foundataionConfig">通道设置实例</param>
/// <param name="ichannel">通道实例</param>
/// <param name="loggerGroup">日志</param>
public ChannelThread(Channel channel, TouchSocketConfig foundataionConfig, IChannel ichannel, LoggerGroup loggerGroup)
{
Localizer = App.CreateLocalizerByType(typeof(ChannelThread))!;
// 初始化日志记录器,使用通道名称作为日志记录器的名称
@@ -125,19 +124,14 @@ public class ChannelThread
ChannelId = channel.Id;
// 初始化底层配置
LogMessage = new LoggerGroup() { LogLevel = TouchSocket.Core.LogLevel.Warning };
LogMessage = loggerGroup;
// 添加默认日志记录器
LogMessage.AddLogger(new EasyLogger(Log_Out) { LogLevel = TouchSocket.Core.LogLevel.Trace });
// 初始化基础配置容器
var foundataionConfig = new TouchSocketConfig();
// 配置容器中注册日志记录器实例
foundataionConfig.ConfigureContainer(a => a.RegisterSingleton<ILog>(LogMessage));
// 根据配置获取通道实例
Channel = getChannel(foundataionConfig);
Channel = ichannel;
// 设置日志路径为通道ID对应的日志路径
LogPath = channel.Id.GetLogPath();
@@ -366,21 +360,12 @@ public class ChannelThread
try
{
//添加保存数据变量读取操作
var saveVariable = driverBase.CurrentDevice.VariableRunTimes.Where(a => a.Value.SaveValue).Select(a =>
{
return new CacheDBItem<JToken>()
{
Id = a.Value.Id,
Value = JToken.FromObject(a.Value.Value)
};
}).ToList();
var saveVariable = driverBase.CurrentDevice.VariableRunTimes.Where(a => a.Value.SaveValue).Select(a => (Variable)a.Value).ToList();
if (saveVariable.Count > 0)
{
var cacheDb = CacheDBUtil.GetCache(typeof(CacheDBItem<JToken>), nameof(VariableRunTime), nameof(VariableRunTime.SaveValue));
var varList = await cacheDb.DBProvider.Storageable(saveVariable).ExecuteCommandAsync().ConfigureAwait(false);
cacheDb.SafeDispose();
using var db = DbContext.Db.GetConnectionScopeWithAttr<Variable>().CopyNew();
var result = await db.Updateable<Variable>(saveVariable).UpdateColumns(a => a.Value).ExecuteCommandAsync().ConfigureAwait(false);
}
}
catch (Exception ex)
@@ -475,7 +460,10 @@ public class ChannelThread
driver?.ConfigurePlugins();
}
// 设置通道的底层配置
Channel?.Setup(FoundataionConfig?.Clone());
if (Channel != null)
{
await Channel.SetupAsync(FoundataionConfig?.Clone()).ConfigureAwait(false);
}
}
else
{

View File

@@ -70,7 +70,8 @@ public abstract class CollectBase : DriverBase
Func<VariableRunTime, bool> source = (a =>
{
return a.RegisterAddress != nameof(DeviceRunTime.DeviceStatus) &&
a.RegisterAddress != "Script"
a.RegisterAddress != "Script" &&
a.RegisterAddress != "ScriptRead"
;
});
@@ -125,7 +126,7 @@ public abstract class CollectBase : DriverBase
if (method != null)
{
// 构建 VariableMethod 对象
var methodResult = new VariableMethod(new Method(method.MethodInfo), item, item.IntervalTime ?? item.CollectDeviceRunTime.IntervalTime);
var methodResult = new VariableMethod(new Method(method.MethodInfo), item, string.IsNullOrWhiteSpace(item.IntervalTime) ? item.CollectDeviceRunTime.IntervalTime : item.IntervalTime);
variablesMethodResult.Add(methodResult);
}
else
@@ -176,70 +177,77 @@ public abstract class CollectBase : DriverBase
/// <returns></returns>
protected override async ValueTask ProtectedExecuteAsync(CancellationToken cancellationToken)
{
ReadResultCount readResultCount = new();
if (cancellationToken.IsCancellationRequested)
return;
if (await TestOnline(cancellationToken).ConfigureAwait(false))
return;
if (CollectProperties.ConcurrentCount > 1)
try
{
// 并行处理每个变量读取
await CurrentDevice.VariableSourceReads.ParallelForEachAsync(async (variableSourceRead, cancellationToken) =>
ReadResultCount readResultCount = new();
if (cancellationToken.IsCancellationRequested)
return;
if (await TestOnline(cancellationToken).ConfigureAwait(false))
return;
if (CollectProperties.ConcurrentCount > 1)
{
if (cancellationToken.IsCancellationRequested)
return;
if (await ReadVariableSource(readResultCount, variableSourceRead, cancellationToken, false).ConfigureAwait(false))
return;
// 并行处理每个变量读取
await CurrentDevice.VariableSourceReads.ParallelForEachAsync(async (variableSourceRead, cancellationToken) =>
{
if (cancellationToken.IsCancellationRequested)
return;
if (await ReadVariableSource(readResultCount, variableSourceRead, cancellationToken, false).ConfigureAwait(false))
return;
}
, CollectProperties.ConcurrentCount, cancellationToken).ConfigureAwait(false);
}
else
{
for (int i = 0; i < CurrentDevice.VariableSourceReads.Count; i++)
{
if (cancellationToken.IsCancellationRequested)
return;
// 每10包延迟一次
if (await ReadVariableSource(readResultCount, CurrentDevice.VariableSourceReads[i], cancellationToken, i % 10 == 9).ConfigureAwait(false))
return;
}
}
if (CollectProperties.ConcurrentCount > 1)
{
// 并行处理每个方法调用
await CurrentDevice.ReadVariableMethods.ParallelForEachAsync(async (readVariableMethods, cancellationToken) =>
{
if (cancellationToken.IsCancellationRequested)
return;
if (await ReadVariableMed(readResultCount, readVariableMethods, cancellationToken, false).ConfigureAwait(false))
return;
}
, CollectProperties.ConcurrentCount, cancellationToken).ConfigureAwait(false);
}
else
{
for (int i = 0; i < CurrentDevice.VariableSourceReads.Count; i++)
{
if (cancellationToken.IsCancellationRequested)
return;
// 每10包延迟一次
if (await ReadVariableSource(readResultCount, CurrentDevice.VariableSourceReads[i], cancellationToken, i % 10 == 9).ConfigureAwait(false))
return;
}
}
if (CollectProperties.ConcurrentCount > 1)
{
// 并行处理每个方法调用
await CurrentDevice.ReadVariableMethods.ParallelForEachAsync(async (readVariableMethods, cancellationToken) =>
else
{
if (cancellationToken.IsCancellationRequested)
return;
if (await ReadVariableMed(readResultCount, readVariableMethods, cancellationToken, false).ConfigureAwait(false))
return;
for (int i = 0; i < CurrentDevice.ReadVariableMethods.Count; i++)
{
if (cancellationToken.IsCancellationRequested)
return;
// 每10包延迟一次
if (await ReadVariableMed(readResultCount, CurrentDevice.ReadVariableMethods[i], cancellationToken, i % 10 == 9).ConfigureAwait(false))
return;
}
}
, CollectProperties.ConcurrentCount, cancellationToken).ConfigureAwait(false);
}
else
{
for (int i = 0; i < CurrentDevice.ReadVariableMethods.Count; i++)
// 如果所有方法和变量读取都成功,则清零错误计数器
if (readResultCount.deviceMethodsVariableFailedNum == 0 && readResultCount.deviceSourceVariableFailedNum == 0 && (readResultCount.deviceMethodsVariableSuccessNum != 0 || readResultCount.deviceSourceVariableSuccessNum != 0))
{
if (cancellationToken.IsCancellationRequested)
return;
// 每10包延迟一次
if (await ReadVariableMed(readResultCount, CurrentDevice.ReadVariableMethods[i], cancellationToken, i % 10 == 9).ConfigureAwait(false))
return;
//只有成功读取一次,失败次数都会清零
CurrentDevice.SetDeviceStatus(TimerX.Now, 0);
}
}
// 如果所有方法和变量读取都成功,则清零错误计数器
if (readResultCount.deviceMethodsVariableFailedNum == 0 && readResultCount.deviceSourceVariableFailedNum == 0 && (readResultCount.deviceMethodsVariableSuccessNum != 0 || readResultCount.deviceSourceVariableSuccessNum != 0))
}
finally
{
//只有成功读取一次,失败次数都会清零
CurrentDevice.SetDeviceStatus(TimerX.Now, 0);
ScriptVariableRun(cancellationToken);
}
ScriptVariableRun(cancellationToken);
#region
async ValueTask<bool> ReadVariableMed(ReadResultCount readResultCount, VariableMethod readVariableMethods, CancellationToken cancellationToken, bool delay = true)
@@ -473,7 +481,7 @@ public abstract class CollectBase : DriverBase
{
variableRunTime.SetValue(variableRunTime.CollectDeviceRunTime.DeviceStatus, dateTime);
}
else
else if (variableRunTime.RegisterAddress == "ScriptRead")
{
variableRunTime.SetValue(default, dateTime);
}
@@ -667,6 +675,7 @@ public abstract class CollectBase : DriverBase
// 初始化结果字典
Dictionary<string, OperResult> results = new Dictionary<string, OperResult>();
// 遍历写入信息列表
foreach (var (deviceVariable, jToken) in writeInfoLists)
{
@@ -690,8 +699,18 @@ public abstract class CollectBase : DriverBase
}
}
var writePList = writeInfoLists.Where(a => !CurrentDevice.OtherVariableRunTimes.Contains(a.Key));
var writeSList = writeInfoLists.Where(a => CurrentDevice.OtherVariableRunTimes.Contains(a.Key));
DateTime now = DateTime.Now;
foreach (var item in writeSList)
{
results.TryAdd(item.Key.Name, item.Key.SetValue(item.Value, now));
}
// 过滤掉转换失败的变量,只保留写入成功的变量进行写入操作
var results1 = await WriteValuesAsync(writeInfoLists
var results1 = await WriteValuesAsync(writePList
.Where(a => !results.Any(b => b.Key == a.Key.Name))
.ToDictionary(item => item.Key, item => item.Value),
cancellationToken).ConfigureAwait(false);

View File

@@ -134,7 +134,7 @@ public abstract class DriverBase : DisposableObject
{
if (Protocol != null)
{
FoundataionConfig.ConfigurePlugins(Protocol.ConfigurePlugins());
FoundataionConfig?.ConfigurePlugins(Protocol.ConfigurePlugins());
}
}
@@ -184,7 +184,7 @@ public abstract class DriverBase : DisposableObject
/// <summary>
/// 底层驱动配置
/// </summary>
public TouchSocketConfig FoundataionConfig => ChannelThread?.FoundataionConfig;
public TouchSocketConfig? FoundataionConfig => ChannelThread?.FoundataionConfig;
/// <summary>
/// 日志

View File

@@ -51,6 +51,11 @@ public class InternalTableColumn(string fieldName, Type fieldType, string? field
public string? PlaceHolder { get; set; }
public Type PropertyType => fieldType;
/// <summary>
/// <inheritdoc/>
/// </summary>
public ILookupService? LookupService { get; set; }
/// <summary>
/// <inheritdoc/>
/// </summary>
@@ -91,7 +96,7 @@ public class InternalTableColumn(string fieldName, Type fieldType, string? field
public RenderFragment<ITableColumn>? ToolboxTemplate { get; set; }
public IFilter? Filter { get; set; }
public string? FormatString { get; set; }
public Func<object?, Task<string?>>? Formatter { get; set; }
public Func<object, Task<string?>>? Formatter { get; set; }
public Alignment? Align { get; set; }
public bool? ShowTips { get; set; }
public Func<object?, Task<string?>>? GetTooltipTextCallback { get; set; }

View File

@@ -57,6 +57,11 @@ public interface IPluginService
/// </summary>
public QueryData<PluginOutput> Page(QueryPageOptions options, PluginTypeEnum? pluginTypeEnum = null);
/// <summary>
/// 重载插件
/// </summary>
void Reload();
/// <summary>
/// 添加插件
/// </summary>

View File

@@ -38,7 +38,7 @@ internal sealed class PluginService : IPluginService
/// <summary>
/// 插件驱动文件夹名称
/// </summary>
public const string DirName = "Plugins";
public const string DirName = "GatewayPlugins";
private const string _cacheKeyGetPluginOutputs = $"{ThingsGatewayCacheConst.Cache_Prefix}{nameof(PluginService)}{nameof(GetList)}";
private const string SaveEx = ".save";
@@ -63,13 +63,25 @@ internal sealed class PluginService : IPluginService
//创建插件文件夹
Directory.CreateDirectory(AppContext.BaseDirectory.CombinePathWithOs(PluginService.DirName));
//主程序上下文驱动类字典
_driverBaseDict = new(App.EffectiveTypes
.Where(x => (typeof(CollectBase).IsAssignableFrom(x) || typeof(BusinessBase).IsAssignableFrom(x)) && x.IsClass && !x.IsAbstract)
.ToDictionary(a => $"{Path.GetFileNameWithoutExtension(new FileInfo(a.Assembly.Location).Name)}.{a.Name}"));
_defaultDriverBaseDict = new(App.EffectiveTypes
.Where(x => (typeof(CollectBase).IsAssignableFrom(x) || typeof(BusinessBase).IsAssignableFrom(x)) && x.IsClass && !x.IsAbstract)
.ToDictionary(a => $"{Path.GetFileNameWithoutExtension(new FileInfo(a.Assembly.Location).Name)}.{a.Name}"));
DeleteBackup(DirName);
DeleteBackup(AppContext.BaseDirectory);
}
/// <summary>
/// 插件文件名称/插件域
/// </summary>
private ConcurrentDictionary<string, (AssemblyLoadContext AssemblyLoadContext, Assembly Assembly)> _assemblyLoadContextDict { get; } = new();
/// <summary>
/// 主程序上下文中的插件FullName/插件Type
/// </summary>
private System.Collections.ObjectModel.ReadOnlyDictionary<string, Type> _defaultDriverBaseDict { get; }
/// <summary>
/// 插件FullName/插件Type
/// </summary>
@@ -92,8 +104,18 @@ internal sealed class PluginService : IPluginService
// 解析插件名称,获取文件名和类型名
var filtResult = PluginServiceUtil.GetFileNameAndTypeName(pluginName);
// 如果是默认键,则搜索主程序上下文中的类型
if (_defaultDriverBaseDict.TryGetValue(pluginName, out var type))
{
var driver = (DriverBase)Activator.CreateInstance(type);
if (!type.Assembly.Location.IsNullOrEmpty())
driver.Directory = Path.GetDirectoryName(type.Assembly.Location);
return driver;
}
// 构建插件目录路径
var dir = AppContext.BaseDirectory.CombinePathWithOs(DirName);
var dir = AppContext.BaseDirectory.CombinePathWithOs(DirName, filtResult.FileName);
// 先判断是否已经拥有插件模块
if (_driverBaseDict.TryGetValue(pluginName, out var value))
@@ -102,11 +124,35 @@ internal sealed class PluginService : IPluginService
driver.Directory = dir;
return driver;
}
else
Assembly assembly = null;
// 根据路径获取DLL文件
var path = dir.CombinePathWithOs($"{filtResult.FileName}.dll");
assembly = GetAssembly(path, filtResult.FileName);
if (assembly != null)
{
// 根据采集/业务类型获取实际插件类
var driverType = assembly.GetTypes()
.Where(x => (typeof(CollectBase).IsAssignableFrom(x) || typeof(BusinessBase).IsAssignableFrom(x)) && x.IsClass && !x.IsAbstract)
.FirstOrDefault(it => it.Name == filtResult.TypeName);
if (driverType != null)
{
var driver = (DriverBase)Activator.CreateInstance(driverType);
_logger?.LogInformation(Localizer[$"LoadTypeSuccess", pluginName]);
_driverBaseDict.TryAdd(pluginName, driverType);
driver.Directory = dir;
return driver;
}
// 抛出异常,插件类型不存在
throw new(Localizer[$"LoadTypeFail1", pluginName]);
}
else
{
// 抛出异常,插件文件不存在
throw new(Localizer[$"LoadTypeFail2", path]);
}
}
finally
{
@@ -288,6 +334,29 @@ internal sealed class PluginService : IPluginService
return query;
}
/// <summary>
/// 移除全部插件
/// </summary>
public void Reload()
{
try
{
_locker.Wait();
_driverBaseDict.Clear();
foreach (var item in _assemblyLoadContextDict)
{
item.Value.AssemblyLoadContext.Unload();
}
GC.Collect();
ClearCache();
_assemblyLoadContextDict.Clear();
}
finally
{
_locker.Release();
}
}
/// <summary>
/// 异步保存驱动程序信息。
/// </summary>
@@ -307,9 +376,24 @@ internal sealed class PluginService : IPluginService
var maxFileSize = 100 * 1024 * 1024; // 最大100MB
// 获取主程序集文件名
//var mainFileName = Path.GetFileNameWithoutExtension(plugin.MainFile.Name);
// 构建插件文件夹绝对路径
var fullDir = AppContext.BaseDirectory.CombinePathWithOs(DirName);
var mainFileName = Path.GetFileNameWithoutExtension(plugin.MainFile.Name);
string fullDir = string.Empty;
bool isDefaultDriver = false;
//判定是否上下文程序集
var defaultDriver = _defaultDriverBaseDict.FirstOrDefault(a => Path.GetFileNameWithoutExtension(new FileInfo(a.Value.Assembly.Location).Name) == mainFileName);
if (defaultDriver.Value != null)
{
var filtResult = PluginServiceUtil.GetFileNameAndTypeName(defaultDriver.Key);
fullDir = Path.GetDirectoryName(filtResult.FileName);
isDefaultDriver = true;
}
else
{
// 构建插件文件夹绝对路径
fullDir = AppContext.BaseDirectory.CombinePathWithOs(DirName, mainFileName);
isDefaultDriver = false;
}
try
{
// 构建主程序集绝对路径
@@ -355,25 +439,56 @@ internal sealed class PluginService : IPluginService
{
throw new(Localizer[$"PluginNotFound"]);
}
assembly = null;
// 将主程序集保存到文件
await MarkSave(fullPath, mainMemoryStream).ConfigureAwait(false);
// 将其他文件保存到文件
foreach (var item in otherFilesStreams)
if (isDefaultDriver)
{
await MarkSave(fullDir.CombinePathWithOs(item.Name), item.MemoryStream).ConfigureAwait(false);
// 将主程序集保存到文件
await MarkSave(fullPath, mainMemoryStream).ConfigureAwait(false);
// 将其他文件保存到文件
foreach (var item in otherFilesStreams)
{
await MarkSave(fullDir.CombinePathWithOs(item.Name), item.MemoryStream).ConfigureAwait(false);
}
}
else
{
// 将主程序集保存到文件
mainMemoryStream.Seek(0, SeekOrigin.Begin);
Directory.CreateDirectory(fullDir);// 创建插件文件夹
using FileStream fs = new(fullPath, FileMode.Create);
await mainMemoryStream.CopyToAsync(fs).ConfigureAwait(false);
// 将其他文件保存到文件
foreach (var item in otherFilesStreams)
{
item.MemoryStream.Seek(0, SeekOrigin.Begin);
using FileStream fs1 = new(fullDir.CombinePathWithOs(item.Name), FileMode.Create);
await item.MemoryStream.CopyToAsync(fs1).ConfigureAwait(false);
await item.MemoryStream.DisposeAsync().ConfigureAwait(false);
}
}
}
finally
{
// 卸载程序集加载上下文并清除缓存
assemblyLoadContext.Unload();
ClearCache();
try
{
// 卸载相同文件的插件域
DeletePlugin(mainFileName);
}
catch
{
}
}
}
finally
@@ -458,7 +573,25 @@ internal sealed class PluginService : IPluginService
lock (this)
{
App.CacheService.Remove(_cacheKeyGetPluginOutputs);
App.CacheService.DelByPattern($"{nameof(PluginService)}_");
//多语言缓存清理
try
{
// 获取私有字段
FieldInfo fieldInfo = typeof(ResourceManagerStringLocalizerFactory).GetField("_localizerCache", BindingFlags.Instance | BindingFlags.NonPublic);
// 获取字段的值
var dictionary = (ConcurrentDictionary<string, ResourceManagerStringLocalizer>)fieldInfo.GetValue(App.StringLocalizerFactory);
foreach (var item in _assemblyLoadContextDict)
{
// 移除特定键
dictionary.RemoveWhere(a => item.Value.Assembly.ExportedTypes.Select(b => b.AssemblyQualifiedName).Contains(a.Key));
}
}
catch
{
}
_ = Task.Run(() =>
{
_dispatchService.Dispatch(new());
@@ -468,6 +601,104 @@ internal sealed class PluginService : IPluginService
#endregion public
/// <summary>
/// 删除插件域,卸载插件
/// </summary>
/// <param name="path">主程序集文件名称</param>
private void DeletePlugin(string path)
{
if (_assemblyLoadContextDict.TryGetValue(path, out var assemblyLoadContext))
{
//移除字典
_driverBaseDict.RemoveWhere(a => path == PluginServiceUtil.GetFileNameAndTypeName(a.Key).FileName);
_assemblyLoadContextDict.Remove(path);
//卸载
assemblyLoadContext.AssemblyLoadContext.Unload();
GC.Collect();
}
}
/// <summary>
/// 获取程序集
/// </summary>
/// <param name="path">插件主文件绝对路径</param>
/// <param name="fileName">插件主文件名称</param>
/// <returns></returns>
private Assembly GetAssembly(string path, string fileName)
{
Assembly assembly = null;
_logger?.LogInformation(Localizer["AddPluginFile", path]);
//全部程序集路径
List<string> paths = new();
Directory.GetFiles(Path.GetDirectoryName(path), "*.dll").ToList().ForEach(a => paths.Add(a));
if (_assemblyLoadContextDict.TryGetValue(fileName, out (AssemblyLoadContext AssemblyLoadContext, Assembly Assembly) value))
{
assembly = value.Assembly;
}
else
{
//新建插件域,并注明可卸载
var assemblyLoadContext = new AssemblyLoadContext(fileName, true);
//获取插件程序集
assembly = GetAssembly(path, paths, assemblyLoadContext);
if (assembly == null)
{
assemblyLoadContext.Unload();
return null;
}
//添加到全局对象
_assemblyLoadContextDict.TryAdd(fileName, (assemblyLoadContext, assembly));
}
return assembly;
}
/// <summary>
/// 获取程序集
/// </summary>
/// <param name="path">主程序的路径</param>
/// <param name="paths">全部文件的路径</param>
/// <param name="assemblyLoadContext">当前插件域</param>
/// <returns></returns>
private Assembly GetAssembly(string path, List<string> paths, AssemblyLoadContext assemblyLoadContext)
{
Assembly assembly = null;
//var cacheId = CommonUtils.GetSingleId();
//foreach (var item in paths)
//{
// var dir = Path.GetDirectoryName(item).CombinePathWithOs($"Cache{cacheId}");
// var cachePath = dir.CombinePath(Path.GetFileName(item));
// Directory.CreateDirectory(dir);
// File.Copy(item, cachePath, true);
//}
foreach (var item in paths)
{
using var fs = new FileStream(item, FileMode.Open);
//var cachePath = Path.GetDirectoryName(item).CombinePathWithOs($"Cache{cacheId}").CombinePath(Path.GetFileName(item));
if (item == path)
{
assembly = assemblyLoadContext.LoadFromStream(fs); //加载主程序集,并获取
//修改为从文件创立满足roslyn引擎的要求
//assembly = assemblyLoadContext.LoadFromAssemblyPath(cachePath);
}
else
{
try
{
//assemblyLoadContext.LoadFromAssemblyPath(cachePath);
assemblyLoadContext.LoadFromStream(fs); //加载主程序集,并获取
}
catch (Exception ex)
{
_logger?.LogWarning(ex, Localizer[$"LoadOtherFileFail", item]);
}
}
}
return assembly;
}
/// <summary>
/// 获取全部插件信息
/// </summary>
@@ -510,8 +741,8 @@ internal sealed class PluginService : IPluginService
List<PluginOutput> plugins = new List<PluginOutput>();
// 主程序上下文
// 遍历程序集上下文插件驱动字典,生成插件驱动信息
foreach (var item in _driverBaseDict)
// 遍历程序集上下文默认驱动字典,生成默认驱动插件信息
foreach (var item in _defaultDriverBaseDict)
{
if (PluginServiceUtil.IsSupported(item.Value))
{
@@ -531,10 +762,59 @@ internal sealed class PluginService : IPluginService
};
pluginOutput.DeviceCount = App.GetService<IDeviceService>().GetAll().Count(a => a.PluginName == pluginOutput.FullName);//关联设备数量
plugins.Add(pluginOutput);
}
}
// 获取插件文件夹路径列表
string[] folderPaths = Directory.GetDirectories(AppContext.BaseDirectory.CombinePathWithOs(DirName));
// 遍历插件文件夹
foreach (string folderPath in folderPaths)
{
//当前插件文件夹
try
{
var driverMainName = Path.GetFileName(folderPath);
FileInfo fileInfo = new FileInfo(folderPath.CombinePathWithOs($"{driverMainName}.dll")); //插件主程序集名称约定为文件夹名称.dll
DateTime lastWriteTime = fileInfo.LastWriteTime;
// 加载插件程序集并获取其中的驱动类型信息
var assembly = GetAssembly(folderPath.CombinePathWithOs($"{driverMainName}.dll"), driverMainName);
var driverTypes = assembly.GetTypes().Where(x => (typeof(CollectBase).IsAssignableFrom(x) || typeof(BusinessBase).IsAssignableFrom(x)) && x.IsClass && !x.IsAbstract);
// 遍历驱动类型,生成插件信息,并将其添加到插件列表中
foreach (var type in driverTypes)
{
if (PluginServiceUtil.IsSupported(type))
{
// 先判断是否已经拥有插件模块
if (!_driverBaseDict.ContainsKey($"{driverMainName}.{type.Name}"))
{
//添加到字典
_driverBaseDict.TryAdd($"{driverMainName}.{type.Name}", type);
_logger?.LogInformation(Localizer[$"LoadTypeSuccess", PluginServiceUtil.GetFullName(driverMainName, type.Name)]);
}
plugins.Add(
new PluginOutput()
{
Name = type.Name, //类型名称
FileName = $"{driverMainName}", //主程序集名称
PluginType = (typeof(CollectBase).IsAssignableFrom(type)) ? PluginTypeEnum.Collect : PluginTypeEnum.Business,//插件类型
Version = assembly.GetName().Version.ToString(),//插件版本
LastWriteTime = lastWriteTime, //编译时间
EducationPlugin = PluginServiceUtil.IsEducation(type),
}
);
}
}
}
catch (Exception ex)
{
// 记录加载插件失败的日志
_logger?.LogWarning(ex, Localizer[$"LoadPluginFail", Path.GetRelativePath(AppContext.BaseDirectory.CombinePathWithOs(DirName), folderPath)]);
}
}
return plugins.OrderBy(a => a.EducationPlugin).ThenByDescending(a => a.DeviceCount).ToList();
}
}

View File

@@ -79,11 +79,9 @@ public interface IVariableService
Task ImportVariableAsync(Dictionary<string, ImportPreviewOutputBase> input);
/// <summary>
/// 创建n个modbus变量n/1000个设备n/1000个通道1个modbus 模拟服务端
/// 创建n个modbus变量
/// </summary>
/// <param name="count"></param>
/// <returns></returns>
Task InsertTestDataAsync(int count);
Task InsertTestDataAsync(int variableCount, int deviceCount, string slaveUrl = "127.0.0.1:502");
/// <summary>
/// 表格查询

View File

@@ -67,15 +67,16 @@ internal sealed class VariableService : BaseService<Variable>, IVariableService
#region
public async Task InsertTestDataAsync(int count)
public async Task InsertTestDataAsync(int variableCount, int deviceCount, string slaveUrl = "127.0.0.1:502")
{
if (slaveUrl.IsNullOrWhiteSpace()) slaveUrl = "127.0.0.1:502";
if (deviceCount > variableCount) variableCount = deviceCount;
List<Channel> newChannels = new();
List<Device> newDevices = new();
List<Variable> newVariables = new();
var addressNum = 1;
var variableCount = 10000;
var channelCount = Math.Max(count / variableCount, 1);
for (int i = 0; i < channelCount; i++)
var groupVariableCount = (int)Math.Ceiling((decimal)variableCount / deviceCount);
for (int i = 0; i < deviceCount; i++)
{
Channel channel = new Channel();
Device device = new Device();
@@ -87,7 +88,7 @@ internal sealed class VariableService : BaseService<Variable>, IVariableService
channel.Id = id;
channel.CreateUserId = UserManager.UserId;
channel.CreateOrgId = UserManager.OrgId;
channel.RemoteUrl = "127.0.0.1:502";
channel.RemoteUrl = slaveUrl;
//动态插件属性默认
newChannels.Add(channel);
}
@@ -98,21 +99,23 @@ internal sealed class VariableService : BaseService<Variable>, IVariableService
device.Id = id;
device.PluginType = PluginTypeEnum.Collect;
device.ChannelId = channel.Id;
device.IntervalTime = 1000;
device.IntervalTime = "1000";
device.CreateUserId = UserManager.UserId;
device.CreateOrgId = UserManager.OrgId;
device.PluginName = "ThingsGateway.Plugin.Modbus.ModbusMaster";
//动态插件属性默认
newDevices.Add(device);
}
if (channelCount == i + 1 && (count % 10000) != 0)
variableCount = Math.Min(count % 10000, 10000);
for (int i1 = 0; i1 < variableCount; i1++)
if (i != 0 && i == deviceCount - 1)
{
groupVariableCount = variableCount - deviceCount * (groupVariableCount - 1);
}
for (int i1 = 0; i1 < groupVariableCount; i1++)
{
addressNum++;
if (addressNum >= 65500)
addressNum = 1;
var address = $"4{addressNum}";
addressNum++;
var id = CommonUtils.GetSingleId();
var name = $"testVariable{id}";
Variable variable = new Variable();
@@ -135,6 +138,7 @@ internal sealed class VariableService : BaseService<Variable>, IVariableService
var name = $"testChannel{id}";
serviceChannel.ChannelType = ChannelTypeEnum.TcpService;
serviceChannel.Name = name;
serviceChannel.Enable = true;
serviceChannel.CreateUserId = UserManager.UserId;
serviceChannel.CreateOrgId = UserManager.OrgId;
serviceChannel.Id = id;
@@ -150,11 +154,41 @@ internal sealed class VariableService : BaseService<Variable>, IVariableService
serviceDevice.CreateUserId = UserManager.UserId;
serviceDevice.CreateOrgId = UserManager.OrgId;
serviceDevice.ChannelId = serviceChannel.Id;
serviceDevice.IntervalTime = 1000;
serviceDevice.IntervalTime = "1000";
serviceDevice.PluginName = "ThingsGateway.Plugin.Modbus.ModbusSlave";
newDevices.Add(serviceDevice);
}
Channel mqttChannel = new Channel();
Device mqttDevice = new Device();
{
var id = CommonUtils.GetSingleId();
var name = $"testChannel{id}";
mqttChannel.ChannelType = ChannelTypeEnum.Other;
mqttChannel.Name = name;
mqttChannel.CreateUserId = UserManager.UserId;
mqttChannel.CreateOrgId = UserManager.OrgId;
mqttChannel.Id = id;
newChannels.Add(mqttChannel);
}
{
var id = CommonUtils.GetSingleId();
var name = $"testDevice{id}";
mqttDevice.Name = name;
mqttDevice.PluginType = PluginTypeEnum.Business;
mqttDevice.Id = id;
mqttDevice.CreateUserId = UserManager.UserId;
mqttDevice.CreateOrgId = UserManager.OrgId;
mqttDevice.ChannelId = mqttChannel.Id;
mqttDevice.IntervalTime = "1000";
mqttDevice.PluginName = "ThingsGateway.Plugin.Mqtt.MqttServer";
mqttDevice.DevicePropertys = new Dictionary<string, string>
{
{"IsAllVariable", "true"}
};
newDevices.Add(mqttDevice);
}
using var db = GetDB();
var result = await db.UseTranAsync(async () =>

View File

@@ -19,6 +19,23 @@ public class Startup : AppStartup
{
public void ConfigureAdminApp(IServiceCollection services)
{
var tempDir = Path.Combine(AppContext.BaseDirectory, "CSSCRIPT");
if (Directory.Exists(tempDir))
{
try
{
Directory.Delete(tempDir);
}
catch
{
}
}
Directory.CreateDirectory(tempDir);//重新创建,防止缓存的一些目录信息错误
Environment.SetEnvironmentVariable("CSS_CUSTOM_TEMPDIR", tempDir); //传入变量
//底层多语言配置
//Foundation.LocalizerUtil.SetLocalizerFactory((a) => App.CreateLocalizerByType(a));

View File

@@ -1,35 +1,38 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="$(SolutionDir)Version.props" />
<Import Project="$(SolutionDir)PackNuget.props" />
<Import Project="$(SolutionDir)Version.props" />
<Import Project="$(SolutionDir)PackNuget.props" />
<ItemGroup>
<PackageReference Include="SqlSugar.TDengineCore" Version="4.18.0" />
<PackageReference Include="Rougamo.Fody" Version="5.0.0" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="SqlSugar.TDengineCore" Version="4.18.1" />
<PackageReference Include="Rougamo.Fody" Version="5.0.0" />
<PackageReference Include="TouchSocket.Dmtp" Version="3.0.10" />
<PackageReference Include="TouchSocket.WebApi.Swagger" Version="3.0.10" />
<ItemGroup>
<PackageReference Include="ThingsGateway.Admin.Application" Version="$(AdminVersion)" />
</ItemGroup>
</ItemGroup>
<ItemGroup>
<Content Remove="Locales\*.json" />
<EmbeddedResource Include="Locales\*.json">
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\Admin\src\ThingsGateway.Admin.Application\ThingsGateway.Admin.Application.csproj" />
</ItemGroup>
<ItemGroup>
<Content Remove="SeedData\Gateway\*.json" />
<Content Include="SeedData\Gateway\*.json" Pack="true" PackagePath="\Content\SeedData\Gateway\">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<Content Remove="Locales\*.json" />
<EmbeddedResource Include="Locales\*.json">
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<Content Remove="SeedData\Gateway\*.json" />
<Content Include="SeedData\Gateway\*.json" Pack="true" PackagePath="\Content\SeedData\Gateway\">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Foundation\ThingsGateway.CSScript\ThingsGateway.CSScript.csproj" />
<ProjectReference Include="..\..\Foundation\ThingsGateway.Foundation\ThingsGateway.Foundation.csproj" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Foundation\ThingsGateway.CSScript\ThingsGateway.CSScript.csproj" />
<ProjectReference Include="..\..\Foundation\ThingsGateway.Foundation\ThingsGateway.Foundation.csproj" />
</ItemGroup>
</Project>

View File

@@ -1,5 +1,7 @@
{
"ThingsGateway.Gateway.Razor.PluginPage": {
"Reload": "Reload"
},
"ThingsGateway.Gateway.Razor.TcpSessionClientDto": {
"Id": "Id",
@@ -62,7 +64,7 @@
},
"ThingsGateway.Gateway.Razor.SavePlugin": {
"SavePlugin": "Note: Plugins with the same file name will be overwritten",
"SavePlugin1": "Plugin changes will take effect after restarting the software"
"SavePlugin1": "Plugin changes may take effect after restarting the software"
},
"ThingsGateway.Gateway.Razor.BackendLogPage": {
@@ -92,7 +94,9 @@
"ThingsGateway.Gateway.Razor.VariablePage": {
"ImportExcel": "Import Variable",
"Clear": "Clear",
"TestCount": "TestVariableCount",
"TestVariableCount": "TestVariableCount",
"TestDeviceCount": "TestDeviceCount",
"SlaveUrl": "SlaveUrl",
"Test": "AddTestVariable"
},
"ThingsGateway.Gateway.Razor.DeviceEditComponent": {

View File

@@ -1,5 +1,7 @@
{
"ThingsGateway.Gateway.Razor.PluginPage": {
"Reload": "重载"
},
"ThingsGateway.Gateway.Razor.TcpSessionClientDto": {
"Id": "Id",
"IP": "IP",
@@ -61,7 +63,7 @@
},
"ThingsGateway.Gateway.Razor.SavePlugin": {
"SavePlugin": "注意:文件名称相同的插件将被覆盖",
"SavePlugin1": "插件变动需重启软件后生效"
"SavePlugin1": "插件变动可能需重启软件后生效"
},
"ThingsGateway.Gateway.Razor.BackendLogPage": {
@@ -91,7 +93,9 @@
"ThingsGateway.Gateway.Razor.VariablePage": {
"ImportExcel": "导入变量",
"Clear": "清空",
"TestCount": "测试变量数量",
"TestVariableCount": "变量数量",
"TestDeviceCount": "采集设备数量",
"SlaveUrl": "服务端Url",
"Test": "一键添加测试变量"
},
"ThingsGateway.Gateway.Razor.DeviceEditComponent": {

View File

@@ -1,5 +1,7 @@
{
"ThingsGateway.Gateway.Razor.PluginPage": {
"Reload": "重載"
},
"ThingsGateway.Gateway.Razor.TcpSessionClientDto": {
"Id": "Id",
"IP": "IP",
@@ -59,7 +61,7 @@
},
"ThingsGateway.Gateway.Razor.SavePlugin": {
"SavePlugin": "注意:文件名稱相同的插件將被覆蓋",
"SavePlugin1": "挿件變動需重啓軟件後生效"
"SavePlugin1": "挿件變動可能需重啓軟件後生效"
},
"ThingsGateway.Gateway.Razor.BackendLogPage": {
"BackendLog": "網關後台日誌",
@@ -88,7 +90,9 @@
"ThingsGateway.Gateway.Razor.VariablePage": {
"ImportExcel": "導入變量",
"Clear": "清空",
"TestCount": "測試變量數量",
"TestVariableCount": "變量數量",
"TestDeviceCount": "採集設備數量",
"SlaveUrl": "服務端Url",
"Test": "一鍵添加測試變量"
},
"ThingsGateway.Gateway.Razor.DeviceEditComponent": {

View File

@@ -6,9 +6,16 @@
@using ThingsGateway.Gateway.Application
@namespace ThingsGateway.Gateway.Razor
<ValidateForm Model="Model.PluginPropertyModel.Value" @ref=Model.PluginPropertyModel.ValidateForm Id="@Model.Id.ToString()">
<EditorFormObject class="p-2" Items=PluginPropertyEditorItems AutoGenerateAllItem="false" RowType=RowType.Inline ItemsPerRow=2 ShowLabelTooltip=true LabelWidth=250 Model="Model.PluginPropertyModel.Value" @key=Model.PluginPropertyModel.Value.GetType().TypeHandle.Value>
<ValidateForm Model="Model.PluginPropertyModel.Value"
@key=@($"DeviceEditValidateForm{Model.Id}{Model.PluginPropertyModel.Value.GetType().TypeHandle.Value}")
@ref=Model.PluginPropertyModel.ValidateForm
Id=@($"DeviceEditValidateForm{Model.Id}{Model.PluginPropertyModel.Value.GetType().TypeHandle.Value}")
>
<EditorFormObject class="p-2" Items=PluginPropertyEditorItems AutoGenerateAllItem="false" RowType=RowType.Inline ItemsPerRow=2 ShowLabelTooltip=true LabelWidth=250 Model="Model.PluginPropertyModel.Value" @key=@($"DeviceEditEditorFormObject{Model.Id}{Model.PluginPropertyModel.Value.GetType().TypeHandle.Value}")
>
<FieldItems>
@if (Model.PluginPropertyModel.Value is BusinessPropertyWithCacheIntervalScript businessProperty)

View File

@@ -31,6 +31,9 @@
<TableToolbarButton TItem="PluginOutput" Color="Color.Info" Icon="fa fa-plus" Text="@RazorLocalizer["Add"]"
OnClickCallback="OnAdd" IsShow=@(AuthorizeButton("添加")) />
<TableToolbarButton TItem="PluginOutput" Color="Color.Warning" Icon="fa-solid fa-rotate" Text="@PluginPageLoaclozer["Reload"]"
OnClick="OnReload" IsShow=@(AuthorizeButton("添加")) />
</TableToolbarTemplate>
<TableColumns>
<TableColumn @bind-Field="@context.Name">

View File

@@ -54,6 +54,9 @@ public partial class PluginPage
[Inject]
private IStringLocalizer<PluginAddInput> PluginAddInputLoaclozer { get; set; }
[Inject]
private IStringLocalizer<PluginPage> PluginPageLoaclozer { get; set; }
private async Task OnAdd(IEnumerable<PluginOutput> pluginOutputs)
{
var op = new DialogOption()
@@ -76,5 +79,10 @@ public partial class PluginPage
await DialogService.Show(op);
}
private void OnReload()
{
PluginService.Reload();
}
#endregion
}

View File

@@ -8,242 +8,259 @@
@if (ValidateEnable)
{
<ValidateForm Model="Model" OnValidSubmit="ValidSubmit">
<Tab>
<TabItem Text=@Localizer["VariableInformation"]>
<EditorForm class="p-2" AutoGenerateAllItem="false" RowType=RowType.Inline ItemsPerRow=2 LabelWidth=250 Model="Model">
<FieldItems>
<EditorItem @bind-Field="@context.Description">
<EditTemplate Context="value">
<div class="col-12">
<h6>@Localizer["BasicInformation"]</h6>
</div>
</EditTemplate>
</EditorItem>
<EditorItem @bind-Field="@context.Name" Readonly=BatchEditEnable />
<EditorItem @bind-Field="@context.Description" />
<EditorItem @bind-Field="@context.Unit" />
<EditorItem @bind-Field="@context.ProtectType" />
<EditorItem @bind-Field="@context.Enable" />
<EditorItem @bind-Field="@context.RpcWriteEnable" />
<EditorItem @bind-Field="@context.SaveValue" />
<ValidateForm Model="Model" OnValidSubmit="ValidSubmit">
<Tab>
<TabItem Text=@Localizer["VariableInformation"]>
<EditorForm class="p-2" AutoGenerateAllItem="false" RowType=RowType.Inline ItemsPerRow=2 LabelWidth=250 Model="Model">
<FieldItems>
<EditorItem @bind-Field="@context.Description">
<EditTemplate Context="value">
<div class="col-12">
<h6>@Localizer["BasicInformation"]</h6>
</div>
</EditTemplate>
</EditorItem>
<EditorItem @bind-Field="@context.Name" Readonly=BatchEditEnable />
<EditorItem @bind-Field="@context.Description" />
<EditorItem @bind-Field="@context.Unit" />
<EditorItem @bind-Field="@context.ProtectType" />
<EditorItem @bind-Field="@context.Enable" />
<EditorItem @bind-Field="@context.RpcWriteEnable" />
<EditorItem @bind-Field="@context.Description">
<EditTemplate Context="value">
<div class="col-12">
<h6>@Localizer["Connection"]</h6>
</div>
</EditTemplate>
</EditorItem>
<EditorItem @bind-Field="@context.Value" />
<EditorItem @bind-Field="@context.SaveValue" />
<EditorItem @bind-Field="@context.DeviceId">
<EditTemplate Context="value">
<div class="col-12 col-md-6">
<Select IsVirtualize @bind-Value="@value.DeviceId" Items="@CollectDevices" ShowSearch="true" OnSelectedItemChanged="OnDeviceSelectedItemChanged" />
</div>
</EditTemplate>
</EditorItem>
<EditorItem @bind-Field="@context.Description">
<EditTemplate Context="value">
<div class="col-12">
<h6>@Localizer["Connection"]</h6>
</div>
</EditTemplate>
</EditorItem>
<EditorItem @bind-Field="@context.DataType" />
<EditorItem @bind-Field="@context.DeviceId">
<EditTemplate Context="value">
<div class="col-12 col-md-6">
<Select IsVirtualize @bind-Value="@value.DeviceId" Items="@CollectDevices" ShowSearch="true" OnSelectedItemChanged="OnDeviceSelectedItemChanged" />
</div>
</EditTemplate>
</EditorItem>
<EditorItem @bind-Field="@context.IntervalTime" />
<EditorItem @bind-Field="@context.DataType" />
<EditorItem @bind-Field="@context.OtherMethod">
<EditTemplate Context="value">
<div class="col-12 col-md-6">
<Select IsVirtualize @bind-Value="@value.OtherMethod" Items="@OtherMethods" ShowSearch="true" />
</div>
</EditTemplate>
</EditorItem>
<EditorItem @bind-Field="@context.IntervalTime" />
<EditorItem @bind-Field="@context.RegisterAddress" Rows="1" />
<EditorItem @bind-Field="@context.OtherMethod">
<EditTemplate Context="value">
<div class="col-12 col-md-6">
<Select IsVirtualize @bind-Value="@value.OtherMethod" Items="@OtherMethods" ShowSearch="true" />
</div>
</EditTemplate>
</EditorItem>
<EditorItem @bind-Field="@context.ReadExpressions" Rows="1" />
<EditorItem @bind-Field="@context.WriteExpressions" Rows="1" />
<EditorItem @bind-Field="@context.RegisterAddress" Rows="1" />
<EditorItem @bind-Field="@context.ReadExpressions" Rows="1" />
<EditorItem @bind-Field="@context.WriteExpressions" Rows="1" />
<EditorItem @bind-Field="@context.Description">
<EditTemplate Context="value">
<div class="col-12">
<h6>@Localizer["Remark"]</h6>
</div>
</EditTemplate>
</EditorItem>
<EditorItem @bind-Field="@context.Remark1" />
<EditorItem @bind-Field="@context.Remark2" />
<EditorItem @bind-Field="@context.Remark3" />
<EditorItem @bind-Field="@context.Remark4" />
<EditorItem @bind-Field="@context.Remark5" />
</FieldItems>
<EditorItem @bind-Field="@context.Description">
<EditTemplate Context="value">
<div class="col-12">
<h6>@Localizer["Remark"]</h6>
</div>
</EditTemplate>
</EditorItem>
<EditorItem @bind-Field="@context.Remark1" />
<EditorItem @bind-Field="@context.Remark2" />
<EditorItem @bind-Field="@context.Remark3" />
<EditorItem @bind-Field="@context.Remark4" />
<EditorItem @bind-Field="@context.Remark5" />
</FieldItems>
</EditorForm>
</EditorForm>
</TabItem>
<TabItem Text=@Localizer["AlarmInformation"]>
<EditorForm class="p-2" AutoGenerateAllItem="false" RowType=RowType.Inline ItemsPerRow=2 LabelWidth=250 Model="Model">
<FieldItems>
</TabItem>
<TabItem Text=@Localizer["AlarmInformation"]>
<EditorForm class="p-2" AutoGenerateAllItem="false" RowType=RowType.Inline ItemsPerRow=2 LabelWidth=250 Model="Model">
<FieldItems>
<EditorItem @bind-Field="@context.BoolCloseAlarmText" />
<EditorItem @bind-Field="@context.BoolCloseRestrainExpressions" />
<EditorItem @bind-Field="@context.BoolCloseAlarmEnable" Rows="1" />
<EditorItem @bind-Field="@context.BoolOpenAlarmText" />
<EditorItem @bind-Field="@context.BoolOpenRestrainExpressions" />
<EditorItem @bind-Field="@context.BoolOpenAlarmEnable" Rows="1" />
<EditorItem @bind-Field="@context.HHAlarmText" />
<EditorItem @bind-Field="@context.HHAlarmCode" />
<EditorItem @bind-Field="@context.HHRestrainExpressions" />
<EditorItem @bind-Field="@context.HHAlarmEnable" />
<EditorItem @bind-Field="@context.HAlarmText" />
<EditorItem @bind-Field="@context.HAlarmCode" />
<EditorItem @bind-Field="@context.HRestrainExpressions" />
<EditorItem @bind-Field="@context.HAlarmEnable" />
<EditorItem @bind-Field="@context.LAlarmText" />
<EditorItem @bind-Field="@context.LAlarmCode" />
<EditorItem @bind-Field="@context.LRestrainExpressions" />
<EditorItem @bind-Field="@context.LAlarmEnable" />
<EditorItem @bind-Field="@context.LLAlarmText" />
<EditorItem @bind-Field="@context.LLAlarmCode" />
<EditorItem @bind-Field="@context.LLRestrainExpressions" />
<EditorItem @bind-Field="@context.LLAlarmEnable" />
<EditorItem @bind-Field="@context.CustomAlarmText" />
<EditorItem @bind-Field="@context.CustomAlarmCode" />
<EditorItem @bind-Field="@context.CustomRestrainExpressions" />
<EditorItem @bind-Field="@context.CustomAlarmEnable" />
<EditorItem @bind-Field="@context.AlarmDelay" />
</FieldItems>
</EditorForm>
</TabItem>
</Tab>
<div class="form-footer">
<EditorItem @bind-Field="@context.BoolCloseAlarmText" />
<EditorItem @bind-Field="@context.BoolCloseRestrainExpressions" />
<EditorItem @bind-Field="@context.BoolCloseAlarmEnable" Rows="1" />
<EditorItem @bind-Field="@context.BoolOpenAlarmText" />
<EditorItem @bind-Field="@context.BoolOpenRestrainExpressions" />
<EditorItem @bind-Field="@context.BoolOpenAlarmEnable" Rows="1" />
<EditorItem @bind-Field="@context.HHAlarmText" />
<EditorItem @bind-Field="@context.HHAlarmCode" />
<EditorItem @bind-Field="@context.HHRestrainExpressions" />
<EditorItem @bind-Field="@context.HHAlarmEnable" />
<EditorItem @bind-Field="@context.HAlarmText" />
<EditorItem @bind-Field="@context.HAlarmCode" />
<EditorItem @bind-Field="@context.HRestrainExpressions" />
<EditorItem @bind-Field="@context.HAlarmEnable" />
<EditorItem @bind-Field="@context.LAlarmText" />
<EditorItem @bind-Field="@context.LAlarmCode" />
<EditorItem @bind-Field="@context.LRestrainExpressions" />
<EditorItem @bind-Field="@context.LAlarmEnable" />
<EditorItem @bind-Field="@context.LLAlarmText" />
<EditorItem @bind-Field="@context.LLAlarmCode" />
<EditorItem @bind-Field="@context.LLRestrainExpressions" />
<EditorItem @bind-Field="@context.LLAlarmEnable" />
<EditorItem @bind-Field="@context.CustomAlarmText" />
<EditorItem @bind-Field="@context.CustomAlarmCode" />
<EditorItem @bind-Field="@context.CustomRestrainExpressions" />
<EditorItem @bind-Field="@context.CustomAlarmEnable" />
<EditorItem @bind-Field="@context.AlarmDelay" />
</FieldItems>
</EditorForm>
</TabItem>
</Tab>
<div class="form-footer">
<Button class="mt-2" ButtonType="ButtonType.Submit" Icon="fa-solid fa-floppy-disk" IsAsync Text=@RazorLocalizer["Save"] />
</div>
</ValidateForm>
<Button class="mt-2" ButtonType="ButtonType.Submit" Icon="fa-solid fa-floppy-disk" IsAsync Text=@RazorLocalizer["Save"] />
</div>
</ValidateForm>
}
else
{
<Tab>
<TabItem Text=@Localizer["VariableInformation"]>
<EditorForm class="p-2" AutoGenerateAllItem="false" RowType=RowType.Inline ItemsPerRow=2 LabelWidth=250 Model="Model">
<FieldItems>
<EditorItem @bind-Field="@context.Description">
<EditTemplate Context="value">
<div class="col-12">
<h6>@Localizer["BasicInformation"]</h6>
</div>
</EditTemplate>
</EditorItem>
<EditorItem @bind-Field="@context.Name" />
<EditorItem @bind-Field="@context.Description" />
<EditorItem @bind-Field="@context.Unit" />
<EditorItem @bind-Field="@context.ProtectType" />
<EditorItem @bind-Field="@context.Enable" />
<EditorItem @bind-Field="@context.RpcWriteEnable" />
<EditorItem @bind-Field="@context.SaveValue" />
<EditorItem @bind-Field="@context.Description">
<EditTemplate Context="value">
<div class="col-12">
<h6>@Localizer["Connection"]</h6>
</div>
</EditTemplate>
</EditorItem>
<EditorItem @bind-Field="@context.DeviceId">
<EditTemplate Context="value">
<div class="col-12 col-md-6">
<Select IsVirtualize @bind-Value="@value.DeviceId" Items="@CollectDevices" ShowSearch="true" OnSelectedItemChanged="OnDeviceSelectedItemChanged" />
</div>
</EditTemplate>
</EditorItem>
<EditorItem @bind-Field="@context.DataType" />
<EditorItem @bind-Field="@context.IntervalTime" />
<EditorItem @bind-Field="@context.OtherMethod">
<EditTemplate Context="value">
<div class="col-12 col-md-6">
<Select IsVirtualize @bind-Value="@value.OtherMethod" Items="@OtherMethods" ShowSearch="true" />
</div>
</EditTemplate>
</EditorItem>
<EditorItem @bind-Field="@context.RegisterAddress" Rows="1" />
<EditorItem @bind-Field="@context.ReadExpressions" Rows="1" />
<EditorItem @bind-Field="@context.WriteExpressions" Rows="1" />
<EditorItem @bind-Field="@context.Description">
<EditTemplate Context="value">
<div class="col-12">
<h6>@Localizer["Remark"]</h6>
</div>
</EditTemplate>
</EditorItem>
<EditorItem @bind-Field="@context.Remark1" />
<EditorItem @bind-Field="@context.Remark2" />
<EditorItem @bind-Field="@context.Remark3" />
<EditorItem @bind-Field="@context.Remark4" />
<EditorItem @bind-Field="@context.Remark5" />
</FieldItems>
</EditorForm>
</TabItem>
<TabItem Text=@Localizer["AlarmInformation"]>
<EditorForm class="p-2" AutoGenerateAllItem="false" RowType=RowType.Inline ItemsPerRow=2 LabelWidth=250 Model="Model">
<FieldItems>
<EditorItem @bind-Field="@context.BoolCloseAlarmText" />
<EditorItem @bind-Field="@context.BoolCloseRestrainExpressions" />
<EditorItem @bind-Field="@context.BoolCloseAlarmEnable" Rows="1" />
<EditorItem @bind-Field="@context.BoolOpenAlarmText" />
<EditorItem @bind-Field="@context.BoolOpenRestrainExpressions" />
<EditorItem @bind-Field="@context.BoolOpenAlarmEnable" Rows="1" />
<EditorItem @bind-Field="@context.HHAlarmText" />
<EditorItem @bind-Field="@context.HHAlarmCode" />
<EditorItem @bind-Field="@context.HHRestrainExpressions" />
<EditorItem @bind-Field="@context.HHAlarmEnable" />
<EditorItem @bind-Field="@context.HAlarmText" />
<EditorItem @bind-Field="@context.HAlarmCode" />
<EditorItem @bind-Field="@context.HRestrainExpressions" />
<EditorItem @bind-Field="@context.HAlarmEnable" />
<EditorItem @bind-Field="@context.LAlarmText" />
<EditorItem @bind-Field="@context.LAlarmCode" />
<EditorItem @bind-Field="@context.LRestrainExpressions" />
<EditorItem @bind-Field="@context.LAlarmEnable" />
<EditorItem @bind-Field="@context.LLAlarmText" />
<EditorItem @bind-Field="@context.LLAlarmCode" />
<EditorItem @bind-Field="@context.LLRestrainExpressions" />
<EditorItem @bind-Field="@context.LLAlarmEnable" />
<EditorItem @bind-Field="@context.CustomAlarmText" />
<EditorItem @bind-Field="@context.CustomAlarmCode" />
<EditorItem @bind-Field="@context.CustomRestrainExpressions" />
<EditorItem @bind-Field="@context.CustomAlarmEnable" />
<EditorItem @bind-Field="@context.AlarmDelay" />
</FieldItems>
</EditorForm>
</TabItem>
<TabItem Text=@Localizer["PluginInformation"]>
<div style="min-height:500px" class="px-4">
<div class="row g-2 mx-1 form-inline">
<div class="col-12 col-md-8">
<label required="true" style="width:20%">@Localizer["ChoiceBusinessDeviceId"]</label>
<Select SkipValidate IsVirtualize @bind-Value="@ChoiceBusinessDeviceId" Items="@BusinessDevices" ShowSearch="true" ShowLabel="false" />
<Tab>
<TabItem Text=@Localizer["VariableInformation"]>
<EditorForm class="p-2" AutoGenerateAllItem="false" RowType=RowType.Inline ItemsPerRow=2 LabelWidth=250 Model="Model">
<FieldItems>
<EditorItem @bind-Field="@context.Description">
<EditTemplate Context="value">
<div class="col-12">
<h6>@Localizer["BasicInformation"]</h6>
</div>
<div class="col-12 col-md-4">
<Button OnClick="() => RefreshBusinessPropertyClickAsync(ChoiceBusinessDeviceId)">@Localizer["RefreshBusinessProperty"]</Button>
</EditTemplate>
</EditorItem>
<EditorItem @bind-Field="@context.Name" />
<EditorItem @bind-Field="@context.Description" />
<EditorItem @bind-Field="@context.Unit" />
<EditorItem @bind-Field="@context.ProtectType" />
<EditorItem @bind-Field="@context.Enable" />
<EditorItem @bind-Field="@context.RpcWriteEnable" />
<EditorItem @bind-Field="@context.Value" />
<EditorItem @bind-Field="@context.SaveValue" />
<EditorItem @bind-Field="@context.Description">
<EditTemplate Context="value">
<div class="col-12">
<h6>@Localizer["Connection"]</h6>
</div>
</div>
</EditTemplate>
</EditorItem>
<EditorItem @bind-Field="@context.DeviceId">
<EditTemplate Context="value">
<div class="col-12 col-md-6">
<Select IsVirtualize @bind-Value="@value.DeviceId" Items="@CollectDevices" ShowSearch="true" OnSelectedItemChanged="OnDeviceSelectedItemChanged" />
</div>
</EditTemplate>
</EditorItem>
<EditorItem @bind-Field="@context.DataType" />
<EditorItem @bind-Field="@context.IntervalTime" />
<EditorItem @bind-Field="@context.OtherMethod">
<EditTemplate Context="value">
<div class="col-12 col-md-6">
<Select IsVirtualize @bind-Value="@value.OtherMethod" Items="@OtherMethods" ShowSearch="true" />
</div>
</EditTemplate>
</EditorItem>
<EditorItem @bind-Field="@context.RegisterAddress" Rows="1" />
<EditorItem @bind-Field="@context.ReadExpressions" Rows="1" />
<EditorItem @bind-Field="@context.WriteExpressions" Rows="1" />
<EditorItem @bind-Field="@context.Description">
<EditTemplate Context="value">
<div class="col-12">
<h6>@Localizer["Remark"]</h6>
</div>
</EditTemplate>
</EditorItem>
<EditorItem @bind-Field="@context.Remark1" />
<EditorItem @bind-Field="@context.Remark2" />
<EditorItem @bind-Field="@context.Remark3" />
<EditorItem @bind-Field="@context.Remark4" />
<EditorItem @bind-Field="@context.Remark5" />
</FieldItems>
</EditorForm>
</TabItem>
<TabItem Text=@Localizer["AlarmInformation"]>
<EditorForm class="p-2" AutoGenerateAllItem="false" RowType=RowType.Inline ItemsPerRow=2 LabelWidth=250 Model="Model">
<FieldItems>
<EditorItem @bind-Field="@context.BoolCloseAlarmText" />
<EditorItem @bind-Field="@context.BoolCloseRestrainExpressions" />
<EditorItem @bind-Field="@context.BoolCloseAlarmEnable" Rows="1" />
<EditorItem @bind-Field="@context.BoolOpenAlarmText" />
<EditorItem @bind-Field="@context.BoolOpenRestrainExpressions" />
<EditorItem @bind-Field="@context.BoolOpenAlarmEnable" Rows="1" />
<EditorItem @bind-Field="@context.HHAlarmText" />
<EditorItem @bind-Field="@context.HHAlarmCode" />
<EditorItem @bind-Field="@context.HHRestrainExpressions" />
<EditorItem @bind-Field="@context.HHAlarmEnable" />
<EditorItem @bind-Field="@context.HAlarmText" />
<EditorItem @bind-Field="@context.HAlarmCode" />
<EditorItem @bind-Field="@context.HRestrainExpressions" />
<EditorItem @bind-Field="@context.HAlarmEnable" />
<EditorItem @bind-Field="@context.LAlarmText" />
<EditorItem @bind-Field="@context.LAlarmCode" />
<EditorItem @bind-Field="@context.LRestrainExpressions" />
<EditorItem @bind-Field="@context.LAlarmEnable" />
<EditorItem @bind-Field="@context.LLAlarmText" />
<EditorItem @bind-Field="@context.LLAlarmCode" />
<EditorItem @bind-Field="@context.LLRestrainExpressions" />
<EditorItem @bind-Field="@context.LLAlarmEnable" />
<EditorItem @bind-Field="@context.CustomAlarmText" />
<EditorItem @bind-Field="@context.CustomAlarmCode" />
<EditorItem @bind-Field="@context.CustomRestrainExpressions" />
<EditorItem @bind-Field="@context.CustomAlarmEnable" />
<EditorItem @bind-Field="@context.AlarmDelay" />
</FieldItems>
</EditorForm>
</TabItem>
<TabItem Text=@Localizer["PluginInformation"]>
<div style="min-height:500px" class="px-4">
<div class="row g-2 mx-1 form-inline">
<div class="col-12 col-md-8">
@* <label required="true" style="width:20%">@Localizer["ChoiceBusinessDeviceId"]</label> *@
<Select SkipValidate IsVirtualize @bind-Value="@ChoiceBusinessDeviceId" Items="@BusinessDevices" ShowSearch="true" ShowLabel="true" />
</div>
<div class="col-12 col-md-4">
<Button OnClick="async() =>{
await RefreshBusinessPropertyClickAsync(ChoiceBusinessDeviceId);
foreach (var item in Model.VariablePropertyModels)
{
if(item.Value!=null)
{
item.Value.ValidateForm=null;
}
}
}">@Localizer["RefreshBusinessProperty"]</Button>
</div>
</div>
@if (Model.VariablePropertyModels != null)
{
@foreach (var item in Model.VariablePropertyModels)
@foreach (var a in Model.VariablePropertyModels)
{
var item = a;
var custom = VariablePropertyRenderFragments.TryGetValue(item.Key, out var renderFragment);
if (!custom)
@@ -252,31 +269,35 @@ else
if (has)
{
<Card IsShadow=true class="m-2 flex-fill" Color="Color.Primary">
<HeaderTemplate>
<Card IsShadow=true class="m-2 flex-fill" Color="Color.Primary">
<HeaderTemplate>
@{
BusinessDeviceDict.TryGetValue(item.Key, out var items);
}
<div class="flex-fill">
<div class="flex-fill">
@($"{items.Name} - {PluginServiceUtil.GetFileNameAndTypeName(items?.PluginName).TypeName}")
</div>
</div>
<Button OnClick=@((a)=>
{
Model.VariablePropertyModels.Remove(item.Key);
}) class="mx-2" Color="Color.None" style="color: var(--bs-card-titlecolor);" Icon=@("fas fa-delete-left") />
<Button OnClick=@((a)=>
{
Model.VariablePropertyModels.Remove(item.Key);
}) class="mx-2" Color="Color.None" style="color: var(--bs-card-titlecolor);" Icon=@("fas fa-delete-left") />
</HeaderTemplate>
</HeaderTemplate>
<BodyTemplate>
<ValidateForm Model="item.Value.Value" Id=@((item.Key+Model.Id).ToString()) @ref=item.Value.ValidateForm>
<BodyTemplate>
<EditorFormObject class="p-2" Items=items AutoGenerateAllItem="false" RowType=RowType.Inline ItemsPerRow=2 ShowLabelTooltip=true LabelWidth=250 Model="item.Value.Value" @key=item.Value.Value.GetType().TypeHandle.Value>
<ValidateForm Model="item.Value.Value" @ref=item.Value.ValidateForm
@key=@($"VariableEditValidateForm{item.Key}{Model.Id}{item.Value.Value.GetType().TypeHandle.Value}")
Id=@($"VariableEditValidateForm{item.Key}{Model.Id}{item.Value.Value.GetType().TypeHandle.Value}")>
</EditorFormObject>
</ValidateForm>
</BodyTemplate>
</Card>
<EditorFormObject class="p-2" Items=items AutoGenerateAllItem="false" RowType=RowType.Inline ItemsPerRow=2 ShowLabelTooltip=true LabelWidth=250 Model="item.Value.Value" @key=@($"VariableEditEditorFormObject{item.Key}{Model.Id}{item.Value.Value.GetType().TypeHandle.Value}")>
</EditorFormObject>
</ValidateForm>
</BodyTemplate>
</Card>
}
}
else
@@ -285,10 +306,10 @@ else
}
}
}
</div>
</div>
</TabItem>
</Tab>
</TabItem>
</Tab>
}

View File

@@ -78,7 +78,9 @@
IsAsync OnConfirm=@(InsertTestDataAsync)>
<BodyTemplate>
<BootstrapInput @bind-Value=TestCount ShowLabel="true" ShowLabelTooltip="true" />
<BootstrapInput @bind-Value=TestVariableCount ShowLabel="true" ShowLabelTooltip="true" />
<BootstrapInput @bind-Value=TestDeviceCount ShowLabel="true" ShowLabelTooltip="true" />
<BootstrapInput @bind-Value=SlaveUrl ShowLabel="true" ShowLabelTooltip="true" />
</BodyTemplate>
</PopConfirmButton>

View File

@@ -38,7 +38,10 @@ public partial class VariablePage : IDisposable
[NotNull]
private IDispatchService<bool>? DispatchService { get; set; }
private int TestCount { get; set; }
private int TestVariableCount { get; set; }
private int TestDeviceCount { get; set; }
private string SlaveUrl { get; set; }
@@ -315,7 +318,7 @@ public partial class VariablePage : IDisposable
{
await Task.Run(async () =>
{
await VariableService.InsertTestDataAsync(TestCount);
await VariableService.InsertTestDataAsync(TestVariableCount, TestDeviceCount, SlaveUrl);
await InvokeAsync(async () =>
{
await ToastService.Default();

View File

@@ -43,7 +43,9 @@
@if (_driverBaseItem != null && _driverBaseItem?.CurrentDevice != null && _driverBaseItem.CurrentDevice.Channel.LogEnable == true)
{
<LogConsole Logger=@_driverBaseItem.LogMessage LogPath=@_driverBaseItem.LogPath HeaderText="@Localizer["LogConsole"]" HeightString="var(--logconsle-height)"></LogConsole>
<LogConsole LogLevel=@(_driverBaseItem.LogMessage.LogLevel) LogLevelChanged="(a)=>{
_driverBaseItem.LogMessage.LogLevel=a;
}" LogPath=@_driverBaseItem.LogPath HeaderText="@Localizer["LogConsole"]" HeightString="var(--logconsle-height)"></LogConsole>
}

View File

@@ -4,9 +4,9 @@
<ItemGroup>
<ProjectReference Include="..\ThingsGateway.Gateway.Application\ThingsGateway.Gateway.Application.csproj" />
<PackageReference Include="BootstrapBlazor.WinBox" Version="9.0.5" />
<PackageReference Include="BootstrapBlazor.CodeEditor" Version="9.0.0" />
<PackageReference Include="ThingsGateway.Admin.Razor" Version="$(AdminVersion)" />
<PackageReference Include="BootstrapBlazor.WinBox" Version="9.0.7" />
<PackageReference Include="BootstrapBlazor.CodeEditor" Version="9.0.1" />
<ProjectReference Include="..\..\..\Admin\src\ThingsGateway.Admin.Razor\ThingsGateway.Admin.Razor.csproj" />
<ProjectReference Include="..\..\Foundation\ThingsGateway.Foundation.Razor\ThingsGateway.Foundation.Razor.csproj" />
</ItemGroup>

View File

@@ -1,9 +1,10 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# 17
# Visual Studio Version 17
VisualStudioVersion = 17.9.34622.214
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "other", "other", "{0B748352-5D27-4F14-8A20-364034C72867}"
ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig
Directory.Build.props = Directory.Build.props
FoundationVersion.props = FoundationVersion.props
Version.props = Version.props
@@ -17,8 +18,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.CSScript", "F
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Foundation", "Foundation\ThingsGateway.Foundation\ThingsGateway.Foundation.csproj", "{9D49F8E2-A82C-4E36-8F54-4F45BF3E47C0}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Foundation.Razor", "Foundation\ThingsGateway.Foundation.Razor\ThingsGateway.Foundation.Razor.csproj", "{70665B3C-5E43-4A2B-B032-30986A16DE67}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Foundation.SourceGenerator", "Foundation\ThingsGateway.Foundation.SourceGenerator\ThingsGateway.Foundation.SourceGenerator.csproj", "{E325A64C-2B72-4DD0-8A6C-229B29FB95EB}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Foundation.Variable", "Foundation\ThingsGateway.Foundation.Variable\ThingsGateway.Foundation.Variable.csproj", "{8EE037EC-ED21-42A0-BE1F-219E9886E43A}"
@@ -27,6 +26,31 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Gateway.Appli
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Gateway.Razor", "Gateway\ThingsGateway.Gateway.Razor\ThingsGateway.Gateway.Razor.csproj", "{6B92AF2D-0997-4887-B1C5-31861F298238}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ThingsGateway.Foundation.Razor", "Foundation\ThingsGateway.Foundation.Razor\ThingsGateway.Foundation.Razor.csproj", "{569F229F-FB10-4789-B00B-D9D12383177A}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Admin", "Admin", "{72C65578-92A5-4E99-9779-27835B12B32F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ThingsGateway.Admin.Application", "..\Admin\src\ThingsGateway.Admin.Application\ThingsGateway.Admin.Application.csproj", "{C4C01993-DB12-49D9-BDDB-C312042A6C29}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ThingsGateway.Admin.Razor", "..\Admin\src\ThingsGateway.Admin.Razor\ThingsGateway.Admin.Razor.csproj", "{BC3AE9E7-7B56-4894-9514-52A493A0D724}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ThingsGateway.Core", "..\Admin\src\ThingsGateway.Core\ThingsGateway.Core.csproj", "{D473E69E-FDBE-4A8E-A835-7F380C8E6D86}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ThingsGateway.Furion", "..\Admin\src\ThingsGateway.Furion\ThingsGateway.Furion.csproj", "{3759E861-BA1E-498A-85CF-48A0BE6B00BB}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ThingsGateway.NewLife.X", "..\Admin\src\ThingsGateway.NewLife.X\ThingsGateway.NewLife.X.csproj", "{42928B73-237A-4483-935E-E7FAAC52F288}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ThingsGateway.Razor", "..\Admin\src\ThingsGateway.Razor\ThingsGateway.Razor.csproj", "{015DD6B7-325F-45B5-B69E-CBD62DC6235A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ThingsGateway.AdminServer", "..\Admin\src\ThingsGateway.AdminServer\ThingsGateway.AdminServer.csproj", "{BE13F278-C153-4CB8-A4EF-59BEFAE54CF8}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "other", "other", "{568A28E1-6CAE-461C-8578-C28BEAC81945}"
ProjectSection(SolutionItems) = preProject
..\Admin\src\Admin.props = ..\Admin\src\Admin.props
..\Admin\src\PackNuget.props = ..\Admin\src\PackNuget.props
..\Admin\src\Version.props = ..\Admin\src\Version.props
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -41,10 +65,6 @@ Global
{9D49F8E2-A82C-4E36-8F54-4F45BF3E47C0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9D49F8E2-A82C-4E36-8F54-4F45BF3E47C0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9D49F8E2-A82C-4E36-8F54-4F45BF3E47C0}.Release|Any CPU.Build.0 = Release|Any CPU
{70665B3C-5E43-4A2B-B032-30986A16DE67}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{70665B3C-5E43-4A2B-B032-30986A16DE67}.Debug|Any CPU.Build.0 = Debug|Any CPU
{70665B3C-5E43-4A2B-B032-30986A16DE67}.Release|Any CPU.ActiveCfg = Release|Any CPU
{70665B3C-5E43-4A2B-B032-30986A16DE67}.Release|Any CPU.Build.0 = Release|Any CPU
{E325A64C-2B72-4DD0-8A6C-229B29FB95EB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E325A64C-2B72-4DD0-8A6C-229B29FB95EB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E325A64C-2B72-4DD0-8A6C-229B29FB95EB}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -61,6 +81,38 @@ Global
{6B92AF2D-0997-4887-B1C5-31861F298238}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6B92AF2D-0997-4887-B1C5-31861F298238}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6B92AF2D-0997-4887-B1C5-31861F298238}.Release|Any CPU.Build.0 = Release|Any CPU
{569F229F-FB10-4789-B00B-D9D12383177A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{569F229F-FB10-4789-B00B-D9D12383177A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{569F229F-FB10-4789-B00B-D9D12383177A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{569F229F-FB10-4789-B00B-D9D12383177A}.Release|Any CPU.Build.0 = Release|Any CPU
{C4C01993-DB12-49D9-BDDB-C312042A6C29}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C4C01993-DB12-49D9-BDDB-C312042A6C29}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C4C01993-DB12-49D9-BDDB-C312042A6C29}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C4C01993-DB12-49D9-BDDB-C312042A6C29}.Release|Any CPU.Build.0 = Release|Any CPU
{BC3AE9E7-7B56-4894-9514-52A493A0D724}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BC3AE9E7-7B56-4894-9514-52A493A0D724}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BC3AE9E7-7B56-4894-9514-52A493A0D724}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BC3AE9E7-7B56-4894-9514-52A493A0D724}.Release|Any CPU.Build.0 = Release|Any CPU
{D473E69E-FDBE-4A8E-A835-7F380C8E6D86}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D473E69E-FDBE-4A8E-A835-7F380C8E6D86}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D473E69E-FDBE-4A8E-A835-7F380C8E6D86}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D473E69E-FDBE-4A8E-A835-7F380C8E6D86}.Release|Any CPU.Build.0 = Release|Any CPU
{3759E861-BA1E-498A-85CF-48A0BE6B00BB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3759E861-BA1E-498A-85CF-48A0BE6B00BB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3759E861-BA1E-498A-85CF-48A0BE6B00BB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3759E861-BA1E-498A-85CF-48A0BE6B00BB}.Release|Any CPU.Build.0 = Release|Any CPU
{42928B73-237A-4483-935E-E7FAAC52F288}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{42928B73-237A-4483-935E-E7FAAC52F288}.Debug|Any CPU.Build.0 = Debug|Any CPU
{42928B73-237A-4483-935E-E7FAAC52F288}.Release|Any CPU.ActiveCfg = Release|Any CPU
{42928B73-237A-4483-935E-E7FAAC52F288}.Release|Any CPU.Build.0 = Release|Any CPU
{015DD6B7-325F-45B5-B69E-CBD62DC6235A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{015DD6B7-325F-45B5-B69E-CBD62DC6235A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{015DD6B7-325F-45B5-B69E-CBD62DC6235A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{015DD6B7-325F-45B5-B69E-CBD62DC6235A}.Release|Any CPU.Build.0 = Release|Any CPU
{BE13F278-C153-4CB8-A4EF-59BEFAE54CF8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BE13F278-C153-4CB8-A4EF-59BEFAE54CF8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BE13F278-C153-4CB8-A4EF-59BEFAE54CF8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BE13F278-C153-4CB8-A4EF-59BEFAE54CF8}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -68,11 +120,19 @@ Global
GlobalSection(NestedProjects) = preSolution
{506232CE-0FB6-4ACB-96DE-C8C8D075A642} = {2AC600BB-4325-4E0A-93A7-B1F53C8E2CA7}
{9D49F8E2-A82C-4E36-8F54-4F45BF3E47C0} = {2AC600BB-4325-4E0A-93A7-B1F53C8E2CA7}
{70665B3C-5E43-4A2B-B032-30986A16DE67} = {2AC600BB-4325-4E0A-93A7-B1F53C8E2CA7}
{E325A64C-2B72-4DD0-8A6C-229B29FB95EB} = {2AC600BB-4325-4E0A-93A7-B1F53C8E2CA7}
{8EE037EC-ED21-42A0-BE1F-219E9886E43A} = {2AC600BB-4325-4E0A-93A7-B1F53C8E2CA7}
{A1163B55-31D8-4F58-872F-1DD5E0F14490} = {5948EA23-4B42-4C22-A266-2E0AE5FA575F}
{6B92AF2D-0997-4887-B1C5-31861F298238} = {5948EA23-4B42-4C22-A266-2E0AE5FA575F}
{569F229F-FB10-4789-B00B-D9D12383177A} = {2AC600BB-4325-4E0A-93A7-B1F53C8E2CA7}
{C4C01993-DB12-49D9-BDDB-C312042A6C29} = {72C65578-92A5-4E99-9779-27835B12B32F}
{BC3AE9E7-7B56-4894-9514-52A493A0D724} = {72C65578-92A5-4E99-9779-27835B12B32F}
{D473E69E-FDBE-4A8E-A835-7F380C8E6D86} = {72C65578-92A5-4E99-9779-27835B12B32F}
{3759E861-BA1E-498A-85CF-48A0BE6B00BB} = {72C65578-92A5-4E99-9779-27835B12B32F}
{42928B73-237A-4483-935E-E7FAAC52F288} = {72C65578-92A5-4E99-9779-27835B12B32F}
{015DD6B7-325F-45B5-B69E-CBD62DC6235A} = {72C65578-92A5-4E99-9779-27835B12B32F}
{BE13F278-C153-4CB8-A4EF-59BEFAE54CF8} = {72C65578-92A5-4E99-9779-27835B12B32F}
{568A28E1-6CAE-461C-8578-C28BEAC81945} = {72C65578-92A5-4E99-9779-27835B12B32F}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {199B1B96-4F56-4828-9531-813BA02DB282}

View File

@@ -1,126 +1,125 @@
<Project Sdk="Microsoft.NET.Sdk.Razor">
<Import Project="$(SolutionDir)Version.props" />
<Import Project="..\ThingsGateway.Server\targets\Admin.targets" />
<Import Project="..\ThingsGateway.Server\targets\Plugin.targets" />
<Import Project="..\ThingsGateway.Server\targets\CustomPlugin.targets" />
<Import Project="..\ThingsGateway.Server\targets\Custom.targets" />
<PropertyGroup>
<CustomTargetFramework>$(TargetFramework)</CustomTargetFramework>
<OutputType>WinExe</OutputType>
<OpenApiGenerateDocuments>false</OpenApiGenerateDocuments>
<SatelliteResourceLanguages>zh-Hans;en-US</SatelliteResourceLanguages>
<PublishReadyToRunComposite>true</PublishReadyToRunComposite>
<ApplicationIcon>favicon.ico</ApplicationIcon>
<!--<PublishAot>true</PublishAot>-->
<Import Project="$(SolutionDir)Version.props" />
<!--<Import Project="..\ThingsGateway.Server\targets\Admin.targets" />-->
<Import Project="..\ThingsGateway.Server\targets\PluginContext.targets" />
<!--<Import Project="..\ThingsGateway.Server\targets\Plugin.targets" />-->
<Import Project="..\ThingsGateway.Server\targets\CustomPlugin.targets" />
<Import Project="..\ThingsGateway.Server\targets\Custom.targets" />
<PropertyGroup>
<CustomTargetFramework>$(TargetFramework)</CustomTargetFramework>
<OutputType>WinExe</OutputType>
<OpenApiGenerateDocuments>false</OpenApiGenerateDocuments>
<SatelliteResourceLanguages>zh-Hans;en-US</SatelliteResourceLanguages>
<PublishReadyToRunComposite>true</PublishReadyToRunComposite>
<ApplicationIcon>favicon.ico</ApplicationIcon>
<!--<PublishAot>true</PublishAot>-->
<!--动态适用GC-->
<GarbageCollectionAdaptationMode>1</GarbageCollectionAdaptationMode>
<!--使用工作站GC-->
<!--<ServerGarbageCollection>true</ServerGarbageCollection>-->
<!--<PlatformTarget>x86</PlatformTarget>-->
</PropertyGroup>
<!--动态适用GC-->
<GarbageCollectionAdaptationMode>1</GarbageCollectionAdaptationMode>
<!--使用工作站GC-->
<!--<ServerGarbageCollection>true</ServerGarbageCollection>-->
<!--<PlatformTarget>x86</PlatformTarget>-->
</PropertyGroup>
<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>
<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net9.0' ">
<PackageReference Include="Microsoft.Data.Sqlite" Version="9.0.0" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' != 'net6.0' ">
<PackageReference Include="Microsoft.Data.Sqlite" Version="9.0.0" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)'=='net8.0'">
<PackageReference Include="Microsoft.Data.Sqlite" Version="8.0.10" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)'=='net6.0'">
<PackageReference Include="Microsoft.Data.Sqlite" Version="7.0.20" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)'=='net6.0'">
<PackageReference Include="Microsoft.Data.Sqlite" Version="7.0.20" />
</ItemGroup>
<!--安装服务守护-->
<ItemGroup Condition=" '$(TargetFramework)' != 'net9.0' ">
<PackageReference Include="Microsoft.Extensions.Hosting.Systemd" Version="8.0.1" />
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="8.0.1" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net9.0' ">
<PackageReference Include="Microsoft.Extensions.Hosting.Systemd" Version="9.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="9.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Gateway\ThingsGateway.Gateway.Application\ThingsGateway.Gateway.Application.csproj" />
<ProjectReference Include="..\Gateway\ThingsGateway.Gateway.Razor\ThingsGateway.Gateway.Razor.csproj" />
</ItemGroup>
<!--安装服务守护-->
<ItemGroup Condition=" '$(TargetFramework)' != 'net9.0' ">
<PackageReference Include="Microsoft.Extensions.Hosting.Systemd" Version="8.0.1" />
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="8.0.1" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net9.0' ">
<PackageReference Include="Microsoft.Extensions.Hosting.Systemd" Version="9.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="9.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Gateway\ThingsGateway.Gateway.Application\ThingsGateway.Gateway.Application.csproj" />
<ProjectReference Include="..\Gateway\ThingsGateway.Gateway.Razor\ThingsGateway.Gateway.Razor.csproj" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net6.0'">
<PackageReference Include="Microsoft.AspNetCore.Components.WebView" Version="6.0.33" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net6.0'">
<PackageReference Include="Microsoft.AspNetCore.Components.WebView" Version="6.0.33" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net8.0'">
<PackageReference Include="Microsoft.AspNetCore.Components.WebView" Version="8.0.10" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net9.0' ">
<PackageReference Include="Microsoft.AspNetCore.Components.WebView" Version="9.0.0" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Photino.NET" Version="3.2.3" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net8.0'">
<PackageReference Include="Microsoft.AspNetCore.Components.WebView" Version="8.0.10" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net9.0' ">
<PackageReference Include="Microsoft.AspNetCore.Components.WebView" Version="9.0.0" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Photino.NET" Version="3.2.3" />
</ItemGroup>
<ItemGroup>
<Content Include="..\ThingsGateway.Server\Index\GatewayIndex.razor" Link="Index\GatewayIndex.razor" />
<Compile Include="..\ThingsGateway.Server\Index\GatewayIndex.razor.cs" Link="Index\GatewayIndex.razor.cs" />
<Content Include="..\ThingsGateway.Server\Layout\MainLayout.razor" Link="Layout\MainLayout.razor" />
<Compile Include="..\ThingsGateway.Server\Layout\MainLayout.razor.cs" Link="Layout\MainLayout.razor.cs" />
<Content Include="..\ThingsGateway.Server\Layout\MainLayout.razor.css" Link="Layout\MainLayout.razor.css" />
<Content Include="..\ThingsGateway.Server\Layout\AccessDenied.razor" Link="Layout\AccessDenied.razor" />
<Compile Include="..\ThingsGateway.Server\Layout\AccessDenied.razor.cs" Link="Layout\AccessDenied.razor.cs" />
<Content Include="..\ThingsGateway.Server\Layout\Login.razor" Link="Layout\Login.razor" />
<Compile Include="..\ThingsGateway.Server\Layout\Login.razor.cs" Link="Layout\Login.razor.cs" />
<Content Include="..\ThingsGateway.Server\Layout\Login.razor.css" Link="Layout\Login.razor.css" />
<Content Include="..\ThingsGateway.Server\Layout\NotFound404.razor" Link="Layout\NotFound404.razor" />
<Compile Include="..\ThingsGateway.Server\Layout\NotFound404.razor.cs" Link="Layout\NotFound404.razor.cs" />
<Content Include="..\ThingsGateway.Server\Index\GatewayIndexComponent.razor" Link="Index\GatewayIndexComponent.razor" />
<Compile Include="..\ThingsGateway.Server\Index\GatewayIndexComponent.razor.cs" Link="Index\GatewayIndexComponent.razor.cs" />
<Compile Include="..\ThingsGateway.Server\Program\SingleFilePublish.cs" Link="Program\SingleFilePublish.cs" />
<ItemGroup>
<Content Include="..\ThingsGateway.Server\Index\GatewayIndex.razor" Link="Index\GatewayIndex.razor" />
<Compile Include="..\ThingsGateway.Server\Index\GatewayIndex.razor.cs" Link="Index\GatewayIndex.razor.cs" />
<Content Include="..\ThingsGateway.Server\Layout\MainLayout.razor" Link="Layout\MainLayout.razor" />
<Compile Include="..\ThingsGateway.Server\Layout\MainLayout.razor.cs" Link="Layout\MainLayout.razor.cs" />
<Content Include="..\ThingsGateway.Server\Layout\MainLayout.razor.css" Link="Layout\MainLayout.razor.css" />
<Content Include="..\ThingsGateway.Server\Layout\AccessDenied.razor" Link="Layout\AccessDenied.razor" />
<Compile Include="..\ThingsGateway.Server\Layout\AccessDenied.razor.cs" Link="Layout\AccessDenied.razor.cs" />
<Content Include="..\ThingsGateway.Server\Layout\Login.razor" Link="Layout\Login.razor" />
<Compile Include="..\ThingsGateway.Server\Layout\Login.razor.cs" Link="Layout\Login.razor.cs" />
<Content Include="..\ThingsGateway.Server\Layout\Login.razor.css" Link="Layout\Login.razor.css" />
<Content Include="..\ThingsGateway.Server\Layout\NotFound404.razor" Link="Layout\NotFound404.razor" />
<Compile Include="..\ThingsGateway.Server\Layout\NotFound404.razor.cs" Link="Layout\NotFound404.razor.cs" />
<Content Include="..\ThingsGateway.Server\Index\GatewayIndexComponent.razor" Link="Index\GatewayIndexComponent.razor" />
<Compile Include="..\ThingsGateway.Server\Index\GatewayIndexComponent.razor.cs" Link="Index\GatewayIndexComponent.razor.cs" />
<Compile Include="..\ThingsGateway.Server\Program\SingleFilePublish.cs" Link="Program\SingleFilePublish.cs" />
<Content Include="..\ThingsGateway.Server\wwwroot\favicon.ico" Link="wwwroot\favicon.ico" />
<Content Include="..\ThingsGateway.Server\wwwroot\favicon.png" Link="wwwroot\favicon.png" />
<Content Include="..\ThingsGateway.Server\appsettings.Development.json" Link="appsettings.Development.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="..\ThingsGateway.Server\appsettings.json" Link="appsettings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="..\ThingsGateway.Server\wwwroot\favicon.ico" Link="wwwroot\favicon.ico" />
<Content Include="..\ThingsGateway.Server\wwwroot\favicon.png" Link="wwwroot\favicon.png" />
<Content Include="..\ThingsGateway.Server\appsettings.Development.json" Link="appsettings.Development.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="..\ThingsGateway.Server\appsettings.json" Link="appsettings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="..\ThingsGateway.Server\Configuration\*" LinkBase="Configuration">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="..\ThingsGateway.Server\Configuration\*" LinkBase="Configuration">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="favicon.ico">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="favicon.ico">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<EmbeddedResource Include="..\ThingsGateway.Server\Locales\en-US.json" Link="Locales\en-US.json" />
<EmbeddedResource Include="..\ThingsGateway.Server\Locales\zh-CN.json" Link="Locales\zh-CN.json" />
<EmbeddedResource Include="..\ThingsGateway.Server\Locales\zh-TW.json">
<Link>Locales\zh-TW.json</Link>
</EmbeddedResource>
<EmbeddedResource Include="Locales\*.json">
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
</EmbeddedResource>
<EmbeddedResource Include="..\ThingsGateway.Server\Locales\en-US.json" Link="Locales\en-US.json" />
<EmbeddedResource Include="..\ThingsGateway.Server\Locales\zh-CN.json" Link="Locales\zh-CN.json" />
<EmbeddedResource Include="..\ThingsGateway.Server\Locales\zh-TW.json">
<Link>Locales\zh-TW.json</Link>
</EmbeddedResource>
<EmbeddedResource Include="Locales\*.json">
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
</EmbeddedResource>
<Content Update="wwwroot\**">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
<Content Update="wwwroot\**">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
</Project>

View File

@@ -52,6 +52,7 @@ public class SingleFilePublish : ISingleFilePublish
"ThingsGateway.CSScript",
"ThingsGateway.Foundation" ,
"ThingsGateway.Foundation.Razor",
"ThingsGateway.Gateway.Application",
"ThingsGateway.Gateway.Razor" ,
"SqlSugar.TDengineCore",

View File

@@ -1,112 +1,115 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<Import Project="$(SolutionDir)Version.props" />
<Import Project="targets\Admin.targets" />
<Import Project="targets\Plugin.targets" />
<Import Project="targets\CustomPlugin.targets" />
<Import Project="targets\Custom.targets" />
<!--<Import Project="targets\OtherPlugin.targets" />-->
<!--<Import Project="targets\OtherPlugin1.targets" />-->
<PropertyGroup>
<CustomTargetFramework>$(TargetFramework)</CustomTargetFramework>
<OpenApiGenerateDocuments>false</OpenApiGenerateDocuments>
<SatelliteResourceLanguages>zh-Hans;en-US</SatelliteResourceLanguages>
<PublishReadyToRunComposite>true</PublishReadyToRunComposite>
<ApplicationIcon>wwwroot\favicon.ico</ApplicationIcon>
<Import Project="$(SolutionDir)Version.props" />
<Import Project="targets\PluginContext.targets" />
<Import Project="targets\Publish1.targets" />
<!--<Import Project="targets\Plugin.targets" />-->
<Import Project="targets\CustomPlugin.targets" />
<Import Project="targets\Custom.targets" />
<!--<Import Project="targets\Admin.targets" />-->
<!--<Import Project="targets\OtherPlugin.targets" />-->
<!--<Import Project="targets\OtherPlugin1.targets" />-->
<PropertyGroup>
<CustomTargetFramework>$(TargetFramework)</CustomTargetFramework>
<OpenApiGenerateDocuments>false</OpenApiGenerateDocuments>
<SatelliteResourceLanguages>zh-Hans;en-US</SatelliteResourceLanguages>
<PublishReadyToRunComposite>true</PublishReadyToRunComposite>
<ApplicationIcon>wwwroot\favicon.ico</ApplicationIcon>
<!--动态适用GC-->
<GarbageCollectionAdaptationMode>1</GarbageCollectionAdaptationMode>
<!--使用自托管线程池-->
<!--<UseWindowsThreadPool>false</UseWindowsThreadPool> -->
<!--动态适用GC-->
<GarbageCollectionAdaptationMode>1</GarbageCollectionAdaptationMode>
<!--使用自托管线程池-->
<!--<UseWindowsThreadPool>false</UseWindowsThreadPool> -->
<!--使用工作站GC-->
<!--<ServerGarbageCollection>true</ServerGarbageCollection>-->
<!--使用工作站GC-->
<!--<ServerGarbageCollection>true</ServerGarbageCollection>-->
<!--<PlatformTarget>x86</PlatformTarget>-->
</PropertyGroup>
<!--<PlatformTarget>x86</PlatformTarget>-->
</PropertyGroup>
<ItemGroup>
<Content Include="DockerPush.ps1">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="DockerPush_arm64.ps1">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Locales\*.json">
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<Content Include="DockerPush.ps1">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="DockerPush_arm64.ps1">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<Content Update="wwwroot\**">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Locales\*.json">
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
</EmbeddedResource>
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net9.0' ">
<PackageReference Include="Microsoft.Data.Sqlite" Version="9.0.0" />
</ItemGroup>
<ItemGroup>
<Content Update="wwwroot\**">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)'=='net8.0'">
<PackageReference Include="Microsoft.Data.Sqlite" Version="8.0.10" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)'=='net6.0'">
<PackageReference Include="Microsoft.Data.Sqlite" Version="7.0.20" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' != 'net6.0' ">
<PackageReference Include="Microsoft.Data.Sqlite" Version="9.0.0" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)'=='net6.0'">
<PackageReference Include="Microsoft.Data.Sqlite" Version="7.0.20" />
</ItemGroup>
<!--安装服务守护-->
<ItemGroup Condition=" '$(TargetFramework)' != 'net9.0' ">
<PackageReference Include="Microsoft.Extensions.Hosting.Systemd" Version="8.0.1" />
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="8.0.1" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net9.0' ">
<PackageReference Include="Microsoft.Extensions.Hosting.Systemd" Version="9.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="9.0.0" />
</ItemGroup>
<ItemGroup>
<Content Include="favicon.ico">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<None Update="WindowsService">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Dockerfile">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Dockerfile_arm64">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<!--安装服务守护-->
<ItemGroup Condition=" '$(TargetFramework)' != 'net9.0' ">
<PackageReference Include="Microsoft.Extensions.Hosting.Systemd" Version="8.0.1" />
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="8.0.1" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net9.0' ">
<PackageReference Include="Microsoft.Extensions.Hosting.Systemd" Version="9.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="9.0.0" />
</ItemGroup>
<None Update="pm2-windows.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="pm2-linux.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="thingsgateway.service">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="WindowsServiceCreate.bat">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="WindowsServiceDelete.bat">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<Content Include="favicon.ico">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<None Update="WindowsService">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Dockerfile">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Dockerfile_arm64">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="pm2-windows.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="pm2-linux.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="thingsgateway.service">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="WindowsServiceCreate.bat">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="WindowsServiceDelete.bat">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Gateway\ThingsGateway.Gateway.Application\ThingsGateway.Gateway.Application.csproj" />
<ProjectReference Include="..\Gateway\ThingsGateway.Gateway.Razor\ThingsGateway.Gateway.Razor.csproj" />
</ItemGroup>
<ItemGroup Condition=" '$(SolutionName)' != 'ThingsGatewayRelease' ">
<ProjectReference Include="..\Gateway\ThingsGateway.Gateway.Application\ThingsGateway.Gateway.Application.csproj" />
<ProjectReference Include="..\Gateway\ThingsGateway.Gateway.Razor\ThingsGateway.Gateway.Razor.csproj" />
</ItemGroup>
<Import Project="targets\Gateway.targets" Condition=" '$(SolutionName)' == 'ThingsGatewayRelease' " />
<Import Project="targets\Admin.targets" Condition=" '$(SolutionName)' == 'ThingsGatewayRelease' " />
</Project>

View File

@@ -1,71 +1,71 @@
<Project>
<ItemGroup>
<PackageReference Include="ThingsGateway.Management" Version="$(ProPluginVersion)" GeneratePathProperty="true"/>
<PackageReference Include="ThingsGateway.RulesEngine" Version="$(ProPluginVersion)" GeneratePathProperty="true"/>
</ItemGroup>
<ItemGroup>
<PackageReference Include="ThingsGateway.Management" Version="$(ProPluginVersion)" GeneratePathProperty="true"/>
<PackageReference Include="ThingsGateway.RulesEngine" Version="$(ProPluginVersion)" GeneratePathProperty="true"/>
</ItemGroup>
<Target Name="_ResolveCopyManagementLocalNuGetPkgXmls" AfterTargets="ResolveReferences">
<ItemGroup>
<ReferenceCopyLocalPaths Include="@(ReferenceCopyLocalPaths->'%(RootDir)%(Directory)%(Filename).xml')" Condition="'%(ReferenceCopyLocalPaths.NuGetPackageId)'=='ThingsGateway.Management' and Exists('%(RootDir)%(Directory)%(Filename).xml')" />
</ItemGroup>
</Target>
<Target Name="_ResolveCopyRulesEngineLocalNuGetPkgXmls" AfterTargets="ResolveReferences">
<ItemGroup>
<ReferenceCopyLocalPaths Include="@(ReferenceCopyLocalPaths->'%(RootDir)%(Directory)%(Filename).xml')" Condition="'%(ReferenceCopyLocalPaths.NuGetPackageId)'=='ThingsGateway.RulesEngine' and Exists('%(RootDir)%(Directory)%(Filename).xml')" />
</ItemGroup>
</Target>
<Target Name="_ResolveCopyManagementLocalNuGetPkgXmls" AfterTargets="ResolveReferences">
<ItemGroup>
<ReferenceCopyLocalPaths Include="@(ReferenceCopyLocalPaths->'%(RootDir)%(Directory)%(Filename).xml')" Condition="'%(ReferenceCopyLocalPaths.NuGetPackageId)'=='ThingsGateway.Management' and Exists('%(RootDir)%(Directory)%(Filename).xml')" />
</ItemGroup>
</Target>
<Target Name="_ResolveCopyRulesEngineLocalNuGetPkgXmls" AfterTargets="ResolveReferences">
<ItemGroup>
<ReferenceCopyLocalPaths Include="@(ReferenceCopyLocalPaths->'%(RootDir)%(Directory)%(Filename).xml')" Condition="'%(ReferenceCopyLocalPaths.NuGetPackageId)'=='ThingsGateway.RulesEngine' and Exists('%(RootDir)%(Directory)%(Filename).xml')" />
</ItemGroup>
</Target>
<Target Name="CopyManagementNugetPackages" AfterTargets="Build">
<ItemGroup>
<!-- setting up the variable for convenience -->
<ManagementApplicationPackageFiles Include="$(PkgThingsGateway_Management)\Content\SeedData\Management\*.*" />
</ItemGroup>
<PropertyGroup>
<ManagementApplicationFolder>$(TargetDir)SeedData\Management\</ManagementApplicationFolder>
</PropertyGroup>
<RemoveDir Directories="$(ManagementApplicationFolder)" />
<Copy SourceFiles="@(ManagementApplicationPackageFiles)" DestinationFolder="$(ManagementApplicationFolder)%(RecursiveDir)" />
</Target>
<Target Name="CopyRulesEngineNugetPackages" AfterTargets="Build">
<ItemGroup>
<!-- setting up the variable for convenience -->
<RulesEngineApplicationPackageFiles Include="$(PkgThingsGateway_RulesEngine)\Content\SeedData\RulesEngine\*.*" />
</ItemGroup>
<PropertyGroup>
<RulesEngineApplicationFolder>$(TargetDir)SeedData\RulesEngine\</RulesEngineApplicationFolder>
</PropertyGroup>
<RemoveDir Directories="$(RulesEngineApplicationFolder)" />
<Copy SourceFiles="@(RulesEngineApplicationPackageFiles)" DestinationFolder="$(RulesEngineApplicationFolder)%(RecursiveDir)" />
<Target Name="CopyManagementNugetPackages" AfterTargets="Build">
<ItemGroup>
<!-- setting up the variable for convenience -->
<ManagementApplicationPackageFiles Include="$(PkgThingsGateway_Management)\Content\SeedData\Management\*.*" />
</ItemGroup>
<PropertyGroup>
<ManagementApplicationFolder>$(TargetDir)SeedData\Management\</ManagementApplicationFolder>
</PropertyGroup>
<RemoveDir Directories="$(ManagementApplicationFolder)" />
<Copy SourceFiles="@(ManagementApplicationPackageFiles)" DestinationFolder="$(ManagementApplicationFolder)%(RecursiveDir)" />
</Target>
</Target>
<Target Name="CopyRulesEngineNugetPackages" AfterTargets="Build">
<ItemGroup>
<!-- setting up the variable for convenience -->
<RulesEngineApplicationPackageFiles Include="$(PkgThingsGateway_RulesEngine)\Content\SeedData\RulesEngine\*.*" />
</ItemGroup>
<PropertyGroup>
<RulesEngineApplicationFolder>$(TargetDir)SeedData\RulesEngine\</RulesEngineApplicationFolder>
</PropertyGroup>
<RemoveDir Directories="$(RulesEngineApplicationFolder)" />
<Copy SourceFiles="@(RulesEngineApplicationPackageFiles)" DestinationFolder="$(RulesEngineApplicationFolder)%(RecursiveDir)" />
<Target Name="ManagementPostPublish" AfterTargets="Publish">
<ItemGroup>
<!-- setting up the variable for convenience -->
<ManagementFiles Include="bin\$(Configuration)\$(TargetFramework)\SeedData\**" />
</ItemGroup>
<PropertyGroup>
</PropertyGroup>
<Copy SourceFiles="@(ManagementFiles)" DestinationFolder="$(PublishDir)SeedData\%(RecursiveDir)" />
</Target>
</Target>
<Target Name="RulesEnginePostPublish" AfterTargets="Publish">
<ItemGroup>
<!-- setting up the variable for convenience -->
<RulesEngineFiles Include="bin\$(Configuration)\$(TargetFramework)\SeedData\**" />
</ItemGroup>
<PropertyGroup>
</PropertyGroup>
<Copy SourceFiles="@(RulesEngineFiles)" DestinationFolder="$(PublishDir)SeedData\%(RecursiveDir)" />
<Target Name="ManagementPostPublish" AfterTargets="Publish">
<ItemGroup>
<!-- setting up the variable for convenience -->
<ManagementFiles Include="bin\$(Configuration)\$(TargetFramework)\SeedData\**" />
</ItemGroup>
<PropertyGroup>
</PropertyGroup>
<Copy SourceFiles="@(ManagementFiles)" DestinationFolder="$(PublishDir)SeedData\%(RecursiveDir)" />
</Target>
</Target>
<Target Name="RulesEnginePostPublish" AfterTargets="Publish">
<ItemGroup>
<!-- setting up the variable for convenience -->
<RulesEngineFiles Include="bin\$(Configuration)\$(TargetFramework)\SeedData\**" />
</ItemGroup>
<PropertyGroup>
</PropertyGroup>
<Copy SourceFiles="@(RulesEngineFiles)" DestinationFolder="$(PublishDir)SeedData\%(RecursiveDir)" />
</Target>
</Project>

View File

@@ -58,27 +58,27 @@
<MainFile3 Include="$(PkgThingsGateway_Plugin_URF_R330)\Main\**" />
</ItemGroup>
<PropertyGroup>
<PluginFolder>$(TargetDir)Plugins\</PluginFolder>
<PluginFolder>$(TargetDir)GatewayPlugins\</PluginFolder>
</PropertyGroup>
<Message Text="将插件复制到插件目录 $(PluginFolder)" Importance="high" />
<Copy SourceFiles="@(PkgThingsGateway_Plugin_HJ212PackageFiles)" DestinationFolder="$(PluginFolder)%(RecursiveDir)" />
<Copy SourceFiles="@(PkgThingsGateway_Plugin_BACnetPackageFiles)" DestinationFolder="$(PluginFolder)%(RecursiveDir)" />
<Copy SourceFiles="@(PkgThingsGateway_Plugin_AllenBradleyCipPackageFiles)" DestinationFolder="$(PluginFolder)%(RecursiveDir)" />
<Copy SourceFiles="@(PkgThingsGateway_Plugin_DCONPackageFiles)" DestinationFolder="$(PluginFolder)%(RecursiveDir)" />
<Copy SourceFiles="@(PkgThingsGateway_Plugin_EDPF_NTPackageFiles)" DestinationFolder="$(PluginFolder)%(RecursiveDir)" />
<Copy SourceFiles="@(PkgThingsGateway_Plugin_KELID2008PackageFiles)" DestinationFolder="$(PluginFolder)%(RecursiveDir)" />
<Copy SourceFiles="@(PkgThingsGateway_Plugin_LKSISPackageFiles)" DestinationFolder="$(PluginFolder)%(RecursiveDir)" />
<Copy SourceFiles="@(PkgThingsGateway_Plugin_MelsecPackageFiles)" DestinationFolder="$(PluginFolder)%(RecursiveDir)" />
<Copy SourceFiles="@(PkgThingsGateway_Plugin_OmronPackageFiles)" DestinationFolder="$(PluginFolder)%(RecursiveDir)" />
<Copy SourceFiles="@(PkgThingsGateway_Plugin_DKQ_A16DPackageFiles)" DestinationFolder="$(PluginFolder)%(RecursiveDir)" />
<Copy SourceFiles="@(PkgThingsGateway_Plugin_IDR210PackageFiles)" DestinationFolder="$(PluginFolder)%(RecursiveDir)" />
<Copy SourceFiles="@(PkgThingsGateway_Plugin_URF_R330PackageFiles)" DestinationFolder="$(PluginFolder)%(RecursiveDir)" />
<Copy SourceFiles="@(PkgThingsGateway_Plugin_USBScanerPackageFiles)" DestinationFolder="$(PluginFolder)%(RecursiveDir)" />
<Copy SourceFiles="@(PkgThingsGateway_Plugin_SECSPackageFiles)" DestinationFolder="$(PluginFolder)%(RecursiveDir)" />
<Copy SourceFiles="@(PkgThingsGateway_Plugin_TS550PackageFiles)" DestinationFolder="$(PluginFolder)%(RecursiveDir)" />
<Copy SourceFiles="@(PkgThingsGateway_Plugin_VigorPackageFiles)" DestinationFolder="$(PluginFolder)%(RecursiveDir)" />
<Copy SourceFiles="@(PkgThingsGateway_Plugin_HJ212PackageFiles)" DestinationFolder="$(PluginFolder)ThingsGateway.Plugin.HJ212%(RecursiveDir)" />
<Copy SourceFiles="@(PkgThingsGateway_Plugin_BACnetPackageFiles)" DestinationFolder="$(PluginFolder)ThingsGateway.Plugin.BACnet%(RecursiveDir)" />
<Copy SourceFiles="@(PkgThingsGateway_Plugin_AllenBradleyCipPackageFiles)" DestinationFolder="$(PluginFolder)ThingsGateway.Plugin.AllenBradleyCip%(RecursiveDir)" />
<Copy SourceFiles="@(PkgThingsGateway_Plugin_DCONPackageFiles)" DestinationFolder="$(PluginFolder)ThingsGateway.Plugin.DCON%(RecursiveDir)" />
<Copy SourceFiles="@(PkgThingsGateway_Plugin_EDPF_NTPackageFiles)" DestinationFolder="$(PluginFolder)ThingsGateway.Plugin.EDPF_NT%(RecursiveDir)" />
<Copy SourceFiles="@(PkgThingsGateway_Plugin_KELID2008PackageFiles)" DestinationFolder="$(PluginFolder)ThingsGateway.Plugin.KELID2008%(RecursiveDir)" />
<Copy SourceFiles="@(PkgThingsGateway_Plugin_LKSISPackageFiles)" DestinationFolder="$(PluginFolder)ThingsGateway.Plugin.LKSIS%(RecursiveDir)" />
<Copy SourceFiles="@(PkgThingsGateway_Plugin_MelsecPackageFiles)" DestinationFolder="$(PluginFolder)ThingsGateway.Plugin.Melsec%(RecursiveDir)" />
<Copy SourceFiles="@(PkgThingsGateway_Plugin_OmronPackageFiles)" DestinationFolder="$(PluginFolder)ThingsGateway.Plugin.Omron%(RecursiveDir)" />
<Copy SourceFiles="@(PkgThingsGateway_Plugin_DKQ_A16DPackageFiles)" DestinationFolder="$(PluginFolder)ThingsGateway.Plugin.DKQ_A16D%(RecursiveDir)" />
<Copy SourceFiles="@(PkgThingsGateway_Plugin_IDR210PackageFiles)" DestinationFolder="$(PluginFolder)ThingsGateway.Plugin.IDR210%(RecursiveDir)" />
<Copy SourceFiles="@(PkgThingsGateway_Plugin_URF_R330PackageFiles)" DestinationFolder="$(PluginFolder)ThingsGateway.Plugin.URF_R330%(RecursiveDir)" />
<Copy SourceFiles="@(PkgThingsGateway_Plugin_USBScanerPackageFiles)" DestinationFolder="$(PluginFolder)ThingsGateway.Plugin.USBScaner%(RecursiveDir)" />
<Copy SourceFiles="@(PkgThingsGateway_Plugin_SECSPackageFiles)" DestinationFolder="$(PluginFolder)ThingsGateway.Plugin.SECS%(RecursiveDir)" />
<Copy SourceFiles="@(PkgThingsGateway_Plugin_TS550PackageFiles)" DestinationFolder="$(PluginFolder)ThingsGateway.Plugin.TS550%(RecursiveDir)" />
<Copy SourceFiles="@(PkgThingsGateway_Plugin_VigorPackageFiles)" DestinationFolder="$(PluginFolder)ThingsGateway.Plugin.Vigor%(RecursiveDir)" />
<Copy SourceFiles="@(MainFile1)" DestinationFolder="$(TargetDir)%(RecursiveDir)" />
<Copy SourceFiles="@(MainFile2)" DestinationFolder="$(TargetDir)%(RecursiveDir)" />

View File

@@ -0,0 +1,43 @@
<Project>
<!--Gateway-->
<ItemGroup>
<PackageReference Include="ThingsGateway.Gateway.Razor" Version="$(GatewayVersion)" />
<PackageReference Include="ThingsGateway.Gateway.Application" Version="$(GatewayVersion)" GeneratePathProperty="true"/>
</ItemGroup>
<Target Name="_ResolveCopyGatewayLocalNuGetPkgXmls" AfterTargets="ResolveReferences">
<ItemGroup>
<ReferenceCopyLocalPaths Include="@(ReferenceCopyLocalPaths->'%(RootDir)%(Directory)%(Filename).xml')" Condition="'%(ReferenceCopyLocalPaths.NuGetPackageId)'=='ThingsGateway.Gateway.Application' and Exists('%(RootDir)%(Directory)%(Filename).xml')" />
</ItemGroup>
</Target>
<Target Name="CopyGatewayNugetPackages" AfterTargets="Build">
<ItemGroup>
<!-- setting up the variable for convenience -->
<GatewayApplicationPackageFiles Include="$(PkgThingsGateway_Gateway_Application)\Content\SeedData\Gateway\*.*" />
</ItemGroup>
<PropertyGroup>
<GatewayApplicationFolder>$(TargetDir)SeedData\Gateway\</GatewayApplicationFolder>
</PropertyGroup>
<RemoveDir Directories="$(GatewayApplicationFolder)" />
<Copy SourceFiles="@(GatewayApplicationPackageFiles)" DestinationFolder="$(GatewayApplicationFolder)%(RecursiveDir)" />
</Target>
<Target Name="GatewayPostPublish" AfterTargets="Publish">
<ItemGroup>
<!-- setting up the variable for convenience -->
<GatewayFiles Include="bin\$(Configuration)\$(TargetFramework)\SeedData\**" />
</ItemGroup>
<PropertyGroup>
</PropertyGroup>
<Copy SourceFiles="@(GatewayFiles)" DestinationFolder="$(PublishDir)SeedData\%(RecursiveDir)" />
</Target>
<!--Gateway-->
</Project>

View File

@@ -1,30 +1,33 @@
<Project>
<ItemGroup>
<!--MqttYINGKE 插件-->
<PackageReference Include="ThingsGateway.Plugin.MqttYINGKE" Version="$(ProPluginVersion)" GeneratePathProperty="true" Private="false" IncludeAssets="native;" />
<!--HUANANSFSK 插件-->
<PackageReference Include="ThingsGateway.Plugin.HUANANSFSK" Version="$(ProPluginVersion)" GeneratePathProperty="true" Private="false" IncludeAssets="native;" />
<ItemGroup>
<!--MqttYINGKE 插件-->
<PackageReference Include="ThingsGateway.Plugin.MqttYINGKE" Version="$(ProPluginVersion)" GeneratePathProperty="true" Private="false" IncludeAssets="native;" />
<!--HUANANSFSK 插件-->
<PackageReference Include="ThingsGateway.Plugin.HUANANSFSK" Version="$(ProPluginVersion)" GeneratePathProperty="true" Private="false" IncludeAssets="native;" />
<!--YPSFSK 插件-->
<PackageReference Include="ThingsGateway.Plugin.YPSFSK" Version="$(ProPluginVersion)" GeneratePathProperty="true" Private="false" IncludeAssets="native;" />
</ItemGroup>
</ItemGroup>
<Target Name="CopyOtherPluginNugetPackages" AfterTargets="Build">
<ItemGroup>
<!-- setting up the variable for convenience -->
<PkgThingsGateway_Plugin_MqttYINGKEPackageFiles Include="$(PkgThingsGateway_Plugin_MqttYINGKE)\Content\net6.0\**\*.*" />
<PkgThingsGateway_Plugin_HUANANSFSKPackageFiles Include="$(PkgThingsGateway_Plugin_HUANANSFSK)\Content\net6.0\**\*.*" />
<PkgThingsGateway_Plugin_YPSFSKPackageFiles Include="$(PkgThingsGateway_Plugin_YPSFSK)\Content\net6.0\**\*.*" />
<Target Name="CopyOtherPluginNugetPackages" AfterTargets="Build">
<ItemGroup>
<!-- setting up the variable for convenience -->
<PkgThingsGateway_Plugin_MqttYINGKEPackageFiles Include="$(PkgThingsGateway_Plugin_MqttYINGKE)\Content\net6.0\**\*.*" />
<PkgThingsGateway_Plugin_HUANANSFSKPackageFiles Include="$(PkgThingsGateway_Plugin_HUANANSFSK)\Content\net6.0\**\*.*" />
</ItemGroup>
<PropertyGroup>
<PluginFolder>$(TargetDir)Plugins\</PluginFolder>
</PropertyGroup>
<Message Text="将插件复制到插件目录 $(PluginFolder)" Importance="high" />
<Copy SourceFiles="@(PkgThingsGateway_Plugin_MqttYINGKEPackageFiles)" DestinationFolder="$(PluginFolder)%(RecursiveDir)" />
</ItemGroup>
<PropertyGroup>
<PluginFolder>$(TargetDir)Plugins\</PluginFolder>
</PropertyGroup>
<Message Text="将插件复制到插件目录 $(PluginFolder)" Importance="high" />
<Copy SourceFiles="@(PkgThingsGateway_Plugin_HUANANSFSKPackageFiles)" DestinationFolder="$(PluginFolder)%(RecursiveDir)" />
<Copy SourceFiles="@(PkgThingsGateway_Plugin_MqttYINGKEPackageFiles)" DestinationFolder="$(PluginFolder)%(RecursiveDir)" />
<Copy SourceFiles="@(PkgThingsGateway_Plugin_HUANANSFSKPackageFiles)" DestinationFolder="$(PluginFolder)%(RecursiveDir)" />
<Copy SourceFiles="@(PkgThingsGateway_Plugin_YPSFSKPackageFiles)" DestinationFolder="$(PluginFolder)%(RecursiveDir)" />
</Target>
</Target>
</Project>

View File

@@ -1,20 +1,7 @@
<Project>
<!--插件隔离-->
<Target Name="PostPublish" AfterTargets="Publish">
<ItemGroup>
<!-- setting up the variable for convenience -->
<PluginsFiles Include="bin\$(Configuration)\$(TargetFramework)\Plugins\**" />
</ItemGroup>
<PropertyGroup>
</PropertyGroup>
<Copy SourceFiles="@(PluginsFiles)" DestinationFolder="$(PublishDir)Plugins\%(RecursiveDir)" />
</Target>
<ItemGroup>
<ItemGroup>
<!--Modbus 插件-->
<PackageReference Include="ThingsGateway.Plugin.Modbus" Version="$(PluginVersion)" GeneratePathProperty="true">
<Private>false</Private>
@@ -100,21 +87,21 @@
<PkgThingsGateway_Plugin_VariableExpressionPackageFiles Include="$(PkgThingsGateway_Plugin_VariableExpression)\Content\$(CustomTargetFramework)\**\*.*" />
</ItemGroup>
<PropertyGroup>
<PluginFolder>$(TargetDir)Plugins\</PluginFolder>
<PluginFolder>$(TargetDir)GatewayPlugins\</PluginFolder>
</PropertyGroup>
<RemoveDir Directories="$(PluginFolder)" />
<Message Text="将插件复制到插件目录 $(PluginFolder)" Importance="high" />
<Copy SourceFiles="@(PkgThingsGateway_Plugin_ModbusPackageFiles)" DestinationFolder="$(PluginFolder)%(RecursiveDir)" />
<Copy SourceFiles="@(PkgThingsGateway_Plugin_SiemensS7PackageFiles)" DestinationFolder="$(PluginFolder)%(RecursiveDir)" />
<Copy SourceFiles="@(PkgThingsGateway_Plugin_Dlt645PackageFiles)" DestinationFolder="$(PluginFolder)%(RecursiveDir)" />
<Copy SourceFiles="@(PkgThingsGateway_Plugin_DBPackageFiles)" DestinationFolder="$(PluginFolder)%(RecursiveDir)" />
<Copy SourceFiles="@(PkgThingsGateway_Plugin_KafkaPackageFiles)" DestinationFolder="$(PluginFolder)%(RecursiveDir)" />
<Copy SourceFiles="@(PkgThingsGateway_Plugin_MqttPackageFiles)" DestinationFolder="$(PluginFolder)%(RecursiveDir)" />
<Copy SourceFiles="@(PkgThingsGateway_Plugin_OpcDaPackageFiles)" DestinationFolder="$(PluginFolder)%(RecursiveDir)" />
<Copy SourceFiles="@(PkgThingsGateway_Plugin_OpcDaNetApiPackageFiles)" DestinationFolder="$(PluginFolder)%(RecursiveDir)" />
<Copy SourceFiles="@(PkgThingsGateway_Plugin_OpcUaPackageFiles)" DestinationFolder="$(PluginFolder)%(RecursiveDir)" />
<Copy SourceFiles="@(PkgThingsGateway_Plugin_RabbitMQPackageFiles)" DestinationFolder="$(PluginFolder)%(RecursiveDir)" />
<Copy SourceFiles="@(PkgThingsGateway_Plugin_VariableExpressionPackageFiles)" DestinationFolder="$(PluginFolder)%(RecursiveDir)" />
<Copy SourceFiles="@(PkgThingsGateway_Plugin_ModbusPackageFiles)" DestinationFolder="$(PluginFolder)ThingsGateway.Plugin.Modbus%(RecursiveDir)" />
<Copy SourceFiles="@(PkgThingsGateway_Plugin_SiemensS7PackageFiles)" DestinationFolder="$(PluginFolder)ThingsGateway.Plugin.SiemensS7%(RecursiveDir)" />
<Copy SourceFiles="@(PkgThingsGateway_Plugin_Dlt645PackageFiles)" DestinationFolder="$(PluginFolder)ThingsGateway.Plugin.Dlt645%(RecursiveDir)" />
<Copy SourceFiles="@(PkgThingsGateway_Plugin_DBPackageFiles)" DestinationFolder="$(PluginFolder)ThingsGateway.Plugin.DB%(RecursiveDir)" />
<Copy SourceFiles="@(PkgThingsGateway_Plugin_KafkaPackageFiles)" DestinationFolder="$(PluginFolder)ThingsGateway.Plugin.Kafka%(RecursiveDir)" />
<Copy SourceFiles="@(PkgThingsGateway_Plugin_MqttPackageFiles)" DestinationFolder="$(PluginFolder)ThingsGateway.Plugin.Mqtt%(RecursiveDir)" />
<Copy SourceFiles="@(PkgThingsGateway_Plugin_OpcDaPackageFiles)" DestinationFolder="$(PluginFolder)ThingsGateway.Plugin.OpcDa%(RecursiveDir)" />
<Copy SourceFiles="@(PkgThingsGateway_Plugin_OpcDaNetApiPackageFiles)" DestinationFolder="$(PluginFolder)ThingsGateway.Plugin.OpcDaNetApi%(RecursiveDir)" />
<Copy SourceFiles="@(PkgThingsGateway_Plugin_OpcUaPackageFiles)" DestinationFolder="$(PluginFolder)ThingsGateway.Plugin.OpcUa%(RecursiveDir)" />
<Copy SourceFiles="@(PkgThingsGateway_Plugin_RabbitMQPackageFiles)" DestinationFolder="$(PluginFolder)ThingsGateway.Plugin.RabbitMQ%(RecursiveDir)" />
<Copy SourceFiles="@(PkgThingsGateway_Plugin_VariableExpressionPackageFiles)" DestinationFolder="$(PluginFolder)ThingsGateway.Plugin.VariableExpression%(RecursiveDir)" />
</Target>

View File

@@ -0,0 +1,113 @@
<Project>
<!--插件直接加载到程序上下文,不隔离-->
<ItemGroup>
<!--Modbus 插件-->
<PackageReference Include="ThingsGateway.Plugin.Modbus" Version="$(PluginVersion)" GeneratePathProperty="true">
<Private>false</Private>
<IncludeAssets> native;</IncludeAssets>
</PackageReference>
<!--SiemensS7 插件-->
<PackageReference Include="ThingsGateway.Plugin.SiemensS7" Version="$(PluginVersion)" GeneratePathProperty="true">
<Private>false</Private>
<IncludeAssets> native;</IncludeAssets>
</PackageReference>
<!--Dlt645 插件-->
<PackageReference Include="ThingsGateway.Plugin.Dlt645" Version="$(PluginVersion)" GeneratePathProperty="true">
<Private>false</Private>
<IncludeAssets> native;</IncludeAssets>
</PackageReference>
<!--OpcDa 插件-->
<PackageReference Include="ThingsGateway.Plugin.OpcDa" Version="$(PluginVersion)" GeneratePathProperty="true">
<Private>false</Private>
<IncludeAssets> native;</IncludeAssets>
</PackageReference>
<!--OpcDa 插件-->
<PackageReference Include="ThingsGateway.Plugin.OpcDaNetApi" Version="$(PluginVersion)" GeneratePathProperty="true">
<Private>false</Private>
<IncludeAssets> native;</IncludeAssets>
</PackageReference>
<!--OpcUa 插件-->
<PackageReference Include="ThingsGateway.Plugin.OpcUa" Version="$(PluginVersion)" GeneratePathProperty="true">
<Private>false</Private>
<IncludeAssets> native;</IncludeAssets>
</PackageReference>
<!--DB 插件-->
<PackageReference Include="ThingsGateway.Plugin.DB" Version="$(PluginVersion)" GeneratePathProperty="true">
<Private>false</Private>
<IncludeAssets> native;</IncludeAssets>
</PackageReference>
<!--Kafka 插件-->
<PackageReference Include="ThingsGateway.Plugin.Kafka" Version="$(PluginVersion)" GeneratePathProperty="true">
<Private>false</Private>
<IncludeAssets> native;</IncludeAssets>
</PackageReference>
<!--Mqtt 插件-->
<PackageReference Include="ThingsGateway.Plugin.Mqtt" Version="$(PluginVersion)" GeneratePathProperty="true">
<Private>false</Private>
<IncludeAssets> native;</IncludeAssets>
</PackageReference>
<!--RabbitMQ 插件-->
<PackageReference Include="ThingsGateway.Plugin.RabbitMQ" Version="$(PluginVersion)" GeneratePathProperty="true">
<Private>false</Private>
<IncludeAssets> native;</IncludeAssets>
</PackageReference>
<!--VariableExpression 插件-->
<PackageReference Include="ThingsGateway.Plugin.VariableExpression" Version="$(PluginVersion)" GeneratePathProperty="true">
<Private>false</Private>
<IncludeAssets> native;</IncludeAssets>
</PackageReference>
</ItemGroup>
<Target Name="CopyPluginNugetPackages" AfterTargets="Build">
<ItemGroup>
<!-- setting up the variable for convenience -->
<PkgThingsGateway_Plugin_ModbusPackageFiles Include="$(PkgThingsGateway_Plugin_Modbus)\Content\$(CustomTargetFramework)\**\*.*" />
<PkgThingsGateway_Plugin_SiemensS7PackageFiles Include="$(PkgThingsGateway_Plugin_SiemensS7)\Content\$(CustomTargetFramework)\**\*.*" />
<PkgThingsGateway_Plugin_Dlt645PackageFiles Include="$(PkgThingsGateway_Plugin_Dlt645)\Content\$(CustomTargetFramework)\**\*.*" />
<PkgThingsGateway_Plugin_DBPackageFiles Include="$(PkgThingsGateway_Plugin_DB)\Content\$(CustomTargetFramework)\**\*.*" />
<PkgThingsGateway_Plugin_KafkaPackageFiles Include="$(PkgThingsGateway_Plugin_Kafka)\Content\$(CustomTargetFramework)\**\*.*" />
<PkgThingsGateway_Plugin_MqttPackageFiles Include="$(PkgThingsGateway_Plugin_Mqtt)\Content\$(CustomTargetFramework)\**\*.*" />
<PkgThingsGateway_Plugin_OpcDaPackageFiles Include="$(PkgThingsGateway_Plugin_OpcDa)\Content\$(CustomTargetFramework)\**\*.*" />
<PkgThingsGateway_Plugin_OpcDaNetApiPackageFiles Include="$(PkgThingsGateway_Plugin_OpcDaNetApi)\Content\$(CustomTargetFramework)\**\*.*" />
<PkgThingsGateway_Plugin_OpcUaPackageFiles Include="$(PkgThingsGateway_Plugin_OpcUa)\Content\$(CustomTargetFramework)\**\*.*" />
<PkgThingsGateway_Plugin_RabbitMQPackageFiles Include="$(PkgThingsGateway_Plugin_RabbitMQ)\Content\$(CustomTargetFramework)\**\*.*" />
<PkgThingsGateway_Plugin_VariableExpressionPackageFiles Include="$(PkgThingsGateway_Plugin_VariableExpression)\Content\$(CustomTargetFramework)\**\*.*" />
</ItemGroup>
<PropertyGroup>
<PluginFolder>$(TargetDir)Plugins\</PluginFolder>
<GatewayPluginFolder>$(TargetDir)GatewayPlugins\</GatewayPluginFolder>
</PropertyGroup>
<RemoveDir Directories="$(PluginFolder)" />
<RemoveDir Directories="$(GatewayPluginFolder)" />
<Message Text="将插件复制到插件目录 $(PluginFolder)" Importance="high" />
<Copy SourceFiles="@(PkgThingsGateway_Plugin_ModbusPackageFiles)" DestinationFolder="$(PluginFolder)%(RecursiveDir)" />
<Copy SourceFiles="@(PkgThingsGateway_Plugin_SiemensS7PackageFiles)" DestinationFolder="$(PluginFolder)%(RecursiveDir)" />
<Copy SourceFiles="@(PkgThingsGateway_Plugin_Dlt645PackageFiles)" DestinationFolder="$(PluginFolder)%(RecursiveDir)" />
<Copy SourceFiles="@(PkgThingsGateway_Plugin_DBPackageFiles)" DestinationFolder="$(PluginFolder)%(RecursiveDir)" />
<Copy SourceFiles="@(PkgThingsGateway_Plugin_KafkaPackageFiles)" DestinationFolder="$(PluginFolder)%(RecursiveDir)" />
<Copy SourceFiles="@(PkgThingsGateway_Plugin_MqttPackageFiles)" DestinationFolder="$(PluginFolder)%(RecursiveDir)" />
<Copy SourceFiles="@(PkgThingsGateway_Plugin_OpcDaPackageFiles)" DestinationFolder="$(PluginFolder)%(RecursiveDir)" />
<Copy SourceFiles="@(PkgThingsGateway_Plugin_OpcDaNetApiPackageFiles)" DestinationFolder="$(PluginFolder)%(RecursiveDir)" />
<Copy SourceFiles="@(PkgThingsGateway_Plugin_OpcUaPackageFiles)" DestinationFolder="$(PluginFolder)%(RecursiveDir)" />
<Copy SourceFiles="@(PkgThingsGateway_Plugin_RabbitMQPackageFiles)" DestinationFolder="$(PluginFolder)%(RecursiveDir)" />
<Copy SourceFiles="@(PkgThingsGateway_Plugin_VariableExpressionPackageFiles)" DestinationFolder="$(PluginFolder)%(RecursiveDir)" />
</Target>
</Project>

View File

@@ -0,0 +1,19 @@
<Project>
<Target Name="PostPublish" AfterTargets="Publish">
<ItemGroup>
<!-- setting up the variable for convenience -->
<PluginsFiles Include="bin\$(Configuration)\$(TargetFramework)\Plugins\**" />
<GatewayPluginsFiles Include="bin\$(Configuration)\$(TargetFramework)\GatewayPlugins\**" />
</ItemGroup>
<PropertyGroup>
</PropertyGroup>
<Copy SourceFiles="@(PluginsFiles)" DestinationFolder="$(PublishDir)Plugins\%(RecursiveDir)" />
<Copy SourceFiles="@(GatewayPluginsFiles)" DestinationFolder="$(PublishDir)GatewayPlugins\%(RecursiveDir)" />
</Target>
</Project>

View File

@@ -1,119 +1,118 @@
<Project Sdk="Microsoft.NET.Sdk.Razor">
<Import Project="$(SolutionDir)Version.props" />
<Import Project="..\ThingsGateway.Server\targets\Admin.targets" />
<Import Project="..\ThingsGateway.Server\targets\Plugin.targets" />
<Import Project="..\ThingsGateway.Server\targets\CustomPlugin.targets" />
<Import Project="..\ThingsGateway.Server\targets\Custom.targets" />
<PropertyGroup>
<CustomTargetFramework>net9.0</CustomTargetFramework>
<TargetFrameworks>net9.0-windows;</TargetFrameworks>
<OutputType>WinExe</OutputType>
<OpenApiGenerateDocuments>false</OpenApiGenerateDocuments>
<SatelliteResourceLanguages>zh-Hans;en-US</SatelliteResourceLanguages>
<PublishReadyToRunComposite>true</PublishReadyToRunComposite>
<ApplicationIcon>favicon.ico</ApplicationIcon>
<TargetFrameworks>net9.0-windows;</TargetFrameworks>
<Import Project="$(SolutionDir)Version.props" />
<!--<Import Project="..\ThingsGateway.Server\targets\Admin.targets" />-->
<Import Project="..\ThingsGateway.Server\targets\PluginContext.targets" />
<!--<Import Project="..\ThingsGateway.Server\targets\Plugin.targets" />-->
<Import Project="..\ThingsGateway.Server\targets\CustomPlugin.targets" />
<Import Project="..\ThingsGateway.Server\targets\Custom.targets" />
<PropertyGroup>
<CustomTargetFramework>net9.0</CustomTargetFramework>
<TargetFrameworks>net9.0-windows;</TargetFrameworks>
<OutputType>WinExe</OutputType>
<OpenApiGenerateDocuments>false</OpenApiGenerateDocuments>
<SatelliteResourceLanguages>zh-Hans;en-US</SatelliteResourceLanguages>
<PublishReadyToRunComposite>true</PublishReadyToRunComposite>
<ApplicationIcon>favicon.ico</ApplicationIcon>
<TargetFrameworks>net9.0-windows;</TargetFrameworks>
<!--动态适用GC-->
<GarbageCollectionAdaptationMode>1</GarbageCollectionAdaptationMode>
<!--使用工作站GC-->
<!--<ServerGarbageCollection>true</ServerGarbageCollection>-->
<!--<PlatformTarget>x86</PlatformTarget>-->
</PropertyGroup>
<!--动态适用GC-->
<GarbageCollectionAdaptationMode>1</GarbageCollectionAdaptationMode>
<!--使用工作站GC-->
<!--<ServerGarbageCollection>true</ServerGarbageCollection>-->
<!--<PlatformTarget>x86</PlatformTarget>-->
</PropertyGroup>
<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebView.WindowsForms" Version="9.0.12" />
<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebView.WindowsForms" Version="9.0.21" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net9.0' ">
<PackageReference Include="Microsoft.Data.Sqlite" Version="9.0.0" />
</ItemGroup>
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' != 'net6.0' ">
<PackageReference Include="Microsoft.Data.Sqlite" Version="9.0.0" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)'=='net8.0'">
<PackageReference Include="Microsoft.Data.Sqlite" Version="8.0.10" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)'=='net6.0'">
<PackageReference Include="Microsoft.Data.Sqlite" Version="7.0.20" />
</ItemGroup>
<!--安装服务守护-->
<ItemGroup Condition=" '$(TargetFramework)' != 'net9.0' ">
<PackageReference Include="Microsoft.Extensions.Hosting.Systemd" Version="8.0.1" />
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="8.0.1" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net9.0' ">
<PackageReference Include="Microsoft.Extensions.Hosting.Systemd" Version="9.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="9.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Gateway\ThingsGateway.Gateway.Application\ThingsGateway.Gateway.Application.csproj" />
<ProjectReference Include="..\Gateway\ThingsGateway.Gateway.Razor\ThingsGateway.Gateway.Razor.csproj" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)'=='net6.0'">
<PackageReference Include="Microsoft.Data.Sqlite" Version="7.0.20" />
</ItemGroup>
<ItemGroup>
<Content Include="..\ThingsGateway.Server\Index\GatewayIndex.razor" Link="Index\GatewayIndex.razor" />
<Compile Include="..\ThingsGateway.Server\Index\GatewayIndex.razor.cs" Link="Index\GatewayIndex.razor.cs" />
<Content Include="..\ThingsGateway.Server\Layout\MainLayout.razor" Link="Layout\MainLayout.razor" />
<Compile Include="..\ThingsGateway.Server\Layout\MainLayout.razor.cs" Link="Layout\MainLayout.razor.cs" />
<Content Include="..\ThingsGateway.Server\Layout\MainLayout.razor.css" Link="Layout\MainLayout.razor.css" />
<Content Include="..\ThingsGateway.Server\Layout\AccessDenied.razor" Link="Layout\AccessDenied.razor" />
<Compile Include="..\ThingsGateway.Server\Layout\AccessDenied.razor.cs" Link="Layout\AccessDenied.razor.cs" />
<Content Include="..\ThingsGateway.Server\Layout\Login.razor" Link="Layout\Login.razor" />
<Compile Include="..\ThingsGateway.Server\Layout\Login.razor.cs" Link="Layout\Login.razor.cs" />
<Content Include="..\ThingsGateway.Server\Layout\Login.razor.css" Link="Layout\Login.razor.css" />
<Content Include="..\ThingsGateway.Server\Layout\NotFound404.razor" Link="Layout\NotFound404.razor" />
<Compile Include="..\ThingsGateway.Server\Layout\NotFound404.razor.cs" Link="Layout\NotFound404.razor.cs" />
<Content Include="..\ThingsGateway.Server\Index\GatewayIndexComponent.razor" Link="Index\GatewayIndexComponent.razor" />
<Compile Include="..\ThingsGateway.Server\Index\GatewayIndexComponent.razor.cs" Link="Index\GatewayIndexComponent.razor.cs" />
<!--安装服务守护-->
<ItemGroup Condition=" '$(TargetFramework)' != 'net9.0' ">
<PackageReference Include="Microsoft.Extensions.Hosting.Systemd" Version="8.0.1" />
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="8.0.1" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net9.0' ">
<PackageReference Include="Microsoft.Extensions.Hosting.Systemd" Version="9.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="9.0.0" />
</ItemGroup>
<Compile Include="..\ThingsGateway.Server\Program\SingleFilePublish.cs" Link="Program\SingleFilePublish.cs" />
<ItemGroup>
<ProjectReference Include="..\Gateway\ThingsGateway.Gateway.Application\ThingsGateway.Gateway.Application.csproj" />
<ProjectReference Include="..\Gateway\ThingsGateway.Gateway.Razor\ThingsGateway.Gateway.Razor.csproj" />
</ItemGroup>
<Content Include="..\ThingsGateway.Server\wwwroot\favicon.ico" Link="wwwroot\favicon.ico" />
<Content Include="..\ThingsGateway.Server\wwwroot\favicon.png" Link="wwwroot\favicon.png" />
<Content Include="..\ThingsGateway.Server\appsettings.Development.json" Link="appsettings.Development.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="..\ThingsGateway.Server\appsettings.json" Link="appsettings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<ItemGroup>
<Content Include="..\ThingsGateway.Server\Index\GatewayIndex.razor" Link="Index\GatewayIndex.razor" />
<Compile Include="..\ThingsGateway.Server\Index\GatewayIndex.razor.cs" Link="Index\GatewayIndex.razor.cs" />
<Content Include="..\ThingsGateway.Server\Layout\MainLayout.razor" Link="Layout\MainLayout.razor" />
<Compile Include="..\ThingsGateway.Server\Layout\MainLayout.razor.cs" Link="Layout\MainLayout.razor.cs" />
<Content Include="..\ThingsGateway.Server\Layout\MainLayout.razor.css" Link="Layout\MainLayout.razor.css" />
<Content Include="..\ThingsGateway.Server\Layout\AccessDenied.razor" Link="Layout\AccessDenied.razor" />
<Compile Include="..\ThingsGateway.Server\Layout\AccessDenied.razor.cs" Link="Layout\AccessDenied.razor.cs" />
<Content Include="..\ThingsGateway.Server\Layout\Login.razor" Link="Layout\Login.razor" />
<Compile Include="..\ThingsGateway.Server\Layout\Login.razor.cs" Link="Layout\Login.razor.cs" />
<Content Include="..\ThingsGateway.Server\Layout\Login.razor.css" Link="Layout\Login.razor.css" />
<Content Include="..\ThingsGateway.Server\Layout\NotFound404.razor" Link="Layout\NotFound404.razor" />
<Compile Include="..\ThingsGateway.Server\Layout\NotFound404.razor.cs" Link="Layout\NotFound404.razor.cs" />
<Content Include="..\ThingsGateway.Server\Index\GatewayIndexComponent.razor" Link="Index\GatewayIndexComponent.razor" />
<Compile Include="..\ThingsGateway.Server\Index\GatewayIndexComponent.razor.cs" Link="Index\GatewayIndexComponent.razor.cs" />
<Content Include="..\ThingsGateway.Server\Configuration\*" LinkBase="Configuration">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Compile Include="..\ThingsGateway.Server\Program\SingleFilePublish.cs" Link="Program\SingleFilePublish.cs" />
<Content Include="favicon.ico">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<EmbeddedResource Include="..\ThingsGateway.Server\Locales\en-US.json" Link="Locales\en-US.json" />
<EmbeddedResource Include="..\ThingsGateway.Server\Locales\zh-CN.json" Link="Locales\zh-CN.json" />
<EmbeddedResource Include="..\ThingsGateway.Server\Locales\zh-TW.json">
<Link>Locales\zh-TW.json</Link>
</EmbeddedResource>
<EmbeddedResource Include="Locales\*.json">
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
</EmbeddedResource>
<Content Update="wwwroot\**">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<Compile Include="..\ThingsGateway.Photino\Startup.cs" Link="Startup.cs" />
<Compile Include="..\ThingsGateway.Photino\Services\*" LinkBase="Services">
</Compile>
</ItemGroup>
<Content Include="..\ThingsGateway.Server\wwwroot\favicon.ico" Link="wwwroot\favicon.ico" />
<Content Include="..\ThingsGateway.Server\wwwroot\favicon.png" Link="wwwroot\favicon.png" />
<Content Include="..\ThingsGateway.Server\appsettings.Development.json" Link="appsettings.Development.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="..\ThingsGateway.Server\appsettings.json" Link="appsettings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="..\ThingsGateway.Server\Configuration\*" LinkBase="Configuration">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="favicon.ico">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<EmbeddedResource Include="..\ThingsGateway.Server\Locales\en-US.json" Link="Locales\en-US.json" />
<EmbeddedResource Include="..\ThingsGateway.Server\Locales\zh-CN.json" Link="Locales\zh-CN.json" />
<EmbeddedResource Include="..\ThingsGateway.Server\Locales\zh-TW.json">
<Link>Locales\zh-TW.json</Link>
</EmbeddedResource>
<EmbeddedResource Include="Locales\*.json">
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
</EmbeddedResource>
<Content Update="wwwroot\**">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<Compile Include="..\ThingsGateway.Photino\Startup.cs" Link="Startup.cs" />
<Compile Include="..\ThingsGateway.Photino\Services\*" LinkBase="Services">
</Compile>
</ItemGroup>
</Project>

View File

@@ -14,6 +14,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "other", "other", "{0B748352
Directory.Build.props = Directory.Build.props
FoundationVersion.props = FoundationVersion.props
Version.props = Version.props
..\git_pull.bat = ..\git_pull.bat
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Gateway", "Gateway", "{5948EA23-4B42-4C22-A266-2E0AE5FA575F}"
@@ -34,6 +35,29 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Gateway.Razor
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ThingsGateway.Foundation.Razor", "Foundation\ThingsGateway.Foundation.Razor\ThingsGateway.Foundation.Razor.csproj", "{569F229F-FB10-4789-B00B-D9D12383177A}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Admin", "Admin", "{72C65578-92A5-4E99-9779-27835B12B32F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ThingsGateway.Admin.Application", "..\Admin\src\ThingsGateway.Admin.Application\ThingsGateway.Admin.Application.csproj", "{C4C01993-DB12-49D9-BDDB-C312042A6C29}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ThingsGateway.Admin.Razor", "..\Admin\src\ThingsGateway.Admin.Razor\ThingsGateway.Admin.Razor.csproj", "{BC3AE9E7-7B56-4894-9514-52A493A0D724}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ThingsGateway.Core", "..\Admin\src\ThingsGateway.Core\ThingsGateway.Core.csproj", "{D473E69E-FDBE-4A8E-A835-7F380C8E6D86}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ThingsGateway.Furion", "..\Admin\src\ThingsGateway.Furion\ThingsGateway.Furion.csproj", "{3759E861-BA1E-498A-85CF-48A0BE6B00BB}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ThingsGateway.NewLife.X", "..\Admin\src\ThingsGateway.NewLife.X\ThingsGateway.NewLife.X.csproj", "{42928B73-237A-4483-935E-E7FAAC52F288}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ThingsGateway.Razor", "..\Admin\src\ThingsGateway.Razor\ThingsGateway.Razor.csproj", "{015DD6B7-325F-45B5-B69E-CBD62DC6235A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ThingsGateway.AdminServer", "..\Admin\src\ThingsGateway.AdminServer\ThingsGateway.AdminServer.csproj", "{BE13F278-C153-4CB8-A4EF-59BEFAE54CF8}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "other", "other", "{568A28E1-6CAE-461C-8578-C28BEAC81945}"
ProjectSection(SolutionItems) = preProject
..\Admin\src\Admin.props = ..\Admin\src\Admin.props
..\Admin\src\PackNuget.props = ..\Admin\src\PackNuget.props
..\Admin\src\Version.props = ..\Admin\src\Version.props
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -80,6 +104,34 @@ Global
{569F229F-FB10-4789-B00B-D9D12383177A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{569F229F-FB10-4789-B00B-D9D12383177A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{569F229F-FB10-4789-B00B-D9D12383177A}.Release|Any CPU.Build.0 = Release|Any CPU
{C4C01993-DB12-49D9-BDDB-C312042A6C29}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C4C01993-DB12-49D9-BDDB-C312042A6C29}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C4C01993-DB12-49D9-BDDB-C312042A6C29}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C4C01993-DB12-49D9-BDDB-C312042A6C29}.Release|Any CPU.Build.0 = Release|Any CPU
{BC3AE9E7-7B56-4894-9514-52A493A0D724}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BC3AE9E7-7B56-4894-9514-52A493A0D724}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BC3AE9E7-7B56-4894-9514-52A493A0D724}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BC3AE9E7-7B56-4894-9514-52A493A0D724}.Release|Any CPU.Build.0 = Release|Any CPU
{D473E69E-FDBE-4A8E-A835-7F380C8E6D86}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D473E69E-FDBE-4A8E-A835-7F380C8E6D86}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D473E69E-FDBE-4A8E-A835-7F380C8E6D86}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D473E69E-FDBE-4A8E-A835-7F380C8E6D86}.Release|Any CPU.Build.0 = Release|Any CPU
{3759E861-BA1E-498A-85CF-48A0BE6B00BB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3759E861-BA1E-498A-85CF-48A0BE6B00BB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3759E861-BA1E-498A-85CF-48A0BE6B00BB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3759E861-BA1E-498A-85CF-48A0BE6B00BB}.Release|Any CPU.Build.0 = Release|Any CPU
{42928B73-237A-4483-935E-E7FAAC52F288}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{42928B73-237A-4483-935E-E7FAAC52F288}.Debug|Any CPU.Build.0 = Debug|Any CPU
{42928B73-237A-4483-935E-E7FAAC52F288}.Release|Any CPU.ActiveCfg = Release|Any CPU
{42928B73-237A-4483-935E-E7FAAC52F288}.Release|Any CPU.Build.0 = Release|Any CPU
{015DD6B7-325F-45B5-B69E-CBD62DC6235A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{015DD6B7-325F-45B5-B69E-CBD62DC6235A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{015DD6B7-325F-45B5-B69E-CBD62DC6235A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{015DD6B7-325F-45B5-B69E-CBD62DC6235A}.Release|Any CPU.Build.0 = Release|Any CPU
{BE13F278-C153-4CB8-A4EF-59BEFAE54CF8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BE13F278-C153-4CB8-A4EF-59BEFAE54CF8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BE13F278-C153-4CB8-A4EF-59BEFAE54CF8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BE13F278-C153-4CB8-A4EF-59BEFAE54CF8}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -92,10 +144,18 @@ Global
{A1163B55-31D8-4F58-872F-1DD5E0F14490} = {5948EA23-4B42-4C22-A266-2E0AE5FA575F}
{6B92AF2D-0997-4887-B1C5-31861F298238} = {5948EA23-4B42-4C22-A266-2E0AE5FA575F}
{569F229F-FB10-4789-B00B-D9D12383177A} = {2AC600BB-4325-4E0A-93A7-B1F53C8E2CA7}
{C4C01993-DB12-49D9-BDDB-C312042A6C29} = {72C65578-92A5-4E99-9779-27835B12B32F}
{BC3AE9E7-7B56-4894-9514-52A493A0D724} = {72C65578-92A5-4E99-9779-27835B12B32F}
{D473E69E-FDBE-4A8E-A835-7F380C8E6D86} = {72C65578-92A5-4E99-9779-27835B12B32F}
{3759E861-BA1E-498A-85CF-48A0BE6B00BB} = {72C65578-92A5-4E99-9779-27835B12B32F}
{42928B73-237A-4483-935E-E7FAAC52F288} = {72C65578-92A5-4E99-9779-27835B12B32F}
{015DD6B7-325F-45B5-B69E-CBD62DC6235A} = {72C65578-92A5-4E99-9779-27835B12B32F}
{BE13F278-C153-4CB8-A4EF-59BEFAE54CF8} = {72C65578-92A5-4E99-9779-27835B12B32F}
{568A28E1-6CAE-461C-8578-C28BEAC81945} = {72C65578-92A5-4E99-9779-27835B12B32F}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {199B1B96-4F56-4828-9531-813BA02DB282}
RESX_Rules = {"EnabledRules":[]}
RESX_NeutralResourcesLanguage = zh-Hans
SolutionGuid = {199B1B96-4F56-4828-9531-813BA02DB282}
EndGlobalSection
EndGlobal

View File

@@ -0,0 +1,26 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.9.34622.214
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Server", "ThingsGateway.Server\ThingsGateway.Server.csproj", "{22875EFB-DADF-4612-A572-33BCC092F644}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{22875EFB-DADF-4612-A572-33BCC092F644}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{22875EFB-DADF-4612-A572-33BCC092F644}.Debug|Any CPU.Build.0 = Debug|Any CPU
{22875EFB-DADF-4612-A572-33BCC092F644}.Release|Any CPU.ActiveCfg = Release|Any CPU
{22875EFB-DADF-4612-A572-33BCC092F644}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {199B1B96-4F56-4828-9531-813BA02DB282}
RESX_Rules = {"EnabledRules":[]}
RESX_NeutralResourcesLanguage = zh-Hans
EndGlobalSection
EndGlobal

View File

@@ -1,6 +1,6 @@
<Project>
<PropertyGroup>
<Version>7.2.0.60</Version>
<Version>7.2.3.12</Version>
</PropertyGroup>
<ItemGroup>