Compare commits

..

41 Commits
2.0.3 ... 2.0.8

Author SHA1 Message Date
Kimdiego2098
f1331b6a0c 2.0.8 2023-08-17 10:58:48 +08:00
Kimdiego2098
10d66b642b 调整readme,方便copy账密 2023-08-17 10:28:47 +08:00
Kimdiego2098
cd2310e4a8 优化变量地址输入分割方法;s7读写字符串添加默认编码 2023-08-17 09:50:10 +08:00
Kimdiego2098
1b399cf6b0 优化s7读写字符串,更改特殊方法为读写共用 2023-08-16 17:20:20 +08:00
Kimdiego2098
877445bc0a 修复写入返回结果时的解析,0XFF为成功 2023-08-16 15:56:25 +08:00
Kimdiego2098
9a5b345bde 修复s7字符串打包读取 2023-08-16 15:47:37 +08:00
Kimdiego2098
fc9e8ea7b3 修复s7字符串读取 2023-08-16 15:35:15 +08:00
Kimdiego2098
32be6fcfc1 主动释放锁 2023-08-16 14:56:47 +08:00
Kimdiego2098
49847236c2 OPCUA死区不再固定传入 2023-08-16 14:21:19 +08:00
Kimdiego2098
d8424443e6 fix: 优化JsonUtils中的获取type语句顺序 2023-08-16 08:59:18 +08:00
Kimdiego2098
f3b571ec3f OPC系列增加导出excel/导入系统双选项 2023-08-15 17:38:40 +08:00
Kimdiego2098
99318bb5d7 OPCUA采集设备写入字符串,不再需要传入双引号 2023-08-15 14:15:39 +08:00
Kimdiego2098
1aa154c9aa 历史数据/报警 线程初始化时不再阻塞 2023-08-15 12:12:00 +08:00
Kimdiego2098
c65d8a445b 更改ItemGroup Condition条件 2023-08-14 17:36:59 +08:00
Kimdiego2098
80f4f85570 传播token;
更新admin解决方案
2023-08-14 16:43:30 +08:00
Kimdiego2098
5beee43a6b 优化适配器解析代码 2023-08-14 14:21:00 +08:00
Kimdiego2098
8d6ae203a0 2.0.6 2023-08-13 16:57:12 +08:00
Kimdiego2098
4353479a5c OPCUACClient订阅初始化添加trycatch 2023-08-13 16:29:59 +08:00
Kimdiego2098
34d7687f9e Merge branch 'master' of https://gitee.com/dotnetchina/ThingsGateway 2023-08-13 15:53:33 +08:00
Kimdiego2098
b1dc3cf4af 更新opcuaclient,初始读取server类型改为动态读取 2023-08-13 15:50:47 +08:00
Diego2098
6a58b95933 更新nuget包 2023-08-12 12:09:50 +08:00
Kimdiego2098
d3badfd02b 修复批量令牌强退失效 2023-08-11 15:15:50 +08:00
Kimdiego2098
0098be057b 中间变量导入时添加标识 2023-08-11 14:15:22 +08:00
Kimdiego2098
6f972aa515 2.0.5 2023-08-10 18:23:40 +08:00
Kimdiego2098
7407ba6313 去除不需要的模板 2023-08-10 16:28:52 +08:00
Kimdiego2098
1c79de207b 修改详情模板 2023-08-10 11:23:18 +08:00
Kimdiego2098
257c79db92 添加自定义详情模板 2023-08-10 10:35:19 +08:00
Kimdiego2098
9d1934a308 底层修改:如果连接端口断开,停止等待返回 2023-08-10 10:35:01 +08:00
Kimdiego2098
d70f959902 上传DTO添加 单位 参数 2023-08-10 09:09:09 +08:00
Kimdiego2098
e4d810222f 调整调试页面 2023-08-09 23:45:53 +08:00
Kimdiego2098
bc1af4ae07 添加单文件发布配置 2023-08-09 15:12:55 +08:00
Kimdiego2098
6e688ef43f 去除页面多余引号 2023-08-09 13:45:27 +08:00
Kimdiego2098
f0fe1b23dc 更新2.0.4 2023-08-09 13:00:10 +08:00
Kimdiego2098
aaf2006401 更新opcua写入 2023-08-09 12:43:05 +08:00
Kimdiego2098
b821e26935 更新opcua调试页面 2023-08-09 12:01:23 +08:00
Kimdiego2098
7ae4287157 更新opcua 2023-08-09 11:47:04 +08:00
Kimdiego2098
c6fcc38a65 修复中间变量导入错误 2023-08-09 09:00:49 +08:00
Kimdiego2098
ab2d5c8853 修正错误注释 2023-08-08 18:09:23 +08:00
Kimdiego2098
5e557ff0bc opcua添加初始化连接错误重试 2023-08-08 18:04:24 +08:00
Kimdiego2098
918ca449a1 更新readme 2023-08-08 16:14:06 +08:00
Kimdiego2098
8e73368008 优化大量设备线程重启的耗时 2023-08-08 14:02:00 +08:00
109 changed files with 1157 additions and 752 deletions

View File

@@ -5,12 +5,11 @@
**NetCore** 跨平台边缘采集网关(工业设备采集) **NetCore** 跨平台边缘采集网关(工业设备采集)
**ThingsGateway** 存储库同时提供 [**设备采集驱动**](https://www.nuget.org/profiles/KimDiego) **ThingsGateway** 存储库同时提供 [**设备采集驱动**](https://www.nuget.org/packages?q=Tags%3A%22ThingsGateway%22)
**ThingsGateway** 存储库同时提供 **基于Blazor的权限框架** 查看 [**ThingsGateway.Admin**](https://gitee.com/dotnetchina/ThingsGateway/blob/master/framework/ThingsGateway.Admin.sln) **ThingsGateway** 存储库同时提供 **基于Blazor的权限框架** 查看 [**ThingsGateway.Admin**](https://gitee.com/dotnetchina/ThingsGateway/blob/master/framework/ThingsGateway.Admin.sln)
## 文档 ## 文档
[ThingsGateway](https://diego2098.gitee.io/thingsgateway-docs/) 文档。 [ThingsGateway](https://diego2098.gitee.io/thingsgateway-docs/) 文档。
@@ -23,7 +22,9 @@
[ThingsGateway演示地址](http://120.24.62.140:5000/) [ThingsGateway演示地址](http://120.24.62.140:5000/)
账户 : **superAdmin** 密码 : **111111** 账户 : **superAdmin**
密码 : **111111**
## 赞助 ## 赞助

View File

@@ -81,6 +81,15 @@ dotnet_diagnostic.CA2208.severity = none
# IDE0057: 使用范围运算符 # IDE0057: 使用范围运算符
dotnet_diagnostic.IDE0057.severity = none dotnet_diagnostic.IDE0057.severity = none
# IDE0056: 使用索引运算符
dotnet_diagnostic.IDE0056.severity = none
# CA1830: 最好使用 StringBuilder 的强类型 Append 和 Insert 方法重载
dotnet_diagnostic.CA1830.severity = none
# CA1847: 将字符型文本用于单个字符查找
dotnet_diagnostic.CA1847.severity = none
[*.vb] [*.vb]
#### 命名样式 #### #### 命名样式 ####

View File

@@ -1,7 +1,7 @@
<Project> <Project>
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>net6.0;net7.0</TargetFrameworks> <TargetFrameworks>net6.0;net7.0</TargetFrameworks>
<Version>2.0.3.0</Version> <Version>2.0.8.0</Version>
<Authors>Diego</Authors> <Authors>Diego</Authors>
<Product>ThingsGateway</Product> <Product>ThingsGateway</Product>
<Copyright>© 2023-present Diego</Copyright> <Copyright>© 2023-present Diego</Copyright>

View File

@@ -19,7 +19,7 @@ using ThingsGateway.Admin.Core;
namespace ThingsGateway.Admin.Application; namespace ThingsGateway.Admin.Application;
/// <summary> /// <summary>
/// 后台登录控制器 /// 文件下载
/// </summary> /// </summary>
[ApiDescriptionSettings(CateGoryConst.ThingsGatewayAdmin, Order = 200)] [ApiDescriptionSettings(CateGoryConst.ThingsGatewayAdmin, Order = 200)]
[Route("file")] [Route("file")]

View File

@@ -30,7 +30,7 @@
</member> </member>
<member name="T:ThingsGateway.Admin.Application.FileController"> <member name="T:ThingsGateway.Admin.Application.FileController">
<summary> <summary>
后台登录控制器 文件下载
</summary> </summary>
</member> </member>
<member name="M:ThingsGateway.Admin.Application.FileController.#ctor(ThingsGateway.Admin.Application.IFileService,ThingsGateway.Admin.Application.IOperateLogService,ThingsGateway.Admin.Application.IVisitLogService)"> <member name="M:ThingsGateway.Admin.Application.FileController.#ctor(ThingsGateway.Admin.Application.IFileService,ThingsGateway.Admin.Application.IOperateLogService,ThingsGateway.Admin.Application.IVisitLogService)">

View File

@@ -52,8 +52,8 @@ public class OpenApiSessionService : DbRepository<OpenApiUser>, IOpenApiSessionS
//获取该用户的verificat信息 //获取该用户的verificat信息
List<VerificatInfo> verificatInfos = await _verificatService.GetOpenApiVerificatIdAsync(input.Id); List<VerificatInfo> verificatInfos = await _verificatService.GetOpenApiVerificatIdAsync(input.Id);
//当前需要踢掉用户的verificat //当前需要踢掉用户的verificat
var deleteVerificats = verificatInfos.Where(it => input.VerificatIds.Contains(it.Id)).ToList(); var setVerificats = verificatInfos.Where(it => !input.VerificatIds.Contains(it.Id)).ToList();
await _verificatService.SetOpenApiVerificatIdAsync(input.Id, deleteVerificats);//如果还有verificat则更新verificat await _verificatService.SetOpenApiVerificatIdAsync(input.Id, setVerificats);//如果还有verificat则更新verificat
} }
/// <summary> /// <summary>

View File

@@ -52,12 +52,11 @@ public class SessionService : DbRepository<SysUser>, ISessionService
{ {
//获取该用户的verificat信息 //获取该用户的verificat信息
List<VerificatInfo> verificatInfos = await _verificatService.GetVerificatIdAsync(input.Id); List<VerificatInfo> verificatInfos = await _verificatService.GetVerificatIdAsync(input.Id);
//当前需要踢掉用户的verificat
List<VerificatInfo> deleteVerificats = new();
//踢掉包含verificat列表的verificat信息 //踢掉包含verificat列表的verificat信息
deleteVerificats = verificatInfos.Where(it => input.VerificatIds.Contains(it.Id)).ToList(); var setVerificats = verificatInfos.Where(it => !input.VerificatIds.Contains(it.Id)).ToList();
await _verificatService.SetVerificatIdAsync(input.Id, deleteVerificats);//如果还有verificat则更新verificat var deleteVerificats = verificatInfos.Where(it => input.VerificatIds.Contains(it.Id)).ToList();
await _verificatService.SetVerificatIdAsync(input.Id, setVerificats);//如果还有verificat则更新verificat
var message = "您已被强制下线!"; var message = "您已被强制下线!";
await _noticeService.LogoutAsync(input.Id, deleteVerificats, message);//通知下线 await _noticeService.LogoutAsync(input.Id, deleteVerificats, message);//通知下线

View File

@@ -15,7 +15,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Masa.Blazor" Version="1.0.1" /> <PackageReference Include="Masa.Blazor" Version="1.0.1" />
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="7.0.9" /> <PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="7.0.10" />
</ItemGroup> </ItemGroup>

View File

@@ -48,8 +48,7 @@
var controllerTypes = App.EffectiveTypes. var controllerTypes = App.EffectiveTypes.
Where(u => !u.IsInterface && !u.IsAbstract && u.IsClass Where(u => !u.IsInterface && !u.IsAbstract && u.IsClass
&& u.IsDefined(typeof(Microsoft.AspNetCore.Components.RouteAttribute), false)) && u.IsDefined(typeof(Microsoft.AspNetCore.Components.RouteAttribute), false))
.Where(it => it.Assembly != typeof(BlazorApp).Assembly) .Where(it => it.Assembly != typeof(BlazorApp).Assembly);
;
var assemblys = controllerTypes?.Select(it => it.Assembly)?.Distinct(); var assemblys = controllerTypes?.Select(it => it.Assembly)?.Distinct();
return assemblys; return assemblys;
} }

View File

@@ -189,7 +189,7 @@
} }
else else
{ {
if (ItemColWithDTTemplate!=null) if (ItemColWithDTTemplate != null)
{ {
@ItemColWithDTTemplate(context) @ItemColWithDTTemplate(context)
} }
@@ -220,7 +220,7 @@
} }
} }
} }
</ItemColContent> </ItemColContent>
@@ -310,16 +310,24 @@
{ {
string value = DetailModelPairs[item.Value]; string value = DetailModelPairs[item.Value];
{ {
<tr @key="item.Text"> @if (Detailemplate != null)
<td class="text-start table-minwidth"> {
@item.Text @Detailemplate((item,value))
</td> }
<td class="text-start "> else
<div style="word-break:break-all; white-space:pre-wrap;"> {
@value <tr @key="item.Text">
</div> <td class="text-start table-minwidth">
</td> @item.Text
</tr> </td>
<td class="text-start ">
<div style="word-break:break-all; white-space:pre-wrap;">
@value
</div>
</td>
</tr>
}
} }
} }
} }

View File

@@ -72,12 +72,6 @@ public partial class AppDataTable<TItem, SearchItem, AddItem, EditItem> : IAppDa
[Parameter] [Parameter]
public bool Dense { get; set; } public bool Dense { get; set; }
/// <summary>
/// 获得/设置 明细行模板
/// </summary>
[Parameter]
public RenderFragment<TItem> DetailRowTemplate { get; set; }
/// <summary> /// <summary>
/// 编辑项委托 /// 编辑项委托
/// </summary> /// </summary>
@@ -89,6 +83,11 @@ public partial class AppDataTable<TItem, SearchItem, AddItem, EditItem> : IAppDa
/// </summary> /// </summary>
[Parameter] [Parameter]
public RenderFragment<EditItem> EditTemplate { get; set; } public RenderFragment<EditItem> EditTemplate { get; set; }
/// <summary>
/// 获得/设置 详情模板
/// </summary>
[Parameter]
public RenderFragment<(DataTableHeader<TItem>, string)> Detailemplate { get; set; }
/// <summary> /// <summary>
/// 表头过滤返回DataTableHeader列表传输参数已包含全部初始表头与表头标题 /// 表头过滤返回DataTableHeader列表传输参数已包含全部初始表头与表头标题

View File

@@ -43,11 +43,6 @@
表格紧凑 表格紧凑
</summary> </summary>
</member> </member>
<member name="P:ThingsGateway.Admin.Blazor.Core.AppDataTable`4.DetailRowTemplate">
<summary>
获得/设置 明细行模板
</summary>
</member>
<member name="P:ThingsGateway.Admin.Blazor.Core.AppDataTable`4.EditCallAsync"> <member name="P:ThingsGateway.Admin.Blazor.Core.AppDataTable`4.EditCallAsync">
<summary> <summary>
编辑项委托 编辑项委托
@@ -58,6 +53,11 @@
获得/设置 编辑模板 获得/设置 编辑模板
</summary> </summary>
</member> </member>
<member name="P:ThingsGateway.Admin.Blazor.Core.AppDataTable`4.Detailemplate">
<summary>
获得/设置 详情模板
</summary>
</member>
<member name="P:ThingsGateway.Admin.Blazor.Core.AppDataTable`4.FilterHeaders"> <member name="P:ThingsGateway.Admin.Blazor.Core.AppDataTable`4.FilterHeaders">
<summary> <summary>
表头过滤返回DataTableHeader列表传输参数已包含全部初始表头与表头标题 表头过滤返回DataTableHeader列表传输参数已包含全部初始表头与表头标题

View File

@@ -12,10 +12,10 @@
<PackageReference Include="Furion.Extras.Authentication.JwtBearer" Version="4.8.8.40" /> <PackageReference Include="Furion.Extras.Authentication.JwtBearer" Version="4.8.8.40" />
<PackageReference Include="Furion.Extras.ObjectMapper.Mapster" Version="4.8.8.40" /> <PackageReference Include="Furion.Extras.ObjectMapper.Mapster" Version="4.8.8.40" />
<PackageReference Include="Furion.Pure" Version="4.8.8.40" /> <PackageReference Include="Furion.Pure" Version="4.8.8.40" />
<PackageReference Include="SqlSugarCore" Version="5.1.4.94" /> <PackageReference Include="SqlSugarCore" Version="5.1.4.99" />
<PackageReference Include="UAParser" Version="3.1.47" /> <PackageReference Include="UAParser" Version="3.1.47" />
<PackageReference Include="Yitter.IdGenerator" Version="1.0.14" /> <PackageReference Include="Yitter.IdGenerator" Version="1.0.14" />
<PackageReference Include="MiniExcel" Version="1.31.0" /> <PackageReference Include="MiniExcel" Version="1.31.2" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@@ -21,7 +21,7 @@ using ThingsGateway.Application;
namespace ThingsGateway.ApiController; namespace ThingsGateway.ApiController;
/// <summary> /// <summary>
/// 后台登录控制器 /// 文件下载
/// </summary> /// </summary>
[ApiDescriptionSettings(CateGoryConst.ThingsGatewayAdmin, Order = 200)] [ApiDescriptionSettings(CateGoryConst.ThingsGatewayAdmin, Order = 200)]
[Route("gatewayFile")] [Route("gatewayFile")]

View File

@@ -26,7 +26,7 @@
</member> </member>
<member name="T:ThingsGateway.ApiController.FileController"> <member name="T:ThingsGateway.ApiController.FileController">
<summary> <summary>
后台登录控制器 文件下载
</summary> </summary>
</member> </member>
<member name="M:ThingsGateway.ApiController.FileController.#ctor(ThingsGateway.Application.IRpcLogService,ThingsGateway.Application.IBackendLogService,ThingsGateway.Application.ICollectDeviceService,ThingsGateway.Application.IUploadDeviceService,ThingsGateway.Application.IVariableService)"> <member name="M:ThingsGateway.ApiController.FileController.#ctor(ThingsGateway.Application.IRpcLogService,ThingsGateway.Application.IBackendLogService,ThingsGateway.Application.ICollectDeviceService,ThingsGateway.Application.IUploadDeviceService,ThingsGateway.Application.IVariableService)">

View File

@@ -31,7 +31,7 @@ public class DeviceVariable : MemoryVariable
public virtual long DeviceId { get; set; } public virtual long DeviceId { get; set; }
/// <summary> /// <summary>
/// 写入表达式 /// 单位
/// </summary> /// </summary>
[SugarColumn(ColumnName = "Unit", ColumnDescription = "单位", Length = 200, IsNullable = true)] [SugarColumn(ColumnName = "Unit", ColumnDescription = "单位", Length = 200, IsNullable = true)]
[DataTable(Order = 3, IsShow = true, Sortable = true)] [DataTable(Order = 3, IsShow = true, Sortable = true)]
@@ -59,12 +59,7 @@ public class DeviceVariable : MemoryVariable
[DataTable(Order = 3, IsShow = true, Sortable = true)] [DataTable(Order = 3, IsShow = true, Sortable = true)]
public string VariableAddress { get; set; } public string VariableAddress { get; set; }
/// <summary>
/// 写入表达式
/// </summary>
[SugarColumn(ColumnName = "WriteExpressions", ColumnDescription = "写入表达式", Length = 200, IsNullable = true)]
[DataTable(Order = 7, IsShow = true, Sortable = true)]
public string WriteExpressions { get; set; }
/// <summary> /// <summary>
/// 是否中间变量 /// 是否中间变量

View File

@@ -51,7 +51,12 @@ public class MemoryVariable : BaseEntity
[SugarColumn(ColumnName = "ReadExpressions", ColumnDescription = "读取表达式", Length = 200, IsNullable = true)] [SugarColumn(ColumnName = "ReadExpressions", ColumnDescription = "读取表达式", Length = 200, IsNullable = true)]
[DataTable(Order = 7, IsShow = true, Sortable = true, CellClass = " table-text-truncate ")] [DataTable(Order = 7, IsShow = true, Sortable = true, CellClass = " table-text-truncate ")]
public string ReadExpressions { get; set; } public string ReadExpressions { get; set; }
/// <summary>
/// 写入表达式
/// </summary>
[SugarColumn(ColumnName = "WriteExpressions", ColumnDescription = "写入表达式", Length = 200, IsNullable = true)]
[DataTable(Order = 7, IsShow = true, Sortable = true)]
public string WriteExpressions { get; set; }
/// <summary> /// <summary>
/// 是否中间变量 /// 是否中间变量
/// </summary> /// </summary>

View File

@@ -20,6 +20,8 @@ public class VariableData
public long Id { get; set; } public long Id { get; set; }
/// <inheritdoc cref="MemoryVariable.Name"/> /// <inheritdoc cref="MemoryVariable.Name"/>
public string Name { get; set; } public string Name { get; set; }
/// <inheritdoc cref="DeviceVariable.Unit"/>
public string Unit { get; set; }
/// <inheritdoc cref="MemoryVariable.Description"/> /// <inheritdoc cref="MemoryVariable.Description"/>
public string Description { get; set; } public string Description { get; set; }
/// <inheritdoc cref="DeviceVariableRunTime.DeviceName"/> /// <inheritdoc cref="DeviceVariableRunTime.DeviceName"/>
@@ -36,7 +38,7 @@ public class VariableData
public bool IsOnline { get; set; } public bool IsOnline { get; set; }
/// <inheritdoc cref="MemoryVariable.ReadExpressions"/> /// <inheritdoc cref="MemoryVariable.ReadExpressions"/>
public string ReadExpressions { get; set; } public string ReadExpressions { get; set; }
/// <inheritdoc cref="DeviceVariable.WriteExpressions"/> /// <inheritdoc cref="MemoryVariable.WriteExpressions"/>
public string WriteExpressions { get; set; } public string WriteExpressions { get; set; }
/// <inheritdoc cref="DeviceVariable.IntervalTime"/> /// <inheritdoc cref="DeviceVariable.IntervalTime"/>
public int IntervalTime { get; set; } public int IntervalTime { get; set; }

View File

@@ -17,8 +17,6 @@ using Newtonsoft.Json.Linq;
using ThingsGateway.Foundation; using ThingsGateway.Foundation;
using ThingsGateway.Foundation.Serial; using ThingsGateway.Foundation.Serial;
using TouchSocket.Core;
namespace ThingsGateway.Application; namespace ThingsGateway.Application;
/// <summary> /// <summary>
/// <para></para> /// <para></para>

View File

@@ -55,7 +55,7 @@ public interface IVariableService : ITransient
/// <summary> /// <summary>
/// 导出 /// 导出
/// </summary> /// </summary>
Task<MemoryStream> ExportFileAsync(List<DeviceVariable> collectDeviceVariables = null); Task<MemoryStream> ExportFileAsync(List<DeviceVariable> collectDeviceVariables = null, string deviceName = null);
/// <summary> /// <summary>
/// 导出 /// 导出
/// </summary> /// </summary>

View File

@@ -318,7 +318,7 @@ public class VariableService : DbRepository<DeviceVariable>, IVariableService
/// <inheritdoc/> /// <inheritdoc/>
[OperDesc("导出采集变量表", IsRecordPar = false)] [OperDesc("导出采集变量表", IsRecordPar = false)]
public async Task<MemoryStream> ExportFileAsync(List<DeviceVariable> deviceVariables = null) public async Task<MemoryStream> ExportFileAsync(List<DeviceVariable> deviceVariables = null, string deviceName = null)
{ {
deviceVariables ??= await GetListAsync(a => !a.IsMemoryVariable); deviceVariables ??= await GetListAsync(a => !a.IsMemoryVariable);
@@ -343,7 +343,8 @@ public class VariableService : DbRepository<DeviceVariable>, IVariableService
); );
Dictionary<string, object> variableExport = new(); Dictionary<string, object> variableExport = new();
//变量实体没有包含设备名称,手动插入 //变量实体没有包含设备名称,手动插入
variableExport.Add(ExportHelpers.DeviceName, collectDeviceDicts[devData.DeviceId].Name); var devName = collectDeviceDicts.GetValueOrDefault(devData.DeviceId)?.Name ?? deviceName;
variableExport.Add(ExportHelpers.DeviceName, devName);
foreach (var item in data) foreach (var item in data)
{ {
@@ -445,7 +446,7 @@ public class VariableService : DbRepository<DeviceVariable>, IVariableService
try try
{ {
var variable = ((ExpandoObject)item).ConvertToEntity<DeviceVariable>(true); var variable = ((ExpandoObject)item).ConvertToEntity<DeviceVariable>(true);
variable.IsMemoryVariable = true;
//变量ID都需要手动补录 //变量ID都需要手动补录
variables.Add(variable); variables.Add(variable);
if (dbVariableDicts.TryGetValue(variable.Name, out var dbvar1)) if (dbVariableDicts.TryGetValue(variable.Name, out var dbvar1))

View File

@@ -360,7 +360,7 @@
</member> </member>
<member name="P:ThingsGateway.Application.DeviceVariable.Unit"> <member name="P:ThingsGateway.Application.DeviceVariable.Unit">
<summary> <summary>
写入表达式 单位
</summary> </summary>
</member> </member>
<member name="P:ThingsGateway.Application.DeviceVariable.IntervalTime"> <member name="P:ThingsGateway.Application.DeviceVariable.IntervalTime">
@@ -378,11 +378,6 @@
变量地址,可能带有额外的信息,比如<see cref="T:ThingsGateway.Foundation.DataFormat"/> ,以;分割 变量地址,可能带有额外的信息,比如<see cref="T:ThingsGateway.Foundation.DataFormat"/> ,以;分割
</summary> </summary>
</member> </member>
<member name="P:ThingsGateway.Application.DeviceVariable.WriteExpressions">
<summary>
写入表达式
</summary>
</member>
<member name="P:ThingsGateway.Application.DeviceVariable.IsMemoryVariable"> <member name="P:ThingsGateway.Application.DeviceVariable.IsMemoryVariable">
<summary> <summary>
是否中间变量 是否中间变量
@@ -524,6 +519,11 @@
读取表达式 读取表达式
</summary> </summary>
</member> </member>
<member name="P:ThingsGateway.Application.MemoryVariable.WriteExpressions">
<summary>
写入表达式
</summary>
</member>
<member name="P:ThingsGateway.Application.MemoryVariable.IsMemoryVariable"> <member name="P:ThingsGateway.Application.MemoryVariable.IsMemoryVariable">
<summary> <summary>
是否中间变量 是否中间变量
@@ -1791,6 +1791,9 @@
<member name="P:ThingsGateway.Application.VariableData.Name"> <member name="P:ThingsGateway.Application.VariableData.Name">
<inheritdoc cref="P:ThingsGateway.Application.MemoryVariable.Name"/> <inheritdoc cref="P:ThingsGateway.Application.MemoryVariable.Name"/>
</member> </member>
<member name="P:ThingsGateway.Application.VariableData.Unit">
<inheritdoc cref="P:ThingsGateway.Application.DeviceVariable.Unit"/>
</member>
<member name="P:ThingsGateway.Application.VariableData.Description"> <member name="P:ThingsGateway.Application.VariableData.Description">
<inheritdoc cref="P:ThingsGateway.Application.MemoryVariable.Description"/> <inheritdoc cref="P:ThingsGateway.Application.MemoryVariable.Description"/>
</member> </member>
@@ -1816,7 +1819,7 @@
<inheritdoc cref="P:ThingsGateway.Application.MemoryVariable.ReadExpressions"/> <inheritdoc cref="P:ThingsGateway.Application.MemoryVariable.ReadExpressions"/>
</member> </member>
<member name="P:ThingsGateway.Application.VariableData.WriteExpressions"> <member name="P:ThingsGateway.Application.VariableData.WriteExpressions">
<inheritdoc cref="P:ThingsGateway.Application.DeviceVariable.WriteExpressions"/> <inheritdoc cref="P:ThingsGateway.Application.MemoryVariable.WriteExpressions"/>
</member> </member>
<member name="P:ThingsGateway.Application.VariableData.IntervalTime"> <member name="P:ThingsGateway.Application.VariableData.IntervalTime">
<inheritdoc cref="P:ThingsGateway.Application.DeviceVariable.IntervalTime"/> <inheritdoc cref="P:ThingsGateway.Application.DeviceVariable.IntervalTime"/>
@@ -3172,7 +3175,7 @@
编辑变量 编辑变量
</summary> </summary>
</member> </member>
<member name="M:ThingsGateway.Application.IVariableService.ExportFileAsync(System.Collections.Generic.List{ThingsGateway.Application.DeviceVariable})"> <member name="M:ThingsGateway.Application.IVariableService.ExportFileAsync(System.Collections.Generic.List{ThingsGateway.Application.DeviceVariable},System.String)">
<summary> <summary>
导出 导出
</summary> </summary>
@@ -3267,7 +3270,7 @@
<member name="M:ThingsGateway.Application.VariableService.MemoryVariableExportFileAsync(System.Collections.Generic.List{ThingsGateway.Application.MemoryVariable})"> <member name="M:ThingsGateway.Application.VariableService.MemoryVariableExportFileAsync(System.Collections.Generic.List{ThingsGateway.Application.MemoryVariable})">
<inheritdoc/> <inheritdoc/>
</member> </member>
<member name="M:ThingsGateway.Application.VariableService.ExportFileAsync(System.Collections.Generic.List{ThingsGateway.Application.DeviceVariable})"> <member name="M:ThingsGateway.Application.VariableService.ExportFileAsync(System.Collections.Generic.List{ThingsGateway.Application.DeviceVariable},System.String)">
<inheritdoc/> <inheritdoc/>
</member> </member>
<member name="M:ThingsGateway.Application.VariableService.MemoryVariablePreviewAsync(Microsoft.AspNetCore.Components.Forms.IBrowserFile)"> <member name="M:ThingsGateway.Application.VariableService.MemoryVariablePreviewAsync(Microsoft.AspNetCore.Components.Forms.IBrowserFile)">
@@ -3561,6 +3564,11 @@
开始采集 开始采集
</summary> </summary>
</member> </member>
<member name="M:ThingsGateway.Application.CollectDeviceThread.BeforeStopThreadAsync">
<summary>
停止采集前提前取消Token
</summary>
</member>
<member name="M:ThingsGateway.Application.CollectDeviceThread.StopThreadAsync"> <member name="M:ThingsGateway.Application.CollectDeviceThread.StopThreadAsync">
<summary> <summary>
停止采集 停止采集
@@ -3934,6 +3942,11 @@
开始上传 开始上传
</summary> </summary>
</member> </member>
<member name="M:ThingsGateway.Application.UploadDeviceThread.BeforeStopThreadAsync">
<summary>
停止采集前提前取消Token
</summary>
</member>
<member name="M:ThingsGateway.Application.UploadDeviceThread.StopThreadAsync"> <member name="M:ThingsGateway.Application.UploadDeviceThread.StopThreadAsync">
<summary> <summary>
停止上传 停止上传

View File

@@ -66,7 +66,7 @@ public class AlarmWorker : BackgroundService
/// </summary> /// </summary>
public OperResult StatuString { get; set; } = new OperResult("初始化"); public OperResult StatuString { get; set; } = new OperResult("初始化");
private ConcurrentQueue<DeviceVariableRunTime> DeviceVariables { get; set; } = new(); private ConcurrentQueue<DeviceVariableRunTime> DeviceVariables { get; set; } = new();
private ConcurrentQueue<DeviceVariableRunTime> HisAlarmDeviceVariables { get; set; } = new(); private ConcurrentQueue<HistoryAlarm> HisAlarmDeviceVariables { get; set; } = new();
/// <summary> /// <summary>
/// 获取数据库链接 /// 获取数据库链接
/// </summary> /// </summary>
@@ -465,7 +465,7 @@ public class AlarmWorker : BackgroundService
OnAlarmChanged?.Invoke(item.Adapt<DeviceVariableRunTime>()); OnAlarmChanged?.Invoke(item.Adapt<DeviceVariableRunTime>());
if (!IsExited) if (!IsExited)
{ {
HisAlarmDeviceVariables.Enqueue(item); HisAlarmDeviceVariables.Enqueue(item.Adapt<HistoryAlarm>());
} }
if (eventEnum == EventEnum.Alarm) if (eventEnum == EventEnum.Alarm)
@@ -536,6 +536,7 @@ public class AlarmWorker : BackgroundService
HisAlarmTask = await Task.Factory.StartNew(async () => HisAlarmTask = await Task.Factory.StartNew(async () =>
{ {
_logger?.LogInformation($"历史报警线程开始"); _logger?.LogInformation($"历史报警线程开始");
await Task.Yield();//返回线程控制,不再阻塞
try try
{ {
await Task.Delay(500, stoppingToken.Token); await Task.Delay(500, stoppingToken.Token);
@@ -561,8 +562,14 @@ public class AlarmWorker : BackgroundService
} }
catch (Exception) catch (Exception)
{ {
if (stoppingToken.Token.IsCancellationRequested)
{
IsExited = true;
return;
}
try try
{ {
_logger.LogWarning("连接历史报警表失败,尝试初始化表");
sqlSugarClient.CodeFirst.InitTables(typeof(HistoryAlarm)); sqlSugarClient.CodeFirst.InitTables(typeof(HistoryAlarm));
isSuccess = true; isSuccess = true;
StatuString = OperResult.CreateSuccessResult(); StatuString = OperResult.CreateSuccessResult();
@@ -586,18 +593,20 @@ public class AlarmWorker : BackgroundService
//缓存值 //缓存值
var cacheData = await CacheDb.GetCacheData(); var cacheData = await CacheDb.GetCacheData();
var data = cacheData.SelectMany(a => a.CacheStr.FromJson<List<HistoryAlarm>>()).ToList(); if (cacheData.Count > 0)
try
{ {
var count = await sqlSugarClient.Insertable(data).ExecuteCommandAsync(stoppingToken.Token); var data = cacheData.SelectMany(a => a.CacheStr.FromJson<List<HistoryAlarm>>()).ToList();
await CacheDb.DeleteCacheData(cacheData.Select(a => a.Id).ToArray()); try
{
var count = await sqlSugarClient.Insertable(data).ExecuteCommandAsync(stoppingToken.Token);
await CacheDb.DeleteCacheData(cacheData.Select(a => a.Id).ToArray());
}
catch (Exception ex)
{
if (isSuccess)
_logger.LogWarning(ex, "写入历史报警失败");
}
} }
catch (Exception ex)
{
if (isSuccess)
_logger.LogWarning(ex, "写入历史报警失败");
}
if (stoppingToken.Token.IsCancellationRequested) if (stoppingToken.Token.IsCancellationRequested)
break; break;
@@ -605,16 +614,15 @@ public class AlarmWorker : BackgroundService
var list = HisAlarmDeviceVariables.ToListWithDequeue(); var list = HisAlarmDeviceVariables.ToListWithDequeue();
if (list.Count != 0) if (list.Count != 0)
{ {
var hisalarm = list.Adapt<List<HistoryAlarm>>();
////Sql保存 ////Sql保存
hisalarm.ForEach(it => list.ForEach(it =>
{ {
it.Id = YitIdHelper.NextId(); it.Id = YitIdHelper.NextId();
}); });
//插入 //插入
try try
{ {
await sqlSugarClient.Insertable(hisalarm).ExecuteCommandAsync(stoppingToken.Token); await sqlSugarClient.Insertable(list).ExecuteCommandAsync(stoppingToken.Token);
isSuccess = true; isSuccess = true;
} }
catch (Exception ex) catch (Exception ex)
@@ -622,7 +630,7 @@ public class AlarmWorker : BackgroundService
if (isSuccess) if (isSuccess)
_logger.LogWarning(ex, "写入历史报警失败"); _logger.LogWarning(ex, "写入历史报警失败");
var cacheDatas = hisalarm.ChunkTrivialBetter(500); var cacheDatas = list.ChunkTrivialBetter(500);
foreach (var a in cacheDatas) foreach (var a in cacheDatas)
{ {
await CacheDb.AddCacheData("", a.ToJson(), 50000); await CacheDb.AddCacheData("", a.ToJson(), 50000);

View File

@@ -269,6 +269,7 @@ public class CollectDeviceCore
{ {
isInitSuccess = false; isInitSuccess = false;
GlobalDeviceData.CollectDevices.RemoveWhere(it => it.Id == Device.Id); GlobalDeviceData.CollectDevices.RemoveWhere(it => it.Id == Device.Id);
easyLock.SafeDispose();
} }
} }
@@ -329,11 +330,6 @@ public class CollectDeviceCore
return ThreadRunReturn.Continue; return ThreadRunReturn.Continue;
} }
if (DeviceVariableSourceReads.Count == 0 && Device.DeviceVariableRunTimes.Where(a => string.IsNullOrEmpty(a.OtherMethod)).Any())
{
//无采集变量
return ThreadRunReturn.Continue;
}
if (token.IsCancellationRequested) if (token.IsCancellationRequested)
return ThreadRunReturn.Break; return ThreadRunReturn.Break;
@@ -533,7 +529,7 @@ public class CollectDeviceCore
if (!string.IsNullOrEmpty(methodResult.MethodStr)) if (!string.IsNullOrEmpty(methodResult.MethodStr))
{ {
string[] strs = methodResult.MethodStr?.Trim()?.Split(';'); string[] strs = methodResult.MethodStr?.Trim()?.TrimEnd(';').Split(';');
try try
{ {
int index = 0; int index = 0;
@@ -545,6 +541,8 @@ public class CollectDeviceCore
} }
else else
{ {
if (strs.Length <= index)
continue;
//得到对于的方法参数值 //得到对于的方法参数值
methodResult.MethodObj[i] = methodResult.Converter.ConvertFrom(strs[index], ps[i].ParameterType); methodResult.MethodObj[i] = methodResult.Converter.ConvertFrom(strs[index], ps[i].ParameterType);
index++; index++;
@@ -600,8 +598,8 @@ public class CollectDeviceCore
if (!string.IsNullOrEmpty(deviceVariableMethodSource.MethodStr) || !string.IsNullOrEmpty(value)) if (!string.IsNullOrEmpty(deviceVariableMethodSource.MethodStr) || !string.IsNullOrEmpty(value))
{ {
string[] strs1 = deviceVariableMethodSource.MethodStr?.Trim()?.Split(';'); string[] strs1 = deviceVariableMethodSource.MethodStr?.Trim()?.TrimEnd(';').Split(';');
string[] strs2 = value?.Trim()?.Split(';'); string[] strs2 = value?.Trim()?.TrimEnd(';').Split(';');
//通过分号分割,并且合并参数 //通过分号分割,并且合并参数
var strs = strs1?.SpliceArray(strs2); var strs = strs1?.SpliceArray(strs2);
int index = 0; int index = 0;
@@ -667,18 +665,25 @@ public class CollectDeviceCore
if (method.HasReturn && result != null && result.IsSuccess) if (method.HasReturn && result != null && result.IsSuccess)
{ {
var content = deviceVariableMethodSource.Converter.ConvertTo(result.Content?.ToString()?.Replace($"\0", "")); var content = deviceVariableMethodSource.Converter.ConvertTo(result.Content?.ToString()?.Replace($"\0", ""));
var operResult = deviceVariableMethodSource.DeviceVariable.SetValue(content); if (isRead)
if (!operResult.IsSuccess)
{ {
_logger?.LogWarning(operResult.Message, ToString()); var operResult = deviceVariableMethodSource.DeviceVariable.SetValue(content);
if (!operResult.IsSuccess)
{
_logger?.LogWarning(operResult.Message, ToString());
}
} }
} }
else else
{ {
var operResult = deviceVariableMethodSource.DeviceVariable.SetValue(null); if (isRead)
if (!operResult.IsSuccess)
{ {
_logger?.LogWarning(operResult.Message, ToString()); var operResult = deviceVariableMethodSource.DeviceVariable.SetValue(null);
if (!operResult.IsSuccess)
{
_logger?.LogWarning(operResult.Message, ToString());
}
} }
} }
return result; return result;

View File

@@ -62,6 +62,7 @@ public class CollectDeviceThread : IAsyncDisposable
public async ValueTask DisposeAsync() public async ValueTask DisposeAsync()
{ {
await StopThreadAsync(); await StopThreadAsync();
easyLock.SafeDispose();
CollectDeviceCores.Clear(); CollectDeviceCores.Clear();
} }
@@ -85,6 +86,29 @@ public class CollectDeviceThread : IAsyncDisposable
easyLock.Release(); easyLock.Release();
} }
} }
/// <summary>
/// 停止采集前提前取消Token
/// </summary>
public virtual async Task BeforeStopThreadAsync()
{
try
{
await easyLock.WaitAsync();
if (DeviceTask == null)
{
return;
}
foreach (var token in StoppingTokens)
{
token.Cancel();
}
}
finally
{
easyLock.Release();
}
}
/// <summary> /// <summary>
/// 停止采集 /// 停止采集
@@ -173,7 +197,10 @@ public class CollectDeviceThread : IAsyncDisposable
foreach (var device in CollectDeviceCores) foreach (var device in CollectDeviceCores)
{ {
try try
{//初始化成功才能执行 {
if (stoppingToken.IsCancellationRequested)
break;
//初始化成功才能执行
if (device.IsInitSuccess) if (device.IsInitSuccess)
{ {
//如果是共享通道类型,需要每次转换时切换适配器 //如果是共享通道类型,需要每次转换时切换适配器

View File

@@ -328,6 +328,18 @@ public class CollectDeviceWorker : BackgroundService
/// </summary> /// </summary>
private async Task RemoveAllDeviceThreadAsync() private async Task RemoveAllDeviceThreadAsync()
{ {
await CollectDeviceThreads.ParallelForEachAsync(async (deviceThread, token) =>
{
try
{
await deviceThread.BeforeStopThreadAsync();
}
catch (Exception ex)
{
_logger?.LogError(ex, deviceThread.ToString());
}
}, 10);
await CollectDeviceThreads.ParallelForEachAsync(async (deviceThread, token) => await CollectDeviceThreads.ParallelForEachAsync(async (deviceThread, token) =>
{ {
try try

View File

@@ -167,7 +167,7 @@ public class HistoryValueWorker : BackgroundService
HistoryValueTask = await Task.Factory.StartNew(async () => HistoryValueTask = await Task.Factory.StartNew(async () =>
{ {
_logger?.LogInformation($"历史数据线程开始"); _logger?.LogInformation($"历史数据线程开始");
IsExited = false; await Task.Yield();//返回线程控制,不再阻塞
try try
{ {
var result = await GetHisDbAsync(); var result = await GetHisDbAsync();
@@ -191,8 +191,14 @@ public class HistoryValueWorker : BackgroundService
} }
catch (Exception) catch (Exception)
{ {
if (stoppingToken.Token.IsCancellationRequested)
{
IsExited = true;
return;
}
try try
{ {
_logger.LogWarning("连接历史数据表失败,尝试初始化表");
sqlSugarClient.CodeFirst.InitTables(typeof(HistoryValue)); sqlSugarClient.CodeFirst.InitTables(typeof(HistoryValue));
LastIsSuccess = true; LastIsSuccess = true;
StatuString = OperResult.CreateSuccessResult(); StatuString = OperResult.CreateSuccessResult();
@@ -204,6 +210,7 @@ public class HistoryValueWorker : BackgroundService
_logger.LogWarning(ex, "连接历史数据库失败"); _logger.LogWarning(ex, "连接历史数据库失败");
} }
} }
IsExited = false;
while (!stoppingToken.Token.IsCancellationRequested) while (!stoppingToken.Token.IsCancellationRequested)
{ {
@@ -299,7 +306,6 @@ public class HistoryValueWorker : BackgroundService
LastIsSuccess = false; LastIsSuccess = false;
} }
} }
IsExited = true;
} }
} }
@@ -317,9 +323,11 @@ public class HistoryValueWorker : BackgroundService
IsExited = true; IsExited = true;
_logger?.LogError(ex, $"历史数据循环异常"); _logger?.LogError(ex, $"历史数据循环异常");
} }
IsExited = true;
} }
, TaskCreationOptions.LongRunning); , TaskCreationOptions.LongRunning);
} }
/// <summary> /// <summary>
/// 重启 /// 重启
/// </summary> /// </summary>

View File

@@ -225,7 +225,13 @@ public class UploadDeviceCore
{ {
_logger?.LogError(ex, $"{Device.Name} 释放失败"); _logger?.LogError(ex, $"{Device.Name} 释放失败");
} }
isInitSuccess = false; finally
{
isInitSuccess = false;
easyLock.SafeDispose();
}
} }
/// <summary> /// <summary>

View File

@@ -51,6 +51,7 @@ public class UploadDeviceThread : IAsyncDisposable
public async ValueTask DisposeAsync() public async ValueTask DisposeAsync()
{ {
await StopThreadAsync(); await StopThreadAsync();
easyLock.SafeDispose();
UploadDeviceCores.Clear(); UploadDeviceCores.Clear();
} }
@@ -74,6 +75,29 @@ public class UploadDeviceThread : IAsyncDisposable
easyLock.Release(); easyLock.Release();
} }
} }
/// <summary>
/// 停止采集前提前取消Token
/// </summary>
public virtual async Task BeforeStopThreadAsync()
{
try
{
await easyLock.WaitAsync();
if (DeviceTask == null)
{
return;
}
foreach (var token in StoppingTokens)
{
token.Cancel();
}
}
finally
{
easyLock.Release();
}
}
/// <summary> /// <summary>
/// 停止上传 /// 停止上传
@@ -152,7 +176,10 @@ public class UploadDeviceThread : IAsyncDisposable
foreach (var device in UploadDeviceCores) foreach (var device in UploadDeviceCores)
{ {
try try
{//初始化成功才能执行 {
if (stoppingToken.IsCancellationRequested)
break;
//初始化成功才能执行
if (device.IsInitSuccess) if (device.IsInitSuccess)
{ {

View File

@@ -239,6 +239,17 @@ public class UploadDeviceWorker : BackgroundService
/// </summary> /// </summary>
private async Task RemoveAllDeviceThreadAsync() private async Task RemoveAllDeviceThreadAsync()
{ {
await UploadDeviceThreads.ParallelForEachAsync(async (deviceThread, token) =>
{
try
{
await deviceThread.BeforeStopThreadAsync();
}
catch (Exception ex)
{
_logger?.LogError(ex, deviceThread.ToString());
}
}, 10);
await UploadDeviceThreads.ParallelForEachAsync(async (deviceThread, token) => await UploadDeviceThreads.ParallelForEachAsync(async (deviceThread, token) =>
{ {
try try

View File

@@ -102,44 +102,6 @@
} }
@foreach (var item in ImportPreviews)
{
<MSubheader Class="mt-2 font-weight-black">
@(
$"{item.Key},预计导入{item.Value.DataCount}条数据"
)
</MSubheader>
<MSubheader Class=@((item.Value.HasError?"mt-2 red--text":"mt-2 green--text"))>
@(
(item.Value.HasError ? "出现错误" : "验证成功")
)
</MSubheader>
<MVirtualScroll Context="item1" Height=300 OverscanCount=2 ItemSize="60" Items="item.Value.Results">
<ItemContent>
<MListItem>
<MListItemAction>
<MChip Class="ma-2">
@(
$"第{item1.row}行"
)
</MChip>
</MListItemAction>
<MListItemContent>
<MListItemTitle Class=@((item1.isSuccess?"green--text":"red--text"))>
<strong>@item1.resultString</strong>
</MListItemTitle>
</MListItemContent>
</MListItem>
<MDivider></MDivider>
</ItemContent>
</MVirtualScroll>
}
</div> </div>
<div class="mt-6"> <div class="mt-6">
<MButton Color="primary" Disabled=@ImportPreviews.Any(it=>it.Value.HasError) OnClick="()=>Step=3">下一步</MButton> <MButton Color="primary" Disabled=@ImportPreviews.Any(it=>it.Value.HasError) OnClick="()=>Step=3">下一步</MButton>

View File

@@ -127,7 +127,7 @@ public abstract class DriverDebugUIBase : ComponentBase, IDisposable
/// 导入设备 /// 导入设备
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
public async Task DownDeviceExportAsync(CollectDevice data) public async Task DeviceImportAsync(CollectDevice data)
{ {
try try
{ {
@@ -145,7 +145,7 @@ public abstract class DriverDebugUIBase : ComponentBase, IDisposable
/// 导入变量 /// 导入变量
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
public async Task DownDeviceExportAsync(List<DeviceVariable> data) public async Task DeviceVariableImportAsync(List<DeviceVariable> data)
{ {
try try
{ {
@@ -159,6 +159,7 @@ public abstract class DriverDebugUIBase : ComponentBase, IDisposable
} }
} }
/// <summary> /// <summary>
/// 导出 /// 导出
/// </summary> /// </summary>
@@ -187,6 +188,51 @@ public abstract class DriverDebugUIBase : ComponentBase, IDisposable
isDownExport = false; isDownExport = false;
} }
} }
/// <summary>
/// 导出到excel
/// </summary>
/// <returns></returns>
public async Task DownDeviceExportAsync(CollectDevice data)
{
try
{
isDownExport = true;
StateHasChanged();
using var memoryStream = await CollectDeviceService.ExportFileAsync(new List<CollectDevice>() { data });
memoryStream.Seek(0, SeekOrigin.Begin);
using var streamRef = new DotNetStreamReference(stream: memoryStream);
_helper ??= await JS.InvokeAsync<IJSObjectReference>("import", $"/_content/ThingsGateway.Admin.Blazor.Core/js/downloadFileFromStream.js");
await _helper.InvokeVoidAsync("downloadFileFromStream", $"设备导出{SysDateTimeExtensions.CurrentDateTime.ToFileDateTimeFormat()}.xlsx", streamRef);
}
finally
{
isDownExport = false;
}
}
/// <summary>
/// 导出到excel
/// </summary>
/// <returns></returns>
public async Task DownDeviceVariableExportAsync(List<DeviceVariable> data, string devName)
{
try
{
isDownExport = true;
StateHasChanged();
using var memoryStream = await VariableService.ExportFileAsync(data, devName);
memoryStream.Seek(0, SeekOrigin.Begin);
using var streamRef = new DotNetStreamReference(stream: memoryStream);
_helper ??= await JS.InvokeAsync<IJSObjectReference>("import", $"/_content/ThingsGateway.Admin.Blazor.Core/js/downloadFileFromStream.js");
await _helper.InvokeVoidAsync("downloadFileFromStream", $"变量导出{SysDateTimeExtensions.CurrentDateTime.ToFileDateTimeFormat()}.xlsx", streamRef);
}
finally
{
isDownExport = false;
}
}
/// <inheritdoc/> /// <inheritdoc/>
public void LogOut(TouchSocket.Core.LogLevel logLevel, object source, string message, Exception exception) public void LogOut(TouchSocket.Core.LogLevel logLevel, object source, string message, Exception exception)
{ {

View File

@@ -78,8 +78,7 @@ public partial class SerialClientPage
/// <inheritdoc/> /// <inheritdoc/>
protected override void OnInitialized() protected override void OnInitialized()
{ {
config?.Dispose(); config ??= new TouchSocketConfig();
config = new TouchSocketConfig();
var LogMessage = new TouchSocket.Core.LoggerGroup() { LogLevel = TouchSocket.Core.LogLevel.Trace }; var LogMessage = new TouchSocket.Core.LoggerGroup() { LogLevel = TouchSocket.Core.LogLevel.Trace };
LogMessage.AddLogger(new EasyLogger(LogOut) { LogLevel = TouchSocket.Core.LogLevel.Trace }); LogMessage.AddLogger(new EasyLogger(LogOut) { LogLevel = TouchSocket.Core.LogLevel.Trace });
config.ConfigureContainer(a => a.RegisterSingleton<ILog>(LogMessage)); config.ConfigureContainer(a => a.RegisterSingleton<ILog>(LogMessage));

View File

@@ -78,8 +78,7 @@ public partial class TcpClientPage
/// <returns></returns> /// <returns></returns>
public TcpClientEx GetTcpClient() public TcpClientEx GetTcpClient()
{ {
config?.Dispose(); config ??= new TouchSocketConfig();
config = new TouchSocketConfig();
var LogMessage = new TouchSocket.Core.LoggerGroup() { LogLevel = TouchSocket.Core.LogLevel.Trace }; var LogMessage = new TouchSocket.Core.LoggerGroup() { LogLevel = TouchSocket.Core.LogLevel.Trace };
LogMessage.AddLogger(new EasyLogger(LogOut) { LogLevel = TouchSocket.Core.LogLevel.Trace }); LogMessage.AddLogger(new EasyLogger(LogOut) { LogLevel = TouchSocket.Core.LogLevel.Trace });
config.ConfigureContainer(a => a.RegisterSingleton<ILog>(LogMessage)); config.ConfigureContainer(a => a.RegisterSingleton<ILog>(LogMessage));

View File

@@ -67,8 +67,7 @@ public partial class TcpServerPage
/// <returns></returns> /// <returns></returns>
public TcpService GetTcpServer() public TcpService GetTcpServer()
{ {
config?.Dispose(); config ??= new TouchSocketConfig();
config = new TouchSocketConfig();
var LogMessage = new TouchSocket.Core.LoggerGroup() { LogLevel = TouchSocket.Core.LogLevel.Trace }; var LogMessage = new TouchSocket.Core.LoggerGroup() { LogLevel = TouchSocket.Core.LogLevel.Trace };
LogMessage.AddLogger(new EasyLogger(LogOut) { LogLevel = TouchSocket.Core.LogLevel.Trace }); LogMessage.AddLogger(new EasyLogger(LogOut) { LogLevel = TouchSocket.Core.LogLevel.Trace });
config.ConfigureContainer(a => a.RegisterSingleton<ILog>(LogMessage)); config.ConfigureContainer(a => a.RegisterSingleton<ILog>(LogMessage));

View File

@@ -67,8 +67,7 @@ public partial class UdpSessionPage : IDisposable
/// <returns></returns> /// <returns></returns>
public UdpSession GetUdpSession() public UdpSession GetUdpSession()
{ {
config?.Dispose(); config ??= new TouchSocketConfig();
config = new TouchSocketConfig();
var LogMessage = new TouchSocket.Core.LoggerGroup() { LogLevel = TouchSocket.Core.LogLevel.Trace }; var LogMessage = new TouchSocket.Core.LoggerGroup() { LogLevel = TouchSocket.Core.LogLevel.Trace };
LogMessage.AddLogger(new EasyLogger(LogOut) { LogLevel = TouchSocket.Core.LogLevel.Trace }); LogMessage.AddLogger(new EasyLogger(LogOut) { LogLevel = TouchSocket.Core.LogLevel.Trace });
config.ConfigureContainer(a => a.RegisterSingleton<ILog>(LogMessage)); config.ConfigureContainer(a => a.RegisterSingleton<ILog>(LogMessage));

View File

@@ -62,6 +62,7 @@
<MTextField Dense Style="max-width:200px;" HideDetails=@("auto") Class="mx-2 my-1" @bind-Value="_collectDeviceGroupSearchName" <MTextField Dense Style="max-width:200px;" HideDetails=@("auto") Class="mx-2 my-1" @bind-Value="_collectDeviceGroupSearchName"
Outlined Label=@typeof(CollectDevice).GetDescription(nameof(CollectDevice.DeviceGroup)) /> Outlined Label=@typeof(CollectDevice).GetDescription(nameof(CollectDevice.DeviceGroup)) />
</MCardTitle> </MCardTitle>
<MTreeview Style=@($"height: calc(100vh - {BlazorResourceConst.DefaultHeight + 240}px; overflow-y:auto)") Dense TItem="string" TKey="string" ActiveChanged=@(async a=> <MTreeview Style=@($"height: calc(100vh - {BlazorResourceConst.DefaultHeight + 240}px; overflow-y:auto)") Dense TItem="string" TKey="string" ActiveChanged=@(async a=>
{ {
if(_collectDeviceGroup!=a.FirstOrDefault()) if(_collectDeviceGroup!=a.FirstOrDefault())

View File

@@ -199,7 +199,6 @@ public partial class DeviceStatusPage : IDisposable
// await MainLayout.StateHasChangedAsync(); // await MainLayout.StateHasChangedAsync();
// } // }
//} //}
private async Task RunTimerAsync() private async Task RunTimerAsync()
{ {
while (await _periodicTimer.WaitForNextTickAsync()) while (await _periodicTimer.WaitForNextTickAsync())

View File

@@ -180,8 +180,41 @@ else
} }
</ItemColTemplate> </ItemColTemplate>
<Detailemplate>
@{
switch (context.Item1.Value)
{
case nameof(DeviceVariable.DeviceId):
<tr @key="context.Item1.Text">
<td class="text-start table-minwidth">
@context.Item1.Text
</td>
<td class="text-start ">
<div style="word-break:break-all; white-space:pre-wrap;">
@(App.GetService<ICollectDeviceService>().GetNameById(context.Item2.ToLong()))
</div>
</td>
</tr>
break;
default:
<tr @key="context.Item1.Text">
<td class="text-start table-minwidth">
@context.Item1.Text
</td>
<td class="text-start ">
<div style="word-break:break-all; white-space:pre-wrap;">
@context.Item2
</div>
</td>
</tr>
break;
}
}
</Detailemplate>
</AppDataTable> </AppDataTable>
; ;
return renderFragment; return renderFragment;
} }

View File

@@ -61,6 +61,7 @@ else
</span> </span>
</LabelContent> </LabelContent>
</MTreeview> </MTreeview>
</MCard> </MCard>
</MCol> </MCol>
<MCol Md=10 Cols="12"> <MCol Md=10 Cols="12">
@@ -119,7 +120,38 @@ else
} }
</ItemColTemplate> </ItemColTemplate>
<Detailemplate>
@{
switch (context.Item1.Value)
{
case nameof(DeviceVariable.DeviceId):
<tr @key="context.Item1.Text">
<td class="text-start table-minwidth">
@context.Item1.Text
</td>
<td class="text-start ">
<div style="word-break:break-all; white-space:pre-wrap;">
@(App.GetService<ICollectDeviceService>().GetNameById(context.Item2.ToLong()))
</div>
</td>
</tr>
break;
default:
<tr @key="context.Item1.Text">
<td class="text-start table-minwidth">
@context.Item1.Text
</td>
<td class="text-start ">
<div style="word-break:break-all; white-space:pre-wrap;">
@context.Item2
</div>
</td>
</tr>
break;
}
}
</Detailemplate>
</AppDataTable> </AppDataTable>
; ;

View File

@@ -32,7 +32,7 @@
@layout MainLayout @layout MainLayout
<MRow NoGutters> <MRow NoGutters>
<MCol Md=@(IsShowTreeView?4:0)> <MCol Md=@(IsShowTreeView?3:0)>
<MCard Show=IsShowTreeView Style=@($"height: calc(100vh - {BlazorResourceConst.DefaultHeight}px);border-radius:0px;")> <MCard Show=IsShowTreeView Style=@($"height: calc(100vh - {BlazorResourceConst.DefaultHeight}px);border-radius:0px;")>
<MCardTitle> <MCardTitle>
<MTextField Dense Style="max-width:200px;" HideDetails=@("auto") Class="mx-2 my-1" @bind-Value="_searchName" Outlined Label=@typeof(CollectDevice).GetDescription(nameof(CollectDevice.PluginId)) /> <MTextField Dense Style="max-width:200px;" HideDetails=@("auto") Class="mx-2 my-1" @bind-Value="_searchName" Outlined Label=@typeof(CollectDevice).GetDescription(nameof(CollectDevice.PluginId)) />
@@ -62,7 +62,7 @@
</MTreeview> </MTreeview>
</MCard> </MCard>
</MCol> </MCol>
<MCol Md=@(IsShowTreeView?8:12)> <MCol Md=@(IsShowTreeView?9:12)>
<MCard Style=@($"height: calc(100vh - {BlazorResourceConst.DefaultHeight}px);border-radius:0px;")> <MCard Style=@($"height: calc(100vh - {BlazorResourceConst.DefaultHeight}px);border-radius:0px;")>
<MCard Class="mb-3 pa-2 text-h6" Elevation="0" Flat Rounded="false"> <MCard Class="mb-3 pa-2 text-h6" Elevation="0" Flat Rounded="false">
<MRow Align="AlignTypes.Center" Class="mt-1"> <MRow Align="AlignTypes.Center" Class="mt-1">

View File

@@ -130,7 +130,7 @@
<MCard Elevation="1" Class="ma-2"> <MCard Elevation="1" Class="ma-2">
<MCardSubtitle Class=@("text-h6")> <MCardSubtitle Class=@("text-h6")>
<div class="mt-1 d-flex align-center justify-space-between"> <div class="mt-1 d-flex align-center justify-space-between">
<span>"当前内存/磁盘信息</span> <span>当前内存/磁盘信息</span>
</div> </div>
</MCardSubtitle> </MCardSubtitle>
<MDivider></MDivider> <MDivider></MDivider>

View File

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

View File

@@ -96,7 +96,7 @@ public partial class MemoryVariablePage
Task<Dictionary<string, ImportPreviewOutputBase>> DeviceImportAsync(IBrowserFile file) Task<Dictionary<string, ImportPreviewOutputBase>> DeviceImportAsync(IBrowserFile file)
{ {
return VariableService.PreviewAsync(file); return VariableService.MemoryVariablePreviewAsync(file);
} }

View File

@@ -428,13 +428,13 @@
<member name="M:ThingsGateway.Application.DriverDebugUIBase.WriteAsync"> <member name="M:ThingsGateway.Application.DriverDebugUIBase.WriteAsync">
<inheritdoc/> <inheritdoc/>
</member> </member>
<member name="M:ThingsGateway.Application.DriverDebugUIBase.DownDeviceExportAsync(ThingsGateway.Application.CollectDevice)"> <member name="M:ThingsGateway.Application.DriverDebugUIBase.DeviceImportAsync(ThingsGateway.Application.CollectDevice)">
<summary> <summary>
导入设备 导入设备
</summary> </summary>
<returns></returns> <returns></returns>
</member> </member>
<member name="M:ThingsGateway.Application.DriverDebugUIBase.DownDeviceExportAsync(System.Collections.Generic.List{ThingsGateway.Application.DeviceVariable})"> <member name="M:ThingsGateway.Application.DriverDebugUIBase.DeviceVariableImportAsync(System.Collections.Generic.List{ThingsGateway.Application.DeviceVariable})">
<summary> <summary>
导入变量 导入变量
</summary> </summary>
@@ -447,6 +447,18 @@
<param name="values"></param> <param name="values"></param>
<returns></returns> <returns></returns>
</member> </member>
<member name="M:ThingsGateway.Application.DriverDebugUIBase.DownDeviceExportAsync(ThingsGateway.Application.CollectDevice)">
<summary>
导出到excel
</summary>
<returns></returns>
</member>
<member name="M:ThingsGateway.Application.DriverDebugUIBase.DownDeviceVariableExportAsync(System.Collections.Generic.List{ThingsGateway.Application.DeviceVariable},System.String)">
<summary>
导出到excel
</summary>
<returns></returns>
</member>
<member name="M:ThingsGateway.Application.DriverDebugUIBase.LogOut(TouchSocket.Core.LogLevel,System.Object,System.String,System.Exception)"> <member name="M:ThingsGateway.Application.DriverDebugUIBase.LogOut(TouchSocket.Core.LogLevel,System.Object,System.String,System.Exception)">
<inheritdoc/> <inheritdoc/>
</member> </member>

View File

@@ -30,7 +30,7 @@ public abstract class ReadWriteDevicesSerialBase : ReadWriteDevicesClientBase
public ReadWriteDevicesSerialBase(SerialClient serialClient) public ReadWriteDevicesSerialBase(SerialClient serialClient)
{ {
SerialClient = serialClient; SerialClient = serialClient;
WaitingClientEx = SerialClient.GetWaitingClientEx(new()); WaitingClientEx = SerialClient.GetWaitingClientEx(new() { BreakTrigger = true });
SerialClient.Connecting -= Connecting; SerialClient.Connecting -= Connecting;
SerialClient.Connected -= Connected; SerialClient.Connected -= Connected;

View File

@@ -26,7 +26,7 @@ public abstract class ReadWriteDevicesTcpClientBase : ReadWriteDevicesClientBase
public ReadWriteDevicesTcpClientBase(TcpClientEx tcpClient) public ReadWriteDevicesTcpClientBase(TcpClientEx tcpClient)
{ {
TcpClientEx = tcpClient; TcpClientEx = tcpClient;
WaitingClientEx = TcpClientEx.GetWaitingClientEx(new()); WaitingClientEx = TcpClientEx.GetWaitingClientEx(new() { BreakTrigger = true });
TcpClientEx.Connecting -= Connecting; TcpClientEx.Connecting -= Connecting;
TcpClientEx.Connected -= Connected; TcpClientEx.Connected -= Connected;
TcpClientEx.Disconnecting -= Disconnecting; TcpClientEx.Disconnecting -= Disconnecting;

View File

@@ -24,10 +24,8 @@ public abstract class ReadWriteDevicesUdpBase : ReadWriteDevicesClientBase
/// <inheritdoc cref="ReadWriteDevicesUdpBase"/> /// <inheritdoc cref="ReadWriteDevicesUdpBase"/>
public ReadWriteDevicesUdpBase(UdpSession udpSession) public ReadWriteDevicesUdpBase(UdpSession udpSession)
{ {
UdpSession = udpSession; UdpSession = udpSession;
WaitingClientEx = UdpSession.GetWaitingClientEx(new()); WaitingClientEx = UdpSession.GetWaitingClientEx(new() { BreakTrigger = true });
SetDataAdapter(); SetDataAdapter();
} }
/// <summary> /// <summary>

View File

@@ -159,7 +159,7 @@ public class TcpClientBaseEx : BaseSocket, ITcpClient
{ {
return; return;
} }
this.PluginsManager.Raise(nameof(ITcpDisconnectedPlguin.OnTcpDisconnected), this, e); this.PluginsManager.Raise(nameof(ITcpDisconnectedPlugin.OnTcpDisconnected), this, e);
} }
/// <summary> /// <summary>
@@ -346,6 +346,8 @@ public class TcpClientBaseEx : BaseSocket, ITcpClient
{ {
privateEasyLock.Release(); privateEasyLock.Release();
} }
privateEasyLock.SafeDispose();
base.Dispose(disposing); base.Dispose(disposing);
} }

View File

@@ -12,6 +12,8 @@
using ThingsGateway.Foundation.Serial;
namespace ThingsGateway.Foundation; namespace ThingsGateway.Foundation;
internal class WaitingClientEx<TClient> : DisposableObject, IWaitingClient<TClient> where TClient : IClient, IDefaultSender, ISender internal class WaitingClientEx<TClient> : DisposableObject, IWaitingClient<TClient> where TClient : IClient, IDefaultSender, ISender
@@ -52,6 +54,7 @@ internal class WaitingClientEx<TClient> : DisposableObject, IWaitingClient<TClie
{ {
this.Client = default; this.Client = default;
this.m_waitData.SafeDispose(); this.m_waitData.SafeDispose();
this.m_waitDataAsync.SafeDispose();
base.Dispose(disposing); base.Dispose(disposing);
} }
@@ -66,7 +69,11 @@ internal class WaitingClientEx<TClient> : DisposableObject, IWaitingClient<TClie
this.m_breaked = true; this.m_breaked = true;
this.Cancel(); this.Cancel();
} }
private void OnSerialClientDisconnected(ISerialClientBase client, DisconnectEventArgs e)
{
this.m_breaked = true;
this.Cancel();
}
private bool OnHandleRawBuffer(ByteBlock byteBlock) private bool OnHandleRawBuffer(ByteBlock byteBlock)
{ {
var responsedData = new ResponsedData(byteBlock.ToArray(), null, true); var responsedData = new ResponsedData(byteBlock.ToArray(), null, true);
@@ -121,7 +128,10 @@ internal class WaitingClientEx<TClient> : DisposableObject, IWaitingClient<TClie
{ {
tcpClient.Disconnected += this.OnDisconnected; tcpClient.Disconnected += this.OnDisconnected;
} }
if (this.WaitingOptions.BreakTrigger && this.Client is ISerialClientBase serialClient)
{
serialClient.Disconnected += this.OnSerialClientDisconnected;
}
if (this.WaitingOptions.AdapterFilter == AdapterFilter.AllAdapter || this.WaitingOptions.AdapterFilter == AdapterFilter.WaitAdapter) if (this.WaitingOptions.AdapterFilter == AdapterFilter.AllAdapter || this.WaitingOptions.AdapterFilter == AdapterFilter.WaitAdapter)
{ {
this.Client.OnHandleReceivedData += this.OnHandleReceivedData; this.Client.OnHandleReceivedData += this.OnHandleReceivedData;
@@ -179,7 +189,10 @@ internal class WaitingClientEx<TClient> : DisposableObject, IWaitingClient<TClie
{ {
tcpClient.Disconnected -= this.OnDisconnected; tcpClient.Disconnected -= this.OnDisconnected;
} }
if (this.WaitingOptions.BreakTrigger && this.Client is ISerialClientBase serialClient)
{
serialClient.Disconnected -= this.OnSerialClientDisconnected;
}
if (this.WaitingOptions.AdapterFilter == AdapterFilter.AllAdapter || this.WaitingOptions.AdapterFilter == AdapterFilter.WaitAdapter) if (this.WaitingOptions.AdapterFilter == AdapterFilter.AllAdapter || this.WaitingOptions.AdapterFilter == AdapterFilter.WaitAdapter)
{ {
this.Client.OnHandleReceivedData -= this.OnHandleReceivedData; this.Client.OnHandleReceivedData -= this.OnHandleReceivedData;
@@ -191,6 +204,8 @@ internal class WaitingClientEx<TClient> : DisposableObject, IWaitingClient<TClie
} }
} }
public ResponsedData SendThenResponse(byte[] buffer, int timeout = 1000 * 5, CancellationToken token = default) public ResponsedData SendThenResponse(byte[] buffer, int timeout = 1000 * 5, CancellationToken token = default)
{ {
return this.SendThenResponse(buffer, 0, buffer.Length, timeout, token); return this.SendThenResponse(buffer, 0, buffer.Length, timeout, token);
@@ -216,7 +231,10 @@ internal class WaitingClientEx<TClient> : DisposableObject, IWaitingClient<TClie
{ {
tcpClient.Disconnected += this.OnDisconnected; tcpClient.Disconnected += this.OnDisconnected;
} }
if (this.WaitingOptions.BreakTrigger && this.Client is ISerialClientBase serialClient)
{
serialClient.Disconnected += this.OnSerialClientDisconnected;
}
if (this.WaitingOptions.AdapterFilter == AdapterFilter.AllAdapter || this.WaitingOptions.AdapterFilter == AdapterFilter.WaitAdapter) if (this.WaitingOptions.AdapterFilter == AdapterFilter.AllAdapter || this.WaitingOptions.AdapterFilter == AdapterFilter.WaitAdapter)
{ {
this.Client.OnHandleReceivedData += this.OnHandleReceivedData; this.Client.OnHandleReceivedData += this.OnHandleReceivedData;
@@ -274,7 +292,10 @@ internal class WaitingClientEx<TClient> : DisposableObject, IWaitingClient<TClie
{ {
tcpClient.Disconnected -= this.OnDisconnected; tcpClient.Disconnected -= this.OnDisconnected;
} }
if (this.WaitingOptions.BreakTrigger && this.Client is ISerialClientBase serialClient)
{
serialClient.Disconnected -= this.OnSerialClientDisconnected;
}
if (this.WaitingOptions.AdapterFilter == AdapterFilter.AllAdapter || this.WaitingOptions.AdapterFilter == AdapterFilter.WaitAdapter) if (this.WaitingOptions.AdapterFilter == AdapterFilter.AllAdapter || this.WaitingOptions.AdapterFilter == AdapterFilter.WaitAdapter)
{ {
this.Client.OnHandleReceivedData -= this.OnHandleReceivedData; this.Client.OnHandleReceivedData -= this.OnHandleReceivedData;

View File

@@ -15,6 +15,17 @@ namespace ThingsGateway.Foundation.Extension;
/// <inheritdoc/> /// <inheritdoc/>
public static class OperResultExtensions public static class OperResultExtensions
{ {
/// <summary>
/// 复制信息,包含第一个泛型类
/// </summary>
public static OperResult<T1> Copy<T1, T2>(this OperResult<T1, T2> result)
{
OperResult<T1> result1 = new(result.ResultCode, result.Message)
{
Content = result.Content1
};
return result1;
}
#region Public Methods #region Public Methods
/// <summary> /// <summary>

View File

@@ -13,6 +13,7 @@
using System.IO.Ports; using System.IO.Ports;
using TouchSocket.Resources; using TouchSocket.Resources;
using TouchSocket.Sockets;
namespace ThingsGateway.Foundation.Serial; namespace ThingsGateway.Foundation.Serial;
@@ -172,7 +173,7 @@ public class SerialClientBase : BaseSerial, ISerialClient
{ {
return; return;
} }
this.PluginsManager.Raise(nameof(ITcpDisconnectedPlguin.OnTcpDisconnected), this, e); this.PluginsManager.Raise(nameof(ITcpDisconnectedPlugin.OnTcpDisconnected), this, e);
} }
private void PrivateOnDisconnecting(DisconnectEventArgs e) private void PrivateOnDisconnecting(DisconnectEventArgs e)
{ {

View File

@@ -45,4 +45,9 @@ public class SerialProperty
/// </summary> /// </summary>
[Description("停止位")] [Description("停止位")]
public StopBits StopBits { get; set; } = StopBits.One; public StopBits StopBits { get; set; } = StopBits.One;
/// <inheritdoc/>
public override string ToString()
{
return $"{PortName}[{BaudRate},{DataBits},{StopBits},{Parity}]";
}
} }

View File

@@ -13,7 +13,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="TouchSocket" Version="2.0.0-beta.120" /> <PackageReference Include="TouchSocket" Version="2.0.0-beta.138" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition="'$(TargetFramework)'!='net45'"> <ItemGroup Condition="'$(TargetFramework)'!='net45'">
<PackageReference Include="System.IO.Ports" Version="7.0.0" /> <PackageReference Include="System.IO.Ports" Version="7.0.0" />

View File

@@ -1313,6 +1313,11 @@
<member name="T:ThingsGateway.Foundation.Extension.OperResultExtensions"> <member name="T:ThingsGateway.Foundation.Extension.OperResultExtensions">
<inheritdoc/> <inheritdoc/>
</member> </member>
<member name="M:ThingsGateway.Foundation.Extension.OperResultExtensions.Copy``2(ThingsGateway.Foundation.OperResult{``0,``1})">
<summary>
复制信息,包含第一个泛型类
</summary>
</member>
<member name="M:ThingsGateway.Foundation.Extension.OperResultExtensions.Copy``1(ThingsGateway.Foundation.OperResult)"> <member name="M:ThingsGateway.Foundation.Extension.OperResultExtensions.Copy``1(ThingsGateway.Foundation.OperResult)">
<summary> <summary>
复制信息,不包含泛型类 复制信息,不包含泛型类
@@ -2288,6 +2293,9 @@
停止位 停止位
</summary> </summary>
</member> </member>
<member name="M:ThingsGateway.Foundation.Serial.SerialProperty.ToString">
<inheritdoc/>
</member>
<member name="T:ThingsGateway.Foundation.SerialPortExtensions"> <member name="T:ThingsGateway.Foundation.SerialPortExtensions">
<summary> <summary>
SocketExtension SocketExtension

View File

@@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\Foundataion\ThingsGateway.Foundation.Adapter.OPCUA\ThingsGateway.Foundation.Adapter.OPCUA.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,8 @@
<?xml version="1.0"?>
<doc>
<assembly>
<name>OPCUAClientDemo</name>
</assembly>
<members>
</members>
</doc>

View File

@@ -0,0 +1,26 @@
using ThingsGateway.Foundation.Adapter.OPCUA;
using TouchSocket.Core;
namespace ModbusDemo
{
internal class Program
{
static async Task Main(string[] args)
{
OPCUAClient oPCUAClient = new(new EasyLogger(a => Console.WriteLine(a)))
{
OPCNode = new()
{
OPCUrl = "opc.tcp://desktop-p5gb4iq:50001/StandardServer",
IsUseSecurity = true,
}
};
await oPCUAClient.ConnectAsync();
var testData1 = await oPCUAClient.ReadJTokenValueAsync(new[] { "ns=2;i=2897" });
await oPCUAClient.WriteNodeAsync("ns=2;i=2897", testData1.FirstOrDefault().Item3);
}
}
}

View File

@@ -1,7 +1,7 @@
<Project> <Project>
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>net6.0;net7.0</TargetFrameworks> <TargetFrameworks>net6.0;net7.0</TargetFrameworks>
<Version>2.0.3.0</Version> <Version>2.0.8.0</Version>
<Authors>Diego</Authors> <Authors>Diego</Authors>
<Product>ThingsGateway</Product> <Product>ThingsGateway</Product>
<Copyright>© 2023-present Diego</Copyright> <Copyright>© 2023-present Diego</Copyright>

View File

@@ -16,290 +16,294 @@ using ThingsGateway.Foundation.Extension.Bool;
using ThingsGateway.Foundation.Extension.Byte; using ThingsGateway.Foundation.Extension.Byte;
using ThingsGateway.Foundation.Extension.Generic; using ThingsGateway.Foundation.Extension.Generic;
namespace ThingsGateway.Foundation.Adapter.Modbus namespace ThingsGateway.Foundation.Adapter.Modbus;
internal class ModbusHelper
{ {
internal class ModbusHelper /// <summary>
/// 添加Crc16
/// </summary>
internal static byte[] AddCrc(byte[] command)
{ {
/// <summary> return EasyCRC16.CRC16(command);
/// 添加Crc16 }
/// </summary>
internal static byte[] AddCrc(byte[] command)
{
return EasyCRC16.CRC16(command);
}
/// <summary> /// <summary>
/// 添加ModbusTcp报文头 /// 添加ModbusTcp报文头
/// </summary> /// </summary>
internal static byte[] AddModbusTcpHead(byte[] modbus, ushort id) internal static byte[] AddModbusTcpHead(byte[] modbus, ushort id)
{ {
byte[] tcp = new byte[modbus.Length + 6]; byte[] tcp = new byte[modbus.Length + 6];
tcp[0] = BitConverter.GetBytes(id)[1]; tcp[0] = BitConverter.GetBytes(id)[1];
tcp[1] = BitConverter.GetBytes(id)[0]; tcp[1] = BitConverter.GetBytes(id)[0];
tcp[4] = BitConverter.GetBytes(modbus.Length)[1]; tcp[4] = BitConverter.GetBytes(modbus.Length)[1];
tcp[5] = BitConverter.GetBytes(modbus.Length)[0]; tcp[5] = BitConverter.GetBytes(modbus.Length)[0];
modbus.CopyTo(tcp, 6); modbus.CopyTo(tcp, 6);
return tcp; return tcp;
} }
/// <summary> /// <summary>
/// modbus地址格式说明 /// modbus地址格式说明
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
internal static string GetAddressDescription() internal static string GetAddressDescription()
{
StringBuilder stringBuilder = new();
stringBuilder.AppendLine("Modbus寄存器");
stringBuilder.AppendLine("线圈寄存器使用从 00001 开始的地址编号。");
stringBuilder.AppendLine("离散输入寄存器使用从 10001 开始的地址编号。");
stringBuilder.AppendLine("输入寄存器使用从 30001 开始的地址编号。");
stringBuilder.AppendLine("保持寄存器使用从 40001 开始的地址编号。");
stringBuilder.AppendLine("举例:");
stringBuilder.AppendLine("40001=>保持寄存器第一个寄存器");
stringBuilder.AppendLine("额外格式:");
stringBuilder.AppendLine("设备站号 比如40001;s=2; 代表设备地址为2的保持寄存器第一个寄存器");
stringBuilder.AppendLine("写入功能码 比如40001;w=16; 代表保持寄存器第一个寄存器写入值时采用0x10功能码而不是默认的0x06功能码");
return stringBuilder.ToString();
}
/// <summary>
/// 通过错误码来获取到对应的文本消息
/// </summary>
internal static string GetDescriptionByErrorCode(byte code)
{
return code switch
{ {
StringBuilder stringBuilder = new(); 1 => "不支持的功能码",
stringBuilder.AppendLine("Modbus寄存器"); 2 => "读取寄存器越界",
stringBuilder.AppendLine("线圈寄存器使用从 00001 开始的地址编号。"); 3 => "读取长度超限",
stringBuilder.AppendLine("离散输入寄存器使用从 10001 开始的地址编号。"); 4 => "读写异常",
stringBuilder.AppendLine("输入寄存器使用从 30001 开始的地址编号。"); _ => "未知错误",
stringBuilder.AppendLine("保持寄存器使用从 40001 开始的地址编号。"); };
stringBuilder.AppendLine("举例:"); }
stringBuilder.AppendLine("40001=>保持寄存器第一个寄存器");
stringBuilder.AppendLine("额外格式:");
stringBuilder.AppendLine("设备站号 比如40001;s=2; 代表设备地址为2的保持寄存器第一个寄存器");
stringBuilder.AppendLine("写入功能码 比如40001;w=16; 代表保持寄存器第一个寄存器写入值时采用0x10功能码而不是默认的0x06功能码");
return stringBuilder.ToString();
}
/// <summary>
/// 通过错误码来获取到对应的文本消息
/// </summary>
internal static string GetDescriptionByErrorCode(byte code)
{
return code switch
{
1 => "不支持的功能码",
2 => "读取寄存器越界",
3 => "读取长度超限",
4 => "读写异常",
_ => "未知错误",
};
}
/// <summary> /// <summary>
/// 获取modbus数据区内容返回数据需去除Crc和报文头例如01 03 02 00 01发送数据需报文头 /// 获取modbus数据区内容返回数据需去除Crc和报文头例如01 03 02 00 01发送数据需报文头
/// </summary> /// </summary>
/// <param name="send">发送数据</param> /// <param name="send">发送数据</param>
/// <param name="response">返回数据</param> /// <param name="response">返回数据</param>
/// <returns></returns> /// <returns></returns>
internal static OperResult<byte[]> GetModbusData(byte[] send, byte[] response) internal static OperResult<byte[], FilterResult> GetModbusData(byte[] send, byte[] response)
{
try
{ {
try if (response.Length < 3)
{ return new OperResult<byte[], FilterResult>("数据长度不足" + response.ToHexString()) { Content2 = FilterResult.Cache };
if (response[1] >= 0x80)//错误码
return new OperResult<byte[]>(GetDescriptionByErrorCode(response[2]));
if (send.Length == 0)
{
var result = OperResult.CreateSuccessResult(GenericExtensions.ArrayRemoveBegin(response, 3));
result.Message = "接收数据正确,但主机并没有主动请求数据";
result.ResultCode = ResultCode.Canceled;
return result;
}
if (send[0] != response[0])
return new OperResult<byte[]>(string.Format("站号不一致", send[0], response[0]));
if (send[1] != response[1])
return new OperResult<byte[]>(response) { Message = "功能码不一致" };
if (response.Length > 3)
return OperResult.CreateSuccessResult(GenericExtensions.ArrayRemoveBegin(response, 3));
else
return new OperResult<byte[]>(response) { Message = "数据长度为0" };
}
catch (Exception ex)
{
return new OperResult<byte[]>(ex.Message);
}
}
/// <summary>
/// 去除Crc返回modbus数据区
/// </summary>
/// <param name="send"></param>
/// <param name="response"></param>
/// <param name="crcCheck"></param>
/// <returns></returns>
internal static OperResult<byte[]> GetModbusRtuData(byte[] send, byte[] response, bool crcCheck = true)
{
if (response.Length < 5)
return new OperResult<byte[]>("数据长度不足" + response.ToHexString());
if (crcCheck && !EasyCRC16.CheckCRC16(response))
return new OperResult<byte[]>("Crc校验失败" + DataTransUtil.ByteToHexString(response, ' '));
return GetModbusData(send, response.RemoveLast(2));
}
/// <summary>
/// 获取读取报文
/// </summary>
internal static OperResult<byte[]> GetReadModbusCommand(string address, int length, byte station)
{
try
{
ModbusAddress mAddress = new(address, station);
return GetReadModbusCommand(mAddress, length);
}
catch (Exception ex)
{
return new OperResult<byte[]>(ex.Message);
}
}
/// <summary>
/// 获取写入布尔量报文,根据地址识别功能码
/// </summary>
internal static OperResult<byte[]> GetWriteBoolModbusCommand(string address, bool[] values, byte station)
{
try
{
ModbusAddress mAddress = new(address, station);
//功能码或实际长度
if (values?.Length > 1 || mAddress.WriteFunction == 15)
return GetWriteBoolModbusCommand(mAddress, values, values.Length);
else
return GetWriteBoolModbusCommand(address, values[0], station);
}
catch (Exception ex)
{
return new OperResult<byte[]>(ex.Message);
}
}
/// <summary>
/// 获取写入字报文,根据地址识别功能码
/// </summary>
internal static OperResult<byte[]> GetWriteModbusCommand(string address, byte[] value, byte station)
{
try
{
ModbusAddress mAddress = new(address, station);
//功能码或实际长度
if (value?.Length > 2 || mAddress.WriteFunction == 16)
return GetWriteModbusCommand(mAddress, value);
else
return GetWriteOneModbusCommand(mAddress, value);
} if (response[1] >= 0x80)//错误码
catch (Exception ex) return new OperResult<byte[], FilterResult>(GetDescriptionByErrorCode(response[2])) { Content2 = FilterResult.Success };
if ((response.Length < response[2] + 3))
return new OperResult<byte[], FilterResult>("数据长度不足" + response.ToHexString()) { Content2 = FilterResult.Cache };
if (send.Length == 0)
{ {
return new OperResult<byte[]>(ex.Message); var result = OperResult.CreateSuccessResult(GenericExtensions.ArrayRemoveBegin(response, 3), FilterResult.Success);
result.Message = "接收数据正确,但主机并没有主动请求数据";
return result;
} }
if (send[0] != response[0])
return new OperResult<byte[], FilterResult>(string.Format("站号不一致", send[0], response[0])) { Content2 = FilterResult.Success };
if (send[1] != response[1])
return new OperResult<byte[], FilterResult>() { Message = "功能码不一致", Content2 = FilterResult.Success };
return OperResult.CreateSuccessResult(GenericExtensions.ArrayRemoveBegin(response, 3), FilterResult.Success);
} }
/// <summary> catch (Exception ex)
/// 获取读取报文
/// </summary>
private static OperResult<byte[]> GetReadModbusCommand(ModbusAddress mAddress, int length)
{ {
byte[] array = new byte[6] return new OperResult<byte[], FilterResult>(ex.Message) { Content2 = FilterResult.Success };
{
(byte) mAddress.Station,
(byte) mAddress.ReadFunction,
BitConverter.GetBytes(mAddress.AddressStart)[1],
BitConverter.GetBytes(mAddress.AddressStart)[0],
BitConverter.GetBytes(length)[1],
BitConverter.GetBytes(length)[0]
};
return OperResult.CreateSuccessResult(array);
} }
}
/// <summary>
/// 去除Crc返回modbus数据区
/// </summary>
/// <param name="send"></param>
/// <param name="response"></param>
/// <param name="crcCheck"></param>
/// <returns></returns>
internal static OperResult<byte[], FilterResult> GetModbusRtuData(byte[] send, byte[] response, bool crcCheck = true)
{
if (response.Length < 3)
return new OperResult<byte[], FilterResult>("数据长度不足" + response.ToHexString()) { Content2 = FilterResult.Cache };
/// <summary> if (crcCheck && !EasyCRC16.CheckCRC16(response))
/// 获取05写入布尔量报文 return new OperResult<byte[], FilterResult>("Crc校验失败" + DataTransUtil.ByteToHexString(response, ' ')) { Content2 = FilterResult.Success };
/// </summary> return GetModbusData(send, response.RemoveLast(2));
private static OperResult<byte[]> GetWriteBoolModbusCommand(string address, bool value, byte station) }
/// <summary>
/// 获取读取报文
/// </summary>
internal static OperResult<byte[]> GetReadModbusCommand(string address, int length, byte station)
{
try
{ {
try ModbusAddress mAddress = new(address, station);
{ return GetReadModbusCommand(mAddress, length);
if (address.IndexOf('.') <= 0)
{
ModbusAddress mAddress = new(address, station);
return GetWriteBoolModbusCommand(mAddress, value);
}
return new("不支持写入字寄存器的某一位");
}
catch (Exception ex)
{
return new OperResult<byte[]>(ex.Message);
}
} }
catch (Exception ex)
/// <summary> {
/// 获取05写入布尔量报文 return new OperResult<byte[]>(ex.Message);
/// </summary> }
private static OperResult<byte[]> GetWriteBoolModbusCommand(ModbusAddress mAddress, bool value) }
/// <summary>
/// 获取写入布尔量报文,根据地址识别功能码
/// </summary>
internal static OperResult<byte[]> GetWriteBoolModbusCommand(string address, bool[] values, byte station)
{
try
{
ModbusAddress mAddress = new(address, station);
//功能码或实际长度
if (values?.Length > 1 || mAddress.WriteFunction == 15)
return GetWriteBoolModbusCommand(mAddress, values, values.Length);
else
return GetWriteBoolModbusCommand(address, values[0], station);
}
catch (Exception ex)
{
return new OperResult<byte[]>(ex.Message);
}
}
/// <summary>
/// 获取写入字报文,根据地址识别功能码
/// </summary>
internal static OperResult<byte[]> GetWriteModbusCommand(string address, byte[] value, byte station)
{
try
{
ModbusAddress mAddress = new(address, station);
//功能码或实际长度
if (value?.Length > 2 || mAddress.WriteFunction == 16)
return GetWriteModbusCommand(mAddress, value);
else
return GetWriteOneModbusCommand(mAddress, value);
}
catch (Exception ex)
{
return new OperResult<byte[]>(ex.Message);
}
}
/// <summary>
/// 获取读取报文
/// </summary>
private static OperResult<byte[]> GetReadModbusCommand(ModbusAddress mAddress, int length)
{
byte[] array = new byte[6]
{ {
byte[] array = new byte[6]
{
(byte) mAddress.Station, (byte) mAddress.Station,
(byte)5, (byte) mAddress.ReadFunction,
BitConverter.GetBytes(mAddress.AddressStart)[1], BitConverter.GetBytes(mAddress.AddressStart)[1],
BitConverter.GetBytes(mAddress.AddressStart)[0], BitConverter.GetBytes(mAddress.AddressStart)[0],
0, BitConverter.GetBytes(length)[1],
0 BitConverter.GetBytes(length)[0]
}; };
if (value)
{
array[4] = 0xFF;
array[5] = 0;
}
else
{
array[4] = 0;
array[5] = 0;
}
return OperResult.CreateSuccessResult(array);
}
/// <summary> return OperResult.CreateSuccessResult(array);
/// 获取15写入布尔量报文 }
/// </summary>
private static OperResult<byte[]> GetWriteBoolModbusCommand(ModbusAddress mAddress, bool[] values, int length) /// <summary>
/// 获取05写入布尔量报文
/// </summary>
private static OperResult<byte[]> GetWriteBoolModbusCommand(string address, bool value, byte station)
{
try
{ {
try if (address.IndexOf('.') <= 0)
{ {
byte[] numArray1 = values.BoolArrayToByte(); ModbusAddress mAddress = new(address, station);
byte[] numArray2 = new byte[7 + numArray1.Length]; return GetWriteBoolModbusCommand(mAddress, value);
numArray2[0] = (byte)mAddress.Station;
numArray2[1] = (byte)15;
numArray2[2] = BitConverter.GetBytes(mAddress.AddressStart)[1];
numArray2[3] = BitConverter.GetBytes(mAddress.AddressStart)[0];
numArray2[4] = (byte)(length / 256);
numArray2[5] = (byte)(length % 256);
numArray2[6] = (byte)numArray1.Length;
numArray1.CopyTo(numArray2, 7);
return OperResult.CreateSuccessResult(numArray2);
}
catch (Exception ex)
{
return new OperResult<byte[]>(ex.Message);
} }
return new("不支持写入字寄存器的某一位");
} }
catch (Exception ex)
/// <summary>
/// 获取16写入字报文
/// </summary>
private static OperResult<byte[]> GetWriteModbusCommand(ModbusAddress mAddress, byte[] values)
{ {
byte[] numArray = new byte[7 + values.Length]; return new OperResult<byte[]>(ex.Message);
numArray[0] = (byte)mAddress.Station;
numArray[1] = (byte)16;
numArray[2] = BitConverter.GetBytes(mAddress.AddressStart)[1];
numArray[3] = BitConverter.GetBytes(mAddress.AddressStart)[0];
numArray[4] = (byte)(values.Length / 2 / 256);
numArray[5] = (byte)(values.Length / 2 % 256);
numArray[6] = (byte)values.Length;
values.CopyTo(numArray, 7);
return OperResult.CreateSuccessResult(numArray);
} }
}
/// <summary> /// <summary>
/// 获取6写入字报文 /// 获取05写入布尔量报文
/// </summary> /// </summary>
private static OperResult<byte[]> GetWriteOneModbusCommand(ModbusAddress mAddress, byte[] values) private static OperResult<byte[]> GetWriteBoolModbusCommand(ModbusAddress mAddress, bool value)
{
byte[] array = new byte[6]
{ {
byte[] numArray = new byte[4 + values.Length]; (byte) mAddress.Station,
numArray[0] = (byte)mAddress.Station; (byte)5,
numArray[1] = (byte)6; BitConverter.GetBytes(mAddress.AddressStart)[1],
numArray[2] = BitConverter.GetBytes(mAddress.AddressStart)[1]; BitConverter.GetBytes(mAddress.AddressStart)[0],
numArray[3] = BitConverter.GetBytes(mAddress.AddressStart)[0]; 0,
values.CopyTo(numArray, 4); 0
return OperResult.CreateSuccessResult(numArray); };
if (value)
{
array[4] = 0xFF;
array[5] = 0;
} }
else
{
array[4] = 0;
array[5] = 0;
}
return OperResult.CreateSuccessResult(array);
}
/// <summary>
/// 获取15写入布尔量报文
/// </summary>
private static OperResult<byte[]> GetWriteBoolModbusCommand(ModbusAddress mAddress, bool[] values, int length)
{
try
{
byte[] numArray1 = values.BoolArrayToByte();
byte[] numArray2 = new byte[7 + numArray1.Length];
numArray2[0] = (byte)mAddress.Station;
numArray2[1] = (byte)15;
numArray2[2] = BitConverter.GetBytes(mAddress.AddressStart)[1];
numArray2[3] = BitConverter.GetBytes(mAddress.AddressStart)[0];
numArray2[4] = (byte)(length / 256);
numArray2[5] = (byte)(length % 256);
numArray2[6] = (byte)numArray1.Length;
numArray1.CopyTo(numArray2, 7);
return OperResult.CreateSuccessResult(numArray2);
}
catch (Exception ex)
{
return new OperResult<byte[]>(ex.Message);
}
}
/// <summary>
/// 获取16写入字报文
/// </summary>
private static OperResult<byte[]> GetWriteModbusCommand(ModbusAddress mAddress, byte[] values)
{
byte[] numArray = new byte[7 + values.Length];
numArray[0] = (byte)mAddress.Station;
numArray[1] = (byte)16;
numArray[2] = BitConverter.GetBytes(mAddress.AddressStart)[1];
numArray[3] = BitConverter.GetBytes(mAddress.AddressStart)[0];
numArray[4] = (byte)(values.Length / 2 / 256);
numArray[5] = (byte)(values.Length / 2 % 256);
numArray[6] = (byte)values.Length;
values.CopyTo(numArray, 7);
return OperResult.CreateSuccessResult(numArray);
} }
/// <summary>
/// 获取6写入字报文
/// </summary>
private static OperResult<byte[]> GetWriteOneModbusCommand(ModbusAddress mAddress, byte[] values)
{
byte[] numArray = new byte[4 + values.Length];
numArray[0] = (byte)mAddress.Station;
numArray[1] = (byte)6;
numArray[2] = BitConverter.GetBytes(mAddress.AddressStart)[1];
numArray[3] = BitConverter.GetBytes(mAddress.AddressStart)[0];
values.CopyTo(numArray, 4);
return OperResult.CreateSuccessResult(numArray);
}
} }

View File

@@ -82,12 +82,12 @@ public class ModbusRtu : ReadWriteDevicesSerialBase
/// <inheritdoc/> /// <inheritdoc/>
public override void SetDataAdapter() public override void SetDataAdapter()
{ {
ModbusRtuDataHandleAdapter DataHandleAdapter = new() ModbusRtuDataHandleAdapter dataHandleAdapter = new()
{ {
Crc16CheckEnable = Crc16CheckEnable, Crc16CheckEnable = Crc16CheckEnable,
CacheTimeout = TimeSpan.FromMilliseconds(CacheTimeout) CacheTimeout = TimeSpan.FromMilliseconds(CacheTimeout)
}; };
SerialClient.SetDataHandlingAdapter(DataHandleAdapter); SerialClient.SetDataHandlingAdapter(dataHandleAdapter);
} }
/// <inheritdoc/> /// <inheritdoc/>

View File

@@ -48,47 +48,14 @@ public class ModbusRtuDataHandleAdapter : ReadWriteDevicesTcpDataHandleAdapter<M
{ {
request.ResultCode = result.ResultCode; request.ResultCode = result.ResultCode;
request.Message = result.Message; request.Message = result.Message;
request.Content = result.Content; request.Content = result.Content1;
return FilterResult.Success;
} }
else else
{ {
if (response.Length <= 1) request.ResultCode = result.ResultCode;
{ request.Message = result.Message;
request.ResultCode = result.ResultCode;
request.Message = result.Message;
request.Content = result.Content;
//如果长度不足,返回缓存
return FilterResult.Cache;
}
if (!(response[1] <= 0x10))
{
request.ResultCode = result.ResultCode;
request.Message = result.Message;
request.Content = result.Content;
//功能码不对,返回放弃
return FilterResult.Success;
}
else
{
if ((response.Length > response[2] + 4))
{
request.ResultCode = result.ResultCode;
request.Message = result.Message;
request.Content = result.Content;
//如果长度已经超了,说明这段报文已经不能继续解析了,直接返回放弃
return FilterResult.Success;
}
else
{
request.ResultCode = result.ResultCode;
request.Message = result.Message;
request.Content = result.Content;
//否则返回缓存
return FilterResult.Cache;
}
}
} }
return result.Content2;
} }
} }

View File

@@ -78,12 +78,12 @@ public class ModbusRtuOverTcp : ReadWriteDevicesTcpClientBase
/// <inheritdoc/> /// <inheritdoc/>
public override void SetDataAdapter() public override void SetDataAdapter()
{ {
ModbusRtuDataHandleAdapter DataHandleAdapter = new() ModbusRtuDataHandleAdapter dataHandleAdapter = new()
{ {
Crc16CheckEnable = Crc16CheckEnable, Crc16CheckEnable = Crc16CheckEnable,
CacheTimeout = TimeSpan.FromMilliseconds(CacheTimeout) CacheTimeout = TimeSpan.FromMilliseconds(CacheTimeout)
}; };
TcpClientEx.SetDataHandlingAdapter(DataHandleAdapter); TcpClientEx.SetDataHandlingAdapter(dataHandleAdapter);
} }
/// <inheritdoc/> /// <inheritdoc/>

View File

@@ -72,11 +72,14 @@ public class ModbusRtuOverUdp : ReadWriteDevicesUdpBase
/// <inheritdoc/> /// <inheritdoc/>
public override void SetDataAdapter() public override void SetDataAdapter()
{ {
ModbusRtuOverUdpDataHandleAdapter DataHandleAdapter = new() ModbusRtuOverUdpDataHandleAdapter dataHandleAdapter = new()
{ {
Crc16CheckEnable = Crc16CheckEnable, Crc16CheckEnable = Crc16CheckEnable,
}; };
UdpSession.SetDataHandlingAdapter(DataHandleAdapter); UdpSession.Config.SetUdpDataHandlingAdapter(() =>
{
return dataHandleAdapter;
});
} }
/// <inheritdoc/> /// <inheritdoc/>

View File

@@ -10,6 +10,8 @@
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
#endregion #endregion
using ThingsGateway.Foundation.Extension;
namespace ThingsGateway.Foundation.Adapter.Modbus; namespace ThingsGateway.Foundation.Adapter.Modbus;
/// <summary> /// <summary>
@@ -38,6 +40,7 @@ public class ModbusRtuOverUdpDataHandleAdapter : ReadWriteDevicesUdpDataHandleAd
protected override OperResult<byte[]> UnpackResponse( protected override OperResult<byte[]> UnpackResponse(
byte[] send, byte[] response) byte[] send, byte[] response)
{ {
return ModbusHelper.GetModbusRtuData(send, response, Crc16CheckEnable); var result = ModbusHelper.GetModbusRtuData(send, response, Crc16CheckEnable);
return result.Copy();
} }
} }

View File

@@ -78,8 +78,8 @@ public class ModbusServer : ReadWriteDevicesTcpServerBase
/// <inheritdoc/> /// <inheritdoc/>
public override void SetDataAdapter(SocketClient client) public override void SetDataAdapter(SocketClient client)
{ {
ModbusServerDataHandleAdapter DataHandleAdapter = new(); ModbusServerDataHandleAdapter dataHandleAdapter = new();
client.SetDataHandlingAdapter(DataHandleAdapter); client.SetDataHandlingAdapter(dataHandleAdapter);
} }
/// <inheritdoc/> /// <inheritdoc/>

View File

@@ -79,12 +79,12 @@ public class ModbusTcp : ReadWriteDevicesTcpClientBase
/// <inheritdoc/> /// <inheritdoc/>
public override void SetDataAdapter() public override void SetDataAdapter()
{ {
ModbusTcpDataHandleAdapter DataHandleAdapter = new() ModbusTcpDataHandleAdapter dataHandleAdapter = new()
{ {
IsCheckMessageId = IsCheckMessageId, IsCheckMessageId = IsCheckMessageId,
CacheTimeout = TimeSpan.FromMilliseconds(CacheTimeout) CacheTimeout = TimeSpan.FromMilliseconds(CacheTimeout)
}; };
TcpClientEx.SetDataHandlingAdapter(DataHandleAdapter); TcpClientEx.SetDataHandlingAdapter(dataHandleAdapter);
} }
/// <inheritdoc/> /// <inheritdoc/>

View File

@@ -10,13 +10,12 @@
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
#endregion #endregion
using ThingsGateway.Foundation.Extension;
using ThingsGateway.Foundation.Extension.Generic; using ThingsGateway.Foundation.Extension.Generic;
namespace ThingsGateway.Foundation.Adapter.Modbus; namespace ThingsGateway.Foundation.Adapter.Modbus;
/// <summary> /// <summary>
/// <inheritdoc/> /// ModbusTcpDataHandleAdapter
/// </summary> /// </summary>
public class ModbusTcpDataHandleAdapter : ReadWriteDevicesTcpDataHandleAdapter<ModbusTcpMessage> public class ModbusTcpDataHandleAdapter : ReadWriteDevicesTcpDataHandleAdapter<ModbusTcpMessage>
{ {
@@ -58,48 +57,14 @@ public class ModbusTcpDataHandleAdapter : ReadWriteDevicesTcpDataHandleAdapter<M
{ {
request.ResultCode = result.ResultCode; request.ResultCode = result.ResultCode;
request.Message = result.Message; request.Message = result.Message;
request.Content = result.Content; request.Content = result.Content1;
return FilterResult.Success;
} }
else else
{ {
//如果返回错误,具体分析 request.ResultCode = result.ResultCode;
var op = result.Copy<byte[], FilterResult>(); request.Message = result.Message;
if (response.Length == 9)
{
if (response[7] >= 0x80)//错误码
{
request.ResultCode = result.ResultCode;
request.Message = result.Message;
request.Content = result.Content;
return FilterResult.Success;
}
}
if (response.Length < 10)
{
request.ResultCode = result.ResultCode;
request.Message = result.Message;
request.Content = result.Content;
return FilterResult.Cache;
//如果长度不足,返回缓存
}
if ((response.Length > response[8] + 9))
{
request.ResultCode = result.ResultCode;
request.Message = result.Message;
request.Content = result.Content;
return FilterResult.Success;
//如果长度已经超了,说明这段报文已经不能继续解析了,直接返回放弃
}
else
{
request.ResultCode = result.ResultCode;
request.Message = result.Message;
request.Content = result.Content;
return FilterResult.Cache;
//否则返回缓存
}
} }
return result.Content2;
} }
} }

View File

@@ -75,11 +75,14 @@ public class ModbusUdp : ReadWriteDevicesUdpBase
/// <inheritdoc/> /// <inheritdoc/>
public override void SetDataAdapter() public override void SetDataAdapter()
{ {
ModbusUdpDataHandleAdapter DataHandleAdapter = new() ModbusUdpDataHandleAdapter dataHandleAdapter = new()
{ {
IsCheckMessageId = IsCheckMessageId IsCheckMessageId = IsCheckMessageId
}; };
UdpSession.SetDataHandlingAdapter(DataHandleAdapter); UdpSession.Config.SetUdpDataHandlingAdapter(() =>
{
return dataHandleAdapter;
});
} }
/// <inheritdoc/> /// <inheritdoc/>

View File

@@ -10,6 +10,7 @@
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
#endregion #endregion
using ThingsGateway.Foundation.Extension;
using ThingsGateway.Foundation.Extension.Generic; using ThingsGateway.Foundation.Extension.Generic;
namespace ThingsGateway.Foundation.Adapter.Modbus; namespace ThingsGateway.Foundation.Adapter.Modbus;
@@ -50,6 +51,7 @@ public class ModbusUdpDataHandleAdapter : ReadWriteDevicesUdpDataHandleAdapter<M
/// <inheritdoc/> /// <inheritdoc/>
protected override OperResult<byte[]> UnpackResponse(byte[] send, byte[] response) protected override OperResult<byte[]> UnpackResponse(byte[] send, byte[] response)
{ {
return ModbusHelper.GetModbusData(send.RemoveBegin(6), response.RemoveBegin(6)); var result = ModbusHelper.GetModbusData(send.RemoveBegin(6), response.RemoveBegin(6));
return result.Copy();
} }
} }

View File

@@ -458,7 +458,7 @@
</member> </member>
<member name="T:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcpDataHandleAdapter"> <member name="T:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcpDataHandleAdapter">
<summary> <summary>
<inheritdoc/> ModbusTcpDataHandleAdapter
</summary> </summary>
</member> </member>
<member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcpDataHandleAdapter.IsCheckMessageId"> <member name="P:ThingsGateway.Foundation.Adapter.Modbus.ModbusTcpDataHandleAdapter.IsCheckMessageId">

View File

@@ -349,6 +349,7 @@ public class OPCDAClient : DisposableObject
protected override void Dispose(bool disposing) protected override void Dispose(bool disposing)
{ {
PrivateDisconnect(); PrivateDisconnect();
checkLock.SafeDispose();
base.Dispose(disposing); base.Dispose(disposing);
} }

View File

@@ -31,11 +31,6 @@ public static class JsonUtils
/// <summary> /// <summary>
/// 解析获取DataValue /// 解析获取DataValue
/// </summary> /// </summary>
/// <param name="Context"></param>
/// <param name="dataTypeId"></param>
/// <param name="builtInType"></param>
/// <param name="valueRank"></param>
/// <param name="json"></param>
/// <returns></returns> /// <returns></returns>
public static DataValue Decode( public static DataValue Decode(
IServiceMessageContext Context, IServiceMessageContext Context,
@@ -45,6 +40,7 @@ public static class JsonUtils
JToken json JToken json
) )
{ {
var data = DecoderObject(Context, dataTypeId, builtInType, valueRank, json); var data = DecoderObject(Context, dataTypeId, builtInType, valueRank, json);
var dataValue = new DataValue(new Variant(data)); var dataValue = new DataValue(new Variant(data));
return dataValue; return dataValue;
@@ -54,11 +50,6 @@ public static class JsonUtils
/// <summary> /// <summary>
/// 解析获取object /// 解析获取object
/// </summary> /// </summary>
/// <param name="Context"></param>
/// <param name="dataTypeId"></param>
/// <param name="builtInType"></param>
/// <param name="valueRank"></param>
/// <param name="json"></param>
/// <returns></returns> /// <returns></returns>
public static object DecoderObject( public static object DecoderObject(
IServiceMessageContext Context, IServiceMessageContext Context,
@@ -76,10 +67,9 @@ public static class JsonUtils
{ {
Value = new Value = new
{ {
TypeId = new { Id = dataTypeId.Identifier }, TypeId = new { Id = dataTypeId.Identifier, Namespace = dataTypeId.NamespaceIndex },
Body = json Body = json
} }
}.ToJson(); }.ToJson();
break; break;
case BuiltInType.Variant: case BuiltInType.Variant:
@@ -119,7 +109,6 @@ public static class JsonUtils
{ {
if (ValueRank == ValueRanks.Scalar) if (ValueRank == ValueRanks.Scalar)
{ {
Type type = TypeInfo.GetSystemType(builtInType, ValueRank);
switch (builtInType) switch (builtInType)
{ {
case BuiltInType.Null: { var variant = decoder.ReadVariant(fieldName); return variant.Value; } case BuiltInType.Null: { var variant = decoder.ReadVariant(fieldName); return variant.Value; }
@@ -134,7 +123,7 @@ public static class JsonUtils
case BuiltInType.UInt64: { return decoder.ReadUInt64(fieldName); } case BuiltInType.UInt64: { return decoder.ReadUInt64(fieldName); }
case BuiltInType.Float: { return decoder.ReadFloat(fieldName); } case BuiltInType.Float: { return decoder.ReadFloat(fieldName); }
case BuiltInType.Double: { return decoder.ReadDouble(fieldName); } case BuiltInType.Double: { return decoder.ReadDouble(fieldName); }
case BuiltInType.String: { return decoder.ReadString(fieldName); } case BuiltInType.String: { return decoder.ReadField(fieldName, out var token) ? token?.ToString() : null; }
case BuiltInType.DateTime: { return decoder.ReadDateTime(fieldName); } case BuiltInType.DateTime: { return decoder.ReadDateTime(fieldName); }
case BuiltInType.Guid: { return decoder.ReadGuid(fieldName); } case BuiltInType.Guid: { return decoder.ReadGuid(fieldName); }
case BuiltInType.ByteString: { return decoder.ReadByteString(fieldName); } case BuiltInType.ByteString: { return decoder.ReadByteString(fieldName); }
@@ -148,7 +137,8 @@ public static class JsonUtils
case BuiltInType.DataValue: { return decoder.ReadDataValue(fieldName); } case BuiltInType.DataValue: { return decoder.ReadDataValue(fieldName); }
case BuiltInType.Enumeration: case BuiltInType.Enumeration:
{ {
return type.IsEnum ? decoder.ReadEnumerated(fieldName, type) : (object)decoder.ReadInt32(fieldName); Type type = TypeInfo.GetSystemType(builtInType, ValueRank);
return type.IsEnum ? decoder.ReadEnumerated(fieldName, type) : decoder.ReadInt32(fieldName);
} }
case BuiltInType.DiagnosticInfo: { return decoder.ReadDiagnosticInfo(fieldName); } case BuiltInType.DiagnosticInfo: { return decoder.ReadDiagnosticInfo(fieldName); }
case BuiltInType.Variant: { return decoder.ReadVariant(fieldName); } case BuiltInType.Variant: { return decoder.ReadVariant(fieldName); }
@@ -310,7 +300,7 @@ public static class JsonUtils
case BuiltInType.DataValue: { encoder.WriteDataValue(fieldName, (DataValue)value); return; } case BuiltInType.DataValue: { encoder.WriteDataValue(fieldName, (DataValue)value); return; }
case BuiltInType.Enumeration: case BuiltInType.Enumeration:
{ {
if (value.GetType().IsEnum) if (value?.GetType().IsEnum == true)
{ {
encoder.WriteEnumerated(fieldName, (Enum)value); encoder.WriteEnumerated(fieldName, (Enum)value);
} }

View File

@@ -14,6 +14,7 @@ using Newtonsoft.Json.Linq;
using Opc.Ua; using Opc.Ua;
using Opc.Ua.Client; using Opc.Ua.Client;
using Opc.Ua.Client.ComplexTypes;
using Opc.Ua.Configuration; using Opc.Ua.Configuration;
using System.Collections.Generic; using System.Collections.Generic;
@@ -204,9 +205,9 @@ public class OPCUAClient : DisposableObject
#region #region
/// <summary> /// <summary>
/// 新增订阅,需要指定订阅的关键字订阅的tag名数组,以及回调方法 /// 新增订阅,需要指定订阅组名称订阅的tag名数组
/// </summary> /// </summary>
public void AddSubscription(string subscriptionName, string[] items) public async Task AddSubscriptionAsync(string subscriptionName, string[] items)
{ {
Subscription m_subscription = new(m_session.DefaultSubscription) Subscription m_subscription = new(m_session.DefaultSubscription)
{ {
@@ -221,16 +222,26 @@ public class OPCUAClient : DisposableObject
List<MonitoredItem> monitoredItems = new(); List<MonitoredItem> monitoredItems = new();
for (int i = 0; i < items.Length; i++) for (int i = 0; i < items.Length; i++)
{ {
var item = new MonitoredItem try
{ {
StartNodeId = new NodeId(items[i]), var variableNode = (VariableNode)ReadNode(items[i], false);
AttributeId = Attributes.Value, var item = new MonitoredItem
DisplayName = items[i], {
Filter = new DataChangeFilter() { DeadbandValue = OPCNode.DeadBand, DeadbandType = (int)DeadbandType.Absolute, Trigger = DataChangeTrigger.StatusValue }, StartNodeId = variableNode.NodeId,
SamplingInterval = OPCNode?.UpdateRate ?? 1000, AttributeId = Attributes.Value,
}; DisplayName = items[i],
item.Notification += Callback; Filter = OPCNode.DeadBand == 0 ? null : new DataChangeFilter() { DeadbandValue = OPCNode.DeadBand, DeadbandType = (int)DeadbandType.Absolute, Trigger = DataChangeTrigger.StatusValue },
monitoredItems.Add(item); SamplingInterval = OPCNode?.UpdateRate ?? 1000,
};
await typeSystem.LoadType(variableNode.DataType, true, true);
item.Notification += Callback;
monitoredItems.Add(item);
}
catch (Exception ex)
{
Log.Error($"初始化{items[i]}变量订阅失败" + ex.Message);
}
} }
m_subscription.AddItems(monitoredItems); m_subscription.AddItems(monitoredItems);
@@ -238,7 +249,7 @@ public class OPCUAClient : DisposableObject
m_subscription.Create(); m_subscription.Create();
foreach (var item in m_subscription.MonitoredItems.Where(a => a.Status.Error != null && StatusCode.IsBad(a.Status.Error.StatusCode))) foreach (var item in m_subscription.MonitoredItems.Where(a => a.Status.Error != null && StatusCode.IsBad(a.Status.Error.StatusCode)))
{ {
item.Filter = new DataChangeFilter() { DeadbandValue = OPCNode.DeadBand, DeadbandType = (int)DeadbandType.None, Trigger = DataChangeTrigger.StatusValue }; item.Filter = OPCNode.DeadBand == 0 ? null : new DataChangeFilter() { DeadbandValue = OPCNode.DeadBand, DeadbandType = (int)DeadbandType.None, Trigger = DataChangeTrigger.StatusValue };
} }
m_subscription.ApplyChanges(); m_subscription.ApplyChanges();
@@ -309,12 +320,21 @@ public class OPCUAClient : DisposableObject
VariableNode variableNode = (VariableNode)ReadNode(monitoreditem.StartNodeId.ToString(), false); VariableNode variableNode = (VariableNode)ReadNode(monitoreditem.StartNodeId.ToString(), false);
foreach (var value in monitoreditem.DequeueValues()) foreach (var value in monitoreditem.DequeueValues())
{ {
var data = JsonUtils.Encode(m_session.MessageContext, TypeInfo.GetBuiltInType(variableNode.DataType, m_session.SystemContext.TypeTable), value.Value); if (value.Value != null)
if (data == null && value.Value != null)
{ {
Log.Warning($"{monitoreditem.StartNodeId}转换出错原始值String为{value.Value}"); var data = JsonUtils.Encode(m_session.MessageContext, TypeInfo.GetBuiltInType(variableNode.DataType, m_session.SystemContext.TypeTable), value.Value);
if (data == null && value.Value != null)
{
Log.Warning($"{monitoreditem.StartNodeId}转换出错原始值String为{value.Value}");
}
DataChangedHandler?.Invoke((variableNode, value, data));
} }
DataChangedHandler?.Invoke((variableNode, value, data)); else
{
var data = JValue.CreateNull();
DataChangedHandler?.Invoke((variableNode, value, data));
}
} }
} }
catch (Exception ex) catch (Exception ex)
@@ -454,13 +474,24 @@ public class OPCUAClient : DisposableObject
/// </summary> /// </summary>
public async Task ConnectAsync() public async Task ConnectAsync()
{ {
m_session = await ConnectAsync(OPCNode.OPCUrl); await ConnectAsync(OPCNode.OPCUrl);
} }
/// <summary> /// <summary>
/// 断开连接。 /// 断开连接。
/// </summary> /// </summary>
public void Disconnect() public void Disconnect()
{
PrivateDisconnect();
// disconnect any existing session.
if (m_session != null)
{
Log.Debug("断开连接");
m_session = null;
}
}
private void PrivateDisconnect()
{ {
// stop any reconnect operation. // stop any reconnect operation.
if (m_reConnectHandler != null) if (m_reConnectHandler != null)
@@ -468,15 +499,7 @@ public class OPCUAClient : DisposableObject
m_reConnectHandler.SafeDispose(); m_reConnectHandler.SafeDispose();
m_reConnectHandler = null; m_reConnectHandler = null;
} }
m_session?.Close(10000);
// disconnect any existing session.
if (m_session != null)
{
Log.Debug("断开连接");
m_session.Close(10000);
m_session = null;
}
} }
@@ -507,11 +530,12 @@ public class OPCUAClient : DisposableObject
NodeId = new NodeId(tag), NodeId = new NodeId(tag),
AttributeId = Attributes.Value, AttributeId = Attributes.Value,
}; };
var node = (VariableNode)ReadNode(tag.ToString(), false); var variableNode = (VariableNode)ReadNode(tag.ToString(), false);
await typeSystem.LoadType(variableNode.DataType, true, true);
var dataValue = JsonUtils.Decode( var dataValue = JsonUtils.Decode(
m_session.MessageContext, m_session.MessageContext,
node.DataType, variableNode.DataType,
TypeInfo.GetBuiltInType(node.DataType, m_session.SystemContext.TypeTable), TypeInfo.GetBuiltInType(variableNode.DataType, m_session.SystemContext.TypeTable),
value.CalculateActualValueRank(), value.CalculateActualValueRank(),
value value
); );
@@ -552,6 +576,8 @@ public class OPCUAClient : DisposableObject
ReadValueIdCollection nodesToRead = new(); ReadValueIdCollection nodesToRead = new();
for (int i = 0; i < nodeIds.Length; i++) for (int i = 0; i < nodeIds.Length; i++)
{ {
var variableNode = (VariableNode)ReadNode(nodeIds[i].ToString(), false);
await typeSystem.LoadType(variableNode.DataType, true, true);
nodesToRead.Add(new ReadValueId() nodesToRead.Add(new ReadValueId()
{ {
NodeId = nodeIds[i], NodeId = nodeIds[i],
@@ -573,10 +599,10 @@ public class OPCUAClient : DisposableObject
List<(string, DataValue, JToken)> jTokens = new(); List<(string, DataValue, JToken)> jTokens = new();
for (int i = 0; i < results.Count; i++) for (int i = 0; i < results.Count; i++)
{ {
var node = (VariableNode)ReadNode(nodeIds[i].ToString(), false); var variableNode = (VariableNode)ReadNode(nodeIds[i].ToString(), false);
var type = TypeInfo.GetBuiltInType(node.DataType, m_session.SystemContext.TypeTable); var type = TypeInfo.GetBuiltInType(variableNode.DataType, m_session.SystemContext.TypeTable);
var jToken = JsonUtils.Encode(m_session.MessageContext, type, results[i].Value); var jToken = JsonUtils.Encode(m_session.MessageContext, type, results[i].Value);
jTokens.Add((node.NodeId.ToString(), results[i], jToken)); jTokens.Add((variableNode.NodeId.ToString(), results[i], jToken));
} }
return jTokens.ToList(); return jTokens.ToList();
} }
@@ -838,6 +864,7 @@ public class OPCUAClient : DisposableObject
protected override void Dispose(bool disposing) protected override void Dispose(bool disposing)
{ {
Disconnect(); Disconnect();
checkLock.SafeDispose();
base.Dispose(disposing); base.Dispose(disposing);
} }
@@ -852,14 +879,14 @@ public class OPCUAClient : DisposableObject
else else
throw new Exception(string.Format("验证证书失败,错误代码:{0}: {1}", eventArgs.Error.Code, eventArgs.Error.AdditionalInfo)); throw new Exception(string.Format("验证证书失败,错误代码:{0}: {1}", eventArgs.Error.Code, eventArgs.Error.AdditionalInfo));
} }
private ComplexTypeSystem typeSystem;
/// <summary> /// <summary>
/// Creates a new session. /// Creates a new session.
/// </summary> /// </summary>
/// <returns>The new session object.</returns> /// <returns>The new session object.</returns>
private async Task<ISession> ConnectAsync(string serverUrl) private async Task<ISession> ConnectAsync(string serverUrl)
{ {
Disconnect(); PrivateDisconnect();
if (m_configuration == null) if (m_configuration == null)
{ {
@@ -890,6 +917,7 @@ public class OPCUAClient : DisposableObject
userIdentity, userIdentity,
Array.Empty<string>()); Array.Empty<string>());
typeSystem = new ComplexTypeSystem(m_session);
Log.Debug("连接成功"); Log.Debug("连接成功");
m_session.KeepAliveInterval = OPCNode.KeepAliveInterval == 0 ? 60000 : OPCNode.KeepAliveInterval; m_session.KeepAliveInterval = OPCNode.KeepAliveInterval == 0 ? 60000 : OPCNode.KeepAliveInterval;
@@ -897,7 +925,7 @@ public class OPCUAClient : DisposableObject
//如果是订阅模式,连接时添加订阅组 //如果是订阅模式,连接时添加订阅组
if (OPCNode.ActiveSubscribe) if (OPCNode.ActiveSubscribe)
AddSubscription(Guid.NewGuid().ToString(), Variables.ToArray()); await AddSubscriptionAsync(Guid.NewGuid().ToString(), Variables.ToArray());
return m_session; return m_session;
} }

View File

@@ -8,6 +8,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="OPCFoundation.NetStandard.Opc.Ua.Client" Version="1.4.371.96" /> <PackageReference Include="OPCFoundation.NetStandard.Opc.Ua.Client" Version="1.4.371.96" />
<PackageReference Include="OPCFoundation.NetStandard.Opc.Ua.Client.ComplexTypes" Version="1.4.371.96" />
<PackageReference Include="OPCFoundation.NetStandard.Opc.Ua.Server" Version="1.4.371.96" /> <PackageReference Include="OPCFoundation.NetStandard.Opc.Ua.Server" Version="1.4.371.96" />
</ItemGroup> </ItemGroup>

View File

@@ -161,22 +161,12 @@
<summary> <summary>
解析获取DataValue 解析获取DataValue
</summary> </summary>
<param name="Context"></param>
<param name="dataTypeId"></param>
<param name="builtInType"></param>
<param name="valueRank"></param>
<param name="json"></param>
<returns></returns> <returns></returns>
</member> </member>
<member name="M:ThingsGateway.Foundation.Adapter.OPCUA.JsonUtils.DecoderObject(Opc.Ua.IServiceMessageContext,Opc.Ua.NodeId,Opc.Ua.BuiltInType,System.Int32,Newtonsoft.Json.Linq.JToken)"> <member name="M:ThingsGateway.Foundation.Adapter.OPCUA.JsonUtils.DecoderObject(Opc.Ua.IServiceMessageContext,Opc.Ua.NodeId,Opc.Ua.BuiltInType,System.Int32,Newtonsoft.Json.Linq.JToken)">
<summary> <summary>
解析获取object 解析获取object
</summary> </summary>
<param name="Context"></param>
<param name="dataTypeId"></param>
<param name="builtInType"></param>
<param name="valueRank"></param>
<param name="json"></param>
<returns></returns> <returns></returns>
</member> </member>
<member name="M:ThingsGateway.Foundation.Adapter.OPCUA.JsonUtils.DecodeRawData(Opc.Ua.JsonDecoder,Opc.Ua.BuiltInType,System.Int32,System.String)"> <member name="M:ThingsGateway.Foundation.Adapter.OPCUA.JsonUtils.DecodeRawData(Opc.Ua.JsonDecoder,Opc.Ua.BuiltInType,System.Int32,System.String)">
@@ -326,9 +316,9 @@
当前活动会话。 当前活动会话。
</summary> </summary>
</member> </member>
<member name="M:ThingsGateway.Foundation.Adapter.OPCUA.OPCUAClient.AddSubscription(System.String,System.String[])"> <member name="M:ThingsGateway.Foundation.Adapter.OPCUA.OPCUAClient.AddSubscriptionAsync(System.String,System.String[])">
<summary> <summary>
新增订阅,需要指定订阅的关键字订阅的tag名数组,以及回调方法 新增订阅,需要指定订阅组名称订阅的tag名数组
</summary> </summary>
</member> </member>
<member name="M:ThingsGateway.Foundation.Adapter.OPCUA.OPCUAClient.RemoveAllSubscription"> <member name="M:ThingsGateway.Foundation.Adapter.OPCUA.OPCUAClient.RemoveAllSubscription">

View File

@@ -124,7 +124,7 @@ internal partial class SiemensHelper
} }
byte err = content[21]; byte err = content[21];
if (err == byte.MaxValue) if (err != byte.MaxValue)
{ {
return new OperResult<byte[]>((int)content[21] + GetCpuError(content[21])); return new OperResult<byte[]>((int)content[21] + GetCpuError(content[21]));
} }
@@ -301,7 +301,7 @@ internal partial class SiemensHelper
{ {
return new OperResult<string>("在PLC中不是字符串类型"); return new OperResult<string>("在PLC中不是字符串类型");
} }
var result2 = await plc.ReadAsync(address, 2 + result1.Content[1]); var result2 = await plc.ReadAsync(address, 2 + result1.Content[1], token);
if (!result2.IsSuccess) if (!result2.IsSuccess)
{ {
return result2.Copy<string>(); return result2.Copy<string>();

View File

@@ -305,6 +305,8 @@ namespace ThingsGateway.Foundation.Adapter.Siemens
foreach (var item in commandResult.Content) foreach (var item in commandResult.Content)
{ {
ResponsedData result = await WaitingClientEx.SendThenResponseAsync(item, TimeOut, token); ResponsedData result = await WaitingClientEx.SendThenResponseAsync(item, TimeOut, token);
if (!((MessageBase)result.RequestInfo).IsSuccess)
return OperResult.CreateFailedResult<byte[]>(((MessageBase)result.RequestInfo));
bytes.Add(result); bytes.Add(result);
} }
return OperResult.CreateSuccessResult(bytes.ToArray()); return OperResult.CreateSuccessResult(bytes.ToArray());
@@ -338,7 +340,7 @@ namespace ThingsGateway.Foundation.Adapter.Siemens
if (commandResult.IsSuccess) if (commandResult.IsSuccess)
{ {
var result = await WaitingClientEx.SendThenResponseAsync(commandResult.Content, TimeOut, token); var result = await WaitingClientEx.SendThenResponseAsync(commandResult.Content, TimeOut, token);
return OperResult.CreateSuccessResult(result); return (MessageBase)result.RequestInfo;
} }
else else
{ {

View File

@@ -54,32 +54,9 @@ public class SiemensS7PLCDataHandleAdapter : ReadWriteDevicesTcpDataHandleAdapte
break; break;
} }
} }
if (result.IsSuccess) request.ResultCode = result.ResultCode;
{ request.Message = result.Message;
request.ResultCode = result.ResultCode; request.Content = result.Content;
request.Message = result.Message; return FilterResult.Success;
request.Content = result.Content;
return FilterResult.Success;
}
else
{
//如果返回错误,具体分析
if (response.Length < 21)
{
request.ResultCode = result.ResultCode;
request.Message = result.Message;
request.Content = result.Content;
//如果长度不足,返回缓存
return FilterResult.Cache;
}
else
{
request.ResultCode = result.ResultCode;
request.Message = result.Message;
request.Content = result.Content;
//如果长度已经超了,说明这段报文已经不能继续解析了,直接返回放弃
return FilterResult.Success;
}
}
} }
} }

View File

@@ -32,7 +32,7 @@ internal static class ModbusHelper
IThingsGatewayBitConverter transformParameter = ByteTransformUtil.GetTransByAddress(ref address, byteConverter); IThingsGatewayBitConverter transformParameter = ByteTransformUtil.GetTransByAddress(ref address, byteConverter);
item.ThingsGatewayBitConverter = transformParameter; item.ThingsGatewayBitConverter = transformParameter;
//item.VariableAddress = address; item.VariableAddress = address;
item.Index = device.GetBitOffset(item.VariableAddress); item.Index = device.GetBitOffset(item.VariableAddress);
} }

View File

@@ -26,13 +26,13 @@
<DefalutDebugDriverPage Channel="ChannelEnum.SerialPort" @ref=defalutDebugDriverPage> <DefalutDebugDriverPage Channel="ChannelEnum.SerialPort" @ref=defalutDebugDriverPage>
<MCard Flat Elevation="0"> <MCard Flat Elevation="0">
<MRow Class="my-1" NoGutters Justify="JustifyTypes.SpaceBetween" Align="AlignTypes.Center"> <MRow Class="my-1" NoGutters Justify="JustifyTypes.SpaceBetween" Align="AlignTypes.Center">
<MCheckbox Class="mx-1" Label=@(_plc.Description(x => x.Crc16CheckEnable)) Dense Outlined HideDetails="@("auto")" @bind-Value=@_plc.Crc16CheckEnable></MCheckbox> <MCheckbox Class="ma-1" Label=@(_plc.Description(x => x.Crc16CheckEnable)) Dense Outlined HideDetails="@("auto")" @bind-Value=@_plc.Crc16CheckEnable></MCheckbox>
<MTextField Class="mx-1" Label=@(_plc.Description(x => x.FrameTime)) Dense Outlined HideDetails="@("auto")" @bind-Value=@_plc.FrameTime></MTextField> <MTextField Class="ma-1" Label=@(_plc.Description(x => x.FrameTime)) Dense Outlined HideDetails="@("auto")" @bind-Value=@_plc.FrameTime></MTextField>
<MTextField Class="mx-1" Label=@(_plc.Description(x => x.CacheTimeout)) Dense Outlined HideDetails="@("auto")" @bind-Value=@_plc.CacheTimeout></MTextField> <MTextField Class="ma-1" Label=@(_plc.Description(x => x.CacheTimeout)) Dense Outlined HideDetails="@("auto")" @bind-Value=@_plc.CacheTimeout></MTextField>
<MTextField Class="mx-1" Label=@(_plc.Description(x => x.Station)) Dense Outlined HideDetails="@("auto")" @bind-Value=@_plc.Station></MTextField> <MTextField Class="ma-1" Label=@(_plc.Description(x => x.Station)) Dense Outlined HideDetails="@("auto")" @bind-Value=@_plc.Station></MTextField>
<MTextField Class="mx-1" Label=@(_plc.Description(x => x.TimeOut)) Dense Outlined HideDetails="@("auto")" @bind-Value=@_plc.TimeOut></MTextField> <MTextField Class="ma-1" Label=@(_plc.Description(x => x.TimeOut)) Dense Outlined HideDetails="@("auto")" @bind-Value=@_plc.TimeOut></MTextField>
<MSelect Class="mx-1" Style="max-width:200px" @bind-Value="_plc.DataFormat" Outlined Label="@(_plc.Description(x => x.DataFormat))" <MSelect Class="ma-1" Style="max-width:200px" @bind-Value="_plc.DataFormat" Outlined Label="@(_plc.Description(x => x.DataFormat))"
Items=@(typeof(DataFormat).GetEnumList()) Items=@(typeof(DataFormat).GetEnumList())
MenuProps="@(props => { props.Auto = true; props.OffsetY = true; })" MenuProps="@(props => { props.Auto = true; props.OffsetY = true; })"
ItemText=@((u) =>u.des) ItemText=@((u) =>u.des)
@@ -54,6 +54,7 @@
{ {
//载入配置 //载入配置
_plc = new ThingsGateway.Foundation.Adapter.Modbus.ModbusRtu((SerialClient)defalutDebugDriverPage.SerialClientPage.GetSerialClient()); _plc = new ThingsGateway.Foundation.Adapter.Modbus.ModbusRtu((SerialClient)defalutDebugDriverPage.SerialClientPage.GetSerialClient());
defalutDebugDriverPage.Plc = _plc;
} }
base.OnAfterRender(firstRender); base.OnAfterRender(firstRender);
} }

View File

@@ -25,13 +25,13 @@
<DefalutDebugDriverPage Channel="ChannelEnum.TcpClientEx" @ref=defalutDebugDriverPage> <DefalutDebugDriverPage Channel="ChannelEnum.TcpClientEx" @ref=defalutDebugDriverPage>
<MCard Flat Elevation="0"> <MCard Flat Elevation="0">
<MRow Class="my-1" NoGutters Justify="JustifyTypes.SpaceBetween" Align="AlignTypes.Center"> <MRow Class="my-1" NoGutters Justify="JustifyTypes.SpaceBetween" Align="AlignTypes.Center">
<MCheckbox Class="mx-1" Label=@(_plc.Description(x => x.Crc16CheckEnable)) Dense Outlined HideDetails="@("auto")" @bind-Value=@_plc.Crc16CheckEnable></MCheckbox> <MCheckbox Class="ma-1" Label=@(_plc.Description(x => x.Crc16CheckEnable)) Dense Outlined HideDetails="@("auto")" @bind-Value=@_plc.Crc16CheckEnable></MCheckbox>
<MTextField Class="mx-1" Label=@(_plc.Description(x => x.FrameTime)) Dense Outlined HideDetails="@("auto")" @bind-Value=@_plc.FrameTime></MTextField> <MTextField Class="ma-1" Label=@(_plc.Description(x => x.FrameTime)) Dense Outlined HideDetails="@("auto")" @bind-Value=@_plc.FrameTime></MTextField>
<MTextField Class="mx-1" Label=@(_plc.Description(x => x.CacheTimeout)) Dense Outlined HideDetails="@("auto")" @bind-Value=@_plc.CacheTimeout></MTextField> <MTextField Class="ma-1" Label=@(_plc.Description(x => x.CacheTimeout)) Dense Outlined HideDetails="@("auto")" @bind-Value=@_plc.CacheTimeout></MTextField>
<MTextField Class="mx-1" Label=@(_plc.Description(x => x.Station)) Dense Outlined HideDetails="@("auto")" @bind-Value=@_plc.Station></MTextField> <MTextField Class="ma-1" Label=@(_plc.Description(x => x.Station)) Dense Outlined HideDetails="@("auto")" @bind-Value=@_plc.Station></MTextField>
<MTextField Class="mx-1" Label=@(_plc.Description(x => x.TimeOut)) Dense Outlined HideDetails="@("auto")" @bind-Value=@_plc.TimeOut></MTextField> <MTextField Class="ma-1" Label=@(_plc.Description(x => x.TimeOut)) Dense Outlined HideDetails="@("auto")" @bind-Value=@_plc.TimeOut></MTextField>
<MSelect Class="mx-1" Style="max-width:200px" @bind-Value="_plc.DataFormat" Outlined Label="@(_plc.Description(x => x.DataFormat))" <MSelect Class="ma-1" Style="max-width:200px" @bind-Value="_plc.DataFormat" Outlined Label="@(_plc.Description(x => x.DataFormat))"
Items=@(typeof(DataFormat).GetEnumList()) Items=@(typeof(DataFormat).GetEnumList())
MenuProps="@(props => { props.Auto = true; props.OffsetY = true; })" MenuProps="@(props => { props.Auto = true; props.OffsetY = true; })"
ItemText=@((u) =>u.des) ItemText=@((u) =>u.des)

View File

@@ -25,12 +25,12 @@
<DefalutDebugDriverPage Channel="ChannelEnum.UdpSession" @ref=defalutDebugDriverPage> <DefalutDebugDriverPage Channel="ChannelEnum.UdpSession" @ref=defalutDebugDriverPage>
<MCard Flat Elevation="0"> <MCard Flat Elevation="0">
<MRow Class="my-1" NoGutters Justify="JustifyTypes.SpaceBetween" Align="AlignTypes.Center"> <MRow Class="my-1" NoGutters Justify="JustifyTypes.SpaceBetween" Align="AlignTypes.Center">
<MCheckbox Class="mx-1" Label=@(_plc.Description(x => x.Crc16CheckEnable)) Dense Outlined HideDetails="@("auto")" @bind-Value=@_plc.Crc16CheckEnable></MCheckbox> <MCheckbox Class="ma-1" Label=@(_plc.Description(x => x.Crc16CheckEnable)) Dense Outlined HideDetails="@("auto")" @bind-Value=@_plc.Crc16CheckEnable></MCheckbox>
<MTextField Class="mx-1" Label=@(_plc.Description(x => x.FrameTime)) Dense Outlined HideDetails="@("auto")" @bind-Value=@_plc.FrameTime></MTextField> <MTextField Class="ma-1" Label=@(_plc.Description(x => x.FrameTime)) Dense Outlined HideDetails="@("auto")" @bind-Value=@_plc.FrameTime></MTextField>
<MTextField Class="mx-1" Label=@(_plc.Description(x => x.Station)) Dense Outlined HideDetails="@("auto")" @bind-Value=@_plc.Station></MTextField> <MTextField Class="ma-1" Label=@(_plc.Description(x => x.Station)) Dense Outlined HideDetails="@("auto")" @bind-Value=@_plc.Station></MTextField>
<MTextField Class="mx-1" Label=@(_plc.Description(x => x.TimeOut)) Dense Outlined HideDetails="@("auto")" @bind-Value=@_plc.TimeOut></MTextField> <MTextField Class="ma-1" Label=@(_plc.Description(x => x.TimeOut)) Dense Outlined HideDetails="@("auto")" @bind-Value=@_plc.TimeOut></MTextField>
<MSelect Class="mx-1" Style="max-width:200px" @bind-Value="_plc.DataFormat" Outlined Label="@(_plc.Description(x => x.DataFormat))" <MSelect Class="ma-1" Style="max-width:200px" @bind-Value="_plc.DataFormat" Outlined Label="@(_plc.Description(x => x.DataFormat))"
Items=@(typeof(DataFormat).GetEnumList()) Items=@(typeof(DataFormat).GetEnumList())
MenuProps="@(props => { props.Auto = true; props.OffsetY = true; })" MenuProps="@(props => { props.Auto = true; props.OffsetY = true; })"
ItemText=@((u) =>u.des) ItemText=@((u) =>u.des)

View File

@@ -25,11 +25,11 @@
<DefalutDebugDriverPage Channel="ChannelEnum.TcpServer" @ref=defalutDebugDriverPage> <DefalutDebugDriverPage Channel="ChannelEnum.TcpServer" @ref=defalutDebugDriverPage>
<MCard Flat Elevation="0"> <MCard Flat Elevation="0">
<MRow Class="my-1" NoGutters Justify="JustifyTypes.SpaceBetween" Align="AlignTypes.Center"> <MRow Class="my-1" NoGutters Justify="JustifyTypes.SpaceBetween" Align="AlignTypes.Center">
<MCheckbox Class="mx-1" Label=@(_plc.Description(x => x.MulStation)) Dense Outlined HideDetails="@("auto")" @bind-Value=@_plc.MulStation></MCheckbox> <MCheckbox Class="ma-1" Label=@(_plc.Description(x => x.MulStation)) Dense Outlined HideDetails="@("auto")" @bind-Value=@_plc.MulStation></MCheckbox>
<MTextField Class="mx-1" Label=@(_plc.Description(x => x.Station)) Dense Outlined HideDetails="@("auto")" @bind-Value=@_plc.Station></MTextField> <MTextField Class="ma-1" Label=@(_plc.Description(x => x.Station)) Dense Outlined HideDetails="@("auto")" @bind-Value=@_plc.Station></MTextField>
<MTextField Class="mx-1" Label=@(_plc.Description(x => x.TimeOut)) Dense Outlined HideDetails="@("auto")" @bind-Value=@_plc.TimeOut></MTextField> <MTextField Class="ma-1" Label=@(_plc.Description(x => x.TimeOut)) Dense Outlined HideDetails="@("auto")" @bind-Value=@_plc.TimeOut></MTextField>
<MSelect Class="mx-1" Style="max-width:200px" @bind-Value="_plc.DataFormat" Outlined Label="@(_plc.Description(x => x.DataFormat))" <MSelect Class="ma-1" Style="max-width:200px" @bind-Value="_plc.DataFormat" Outlined Label="@(_plc.Description(x => x.DataFormat))"
Items=@(typeof(DataFormat).GetEnumList()) Items=@(typeof(DataFormat).GetEnumList())
MenuProps="@(props => { props.Auto = true; props.OffsetY = true; })" MenuProps="@(props => { props.Auto = true; props.OffsetY = true; })"
ItemText=@((u) =>u.des) ItemText=@((u) =>u.des)

View File

@@ -25,13 +25,13 @@
<DefalutDebugDriverPage Channel="ChannelEnum.TcpClientEx" @ref=defalutDebugDriverPage> <DefalutDebugDriverPage Channel="ChannelEnum.TcpClientEx" @ref=defalutDebugDriverPage>
<MCard Flat Elevation="0"> <MCard Flat Elevation="0">
<MRow Class="my-1" NoGutters Justify="JustifyTypes.SpaceBetween" Align="AlignTypes.Center"> <MRow Class="my-1" NoGutters Justify="JustifyTypes.SpaceBetween" Align="AlignTypes.Center">
<MCheckbox Class="mx-1" Label=@(_plc.Description(x => x.IsCheckMessageId)) Dense Outlined HideDetails="@("auto")" @bind-Value=@_plc.IsCheckMessageId></MCheckbox> <MCheckbox Class="ma-1" Label=@(_plc.Description(x => x.IsCheckMessageId)) Dense Outlined HideDetails="@("auto")" @bind-Value=@_plc.IsCheckMessageId></MCheckbox>
<MTextField Class="mx-1" Label=@(_plc.Description(x => x.FrameTime)) Dense Outlined HideDetails="@("auto")" @bind-Value=@_plc.FrameTime></MTextField> <MTextField Class="ma-1" Label=@(_plc.Description(x => x.FrameTime)) Dense Outlined HideDetails="@("auto")" @bind-Value=@_plc.FrameTime></MTextField>
<MTextField Class="mx-1" Label=@(_plc.Description(x => x.CacheTimeout)) Dense Outlined HideDetails="@("auto")" @bind-Value=@_plc.CacheTimeout></MTextField> <MTextField Class="ma-1" Label=@(_plc.Description(x => x.CacheTimeout)) Dense Outlined HideDetails="@("auto")" @bind-Value=@_plc.CacheTimeout></MTextField>
<MTextField Class="mx-1" Label=@(_plc.Description(x => x.Station)) Dense Outlined HideDetails="@("auto")" @bind-Value=@_plc.Station></MTextField> <MTextField Class="ma-1" Label=@(_plc.Description(x => x.Station)) Dense Outlined HideDetails="@("auto")" @bind-Value=@_plc.Station></MTextField>
<MTextField Class="mx-1" Label=@(_plc.Description(x => x.TimeOut)) Dense Outlined HideDetails="@("auto")" @bind-Value=@_plc.TimeOut></MTextField> <MTextField Class="ma-1" Label=@(_plc.Description(x => x.TimeOut)) Dense Outlined HideDetails="@("auto")" @bind-Value=@_plc.TimeOut></MTextField>
<MSelect Class="mx-1" Style="max-width:200px" @bind-Value="_plc.DataFormat" Outlined Label="@(_plc.Description(x => x.DataFormat))" <MSelect Class="ma-1" Style="max-width:200px" @bind-Value="_plc.DataFormat" Outlined Label="@(_plc.Description(x => x.DataFormat))"
Items=@(typeof(DataFormat).GetEnumList()) Items=@(typeof(DataFormat).GetEnumList())
MenuProps="@(props => { props.Auto = true; props.OffsetY = true; })" MenuProps="@(props => { props.Auto = true; props.OffsetY = true; })"
ItemText=@((u) =>u.des) ItemText=@((u) =>u.des)

View File

@@ -25,12 +25,12 @@
<DefalutDebugDriverPage Channel="ChannelEnum.UdpSession" @ref=defalutDebugDriverPage> <DefalutDebugDriverPage Channel="ChannelEnum.UdpSession" @ref=defalutDebugDriverPage>
<MCard Flat Elevation="0"> <MCard Flat Elevation="0">
<MRow Class="my-1" NoGutters Justify="JustifyTypes.SpaceBetween" Align="AlignTypes.Center"> <MRow Class="my-1" NoGutters Justify="JustifyTypes.SpaceBetween" Align="AlignTypes.Center">
<MCheckbox Class="mx-1" Label=@(_plc.Description(x => x.IsCheckMessageId)) Dense Outlined HideDetails="@("auto")" @bind-Value=@_plc.IsCheckMessageId></MCheckbox> <MCheckbox Class="ma-1" Label=@(_plc.Description(x => x.IsCheckMessageId)) Dense Outlined HideDetails="@("auto")" @bind-Value=@_plc.IsCheckMessageId></MCheckbox>
<MTextField Class="mx-1" Label=@(_plc.Description(x => x.FrameTime)) Dense Outlined HideDetails="@("auto")" @bind-Value=@_plc.FrameTime></MTextField> <MTextField Class="ma-1" Label=@(_plc.Description(x => x.FrameTime)) Dense Outlined HideDetails="@("auto")" @bind-Value=@_plc.FrameTime></MTextField>
<MTextField Class="mx-1" Label=@(_plc.Description(x => x.Station)) Dense Outlined HideDetails="@("auto")" @bind-Value=@_plc.Station></MTextField> <MTextField Class="ma-1" Label=@(_plc.Description(x => x.Station)) Dense Outlined HideDetails="@("auto")" @bind-Value=@_plc.Station></MTextField>
<MTextField Class="mx-1" Label=@(_plc.Description(x => x.TimeOut)) Dense Outlined HideDetails="@("auto")" @bind-Value=@_plc.TimeOut></MTextField> <MTextField Class="ma-1" Label=@(_plc.Description(x => x.TimeOut)) Dense Outlined HideDetails="@("auto")" @bind-Value=@_plc.TimeOut></MTextField>
<MSelect Class="mx-1" Style="max-width:200px" @bind-Value="_plc.DataFormat" Outlined Label="@(_plc.Description(x => x.DataFormat))" <MSelect Class="ma-1" Style="max-width:200px" @bind-Value="_plc.DataFormat" Outlined Label="@(_plc.Description(x => x.DataFormat))"
Items=@(typeof(DataFormat).GetEnumList()) Items=@(typeof(DataFormat).GetEnumList())
MenuProps="@(props => { props.Auto = true; props.OffsetY = true; })" MenuProps="@(props => { props.Auto = true; props.OffsetY = true; })"
ItemText=@((u) =>u.des) ItemText=@((u) =>u.des)

View File

@@ -49,7 +49,7 @@ public class IotSharpClient : UpLoadBase
private const string WriteMethod = "WRITE"; private const string WriteMethod = "WRITE";
private readonly IotSharpClientProperty driverPropertys = new(); private readonly IotSharpClientProperty driverPropertys = new();
private readonly EasyLock lockobj = new(); private readonly EasyLock easyLock = new();
private readonly IotSharpClientVariableProperty variablePropertys = new(); private readonly IotSharpClientVariableProperty variablePropertys = new();
private ConcurrentQueue<DeviceData> _collectDeviceRunTimes = new(); private ConcurrentQueue<DeviceData> _collectDeviceRunTimes = new();
private ConcurrentQueue<VariableData> _collectVariableRunTimes = new(); private ConcurrentQueue<VariableData> _collectVariableRunTimes = new();
@@ -204,6 +204,7 @@ public class IotSharpClient : UpLoadBase
{ {
try try
{ {
easyLock.SafeDispose();
_globalDeviceData?.AllVariables?.ForEach(a => a.VariableValueChange -= VariableValueChange); _globalDeviceData?.AllVariables?.ForEach(a => a.VariableValueChange -= VariableValueChange);
_globalDeviceData?.CollectDevices?.ForEach(a => _globalDeviceData?.CollectDevices?.ForEach(a =>
@@ -472,7 +473,7 @@ GetPropertyValue(tag, nameof(variablePropertys.VariableRpcEnable)).ToBoolean()
return OperResult.CreateSuccessResult(); return OperResult.CreateSuccessResult();
try try
{ {
await lockobj.WaitAsync(); await easyLock.WaitAsync();
if (_mqttClient?.IsConnected == true) if (_mqttClient?.IsConnected == true)
return OperResult.CreateSuccessResult(); return OperResult.CreateSuccessResult();
using var timeoutToken = new CancellationTokenSource(TimeSpan.FromMilliseconds(driverPropertys.ConnectTimeOut)); using var timeoutToken = new CancellationTokenSource(TimeSpan.FromMilliseconds(driverPropertys.ConnectTimeOut));
@@ -497,7 +498,7 @@ GetPropertyValue(tag, nameof(variablePropertys.VariableRpcEnable)).ToBoolean()
} }
finally finally
{ {
lockobj.Release(); easyLock.Release();
} }
} }
} }

View File

@@ -43,7 +43,7 @@ namespace ThingsGateway.Mqtt;
public class MqttClient : UpLoadBase public class MqttClient : UpLoadBase
{ {
private readonly MqttClientProperty driverPropertys = new(); private readonly MqttClientProperty driverPropertys = new();
private readonly EasyLock lockobj = new(); private readonly EasyLock easyLock = new();
private readonly MqttClientVariableProperty variablePropertys = new(); private readonly MqttClientVariableProperty variablePropertys = new();
private List<CollectDeviceRunTime> _collectDevice; private List<CollectDeviceRunTime> _collectDevice;
private ConcurrentQueue<DeviceData> _collectDeviceRunTimes = new(); private ConcurrentQueue<DeviceData> _collectDeviceRunTimes = new();
@@ -285,6 +285,7 @@ public class MqttClient : UpLoadBase
{ {
try try
{ {
easyLock.SafeDispose();
_globalDeviceData?.AllVariables?.ForEach(a => a.VariableValueChange -= VariableValueChange); _globalDeviceData?.AllVariables?.ForEach(a => a.VariableValueChange -= VariableValueChange);
_globalDeviceData?.CollectDevices?.ForEach(a => _globalDeviceData?.CollectDevices?.ForEach(a =>
@@ -311,7 +312,7 @@ public class MqttClient : UpLoadBase
{ {
var mqttFactory = new MqttFactory(new PrivateLogger(LogMessage)); var mqttFactory = new MqttFactory(new PrivateLogger(LogMessage));
_mqttClientOptions = mqttFactory.CreateClientOptionsBuilder() _mqttClientOptions = mqttFactory.CreateClientOptionsBuilder()
.WithClientId(driverPropertys.ConnectId) .WithClientId(driverPropertys.ConnectId)
.WithCredentials(driverPropertys.UserName, driverPropertys.Password)//账密 .WithCredentials(driverPropertys.UserName, driverPropertys.Password)//账密
.WithTcpServer(driverPropertys.IP, driverPropertys.Port)//服务器 .WithTcpServer(driverPropertys.IP, driverPropertys.Port)//服务器
.WithCleanSession(true) .WithCleanSession(true)
@@ -520,7 +521,7 @@ public class MqttClient : UpLoadBase
return OperResult.CreateSuccessResult(); return OperResult.CreateSuccessResult();
try try
{ {
await lockobj.WaitAsync(); await easyLock.WaitAsync();
if (_mqttClient?.IsConnected == true) if (_mqttClient?.IsConnected == true)
return OperResult.CreateSuccessResult(); return OperResult.CreateSuccessResult();
using var timeoutToken = new CancellationTokenSource(TimeSpan.FromMilliseconds(driverPropertys.ConnectTimeOut)); using var timeoutToken = new CancellationTokenSource(TimeSpan.FromMilliseconds(driverPropertys.ConnectTimeOut));
@@ -548,7 +549,7 @@ public class MqttClient : UpLoadBase
} }
finally finally
{ {
lockobj.Release(); easyLock.Release();
} }
} }
} }

View File

@@ -60,6 +60,25 @@
<PModal @bind-Value="IsShowImportVariableList" OnCancel="() => IsShowImportVariableList = false" <PModal @bind-Value="IsShowImportVariableList" OnCancel="() => IsShowImportVariableList = false"
Title=导入OPC变量 Height=@("700") Persistent Title=导入OPC变量 Height=@("700") Persistent
MaxWidth="1500" OnSave="DownDeviceExport"> MaxWidth="1500" OnSave="DownDeviceExport">
<SaveContent Context="save">
<MMenu OffsetY Context="menu">
<ActivatorContent>
<MButton @attributes="@menu.Attrs" Color="primary"
Class="my-1 mx-2 ">
导出
<AppChevronDown></AppChevronDown>
</MButton>
</ActivatorContent>
<ChildContent>
<MList>
<MListItem OnClick="()=>DownDeviceExport()" Disabled="save.Loading" Loading="save.Loading">导出到excel</MListItem>
<MListItem OnClick="()=>DeviceImport()" Disabled="save.Loading" Loading="save.Loading">导入到系统</MListItem>
</MList>
</ChildContent>
</MMenu>
</SaveContent>
<ChildContent> <ChildContent>
@if (IsShowImportVariableList) @if (IsShowImportVariableList)
{ {
@@ -67,11 +86,5 @@
} }
</ChildContent> </ChildContent>
<SaveContent Context="save">
<MButton Text Color="primary" OnClick="save.Click" Disabled="defalutDebugDriverPage.isDownExport" Loading="defalutDebugDriverPage.isDownExport">
<MLabel>导入</MLabel>
</MButton>
</SaveContent>
</PModal> </PModal>

View File

@@ -96,7 +96,22 @@ public partial class OPCDAClientDebugDriverPage : IDisposable
return; return;
} }
await defalutDebugDriverPage.DownDeviceExportAsync(data?.Item1); await defalutDebugDriverPage.DownDeviceExportAsync(data?.Item1);
await defalutDebugDriverPage.DownDeviceExportAsync(data?.Item2); await defalutDebugDriverPage.DownDeviceVariableExportAsync(data?.Item2, data?.Item1.Name);
await PopupService.EnqueueSnackbarAsync("<22>ɹ<EFBFBD>", AlertTypes.Success);
}
}
private async Task DeviceImport()
{
var data = ImportVariable?.GetImportVariableList();
if (data != null)
{
if (data?.Item2?.Count == 0)
{
await PopupService.EnqueueSnackbarAsync("<22>޿<EFBFBD><DEBF>ñ<EFBFBD><C3B1><EFBFBD>", AlertTypes.Warning);
return;
}
await defalutDebugDriverPage.DeviceImportAsync(data?.Item1);
await defalutDebugDriverPage.DeviceVariableImportAsync(data?.Item2);
await PopupService.EnqueueSnackbarAsync("<22>ɹ<EFBFBD>", AlertTypes.Success); await PopupService.EnqueueSnackbarAsync("<22>ɹ<EFBFBD>", AlertTypes.Success);
} }
} }

View File

@@ -89,7 +89,7 @@ public partial class ImportVariable
var dataTypeId = (Opc.Ua.NodeId)(await PLC.ReadNoteAttributeAsync(a.NodeId.ToString(), Attributes.DataType)).Content.FirstOrDefault().Value; var dataTypeId = (Opc.Ua.NodeId)(await PLC.ReadNoteAttributeAsync(a.NodeId.ToString(), Attributes.DataType)).Content.FirstOrDefault().Value;
var dataType = Opc.Ua.TypeInfo.GetSystemType(dataTypeId, PLC.Session.Factory); var dataType = Opc.Ua.TypeInfo.GetSystemType(dataTypeId, PLC.Session.Factory);
var result = dataType != null ? Enum.TryParse<DataTypeEnum>(dataType.Name, out dataTypeEnum) : false; var result = dataType != null && Enum.TryParse<DataTypeEnum>(dataType.Name, out dataTypeEnum);
if (!result) if (!result)
{ {
dataTypeEnum = DataTypeEnum.Object; dataTypeEnum = DataTypeEnum.Object;

View File

@@ -36,6 +36,34 @@ namespace ThingsGateway.OPCUA;
/// </summary> /// </summary>
public class OPCUAClient : CollectBase public class OPCUAClient : CollectBase
{ {
readonly PeriodicTimer _periodicTimer = new(TimeSpan.FromSeconds(60));
/// <summary>
/// OPCUA客户端
/// </summary>
public OPCUAClient()
{
_ = RunTimerAsync();
}
private async Task RunTimerAsync()
{
while (await _periodicTimer.WaitForNextTickAsync())
{
if (PLC != null && PLC.Session == null)
{
try
{
await PLC.ConnectAsync();
}
catch (Exception ex)
{
LogMessage.Exception(ex);
}
}
}
}
internal CollectDeviceRunTime Device; internal CollectDeviceRunTime Device;
internal Foundation.Adapter.OPCUA.OPCUAClient PLC = null; internal Foundation.Adapter.OPCUA.OPCUAClient PLC = null;
@@ -73,7 +101,11 @@ public class OPCUAClient : CollectBase
} }
/// <inheritdoc/> /// <inheritdoc/>
public override bool IsConnected() => PLC.Connected; public override bool IsConnected()
{
return PLC.Connected;
}
/// <inheritdoc/> /// <inheritdoc/>
public override List<DeviceVariableSourceRead> LoadSourceRead(List<DeviceVariableRunTime> deviceVariables) public override List<DeviceVariableSourceRead> LoadSourceRead(List<DeviceVariableRunTime> deviceVariables)
@@ -99,7 +131,6 @@ public class OPCUAClient : CollectBase
/// <inheritdoc/> /// <inheritdoc/>
public override async Task<OperResult<byte[]>> ReadSourceAsync(DeviceVariableSourceRead deviceVariableSourceRead, CancellationToken token) public override async Task<OperResult<byte[]>> ReadSourceAsync(DeviceVariableSourceRead deviceVariableSourceRead, CancellationToken token)
{ {
await Task.CompletedTask;
var result = await PLC.ReadJTokenValueAsync(deviceVariableSourceRead.DeviceVariables.Select(a => a.VariableAddress).ToArray(), token); var result = await PLC.ReadJTokenValueAsync(deviceVariableSourceRead.DeviceVariables.Select(a => a.VariableAddress).ToArray(), token);
foreach (var data in result) foreach (var data in result)
{ {
@@ -163,6 +194,7 @@ public class OPCUAClient : CollectBase
/// <inheritdoc/> /// <inheritdoc/>
protected override void Dispose(bool disposing) protected override void Dispose(bool disposing)
{ {
_periodicTimer?.Dispose();
if (PLC != null) if (PLC != null)
{ {
PLC.DataChangedHandler -= DataChangedHandler; PLC.DataChangedHandler -= DataChangedHandler;

View File

@@ -61,6 +61,25 @@
<PModal @bind-Value="IsShowImportVariableList" OnCancel="() => IsShowImportVariableList = false" <PModal @bind-Value="IsShowImportVariableList" OnCancel="() => IsShowImportVariableList = false"
Title=导入OPC变量 Height=@("700") Persistent Title=导入OPC变量 Height=@("700") Persistent
MaxWidth="1500" OnSave="DownDeviceExport"> MaxWidth="1500" OnSave="DownDeviceExport">
<SaveContent Context="save">
<MMenu OffsetY Context="menu">
<ActivatorContent>
<MButton @attributes="@menu.Attrs" Color="primary" Class="my-1 mx-2 ">
导出
<AppChevronDown></AppChevronDown>
</MButton>
</ActivatorContent>
<ChildContent>
<MList>
<MListItem OnClick="()=>DownDeviceExport()" Disabled="save.Loading" Loading="save.Loading">导出到excel</MListItem>
<MListItem OnClick="()=>DeviceImport()" Disabled="save.Loading" Loading="save.Loading">导入到系统</MListItem>
</MList>
</ChildContent>
</MMenu>
</SaveContent>
<ChildContent> <ChildContent>
@if (IsShowImportVariableList) @if (IsShowImportVariableList)
{ {
@@ -69,11 +88,6 @@
</ChildContent> </ChildContent>
<SaveContent Context="save">
<MButton Text Color="primary" OnClick="save.Click" Disabled="defalutDebugDriverPage.isDownExport" Loading="defalutDebugDriverPage.isDownExport">
<MLabel>导入</MLabel>
</MButton>
</SaveContent>
</PModal> </PModal>

View File

@@ -77,10 +77,10 @@ public partial class OPCUAClientDebugDriverPage
base.OnAfterRender(firstRender); base.OnAfterRender(firstRender);
} }
private void Add() private async Task Add()
{ {
if (_plc.Connected) if (_plc.Connected)
_plc.AddSubscription(YitIdHelper.NextId().ToString(), new[] { defalutDebugDriverPage.Address }); await _plc.AddSubscriptionAsync(YitIdHelper.NextId().ToString(), new[] { defalutDebugDriverPage.Address });
else else
{ {
defalutDebugDriverPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Debug, SysDateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset) + " - " + <><CEB4><EFBFBD><EFBFBD>")); defalutDebugDriverPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Debug, SysDateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset) + " - " + <><CEB4><EFBFBD><EFBFBD>"));
@@ -95,7 +95,20 @@ public partial class OPCUAClientDebugDriverPage
return; return;
} }
await defalutDebugDriverPage.DownDeviceExportAsync(data.Item1); await defalutDebugDriverPage.DownDeviceExportAsync(data.Item1);
await defalutDebugDriverPage.DownDeviceExportAsync(data.Item2); await defalutDebugDriverPage.DownDeviceVariableExportAsync(data.Item2, data.Item1.Name);
await PopupService.EnqueueSnackbarAsync("<22>ɹ<EFBFBD>", AlertTypes.Success);
}
private async Task DeviceImport()
{
var data = await ImportVariable?.GetImportVariableListAsync();
if (data.Item2?.Count == 0)
{
await PopupService.EnqueueSnackbarAsync("<22>޿<EFBFBD><DEBF>ñ<EFBFBD><C3B1><EFBFBD>", AlertTypes.Warning);
return;
}
await defalutDebugDriverPage.DeviceImportAsync(data.Item1);
await defalutDebugDriverPage.DeviceVariableImportAsync(data.Item2);
await PopupService.EnqueueSnackbarAsync("<22>ɹ<EFBFBD>", AlertTypes.Success); await PopupService.EnqueueSnackbarAsync("<22>ɹ<EFBFBD>", AlertTypes.Success);
} }
@@ -115,7 +128,7 @@ public partial class OPCUAClientDebugDriverPage
try try
{ {
var data = await _plc.ReadJTokenValueAsync(new string[] { defalutDebugDriverPage.Address }); var data = await _plc.ReadJTokenValueAsync(new string[] { defalutDebugDriverPage.Address });
defalutDebugDriverPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Debug, SysDateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset) + " - " + data.ToJson())); defalutDebugDriverPage.Messages.Add((Microsoft.Extensions.Logging.LogLevel.Debug, SysDateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat(InitTimezone.TimezoneOffset) + " - " + (data[0].Item1 + ":" + data[0].Item3)));
} }
catch (Exception ex) catch (Exception ex)
{ {

View File

@@ -33,6 +33,11 @@
OPCUA客户端 OPCUA客户端
</summary> </summary>
</member> </member>
<member name="M:ThingsGateway.OPCUA.OPCUAClient.#ctor">
<summary>
OPCUA客户端
</summary>
</member>
<member name="P:ThingsGateway.OPCUA.OPCUAClient.DriverDebugUIType"> <member name="P:ThingsGateway.OPCUA.OPCUAClient.DriverDebugUIType">
<inheritdoc/> <inheritdoc/>
</member> </member>

View File

@@ -12,7 +12,6 @@
global using ThingsGateway.Foundation.Adapter.Siemens; global using ThingsGateway.Foundation.Adapter.Siemens;
global using TouchSocket.Core;
global using TouchSocket.Sockets; global using TouchSocket.Sockets;

View File

@@ -33,7 +33,7 @@ namespace ThingsGateway.Siemens
IThingsGatewayBitConverter transformParameter = ByteTransformUtil.GetTransByAddress(ref address, byteConverter); IThingsGatewayBitConverter transformParameter = ByteTransformUtil.GetTransByAddress(ref address, byteConverter);
item.ThingsGatewayBitConverter = transformParameter; item.ThingsGatewayBitConverter = transformParameter;
//item.VariableAddress = address; item.VariableAddress = address;//需要使用过滤后的地址
item.Index = siemensS7Net.GetBitOffset(item.VariableAddress); item.Index = siemensS7Net.GetBitOffset(item.VariableAddress);
} }
@@ -53,7 +53,20 @@ namespace ThingsGateway.Siemens
} }
else if (it.DataTypeEnum.GetSystemType() == typeof(string)) else if (it.DataTypeEnum.GetSystemType() == typeof(string))
{ {
lastLen = it.ThingsGatewayBitConverter.StringLength; if (siemensS7Net.CurrentPlc == SiemensEnum.S200Smart)
{
//字符串在S200Smart中第一个字节不属于实际内容
it.Index += 1;
//it.ThingsGatewayBitConverter.StringLength -= 1;
lastLen = it.ThingsGatewayBitConverter.StringLength + 1;
}
else
{
//字符串在S7中前两个字节不属于实际内容
it.Index += 2;
//it.ThingsGatewayBitConverter.StringLength -= 2;
lastLen = it.ThingsGatewayBitConverter.StringLength + 2;
}
} }
else if (it.DataTypeEnum.GetSystemType() == typeof(object)) else if (it.DataTypeEnum.GetSystemType() == typeof(object))
{ {

View File

@@ -25,11 +25,11 @@
<DefalutDebugDriverPage Channel="ChannelEnum.TcpClientEx" @ref=defalutDebugDriverPage> <DefalutDebugDriverPage Channel="ChannelEnum.TcpClientEx" @ref=defalutDebugDriverPage>
<MCard Flat Elevation="0"> <MCard Flat Elevation="0">
<MRow Class="my-1" NoGutters Justify="JustifyTypes.SpaceBetween" Align="AlignTypes.Center"> <MRow Class="my-1" NoGutters Justify="JustifyTypes.SpaceBetween" Align="AlignTypes.Center">
<MTextField Class="mx-1" Label=@(_plc.Description(x => x.Slot)) Dense Outlined HideDetails="@("auto")" @bind-Value=@_plc.Slot></MTextField> <MTextField Class="ma-1" Label=@(_plc.Description(x => x.Slot)) Dense Outlined HideDetails="@("auto")" @bind-Value=@_plc.Slot></MTextField>
<MTextField Class="mx-1" Label=@(_plc.Description(x => x.Rack)) Dense Outlined HideDetails="@("auto")" @bind-Value=@_plc.Rack></MTextField> <MTextField Class="ma-1" Label=@(_plc.Description(x => x.Rack)) Dense Outlined HideDetails="@("auto")" @bind-Value=@_plc.Rack></MTextField>
<MTextField Class="mx-1" Label=@(_plc.Description(x => x.TimeOut)) Dense Outlined HideDetails="@("auto")" @bind-Value=@_plc.TimeOut></MTextField> <MTextField Class="ma-1" Label=@(_plc.Description(x => x.TimeOut)) Dense Outlined HideDetails="@("auto")" @bind-Value=@_plc.TimeOut></MTextField>
<MSelect Class="mx-1" Style="max-width:200px" @bind-Value="_plc.DataFormat" Outlined Label="@(_plc.Description(x => x.DataFormat))" <MSelect Class="ma-1" Style="max-width:200px" @bind-Value="_plc.DataFormat" Outlined Label="@(_plc.Description(x => x.DataFormat))"
Items=@(typeof(DataFormat).GetEnumList()) Items=@(typeof(DataFormat).GetEnumList())
MenuProps="@(props => { props.Auto = true; props.OffsetY = true; })" MenuProps="@(props => { props.Auto = true; props.OffsetY = true; })"
ItemText=@((u) =>u.des) ItemText=@((u) =>u.des)

View File

@@ -25,11 +25,11 @@
<DefalutDebugDriverPage Channel="ChannelEnum.TcpClientEx" @ref=defalutDebugDriverPage> <DefalutDebugDriverPage Channel="ChannelEnum.TcpClientEx" @ref=defalutDebugDriverPage>
<MCard Flat Elevation="0"> <MCard Flat Elevation="0">
<MRow Class="my-1" NoGutters Justify="JustifyTypes.SpaceBetween" Align="AlignTypes.Center"> <MRow Class="my-1" NoGutters Justify="JustifyTypes.SpaceBetween" Align="AlignTypes.Center">
<MTextField Class="mx-1" Label=@(_plc.Description(x => x.Slot)) Dense Outlined HideDetails="@("auto")" @bind-Value=@_plc.Slot></MTextField> <MTextField Class="ma-1" Label=@(_plc.Description(x => x.Slot)) Dense Outlined HideDetails="@("auto")" @bind-Value=@_plc.Slot></MTextField>
<MTextField Class="mx-1" Label=@(_plc.Description(x => x.Rack)) Dense Outlined HideDetails="@("auto")" @bind-Value=@_plc.Rack></MTextField> <MTextField Class="ma-1" Label=@(_plc.Description(x => x.Rack)) Dense Outlined HideDetails="@("auto")" @bind-Value=@_plc.Rack></MTextField>
<MTextField Class="mx-1" Label=@(_plc.Description(x => x.TimeOut)) Dense Outlined HideDetails="@("auto")" @bind-Value=@_plc.TimeOut></MTextField> <MTextField Class="ma-1" Label=@(_plc.Description(x => x.TimeOut)) Dense Outlined HideDetails="@("auto")" @bind-Value=@_plc.TimeOut></MTextField>
<MSelect Class="mx-1" Style="max-width:200px" @bind-Value="_plc.DataFormat" Outlined Label="@(_plc.Description(x => x.DataFormat))" <MSelect Class="ma-1" Style="max-width:200px" @bind-Value="_plc.DataFormat" Outlined Label="@(_plc.Description(x => x.DataFormat))"
Items=@(typeof(DataFormat).GetEnumList()) Items=@(typeof(DataFormat).GetEnumList())
MenuProps="@(props => { props.Auto = true; props.OffsetY = true; })" MenuProps="@(props => { props.Auto = true; props.OffsetY = true; })"
ItemText=@((u) =>u.des) ItemText=@((u) =>u.des)

View File

@@ -25,11 +25,11 @@
<DefalutDebugDriverPage Channel="ChannelEnum.TcpClientEx" @ref=defalutDebugDriverPage> <DefalutDebugDriverPage Channel="ChannelEnum.TcpClientEx" @ref=defalutDebugDriverPage>
<MCard Flat Elevation="0"> <MCard Flat Elevation="0">
<MRow Class="my-1" NoGutters Justify="JustifyTypes.SpaceBetween" Align="AlignTypes.Center"> <MRow Class="my-1" NoGutters Justify="JustifyTypes.SpaceBetween" Align="AlignTypes.Center">
<MTextField Class="mx-1" Label=@(_plc.Description(x => x.Slot)) Dense Outlined HideDetails="@("auto")" @bind-Value=@_plc.Slot></MTextField> <MTextField Class="ma-1" Label=@(_plc.Description(x => x.Slot)) Dense Outlined HideDetails="@("auto")" @bind-Value=@_plc.Slot></MTextField>
<MTextField Class="mx-1" Label=@(_plc.Description(x => x.Rack)) Dense Outlined HideDetails="@("auto")" @bind-Value=@_plc.Rack></MTextField> <MTextField Class="ma-1" Label=@(_plc.Description(x => x.Rack)) Dense Outlined HideDetails="@("auto")" @bind-Value=@_plc.Rack></MTextField>
<MTextField Class="mx-1" Label=@(_plc.Description(x => x.TimeOut)) Dense Outlined HideDetails="@("auto")" @bind-Value=@_plc.TimeOut></MTextField> <MTextField Class="ma-1" Label=@(_plc.Description(x => x.TimeOut)) Dense Outlined HideDetails="@("auto")" @bind-Value=@_plc.TimeOut></MTextField>
<MSelect Class="mx-1" Style="max-width:200px" @bind-Value="_plc.DataFormat" Outlined Label="@(_plc.Description(x => x.DataFormat))" <MSelect Class="ma-1" Style="max-width:200px" @bind-Value="_plc.DataFormat" Outlined Label="@(_plc.Description(x => x.DataFormat))"
Items=@(typeof(DataFormat).GetEnumList()) Items=@(typeof(DataFormat).GetEnumList())
MenuProps="@(props => { props.Auto = true; props.OffsetY = true; })" MenuProps="@(props => { props.Auto = true; props.OffsetY = true; })"
ItemText=@((u) =>u.des) ItemText=@((u) =>u.des)

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