mirror of
https://gitee.com/ThingsGateway/ThingsGateway.git
synced 2025-10-30 07:03:59 +08:00
Compare commits
6 Commits
10.10.18.0
...
10.11.4.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2d7effadf9 | ||
|
|
346c560f8b | ||
|
|
8e3bd89f61 | ||
|
|
6da142d080 | ||
|
|
ff7d029e6f | ||
|
|
21b4695683 |
@@ -37,9 +37,8 @@ public class FileController : ControllerBase
|
|||||||
var root = Directory.GetCurrentDirectory();
|
var root = Directory.GetCurrentDirectory();
|
||||||
var wwwroot = Path.Combine(root, "wwwroot");
|
var wwwroot = Path.Combine(root, "wwwroot");
|
||||||
var filePath = Path.Combine(wwwroot, fileName);
|
var filePath = Path.Combine(wwwroot, fileName);
|
||||||
// 防止路径穿越攻击
|
|
||||||
#pragma warning disable CA3003
|
#pragma warning disable CA3003
|
||||||
if (filePath.Contains("..") || !System.IO.File.Exists(filePath))
|
if ((!(fileName.StartsWith(@"../Logs") || fileName.StartsWith(@"..\Logs")) && filePath.Contains("..")) || !System.IO.File.Exists(filePath))
|
||||||
{
|
{
|
||||||
return NotFound();
|
return NotFound();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -377,9 +377,9 @@ internal sealed class SysUserService : BaseService<SysUser>, ISysUserService
|
|||||||
/// 获取用户拥有的资源
|
/// 获取用户拥有的资源
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="id">用户id</param>
|
/// <param name="id">用户id</param>
|
||||||
public async Task<GrantResourceData> OwnResourceAsync(long id)
|
public Task<GrantResourceData> OwnResourceAsync(long id)
|
||||||
{
|
{
|
||||||
return await _roleService.OwnResourceAsync(id, RelationCategoryEnum.UserHasResource).ConfigureAwait(false);
|
return _roleService.OwnResourceAsync(id, RelationCategoryEnum.UserHasResource);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -505,10 +505,10 @@ internal sealed class SysUserService : BaseService<SysUser>, ISysUserService
|
|||||||
var password = await GetDefaultPassWord(true).ConfigureAwait(false);//获取默认密码,这里不走Aop所以需要加密一下
|
var password = await GetDefaultPassWord(true).ConfigureAwait(false);//获取默认密码,这里不走Aop所以需要加密一下
|
||||||
using var db = GetDB();
|
using var db = GetDB();
|
||||||
//重置密码
|
//重置密码
|
||||||
if (await db.UpdateSetColumnsTrueAsync<SysUser>(it => new SysUser
|
if ((await db.UpdateSetColumnsTrueAsync<SysUser>(it => new SysUser
|
||||||
{
|
{
|
||||||
Password = password
|
Password = password
|
||||||
}, it => it.Id == id).ConfigureAwait(false))
|
}, it => it.Id == id).ConfigureAwait(false)) > 0)
|
||||||
{
|
{
|
||||||
DeleteUserFromCache(id);//从cache删除用户信息
|
DeleteUserFromCache(id);//从cache删除用户信息
|
||||||
var verificatInfoIds = _verificatInfoService.GetListByUserId(id);
|
var verificatInfoIds = _verificatInfoService.GetListByUserId(id);
|
||||||
|
|||||||
@@ -185,12 +185,12 @@ internal sealed class UserCenterService : BaseService<SysUser>, IUserCenterServi
|
|||||||
using var db = GetDB();
|
using var db = GetDB();
|
||||||
|
|
||||||
//更新指定字段
|
//更新指定字段
|
||||||
var result = await db.UpdateSetColumnsTrueAsync<SysUser>(it => new SysUser
|
var result = (await db.UpdateSetColumnsTrueAsync<SysUser>(it => new SysUser
|
||||||
{
|
{
|
||||||
Email = input.Email,
|
Email = input.Email,
|
||||||
Phone = input.Phone,
|
Phone = input.Phone,
|
||||||
Avatar = input.Avatar,
|
Avatar = input.Avatar,
|
||||||
}, it => it.Id == UserManager.UserId).ConfigureAwait(false);
|
}, it => it.Id == UserManager.UserId).ConfigureAwait(false)) > 0;
|
||||||
if (result)
|
if (result)
|
||||||
_userService.DeleteUserFromCache(UserManager.UserId);//cache删除用户数据
|
_userService.DeleteUserFromCache(UserManager.UserId);//cache删除用户数据
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,12 +26,12 @@
|
|||||||
OnQueryAsync="OnQueryAsync" CustomerSearchModel="@CustomerSearchModel"
|
OnQueryAsync="OnQueryAsync" CustomerSearchModel="@CustomerSearchModel"
|
||||||
OnSaveAsync="Save" OnDeleteAsync="Delete">
|
OnSaveAsync="Save" OnDeleteAsync="Delete">
|
||||||
<TableToolbarTemplate>
|
<TableToolbarTemplate>
|
||||||
<PopConfirmButton Color=Color.Warning IsDisabled="SelectedRows.Count<=0||!AuthorizeButton(AdminOperConst.Add)" Text=@OperDescLocalizer["CopyResource"] Icon="fa fa-copy" OnConfirm="OnCopy">
|
<PopConfirmButton Color=Color.Warning IsKeepDisabled="SelectedRows.Count <= 0 || !AuthorizeButton(AdminOperConst.Add)" Text=@OperDescLocalizer["CopyResource"] Icon="fa fa-copy" OnConfirm="OnCopy">
|
||||||
<BodyTemplate>
|
<BodyTemplate>
|
||||||
<Select Items="ModuleSelectedItems" @bind-Value=CopyModule ShowLabel="false" />
|
<Select Items="ModuleSelectedItems" @bind-Value=CopyModule ShowLabel="false" />
|
||||||
</BodyTemplate>
|
</BodyTemplate>
|
||||||
</PopConfirmButton>
|
</PopConfirmButton>
|
||||||
<PopConfirmButton Color=Color.Warning IsDisabled="SelectedRows.Count!=1||!AuthorizeButton(AdminOperConst.Edit)" Text=@OperDescLocalizer["ChangeParentResource"] Icon="fa fa-copy" OnConfirm="OnChangeParent">
|
<PopConfirmButton Color=Color.Warning IsKeepDisabled="SelectedRows.Count != 1 || !AuthorizeButton(AdminOperConst.Edit)" Text=@OperDescLocalizer["ChangeParentResource"] Icon="fa fa-copy" OnConfirm="OnChangeParent">
|
||||||
<BodyTemplate>
|
<BodyTemplate>
|
||||||
<div class="overflow-y-auto" style="height:500px">
|
<div class="overflow-y-auto" style="height:500px">
|
||||||
<TreeView Items="MenuTreeItems" IsVirtualize="true" OnTreeItemClick="a=>{ChangeParentId=a.Value.Id;return Task.CompletedTask;}" />
|
<TreeView Items="MenuTreeItems" IsVirtualize="true" OnTreeItemClick="a=>{ChangeParentId=a.Value.Id;return Task.CompletedTask;}" />
|
||||||
|
|||||||
@@ -29,7 +29,7 @@
|
|||||||
<Target Name="AdminPostPublish" AfterTargets="Publish">
|
<Target Name="AdminPostPublish" AfterTargets="Publish">
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<!-- setting up the variable for convenience -->
|
<!-- setting up the variable for convenience -->
|
||||||
<AdminFiles Include="bin\$(Configuration)\$(TargetFramework)\SeedData\**" />
|
<AdminFiles Include="$(OutputPath)\$(TargetFramework)\SeedData\**" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="BootstrapBlazor.TableExport" Version="9.2.6" />
|
<PackageReference Include="BootstrapBlazor.TableExport" Version="9.2.6" />
|
||||||
<PackageReference Include="Yitter.IdGenerator" Version="1.0.14" />
|
<PackageReference Include="Yitter.IdGenerator" Version="1.0.14" />
|
||||||
<PackageReference Include="BootstrapBlazor" Version="9.9.1" />
|
<PackageReference Include="BootstrapBlazor" Version="9.9.2" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -209,16 +209,10 @@ public static class SqlSugarExtensions
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public static async Task<bool> UpdateRangeAsync<T>(this SqlSugarClient db, List<T> updateObjs) where T : class, new()
|
public static Task<int> UpdateSetColumnsTrueAsync<T>(this SqlSugarClient db, Expression<Func<T, T>> columns, Expression<Func<T, bool>> whereExpression) where T : class, new()
|
||||||
{
|
{
|
||||||
return await db.Updateable(updateObjs).ExecuteCommandAsync().ConfigureAwait(false) > 0;
|
return db.Updateable<T>().SetColumns(columns, appendColumnsByDataFilter: true).Where(whereExpression)
|
||||||
}
|
.ExecuteCommandAsync();
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public static async Task<bool> UpdateSetColumnsTrueAsync<T>(this SqlSugarClient db, Expression<Func<T, T>> columns, Expression<Func<T, bool>> whereExpression) where T : class, new()
|
|
||||||
{
|
|
||||||
return await db.Updateable<T>().SetColumns(columns, appendColumnsByDataFilter: true).Where(whereExpression)
|
|
||||||
.ExecuteCommandAsync().ConfigureAwait(false) > 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IEnumerable<T> Sort<T>(this IEnumerable<T> list, BasePageInput basePageInput)
|
private static IEnumerable<T> Sort<T>(this IEnumerable<T> list, BasePageInput basePageInput)
|
||||||
|
|||||||
@@ -190,7 +190,31 @@ public sealed class Crc32 //: HashAlgorithm
|
|||||||
crc.Update(stream, count);
|
crc.Update(stream, count);
|
||||||
return crc.Value;
|
return crc.Value;
|
||||||
}
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 添加Sequence进行校验
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sequence"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public Crc32 Update(ReadOnlySequence<byte> sequence)
|
||||||
|
{
|
||||||
|
foreach (var segment in sequence)
|
||||||
|
{
|
||||||
|
Update(segment.Span);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 计算校验码 (Sequence)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sequence"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static UInt32 Compute(ReadOnlySequence<byte> sequence)
|
||||||
|
{
|
||||||
|
var crc = new Crc32();
|
||||||
|
crc.Update(sequence);
|
||||||
|
return crc.Value;
|
||||||
|
}
|
||||||
//#region 抽象实现
|
//#region 抽象实现
|
||||||
///// <summary>哈希核心</summary>
|
///// <summary>哈希核心</summary>
|
||||||
///// <param name="array"></param>
|
///// <param name="array"></param>
|
||||||
|
|||||||
@@ -41,7 +41,7 @@
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
<PopConfirmButton IsAsync IsDisabled=@_importPreviews.Any(it => it.Value.HasError) Color=Color.Warning class="mt-2" OnConfirm=@(SaveDeviceImport)>@Localizer["Import"]</PopConfirmButton>
|
<PopConfirmButton IsAsync IsKeepDisabled=@_importPreviews.Any(it => it.Value.HasError) Color=Color.Warning class="mt-2" OnConfirm=@(SaveDeviceImport)>@Localizer["Import"]</PopConfirmButton>
|
||||||
|
|
||||||
@*
|
@*
|
||||||
<Button IsAsync class="mt-2" IsDisabled=@_importPreviews.Any(it => it.Value.HasError) OnClick="() => step.Next()">@Localizer["Next"]</Button> *@
|
<Button IsAsync class="mt-2" IsDisabled=@_importPreviews.Any(it => it.Value.HasError) OnClick="() => step.Next()">@Localizer["Next"]</Button> *@
|
||||||
|
|||||||
@@ -30,11 +30,11 @@
|
|||||||
<PackageReference Include="MySqlConnector" Version="2.4.0" />
|
<PackageReference Include="MySqlConnector" Version="2.4.0" />
|
||||||
<PackageReference Include="Npgsql" Version="9.0.3" />
|
<PackageReference Include="Npgsql" Version="9.0.3" />
|
||||||
<PackageReference Include="CsvHelper" Version="33.1.0" />
|
<PackageReference Include="CsvHelper" Version="33.1.0" />
|
||||||
<PackageReference Include="TDengine.Connector" Version="3.1.7" />
|
<PackageReference Include="TDengine.Connector" Version="3.1.8" />
|
||||||
<PackageReference Include="Oracle.ManagedDataAccess.Core" Version="23.9.1" />
|
<PackageReference Include="Oracle.ManagedDataAccess.Core" Version="23.9.1" />
|
||||||
<PackageReference Include="Oscar.Data.SqlClient" Version="4.2.22" />
|
<PackageReference Include="Oscar.Data.SqlClient" Version="4.2.23" />
|
||||||
<PackageReference Include="System.Data.Common" Version="4.3.0" />
|
<PackageReference Include="System.Data.Common" Version="4.3.0" />
|
||||||
<PackageReference Include="Microsoft.Data.SqlClient" Version="6.1.0" />
|
<PackageReference Include="Microsoft.Data.SqlClient" Version="6.1.1" />
|
||||||
<PackageReference Include="System.Reflection.Emit.Lightweight" Version="4.7.0" />
|
<PackageReference Include="System.Reflection.Emit.Lightweight" Version="4.7.0" />
|
||||||
<PackageReference Include="System.Text.RegularExpressions" Version="4.3.1" />
|
<PackageReference Include="System.Text.RegularExpressions" Version="4.3.1" />
|
||||||
<PackageReference Include="System.Formats.Asn1" Version="8.0.2" />
|
<PackageReference Include="System.Formats.Asn1" Version="8.0.2" />
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
<Project>
|
<Project>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<PluginVersion>10.10.18</PluginVersion>
|
<PluginVersion>10.11.4</PluginVersion>
|
||||||
<ProPluginVersion>10.10.18</ProPluginVersion>
|
<ProPluginVersion>10.11.4</ProPluginVersion>
|
||||||
<DefaultVersion>10.10.18</DefaultVersion>
|
<DefaultVersion>10.11.4</DefaultVersion>
|
||||||
<AuthenticationVersion>10.10.1</AuthenticationVersion>
|
<AuthenticationVersion>10.11.2</AuthenticationVersion>
|
||||||
<SourceGeneratorVersion>10.10.1</SourceGeneratorVersion>
|
<SourceGeneratorVersion>10.11.2</SourceGeneratorVersion>
|
||||||
<NET8Version>8.0.19</NET8Version>
|
<NET8Version>8.0.19</NET8Version>
|
||||||
<NET9Version>9.0.8</NET9Version>
|
<NET9Version>9.0.8</NET9Version>
|
||||||
<SatelliteResourceLanguages>zh-Hans;en-US</SatelliteResourceLanguages>
|
<SatelliteResourceLanguages>zh-Hans;en-US</SatelliteResourceLanguages>
|
||||||
|
|||||||
3088
src/Drivers/ThingsGateway.AllenBradley.deps.json
Normal file
3088
src/Drivers/ThingsGateway.AllenBradley.deps.json
Normal file
File diff suppressed because it is too large
Load Diff
BIN
src/Drivers/ThingsGateway.AllenBradley.dll
Normal file
BIN
src/Drivers/ThingsGateway.AllenBradley.dll
Normal file
Binary file not shown.
@@ -89,7 +89,7 @@ public partial class ChannelComponent : ComponentBase
|
|||||||
await Channel.SetupAsync(config);
|
await Channel.SetupAsync(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
await Channel.ConnectAsync(Channel.ChannelOptions.ConnectTimeout, default);
|
await Channel.ConnectAsync(default);
|
||||||
|
|
||||||
if (OnConnectClick.HasDelegate)
|
if (OnConnectClick.HasDelegate)
|
||||||
await OnConnectClick.InvokeAsync(Channel);
|
await OnConnectClick.InvokeAsync(Channel);
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
// QQ群:605534569
|
// QQ群:605534569
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
namespace ThingsGateway.Gateway.Razor;
|
namespace ThingsGateway.Debug;
|
||||||
|
|
||||||
public class ValueTransformConfig
|
public class ValueTransformConfig
|
||||||
{
|
{
|
||||||
@@ -1,9 +1,5 @@
|
|||||||
@namespace ThingsGateway.Gateway.Razor
|
@namespace ThingsGateway.Debug
|
||||||
@using ThingsGateway.Admin.Application
|
|
||||||
@using ThingsGateway.Admin.Razor
|
|
||||||
@using ThingsGateway.Foundation
|
@using ThingsGateway.Foundation
|
||||||
@using ThingsGateway.Gateway.Application
|
|
||||||
@inherits ComponentDefault
|
|
||||||
|
|
||||||
<ValidateForm class="p-4 h-100" Model="@ValueTransformConfig" OnValidSubmit="OnSave">
|
<ValidateForm class="p-4 h-100" Model="@ValueTransformConfig" OnValidSubmit="OnSave">
|
||||||
<EditorForm AutoGenerateAllItem="false" RowType=RowType.Inline ItemsPerRow=1 LabelWidth=150 Model="ValueTransformConfig">
|
<EditorForm AutoGenerateAllItem="false" RowType=RowType.Inline ItemsPerRow=1 LabelWidth=150 Model="ValueTransformConfig">
|
||||||
@@ -14,7 +14,7 @@ using System.Text.RegularExpressions;
|
|||||||
|
|
||||||
using ThingsGateway.NewLife.Extension;
|
using ThingsGateway.NewLife.Extension;
|
||||||
|
|
||||||
namespace ThingsGateway.Gateway.Razor;
|
namespace ThingsGateway.Debug;
|
||||||
|
|
||||||
public partial class ValueTransformConfigPage
|
public partial class ValueTransformConfigPage
|
||||||
{
|
{
|
||||||
@@ -205,5 +205,10 @@ public partial class ValueTransformConfigPage
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Inject]
|
||||||
|
ToastService ToastService { get; set; }
|
||||||
|
|
||||||
|
[Inject]
|
||||||
|
IStringLocalizer<ThingsGateway.Razor._Imports> RazorLocalizer { get; set; }
|
||||||
#endregion 修改
|
#endregion 修改
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,23 @@
|
|||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
|
"ThingsGateway.Debug.ValueTransformType": {
|
||||||
|
"None": "None",
|
||||||
|
"Linear": "Linear",
|
||||||
|
"Sqrt": "Sqrt"
|
||||||
|
},
|
||||||
|
|
||||||
|
"ThingsGateway.Debug.ValueTransformConfig": {
|
||||||
|
"TransformType": "TransformType",
|
||||||
|
"MinMax": "MinMax",
|
||||||
|
"ClampToRawRange": "ClampToRawRange",
|
||||||
|
"DecimalPlaces": "DecimalPlaces",
|
||||||
|
"RawMin": "RawMin",
|
||||||
|
"RawMax": "RawMax",
|
||||||
|
"ActualMin": "ActualMin",
|
||||||
|
"ActualMax": "ActualMax"
|
||||||
|
},
|
||||||
|
|
||||||
"ThingsGateway.Debug.ChannelComponent": {
|
"ThingsGateway.Debug.ChannelComponent": {
|
||||||
"BaudRate": "Baud Rate",
|
"BaudRate": "Baud Rate",
|
||||||
"BindUrl": "Local Bind IP Address",
|
"BindUrl": "Local Bind IP Address",
|
||||||
|
|||||||
@@ -1,4 +1,21 @@
|
|||||||
{
|
{
|
||||||
|
|
||||||
|
"ThingsGateway.Debug.ValueTransformType": {
|
||||||
|
"None": "无",
|
||||||
|
"Linear": "线性",
|
||||||
|
"Sqrt": "开方"
|
||||||
|
},
|
||||||
|
"ThingsGateway.Debug.ValueTransformConfig": {
|
||||||
|
"TransformType": "转换方式",
|
||||||
|
"MinMax": "最小最大值",
|
||||||
|
"ClampToRawRange": "限制范围",
|
||||||
|
"DecimalPlaces": "保留小数位",
|
||||||
|
"RawMin": "原始最小值",
|
||||||
|
"RawMax": "原始最大值",
|
||||||
|
"ActualMin": "实际最小值",
|
||||||
|
"ActualMax": "实际最大值"
|
||||||
|
},
|
||||||
|
|
||||||
"ThingsGateway.Debug.ChannelComponent": {
|
"ThingsGateway.Debug.ChannelComponent": {
|
||||||
"BaudRate": "波特率",
|
"BaudRate": "波特率",
|
||||||
"BindUrl": "本地url",
|
"BindUrl": "本地url",
|
||||||
|
|||||||
@@ -8,6 +8,8 @@
|
|||||||
// QQ群:605534569
|
// QQ群:605534569
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
using System.Buffers;
|
||||||
|
|
||||||
namespace ThingsGateway.Foundation;
|
namespace ThingsGateway.Foundation;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -16,12 +18,12 @@ namespace ThingsGateway.Foundation;
|
|||||||
public abstract class DDPMessage : MessageBase, IResultMessage
|
public abstract class DDPMessage : MessageBase, IResultMessage
|
||||||
{
|
{
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public override int HeaderLength => 4;
|
public override long HeaderLength => 4;
|
||||||
public byte Type = 0;
|
public byte Type = 0;
|
||||||
public string Id;
|
public string Id;
|
||||||
public override FilterResult CheckBody<TByteBlock>(ref TByteBlock byteBlock)
|
public override FilterResult CheckBody<TByteBlock>(ref TByteBlock byteBlock)
|
||||||
{
|
{
|
||||||
Id = byteBlock.ToString(byteBlock.Position, 11).Replace("\0", "");
|
Id = byteBlock.ToString(byteBlock.BytesRead, 11).Replace("\0", "");
|
||||||
OperCode = 0;
|
OperCode = 0;
|
||||||
|
|
||||||
Content = GetContent(ref byteBlock);
|
Content = GetContent(ref byteBlock);
|
||||||
@@ -44,31 +46,31 @@ public abstract class DDPMessage : MessageBase, IResultMessage
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract int GetBodyLength<TByteBlock>(ref TByteBlock byteBlock) where TByteBlock : IByteBlockReader;
|
public abstract long GetBodyLength<TByteBlock>(ref TByteBlock byteBlock) where TByteBlock : IBytesReader;
|
||||||
public abstract byte[] GetContent<TByteBlock>(ref TByteBlock byteBlock) where TByteBlock : IByteBlockReader;
|
public abstract byte[] GetContent<TByteBlock>(ref TByteBlock byteBlock) where TByteBlock : IBytesReader;
|
||||||
}
|
}
|
||||||
|
|
||||||
public class DDPTcpMessage : DDPMessage
|
public class DDPTcpMessage : DDPMessage
|
||||||
{
|
{
|
||||||
public override int GetBodyLength<TByteBlock>(ref TByteBlock byteBlock)
|
public override long GetBodyLength<TByteBlock>(ref TByteBlock byteBlock)
|
||||||
{
|
{
|
||||||
return ReaderExtension.ReadValue<TByteBlock, ushort>(ref byteBlock, EndianType.Big) - 4;
|
return ReaderExtension.ReadValue<TByteBlock, ushort>(ref byteBlock, EndianType.Big) - 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override byte[] GetContent<TByteBlock>(ref TByteBlock byteBlock)
|
public override byte[] GetContent<TByteBlock>(ref TByteBlock byteBlock)
|
||||||
{
|
{
|
||||||
return byteBlock.Span.Slice(byteBlock.Position + 11, BodyLength - 12).ToArray();
|
return byteBlock.TotalSequence.Slice(byteBlock.BytesRead + 11, BodyLength - 12).ToArray();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class DDPUdpMessage : DDPMessage
|
public class DDPUdpMessage : DDPMessage
|
||||||
{
|
{
|
||||||
public override int GetBodyLength<TByteBlock>(ref TByteBlock byteBlock)
|
public override long GetBodyLength<TByteBlock>(ref TByteBlock byteBlock)
|
||||||
{
|
{
|
||||||
return byteBlock.Length - 4;
|
return (byteBlock.BytesRead + byteBlock.BytesRemaining - 4);
|
||||||
}
|
}
|
||||||
public override byte[] GetContent<TByteBlock>(ref TByteBlock byteBlock)
|
public override byte[] GetContent<TByteBlock>(ref TByteBlock byteBlock)
|
||||||
{
|
{
|
||||||
return byteBlock.Span.Slice(byteBlock.Position + 12, BodyLength - 12).ToArray();
|
return byteBlock.TotalSequence.Slice(byteBlock.BytesRead + 12, BodyLength - 12).ToArray();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -25,6 +25,7 @@ public class DDPSend : ISendMessage
|
|||||||
string Id;
|
string Id;
|
||||||
byte Command;
|
byte Command;
|
||||||
bool Tcp;
|
bool Tcp;
|
||||||
|
|
||||||
public DDPSend(ReadOnlyMemory<byte> readOnlyMemory, string id, bool tcp, byte command = 0x89)
|
public DDPSend(ReadOnlyMemory<byte> readOnlyMemory, string id, bool tcp, byte command = 0x89)
|
||||||
{
|
{
|
||||||
Tcp = tcp;
|
Tcp = tcp;
|
||||||
@@ -32,7 +33,8 @@ public class DDPSend : ISendMessage
|
|||||||
Id = id;
|
Id = id;
|
||||||
Command = command;
|
Command = command;
|
||||||
}
|
}
|
||||||
public void Build<TByteBlock>(ref TByteBlock byteBlock) where TByteBlock : IByteBlockWriter
|
|
||||||
|
public void Build<TByteBlock>(ref TByteBlock byteBlock) where TByteBlock : IBytesWriter
|
||||||
{
|
{
|
||||||
WriterExtension.WriteValue(ref byteBlock, (byte)0x7b);
|
WriterExtension.WriteValue(ref byteBlock, (byte)0x7b);
|
||||||
WriterExtension.WriteValue(ref byteBlock, (byte)Command);
|
WriterExtension.WriteValue(ref byteBlock, (byte)Command);
|
||||||
|
|||||||
@@ -10,8 +10,6 @@
|
|||||||
|
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
using ThingsGateway.NewLife;
|
|
||||||
|
|
||||||
using TouchSocket.Resources;
|
using TouchSocket.Resources;
|
||||||
|
|
||||||
namespace ThingsGateway.Foundation;
|
namespace ThingsGateway.Foundation;
|
||||||
@@ -33,50 +31,120 @@ public class DDPTcpSessionClientChannel : TcpSessionClientChannel
|
|||||||
DDPAdapter.Config(Config);
|
DDPAdapter.Config(Config);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 将当前实例的日志记录器和加载回调设置到适配器中
|
|
||||||
DDPAdapter.Logger = Logger;
|
|
||||||
DDPAdapter.OnLoaded(this);
|
DDPAdapter.OnLoaded(this);
|
||||||
|
|
||||||
DDPAdapter.SendAsyncCallBack = DDPSendAsync;
|
|
||||||
DDPAdapter.ReceivedAsyncCallBack = DDPHandleReceivedData;
|
|
||||||
DataHandlingAdapter.SendAsyncCallBack = DefaultSendAsync;
|
|
||||||
return base.OnTcpConnected(e);
|
return base.OnTcpConnected(e);
|
||||||
}
|
}
|
||||||
protected Task DefaultSendAsync(ReadOnlyMemory<byte> memory, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
return DDPAdapter.SendInputAsync(new DDPSend(memory, Id, true), cancellationToken);
|
|
||||||
}
|
|
||||||
protected Task DDPSendAsync(ReadOnlyMemory<byte> memory, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
return base.ProtectedDefaultSendAsync(memory, cancellationToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
private DDPMessage DDPMessage { get; set; }
|
#region 发送
|
||||||
private Task DDPHandleReceivedData(IByteBlockReader byteBlock, IRequestInfo requestInfo)
|
|
||||||
|
/// <summary>
|
||||||
|
/// 异步发送数据,通过适配器模式灵活处理数据发送。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="memory">待发送的只读字节内存块。</param>
|
||||||
|
/// <param name="token">可取消令箭</param>
|
||||||
|
/// <returns>一个异步任务,表示发送操作。</returns>
|
||||||
|
protected virtual async Task NewProtectedSendAsync(ReadOnlyMemory<byte> memory, CancellationToken token)
|
||||||
{
|
{
|
||||||
if (requestInfo is DDPMessage dDPMessage)
|
this.ThrowIfDisposed();
|
||||||
DDPMessage = dDPMessage;
|
this.ThrowIfClientNotConnected();
|
||||||
|
|
||||||
return EasyTask.CompletedTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
private DeviceSingleStreamDataHandleAdapter<DDPTcpMessage> DDPAdapter = new();
|
if (!await this.OnTcpSending(memory).ConfigureAwait(false)) return;
|
||||||
private WaitLock _waitLock = new(nameof(DDPTcpSessionClientChannel));
|
|
||||||
|
|
||||||
protected override async ValueTask<bool> OnTcpReceiving(IByteBlockReader byteBlock)
|
var transport = this.Transport;
|
||||||
{
|
var adapter = this.DataHandlingAdapter;
|
||||||
DDPMessage? message = null;
|
var locker = transport.SemaphoreSlimForWriter;
|
||||||
|
|
||||||
|
await locker.WaitAsync(token).ConfigureAwait(false);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await _waitLock.WaitAsync().ConfigureAwait(false);
|
// 如果数据处理适配器未设置,则使用默认发送方式。
|
||||||
await DDPAdapter.ReceivedInputAsync(byteBlock).ConfigureAwait(false);
|
if (adapter == null)
|
||||||
|
{
|
||||||
message = DDPMessage;
|
await transport.Output.WriteAsync(memory, token).ConfigureAwait(false);
|
||||||
DDPMessage = null;
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var byteBlock = new ByteBlock(1024);
|
||||||
|
var ddpSend = new DDPSend(memory, Id, true);
|
||||||
|
ddpSend.Build(ref byteBlock);
|
||||||
|
var newMemory = byteBlock.Memory;
|
||||||
|
var writer = new PipeBytesWriter(transport.Output);
|
||||||
|
adapter.SendInput(ref writer, in newMemory);
|
||||||
|
await writer.FlushAsync(token).ConfigureAwait(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
_waitLock.Release();
|
locker.Release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 异步发送请求信息的受保护方法。
|
||||||
|
///
|
||||||
|
/// 此方法首先检查当前对象是否能够发送请求信息,如果不能,则抛出异常。
|
||||||
|
/// 如果可以发送,它将使用数据处理适配器来异步发送输入请求。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="requestInfo">要发送的请求信息。</param>
|
||||||
|
/// <param name="token">可取消令箭</param>
|
||||||
|
/// <returns>返回一个任务,该任务代表异步操作的结果。</returns>
|
||||||
|
protected virtual async Task NewProtectedSendAsync(IRequestInfo requestInfo, CancellationToken token)
|
||||||
|
{
|
||||||
|
// 检查是否具备发送请求的条件,如果不具备则抛出异常
|
||||||
|
this.ThrowIfCannotSendRequestInfo();
|
||||||
|
|
||||||
|
this.ThrowIfDisposed();
|
||||||
|
this.ThrowIfClientNotConnected();
|
||||||
|
|
||||||
|
var transport = this.Transport;
|
||||||
|
var adapter = this.DataHandlingAdapter;
|
||||||
|
var locker = transport.SemaphoreSlimForWriter;
|
||||||
|
|
||||||
|
await locker.WaitAsync(token).ConfigureAwait(false);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var byteBlock = new ByteBlock(1024);
|
||||||
|
if (requestInfo is not IRequestInfoBuilder requestInfoBuilder)
|
||||||
|
{
|
||||||
|
throw new Exception();
|
||||||
|
}
|
||||||
|
requestInfoBuilder.Build(ref byteBlock);
|
||||||
|
var ddpSend = new DDPSend(byteBlock.Memory, Id, true);
|
||||||
|
|
||||||
|
var writer = new PipeBytesWriter(transport.Output);
|
||||||
|
adapter.SendInput(ref writer, ddpSend);
|
||||||
|
await writer.FlushAsync(token).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
locker.Release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endregion 发送
|
||||||
|
public override Task SendAsync(IRequestInfo requestInfo, CancellationToken token = default)
|
||||||
|
{
|
||||||
|
return NewProtectedSendAsync(requestInfo, token);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Task SendAsync(ReadOnlyMemory<byte> memory, CancellationToken token = default)
|
||||||
|
{
|
||||||
|
return NewProtectedSendAsync(memory, token);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private DeviceSingleStreamDataHandleAdapter<DDPTcpMessage> DDPAdapter = new();
|
||||||
|
|
||||||
|
protected override async ValueTask<bool> OnTcpReceiving(IBytesReader byteBlock)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (DDPAdapter.TryParseRequest(ref byteBlock, out var message))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (message != null)
|
if (message != null)
|
||||||
@@ -90,11 +158,11 @@ public class DDPTcpSessionClientChannel : TcpSessionClientChannel
|
|||||||
|
|
||||||
if (this.DataHandlingAdapter == null)
|
if (this.DataHandlingAdapter == null)
|
||||||
{
|
{
|
||||||
await this.OnTcpReceived(new ReceivedDataEventArgs(reader, default)).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
await this.OnTcpReceived(new ReceivedDataEventArgs(message.Content, default)).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
await this.DataHandlingAdapter.ReceivedInputAsync(reader).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
await this.DataHandlingAdapter.ReceivedInputAsync(reader).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -127,16 +195,16 @@ public class DDPTcpSessionClientChannel : TcpSessionClientChannel
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await ResetIdAsync(id).ConfigureAwait(false);
|
await ResetIdAsync(id, ClosedToken).ConfigureAwait(false);
|
||||||
|
|
||||||
//发送成功
|
//发送成功
|
||||||
await DDPAdapter.SendInputAsync(new DDPSend(ReadOnlyMemory<byte>.Empty, id, true, 0x81), ClosedToken).ConfigureAwait(false);
|
await base.ProtectedSendAsync(new DDPSend(ReadOnlyMemory<byte>.Empty, id, true, 0x81), ClosedToken).ConfigureAwait(false);
|
||||||
if (log)
|
if (log)
|
||||||
Logger?.Info(string.Format(AppResource.DtuConnected, Id));
|
Logger?.Info(string.Format(AppResource.DtuConnected, Id));
|
||||||
}
|
}
|
||||||
else if (message.Type == 0x02)
|
else if (message.Type == 0x02)
|
||||||
{
|
{
|
||||||
await DDPAdapter.SendInputAsync(new DDPSend(ReadOnlyMemory<byte>.Empty, Id, true, 0x82), ClosedToken).ConfigureAwait(false);
|
await base.ProtectedSendAsync(new DDPSend(ReadOnlyMemory<byte>.Empty, Id, true, 0x82), ClosedToken).ConfigureAwait(false);
|
||||||
Logger?.Info(string.Format(AppResource.DtuDisconnecting, Id));
|
Logger?.Info(string.Format(AppResource.DtuDisconnecting, Id));
|
||||||
await Task.Delay(100).ConfigureAwait(false);
|
await Task.Delay(100).ConfigureAwait(false);
|
||||||
await this.CloseAsync().ConfigureAwait(false);
|
await this.CloseAsync().ConfigureAwait(false);
|
||||||
|
|||||||
@@ -36,16 +36,12 @@ public class DDPUdpSessionChannel : UdpSessionChannel, IClientChannel, IDtuUdpSe
|
|||||||
DDPAdapter.Config(Config);
|
DDPAdapter.Config(Config);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 将当前实例的日志记录器和加载回调设置到适配器中
|
|
||||||
DDPAdapter.Logger = Logger;
|
|
||||||
|
|
||||||
if (DDPAdapter.Owner != null)
|
if (DDPAdapter.Owner != null)
|
||||||
{
|
{
|
||||||
DDPAdapter.OnLoaded(this);
|
DDPAdapter.OnLoaded(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
DDPAdapter.SendCallBackAsync = DDPSendAsync;
|
DDPAdapter.SendCallBackAsync = base.ProtectedDefaultSendAsync;
|
||||||
DDPAdapter.ReceivedCallBack = DDPHandleReceivedData;
|
|
||||||
DataHandlingAdapter.SendCallBackAsync = DefaultSendAsync;
|
DataHandlingAdapter.SendCallBackAsync = DefaultSendAsync;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,22 +58,7 @@ public class DDPUdpSessionChannel : UdpSessionChannel, IClientChannel, IDtuUdpSe
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Task DDPSendAsync(EndPoint endPoint, ReadOnlyMemory<byte> memory, CancellationToken token)
|
|
||||||
{
|
|
||||||
//获取endpoint
|
|
||||||
return base.ProtectedDefaultSendAsync(endPoint, memory, token);
|
|
||||||
}
|
|
||||||
|
|
||||||
private ConcurrentDictionary<EndPoint, DDPMessage> DDPMessageDict { get; set; } = new();
|
|
||||||
private Task DDPHandleReceivedData(EndPoint endPoint, IByteBlockReader byteBlock, IRequestInfo requestInfo)
|
|
||||||
{
|
|
||||||
if (requestInfo is DDPMessage dDPMessage)
|
|
||||||
{
|
|
||||||
DDPMessageDict.AddOrUpdate(endPoint, dDPMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
return EasyTask.CompletedTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
private DeviceUdpDataHandleAdapter<DDPUdpMessage> DDPAdapter = new();
|
private DeviceUdpDataHandleAdapter<DDPUdpMessage> DDPAdapter = new();
|
||||||
|
|
||||||
@@ -98,27 +79,14 @@ public class DDPUdpSessionChannel : UdpSessionChannel, IClientChannel, IDtuUdpSe
|
|||||||
return base.StopAsync(token);
|
return base.StopAsync(token);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ConcurrentDictionary<EndPoint, WaitLock> _waitLocks = new();
|
|
||||||
|
|
||||||
protected override async ValueTask<bool> OnUdpReceiving(UdpReceiveingEventArgs e)
|
protected override async ValueTask<bool> OnUdpReceiving(UdpReceiveingEventArgs e)
|
||||||
{
|
{
|
||||||
var byteBlock = e.ByteBlock;
|
var byteBlock = e.Memory;
|
||||||
var endPoint = e.EndPoint;
|
var endPoint = e.EndPoint;
|
||||||
DDPMessage? message = null;
|
|
||||||
var waitLock = _waitLocks.GetOrAdd(endPoint, new WaitLock(nameof(DDPUdpSessionChannel)));
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await waitLock.WaitAsync().ConfigureAwait(false);
|
|
||||||
await DDPAdapter.ReceivedInput(endPoint, byteBlock).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
|
||||||
|
|
||||||
if (DDPMessageDict.TryGetValue(endPoint, out var dDPMessage))
|
if (!DDPAdapter.TryParseRequest(endPoint, byteBlock, out var message))
|
||||||
message = dDPMessage;
|
return true;
|
||||||
DDPMessageDict.TryRemove(endPoint, out _);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
waitLock.Release();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (message != null)
|
if (message != null)
|
||||||
{
|
{
|
||||||
@@ -127,15 +95,13 @@ public class DDPUdpSessionChannel : UdpSessionChannel, IClientChannel, IDtuUdpSe
|
|||||||
var id = $"ID={message.Id}";
|
var id = $"ID={message.Id}";
|
||||||
if (message.Type == 0x09)
|
if (message.Type == 0x09)
|
||||||
{
|
{
|
||||||
var reader = new ByteBlockReader(message.Content);
|
|
||||||
|
|
||||||
if (this.DataHandlingAdapter == null)
|
if (this.DataHandlingAdapter == null)
|
||||||
{
|
{
|
||||||
await this.OnUdpReceived(new UdpReceivedDataEventArgs(endPoint, reader, default)).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
await this.OnUdpReceived(new UdpReceivedDataEventArgs(endPoint, message.Content, default)).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
await this.DataHandlingAdapter.ReceivedInput(endPoint, reader).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
await this.DataHandlingAdapter.ReceivedInputAsync(endPoint, message.Content).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -8,8 +8,6 @@
|
|||||||
// QQ群:605534569
|
// QQ群:605534569
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
using System.Collections.Concurrent;
|
|
||||||
|
|
||||||
namespace ThingsGateway.Foundation;
|
namespace ThingsGateway.Foundation;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -59,15 +57,9 @@ public interface IChannel : ISetupConfigObject, IDisposable, IClosableClient, IC
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public ChannelEventHandler Stoping { get; }
|
public ChannelEventHandler Stoping { get; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 主动请求时的等待池
|
|
||||||
/// </summary>
|
|
||||||
public ConcurrentDictionary<long, Func<IClientChannel, ReceivedDataEventArgs, bool, Task>> ChannelReceivedWaitDict { get; }
|
|
||||||
|
|
||||||
void ResetSign(int minSign = 0, int maxSign = ushort.MaxValue);
|
void ResetSign(int minSign = 0, int maxSign = ushort.MaxValue);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ public interface IClientChannel : IChannel, ISender, IClient, IClientSender, IOn
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
DataHandlingAdapter ReadOnlyDataHandlingAdapter { get; }
|
DataHandlingAdapter ReadOnlyDataHandlingAdapter { get; }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 通道等待池
|
/// 通道等待池
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -34,4 +36,6 @@ public interface IClientChannel : IChannel, ISender, IClient, IClientSender, IOn
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="adapter">适配器</param>
|
/// <param name="adapter">适配器</param>
|
||||||
void SetDataHandlingAdapter(DataHandlingAdapter adapter);
|
void SetDataHandlingAdapter(DataHandlingAdapter adapter);
|
||||||
|
|
||||||
|
void SetDataHandlingAdapterLogger(ILog log);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,8 +8,6 @@
|
|||||||
// QQ群:605534569
|
// QQ群:605534569
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
using System.Collections.Concurrent;
|
|
||||||
|
|
||||||
using ThingsGateway.NewLife;
|
using ThingsGateway.NewLife;
|
||||||
|
|
||||||
using TouchSocket.SerialPorts;
|
using TouchSocket.SerialPorts;
|
||||||
@@ -31,13 +29,23 @@ public class OtherChannel : SetupConfigObject, IClientChannel
|
|||||||
}
|
}
|
||||||
|
|
||||||
public override TouchSocketConfig Config => base.Config ?? ChannelOptions.Config;
|
public override TouchSocketConfig Config => base.Config ?? ChannelOptions.Config;
|
||||||
|
public void SetDataHandlingAdapterLogger(ILog log)
|
||||||
|
{
|
||||||
|
if (_deviceDataHandleAdapter == null && ReadOnlyDataHandlingAdapter is IDeviceDataHandleAdapter handleAdapter)
|
||||||
|
{
|
||||||
|
_deviceDataHandleAdapter = handleAdapter;
|
||||||
|
}
|
||||||
|
if (_deviceDataHandleAdapter != null)
|
||||||
|
{
|
||||||
|
_deviceDataHandleAdapter.Logger = log;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void ResetSign(int minSign = 0, int maxSign = ushort.MaxValue)
|
public void ResetSign(int minSign = 0, int maxSign = ushort.MaxValue)
|
||||||
{
|
{
|
||||||
var pool = WaitHandlePool;
|
var pool = WaitHandlePool;
|
||||||
WaitHandlePool = new WaitHandlePool<MessageBase>(minSign, maxSign);
|
WaitHandlePool = new WaitHandlePool<MessageBase>(minSign, maxSign);
|
||||||
pool?.CancelAll();
|
pool?.CancelAll();
|
||||||
pool?.SafeDispose();
|
|
||||||
}
|
}
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public ChannelReceivedEventHandler ChannelReceived { get; } = new();
|
public ChannelReceivedEventHandler ChannelReceived { get; } = new();
|
||||||
@@ -65,22 +73,25 @@ public class OtherChannel : SetupConfigObject, IClientChannel
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 等待池
|
/// 等待池
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public WaitHandlePool<MessageBase> WaitHandlePool { get; internal set; } = new();
|
public WaitHandlePool<MessageBase> WaitHandlePool { get; internal set; } = new(0, ushort.MaxValue);
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public WaitLock WaitLock => ChannelOptions.WaitLock;
|
public WaitLock WaitLock => ChannelOptions.WaitLock;
|
||||||
public virtual WaitLock GetLock(string key) => WaitLock;
|
public virtual WaitLock GetLock(string key) => WaitLock;
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public ConcurrentDictionary<long, Func<IClientChannel, ReceivedDataEventArgs, bool, Task>> ChannelReceivedWaitDict { get; } = new();
|
|
||||||
|
|
||||||
//private readonly WaitLock _connectLock = new WaitLock();
|
//private readonly WaitLock _connectLock = new WaitLock();
|
||||||
|
|
||||||
|
private IDeviceDataHandleAdapter _deviceDataHandleAdapter;
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public void SetDataHandlingAdapter(DataHandlingAdapter adapter)
|
public void SetDataHandlingAdapter(DataHandlingAdapter adapter)
|
||||||
{
|
{
|
||||||
if (adapter is SingleStreamDataHandlingAdapter singleStreamDataHandlingAdapter)
|
if (adapter is SingleStreamDataHandlingAdapter singleStreamDataHandlingAdapter)
|
||||||
SetAdapter(singleStreamDataHandlingAdapter);
|
SetAdapter(singleStreamDataHandlingAdapter);
|
||||||
|
if (adapter is IDeviceDataHandleAdapter deviceDataHandleAdapter)
|
||||||
|
_deviceDataHandleAdapter = deviceDataHandleAdapter;
|
||||||
}
|
}
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 设置数据处理适配器。
|
/// 设置数据处理适配器。
|
||||||
@@ -104,20 +115,17 @@ public class OtherChannel : SetupConfigObject, IClientChannel
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 设置适配器的日志记录器和加载、接收数据的回调方法。
|
// 设置适配器的日志记录器和加载、接收数据的回调方法。
|
||||||
adapter.Logger = Logger;
|
|
||||||
adapter.OnLoaded(this);
|
adapter.OnLoaded(this);
|
||||||
adapter.ReceivedAsyncCallBack = PrivateHandleReceivedData;
|
adapter.ReceivedAsyncCallBack = PrivateHandleReceivedData;
|
||||||
//adapter.SendCallBack = this.ProtectedDefaultSend;
|
|
||||||
adapter.SendAsyncCallBack = ProtectedDefaultSendAsync;
|
|
||||||
|
|
||||||
// 将提供的适配器实例设置为当前实例的数据处理适配器。
|
// 将提供的适配器实例设置为当前实例的数据处理适配器。
|
||||||
m_dataHandlingAdapter = adapter;
|
m_dataHandlingAdapter = adapter;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task PrivateHandleReceivedData(IByteBlockReader byteBlock, IRequestInfo requestInfo)
|
private Task PrivateHandleReceivedData(ReadOnlyMemory<byte> byteBlock, IRequestInfo requestInfo)
|
||||||
{
|
{
|
||||||
LastReceivedTime = DateTime.Now;
|
LastReceivedTime = DateTime.Now;
|
||||||
await this.OnChannelReceivedEvent(new ReceivedDataEventArgs(byteBlock, requestInfo), ChannelReceived).ConfigureAwait(false);
|
return this.OnChannelReceivedEvent(new ReceivedDataEventArgs(byteBlock, requestInfo), ChannelReceived);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -154,7 +162,8 @@ public class OtherChannel : SetupConfigObject, IClientChannel
|
|||||||
return Task.FromResult(Result.Success);
|
return Task.FromResult(Result.Success);
|
||||||
}
|
}
|
||||||
public volatile bool online;
|
public volatile bool online;
|
||||||
public Task ConnectAsync(int millisecondsTimeout, CancellationToken token)
|
|
||||||
|
public Task ConnectAsync(CancellationToken token)
|
||||||
{
|
{
|
||||||
var cts = m_transport;
|
var cts = m_transport;
|
||||||
m_transport = new();
|
m_transport = new();
|
||||||
@@ -180,8 +189,11 @@ public class OtherChannel : SetupConfigObject, IClientChannel
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// 否则,使用适配器的发送方法进行数据发送。
|
var byteBlock = new ByteBlock(1024);
|
||||||
return m_dataHandlingAdapter.SendInputAsync(memory, cancellationToken);
|
m_dataHandlingAdapter.SendInput(ref byteBlock, memory);
|
||||||
|
|
||||||
|
byteBlock.SafeDispose();
|
||||||
|
return EasyTask.CompletedTask;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -190,9 +202,14 @@ public class OtherChannel : SetupConfigObject, IClientChannel
|
|||||||
// 检查是否具备发送请求的条件,如果不具备则抛出异常
|
// 检查是否具备发送请求的条件,如果不具备则抛出异常
|
||||||
ThrowIfCannotSendRequestInfo();
|
ThrowIfCannotSendRequestInfo();
|
||||||
|
|
||||||
// 使用数据处理适配器异步发送输入请求
|
var byteBlock = new ByteBlock(1024);
|
||||||
return m_dataHandlingAdapter.SendInputAsync(requestInfo, cancellationToken);
|
m_dataHandlingAdapter.SendInput(ref byteBlock, requestInfo);
|
||||||
|
|
||||||
|
byteBlock.SafeDispose();
|
||||||
|
return EasyTask.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void ThrowIfCannotSendRequestInfo()
|
private void ThrowIfCannotSendRequestInfo()
|
||||||
{
|
{
|
||||||
if (m_dataHandlingAdapter?.CanSendRequestInfo != true)
|
if (m_dataHandlingAdapter?.CanSendRequestInfo != true)
|
||||||
@@ -200,4 +217,6 @@ public class OtherChannel : SetupConfigObject, IClientChannel
|
|||||||
throw new NotSupportedException($"当前适配器为空或者不支持对象发送。");
|
throw new NotSupportedException($"当前适配器为空或者不支持对象发送。");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,19 +59,18 @@ public class DtuPlugin : PluginBase, ITcpReceivingPlugin
|
|||||||
public bool DtuIdHex { get; set; }
|
public bool DtuIdHex { get; set; }
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public async Task OnTcpReceiving(ITcpSession client, ByteBlockEventArgs e)
|
public async Task OnTcpReceiving(ITcpSession client, BytesReaderEventArgs e)
|
||||||
{
|
{
|
||||||
var len = HeartbeatByte.Length;
|
var len = HeartbeatByte.Length;
|
||||||
if (client is TcpSessionClientChannel socket && socket.Service is ITcpServiceChannel tcpServiceChannel)
|
if (client is TcpSessionClientChannel socket && socket.Service is ITcpServiceChannel tcpServiceChannel)
|
||||||
{
|
{
|
||||||
if (!socket.Id.StartsWith("ID="))
|
if (!socket.Id.StartsWith("ID="))
|
||||||
{
|
{
|
||||||
var id = DtuIdHex ? $"ID={e.ByteBlock.Span.ToHexString()}" : $"ID={e.ByteBlock.ToString(0, e.ByteBlock.Length)}";
|
var id = DtuIdHex ? $"ID={e.Reader.ToHexString()}" : $"ID={e.Reader.ToString()}";
|
||||||
if (tcpServiceChannel.TryGetClient(id, out var oldClient))
|
if (tcpServiceChannel.TryGetClient(id, out var oldClient))
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
//await oldClient.ShutdownAsync(System.Net.Sockets.SocketShutdown.Both).ConfigureAwait(false);
|
|
||||||
await oldClient.CloseAsync().ConfigureAwait(false);
|
await oldClient.CloseAsync().ConfigureAwait(false);
|
||||||
oldClient.Dispose();
|
oldClient.Dispose();
|
||||||
}
|
}
|
||||||
@@ -79,7 +78,7 @@ public class DtuPlugin : PluginBase, ITcpReceivingPlugin
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
await socket.ResetIdAsync(id).ConfigureAwait(false);
|
await socket.ResetIdAsync(id, client.ClosedToken).ConfigureAwait(false);
|
||||||
client.Logger?.Info(string.Format(AppResource.DtuConnected, id));
|
client.Logger?.Info(string.Format(AppResource.DtuConnected, id));
|
||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
}
|
}
|
||||||
@@ -88,7 +87,6 @@ public class DtuPlugin : PluginBase, ITcpReceivingPlugin
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
//await socket.ShutdownAsync(System.Net.Sockets.SocketShutdown.Both).ConfigureAwait(false);
|
|
||||||
await socket.CloseAsync().ConfigureAwait(false);
|
await socket.CloseAsync().ConfigureAwait(false);
|
||||||
socket.Dispose();
|
socket.Dispose();
|
||||||
}
|
}
|
||||||
@@ -102,11 +100,11 @@ public class DtuPlugin : PluginBase, ITcpReceivingPlugin
|
|||||||
|
|
||||||
if (len > 0)
|
if (len > 0)
|
||||||
{
|
{
|
||||||
if (HeartbeatByte.Span.SequenceEqual(e.ByteBlock.Memory.Slice(0, len).Span))
|
if (HeartbeatByte.Span.SequenceEqual(e.Reader.TotalSequence.Slice(0, len).First.Span))
|
||||||
{
|
{
|
||||||
if (DateTimeOffset.Now - socket.LastSentTime < TimeSpan.FromMilliseconds(200))
|
if (DateTimeOffset.Now - socket.LastSentTime < TimeSpan.FromMilliseconds(200))
|
||||||
{
|
{
|
||||||
await Task.Delay(200).ConfigureAwait(false);
|
await Task.Delay(200, client.ClosedToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
//回应心跳包
|
//回应心跳包
|
||||||
await socket.SendAsync(HeartbeatByte, socket.ClosedToken).ConfigureAwait(false);
|
await socket.SendAsync(HeartbeatByte, socket.ClosedToken).ConfigureAwait(false);
|
||||||
@@ -118,4 +116,6 @@ public class DtuPlugin : PluginBase, ITcpReceivingPlugin
|
|||||||
}
|
}
|
||||||
await e.InvokeNext().ConfigureAwait(false);//如果本插件无法处理当前数据,请将数据转至下一个插件。
|
await e.InvokeNext().ConfigureAwait(false);//如果本插件无法处理当前数据,请将数据转至下一个插件。
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ internal sealed class HeartbeatAndReceivePlugin : PluginBase, ITcpConnectedPlugi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private Task Task;
|
private Task _task;
|
||||||
private bool SendHeartbeat;
|
private bool SendHeartbeat;
|
||||||
public int HeartbeatTime { get; set; } = 3000;
|
public int HeartbeatTime { get; set; } = 3000;
|
||||||
|
|
||||||
@@ -125,17 +125,17 @@ internal sealed class HeartbeatAndReceivePlugin : PluginBase, ITcpConnectedPlugi
|
|||||||
{
|
{
|
||||||
await tcpClient.SendAsync(DtuIdByte, tcpClient.ClosedToken).ConfigureAwait(false);
|
await tcpClient.SendAsync(DtuIdByte, tcpClient.ClosedToken).ConfigureAwait(false);
|
||||||
|
|
||||||
if (Task == null)
|
if (_task == null)
|
||||||
{
|
{
|
||||||
Task = Task.Factory.StartNew(async () =>
|
_task = Task.Run(async () =>
|
||||||
{
|
{
|
||||||
var failedCount = 0;
|
var failedCount = 0;
|
||||||
while (SendHeartbeat)
|
while (SendHeartbeat)
|
||||||
{
|
{
|
||||||
await Task.Delay(HeartbeatTime).ConfigureAwait(false);
|
await Task.Delay(HeartbeatTime, client.ClosedToken).ConfigureAwait(false);
|
||||||
if (!client.Online)
|
if (!client.Online)
|
||||||
{
|
{
|
||||||
continue;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
@@ -159,15 +159,15 @@ internal sealed class HeartbeatAndReceivePlugin : PluginBase, ITcpConnectedPlugi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Task = null;
|
_task = null;
|
||||||
}, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default);
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await e.InvokeNext().ConfigureAwait(false);
|
await e.InvokeNext().ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task OnTcpReceiving(ITcpSession client, ByteBlockEventArgs e)
|
public async Task OnTcpReceiving(ITcpSession client, BytesReaderEventArgs e)
|
||||||
{
|
{
|
||||||
if (client is ITcpSessionClient)
|
if (client is ITcpSessionClient)
|
||||||
{
|
{
|
||||||
@@ -181,7 +181,7 @@ internal sealed class HeartbeatAndReceivePlugin : PluginBase, ITcpConnectedPlugi
|
|||||||
var len = HeartbeatByte.Length;
|
var len = HeartbeatByte.Length;
|
||||||
if (len > 0)
|
if (len > 0)
|
||||||
{
|
{
|
||||||
if (HeartbeatByte.Span.SequenceEqual(e.ByteBlock.Memory.Slice(0, len).Span))
|
if (HeartbeatByte.Span.SequenceEqual(e.Reader.TotalSequence.Slice(0, len).First.Span))
|
||||||
{
|
{
|
||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
}
|
}
|
||||||
@@ -190,6 +190,7 @@ internal sealed class HeartbeatAndReceivePlugin : PluginBase, ITcpConnectedPlugi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public Task OnTcpClosed(ITcpSession client, ClosedEventArgs e)
|
public Task OnTcpClosed(ITcpSession client, ClosedEventArgs e)
|
||||||
{
|
{
|
||||||
SendHeartbeat = false;
|
SendHeartbeat = false;
|
||||||
|
|||||||
@@ -77,10 +77,9 @@ public static class PluginUtil
|
|||||||
a.UseTcpSessionCheckClear()
|
a.UseTcpSessionCheckClear()
|
||||||
.SetCheckClearType(CheckClearType.All)
|
.SetCheckClearType(CheckClearType.All)
|
||||||
.SetTick(TimeSpan.FromMilliseconds(channelOptions.CheckClearTime))
|
.SetTick(TimeSpan.FromMilliseconds(channelOptions.CheckClearTime))
|
||||||
.SetOnClose(async (c, t) =>
|
.SetOnClose((c, t) =>
|
||||||
{
|
{
|
||||||
//await c.ShutdownAsync(System.Net.Sockets.SocketShutdown.Both).ConfigureAwait(false);
|
return c.CloseAsync($"{channelOptions.CheckClearTime}ms Timeout");
|
||||||
await c.CloseAsync($"{channelOptions.CheckClearTime}ms Timeout").ConfigureAwait(false);
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,8 +8,6 @@
|
|||||||
// QQ群:605534569
|
// QQ群:605534569
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
using System.Collections.Concurrent;
|
|
||||||
|
|
||||||
using ThingsGateway.NewLife;
|
using ThingsGateway.NewLife;
|
||||||
|
|
||||||
using TouchSocket.SerialPorts;
|
using TouchSocket.SerialPorts;
|
||||||
@@ -34,7 +32,6 @@ public class SerialPortChannel : SerialPortClient, IClientChannel
|
|||||||
var pool = WaitHandlePool;
|
var pool = WaitHandlePool;
|
||||||
WaitHandlePool = new WaitHandlePool<MessageBase>(minSign, maxSign);
|
WaitHandlePool = new WaitHandlePool<MessageBase>(minSign, maxSign);
|
||||||
pool?.CancelAll();
|
pool?.CancelAll();
|
||||||
pool?.SafeDispose();
|
|
||||||
}
|
}
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public ChannelReceivedEventHandler ChannelReceived { get; } = new();
|
public ChannelReceivedEventHandler ChannelReceived { get; } = new();
|
||||||
@@ -50,6 +47,26 @@ public class SerialPortChannel : SerialPortClient, IClientChannel
|
|||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public DataHandlingAdapter ReadOnlyDataHandlingAdapter => ProtectedDataHandlingAdapter;
|
public DataHandlingAdapter ReadOnlyDataHandlingAdapter => ProtectedDataHandlingAdapter;
|
||||||
|
private IDeviceDataHandleAdapter _deviceDataHandleAdapter;
|
||||||
|
public void SetDataHandlingAdapterLogger(ILog log)
|
||||||
|
{
|
||||||
|
if (_deviceDataHandleAdapter == null && ProtectedDataHandlingAdapter is IDeviceDataHandleAdapter handleAdapter)
|
||||||
|
{
|
||||||
|
_deviceDataHandleAdapter = handleAdapter;
|
||||||
|
}
|
||||||
|
if (_deviceDataHandleAdapter != null)
|
||||||
|
{
|
||||||
|
_deviceDataHandleAdapter.Logger = log;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public void SetDataHandlingAdapter(DataHandlingAdapter adapter)
|
||||||
|
{
|
||||||
|
if (adapter is SingleStreamDataHandlingAdapter singleStreamDataHandlingAdapter)
|
||||||
|
SetAdapter(singleStreamDataHandlingAdapter);
|
||||||
|
if (adapter is IDeviceDataHandleAdapter deviceDataHandleAdapter)
|
||||||
|
_deviceDataHandleAdapter = deviceDataHandleAdapter;
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public ChannelEventHandler Started { get; } = new();
|
public ChannelEventHandler Started { get; } = new();
|
||||||
@@ -65,14 +82,13 @@ public class SerialPortChannel : SerialPortClient, IClientChannel
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 等待池
|
/// 等待池
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public WaitHandlePool<MessageBase> WaitHandlePool { get; internal set; } = new();
|
public WaitHandlePool<MessageBase> WaitHandlePool { get; internal set; } = new(0, ushort.MaxValue);
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public WaitLock WaitLock => ChannelOptions.WaitLock;
|
public WaitLock WaitLock => ChannelOptions.WaitLock;
|
||||||
public virtual WaitLock GetLock(string key) => WaitLock;
|
public virtual WaitLock GetLock(string key) => WaitLock;
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public ConcurrentDictionary<long, Func<IClientChannel, ReceivedDataEventArgs, bool, Task>> ChannelReceivedWaitDict { get; } = new();
|
|
||||||
|
|
||||||
//private readonly WaitLock _connectLock = new WaitLock();
|
//private readonly WaitLock _connectLock = new WaitLock();
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
@@ -86,6 +102,7 @@ public class SerialPortChannel : SerialPortClient, IClientChannel
|
|||||||
if (Online)
|
if (Online)
|
||||||
{
|
{
|
||||||
PortName = null;
|
PortName = null;
|
||||||
|
await this.OnChannelEvent(Stoping).ConfigureAwait(false);
|
||||||
var result = await base.CloseAsync(msg, token).ConfigureAwait(false);
|
var result = await base.CloseAsync(msg, token).ConfigureAwait(false);
|
||||||
if (!Online)
|
if (!Online)
|
||||||
{
|
{
|
||||||
@@ -103,7 +120,7 @@ public class SerialPortChannel : SerialPortClient, IClientChannel
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public override async Task ConnectAsync(int millisecondsTimeout, CancellationToken token)
|
public override async Task ConnectAsync(CancellationToken token)
|
||||||
{
|
{
|
||||||
if (!Online)
|
if (!Online)
|
||||||
{
|
{
|
||||||
@@ -119,7 +136,8 @@ public class SerialPortChannel : SerialPortClient, IClientChannel
|
|||||||
if (port != null)
|
if (port != null)
|
||||||
PortName = $"{port.PortName}";
|
PortName = $"{port.PortName}";
|
||||||
|
|
||||||
await base.ConnectAsync(millisecondsTimeout, token).ConfigureAwait(false);
|
await this.OnChannelEvent(Starting).ConfigureAwait(false);
|
||||||
|
await base.ConnectAsync(token).ConfigureAwait(false);
|
||||||
if (Online)
|
if (Online)
|
||||||
{
|
{
|
||||||
if (token.IsCancellationRequested) return;
|
if (token.IsCancellationRequested) return;
|
||||||
@@ -134,12 +152,7 @@ public class SerialPortChannel : SerialPortClient, IClientChannel
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public void SetDataHandlingAdapter(DataHandlingAdapter adapter)
|
|
||||||
{
|
|
||||||
if (adapter is SingleStreamDataHandlingAdapter singleStreamDataHandlingAdapter)
|
|
||||||
SetAdapter(singleStreamDataHandlingAdapter);
|
|
||||||
}
|
|
||||||
private string PortName { get; set; }
|
private string PortName { get; set; }
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public override string? ToString()
|
public override string? ToString()
|
||||||
@@ -153,54 +166,43 @@ public class SerialPortChannel : SerialPortClient, IClientChannel
|
|||||||
return base.ToString();
|
return base.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override async Task OnSerialClosed(ClosedEventArgs e)
|
protected override Task OnSerialClosed(ClosedEventArgs e)
|
||||||
{
|
{
|
||||||
Logger?.Info($"{ToString()} Closed{(e.Message.IsNullOrEmpty() ? string.Empty : $" -{e.Message}")}");
|
Logger?.Info($"{ToString()} Closed{(e.Message.IsNullOrEmpty() ? string.Empty : $" -{e.Message}")}");
|
||||||
|
return base.OnSerialClosed(e);
|
||||||
await base.OnSerialClosed(e).ConfigureAwait(false);
|
|
||||||
}
|
}
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
protected override async Task OnSerialClosing(ClosingEventArgs e)
|
protected override Task OnSerialClosing(ClosingEventArgs e)
|
||||||
{
|
{
|
||||||
await this.OnChannelEvent(Stoping).ConfigureAwait(false);
|
|
||||||
Logger?.Trace($"{ToString()} Closing{(e.Message.IsNullOrEmpty() ? string.Empty : $" -{e.Message}")}");
|
Logger?.Trace($"{ToString()} Closing{(e.Message.IsNullOrEmpty() ? string.Empty : $" -{e.Message}")}");
|
||||||
await base.OnSerialClosing(e).ConfigureAwait(false);
|
return base.OnSerialClosing(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
protected override async Task OnSerialConnecting(ConnectingEventArgs e)
|
protected override Task OnSerialConnecting(ConnectingEventArgs e)
|
||||||
{
|
{
|
||||||
Logger?.Trace($"{ToString()} Connecting{(e.Message.IsNullOrEmpty() ? string.Empty : $" -{e.Message}")}");
|
Logger?.Trace($"{ToString()} Connecting{(e.Message.IsNullOrEmpty() ? string.Empty : $" -{e.Message}")}");
|
||||||
await this.OnChannelEvent(Starting).ConfigureAwait(false);
|
return base.OnSerialConnecting(e);
|
||||||
await base.OnSerialConnecting(e).ConfigureAwait(false);
|
|
||||||
}
|
}
|
||||||
protected override async Task OnSerialConnected(ConnectedEventArgs e)
|
protected override Task OnSerialConnected(ConnectedEventArgs e)
|
||||||
{
|
{
|
||||||
Logger?.Debug($"{ToString()} Connected");
|
Logger?.Debug($"{ToString()} Connected");
|
||||||
await base.OnSerialConnected(e).ConfigureAwait(false);
|
return base.OnSerialConnected(e);
|
||||||
}
|
}
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
protected override async Task OnSerialReceived(ReceivedDataEventArgs e)
|
protected override async Task OnSerialReceived(ReceivedDataEventArgs e)
|
||||||
{
|
{
|
||||||
await base.OnSerialReceived(e).ConfigureAwait(false);
|
await base.OnSerialReceived(e).ConfigureAwait(false);
|
||||||
if (e.RequestInfo is MessageBase response)
|
|
||||||
{
|
|
||||||
if (ChannelReceivedWaitDict.TryRemove(response.Sign, out var func))
|
|
||||||
{
|
|
||||||
await func.Invoke(this, e, ChannelReceived.Count == 1).ConfigureAwait(false);
|
|
||||||
e.Handled = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (e.Handled)
|
if (e.Handled)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
await this.OnChannelReceivedEvent(e, ChannelReceived).ConfigureAwait(false);
|
await this.OnChannelReceivedEvent(e, ChannelReceived).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
protected override void SafetyDispose(bool disposing)
|
protected override void SafetyDispose(bool disposing)
|
||||||
{
|
{
|
||||||
WaitHandlePool.SafeDispose();
|
WaitHandlePool?.CancelAll();
|
||||||
base.SafetyDispose(disposing);
|
base.SafetyDispose(disposing);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,8 +8,6 @@
|
|||||||
// QQ群:605534569
|
// QQ群:605534569
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
using System.Collections.Concurrent;
|
|
||||||
|
|
||||||
using ThingsGateway.NewLife;
|
using ThingsGateway.NewLife;
|
||||||
|
|
||||||
namespace ThingsGateway.Foundation;
|
namespace ThingsGateway.Foundation;
|
||||||
@@ -31,9 +29,27 @@ public class TcpClientChannel : TcpClient, IClientChannel
|
|||||||
var pool = WaitHandlePool;
|
var pool = WaitHandlePool;
|
||||||
WaitHandlePool = new WaitHandlePool<MessageBase>(minSign, maxSign);
|
WaitHandlePool = new WaitHandlePool<MessageBase>(minSign, maxSign);
|
||||||
pool?.CancelAll();
|
pool?.CancelAll();
|
||||||
pool?.SafeDispose();
|
|
||||||
}
|
}
|
||||||
|
private IDeviceDataHandleAdapter _deviceDataHandleAdapter;
|
||||||
|
public void SetDataHandlingAdapterLogger(ILog log)
|
||||||
|
{
|
||||||
|
if (_deviceDataHandleAdapter == null && DataHandlingAdapter is IDeviceDataHandleAdapter handleAdapter)
|
||||||
|
{
|
||||||
|
_deviceDataHandleAdapter = handleAdapter;
|
||||||
|
}
|
||||||
|
if (_deviceDataHandleAdapter != null)
|
||||||
|
{
|
||||||
|
_deviceDataHandleAdapter.Logger = log;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public void SetDataHandlingAdapter(DataHandlingAdapter adapter)
|
||||||
|
{
|
||||||
|
if (adapter is SingleStreamDataHandlingAdapter singleStreamDataHandlingAdapter)
|
||||||
|
SetAdapter(singleStreamDataHandlingAdapter);
|
||||||
|
if (adapter is IDeviceDataHandleAdapter deviceDataHandleAdapter)
|
||||||
|
_deviceDataHandleAdapter = deviceDataHandleAdapter;
|
||||||
|
}
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public ChannelReceivedEventHandler ChannelReceived { get; } = new();
|
public ChannelReceivedEventHandler ChannelReceived { get; } = new();
|
||||||
|
|
||||||
@@ -62,14 +78,13 @@ public class TcpClientChannel : TcpClient, IClientChannel
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 等待池
|
/// 等待池
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public WaitHandlePool<MessageBase> WaitHandlePool { get; internal set; } = new();
|
public WaitHandlePool<MessageBase> WaitHandlePool { get; internal set; } = new(0, ushort.MaxValue);
|
||||||
public virtual WaitLock GetLock(string key) => WaitLock;
|
public virtual WaitLock GetLock(string key) => WaitLock;
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public WaitLock WaitLock => ChannelOptions.WaitLock;
|
public WaitLock WaitLock => ChannelOptions.WaitLock;
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public ConcurrentDictionary<long, Func<IClientChannel, ReceivedDataEventArgs, bool, Task>> ChannelReceivedWaitDict { get; } = new();
|
|
||||||
|
|
||||||
//private readonly WaitLock _connectLock = new WaitLock();
|
//private readonly WaitLock _connectLock = new WaitLock();
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
@@ -82,6 +97,7 @@ public class TcpClientChannel : TcpClient, IClientChannel
|
|||||||
//await _connectLock.WaitAsync().ConfigureAwait(false);
|
//await _connectLock.WaitAsync().ConfigureAwait(false);
|
||||||
if (Online)
|
if (Online)
|
||||||
{
|
{
|
||||||
|
await this.OnChannelEvent(Stoping).ConfigureAwait(false);
|
||||||
var result = await base.CloseAsync(msg, token).ConfigureAwait(false);
|
var result = await base.CloseAsync(msg, token).ConfigureAwait(false);
|
||||||
if (!Online)
|
if (!Online)
|
||||||
{
|
{
|
||||||
@@ -99,7 +115,7 @@ public class TcpClientChannel : TcpClient, IClientChannel
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public override async Task ConnectAsync(int millisecondsTimeout, CancellationToken token)
|
public override async Task ConnectAsync(CancellationToken token)
|
||||||
{
|
{
|
||||||
if (!Online)
|
if (!Online)
|
||||||
{
|
{
|
||||||
@@ -109,7 +125,8 @@ public class TcpClientChannel : TcpClient, IClientChannel
|
|||||||
if (!Online)
|
if (!Online)
|
||||||
{
|
{
|
||||||
if (token.IsCancellationRequested) return;
|
if (token.IsCancellationRequested) return;
|
||||||
await base.ConnectAsync(millisecondsTimeout, token).ConfigureAwait(false);
|
await this.OnChannelEvent(Starting).ConfigureAwait(false);
|
||||||
|
await base.ConnectAsync(token).ConfigureAwait(false);
|
||||||
if (Online)
|
if (Online)
|
||||||
{
|
{
|
||||||
if (token.IsCancellationRequested) return;
|
if (token.IsCancellationRequested) return;
|
||||||
@@ -124,12 +141,6 @@ public class TcpClientChannel : TcpClient, IClientChannel
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public void SetDataHandlingAdapter(DataHandlingAdapter adapter)
|
|
||||||
{
|
|
||||||
if (adapter is SingleStreamDataHandlingAdapter singleStreamDataHandlingAdapter)
|
|
||||||
SetAdapter(singleStreamDataHandlingAdapter);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
@@ -137,48 +148,39 @@ public class TcpClientChannel : TcpClient, IClientChannel
|
|||||||
return $"{IP}:{Port}";
|
return $"{IP}:{Port}";
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override async Task OnTcpClosed(ClosedEventArgs e)
|
protected override Task OnTcpClosed(ClosedEventArgs e)
|
||||||
{
|
{
|
||||||
Logger?.Info($"{ToString()} Closed{(e.Message.IsNullOrEmpty() ? string.Empty : $" -{e.Message}")}");
|
Logger?.Info($"{ToString()} Closed{(e.Message.IsNullOrEmpty() ? string.Empty : $" -{e.Message}")}");
|
||||||
|
|
||||||
await base.OnTcpClosed(e).ConfigureAwait(false);
|
return base.OnTcpClosed(e);
|
||||||
}
|
}
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
protected override async Task OnTcpClosing(ClosingEventArgs e)
|
protected override Task OnTcpClosing(ClosingEventArgs e)
|
||||||
{
|
{
|
||||||
await this.OnChannelEvent(Stoping).ConfigureAwait(false);
|
|
||||||
Logger?.Trace($"{ToString()} Closing{(e.Message.IsNullOrEmpty() ? string.Empty : $" -{e.Message}")}");
|
Logger?.Trace($"{ToString()} Closing{(e.Message.IsNullOrEmpty() ? string.Empty : $" -{e.Message}")}");
|
||||||
|
|
||||||
await base.OnTcpClosing(e).ConfigureAwait(false);
|
return base.OnTcpClosing(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
protected override async Task OnTcpConnecting(ConnectingEventArgs e)
|
protected override Task OnTcpConnecting(ConnectingEventArgs e)
|
||||||
{
|
{
|
||||||
Logger?.Trace($"{ToString()} Connecting{(e.Message.IsNullOrEmpty() ? string.Empty : $"-{e.Message}")}");
|
Logger?.Trace($"{ToString()} Connecting{(e.Message.IsNullOrEmpty() ? string.Empty : $"-{e.Message}")}");
|
||||||
await this.OnChannelEvent(Starting).ConfigureAwait(false);
|
return base.OnTcpConnecting(e);
|
||||||
await base.OnTcpConnecting(e).ConfigureAwait(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override async Task OnTcpConnected(ConnectedEventArgs e)
|
protected override Task OnTcpConnected(ConnectedEventArgs e)
|
||||||
{
|
{
|
||||||
Logger?.Info($"{ToString()} Connected");
|
Logger?.Info($"{ToString()} Connected");
|
||||||
|
|
||||||
await base.OnTcpConnected(e).ConfigureAwait(false);
|
return base.OnTcpConnected(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
protected override async Task OnTcpReceived(ReceivedDataEventArgs e)
|
protected override async Task OnTcpReceived(ReceivedDataEventArgs e)
|
||||||
{
|
{
|
||||||
await base.OnTcpReceived(e).ConfigureAwait(false);
|
await base.OnTcpReceived(e).ConfigureAwait(false);
|
||||||
if (e.RequestInfo is MessageBase response)
|
|
||||||
{
|
|
||||||
if (ChannelReceivedWaitDict.TryRemove(response.Sign, out var func))
|
|
||||||
{
|
|
||||||
await func.Invoke(this, e, ChannelReceived.Count == 1).ConfigureAwait(false);
|
|
||||||
e.Handled = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (e.Handled)
|
if (e.Handled)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -188,7 +190,7 @@ public class TcpClientChannel : TcpClient, IClientChannel
|
|||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
protected override void SafetyDispose(bool disposing)
|
protected override void SafetyDispose(bool disposing)
|
||||||
{
|
{
|
||||||
WaitHandlePool.SafeDispose();
|
WaitHandlePool?.CancelAll();
|
||||||
base.SafetyDispose(disposing);
|
base.SafetyDispose(disposing);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,8 +8,6 @@
|
|||||||
// QQ群:605534569
|
// QQ群:605534569
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
using System.Collections.Concurrent;
|
|
||||||
|
|
||||||
using ThingsGateway.NewLife;
|
using ThingsGateway.NewLife;
|
||||||
|
|
||||||
namespace ThingsGateway.Foundation;
|
namespace ThingsGateway.Foundation;
|
||||||
@@ -23,30 +21,6 @@ public abstract class TcpServiceChannelBase<TClient> : TcpService<TClient>, ITcp
|
|||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public ConcurrentList<IDevice> Collects { get; } = new();
|
public ConcurrentList<IDevice> Collects { get; } = new();
|
||||||
|
|
||||||
///// <summary>
|
|
||||||
///// 停止时是否发送ShutDown
|
|
||||||
///// </summary>
|
|
||||||
//public bool ShutDownEnable { get; set; } = true;
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public override async Task ClearAsync()
|
|
||||||
{
|
|
||||||
foreach (var client in Clients)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
//if (ShutDownEnable)
|
|
||||||
// await client.ShutdownAsync(System.Net.Sockets.SocketShutdown.Both).ConfigureAwait(false);
|
|
||||||
|
|
||||||
await client.CloseAsync().ConfigureAwait(false);
|
|
||||||
client.SafeDispose();
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task ClientDisposeAsync(string id)
|
public async Task ClientDisposeAsync(string id)
|
||||||
{
|
{
|
||||||
if (this.TryGetClient(id, out var client))
|
if (this.TryGetClient(id, out var client))
|
||||||
@@ -136,6 +110,7 @@ public abstract class TcpServiceChannelBase<TClient> : TcpService<TClient>, ITcp
|
|||||||
{
|
{
|
||||||
m_transport?.SafeCancel();
|
m_transport?.SafeCancel();
|
||||||
m_transport?.SafeDispose();
|
m_transport?.SafeDispose();
|
||||||
|
m_transport = null;
|
||||||
base.SafetyDispose(disposing);
|
base.SafetyDispose(disposing);
|
||||||
}
|
}
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
@@ -209,7 +184,7 @@ public class TcpServiceChannel<TClient> : TcpServiceChannelBase<TClient>, IChann
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public Task ConnectAsync(int timeout = 3000, CancellationToken token = default)
|
public Task ConnectAsync(CancellationToken token = default)
|
||||||
{
|
{
|
||||||
if (token.IsCancellationRequested)
|
if (token.IsCancellationRequested)
|
||||||
return EasyTask.CompletedTask;
|
return EasyTask.CompletedTask;
|
||||||
@@ -268,22 +243,13 @@ public class TcpServiceChannel<TClient> : TcpServiceChannelBase<TClient>, IChann
|
|||||||
{
|
{
|
||||||
await base.OnTcpReceived(socketClient, e).ConfigureAwait(false);
|
await base.OnTcpReceived(socketClient, e).ConfigureAwait(false);
|
||||||
|
|
||||||
if (e.RequestInfo is MessageBase response)
|
|
||||||
{
|
|
||||||
if (ChannelReceivedWaitDict.TryRemove(response.Sign, out var func))
|
|
||||||
{
|
|
||||||
await func.Invoke(socketClient, e, ChannelReceived.Count == 1).ConfigureAwait(false);
|
|
||||||
e.Handled = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (e.Handled)
|
if (e.Handled)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
await socketClient.OnChannelReceivedEvent(e, ChannelReceived).ConfigureAwait(false);
|
await socketClient.OnChannelReceivedEvent(e, ChannelReceived).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public ConcurrentDictionary<long, Func<IClientChannel, ReceivedDataEventArgs, bool, Task>> ChannelReceivedWaitDict { get; } = new();
|
|
||||||
|
|
||||||
IEnumerable<TcpSessionClientChannel> ITcpServiceChannel.Clients => base.Clients;
|
IEnumerable<TcpSessionClientChannel> ITcpServiceChannel.Clients => base.Clients;
|
||||||
|
|
||||||
|
|||||||
@@ -8,8 +8,6 @@
|
|||||||
// QQ群:605534569
|
// QQ群:605534569
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
using System.Collections.Concurrent;
|
|
||||||
|
|
||||||
using ThingsGateway.NewLife;
|
using ThingsGateway.NewLife;
|
||||||
|
|
||||||
namespace ThingsGateway.Foundation;
|
namespace ThingsGateway.Foundation;
|
||||||
@@ -23,13 +21,31 @@ public class TcpSessionClientChannel : TcpSessionClient, IClientChannel
|
|||||||
public TcpSessionClientChannel()
|
public TcpSessionClientChannel()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
private IDeviceDataHandleAdapter _deviceDataHandleAdapter;
|
||||||
|
public void SetDataHandlingAdapterLogger(ILog log)
|
||||||
|
{
|
||||||
|
if (_deviceDataHandleAdapter == null && DataHandlingAdapter is IDeviceDataHandleAdapter handleAdapter)
|
||||||
|
{
|
||||||
|
_deviceDataHandleAdapter = handleAdapter;
|
||||||
|
}
|
||||||
|
if (_deviceDataHandleAdapter != null)
|
||||||
|
{
|
||||||
|
_deviceDataHandleAdapter.Logger = log;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public void SetDataHandlingAdapter(DataHandlingAdapter adapter)
|
||||||
|
{
|
||||||
|
if (adapter is SingleStreamDataHandlingAdapter singleStreamDataHandlingAdapter)
|
||||||
|
SetAdapter(singleStreamDataHandlingAdapter);
|
||||||
|
if (adapter is IDeviceDataHandleAdapter deviceDataHandleAdapter)
|
||||||
|
_deviceDataHandleAdapter = deviceDataHandleAdapter;
|
||||||
|
}
|
||||||
public void ResetSign(int minSign = 0, int maxSign = ushort.MaxValue)
|
public void ResetSign(int minSign = 0, int maxSign = ushort.MaxValue)
|
||||||
{
|
{
|
||||||
var pool = WaitHandlePool;
|
var pool = WaitHandlePool;
|
||||||
WaitHandlePool = new WaitHandlePool<MessageBase>(minSign, maxSign);
|
WaitHandlePool = new WaitHandlePool<MessageBase>(minSign, maxSign);
|
||||||
pool?.CancelAll();
|
pool?.CancelAll();
|
||||||
pool?.SafeDispose();
|
|
||||||
}
|
}
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public ChannelReceivedEventHandler ChannelReceived { get; } = new();
|
public ChannelReceivedEventHandler ChannelReceived { get; } = new();
|
||||||
@@ -60,7 +76,7 @@ public class TcpSessionClientChannel : TcpSessionClient, IClientChannel
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 等待池
|
/// 等待池
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public WaitHandlePool<MessageBase> WaitHandlePool { get; private set; } = new();
|
public WaitHandlePool<MessageBase> WaitHandlePool { get; private set; } = new(0, ushort.MaxValue);
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public WaitLock WaitLock { get; internal set; } = new(nameof(TcpSessionClientChannel));
|
public WaitLock WaitLock { get; internal set; } = new(nameof(TcpSessionClientChannel));
|
||||||
@@ -69,25 +85,19 @@ public class TcpSessionClientChannel : TcpSessionClient, IClientChannel
|
|||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public override Task<Result> CloseAsync(string msg, CancellationToken token)
|
public override Task<Result> CloseAsync(string msg, CancellationToken token)
|
||||||
{
|
{
|
||||||
WaitHandlePool.SafeDispose();
|
WaitHandlePool?.CancelAll();
|
||||||
return base.CloseAsync(msg, token);
|
return base.CloseAsync(msg, token);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public Task ConnectAsync(int timeout, CancellationToken token) => Task.CompletedTask;
|
public Task ConnectAsync(CancellationToken token) => Task.CompletedTask;
|
||||||
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public void SetDataHandlingAdapter(DataHandlingAdapter adapter)
|
|
||||||
{
|
|
||||||
if (adapter is SingleStreamDataHandlingAdapter singleStreamDataHandlingAdapter)
|
|
||||||
SetAdapter(singleStreamDataHandlingAdapter);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public Task SetupAsync(TouchSocketConfig config) => Task.CompletedTask;
|
public Task SetupAsync(TouchSocketConfig config) => Task.CompletedTask;
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public ConcurrentDictionary<long, Func<IClientChannel, ReceivedDataEventArgs, bool, Task>> ChannelReceivedWaitDict { get; } = new();
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
@@ -98,7 +108,7 @@ public class TcpSessionClientChannel : TcpSessionClient, IClientChannel
|
|||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
protected override void SafetyDispose(bool disposing)
|
protected override void SafetyDispose(bool disposing)
|
||||||
{
|
{
|
||||||
WaitHandlePool.SafeDispose();
|
WaitHandlePool?.CancelAll();
|
||||||
base.SafetyDispose(disposing);
|
base.SafetyDispose(disposing);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -136,14 +146,7 @@ public class TcpSessionClientChannel : TcpSessionClient, IClientChannel
|
|||||||
protected override async Task OnTcpReceived(ReceivedDataEventArgs e)
|
protected override async Task OnTcpReceived(ReceivedDataEventArgs e)
|
||||||
{
|
{
|
||||||
await base.OnTcpReceived(e).ConfigureAwait(false);
|
await base.OnTcpReceived(e).ConfigureAwait(false);
|
||||||
if (e.RequestInfo is MessageBase response)
|
|
||||||
{
|
|
||||||
if (ChannelReceivedWaitDict.TryRemove(response.Sign, out var func))
|
|
||||||
{
|
|
||||||
await func.Invoke(this, e, ChannelReceived.Count == 1).ConfigureAwait(false);
|
|
||||||
e.Handled = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (e.Handled)
|
if (e.Handled)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|||||||
@@ -8,8 +8,6 @@
|
|||||||
// QQ群:605534569
|
// QQ群:605534569
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
using System.Collections.Concurrent;
|
|
||||||
|
|
||||||
using ThingsGateway.NewLife;
|
using ThingsGateway.NewLife;
|
||||||
|
|
||||||
namespace ThingsGateway.Foundation;
|
namespace ThingsGateway.Foundation;
|
||||||
@@ -28,13 +26,31 @@ public class UdpSessionChannel : UdpSession, IClientChannel
|
|||||||
ResetSign();
|
ResetSign();
|
||||||
}
|
}
|
||||||
public override TouchSocketConfig Config => base.Config ?? ChannelOptions.Config;
|
public override TouchSocketConfig Config => base.Config ?? ChannelOptions.Config;
|
||||||
|
private IDeviceDataHandleAdapter _deviceDataHandleAdapter;
|
||||||
|
public void SetDataHandlingAdapterLogger(ILog log)
|
||||||
|
{
|
||||||
|
if (_deviceDataHandleAdapter == null && DataHandlingAdapter is IDeviceDataHandleAdapter handleAdapter)
|
||||||
|
{
|
||||||
|
_deviceDataHandleAdapter = handleAdapter;
|
||||||
|
}
|
||||||
|
if (_deviceDataHandleAdapter != null)
|
||||||
|
{
|
||||||
|
_deviceDataHandleAdapter.Logger = log;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public void SetDataHandlingAdapter(DataHandlingAdapter adapter)
|
||||||
|
{
|
||||||
|
if (adapter is UdpDataHandlingAdapter udpDataHandlingAdapter)
|
||||||
|
SetAdapter(udpDataHandlingAdapter);
|
||||||
|
if (adapter is IDeviceDataHandleAdapter deviceDataHandleAdapter)
|
||||||
|
_deviceDataHandleAdapter = deviceDataHandleAdapter;
|
||||||
|
}
|
||||||
public void ResetSign(int minSign = 0, int maxSign = ushort.MaxValue)
|
public void ResetSign(int minSign = 0, int maxSign = ushort.MaxValue)
|
||||||
{
|
{
|
||||||
var pool = WaitHandlePool;
|
var pool = WaitHandlePool;
|
||||||
WaitHandlePool = new WaitHandlePool<MessageBase>(minSign, maxSign);
|
WaitHandlePool = new WaitHandlePool<MessageBase>(minSign, maxSign);
|
||||||
pool?.CancelAll();
|
pool?.CancelAll();
|
||||||
pool?.SafeDispose();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
@@ -69,14 +85,13 @@ public class UdpSessionChannel : UdpSession, IClientChannel
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 等待池
|
/// 等待池
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public WaitHandlePool<MessageBase> WaitHandlePool { get; set; } = new();
|
public WaitHandlePool<MessageBase> WaitHandlePool { get; set; } = new(0, ushort.MaxValue);
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public WaitLock WaitLock => ChannelOptions.WaitLock;
|
public WaitLock WaitLock => ChannelOptions.WaitLock;
|
||||||
public virtual WaitLock GetLock(string key) => WaitLock;
|
public virtual WaitLock GetLock(string key) => WaitLock;
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public ConcurrentDictionary<long, Func<IClientChannel, ReceivedDataEventArgs, bool, Task>> ChannelReceivedWaitDict { get; } = new();
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public Task<Result> CloseAsync(string msg, CancellationToken token)
|
public Task<Result> CloseAsync(string msg, CancellationToken token)
|
||||||
@@ -85,19 +100,14 @@ public class UdpSessionChannel : UdpSession, IClientChannel
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public async Task ConnectAsync(int timeout = 3000, CancellationToken token = default)
|
public Task ConnectAsync(CancellationToken token = default)
|
||||||
{
|
{
|
||||||
if (token.IsCancellationRequested)
|
if (token.IsCancellationRequested)
|
||||||
return;
|
return EasyTask.CompletedTask; ;
|
||||||
await StartAsync().ConfigureAwait(false);
|
return StartAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public void SetDataHandlingAdapter(DataHandlingAdapter adapter)
|
|
||||||
{
|
|
||||||
if (adapter is UdpDataHandlingAdapter udpDataHandlingAdapter)
|
|
||||||
SetAdapter(udpDataHandlingAdapter);
|
|
||||||
}
|
|
||||||
public CancellationToken ClosedToken => this.m_transport == null ? new CancellationToken(true) : this.m_transport.Token;
|
public CancellationToken ClosedToken => this.m_transport == null ? new CancellationToken(true) : this.m_transport.Token;
|
||||||
private CancellationTokenSource m_transport;
|
private CancellationTokenSource m_transport;
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
@@ -188,14 +198,6 @@ public class UdpSessionChannel : UdpSession, IClientChannel
|
|||||||
{
|
{
|
||||||
await base.OnUdpReceived(e).ConfigureAwait(false);
|
await base.OnUdpReceived(e).ConfigureAwait(false);
|
||||||
|
|
||||||
if (e.RequestInfo is MessageBase response)
|
|
||||||
{
|
|
||||||
if (ChannelReceivedWaitDict.TryRemove(response.Sign, out var func))
|
|
||||||
{
|
|
||||||
await func.Invoke(this, e, ChannelReceived.Count == 1).ConfigureAwait(false);
|
|
||||||
e.Handled = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (e.Handled)
|
if (e.Handled)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -207,7 +209,8 @@ public class UdpSessionChannel : UdpSession, IClientChannel
|
|||||||
{
|
{
|
||||||
m_transport?.SafeCancel();
|
m_transport?.SafeCancel();
|
||||||
m_transport?.SafeDispose();
|
m_transport?.SafeDispose();
|
||||||
WaitHandlePool.SafeDispose();
|
m_transport = null;
|
||||||
|
WaitHandlePool?.CancelAll();
|
||||||
base.SafetyDispose(disposing);
|
base.SafetyDispose(disposing);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,15 +15,18 @@ namespace ThingsGateway.Foundation;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// TCP/Serial适配器基类
|
/// TCP/Serial适配器基类
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class DeviceSingleStreamDataHandleAdapter<TRequest> : TcpCustomDataHandlingAdapter<TRequest> where TRequest : MessageBase, new()
|
public class DeviceSingleStreamDataHandleAdapter<TRequest> : CustomDataHandlingAdapter<TRequest>, IDeviceDataHandleAdapter where TRequest : MessageBase, new()
|
||||||
{
|
{
|
||||||
|
public new ILog Logger { get; set; }
|
||||||
|
|
||||||
|
|
||||||
/// <inheritdoc cref="DeviceSingleStreamDataHandleAdapter{TRequest}"/>
|
/// <inheritdoc cref="DeviceSingleStreamDataHandleAdapter{TRequest}"/>
|
||||||
public DeviceSingleStreamDataHandleAdapter()
|
public DeviceSingleStreamDataHandleAdapter()
|
||||||
{
|
{
|
||||||
CacheTimeoutEnable = true;
|
CacheTimeoutEnable = true;
|
||||||
SurLength = int.MaxValue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public override bool CanSendRequestInfo => true;
|
public override bool CanSendRequestInfo => true;
|
||||||
|
|
||||||
@@ -40,11 +43,11 @@ public class DeviceSingleStreamDataHandleAdapter<TRequest> : TcpCustomDataHandli
|
|||||||
public TRequest Request { get; set; }
|
public TRequest Request { get; set; }
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void SetRequest(ISendMessage sendMessage, ref ValueByteBlock byteBlock)
|
public void SetRequest(ISendMessage sendMessage)
|
||||||
{
|
{
|
||||||
var request = GetInstance();
|
var request = GetInstance();
|
||||||
request.Sign = sendMessage.Sign;
|
request.Sign = sendMessage.Sign;
|
||||||
request.SendInfo(sendMessage, ref byteBlock);
|
request.SendInfo(sendMessage);
|
||||||
Request = request;
|
Request = request;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,24 +58,24 @@ public class DeviceSingleStreamDataHandleAdapter<TRequest> : TcpCustomDataHandli
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected override FilterResult Filter<TByteBlock>(ref TByteBlock byteBlock, bool beCached, ref TRequest request, ref int tempCapacity)
|
protected override FilterResult Filter<TReader>(ref TReader byteBlock, bool beCached, ref TRequest request)
|
||||||
{
|
{
|
||||||
if (Logger?.LogLevel <= LogLevel.Trace)
|
if (Logger?.LogLevel <= LogLevel.Trace)
|
||||||
Logger?.Trace($"{ToString()}- Receive:{(IsHexLog ? byteBlock.AsSegmentTake().ToHexString(' ') : byteBlock.ToString(byteBlock.Position))}");
|
Logger?.Trace($"{ToString()}- Receive:{(IsHexLog ? byteBlock.ToHexString(byteBlock.BytesRead, ' ') : byteBlock.ToString(byteBlock.BytesRead))}");
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (IsSingleThread)
|
if (IsSingleThread)
|
||||||
request = Request == null ? GetInstance() : Request;
|
request = Request == null ? Request = GetInstance() : Request;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (!beCached)
|
if (!beCached)
|
||||||
request = GetInstance();
|
request = GetInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
var pos = byteBlock.Position;
|
var pos = byteBlock.BytesRead;
|
||||||
|
|
||||||
if (request.HeaderLength > byteBlock.CanReadLength)
|
if (request.HeaderLength > byteBlock.BytesRemaining)
|
||||||
{
|
{
|
||||||
return FilterResult.Cache;//当头部都无法解析时,直接缓存
|
return FilterResult.Cache;//当头部都无法解析时,直接缓存
|
||||||
}
|
}
|
||||||
@@ -80,19 +83,18 @@ public class DeviceSingleStreamDataHandleAdapter<TRequest> : TcpCustomDataHandli
|
|||||||
//检查头部合法性
|
//检查头部合法性
|
||||||
if (request.CheckHead(ref byteBlock))
|
if (request.CheckHead(ref byteBlock))
|
||||||
{
|
{
|
||||||
byteBlock.Position = pos;
|
byteBlock.BytesRead = pos;
|
||||||
if (request.BodyLength > MaxPackageSize)
|
if (request.BodyLength > MaxPackageSize)
|
||||||
{
|
{
|
||||||
request.OperCode = -1;
|
request.OperCode = -1;
|
||||||
request.ErrorMessage = $"Received BodyLength={request.BodyLength}, greater than the set MaxPackageSize={MaxPackageSize}";
|
request.ErrorMessage = $"Received BodyLength={request.BodyLength}, greater than the set MaxPackageSize={MaxPackageSize}";
|
||||||
OnError(default, request.ErrorMessage, true, true);
|
Reset();
|
||||||
SetResult(request);
|
Logger?.LogWarning($"{ToString()} {request.ErrorMessage}");
|
||||||
return FilterResult.GoOn;
|
return FilterResult.GoOn;
|
||||||
}
|
}
|
||||||
if (request.BodyLength + request.HeaderLength > byteBlock.CanReadLength)
|
if (request.BodyLength + request.HeaderLength > byteBlock.BytesRemaining)
|
||||||
{
|
{
|
||||||
//body不满足解析,开始缓存,然后保存对象
|
//body不满足解析,开始缓存,然后保存对象
|
||||||
tempCapacity = request.BodyLength + request.HeaderLength;
|
|
||||||
return FilterResult.Cache;
|
return FilterResult.Cache;
|
||||||
}
|
}
|
||||||
//if (request.BodyLength <= 0)
|
//if (request.BodyLength <= 0)
|
||||||
@@ -101,56 +103,47 @@ public class DeviceSingleStreamDataHandleAdapter<TRequest> : TcpCustomDataHandli
|
|||||||
// request.BodyLength = byteBlock.Length;
|
// request.BodyLength = byteBlock.Length;
|
||||||
//}
|
//}
|
||||||
var headPos = pos + request.HeaderLength;
|
var headPos = pos + request.HeaderLength;
|
||||||
byteBlock.Position = headPos;
|
byteBlock.BytesRead = headPos;
|
||||||
var result = request.CheckBody(ref byteBlock);
|
var result = request.CheckBody(ref byteBlock);
|
||||||
if (result == FilterResult.Cache)
|
if (result == FilterResult.Cache)
|
||||||
{
|
{
|
||||||
|
byteBlock.BytesRead = pos;
|
||||||
if (Logger?.LogLevel <= LogLevel.Trace)
|
if (Logger?.LogLevel <= LogLevel.Trace)
|
||||||
Logger?.Trace($"{ToString()}-Received incomplete, cached message, current length:{byteBlock.Length} {request?.ErrorMessage}");
|
Logger?.Trace($"{ToString()}-Received incomplete, cached message, need length:{request.HeaderLength + request.BodyLength} ,current length:{byteBlock.BytesRead + byteBlock.BytesRemaining} {request?.ErrorMessage}");
|
||||||
tempCapacity = request.BodyLength + request.HeaderLength;
|
|
||||||
request.OperCode = -1;
|
request.OperCode = -1;
|
||||||
}
|
}
|
||||||
else if (result == FilterResult.GoOn)
|
else if (result == FilterResult.GoOn)
|
||||||
{
|
{
|
||||||
var addLen = request.HeaderLength + request.BodyLength;
|
var addLen = request.HeaderLength + request.BodyLength;
|
||||||
byteBlock.Position = pos + (addLen > 0 ? addLen : 1);
|
byteBlock.BytesRead = pos + (addLen > 0 ? addLen : 1);
|
||||||
Logger?.Trace($"{ToString()}-{request?.ToString()}");
|
if (Logger?.LogLevel <= LogLevel.Trace)
|
||||||
|
Logger?.Trace($"{ToString()}-{request?.ToString()}");
|
||||||
request.OperCode = -1;
|
request.OperCode = -1;
|
||||||
SetResult(request);
|
|
||||||
}
|
}
|
||||||
else if (result == FilterResult.Success)
|
else if (result == FilterResult.Success)
|
||||||
{
|
{
|
||||||
var addLen = request.HeaderLength + request.BodyLength;
|
var addLen = request.HeaderLength + request.BodyLength;
|
||||||
byteBlock.Position = pos + (addLen > 0 ? addLen : 1);
|
byteBlock.BytesRead = pos + (addLen > 0 ? addLen : 1);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
byteBlock.Position = pos + 1;//移动游标
|
byteBlock.BytesRead = pos + 1;//移动游标
|
||||||
request.OperCode = -1;
|
request.OperCode = -1;
|
||||||
SetResult(request);
|
|
||||||
return FilterResult.GoOn;//放弃解析
|
return FilterResult.GoOn;//放弃解析
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Logger?.LogWarning(ex, $"{ToString()} Received parsing error");
|
Logger?.LogWarning(ex, $"{ToString()} Received parsing error");
|
||||||
byteBlock.Position = byteBlock.Length;//移动游标
|
byteBlock.BytesRead = byteBlock.BytesRead + byteBlock.BytesRemaining;//移动游标
|
||||||
request.Exception = ex;
|
request.Exception = ex;
|
||||||
request.OperCode = -1;
|
request.OperCode = -1;
|
||||||
SetResult(request);
|
|
||||||
return FilterResult.GoOn;//放弃解析
|
return FilterResult.GoOn;//放弃解析
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SetResult(TRequest request)
|
|
||||||
{
|
|
||||||
if ((Owner as IClientChannel)?.WaitHandlePool?.TryGetDataAsync(request.Sign, out var waitDataAsync) == true)
|
|
||||||
{
|
|
||||||
waitDataAsync.SetResult(request);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取泛型实例。
|
/// 获取泛型实例。
|
||||||
@@ -161,47 +154,32 @@ public class DeviceSingleStreamDataHandleAdapter<TRequest> : TcpCustomDataHandli
|
|||||||
return new TRequest() { OperCode = -1, Sign = -1 };
|
return new TRequest() { OperCode = -1, Sign = -1 };
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
public override void SendInput<TWriter>(ref TWriter writer, in ReadOnlyMemory<byte> memory)
|
||||||
protected override void OnReceivedSuccess(TRequest request)
|
|
||||||
{
|
{
|
||||||
Request = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override async Task PreviewSendAsync(ReadOnlyMemory<byte> memory, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
|
||||||
if (Logger?.LogLevel <= LogLevel.Trace)
|
if (Logger?.LogLevel <= LogLevel.Trace)
|
||||||
Logger?.Trace($"{ToString()}- Send:{(IsHexLog ? memory.Span.ToHexString(' ') : (memory.Span.ToString(Encoding.UTF8)))}");
|
Logger?.Trace($"{ToString()}- Send:{(IsHexLog ? memory.Span.ToHexString(' ') : (memory.Span.ToString(Encoding.UTF8)))}");
|
||||||
|
|
||||||
//发送
|
writer.Write(memory.Span);
|
||||||
await GoSendAsync(memory, cancellationToken).ConfigureAwait(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
public override void SendInput<TWriter>(ref TWriter writer, IRequestInfo requestInfo)
|
||||||
protected override async Task PreviewSendAsync(IRequestInfo requestInfo, CancellationToken cancellationToken)
|
|
||||||
{
|
{
|
||||||
if (!(requestInfo is ISendMessage sendMessage))
|
if (!(requestInfo is ISendMessage sendMessage))
|
||||||
{
|
{
|
||||||
throw new Exception($"Unable to convert {nameof(requestInfo)} to {nameof(ISendMessage)}");
|
throw new Exception($"Unable to convert {nameof(requestInfo)} to {nameof(ISendMessage)}");
|
||||||
}
|
}
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
var span = writer.GetSpan(sendMessage.MaxLength);
|
||||||
var byteBlock = new ValueByteBlock(sendMessage.MaxLength);
|
sendMessage.Build(ref writer);
|
||||||
try
|
if (Logger?.LogLevel <= LogLevel.Trace)
|
||||||
{
|
{
|
||||||
sendMessage.Build(ref byteBlock);
|
Logger?.Trace($"{ToString()}- Send:{(IsHexLog ? span.Slice(0, (int)writer.WrittenCount).ToHexString(' ') : (span.Slice(0, (int)writer.WrittenCount).ToString(Encoding.UTF8)))}");
|
||||||
if (Logger?.LogLevel <= LogLevel.Trace)
|
|
||||||
Logger?.Trace($"{ToString()}- Send:{(IsHexLog ? byteBlock.Span.ToHexString(' ') : (byteBlock.Span.ToString(Encoding.UTF8)))}");
|
|
||||||
//非并发主从协议
|
|
||||||
if (IsSingleThread)
|
|
||||||
{
|
|
||||||
SetRequest(sendMessage, ref byteBlock);
|
|
||||||
}
|
|
||||||
await GoSendAsync(byteBlock.Memory, cancellationToken).ConfigureAwait(false);
|
|
||||||
}
|
}
|
||||||
finally
|
//非并发主从协议
|
||||||
|
if (IsSingleThread)
|
||||||
{
|
{
|
||||||
byteBlock.SafeDispose();
|
SetRequest(sendMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,8 +16,10 @@ namespace ThingsGateway.Foundation;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// UDP适配器基类
|
/// UDP适配器基类
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class DeviceUdpDataHandleAdapter<TRequest> : UdpDataHandlingAdapter where TRequest : MessageBase, new()
|
public class DeviceUdpDataHandleAdapter<TRequest> : UdpDataHandlingAdapter, IDeviceDataHandleAdapter where TRequest : MessageBase, new()
|
||||||
{
|
{
|
||||||
|
public new ILog Logger { get; set; }
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public override bool CanSendRequestInfo => true;
|
public override bool CanSendRequestInfo => true;
|
||||||
|
|
||||||
@@ -34,11 +36,11 @@ public class DeviceUdpDataHandleAdapter<TRequest> : UdpDataHandlingAdapter where
|
|||||||
public TRequest Request { get; set; }
|
public TRequest Request { get; set; }
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void SetRequest(ISendMessage sendMessage, ref ValueByteBlock byteBlock)
|
public void SetRequest(ISendMessage sendMessage)
|
||||||
{
|
{
|
||||||
var request = GetInstance();
|
var request = GetInstance();
|
||||||
request.Sign = sendMessage.Sign;
|
request.Sign = sendMessage.Sign;
|
||||||
request.SendInfo(sendMessage, ref byteBlock);
|
request.SendInfo(sendMessage);
|
||||||
Request = request;
|
Request = request;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,113 +59,132 @@ public class DeviceUdpDataHandleAdapter<TRequest> : UdpDataHandlingAdapter where
|
|||||||
return new TRequest() { OperCode = -1, Sign = -1 };
|
return new TRequest() { OperCode = -1, Sign = -1 };
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
protected override async Task PreviewReceived(EndPoint remoteEndPoint, IByteBlockReader byteBlock)
|
#region ParseRequest
|
||||||
|
/// <summary>
|
||||||
|
/// 尝试从字节读取器中解析出请求信息。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="remoteEndPoint">remoteEndPoint。</param>
|
||||||
|
/// <param name="memory">memory。</param>
|
||||||
|
/// <param name="request">解析出的请求信息。</param>
|
||||||
|
/// <returns>解析成功返回 true,否则返回 false。</returns>
|
||||||
|
public bool TryParseRequest(EndPoint remoteEndPoint, ReadOnlyMemory<byte> memory, out TRequest request)
|
||||||
{
|
{
|
||||||
|
return this.ParseRequestCore(remoteEndPoint, memory, out request);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual bool ParseRequestCore(EndPoint remoteEndPoint, ReadOnlyMemory<byte> memory, out TRequest request1)
|
||||||
|
{
|
||||||
|
request1 = null;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
byteBlock.Position = 0;
|
|
||||||
|
|
||||||
if (Logger?.LogLevel <= LogLevel.Trace)
|
if (Logger?.LogLevel <= LogLevel.Trace)
|
||||||
Logger?.Trace($"{remoteEndPoint}- Receive:{(IsHexLog ? byteBlock.AsSegmentTake().ToHexString(' ') : byteBlock.ToString(byteBlock.Position))}");
|
Logger?.Trace($"{remoteEndPoint}- Receive:{(IsHexLog ? memory.Span.ToHexString(' ') : memory.Span.ToString(Encoding.UTF8))}");
|
||||||
|
|
||||||
TRequest request = null;
|
TRequest request = null;
|
||||||
if (IsSingleThread)
|
if (IsSingleThread)
|
||||||
request = Request == null ? GetInstance() : Request;
|
request = Request == null ? Request = GetInstance() : Request;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
request = GetInstance();
|
request = GetInstance();
|
||||||
}
|
}
|
||||||
|
request1 = request;
|
||||||
|
|
||||||
var pos = byteBlock.Position;
|
var byteBlock = new ByteBlockReader(memory);
|
||||||
|
byteBlock.BytesRead = 0;
|
||||||
|
|
||||||
|
var pos = byteBlock.BytesRead;
|
||||||
|
|
||||||
if (request.HeaderLength > byteBlock.CanReadLength)
|
if (request.HeaderLength > byteBlock.CanReadLength)
|
||||||
{
|
{
|
||||||
return;//当头部都无法解析时,直接缓存
|
return false;//当头部都无法解析时,直接缓存
|
||||||
}
|
}
|
||||||
|
|
||||||
//检查头部合法性
|
//检查头部合法性
|
||||||
if (request.CheckHead(ref byteBlock))
|
if (request.CheckHead(ref byteBlock))
|
||||||
{
|
{
|
||||||
byteBlock.Position = pos;
|
byteBlock.BytesRead = pos;
|
||||||
|
|
||||||
if (request.BodyLength > MaxPackageSize)
|
if (request.BodyLength > MaxPackageSize)
|
||||||
{
|
{
|
||||||
OnError(default, $"Received BodyLength={request.BodyLength}, greater than the set MaxPackageSize={MaxPackageSize}", true, true);
|
request.OperCode = -1;
|
||||||
return;
|
request.ErrorMessage = $"Received BodyLength={request.BodyLength}, greater than the set MaxPackageSize={MaxPackageSize}";
|
||||||
|
Reset();
|
||||||
|
Logger?.LogWarning($"{ToString()} {request.ErrorMessage}");
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
if (request.BodyLength + request.HeaderLength > byteBlock.CanReadLength)
|
if (request.BodyLength + request.HeaderLength > byteBlock.CanReadLength)
|
||||||
{
|
{
|
||||||
//body不满足解析,开始缓存,然后保存对象
|
return false;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
//if (request.BodyLength <= 0)
|
|
||||||
//{
|
|
||||||
// //如果body长度无法确定,直接读取全部
|
|
||||||
// request.BodyLength = byteBlock.Length;
|
|
||||||
//}
|
|
||||||
var headPos = pos + request.HeaderLength;
|
var headPos = pos + request.HeaderLength;
|
||||||
byteBlock.Position = headPos;
|
byteBlock.BytesRead = headPos;
|
||||||
var result = request.CheckBody(ref byteBlock);
|
var result = request.CheckBody(ref byteBlock);
|
||||||
if (result == FilterResult.Cache)
|
if (result == FilterResult.Cache)
|
||||||
{
|
{
|
||||||
if (Logger?.LogLevel <= LogLevel.Trace)
|
if (Logger?.LogLevel <= LogLevel.Trace)
|
||||||
Logger?.Trace($"{ToString()}-Received incomplete, cached message, current length:{byteBlock.Length} {request?.ErrorMessage}");
|
Logger?.Trace($"{ToString()}-Received incomplete, cached message, need length:{request.HeaderLength + request.BodyLength} ,current length:{byteBlock.BytesRead + byteBlock.BytesRemaining} {request?.ErrorMessage}");
|
||||||
request.OperCode = -1;
|
request.OperCode = -1;
|
||||||
}
|
}
|
||||||
else if (result == FilterResult.GoOn)
|
else if (result == FilterResult.GoOn)
|
||||||
{
|
{
|
||||||
var addLen = request.HeaderLength + request.BodyLength;
|
if (Logger?.LogLevel <= LogLevel.Trace)
|
||||||
byteBlock.Position = pos + (addLen > 0 ? addLen : 1);
|
Logger?.Trace($"{ToString()}-{request?.ToString()}");
|
||||||
Logger?.Trace($"{ToString()}-{request?.ToString()}");
|
|
||||||
request.OperCode = -1;
|
request.OperCode = -1;
|
||||||
if ((Owner as IClientChannel)?.WaitHandlePool?.TryGetDataAsync(request.Sign, out var waitDataAsync) == true)
|
|
||||||
{
|
|
||||||
waitDataAsync.SetResult(request);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (result == FilterResult.Success)
|
else if (result == FilterResult.Success)
|
||||||
{
|
{
|
||||||
var addLen = request.HeaderLength + request.BodyLength;
|
request1 = request;
|
||||||
byteBlock.Position = pos + (addLen > 0 ? addLen : 1);
|
return true;
|
||||||
await GoReceived(remoteEndPoint, null, request).ConfigureAwait(false);
|
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
byteBlock.Position = pos + 1;
|
|
||||||
request.OperCode = -1;
|
request.OperCode = -1;
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Logger?.LogWarning(ex, $"{ToString()} Received parsing error");
|
Logger?.LogWarning(ex, $"{ToString()} Received parsing error");
|
||||||
byteBlock.Position = byteBlock.Length;//移动游标
|
return false;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
protected override async Task PreviewSendAsync(EndPoint endPoint, ReadOnlyMemory<byte> memory, CancellationToken cancellationToken)
|
protected override Task PreviewReceivedAsync(EndPoint remoteEndPoint, ReadOnlyMemory<byte> memory)
|
||||||
|
{
|
||||||
|
if (ParseRequestCore(remoteEndPoint, memory, out var request))
|
||||||
|
{
|
||||||
|
return GoReceived(remoteEndPoint, null, request);
|
||||||
|
}
|
||||||
|
return EasyTask.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
protected override Task PreviewSendAsync(EndPoint endPoint, ReadOnlyMemory<byte> memory, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
if (Logger?.LogLevel <= LogLevel.Trace)
|
if (Logger?.LogLevel <= LogLevel.Trace)
|
||||||
Logger?.Trace($"{ToString()}- Send:{(IsHexLog ? memory.Span.ToHexString(' ') : (memory.Span.ToString(Encoding.UTF8)))}");
|
Logger?.Trace($"{ToString()}- Send:{(IsHexLog ? memory.Span.ToHexString(' ') : (memory.Span.ToString(Encoding.UTF8)))}");
|
||||||
//发送
|
//发送
|
||||||
await GoSendAsync(endPoint, memory, cancellationToken).ConfigureAwait(false);
|
return GoSendAsync(endPoint, memory, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
protected override async Task PreviewSendAsync(EndPoint endPoint, IRequestInfo requestInfo, CancellationToken cancellationToken)
|
protected override Task PreviewSendAsync(EndPoint endPoint, IRequestInfo requestInfo, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (!(requestInfo is ISendMessage sendMessage))
|
if (!(requestInfo is ISendMessage sendMessage))
|
||||||
{
|
{
|
||||||
throw new Exception($"Unable to convert {nameof(requestInfo)} to {nameof(ISendMessage)}");
|
throw new Exception($"Unable to convert {nameof(requestInfo)} to {nameof(ISendMessage)}");
|
||||||
}
|
}
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
var byteBlock = new ValueByteBlock(sendMessage.MaxLength);
|
var byteBlock = new ValueByteBlock(sendMessage.MaxLength);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -173,9 +194,9 @@ public class DeviceUdpDataHandleAdapter<TRequest> : UdpDataHandlingAdapter where
|
|||||||
|
|
||||||
if (IsSingleThread)
|
if (IsSingleThread)
|
||||||
{
|
{
|
||||||
SetRequest(sendMessage, ref byteBlock);
|
SetRequest(sendMessage);
|
||||||
}
|
}
|
||||||
await GoSendAsync(endPoint, byteBlock.Memory, cancellationToken).ConfigureAwait(false);
|
return GoSendAsync(endPoint, byteBlock.Memory, cancellationToken);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,46 +0,0 @@
|
|||||||
//------------------------------------------------------------------------------
|
|
||||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
|
||||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
|
||||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
|
||||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
|
||||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
|
||||||
// 使用文档:https://thingsgateway.cn/
|
|
||||||
// QQ群:605534569
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
namespace ThingsGateway.Foundation;
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 定义了字节块构建器的接口,用于从内存池中构建和管理字节块。
|
|
||||||
/// </summary>
|
|
||||||
public interface IByteBlockWriterBuilder
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 构建数据时,指示内存池的申请长度。
|
|
||||||
/// <para>
|
|
||||||
/// 建议:该值可以尽可能的设置大一些,这样可以避免内存池扩容。
|
|
||||||
/// </para>
|
|
||||||
/// </summary>
|
|
||||||
int MaxLength { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 构建对象到<see cref="ByteBlock"/>
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="writer">要构建的字节块对象引用。</param>
|
|
||||||
void Build<TWriter>(ref TWriter writer) where TWriter : IByteBlockWriter
|
|
||||||
#if AllowsRefStruct
|
|
||||||
,allows ref struct
|
|
||||||
#endif
|
|
||||||
;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 指示<see cref="IRequestInfo"/>应当如何构建
|
|
||||||
/// </summary>
|
|
||||||
public interface IRequestInfoByteBlockWriterBuilder : IRequestInfo, IByteBlockWriterBuilder
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://thingsgateway.cn/
|
||||||
|
// QQ群:605534569
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
namespace ThingsGateway.Foundation
|
||||||
|
{
|
||||||
|
public interface IDeviceDataHandleAdapter
|
||||||
|
{
|
||||||
|
bool CanSendRequestInfo { get; }
|
||||||
|
bool IsHexLog { get; set; }
|
||||||
|
bool IsSingleThread { get; set; }
|
||||||
|
ILog Logger { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,7 +18,7 @@ public interface IResultMessage : IOperResult, IRequestInfo
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 数据体长度
|
/// 数据体长度
|
||||||
/// </summary>
|
/// </summary>
|
||||||
int BodyLength { get; set; }
|
long BodyLength { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 解析的字节信息
|
/// 解析的字节信息
|
||||||
@@ -28,7 +28,7 @@ public interface IResultMessage : IOperResult, IRequestInfo
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 消息头的指令长度,不固定时返回0
|
/// 消息头的指令长度,不固定时返回0
|
||||||
/// </summary>
|
/// </summary>
|
||||||
int HeaderLength { get; }
|
long HeaderLength { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 等待标识,对于并发协议,必须从协议中例如固定头部获取标识字段
|
/// 等待标识,对于并发协议,必须从协议中例如固定头部获取标识字段
|
||||||
@@ -42,17 +42,17 @@ public interface IResultMessage : IOperResult, IRequestInfo
|
|||||||
/// <para>然后返回<see cref="FilterResult.GoOn"/></para>
|
/// <para>然后返回<see cref="FilterResult.GoOn"/></para>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>是否成功有效</returns>
|
/// <returns>是否成功有效</returns>
|
||||||
FilterResult CheckBody<TByteBlock>(ref TByteBlock byteBlock) where TByteBlock : IByteBlockReader;
|
FilterResult CheckBody<TByteBlock>(ref TByteBlock byteBlock) where TByteBlock : IBytesReader;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 检查头子节的合法性,并赋值<see cref="BodyLength"/><br />
|
/// 检查头子节的合法性,并赋值<see cref="BodyLength"/><br />
|
||||||
/// <para>如果返回false,意味着放弃本次解析的所有数据,包括已经解析完成的Header</para>
|
/// <para>如果返回false,意味着放弃本次解析的所有数据,包括已经解析完成的Header</para>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>是否成功的结果</returns>
|
/// <returns>是否成功的结果</returns>
|
||||||
bool CheckHead<TByteBlock>(ref TByteBlock byteBlock) where TByteBlock : IByteBlockReader;
|
bool CheckHead<TByteBlock>(ref TByteBlock byteBlock) where TByteBlock : IBytesReader;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 发送前的信息处理,例如存储某些特征信息:站号/功能码等等用于验证后续的返回信息是否合法
|
/// 发送前的信息处理,例如存储某些特征信息:站号/功能码等等用于验证后续的返回信息是否合法
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void SendInfo(ISendMessage sendMessage, ref ValueByteBlock byteBlock);
|
void SendInfo(ISendMessage sendMessage);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,6 @@ namespace ThingsGateway.Foundation;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 发送消息
|
/// 发送消息
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface ISendMessage : IRequestInfo, IWaitHandle, IRequestInfoByteBlockWriterBuilder
|
public interface ISendMessage : IRequestInfo, IWaitHandle, IRequestInfoBuilder
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,28 +33,28 @@ public class MessageBase : OperResultClass<ReadOnlyMemory<byte>>, IResultMessage
|
|||||||
#endregion 构造
|
#endregion 构造
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public int BodyLength { get; set; }
|
public long BodyLength { get; set; }
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public virtual int HeaderLength { get; set; }
|
public virtual long HeaderLength { get; set; }
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public virtual int Sign { get; set; } = -1;
|
public virtual int Sign { get; set; } = -1;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public virtual FilterResult CheckBody<TByteBlock>(ref TByteBlock byteBlock) where TByteBlock : IByteBlockReader
|
public virtual FilterResult CheckBody<TByteBlock>(ref TByteBlock byteBlock) where TByteBlock : IBytesReader
|
||||||
{
|
{
|
||||||
return FilterResult.Success;
|
return FilterResult.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public virtual bool CheckHead<TByteBlock>(ref TByteBlock byteBlock) where TByteBlock : IByteBlockReader
|
public virtual bool CheckHead<TByteBlock>(ref TByteBlock byteBlock) where TByteBlock : IBytesReader
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public virtual void SendInfo(ISendMessage sendMessage, ref TouchSocket.Core.ValueByteBlock byteBlock)
|
public virtual void SendInfo(ISendMessage sendMessage)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,306 +0,0 @@
|
|||||||
//------------------------------------------------------------------------------
|
|
||||||
// 此代码版权(除特别声明或在XREF结尾的命名空间的代码)归作者本人若汝棋茗所有
|
|
||||||
// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权
|
|
||||||
// CSDN博客:https://blog.csdn.net/qq_40374647
|
|
||||||
// 哔哩哔哩视频:https://space.bilibili.com/94253567
|
|
||||||
// Gitee源代码仓库:https://gitee.com/RRQM_Home
|
|
||||||
// Github源代码仓库:https://github.com/RRQM
|
|
||||||
// API首页:https://touchsocket.net/
|
|
||||||
// 交流QQ群:234762506
|
|
||||||
// 感谢您的下载和使用
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
namespace ThingsGateway.Foundation;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 用户自定义数据处理适配器,使用该适配器时,接收方收到的数据中,<see cref="ByteBlock"/>将为<see langword="null"/>,
|
|
||||||
/// 同时<see cref="IRequestInfo"/>将实现为TRequest,发送数据直接发送。
|
|
||||||
/// </summary>
|
|
||||||
public abstract class TcpCustomDataHandlingAdapter<TRequest> : SingleStreamDataHandlingAdapter where TRequest : IRequestInfo
|
|
||||||
{
|
|
||||||
private readonly Type m_requestType;
|
|
||||||
private ValueByteBlock m_tempByteBlock;
|
|
||||||
private TRequest m_tempRequest;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 初始化自定义数据处理适配器。
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// 该构造函数在创建<see cref="TcpCustomDataHandlingAdapter{TRequest}"/>实例时,会指定请求类型。
|
|
||||||
/// </remarks>
|
|
||||||
public TcpCustomDataHandlingAdapter()
|
|
||||||
{
|
|
||||||
this.m_requestType = typeof(TRequest);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public override bool CanSendRequestInfo => false;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 指示需要解析当前包的剩余长度。如果不能得知,请赋值<see cref="int.MaxValue"/>。
|
|
||||||
/// </summary>
|
|
||||||
protected int SurLength { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 尝试解析请求数据块。
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="TByteBlock">字节块类型,必须实现IByteBlock接口。</typeparam>
|
|
||||||
/// <param name="reader">待解析的字节块。</param>
|
|
||||||
/// <param name="request">解析出的请求对象。</param>
|
|
||||||
/// <returns>解析是否成功。</returns>
|
|
||||||
public bool TryParseRequest<TByteBlock>(ref TByteBlock reader, out TRequest request) where TByteBlock : IByteBlockReader
|
|
||||||
{
|
|
||||||
// 检查缓存是否超时,如果超时则清除缓存。
|
|
||||||
if (this.CacheTimeoutEnable && DateTimeOffset.UtcNow - this.LastCacheTime > this.CacheTimeout)
|
|
||||||
{
|
|
||||||
this.Reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 如果临时字节块为空,则尝试直接解析。
|
|
||||||
if (this.m_tempByteBlock.IsEmpty)
|
|
||||||
{
|
|
||||||
return this.Single(ref reader, out request) == FilterResult.Success;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// 如果剩余长度小于等于0,则抛出异常。
|
|
||||||
if (this.SurLength <= 0)
|
|
||||||
{
|
|
||||||
throw new Exception();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 计算本次可以读取的长度。
|
|
||||||
var len = Math.Min(this.SurLength, reader.CanReadLength);
|
|
||||||
|
|
||||||
// 从输入字节块中读取数据到临时字节块中。
|
|
||||||
var block = this.m_tempByteBlock;
|
|
||||||
block.Write(reader.Span.Slice(reader.Position, len));
|
|
||||||
reader.Position += len;
|
|
||||||
this.SurLength -= len;
|
|
||||||
|
|
||||||
// 重置临时字节块并准备下一次使用。
|
|
||||||
this.m_tempByteBlock = default;
|
|
||||||
|
|
||||||
// 回到字节块的起始位置。
|
|
||||||
block.SeekToStart();
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// 尝试解析字节块。
|
|
||||||
var filterResult = this.Single(ref block, out request);
|
|
||||||
switch (filterResult)
|
|
||||||
{
|
|
||||||
case FilterResult.Cache:
|
|
||||||
{
|
|
||||||
// 如果临时字节块不为空,则继续缓存。
|
|
||||||
if (!this.m_tempByteBlock.IsEmpty)
|
|
||||||
{
|
|
||||||
reader.Position += this.m_tempByteBlock.Length;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
case FilterResult.Success:
|
|
||||||
{
|
|
||||||
// 如果字节块中还有剩余数据,则回退指针。
|
|
||||||
if (block.CanReadLength > 0)
|
|
||||||
{
|
|
||||||
reader.Position -= block.CanReadLength;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
case FilterResult.GoOn:
|
|
||||||
default:
|
|
||||||
// 对于需要继续解析的情况,也回退指针。
|
|
||||||
if (block.CanReadLength > 0)
|
|
||||||
{
|
|
||||||
reader.Position -= block.CanReadLength;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
// 释放字节块资源。
|
|
||||||
block.Dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 筛选解析数据。实例化的TRequest会一直保存,直至解析成功,或手动清除。
|
|
||||||
/// <para>当不满足解析条件时,请返回<see cref="FilterResult.Cache"/>,此时会保存<see cref="ByteBlock.CanReadLength"/>的数据</para>
|
|
||||||
/// <para>当数据部分异常时,请移动<see cref="ByteBlock.Position"/>到指定位置,然后返回<see cref="FilterResult.GoOn"/></para>
|
|
||||||
/// <para>当完全满足解析条件时,请返回<see cref="FilterResult.Success"/>最后将<see cref="ByteBlock.Position"/>移至指定位置。</para>
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="reader">字节块</param>
|
|
||||||
/// <param name="beCached">是否为上次遗留对象,当该参数为<see langword="true"/>时,request也将是上次实例化的对象。</param>
|
|
||||||
/// <param name="request">对象。</param>
|
|
||||||
/// <param name="tempCapacity">缓存容量。当需要首次缓存时,指示申请的ByteBlock的容量。合理的值可避免ByteBlock扩容带来的性能消耗。</param>
|
|
||||||
/// <returns></returns>
|
|
||||||
protected abstract FilterResult Filter<TByteBlock>(ref TByteBlock reader, bool beCached, ref TRequest request, ref int tempCapacity)
|
|
||||||
where TByteBlock : IByteBlockReader;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 判断请求对象是否应该被缓存。
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="request">请求对象。</param>
|
|
||||||
/// <returns>返回布尔值,指示请求对象是否应该被缓存。</returns>
|
|
||||||
protected virtual bool IsBeCached(in TRequest request)
|
|
||||||
{
|
|
||||||
return this.m_requestType.IsValueType ? request.GetHashCode() != default(TRequest).GetHashCode() : request != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 成功执行接收以后。
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="request"></param>
|
|
||||||
protected virtual void OnReceivedSuccess(TRequest request)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 即将执行<see cref="SingleStreamDataHandlingAdapter.GoReceivedAsync(IByteBlockReader, IRequestInfo)"/>。
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="request"></param>
|
|
||||||
/// <returns>返回值标识是否继续执行</returns>
|
|
||||||
protected virtual bool OnReceivingSuccess(TRequest request)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
/// <param name="byteBlock"></param>
|
|
||||||
protected override async Task PreviewReceivedAsync(IByteBlockReader byteBlock)
|
|
||||||
{
|
|
||||||
if (this.CacheTimeoutEnable && DateTimeOffset.UtcNow - this.LastCacheTime > this.CacheTimeout)
|
|
||||||
{
|
|
||||||
this.Reset();
|
|
||||||
}
|
|
||||||
if (this.m_tempByteBlock.IsEmpty)
|
|
||||||
{
|
|
||||||
await this.SingleAsync(byteBlock).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
this.m_tempByteBlock.Write(byteBlock.Span);
|
|
||||||
using (var block = this.m_tempByteBlock)
|
|
||||||
{
|
|
||||||
this.m_tempByteBlock = default;
|
|
||||||
await this.SingleAsync(block).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
protected override void Reset()
|
|
||||||
{
|
|
||||||
this.m_tempByteBlock.SafeDispose();
|
|
||||||
this.m_tempByteBlock = default;
|
|
||||||
this.m_tempRequest = default;
|
|
||||||
this.SurLength = 0;
|
|
||||||
base.Reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 处理单个字节块,提取请求对象。
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="TByteBlock">字节块类型,需要实现IByteBlock接口。</typeparam>
|
|
||||||
/// <param name="reader">字节块,将被解析以提取请求对象。</param>
|
|
||||||
/// <param name="request">输出参数,提取出的请求对象。</param>
|
|
||||||
/// <returns>返回过滤结果,指示处理的状态。</returns>
|
|
||||||
protected FilterResult Single<TByteBlock>(ref TByteBlock reader, out TRequest request) where TByteBlock : IByteBlockReader
|
|
||||||
{
|
|
||||||
// 初始化临时缓存容量。
|
|
||||||
var tempCapacity = 1024 * 64;
|
|
||||||
// 执行过滤操作,根据是否应该缓存来决定如何处理字节块和请求对象。
|
|
||||||
var filterResult = this.Filter(ref reader, this.IsBeCached(this.m_tempRequest), ref this.m_tempRequest, ref tempCapacity);
|
|
||||||
switch (filterResult)
|
|
||||||
{
|
|
||||||
case FilterResult.Success:
|
|
||||||
// 如果过滤结果是成功,则设置请求对象并重置临时请求对象为默认值。
|
|
||||||
request = this.m_tempRequest;
|
|
||||||
this.m_tempRequest = default;
|
|
||||||
return filterResult;
|
|
||||||
|
|
||||||
case FilterResult.Cache:
|
|
||||||
// 如果过滤结果需要缓存,则创建一个新的字节块来缓存数据。
|
|
||||||
if (reader.CanReadLength > 0)
|
|
||||||
{
|
|
||||||
this.m_tempByteBlock = new ValueByteBlock(tempCapacity);
|
|
||||||
this.m_tempByteBlock.Write(reader.Span.Slice(reader.Position, reader.CanReadLength));
|
|
||||||
|
|
||||||
// 如果缓存的数据长度超过设定的最大包大小,则抛出异常。
|
|
||||||
if (this.m_tempByteBlock.Length > this.MaxPackageSize)
|
|
||||||
{
|
|
||||||
throw new Exception($"The parsed signal was not received when the cached data length {m_tempByteBlock.Length} exceeds the set value {MaxPackageSize}");
|
|
||||||
}
|
|
||||||
|
|
||||||
// 将字节块指针移到末尾。
|
|
||||||
reader.Advance((int)reader.BytesRemaining);
|
|
||||||
}
|
|
||||||
// 更新缓存时间。
|
|
||||||
if (this.UpdateCacheTimeWhenRev)
|
|
||||||
{
|
|
||||||
this.LastCacheTime = DateTimeOffset.UtcNow;
|
|
||||||
}
|
|
||||||
request = default;
|
|
||||||
return filterResult;
|
|
||||||
|
|
||||||
case FilterResult.GoOn:
|
|
||||||
default:
|
|
||||||
// 对于继续或默认的过滤结果,更新缓存时间。
|
|
||||||
if (this.UpdateCacheTimeWhenRev)
|
|
||||||
{
|
|
||||||
this.LastCacheTime = DateTimeOffset.UtcNow;
|
|
||||||
}
|
|
||||||
request = default;
|
|
||||||
return filterResult;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task SingleAsync<TByteBlock>(TByteBlock reader) where TByteBlock : IByteBlockReader
|
|
||||||
{
|
|
||||||
reader.Position = 0;
|
|
||||||
while (reader.Position < reader.Length)
|
|
||||||
{
|
|
||||||
if (this.DisposedValue)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var tempCapacity = 1024 * 64;
|
|
||||||
var filterResult = this.Filter(ref reader, this.IsBeCached(this.m_tempRequest), ref this.m_tempRequest, ref tempCapacity);
|
|
||||||
|
|
||||||
switch (filterResult)
|
|
||||||
{
|
|
||||||
case FilterResult.Success:
|
|
||||||
if (this.OnReceivingSuccess(this.m_tempRequest))
|
|
||||||
{
|
|
||||||
await this.GoReceivedAsync(null, this.m_tempRequest).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
|
||||||
this.OnReceivedSuccess(this.m_tempRequest);
|
|
||||||
}
|
|
||||||
this.m_tempRequest = default;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case FilterResult.Cache:
|
|
||||||
this.m_tempByteBlock = new ValueByteBlock(tempCapacity);
|
|
||||||
this.m_tempByteBlock.Write(reader.Span);
|
|
||||||
|
|
||||||
if (this.m_tempByteBlock.Length > this.MaxPackageSize)
|
|
||||||
{
|
|
||||||
this.OnError(default, $"The parsed signal was not received when the cached data length {m_tempByteBlock.Length} exceeds the set value {MaxPackageSize}", true, true);
|
|
||||||
}
|
|
||||||
if (this.UpdateCacheTimeWhenRev)
|
|
||||||
{
|
|
||||||
this.LastCacheTime = DateTimeOffset.UtcNow;
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
|
|
||||||
case FilterResult.GoOn:
|
|
||||||
if (this.UpdateCacheTimeWhenRev)
|
|
||||||
{
|
|
||||||
this.LastCacheTime = DateTimeOffset.UtcNow;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -214,10 +214,7 @@ public abstract class DeviceBase : AsyncAndSyncDisposableObject, IDevice
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
protected virtual ValueTask<bool> ChannelStarting(IClientChannel channel, bool last)
|
protected virtual ValueTask<bool> ChannelStarting(IClientChannel channel, bool last)
|
||||||
{
|
{
|
||||||
if (channel.ReadOnlyDataHandlingAdapter != null)
|
channel.SetDataHandlingAdapterLogger(Logger);
|
||||||
{
|
|
||||||
channel.ReadOnlyDataHandlingAdapter.Logger = Logger;
|
|
||||||
}
|
|
||||||
return EasyValueTask.FromResult(true);
|
return EasyValueTask.FromResult(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -318,7 +315,7 @@ public abstract class DeviceBase : AsyncAndSyncDisposableObject, IDevice
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (client.WaitHandlePool.SetRun(response))
|
if (client.WaitHandlePool.Set(response))
|
||||||
{
|
{
|
||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
}
|
}
|
||||||
@@ -366,21 +363,13 @@ public abstract class DeviceBase : AsyncAndSyncDisposableObject, IDevice
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async ValueTask BefortSendAsync(IClientChannel channel, CancellationToken token)
|
private Task BefortSendAsync(IClientChannel channel, CancellationToken token)
|
||||||
{
|
{
|
||||||
SetDataAdapter(channel);
|
SetDataAdapter(channel);
|
||||||
try
|
|
||||||
{
|
|
||||||
await ConnectAsync(token).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
catch (Exception)
|
|
||||||
{
|
|
||||||
await Task.Delay(1000, token).ConfigureAwait(false);
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (token.IsCancellationRequested)
|
return ConnectAsync(token);
|
||||||
throw new OperationCanceledException();
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private WaitLock connectWaitLock = new(nameof(DeviceBase));
|
private WaitLock connectWaitLock = new(nameof(DeviceBase));
|
||||||
@@ -397,13 +386,13 @@ public abstract class DeviceBase : AsyncAndSyncDisposableObject, IDevice
|
|||||||
if (Channel.PluginManager == null)
|
if (Channel.PluginManager == null)
|
||||||
await Channel.SetupAsync(Channel.Config.Clone()).ConfigureAwait(false);
|
await Channel.SetupAsync(Channel.Config.Clone()).ConfigureAwait(false);
|
||||||
await Channel.CloseAsync().ConfigureAwait(false);
|
await Channel.CloseAsync().ConfigureAwait(false);
|
||||||
await Task.Delay(500, token).ConfigureAwait(false);
|
using var ctsTime = new CancellationTokenSource(Channel.ChannelOptions.ConnectTimeout);
|
||||||
await Channel.ConnectAsync(Channel.ChannelOptions.ConnectTimeout, token).ConfigureAwait(false);
|
using var cts = CancellationTokenSource.CreateLinkedTokenSource(ctsTime.Token, token);
|
||||||
|
await Channel.ConnectAsync(cts.Token).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
try { await Task.Delay(500, token).ConfigureAwait(false); } catch { }
|
|
||||||
connectWaitLock.Release();
|
connectWaitLock.Release();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -424,8 +413,7 @@ public abstract class DeviceBase : AsyncAndSyncDisposableObject, IDevice
|
|||||||
await BefortSendAsync(channelResult.Content, cancellationToken).ConfigureAwait(false);
|
await BefortSendAsync(channelResult.Content, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
await waitLock.WaitAsync(cancellationToken).ConfigureAwait(false);
|
await waitLock.WaitAsync(cancellationToken).ConfigureAwait(false);
|
||||||
if (channelResult.Content.ReadOnlyDataHandlingAdapter != null)
|
channelResult.Content.SetDataHandlingAdapterLogger(Logger);
|
||||||
channelResult.Content.ReadOnlyDataHandlingAdapter.Logger = Logger;
|
|
||||||
|
|
||||||
EndPoint? endPoint = GetUdpEndpoint(dtuId);
|
EndPoint? endPoint = GetUdpEndpoint(dtuId);
|
||||||
|
|
||||||
@@ -438,6 +426,8 @@ public abstract class DeviceBase : AsyncAndSyncDisposableObject, IDevice
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
if (!cancellationToken.IsCancellationRequested)
|
||||||
|
await Task.Delay(1000, cancellationToken).ConfigureAwait(false);
|
||||||
return new(ex);
|
return new(ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -513,8 +503,6 @@ public abstract class DeviceBase : AsyncAndSyncDisposableObject, IDevice
|
|||||||
return SendThenReturnAsync(sendMessage, channelResult.Content, cancellationToken);
|
return SendThenReturnAsync(sendMessage, channelResult.Content, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public virtual async ValueTask<OperResult<ReadOnlyMemory<byte>>> SendThenReturnAsync(ISendMessage sendMessage, IClientChannel channel, CancellationToken cancellationToken = default)
|
public virtual async ValueTask<OperResult<ReadOnlyMemory<byte>>> SendThenReturnAsync(ISendMessage sendMessage, IClientChannel channel, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
@@ -530,11 +518,11 @@ public abstract class DeviceBase : AsyncAndSyncDisposableObject, IDevice
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
protected virtual async ValueTask<MessageBase> SendThenReturnMessageAsync(ISendMessage sendMessage, CancellationToken cancellationToken = default)
|
protected virtual ValueTask<MessageBase> SendThenReturnMessageAsync(ISendMessage sendMessage, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
var channelResult = GetChannel(this is IDtu dtu ? dtu.DtuId : null);
|
var channelResult = GetChannel(this is IDtu dtu ? dtu.DtuId : null);
|
||||||
if (!channelResult.IsSuccess) return new MessageBase(channelResult);
|
if (!channelResult.IsSuccess) return EasyValueTask.FromResult(new MessageBase(channelResult));
|
||||||
return await SendThenReturnMessageAsync(sendMessage, channelResult.Content, cancellationToken).ConfigureAwait(false);
|
return SendThenReturnMessageAsync(sendMessage, channelResult.Content, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
@@ -565,10 +553,8 @@ public abstract class DeviceBase : AsyncAndSyncDisposableObject, IDevice
|
|||||||
if (cancellationToken.IsCancellationRequested)
|
if (cancellationToken.IsCancellationRequested)
|
||||||
return new MessageBase(new OperationCanceledException());
|
return new MessageBase(new OperationCanceledException());
|
||||||
|
|
||||||
if (clientChannel.ReadOnlyDataHandlingAdapter != null)
|
clientChannel.SetDataHandlingAdapterLogger(Logger);
|
||||||
clientChannel.ReadOnlyDataHandlingAdapter.Logger = Logger;
|
|
||||||
|
|
||||||
Channel.ChannelReceivedWaitDict.TryAdd(sign, ChannelReceived);
|
|
||||||
|
|
||||||
if (cancellationToken.IsCancellationRequested)
|
if (cancellationToken.IsCancellationRequested)
|
||||||
return new MessageBase(new OperationCanceledException());
|
return new MessageBase(new OperationCanceledException());
|
||||||
@@ -577,20 +563,29 @@ public abstract class DeviceBase : AsyncAndSyncDisposableObject, IDevice
|
|||||||
if (!sendOperResult.IsSuccess)
|
if (!sendOperResult.IsSuccess)
|
||||||
return new MessageBase(sendOperResult);
|
return new MessageBase(sendOperResult);
|
||||||
|
|
||||||
|
using var ctsTime = new CancellationTokenSource(timeout);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
waitData.SetCancellationToken(Channel.ClosedToken);
|
using var cts = CancellationTokenSource.CreateLinkedTokenSource(ctsTime.Token, Channel.ClosedToken);
|
||||||
|
await waitData.WaitAsync(cts.Token).ConfigureAwait(false);
|
||||||
await waitData.WaitAsync(timeout).ConfigureAwait(false);
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
if (ctsTime.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
return new MessageBase(new TimeoutException());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
return new MessageBase(ex);
|
return new MessageBase(ex);
|
||||||
}
|
}
|
||||||
var result = waitData.Check();
|
|
||||||
|
var result = waitData.Check(ctsTime.Token);
|
||||||
|
|
||||||
if (result.IsSuccess)
|
if (result.IsSuccess)
|
||||||
{
|
{
|
||||||
return waitData.WaitResult;
|
return waitData.CompletedData;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -599,13 +594,14 @@ public abstract class DeviceBase : AsyncAndSyncDisposableObject, IDevice
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
if (!cancellationToken.IsCancellationRequested)
|
||||||
|
await Task.Delay(1000, cancellationToken).ConfigureAwait(false);
|
||||||
return new MessageBase(ex);
|
return new MessageBase(ex);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
Channel.ChannelReceivedWaitDict.TryRemove(sign, out _);
|
|
||||||
waitLock?.Release();
|
waitLock?.Release();
|
||||||
clientChannel.WaitHandlePool.Destroy(sign);
|
waitData?.SafeDispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -964,10 +960,10 @@ public abstract class DeviceBase : AsyncAndSyncDisposableObject, IDevice
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public virtual async ValueTask<OperResult> WriteAsync(string address, ReadOnlyMemory<string> value, IThingsGatewayBitConverter bitConverter = null, CancellationToken cancellationToken = default)
|
public virtual ValueTask<OperResult> WriteAsync(string address, ReadOnlyMemory<string> value, IThingsGatewayBitConverter bitConverter = null, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
bitConverter ??= ThingsGatewayBitConverter.GetTransByAddress(address);
|
bitConverter ??= ThingsGatewayBitConverter.GetTransByAddress(address);
|
||||||
if (bitConverter.StringLength == null) return new OperResult(AppResource.StringAddressError);
|
if (bitConverter.StringLength == null) return EasyValueTask.FromResult(new OperResult(AppResource.StringAddressError));
|
||||||
List<ReadOnlyMemory<byte>> bytes = new();
|
List<ReadOnlyMemory<byte>> bytes = new();
|
||||||
foreach (var a in value.Span)
|
foreach (var a in value.Span)
|
||||||
{
|
{
|
||||||
@@ -975,7 +971,7 @@ public abstract class DeviceBase : AsyncAndSyncDisposableObject, IDevice
|
|||||||
bytes.Add(data.ArrayExpandToLength(bitConverter.StringLength ?? data.Length));
|
bytes.Add(data.ArrayExpandToLength(bitConverter.StringLength ?? data.Length));
|
||||||
}
|
}
|
||||||
|
|
||||||
return await WriteAsync(address, bytes.CombineMemoryBlocks(), DataTypeEnum.String, cancellationToken).ConfigureAwait(false);
|
return WriteAsync(address, bytes.CombineMemoryBlocks(), DataTypeEnum.String, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion 写入数组
|
#endregion 写入数组
|
||||||
@@ -997,7 +993,7 @@ public abstract class DeviceBase : AsyncAndSyncDisposableObject, IDevice
|
|||||||
{
|
{
|
||||||
if (Channel is ITcpServiceChannel tcpServiceChannel)
|
if (Channel is ITcpServiceChannel tcpServiceChannel)
|
||||||
{
|
{
|
||||||
tcpServiceChannel.Clients.ForEach(a => a.WaitHandlePool.SafeDispose());
|
tcpServiceChannel.Clients.ForEach(a => a.WaitHandlePool?.CancelAll());
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
@@ -1006,7 +1002,7 @@ public abstract class DeviceBase : AsyncAndSyncDisposableObject, IDevice
|
|||||||
_ = Channel.CloseAsync();
|
_ = Channel.CloseAsync();
|
||||||
if (Channel is IClientChannel client)
|
if (Channel is IClientChannel client)
|
||||||
{
|
{
|
||||||
client.WaitHandlePool.SafeDispose();
|
client.WaitHandlePool?.CancelAll();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@@ -1020,7 +1016,7 @@ public abstract class DeviceBase : AsyncAndSyncDisposableObject, IDevice
|
|||||||
{
|
{
|
||||||
if (tcpServiceChannel.TryGetClient($"ID={dtu.DtuId}", out var client))
|
if (tcpServiceChannel.TryGetClient($"ID={dtu.DtuId}", out var client))
|
||||||
{
|
{
|
||||||
client.WaitHandlePool?.SafeDispose();
|
client.WaitHandlePool?.CancelAll();
|
||||||
_ = client.CloseAsync();
|
_ = client.CloseAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1049,7 +1045,7 @@ public abstract class DeviceBase : AsyncAndSyncDisposableObject, IDevice
|
|||||||
{
|
{
|
||||||
if (Channel is ITcpServiceChannel tcpServiceChannel)
|
if (Channel is ITcpServiceChannel tcpServiceChannel)
|
||||||
{
|
{
|
||||||
tcpServiceChannel.Clients.ForEach(a => a.WaitHandlePool.SafeDispose());
|
tcpServiceChannel.Clients.ForEach(a => a.WaitHandlePool?.CancelAll());
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
@@ -1058,7 +1054,7 @@ public abstract class DeviceBase : AsyncAndSyncDisposableObject, IDevice
|
|||||||
await Channel.CloseAsync().ConfigureAwait(false);
|
await Channel.CloseAsync().ConfigureAwait(false);
|
||||||
if (Channel is IClientChannel client)
|
if (Channel is IClientChannel client)
|
||||||
{
|
{
|
||||||
client.WaitHandlePool.SafeDispose();
|
client.WaitHandlePool?.CancelAll();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@@ -1072,7 +1068,7 @@ public abstract class DeviceBase : AsyncAndSyncDisposableObject, IDevice
|
|||||||
{
|
{
|
||||||
if (tcpServiceChannel.TryGetClient($"ID={dtu.DtuId}", out var client))
|
if (tcpServiceChannel.TryGetClient($"ID={dtu.DtuId}", out var client))
|
||||||
{
|
{
|
||||||
client.WaitHandlePool?.SafeDispose();
|
client.WaitHandlePool?.CancelAll();
|
||||||
await client.CloseAsync().ConfigureAwait(false);
|
await client.CloseAsync().ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -132,24 +132,43 @@ public static partial class DeviceExtension
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 当状态不是<see cref="WaitDataStatus.SetRunning"/>时返回异常。
|
/// 当状态不是<see cref="WaitDataStatus.Success"/>时返回异常。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static OperResult Check(this WaitDataAsync<MessageBase> waitDataAsync)
|
public static OperResult Check(this AsyncWaitData<MessageBase> waitDataAsync, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
switch (waitDataAsync.Status)
|
switch (waitDataAsync.Status)
|
||||||
{
|
{
|
||||||
case WaitDataStatus.SetRunning:
|
case WaitDataStatus.Success:
|
||||||
return new();
|
return new();
|
||||||
|
|
||||||
case WaitDataStatus.Canceled: return new(new OperationCanceledException());
|
case WaitDataStatus.Canceled:
|
||||||
|
if (cancellationToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
if (waitDataAsync.CompletedData != null)
|
||||||
|
{
|
||||||
|
waitDataAsync.CompletedData.Exception = new TimeoutException();
|
||||||
|
if (waitDataAsync.CompletedData.IsSuccess) waitDataAsync.CompletedData.OperCode = 999;
|
||||||
|
if (waitDataAsync.CompletedData.ErrorMessage.IsNullOrEmpty()) waitDataAsync.CompletedData.ErrorMessage = "Timeout";
|
||||||
|
return new(waitDataAsync.CompletedData);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return new(new TimeoutException());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
|
||||||
|
return new(new OperationCanceledException());
|
||||||
|
}
|
||||||
case WaitDataStatus.Overtime:
|
case WaitDataStatus.Overtime:
|
||||||
{
|
{
|
||||||
if (waitDataAsync.WaitResult != null)
|
if (waitDataAsync.CompletedData != null)
|
||||||
{
|
{
|
||||||
waitDataAsync.WaitResult.Exception = new TimeoutException();
|
waitDataAsync.CompletedData.Exception = new TimeoutException();
|
||||||
if (waitDataAsync.WaitResult.IsSuccess) waitDataAsync.WaitResult.OperCode = 999;
|
if (waitDataAsync.CompletedData.IsSuccess) waitDataAsync.CompletedData.OperCode = 999;
|
||||||
if (waitDataAsync.WaitResult.ErrorMessage.IsNullOrEmpty()) waitDataAsync.WaitResult.ErrorMessage = "Timeout";
|
if (waitDataAsync.CompletedData.ErrorMessage.IsNullOrEmpty()) waitDataAsync.CompletedData.ErrorMessage = "Timeout";
|
||||||
return new(waitDataAsync.WaitResult);
|
return new(waitDataAsync.CompletedData);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -160,7 +179,7 @@ public static partial class DeviceExtension
|
|||||||
case WaitDataStatus.Default:
|
case WaitDataStatus.Default:
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
return waitDataAsync.WaitResult == null ? new(new Exception(AppResource.UnknownError)) : new(waitDataAsync.WaitResult);
|
return waitDataAsync.CompletedData == null ? new(new Exception(AppResource.UnknownError)) : new(waitDataAsync.CompletedData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
using System.Buffers;
|
using System.Buffers;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace ThingsGateway.Foundation;
|
namespace ThingsGateway.Foundation;
|
||||||
@@ -20,70 +21,8 @@ namespace ThingsGateway.Foundation;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static class ByteBlockExtension
|
public static class ByteBlockExtension
|
||||||
{
|
{
|
||||||
public static void WriteBackAddValue<TWriter>(ref TWriter writer, byte value, int pos)
|
|
||||||
where TWriter : IByteBlockWriter
|
|
||||||
{
|
|
||||||
int nowPos = writer.Position;
|
|
||||||
writer.Position = pos;
|
|
||||||
WriterExtension.WriteValue(ref writer, (byte)(writer.Span[pos] + value));
|
|
||||||
writer.Position = nowPos;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public static void WriteBackValue<TWriter, T>(ref TWriter writer, T value, int pos)
|
|
||||||
where T : unmanaged
|
|
||||||
where TWriter : IByteBlockWriter
|
|
||||||
{
|
|
||||||
int nowPos = writer.Position;
|
|
||||||
writer.Position = pos;
|
|
||||||
WriterExtension.WriteValue(ref writer, value);
|
|
||||||
writer.Position = nowPos;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void WriteBackValue<TWriter, T>(ref TWriter writer, T value, EndianType endianType, int pos)
|
|
||||||
where T : unmanaged
|
|
||||||
where TWriter : IByteBlockWriter
|
|
||||||
{
|
|
||||||
int nowPos = writer.Position;
|
|
||||||
writer.Position = pos;
|
|
||||||
WriterExtension.WriteValue(ref writer, value, endianType);
|
|
||||||
writer.Position = nowPos;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void WriteBackNormalString<TWriter>(ref TWriter writer, string value, Encoding encoding, int pos)
|
|
||||||
where TWriter : IByteBlockWriter
|
|
||||||
{
|
|
||||||
|
|
||||||
int nowPos = writer.Position;
|
|
||||||
writer.Position = pos;
|
|
||||||
WriterExtension.WriteNormalString(ref writer, value, encoding);
|
|
||||||
writer.Position = nowPos;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public static string ReadNormalString<TReader>(ref TReader reader, int length)
|
|
||||||
where TReader : IBytesReader
|
|
||||||
{
|
|
||||||
var span = reader.GetSpan(length).Slice(0, length);
|
|
||||||
var str = span.ToString(Encoding.UTF8);
|
|
||||||
reader.Advance(length);
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 将值类型的字节块转换为普通的字节块。
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="valueByteBlock">要转换的值类型字节块。</param>
|
|
||||||
/// <returns>一个新的字节块对象。</returns>
|
|
||||||
public static ByteBlock AsByteBlock(this ValueByteBlock valueByteBlock)
|
|
||||||
{
|
|
||||||
ByteBlock byteBlock = new ByteBlock(valueByteBlock.TotalMemory.Slice(0, valueByteBlock.Length));
|
|
||||||
byteBlock.Position = valueByteBlock.Position;
|
|
||||||
byteBlock.SetLength(valueByteBlock.Length);
|
|
||||||
return byteBlock;
|
|
||||||
}
|
|
||||||
|
|
||||||
#region ToArray
|
#region ToArray
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -259,5 +198,252 @@ public static class ByteBlockExtension
|
|||||||
return ToString(byteBlock, offset, byteBlock.BytesRead + byteBlock.BytesRemaining - offset);
|
return ToString(byteBlock, offset, byteBlock.BytesRead + byteBlock.BytesRemaining - offset);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public static string ToString<TByteBlock>(this TByteBlock byteBlock) where TByteBlock : IBytesReader
|
||||||
|
{
|
||||||
|
return byteBlock.TotalSequence.ToString(Encoding.UTF8);
|
||||||
|
}
|
||||||
#endregion ToString
|
#endregion ToString
|
||||||
|
|
||||||
|
#region ToHexString
|
||||||
|
|
||||||
|
|
||||||
|
public static string ToHexString<TByteBlock>(this TByteBlock byteBlock, long offset, long length, char splite = default) where TByteBlock : IBytesReader
|
||||||
|
{
|
||||||
|
return byteBlock.TotalSequence.Slice(offset, length).ToHexString(Encoding.UTF8, splite);
|
||||||
|
}
|
||||||
|
public static string ToHexString(this ReadOnlySequence<byte> byteBlock, Encoding encoding, char splite = default)
|
||||||
|
{
|
||||||
|
return byteBlock.ToHexString(splite);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public static string ToHexString<TByteBlock>(this TByteBlock byteBlock, long offset, char splite = default) where TByteBlock : IBytesReader
|
||||||
|
{
|
||||||
|
return ToHexString(byteBlock, offset, byteBlock.BytesRead + byteBlock.BytesRemaining - offset, splite);
|
||||||
|
|
||||||
|
}
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public static string ToHexString<TByteBlock>(this TByteBlock byteBlock, char splite = default) where TByteBlock : IBytesReader
|
||||||
|
{
|
||||||
|
return byteBlock.TotalSequence.ToHexString(Encoding.UTF8, splite);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion ToHexString
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 在 <see cref="ReadOnlySequence{T}"/> 中查找第一个与指定 <see cref="byte"/> 匹配的子序列的起始索引。
|
||||||
|
/// <para>如果未找到则返回 -1。</para>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sequence">要搜索的字节序列。</param>
|
||||||
|
/// <param name="firstByte">要查找的字节。</param>
|
||||||
|
/// <returns>匹配子序列的起始索引,未找到则返回 -1。</returns>
|
||||||
|
public static long IndexOf(this ReadOnlySequence<byte> sequence, byte firstByte)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (sequence.Length < 1)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
long globalPosition = 0;
|
||||||
|
var enumerator = sequence.GetEnumerator();
|
||||||
|
|
||||||
|
// 遍历每个内存段
|
||||||
|
while (enumerator.MoveNext())
|
||||||
|
{
|
||||||
|
var currentSpan = enumerator.Current.Span;
|
||||||
|
var localIndex = 0;
|
||||||
|
|
||||||
|
// 在当前段中搜索首字节
|
||||||
|
while (localIndex < currentSpan.Length)
|
||||||
|
{
|
||||||
|
// 查找首字节匹配位置
|
||||||
|
var matchIndex = currentSpan.Slice(localIndex).IndexOf(firstByte);
|
||||||
|
if (matchIndex == -1)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
localIndex += matchIndex;
|
||||||
|
var globalIndex = globalPosition + localIndex;
|
||||||
|
|
||||||
|
// 检查剩余长度是否足够
|
||||||
|
if (sequence.Length - globalIndex < 1)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查完整匹配
|
||||||
|
if (IsMatch(sequence, globalIndex, firstByte))
|
||||||
|
{
|
||||||
|
return globalIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
localIndex++; // 继续搜索下一个位置
|
||||||
|
}
|
||||||
|
globalPosition += currentSpan.Length;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsMatch(ReadOnlySequence<byte> sequence, long start, byte value)
|
||||||
|
{
|
||||||
|
return sequence.Slice(start, 1).First.Span[0] == value;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static byte GetByte(this ReadOnlySequence<byte> sequence, long index)
|
||||||
|
{
|
||||||
|
return sequence.Slice(index).First.Span[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 计算指定区间字节的和
|
||||||
|
/// </summary>
|
||||||
|
public static long SumRange(this ReadOnlySequence<byte> sequence, long start, long count)
|
||||||
|
{
|
||||||
|
if (start < 0 || count < 0 || start + count > sequence.Length)
|
||||||
|
throw new ArgumentOutOfRangeException();
|
||||||
|
|
||||||
|
long sum = 0;
|
||||||
|
long remaining = count;
|
||||||
|
|
||||||
|
foreach (var segment in sequence)
|
||||||
|
{
|
||||||
|
if (start >= segment.Length)
|
||||||
|
{
|
||||||
|
// 起点不在当前段
|
||||||
|
start -= segment.Length;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var span = segment.Span;
|
||||||
|
int take = (int)Math.Min(segment.Length - start, remaining);
|
||||||
|
|
||||||
|
for (int i = 0; i < take; i++)
|
||||||
|
sum += span[(int)start + i];
|
||||||
|
|
||||||
|
remaining -= take;
|
||||||
|
start = 0; // 后续段从头开始
|
||||||
|
|
||||||
|
if (remaining == 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sum;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 比较 ReadOnlySequence 与 ReadOnlySpan 的内容是否一致。
|
||||||
|
/// </summary>
|
||||||
|
public static bool SequenceEqual<T>(this ReadOnlySequence<T> sequence, ReadOnlySpan<T> other)
|
||||||
|
where T : IEquatable<T>
|
||||||
|
{
|
||||||
|
if (sequence.Length != other.Length)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// 单段,直接比较
|
||||||
|
if (sequence.IsSingleSegment)
|
||||||
|
{
|
||||||
|
return sequence.First.Span.SequenceEqual(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 多段,逐段比较
|
||||||
|
int offset = 0;
|
||||||
|
foreach (var segment in sequence)
|
||||||
|
{
|
||||||
|
var span = segment.Span;
|
||||||
|
if (!other.Slice(offset, span.Length).SequenceEqual(span))
|
||||||
|
return false;
|
||||||
|
offset += span.Length;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 将指定的字节块从当前位置<see cref="IByteBlockCore.Position"/>转换为【新】字节数组,指定长度。
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TByteBlock">实现<see cref="IByteBlock"/>接口的字节块类型。</typeparam>
|
||||||
|
/// <param name="byteBlock">字节块对象。</param>
|
||||||
|
/// <param name="length">要转换为数组的长度。</param>
|
||||||
|
/// <returns>从当前位置开始,指定长度的【新】字节数组。</returns>
|
||||||
|
public static byte[] ToArrayTake<TByteBlock>(this TByteBlock byteBlock, long length) where TByteBlock : IBytesReader
|
||||||
|
{
|
||||||
|
return byteBlock.Sequence.Slice(0, length).ToArray();
|
||||||
|
}
|
||||||
|
public static void Write<TByteBlock>(ref TByteBlock byteBlock, ReadOnlySequence<byte> bytes) where TByteBlock : IBytesWriter
|
||||||
|
{
|
||||||
|
foreach (var item in bytes)
|
||||||
|
{
|
||||||
|
byteBlock.Write(item.Span);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public static void WriteBackValue<TWriter, T>(ref TWriter writer, T value, EndianType endianType, int pos)
|
||||||
|
where T : unmanaged
|
||||||
|
where TWriter : IBytesWriter
|
||||||
|
{
|
||||||
|
if (writer.SupportsRewind)
|
||||||
|
{
|
||||||
|
var nowPos = (int)writer.WrittenCount - pos;
|
||||||
|
writer.Advance(-nowPos);
|
||||||
|
var size = Unsafe.SizeOf<T>();
|
||||||
|
var span = writer.GetSpan(size);
|
||||||
|
TouchSocketBitConverter.GetBitConverter(endianType).WriteBytes(span, value);
|
||||||
|
writer.Advance(nowPos);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Writer version mismatch or does not support rewind.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public static void WriteBackValue<TWriter, T>(ref TWriter writer, T value, EndianType endianType, long pos)
|
||||||
|
where T : unmanaged
|
||||||
|
where TWriter : IBytesWriter
|
||||||
|
{
|
||||||
|
if (writer.SupportsRewind)
|
||||||
|
{
|
||||||
|
var nowPos = (int)(writer.WrittenCount - pos);
|
||||||
|
writer.Advance(-nowPos);
|
||||||
|
var size = Unsafe.SizeOf<T>();
|
||||||
|
var span = writer.GetSpan(size);
|
||||||
|
TouchSocketBitConverter.GetBitConverter(endianType).WriteBytes(span, value);
|
||||||
|
writer.Advance(nowPos);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Writer version mismatch or does not support rewind.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string ReadNormalString<TReader>(ref TReader reader, int length)
|
||||||
|
where TReader : IBytesReader
|
||||||
|
{
|
||||||
|
var span = reader.GetSpan(length).Slice(0, length);
|
||||||
|
var str = span.ToString(Encoding.UTF8);
|
||||||
|
reader.Advance(length);
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static void WriteBackNormalString<TWriter>(ref TWriter writer, string value, Encoding encoding, int pos)
|
||||||
|
where TWriter : IBytesWriter
|
||||||
|
{
|
||||||
|
if (writer.SupportsRewind)
|
||||||
|
{
|
||||||
|
var nowPos = (int)(writer.WrittenCount - pos);
|
||||||
|
writer.Advance(-nowPos);
|
||||||
|
WriterExtension.WriteNormalString(ref writer, value, encoding);
|
||||||
|
writer.Advance(nowPos);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Writer version mismatch or does not support rewind.");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -8,6 +8,7 @@
|
|||||||
// QQ群:605534569
|
// QQ群:605534569
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
using System.Buffers;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace ThingsGateway.Foundation;
|
namespace ThingsGateway.Foundation;
|
||||||
@@ -47,6 +48,14 @@ public static class ByteExtensions
|
|||||||
}
|
}
|
||||||
return bytes;
|
return bytes;
|
||||||
}
|
}
|
||||||
|
public static byte[] BoolToByte(this Span<bool> value, byte trueData = 0xff)
|
||||||
|
{
|
||||||
|
return BoolToByte((ReadOnlySpan<bool>)value, trueData); ;
|
||||||
|
}
|
||||||
|
public static byte[] BoolToByte(this bool[] value, byte trueData = 0xff)
|
||||||
|
{
|
||||||
|
return BoolToByte((ReadOnlySpan<bool>)value, trueData); ;
|
||||||
|
}
|
||||||
public static bool[] ByteToBool(this ReadOnlySpan<byte> value)
|
public static bool[] ByteToBool(this ReadOnlySpan<byte> value)
|
||||||
{
|
{
|
||||||
bool[] bytes = new bool[value.Length];
|
bool[] bytes = new bool[value.Length];
|
||||||
@@ -76,6 +85,7 @@ public static class ByteExtensions
|
|||||||
{
|
{
|
||||||
return BytesAdd((ReadOnlySpan<byte>)bytes, value);
|
return BytesAdd((ReadOnlySpan<byte>)bytes, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 数组内容分别相加某个数字
|
/// 数组内容分别相加某个数字
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -94,6 +104,31 @@ public static class ByteExtensions
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 将 ReadOnlySequence 的每个字节加上指定值,返回新的 byte 数组。
|
||||||
|
/// </summary>
|
||||||
|
public static byte[] BytesAdd(this ReadOnlySequence<byte> sequence, int value)
|
||||||
|
{
|
||||||
|
if (sequence.Length == 0)
|
||||||
|
return Array.Empty<byte>();
|
||||||
|
|
||||||
|
byte[] result = new byte[sequence.Length];
|
||||||
|
int offset = 0;
|
||||||
|
|
||||||
|
foreach (var segment in sequence)
|
||||||
|
{
|
||||||
|
var span = segment.Span;
|
||||||
|
for (int i = 0; i < span.Length; i++)
|
||||||
|
{
|
||||||
|
result[offset + i] = (byte)(span[i] + value);
|
||||||
|
}
|
||||||
|
offset += span.Length;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 将byte数组按照双字节进行反转,如果为单数的情况,则自动补齐<br />
|
/// 将byte数组按照双字节进行反转,如果为单数的情况,则自动补齐<br />
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -187,6 +222,34 @@ public static class ByteExtensions
|
|||||||
return boolArray;
|
return boolArray;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 从 <see cref="ReadOnlySequence{T}"/> 中提取位数组,length 代表位数
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sequence">原始字节序列</param>
|
||||||
|
/// <param name="length">想要转换的位数,如果超出字节序列长度 * 8,则自动缩小为最大位数</param>
|
||||||
|
/// <returns>转换后的布尔数组</returns>
|
||||||
|
public static bool[] ByteToBoolArray(this ReadOnlySequence<byte> sequence, int length)
|
||||||
|
{
|
||||||
|
// 计算字节序列能提供的最大位数
|
||||||
|
long maxBitLength = sequence.Length * 8;
|
||||||
|
if (length > maxBitLength)
|
||||||
|
length = (int)maxBitLength;
|
||||||
|
|
||||||
|
bool[] boolArray = new bool[length];
|
||||||
|
|
||||||
|
int bitIndex = 0; // 目标位索引
|
||||||
|
|
||||||
|
foreach (var segment in sequence)
|
||||||
|
{
|
||||||
|
var span = segment.Span;
|
||||||
|
for (int i = 0; i < span.Length && bitIndex < length; i++)
|
||||||
|
{
|
||||||
|
boolArray[bitIndex] = span[i / 8].BoolOnByteIndex(i % 8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return boolArray;
|
||||||
|
}
|
||||||
|
|
||||||
public static ReadOnlyMemory<byte> CombineMemoryBlocks(this List<ReadOnlyMemory<byte>> blocks)
|
public static ReadOnlyMemory<byte> CombineMemoryBlocks(this List<ReadOnlyMemory<byte>> blocks)
|
||||||
{
|
{
|
||||||
@@ -281,4 +344,15 @@ public static class ByteExtensions
|
|||||||
{
|
{
|
||||||
return DataTransUtil.ByteToHexString(buffer, splite, newLineCount);
|
return DataTransUtil.ByteToHexString(buffer, splite, newLineCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 字节数组默认转16进制字符
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static string ToHexString(this ReadOnlySequence<byte> buffer, char splite = default, int newLineCount = 0)
|
||||||
|
{
|
||||||
|
return DataTransUtil.ByteToHexString(buffer, splite, newLineCount);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,8 @@
|
|||||||
// QQ群:605534569
|
// QQ群:605534569
|
||||||
// ------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
using System.Buffers;
|
||||||
|
|
||||||
namespace ThingsGateway.Foundation.Extension.Generic;
|
namespace ThingsGateway.Foundation.Extension.Generic;
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
@@ -245,6 +247,29 @@ public static class GenericExtensions
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<ReadOnlySequence<T>> ChunkBetter<T>(this ReadOnlySequence<T> sequence, int groupSize)
|
||||||
|
{
|
||||||
|
if (groupSize <= 0)
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(groupSize));
|
||||||
|
|
||||||
|
var start = sequence.Start;
|
||||||
|
var remaining = sequence.Length;
|
||||||
|
|
||||||
|
while (remaining > 0)
|
||||||
|
{
|
||||||
|
var len = (int)Math.Min(groupSize, remaining);
|
||||||
|
|
||||||
|
// 计算 chunk 的 end 位置
|
||||||
|
var end = sequence.GetPosition(len, start);
|
||||||
|
|
||||||
|
yield return sequence.Slice(start, end);
|
||||||
|
|
||||||
|
// 移动起始位置
|
||||||
|
start = end;
|
||||||
|
remaining -= len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>拷贝当前的实例数组,是基于引用层的浅拷贝,如果类型为值类型,那就是深度拷贝,如果类型为引用类型,就是浅拷贝</summary>
|
/// <summary>拷贝当前的实例数组,是基于引用层的浅拷贝,如果类型为值类型,那就是深度拷贝,如果类型为引用类型,就是浅拷贝</summary>
|
||||||
public static T[] CopyArray<T>(this T[] value)
|
public static T[] CopyArray<T>(this T[] value)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -11,8 +11,8 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.Extensions.Localization.Abstractions" Version="$(NET9Version)" />
|
<PackageReference Include="Microsoft.Extensions.Localization.Abstractions" Version="$(NET9Version)" />
|
||||||
<PackageReference Include="TouchSocket" Version="4.0.0-Alpha.12" />
|
<PackageReference Include="TouchSocket" Version="4.0.0-beta.1" />
|
||||||
<PackageReference Include="TouchSocket.SerialPorts" Version="4.0.0-Alpha.12" />
|
<PackageReference Include="TouchSocket.SerialPorts" Version="4.0.0-beta.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -8,6 +8,8 @@
|
|||||||
// QQ群:605534569
|
// QQ群:605534569
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
using System.Buffers;
|
||||||
|
|
||||||
namespace ThingsGateway.Foundation;
|
namespace ThingsGateway.Foundation;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -70,4 +72,64 @@ public static class CRC16Utils
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 通过指定多项式码来获取对应的数据的CRC校验码
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sequence">需要校验的数据,不包含CRC字节</param>
|
||||||
|
/// <returns>返回带CRC校验码的字节数组,可用于串口发送</returns>
|
||||||
|
public static byte[] Crc16Only(ReadOnlySequence<byte> sequence)
|
||||||
|
{
|
||||||
|
return Crc16Only(sequence, 0xA001);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 通过指定多项式码来获取对应的数据的CRC校验码
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sequence">需要校验的数据,不包含CRC字节</param>
|
||||||
|
/// <param name="xdapoly">多项式</param>
|
||||||
|
/// <param name="crc16">是否低位在前(true=低字节优先,false=高字节优先)</param>
|
||||||
|
/// <returns>返回带CRC校验码的字节数组,可用于串口发送</returns>
|
||||||
|
public static byte[] Crc16Only(ReadOnlySequence<byte> sequence, int xdapoly, bool crc16 = true)
|
||||||
|
{
|
||||||
|
int num = 0xFFFF;
|
||||||
|
|
||||||
|
foreach (var segment in sequence)
|
||||||
|
{
|
||||||
|
var span = segment.Span;
|
||||||
|
for (int i = 0; i < span.Length; i++)
|
||||||
|
{
|
||||||
|
num = (num >> (crc16 ? 0 : 8)) ^ span[i];
|
||||||
|
|
||||||
|
for (int j = 0; j < 8; j++)
|
||||||
|
{
|
||||||
|
int num2 = num & 1;
|
||||||
|
num >>= 1;
|
||||||
|
if (num2 == 1)
|
||||||
|
{
|
||||||
|
num ^= xdapoly;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (crc16)
|
||||||
|
{
|
||||||
|
return new byte[]
|
||||||
|
{
|
||||||
|
(byte)(num & 0xFFu),
|
||||||
|
(byte)(num >> 8)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return new byte[]
|
||||||
|
{
|
||||||
|
(byte)(num >> 8),
|
||||||
|
(byte)(num & 0xFFu)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
// QQ群:605534569
|
// QQ群:605534569
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
using System.Buffers;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace ThingsGateway.Foundation;
|
namespace ThingsGateway.Foundation;
|
||||||
@@ -59,6 +60,53 @@ public static class DataTransUtil
|
|||||||
return sb.ToString();
|
return sb.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static string ByteToHexString(ReadOnlySequence<byte> InBytes, char segment = default, int newLineCount = 0) => ByteToHexString(InBytes, 0, InBytes.Length, segment, newLineCount);
|
||||||
|
|
||||||
|
public static string ByteToHexString(ReadOnlySequence<byte> sequence, long offset, long length, char segment = default, int newLineCount = 0)
|
||||||
|
{
|
||||||
|
if (length <= 0 || offset < 0 || sequence.Length < offset + length)
|
||||||
|
return string.Empty;
|
||||||
|
|
||||||
|
var estimatedSize = length * (segment != default ? 3 : 2) + (length / Math.Max(newLineCount, int.MaxValue));
|
||||||
|
StringBuilder sb = new StringBuilder((int)estimatedSize);
|
||||||
|
|
||||||
|
int totalConsumed = 0;
|
||||||
|
int totalWritten = 0;
|
||||||
|
|
||||||
|
foreach (var memory in sequence)
|
||||||
|
{
|
||||||
|
var span = memory.Span;
|
||||||
|
|
||||||
|
for (int i = 0; i < span.Length && totalWritten < length; i++)
|
||||||
|
{
|
||||||
|
if (totalConsumed >= offset)
|
||||||
|
{
|
||||||
|
sb.Append(span[i].ToString("X2"));
|
||||||
|
|
||||||
|
if (totalWritten < length - 1)
|
||||||
|
{
|
||||||
|
if (segment != default)
|
||||||
|
sb.Append(segment);
|
||||||
|
|
||||||
|
if (newLineCount > 0 && (totalWritten + 1) % newLineCount == 0)
|
||||||
|
sb.AppendLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
totalWritten++;
|
||||||
|
}
|
||||||
|
|
||||||
|
totalConsumed++;
|
||||||
|
if (totalConsumed >= offset + length)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (totalWritten >= length)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取Bcd值
|
/// 获取Bcd值
|
||||||
|
|||||||
@@ -736,10 +736,10 @@ public abstract partial class CollectBase : DriverBase
|
|||||||
|
|
||||||
protected override async Task DisposeAsync(bool disposing)
|
protected override async Task DisposeAsync(bool disposing)
|
||||||
{
|
{
|
||||||
|
await base.DisposeAsync(disposing).ConfigureAwait(false);
|
||||||
_linkedCtsCache?.SafeDispose();
|
_linkedCtsCache?.SafeDispose();
|
||||||
if (ReadWriteLock != null)
|
if (ReadWriteLock != null)
|
||||||
await ReadWriteLock.SafeDisposeAsync().ConfigureAwait(false);
|
await ReadWriteLock.SafeDisposeAsync().ConfigureAwait(false);
|
||||||
await base.DisposeAsync(disposing).ConfigureAwait(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -177,7 +177,7 @@ public partial class VariableRuntime : Variable
|
|||||||
/// 是否在线
|
/// 是否在线
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[AutoGenerateColumn(Visible = true, Filterable = true, Sortable = true, Order = 5)]
|
[AutoGenerateColumn(Visible = true, Filterable = true, Sortable = true, Order = 5)]
|
||||||
public bool IsOnline{get;set;}
|
public bool IsOnline { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 设备名称
|
/// 设备名称
|
||||||
@@ -234,7 +234,9 @@ public partial class VariableRuntime : Variable
|
|||||||
private DateTime collectTime = DateTime.UnixEpoch.ToLocalTime();
|
private DateTime collectTime = DateTime.UnixEpoch.ToLocalTime();
|
||||||
|
|
||||||
#pragma warning disable CS0414
|
#pragma warning disable CS0414
|
||||||
|
#pragma warning disable CS0169
|
||||||
private bool _isOnlineChanged;
|
private bool _isOnlineChanged;
|
||||||
|
#pragma warning restore CS0169
|
||||||
#pragma warning restore CS0414
|
#pragma warning restore CS0414
|
||||||
private bool _valueInited;
|
private bool _valueInited;
|
||||||
|
|
||||||
|
|||||||
@@ -392,16 +392,17 @@ internal sealed class ChannelService : BaseService<Channel>, IChannelService
|
|||||||
ManageHelper.CheckChannelCount(insertData.Count);
|
ManageHelper.CheckChannelCount(insertData.Count);
|
||||||
|
|
||||||
using var db = GetDB();
|
using var db = GetDB();
|
||||||
if (GlobalData.HardwareJob.HardwareInfo.MachineInfo.AvailableMemory > 2 * 1024 * 1024)
|
if (GlobalData.HardwareJob.HardwareInfo.MachineInfo.AvailableMemory < 2 * 1024 * 1024 || WebEnableVariable.WebEnable == false)
|
||||||
{
|
|
||||||
await db.BulkCopyAsync(insertData, 200000).ConfigureAwait(false);
|
|
||||||
await db.BulkUpdateAsync(upData, 200000).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
await db.BulkCopyAsync(insertData, 10000).ConfigureAwait(false);
|
await db.BulkCopyAsync(insertData, 10000).ConfigureAwait(false);
|
||||||
await db.BulkUpdateAsync(upData, 10000).ConfigureAwait(false);
|
await db.BulkUpdateAsync(upData, 10000).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await db.BulkCopyAsync(insertData, 200000).ConfigureAwait(false);
|
||||||
|
await db.BulkUpdateAsync(upData, 200000).ConfigureAwait(false);
|
||||||
|
|
||||||
|
}
|
||||||
DeleteChannelFromCache();
|
DeleteChannelFromCache();
|
||||||
return upData.Select(a => a.Id).Concat(insertData.Select(a => a.Id)).ToHashSet();
|
return upData.Select(a => a.Id).Concat(insertData.Select(a => a.Id)).ToHashSet();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -388,16 +388,17 @@ internal sealed class DeviceService : BaseService<Device>, IDeviceService
|
|||||||
ManageHelper.CheckDeviceCount(insertData.Count);
|
ManageHelper.CheckDeviceCount(insertData.Count);
|
||||||
|
|
||||||
using var db = GetDB();
|
using var db = GetDB();
|
||||||
if (GlobalData.HardwareJob.HardwareInfo.MachineInfo.AvailableMemory > 2 * 1024 * 1024)
|
if (GlobalData.HardwareJob.HardwareInfo.MachineInfo.AvailableMemory < 2 * 1024 * 1024 || WebEnableVariable.WebEnable == false)
|
||||||
{
|
|
||||||
await db.BulkCopyAsync(insertData, 200000).ConfigureAwait(false);
|
|
||||||
await db.BulkUpdateAsync(upData, 200000).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
await db.BulkCopyAsync(insertData, 10000).ConfigureAwait(false);
|
await db.BulkCopyAsync(insertData, 10000).ConfigureAwait(false);
|
||||||
await db.BulkUpdateAsync(upData, 10000).ConfigureAwait(false);
|
await db.BulkUpdateAsync(upData, 10000).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
|
||||||
|
await db.BulkCopyAsync(insertData, 200000).ConfigureAwait(false);
|
||||||
|
await db.BulkUpdateAsync(upData, 200000).ConfigureAwait(false);
|
||||||
|
}
|
||||||
DeleteDeviceFromCache();
|
DeleteDeviceFromCache();
|
||||||
return upData.Select(a => a.Id).Concat(insertData.Select(a => a.Id)).ToHashSet();
|
return upData.Select(a => a.Id).Concat(insertData.Select(a => a.Id)).ToHashSet();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -134,7 +134,7 @@ public partial class ManagementTask : AsyncDisposableObject
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await tcpDmtpClient.ResetIdAsync($"{_managementOptions.Name}:{GlobalData.HardwareJob.HardwareInfo.UUID}").ConfigureAwait(false);
|
await tcpDmtpClient.ResetIdAsync($"{_managementOptions.Name}:{GlobalData.HardwareJob.HardwareInfo.UUID}", tcpDmtpClient.ClosedToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
catch (Exception)
|
catch (Exception)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ internal sealed class RedundancyHostedService : BackgroundService, IRedundancyHo
|
|||||||
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||||
{
|
{
|
||||||
await Task.Yield();
|
await Task.Yield();
|
||||||
await RedundancyTask.StartRedundancyTaskAsync(stoppingToken).ConfigureAwait(false);
|
await RedundancyTask.StartRedundancyTaskAsync().ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task StartRedundancyTaskAsync() => RedundancyTask.StartRedundancyTaskAsync();
|
public Task StartRedundancyTaskAsync() => RedundancyTask.StartRedundancyTaskAsync();
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ using TouchSocket.Core;
|
|||||||
using TouchSocket.Dmtp;
|
using TouchSocket.Dmtp;
|
||||||
using TouchSocket.Dmtp.Rpc;
|
using TouchSocket.Dmtp.Rpc;
|
||||||
using TouchSocket.Rpc;
|
using TouchSocket.Rpc;
|
||||||
using TouchSocket.Rpc.Generators;
|
using TouchSocket.Rpc.DmtpRpc.Generators;
|
||||||
using TouchSocket.Sockets;
|
using TouchSocket.Sockets;
|
||||||
|
|
||||||
namespace ThingsGateway.Gateway.Application;
|
namespace ThingsGateway.Gateway.Application;
|
||||||
@@ -63,7 +63,7 @@ internal sealed class RedundancyTask : IRpcDriver, IAsyncDisposable
|
|||||||
const int highMemorySize = 100000;
|
const int highMemorySize = 100000;
|
||||||
const long memoryThreshold = 2L * 1024 * 1024; // 2GB,单位KB
|
const long memoryThreshold = 2L * 1024 * 1024; // 2GB,单位KB
|
||||||
|
|
||||||
return GlobalData.HardwareJob.HardwareInfo.MachineInfo.AvailableMemory > memoryThreshold
|
return (GlobalData.HardwareJob.HardwareInfo.MachineInfo.AvailableMemory > memoryThreshold && WebEnableVariable.WebEnable == true)
|
||||||
? highMemorySize
|
? highMemorySize
|
||||||
: defaultSize;
|
: defaultSize;
|
||||||
}
|
}
|
||||||
@@ -140,8 +140,8 @@ internal sealed class RedundancyTask : IRpcDriver, IAsyncDisposable
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// 发送 Ping 请求以检查设备是否在线,超时时间为 10000 毫秒
|
using var cts = new CancellationTokenSource(10000);
|
||||||
online = await _tcpDmtpClient.PingAsync(10000).ConfigureAwait(false);
|
online = (await _tcpDmtpClient.PingAsync(cts.Token).ConfigureAwait(false)).IsSuccess;
|
||||||
if (online)
|
if (online)
|
||||||
break;
|
break;
|
||||||
else
|
else
|
||||||
@@ -238,7 +238,7 @@ internal sealed class RedundancyTask : IRpcDriver, IAsyncDisposable
|
|||||||
return GlobalData.ChannelRuntimeService.RestartChannelAsync(GlobalData.ReadOnlyIdChannels.Values);
|
return GlobalData.ChannelRuntimeService.RestartChannelAsync(GlobalData.ReadOnlyIdChannels.Values);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task StartRedundancyTaskAsync(CancellationToken cancellationToken = default)
|
public async Task StartRedundancyTaskAsync()
|
||||||
{
|
{
|
||||||
await StopRedundancyTaskAsync().ConfigureAwait(false);
|
await StopRedundancyTaskAsync().ConfigureAwait(false);
|
||||||
RedundancyOptions = (await _redundancyService.GetRedundancyAsync().ConfigureAwait(false)).AdaptRedundancyOptions();
|
RedundancyOptions = (await _redundancyService.GetRedundancyAsync().ConfigureAwait(false)).AdaptRedundancyOptions();
|
||||||
@@ -268,11 +268,11 @@ internal sealed class RedundancyTask : IRpcDriver, IAsyncDisposable
|
|||||||
LogMessage?.LogInformation($"Redundancy task started");
|
LogMessage?.LogInformation($"Redundancy task started");
|
||||||
if (RedundancyOptions.IsMaster)
|
if (RedundancyOptions.IsMaster)
|
||||||
{
|
{
|
||||||
scheduledTask = new ScheduledAsyncTask(RedundancyOptions.SyncInterval, DoMasterWork, null, null, cancellationToken);
|
scheduledTask = new ScheduledAsyncTask(RedundancyOptions.SyncInterval, DoMasterWork, null, null, CancellationToken.None);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
scheduledTask = new ScheduledAsyncTask(5000, DoSlaveWork, null, null, cancellationToken);
|
scheduledTask = new ScheduledAsyncTask(5000, DoSlaveWork, null, null, CancellationToken.None);
|
||||||
}
|
}
|
||||||
|
|
||||||
scheduledTask.Start();
|
scheduledTask.Start();
|
||||||
@@ -416,7 +416,6 @@ internal sealed class RedundancyTask : IRpcDriver, IAsyncDisposable
|
|||||||
{
|
{
|
||||||
FeedbackType = FeedbackType.WaitInvoke,
|
FeedbackType = FeedbackType.WaitInvoke,
|
||||||
Token = cancellationToken,
|
Token = cancellationToken,
|
||||||
Timeout = 1800000,
|
|
||||||
SerializationType = SerializationType.Json,
|
SerializationType = SerializationType.Json,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -591,11 +590,11 @@ internal sealed class RedundancyTask : IRpcDriver, IAsyncDisposable
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<Dictionary<string, Dictionary<string, OperResult<object>>>> InvokeRpcClientAsync(
|
private Task<Dictionary<string, Dictionary<string, OperResult<object>>>> InvokeRpcClientAsync(
|
||||||
Dictionary<string, Dictionary<string, string>> deviceDatas,
|
Dictionary<string, Dictionary<string, string>> deviceDatas,
|
||||||
DmtpInvokeOption invokeOption)
|
DmtpInvokeOption invokeOption)
|
||||||
{
|
{
|
||||||
return await _tcpDmtpClient.GetDmtpRpcActor().RpcAsync(deviceDatas, invokeOption).ConfigureAwait(false);
|
return _tcpDmtpClient.GetDmtpRpcActor().RpcAsync(deviceDatas, invokeOption);
|
||||||
}
|
}
|
||||||
private async Task InvokeRpcServerAsync(
|
private async Task InvokeRpcServerAsync(
|
||||||
Dictionary<string, Dictionary<string, string>> deviceDatas,
|
Dictionary<string, Dictionary<string, string>> deviceDatas,
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ public class ExecuteScriptNode : TextNode, IActuatorNode, IExexcuteExpressionsBa
|
|||||||
TopicArray topicArray = new()
|
TopicArray topicArray = new()
|
||||||
{
|
{
|
||||||
Topic = "test",
|
Topic = "test",
|
||||||
Json = Encoding.UTF8.GetBytes("test")
|
Payload = Encoding.UTF8.GetBytes("test")
|
||||||
};
|
};
|
||||||
var result = await mqttClient.MqttUpAsync(topicArray, default).ConfigureAwait(false);// 主题 和 负载
|
var result = await mqttClient.MqttUpAsync(topicArray, default).ConfigureAwait(false);// 主题 和 负载
|
||||||
if (!result.IsSuccess)
|
if (!result.IsSuccess)
|
||||||
@@ -56,7 +56,7 @@ public class ExecuteScriptNode : TextNode, IActuatorNode, IExexcuteExpressionsBa
|
|||||||
//TopicArray topicArray = new()
|
//TopicArray topicArray = new()
|
||||||
//{
|
//{
|
||||||
// Topic = "test",
|
// Topic = "test",
|
||||||
// Json = Encoding.UTF8.GetBytes("test")
|
// Payload = Encoding.UTF8.GetBytes("test")
|
||||||
//};
|
//};
|
||||||
//var result = await mqttClient.MqttUpAsync(topicArray, default).ConfigureAwait(false);// 主题 和 负载
|
//var result = await mqttClient.MqttUpAsync(topicArray, default).ConfigureAwait(false);// 主题 和 负载
|
||||||
//if (!result.IsSuccess)
|
//if (!result.IsSuccess)
|
||||||
|
|||||||
@@ -209,18 +209,19 @@ internal sealed class VariableService : BaseService<Variable>, IVariableService
|
|||||||
|
|
||||||
var result = await db.UseTranAsync(async () =>
|
var result = await db.UseTranAsync(async () =>
|
||||||
{
|
{
|
||||||
if (GlobalData.HardwareJob.HardwareInfo.MachineInfo.AvailableMemory > 2 * 1024 * 1024)
|
if (GlobalData.HardwareJob.HardwareInfo.MachineInfo.AvailableMemory < 2 * 1024 * 1024 || WebEnableVariable.WebEnable == false)
|
||||||
{
|
|
||||||
await db.BulkCopyAsync(newChannels, 200000).ConfigureAwait(false);
|
|
||||||
await db.BulkCopyAsync(newDevices, 200000).ConfigureAwait(false);
|
|
||||||
await db.BulkCopyAsync(newVariables, 200000).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
await db.BulkCopyAsync(newChannels, 10000).ConfigureAwait(false);
|
await db.BulkCopyAsync(newChannels, 10000).ConfigureAwait(false);
|
||||||
await db.BulkCopyAsync(newDevices, 10000).ConfigureAwait(false);
|
await db.BulkCopyAsync(newDevices, 10000).ConfigureAwait(false);
|
||||||
await db.BulkCopyAsync(newVariables, 10000).ConfigureAwait(false);
|
await db.BulkCopyAsync(newVariables, 10000).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await db.BulkCopyAsync(newChannels, 200000).ConfigureAwait(false);
|
||||||
|
await db.BulkCopyAsync(newDevices, 200000).ConfigureAwait(false);
|
||||||
|
await db.BulkCopyAsync(newVariables, 200000).ConfigureAwait(false);
|
||||||
|
|
||||||
|
}
|
||||||
}).ConfigureAwait(false);
|
}).ConfigureAwait(false);
|
||||||
if (result.IsSuccess)//如果成功了
|
if (result.IsSuccess)//如果成功了
|
||||||
{
|
{
|
||||||
@@ -492,7 +493,7 @@ internal sealed class VariableService : BaseService<Variable>, IVariableService
|
|||||||
[OperDesc("ExportVariable", isRecordPar: false, localizerType: typeof(Variable))]
|
[OperDesc("ExportVariable", isRecordPar: false, localizerType: typeof(Variable))]
|
||||||
public async Task<Dictionary<string, object>> ExportVariableAsync(GatewayExportFilter exportFilter)
|
public async Task<Dictionary<string, object>> ExportVariableAsync(GatewayExportFilter exportFilter)
|
||||||
{
|
{
|
||||||
if (GlobalData.HardwareJob.HardwareInfo.MachineInfo.AvailableMemory < 4 * 1024 * 1024)
|
if (GlobalData.HardwareJob.HardwareInfo.MachineInfo.AvailableMemory < 4 * 1024 * 1024 || WebEnableVariable.WebEnable == false)
|
||||||
{
|
{
|
||||||
var whereQuery = await GetWhereEnumerableFunc(exportFilter).ConfigureAwait(false);
|
var whereQuery = await GetWhereEnumerableFunc(exportFilter).ConfigureAwait(false);
|
||||||
//导出
|
//导出
|
||||||
@@ -544,7 +545,7 @@ internal sealed class VariableService : BaseService<Variable>, IVariableService
|
|||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
[OperDesc("ImportVariable", isRecordPar: false, localizerType: typeof(Variable))]
|
[OperDesc("ImportVariable", isRecordPar: false, localizerType: typeof(Variable))]
|
||||||
public async Task<HashSet<long>> ImportVariableAsync(Dictionary<string, ImportPreviewOutputBase> input)
|
public Task<HashSet<long>> ImportVariableAsync(Dictionary<string, ImportPreviewOutputBase> input)
|
||||||
{
|
{
|
||||||
IEnumerable<Variable>? variables = new List<Variable>();
|
IEnumerable<Variable>? variables = new List<Variable>();
|
||||||
foreach (var item in input)
|
foreach (var item in input)
|
||||||
@@ -558,7 +559,7 @@ internal sealed class VariableService : BaseService<Variable>, IVariableService
|
|||||||
}
|
}
|
||||||
var upData = variables.Where(a => a.IsUp).ToList();
|
var upData = variables.Where(a => a.IsUp).ToList();
|
||||||
var insertData = variables.Where(a => !a.IsUp).ToList();
|
var insertData = variables.Where(a => !a.IsUp).ToList();
|
||||||
return await ImportVariableAsync(upData, insertData).ConfigureAwait(false);
|
return ImportVariableAsync(upData, insertData);
|
||||||
}
|
}
|
||||||
|
|
||||||
[OperDesc("ImportVariable", isRecordPar: false, localizerType: typeof(Variable))]
|
[OperDesc("ImportVariable", isRecordPar: false, localizerType: typeof(Variable))]
|
||||||
@@ -566,15 +567,16 @@ internal sealed class VariableService : BaseService<Variable>, IVariableService
|
|||||||
{
|
{
|
||||||
ManageHelper.CheckVariableCount(insertData.Count);
|
ManageHelper.CheckVariableCount(insertData.Count);
|
||||||
using var db = GetDB();
|
using var db = GetDB();
|
||||||
if (GlobalData.HardwareJob.HardwareInfo.MachineInfo.AvailableMemory > 2 * 1024 * 1024)
|
if (GlobalData.HardwareJob.HardwareInfo.MachineInfo.AvailableMemory < 2 * 1024 * 1024 || WebEnableVariable.WebEnable == false)
|
||||||
{
|
|
||||||
await db.BulkCopyAsync(insertData, 200000).ConfigureAwait(false);
|
|
||||||
await db.BulkUpdateAsync(upData, 200000).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
await db.BulkCopyAsync(insertData, 10000).ConfigureAwait(false);
|
await db.BulkCopyAsync(insertData, 10000).ConfigureAwait(false);
|
||||||
await db.BulkUpdateAsync(upData, 10000).ConfigureAwait(false);
|
await db.BulkUpdateAsync(upData, 10000).ConfigureAwait(false);
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await db.BulkCopyAsync(insertData, 200000).ConfigureAwait(false);
|
||||||
|
await db.BulkUpdateAsync(upData, 200000).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
DeleteVariableCache();
|
DeleteVariableCache();
|
||||||
return upData.Select(a => a.Id).Concat(insertData.Select(a => a.Id)).ToHashSet();
|
return upData.Select(a => a.Id).Concat(insertData.Select(a => a.Id)).ToHashSet();
|
||||||
|
|||||||
@@ -11,8 +11,8 @@
|
|||||||
<PackageReference Include="Riok.Mapperly" Version="4.2.1" ExcludeAssets="runtime" PrivateAssets="all" />
|
<PackageReference Include="Riok.Mapperly" Version="4.2.1" ExcludeAssets="runtime" PrivateAssets="all" />
|
||||||
<PackageReference Include="Rougamo.Fody" Version="5.0.1" />
|
<PackageReference Include="Rougamo.Fody" Version="5.0.1" />
|
||||||
<PackageReference Include="System.Linq.Async" Version="6.0.3" />
|
<PackageReference Include="System.Linq.Async" Version="6.0.3" />
|
||||||
<PackageReference Include="TouchSocket.Dmtp" Version="4.0.0-Alpha.12" />
|
<PackageReference Include="TouchSocket.Dmtp" Version="4.0.0-beta.1" />
|
||||||
<PackageReference Include="TouchSocket.WebApi.Swagger" Version="4.0.0-Alpha.12" />
|
<PackageReference Include="TouchSocket.WebApi.Swagger" Version="4.0.0-beta.1" />
|
||||||
<PackageReference Include="ThingsGateway.Authentication" Version="$(AuthenticationVersion)" />
|
<PackageReference Include="ThingsGateway.Authentication" Version="$(AuthenticationVersion)" />
|
||||||
<!--<ProjectReference Include="..\..\PluginPro\ThingsGateway.Authentication\ThingsGateway.Authentication.csproj" />-->
|
<!--<ProjectReference Include="..\..\PluginPro\ThingsGateway.Authentication\ThingsGateway.Authentication.csproj" />-->
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,7 @@
|
|||||||
Content=@(ReloadServiceConfirmText)
|
Content=@(ReloadServiceConfirmText)
|
||||||
Icon="fas fa-rotate"
|
Icon="fas fa-rotate"
|
||||||
IsAsync="true"
|
IsAsync="true"
|
||||||
OnConfirm="Restart" IsDisabled=@(!AuthorizeButton("重启")) />
|
OnConfirm="Restart" IsKeepDisabled=@(!AuthorizeButton("重启")) />
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -34,22 +34,6 @@
|
|||||||
"SaveUpdateZipFile": "Upload Version Package"
|
"SaveUpdateZipFile": "Upload Version Package"
|
||||||
},
|
},
|
||||||
|
|
||||||
"ThingsGateway.Gateway.Razor.ValueTransformType": {
|
|
||||||
"None": "None",
|
|
||||||
"Linear": "Linear",
|
|
||||||
"Sqrt": "Sqrt"
|
|
||||||
},
|
|
||||||
|
|
||||||
"ThingsGateway.Gateway.Razor.ValueTransformConfig": {
|
|
||||||
"TransformType": "TransformType",
|
|
||||||
"MinMax": "MinMax",
|
|
||||||
"ClampToRawRange": "ClampToRawRange",
|
|
||||||
"DecimalPlaces": "DecimalPlaces",
|
|
||||||
"RawMin": "RawMin",
|
|
||||||
"RawMax": "RawMax",
|
|
||||||
"ActualMin": "ActualMin",
|
|
||||||
"ActualMax": "ActualMax"
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
"ThingsGateway.Gateway.Razor.Authentication": {
|
"ThingsGateway.Gateway.Razor.Authentication": {
|
||||||
|
|||||||
@@ -34,21 +34,7 @@
|
|||||||
"SaveUpdateZipFile": "上传版本包"
|
"SaveUpdateZipFile": "上传版本包"
|
||||||
},
|
},
|
||||||
|
|
||||||
"ThingsGateway.Gateway.Razor.ValueTransformType": {
|
|
||||||
"None": "无",
|
|
||||||
"Linear": "线性",
|
|
||||||
"Sqrt": "开方"
|
|
||||||
},
|
|
||||||
"ThingsGateway.Gateway.Razor.ValueTransformConfig": {
|
|
||||||
"TransformType": "转换方式",
|
|
||||||
"MinMax": "最小最大值",
|
|
||||||
"ClampToRawRange": "限制范围",
|
|
||||||
"DecimalPlaces": "保留小数位",
|
|
||||||
"RawMin": "原始最小值",
|
|
||||||
"RawMax": "原始最大值",
|
|
||||||
"ActualMin": "实际最小值",
|
|
||||||
"ActualMax": "实际最大值"
|
|
||||||
},
|
|
||||||
|
|
||||||
"ThingsGateway.Gateway.Razor.Authentication": {
|
"ThingsGateway.Gateway.Razor.Authentication": {
|
||||||
"AuthName": "公司名称",
|
"AuthName": "公司名称",
|
||||||
|
|||||||
@@ -58,7 +58,6 @@
|
|||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@if (DeviceRuntime.IsCollect == false && AuthorizeButton("删除缓存"))
|
@if (DeviceRuntime.IsCollect == false && AuthorizeButton("删除缓存"))
|
||||||
{
|
{
|
||||||
@@ -125,15 +124,7 @@
|
|||||||
<EditorItem Field=@context.SourceVariableCount FieldExpression=@(() => context.SourceVariableCount) />
|
<EditorItem Field=@context.SourceVariableCount FieldExpression=@(() => context.SourceVariableCount) />
|
||||||
<EditorItem Field=@context.MethodVariableCount FieldExpression=@(() => context.MethodVariableCount) />
|
<EditorItem Field=@context.MethodVariableCount FieldExpression=@(() => context.MethodVariableCount) />
|
||||||
}
|
}
|
||||||
@*<EditorItem ComponentParameters=@(new Dictionary<string,object>(){
|
|
||||||
["class"]="red--text",
|
|
||||||
}
|
|
||||||
|
|
||||||
) Field=@context.LastErrorMessage FieldExpression=@(()=> context.LastErrorMessage ) Rows="1" ShowLabelTooltip="false">
|
|
||||||
<EditTemplate Context="value">
|
|
||||||
<Display Value=@value.LastErrorMessage ValueExpression=@(()=> context.LastErrorMessage ) ShowTooltip class="red--text" ShowLabel="true" />
|
|
||||||
</EditTemplate>
|
|
||||||
</EditorItem> *@
|
|
||||||
<EditorItem Field=@context.LastErrorMessage FieldExpression=@(() => context.LastErrorMessage) ShowLabelTooltip="true">
|
<EditorItem Field=@context.LastErrorMessage FieldExpression=@(() => context.LastErrorMessage) ShowLabelTooltip="true">
|
||||||
<EditTemplate Context="value">
|
<EditTemplate Context="value">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ using Microsoft.AspNetCore.Components.Web;
|
|||||||
|
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
|
|
||||||
|
using ThingsGateway.Debug;
|
||||||
using ThingsGateway.NewLife.Extension;
|
using ThingsGateway.NewLife.Extension;
|
||||||
using ThingsGateway.NewLife.Json.Extension;
|
using ThingsGateway.NewLife.Json.Extension;
|
||||||
|
|
||||||
|
|||||||
@@ -71,7 +71,7 @@
|
|||||||
</TableColumns>
|
</TableColumns>
|
||||||
<RowButtonTemplate>
|
<RowButtonTemplate>
|
||||||
|
|
||||||
<PopConfirmButton IsDisabled=@(!AuthorizeButton("写入变量")) Size="Size.ExtraSmall" Color="Color.Warning" Icon="fa-solid fa-bars" Text="@Localizer["WriteVariable"]" IsAsync OnConfirm="() => OnWriteVariable(context)">
|
<PopConfirmButton IsKeepDisabled=@(!AuthorizeButton("写入变量")) Size="Size.ExtraSmall" Color="Color.Warning" Icon="fa-solid fa-bars" Text="@Localizer["WriteVariable"]" IsAsync OnConfirm="() => OnWriteVariable(context)">
|
||||||
|
|
||||||
<BodyTemplate>
|
<BodyTemplate>
|
||||||
<Textarea @bind-Value=WriteValue ShowLabel="true" ShowLabelTooltip="true" />
|
<Textarea @bind-Value=WriteValue ShowLabel="true" ShowLabelTooltip="true" />
|
||||||
@@ -125,7 +125,7 @@
|
|||||||
|
|
||||||
@if (WebsiteOption.Value.Demo || App.HostEnvironment.IsDevelopment())
|
@if (WebsiteOption.Value.Demo || App.HostEnvironment.IsDevelopment())
|
||||||
{
|
{
|
||||||
<PopConfirmButton Color=Color.Warning Text="@Localizer["Test"]" IsDisabled=@(!AuthorizeButton(AdminOperConst.Add))
|
<PopConfirmButton Color=Color.Warning Text="@Localizer["Test"]" IsKeepDisabled=@(!AuthorizeButton(AdminOperConst.Add))
|
||||||
IsAsync OnConfirm=@(InsertTestDataAsync)>
|
IsAsync OnConfirm=@(InsertTestDataAsync)>
|
||||||
|
|
||||||
<BodyTemplate>
|
<BodyTemplate>
|
||||||
|
|||||||
@@ -11,6 +11,10 @@
|
|||||||
|
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
using TouchSocket.Core;
|
||||||
|
|
||||||
using Size = BootstrapBlazor.Components.Size;
|
using Size = BootstrapBlazor.Components.Size;
|
||||||
|
|
||||||
namespace ThingsGateway.Gateway.Razor
|
namespace ThingsGateway.Gateway.Razor
|
||||||
@@ -39,18 +43,30 @@ namespace ThingsGateway.Gateway.Razor
|
|||||||
{
|
{
|
||||||
{nameof(ScriptEdit.OnCheckScript), new Func<string,Task<string>>(async a=>{
|
{nameof(ScriptEdit.OnCheckScript), new Func<string,Task<string>>(async a=>{
|
||||||
if(Node is IConditionNode conditionNode)
|
if(Node is IConditionNode conditionNode)
|
||||||
return (await conditionNode.ExecuteAsync(new NodeInput(){Value=a==null?a:JToken.Parse(a??string.Empty) },default).ConfigureAwait(false)).ToString();
|
{
|
||||||
|
StringBuilder stringBuilder=new();
|
||||||
|
conditionNode.Logger=new EasyLogger((a)=>stringBuilder.AppendLine(a));
|
||||||
|
var out1= (await conditionNode.ExecuteAsync(new NodeInput(){Value=a==null?a:JToken.Parse(a??string.Empty) },default).ConfigureAwait(false)).ToString();
|
||||||
|
stringBuilder.AppendLine(out1);
|
||||||
|
return stringBuilder.ToString();
|
||||||
|
}
|
||||||
if(Node is IExpressionNode expressionNode)
|
if(Node is IExpressionNode expressionNode)
|
||||||
{
|
{
|
||||||
|
StringBuilder stringBuilder=new();
|
||||||
|
expressionNode.Logger=new EasyLogger((a)=>stringBuilder.AppendLine(a));
|
||||||
var data=await expressionNode.ExecuteAsync(new NodeInput(){Value=a==null?a:JToken.Parse(a??string.Empty) },default).ConfigureAwait(false);
|
var data=await expressionNode.ExecuteAsync(new NodeInput(){Value=a==null?a:JToken.Parse(a??string.Empty) },default).ConfigureAwait(false);
|
||||||
|
|
||||||
return data.IsSuccess? data.Content.JToken?.ToString()??string.Empty: data.ToString();
|
stringBuilder.AppendLine( data.IsSuccess? data.Content.JToken?.ToString()??string.Empty: data.ToString());
|
||||||
|
return stringBuilder.ToString();
|
||||||
}
|
}
|
||||||
if(Node is IActuatorNode actuatorNode)
|
if(Node is IActuatorNode actuatorNode)
|
||||||
{
|
{
|
||||||
var data=await actuatorNode.ExecuteAsync(new NodeInput(){Value=a==null?a:JToken.Parse(a??string.Empty) },default).ConfigureAwait(false);
|
StringBuilder stringBuilder=new();
|
||||||
|
actuatorNode.Logger=new EasyLogger((a)=>stringBuilder.AppendLine(a));
|
||||||
|
var data=await actuatorNode.ExecuteAsync(new NodeInput(){Value=a==null?a:JToken.Parse(a??string.Empty) },default).ConfigureAwait(false);
|
||||||
|
|
||||||
return data.IsSuccess? data.Content.JToken?.ToString()??string.Empty: data.ToString();
|
stringBuilder.AppendLine( data.IsSuccess? data.Content.JToken?.ToString()??string.Empty: data.ToString());
|
||||||
|
return stringBuilder.ToString();
|
||||||
}
|
}
|
||||||
return string.Empty;
|
return string.Empty;
|
||||||
}) },
|
}) },
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
<ProjectReference Include="..\ThingsGateway.Gateway.Application\ThingsGateway.Gateway.Application.csproj" />
|
<ProjectReference Include="..\ThingsGateway.Gateway.Application\ThingsGateway.Gateway.Application.csproj" />
|
||||||
<PackageReference Include="Riok.Mapperly" Version="4.2.1" ExcludeAssets="runtime" PrivateAssets="all" />
|
<PackageReference Include="Riok.Mapperly" Version="4.2.1" ExcludeAssets="runtime" PrivateAssets="all" />
|
||||||
<PackageReference Include="BootstrapBlazor.WinBox" Version="9.0.7" />
|
<PackageReference Include="BootstrapBlazor.WinBox" Version="9.0.7" />
|
||||||
<PackageReference Include="BootstrapBlazor.CodeEditor" Version="9.0.1" />
|
<PackageReference Include="BootstrapBlazor.CodeEditor" Version="9.0.3" />
|
||||||
<ProjectReference Include="..\..\Admin\ThingsGateway.Admin.Razor\ThingsGateway.Admin.Razor.csproj" />
|
<ProjectReference Include="..\..\Admin\ThingsGateway.Admin.Razor\ThingsGateway.Admin.Razor.csproj" />
|
||||||
<ProjectReference Include="..\..\Foundation\ThingsGateway.Foundation.Razor\ThingsGateway.Foundation.Razor.csproj" />
|
<ProjectReference Include="..\..\Foundation\ThingsGateway.Foundation.Razor\ThingsGateway.Foundation.Razor.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|||||||
@@ -8,6 +8,8 @@
|
|||||||
// QQ群:605534569
|
// QQ群:605534569
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
using System.Buffers;
|
||||||
|
|
||||||
using TouchSocket.Core;
|
using TouchSocket.Core;
|
||||||
|
|
||||||
namespace ThingsGateway.Foundation.Dlt645;
|
namespace ThingsGateway.Foundation.Dlt645;
|
||||||
@@ -28,41 +30,62 @@ public class Dlt645_2007Response : Dlt645_2007Request
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class Dlt645_2007Message : MessageBase, IResultMessage
|
public class Dlt645_2007Message : MessageBase, IResultMessage
|
||||||
{
|
{
|
||||||
private readonly byte[] ReadStation = [0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA];
|
private static readonly byte[] ReadStation = [0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA];
|
||||||
|
|
||||||
private int HeadCodeIndex;
|
private long HeadCodeIndex;
|
||||||
|
|
||||||
public Dlt645_2007Send? Dlt645_2007Send { get; set; }
|
public Dlt645_2007Send? Dlt645_2007Send { get; set; }
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public override int HeaderLength { get; set; } = 10;
|
public override long HeaderLength { get; set; } = 10;
|
||||||
|
|
||||||
public Dlt645_2007Address? Request { get; set; }
|
public Dlt645_2007Address? Request { get; set; }
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override bool CheckHead<TByteBlock>(ref TByteBlock byteBlock)
|
||||||
|
{
|
||||||
|
//因为设备可能带有FE前导符开头,这里找到0x68的位置
|
||||||
|
if (byteBlock != null)
|
||||||
|
{
|
||||||
|
var index = byteBlock.TotalSequence.IndexOf(0x68);
|
||||||
|
if (index > -1)
|
||||||
|
{
|
||||||
|
HeadCodeIndex = index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//帧起始符 地址域 帧起始符 控制码 数据域长度共10个字节
|
||||||
|
HeaderLength = HeadCodeIndex - byteBlock.BytesRead + 10;
|
||||||
|
if (byteBlock.BytesRead + byteBlock.BytesRemaining > HeadCodeIndex + 9)
|
||||||
|
{
|
||||||
|
BodyLength = byteBlock.TotalSequence.GetByte(HeadCodeIndex + 9) + 2;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public override FilterResult CheckBody<TByteBlock>(ref TByteBlock byteBlock)
|
public override FilterResult CheckBody<TByteBlock>(ref TByteBlock byteBlock)
|
||||||
{
|
{
|
||||||
var span = byteBlock.Span;
|
var sequence = byteBlock.TotalSequence;
|
||||||
var pos = byteBlock.Position - HeaderLength;
|
var pos = byteBlock.BytesRead - HeaderLength;
|
||||||
var endIndex = HeaderLength + BodyLength + pos;
|
var endIndex = HeaderLength + BodyLength + pos;
|
||||||
if (span[endIndex - 1] == 0x16)
|
|
||||||
|
if (sequence.GetByte(endIndex - 1) == 0x16)
|
||||||
{
|
{
|
||||||
//检查校验码
|
//检查校验码
|
||||||
int sumCheck = 0;
|
var sumCheck = sequence.SumRange(HeadCodeIndex, endIndex - HeadCodeIndex - 2);
|
||||||
for (int i = HeadCodeIndex; i < endIndex - 2; i++)
|
if ((byte)sumCheck != sequence.GetByte(endIndex - 2))
|
||||||
sumCheck += span[i];
|
|
||||||
if ((byte)sumCheck != span[endIndex - 2])
|
|
||||||
{
|
{
|
||||||
//校验错误
|
//校验错误
|
||||||
ErrorMessage = AppResource.SumError;
|
ErrorMessage = AppResource.SumError;
|
||||||
OperCode = 999;
|
OperCode = 999;
|
||||||
return FilterResult.Success;
|
return FilterResult.Success;
|
||||||
}
|
}
|
||||||
var Station = byteBlock.Span.Slice(HeadCodeIndex + 1, 6);
|
|
||||||
byte? ErrorCode;
|
byte? ErrorCode;
|
||||||
var controlCode = span[HeadCodeIndex + 8];
|
var controlCode = sequence.GetByte(HeadCodeIndex + 8);
|
||||||
if ((controlCode & 0x40) == 0x40)//控制码bit6为1时,返回错误
|
if ((controlCode & 0x40) == 0x40)//控制码bit6为1时,返回错误
|
||||||
{
|
{
|
||||||
ErrorCode = (byte)(span[HeadCodeIndex + 10] - 0x33);
|
ErrorCode = (byte)(sequence.GetByte(HeadCodeIndex + 10) - 0x33);
|
||||||
var error = Dlt645Helper.Get2007ErrorMessage(ErrorCode.Value);
|
var error = Dlt645Helper.Get2007ErrorMessage(ErrorCode.Value);
|
||||||
ErrorMessage = string.Format(AppResource.FunctionError, $"0x{controlCode:X2}", error);
|
ErrorMessage = string.Format(AppResource.FunctionError, $"0x{controlCode:X2}", error);
|
||||||
OperCode = 999;
|
OperCode = 999;
|
||||||
@@ -71,6 +94,7 @@ public class Dlt645_2007Message : MessageBase, IResultMessage
|
|||||||
|
|
||||||
if (Dlt645_2007Send != null)
|
if (Dlt645_2007Send != null)
|
||||||
{
|
{
|
||||||
|
var Station = sequence.Slice(HeadCodeIndex + 1, 6);
|
||||||
if (!Station.SequenceEqual(Request.Station.Span))//设备地址不符合时,返回错误
|
if (!Station.SequenceEqual(Request.Station.Span))//设备地址不符合时,返回错误
|
||||||
{
|
{
|
||||||
if (!Request.Station.Span.SequenceEqual(ReadStation))//读写通讯地址例外
|
if (!Request.Station.Span.SequenceEqual(ReadStation))//读写通讯地址例外
|
||||||
@@ -90,7 +114,7 @@ public class Dlt645_2007Message : MessageBase, IResultMessage
|
|||||||
if (Dlt645_2007Send.ControlCode == ControlCode.Read || Dlt645_2007Send.ControlCode == ControlCode.Write)
|
if (Dlt645_2007Send.ControlCode == ControlCode.Read || Dlt645_2007Send.ControlCode == ControlCode.Write)
|
||||||
{
|
{
|
||||||
//数据标识不符合时,返回错误
|
//数据标识不符合时,返回错误
|
||||||
var DataId = byteBlock.Span.Slice(HeadCodeIndex + 10, 4).BytesAdd(-0x33).AsSpan();
|
var DataId = sequence.Slice(HeadCodeIndex + 10, 4).BytesAdd(-0x33).AsSpan();
|
||||||
if (!DataId.SequenceEqual(Request.DataId.Span))
|
if (!DataId.SequenceEqual(Request.DataId.Span))
|
||||||
{
|
{
|
||||||
ErrorMessage = AppResource.DataIdNotSame;
|
ErrorMessage = AppResource.DataIdNotSame;
|
||||||
@@ -101,38 +125,15 @@ public class Dlt645_2007Message : MessageBase, IResultMessage
|
|||||||
}
|
}
|
||||||
|
|
||||||
OperCode = 0;
|
OperCode = 0;
|
||||||
Content = byteBlock.ToArray(HeadCodeIndex + 10, BodyLength - 2);
|
Content = byteBlock.TotalSequence.Slice(HeadCodeIndex + 10, BodyLength - 2).ToArray();
|
||||||
return FilterResult.Success;
|
return FilterResult.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
return FilterResult.GoOn;
|
return FilterResult.GoOn;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public override bool CheckHead<TByteBlock>(ref TByteBlock byteBlock)
|
|
||||||
{
|
|
||||||
//因为设备可能带有FE前导符开头,这里找到0x68的位置
|
|
||||||
var span = byteBlock.Span;
|
|
||||||
if (byteBlock != null)
|
|
||||||
{
|
|
||||||
for (int index = byteBlock.Position; index < byteBlock.Length; index++)
|
|
||||||
{
|
|
||||||
if (span[index] == 0x68)
|
|
||||||
{
|
|
||||||
HeadCodeIndex = index;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//帧起始符 地址域 帧起始符 控制码 数据域长度共10个字节
|
public override void SendInfo(ISendMessage sendMessage)
|
||||||
HeaderLength = HeadCodeIndex - byteBlock.Position + 10;
|
|
||||||
if (byteBlock.Length > HeadCodeIndex + 9)
|
|
||||||
BodyLength = span[HeadCodeIndex + 9] + 2;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
public override void SendInfo(ISendMessage sendMessage, ref ValueByteBlock byteBlock)
|
|
||||||
{
|
{
|
||||||
Dlt645_2007Send = ((Dlt645_2007Send)sendMessage);
|
Dlt645_2007Send = ((Dlt645_2007Send)sendMessage);
|
||||||
Request = Dlt645_2007Send.Dlt645_2007Address;
|
Request = Dlt645_2007Send.Dlt645_2007Address;
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ public class Dlt645_2007Send : ISendMessage
|
|||||||
public int Sign { get; set; }
|
public int Sign { get; set; }
|
||||||
internal Dlt645_2007Address Dlt645_2007Address { get; }
|
internal Dlt645_2007Address Dlt645_2007Address { get; }
|
||||||
|
|
||||||
public void Build<TByteBlock>(ref TByteBlock byteBlock) where TByteBlock : IByteBlockWriter
|
public void Build<TByteBlock>(ref TByteBlock byteBlock) where TByteBlock : IBytesWriter
|
||||||
{
|
{
|
||||||
if (Dlt645_2007Address?.DataId.Length < 4)
|
if (Dlt645_2007Address?.DataId.Length < 4)
|
||||||
{
|
{
|
||||||
@@ -124,16 +124,17 @@ public class Dlt645_2007Send : ISendMessage
|
|||||||
byteBlock.Write(Fehead.Span);//帧起始符
|
byteBlock.Write(Fehead.Span);//帧起始符
|
||||||
SendHeadCodeIndex = Fehead.Length;
|
SendHeadCodeIndex = Fehead.Length;
|
||||||
}
|
}
|
||||||
|
var span = byteBlock.GetSpan(256);
|
||||||
|
|
||||||
WriterExtension.WriteValue(ref byteBlock, (byte)0x68);//帧起始符
|
WriterExtension.WriteValue(ref byteBlock, (byte)0x68);//帧起始符
|
||||||
byteBlock.Write(Dlt645_2007Address.Station.Span);//6个字节地址域
|
byteBlock.Write(Dlt645_2007Address.Station.Span);//6个字节地址域
|
||||||
WriterExtension.WriteValue(ref byteBlock, (byte)0x68);//帧起始符
|
WriterExtension.WriteValue(ref byteBlock, (byte)0x68);//帧起始符
|
||||||
WriterExtension.WriteValue(ref byteBlock, (byte)ControlCode);//控制码
|
WriterExtension.WriteValue(ref byteBlock, (byte)ControlCode);//控制码
|
||||||
|
|
||||||
WriterExtension.WriteValue(ref byteBlock, (byte)(Dlt645_2007Address.DataId.Length));//数据域长度
|
var writerLenAnchor = new WriterAnchor<TByteBlock>(ref byteBlock, 1);
|
||||||
byteBlock.Write(Dlt645_2007Address.DataId.Span);//数据域标识DI3、DI2、DI1、DI0
|
byteBlock.Write(Dlt645_2007Address.DataId.Span.BytesAdd(0x33));//数据域标识DI3、DI2、DI1、DI0
|
||||||
|
|
||||||
byteBlock.Write(Codes.Span);
|
byteBlock.Write(Codes.Span.BytesAdd(0x33));
|
||||||
|
|
||||||
if (Datas.Length > 0)
|
if (Datas.Length > 0)
|
||||||
{
|
{
|
||||||
@@ -180,18 +181,15 @@ public class Dlt645_2007Send : ISendMessage
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
byteBlock.Write(data);
|
byteBlock.Write(data.BytesAdd(0x33));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ByteBlockExtension.WriteBackValue(ref byteBlock, (byte)(byteBlock.Length - 10 - Fehead.Length), Fehead.Length + 9);//数据域长度
|
var lenSpan = writerLenAnchor.Rewind(ref byteBlock, out var length);
|
||||||
|
lenSpan.WriteValue<byte>((byte)(length - 1));//数据域长度
|
||||||
for (int index = Fehead.Length + 10; index < byteBlock.Length; ++index)
|
|
||||||
ByteBlockExtension.WriteBackAddValue(ref byteBlock, (byte)0x33, index);//传输时发送方按字节进行加33H处理,接收方按字节进行减33H处理
|
|
||||||
|
|
||||||
int num = 0;
|
int num = 0;
|
||||||
var span = byteBlock.Span;
|
for (int index = 0; index < byteBlock.WrittenCount; ++index)
|
||||||
for (int index = Fehead.Length; index < byteBlock.Length; ++index)
|
|
||||||
num += span[index];
|
num += span[index];
|
||||||
WriterExtension.WriteValue(ref byteBlock, (byte)num);//校验码,总加和
|
WriterExtension.WriteValue(ref byteBlock, (byte)num);//校验码,总加和
|
||||||
WriterExtension.WriteValue(ref byteBlock, (byte)0x16);//结束符
|
WriterExtension.WriteValue(ref byteBlock, (byte)0x16);//结束符
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ public class Dlt645_2007Master : DtuServiceDeviceBase
|
|||||||
/// <param name="station">表号</param>
|
/// <param name="station">表号</param>
|
||||||
/// <param name="cancellationToken">取消令箭</param>
|
/// <param name="cancellationToken">取消令箭</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public async ValueTask<OperResult> FreezeAsync(DateTime dateTime, string station = null, CancellationToken cancellationToken = default)
|
public ValueTask<OperResult<ReadOnlyMemory<byte>>> FreezeAsync(DateTime dateTime, string station = null, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -87,11 +87,11 @@ public class Dlt645_2007Master : DtuServiceDeviceBase
|
|||||||
dAddress.SetStation(station ?? Station);
|
dAddress.SetStation(station ?? Station);
|
||||||
dAddress.DataId = str.HexStringToBytes();
|
dAddress.DataId = str.HexStringToBytes();
|
||||||
|
|
||||||
return await Dlt645RequestAsync(dAddress, ControlCode.Freeze, FEHead, cancellationToken: cancellationToken).ConfigureAwait(false);
|
return Dlt645RequestAsync(dAddress, ControlCode.Freeze, FEHead, cancellationToken: cancellationToken);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
return new OperResult<string>(ex);
|
return EasyValueTask.FromResult(new OperResult<ReadOnlyMemory<byte>>(ex));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -217,7 +217,7 @@ public class Dlt645_2007Master : DtuServiceDeviceBase
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
return new OperResult<byte[]>(ex);
|
return new OperResult(ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -253,7 +253,7 @@ public class Dlt645_2007Master : DtuServiceDeviceBase
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
return new OperResult<byte[]>(ex);
|
return new OperResult(ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -291,7 +291,7 @@ public class Dlt645_2007Master : DtuServiceDeviceBase
|
|||||||
/// <param name="station">表号</param>
|
/// <param name="station">表号</param>
|
||||||
/// <param name="cancellationToken">取消令箭</param>
|
/// <param name="cancellationToken">取消令箭</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public async ValueTask<OperResult> WriteBaudRateAsync(int baudRate, string station = null, CancellationToken cancellationToken = default)
|
public ValueTask<OperResult<ReadOnlyMemory<byte>>> WriteBaudRateAsync(int baudRate, string station = null, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -304,18 +304,18 @@ public class Dlt645_2007Master : DtuServiceDeviceBase
|
|||||||
case 4800: baudRateByte = 0x10; break;
|
case 4800: baudRateByte = 0x10; break;
|
||||||
case 9600: baudRateByte = 0x20; break;
|
case 9600: baudRateByte = 0x20; break;
|
||||||
case 19200: baudRateByte = 0x40; break;
|
case 19200: baudRateByte = 0x40; break;
|
||||||
default: return new OperResult<string>(string.Format(AppResource.BaudRateError, baudRate));
|
default: return EasyValueTask.FromResult(new OperResult<ReadOnlyMemory<byte>>(string.Format(AppResource.BaudRateError, baudRate)));
|
||||||
}
|
}
|
||||||
|
|
||||||
Dlt645_2007Address dAddress = new();
|
Dlt645_2007Address dAddress = new();
|
||||||
dAddress.SetStation(station ?? Station);
|
dAddress.SetStation(station ?? Station);
|
||||||
dAddress.DataId = new byte[] { baudRateByte };
|
dAddress.DataId = new byte[] { baudRateByte };
|
||||||
|
|
||||||
return await Dlt645RequestAsync(dAddress, ControlCode.ReadStation, FEHead, cancellationToken: cancellationToken).ConfigureAwait(false);
|
return Dlt645RequestAsync(dAddress, ControlCode.ReadStation, FEHead, cancellationToken: cancellationToken);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
return new OperResult<string>(ex);
|
return EasyValueTask.FromResult(new OperResult<ReadOnlyMemory<byte>>(ex));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -338,7 +338,7 @@ public class Dlt645_2007Master : DtuServiceDeviceBase
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
return new OperResult<string>(ex);
|
return new OperResult(ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -351,7 +351,7 @@ public class Dlt645_2007Master : DtuServiceDeviceBase
|
|||||||
/// <param name="station">station</param>
|
/// <param name="station">station</param>
|
||||||
/// <param name="cancellationToken">取消令箭</param>
|
/// <param name="cancellationToken">取消令箭</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public async ValueTask<OperResult> WritePasswordAsync(byte level, string oldPassword, string newPassword, string station = null, CancellationToken cancellationToken = default)
|
public ValueTask<OperResult<ReadOnlyMemory<byte>>> WritePasswordAsync(byte level, string oldPassword, string newPassword, string station = null, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -371,11 +371,11 @@ public class Dlt645_2007Master : DtuServiceDeviceBase
|
|||||||
dAddress.Station = "AAAAAAAAAAAA".HexStringToBytes();
|
dAddress.Station = "AAAAAAAAAAAA".HexStringToBytes();
|
||||||
dAddress.DataId = bytes;
|
dAddress.DataId = bytes;
|
||||||
|
|
||||||
return await Dlt645RequestAsync(dAddress, ControlCode.WritePassword, FEHead, cancellationToken: cancellationToken).ConfigureAwait(false);
|
return Dlt645RequestAsync(dAddress, ControlCode.WritePassword, FEHead, cancellationToken: cancellationToken);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
return new OperResult<string>(ex);
|
return EasyValueTask.FromResult(new OperResult<ReadOnlyMemory<byte>>(ex));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,8 @@ public class ModbusAddress : ModbusRequest
|
|||||||
public ModbusAddress(ModbusRequest modbusAddress)
|
public ModbusAddress(ModbusRequest modbusAddress)
|
||||||
{
|
{
|
||||||
StartAddress = modbusAddress.StartAddress;
|
StartAddress = modbusAddress.StartAddress;
|
||||||
Data = modbusAddress.Data;
|
MasterWriteDatas = modbusAddress.MasterWriteDatas;
|
||||||
|
SlaveWriteDatas = modbusAddress.SlaveWriteDatas;
|
||||||
FunctionCode = modbusAddress.FunctionCode;
|
FunctionCode = modbusAddress.FunctionCode;
|
||||||
Length = modbusAddress.Length;
|
Length = modbusAddress.Length;
|
||||||
Station = modbusAddress.Station;
|
Station = modbusAddress.Station;
|
||||||
@@ -37,7 +38,8 @@ public class ModbusAddress : ModbusRequest
|
|||||||
public ModbusAddress(ModbusAddress modbusAddress)
|
public ModbusAddress(ModbusAddress modbusAddress)
|
||||||
{
|
{
|
||||||
StartAddress = modbusAddress.StartAddress;
|
StartAddress = modbusAddress.StartAddress;
|
||||||
Data = modbusAddress.Data;
|
MasterWriteDatas = modbusAddress.MasterWriteDatas;
|
||||||
|
SlaveWriteDatas = modbusAddress.SlaveWriteDatas;
|
||||||
FunctionCode = modbusAddress.FunctionCode;
|
FunctionCode = modbusAddress.FunctionCode;
|
||||||
Length = modbusAddress.Length;
|
Length = modbusAddress.Length;
|
||||||
BitIndex = modbusAddress.BitIndex;
|
BitIndex = modbusAddress.BitIndex;
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
// QQ群:605534569
|
// QQ群:605534569
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
using System.Buffers;
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
namespace ThingsGateway.Foundation.Modbus;
|
namespace ThingsGateway.Foundation.Modbus;
|
||||||
@@ -22,7 +23,12 @@ public class ModbusRequest
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 数据
|
/// 数据
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ReadOnlyMemory<byte> Data { get; set; }
|
public ReadOnlyMemory<byte> MasterWriteDatas { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 当前关联的写入字节数组(Slave端使用)
|
||||||
|
/// </summary>
|
||||||
|
internal ReadOnlySequence<byte> SlaveWriteDatas { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 功能码
|
/// 功能码
|
||||||
|
|||||||
@@ -16,96 +16,12 @@ namespace ThingsGateway.Foundation.Modbus;
|
|||||||
public class ModbusRtuMessage : MessageBase, IResultMessage
|
public class ModbusRtuMessage : MessageBase, IResultMessage
|
||||||
{
|
{
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public override int HeaderLength => 3;
|
public override long HeaderLength => 3;
|
||||||
|
|
||||||
public ModbusAddress? Request { get; set; }
|
public ModbusAddress? Request { get; set; }
|
||||||
|
|
||||||
public ModbusResponse Response { get; set; } = new();
|
public ModbusResponse Response { get; set; } = new();
|
||||||
|
|
||||||
public override FilterResult CheckBody<TByteBlock>(ref TByteBlock byteBlock)
|
|
||||||
{
|
|
||||||
if (Response.ErrorCode != null)
|
|
||||||
{
|
|
||||||
if (Request != null)
|
|
||||||
{
|
|
||||||
if (Request.Station == Response.Station)
|
|
||||||
{
|
|
||||||
return FilterResult.Success;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return FilterResult.GoOn;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var pos = byteBlock.Position - HeaderLength;
|
|
||||||
var crcLen = 0;
|
|
||||||
var f = Response.FunctionCode > 0x30 ? Response.FunctionCode - 0x30 : Response.FunctionCode;
|
|
||||||
if (f <= 4)
|
|
||||||
{
|
|
||||||
OperCode = 0;
|
|
||||||
Content = byteBlock.ToArrayTake(BodyLength - 2);
|
|
||||||
Response.Data = Content;
|
|
||||||
crcLen = 3 + Response.Length;
|
|
||||||
}
|
|
||||||
else if (f == 5 || f == 6)
|
|
||||||
{
|
|
||||||
byteBlock.Position = HeaderLength - 1;
|
|
||||||
Response.StartAddress = ReaderExtension.ReadValue<TByteBlock, ushort>(ref byteBlock, EndianType.Big);
|
|
||||||
OperCode = 0;
|
|
||||||
Content = byteBlock.ToArrayTake(BodyLength - 4);
|
|
||||||
Response.Data = Content;
|
|
||||||
crcLen = 6;
|
|
||||||
}
|
|
||||||
else if (f == 15 || f == 16)
|
|
||||||
{
|
|
||||||
byteBlock.Position = HeaderLength - 1;
|
|
||||||
Response.StartAddress = ReaderExtension.ReadValue<TByteBlock, ushort>(ref byteBlock, EndianType.Big);
|
|
||||||
Response.Length = ReaderExtension.ReadValue<TByteBlock, ushort>(ref byteBlock, EndianType.Big);
|
|
||||||
OperCode = 0;
|
|
||||||
Content = Array.Empty<byte>();
|
|
||||||
crcLen = 6;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
OperCode = 999;
|
|
||||||
ErrorMessage = AppResource.ModbusError1;
|
|
||||||
return FilterResult.GoOn;
|
|
||||||
}
|
|
||||||
if (crcLen > 0)
|
|
||||||
{
|
|
||||||
var crc = CRC16Utils.Crc16Only(byteBlock.Span.Slice(pos, crcLen));
|
|
||||||
|
|
||||||
//Crc
|
|
||||||
var checkCrc = byteBlock.Span.Slice(pos + crcLen, 2);
|
|
||||||
if (checkCrc.SequenceEqual(crc))
|
|
||||||
{
|
|
||||||
//验证发送/返回站号与功能码
|
|
||||||
//站号验证
|
|
||||||
if (Request != null)
|
|
||||||
{
|
|
||||||
if (Request.Station != Response.Station)
|
|
||||||
{
|
|
||||||
OperCode = 999;
|
|
||||||
Response.ErrorCode = 1;
|
|
||||||
ErrorMessage = string.Format(AppResource.StationNotSame, Request.Station, Response.Station);
|
|
||||||
return FilterResult.GoOn;
|
|
||||||
}
|
|
||||||
if (f > 4 ? Request.WriteFunctionCode != Response.FunctionCode : Request.FunctionCode != Response.FunctionCode)
|
|
||||||
{
|
|
||||||
OperCode = 999;
|
|
||||||
Response.ErrorCode = 1;
|
|
||||||
ErrorMessage = string.Format(AppResource.FunctionNotSame, Request.FunctionCode, Response.FunctionCode);
|
|
||||||
return FilterResult.GoOn;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return FilterResult.Success;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return FilterResult.GoOn;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool CheckHead<TByteBlock>(ref TByteBlock byteBlock)
|
public override bool CheckHead<TByteBlock>(ref TByteBlock byteBlock)
|
||||||
{
|
{
|
||||||
Response.Station = ReaderExtension.ReadValue<TByteBlock, byte>(ref byteBlock);
|
Response.Station = ReaderExtension.ReadValue<TByteBlock, byte>(ref byteBlock);
|
||||||
@@ -156,7 +72,93 @@ public class ModbusRtuMessage : MessageBase, IResultMessage
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void SendInfo(ISendMessage sendMessage, ref ValueByteBlock byteBlock)
|
|
||||||
|
public override FilterResult CheckBody<TByteBlock>(ref TByteBlock byteBlock)
|
||||||
|
{
|
||||||
|
if (Response.ErrorCode != null)
|
||||||
|
{
|
||||||
|
if (Request != null)
|
||||||
|
{
|
||||||
|
if (Request.Station == Response.Station)
|
||||||
|
{
|
||||||
|
return FilterResult.Success;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return FilterResult.GoOn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var pos = byteBlock.BytesRead - HeaderLength;
|
||||||
|
var crcLen = 0;
|
||||||
|
var f = Response.FunctionCode > 0x30 ? Response.FunctionCode - 0x30 : Response.FunctionCode;
|
||||||
|
if (f <= 4)
|
||||||
|
{
|
||||||
|
OperCode = 0;
|
||||||
|
Content = byteBlock.ToArrayTake(BodyLength - 2);
|
||||||
|
Response.MasterWriteDatas = Content;
|
||||||
|
crcLen = 3 + Response.Length;
|
||||||
|
}
|
||||||
|
else if (f == 5 || f == 6)
|
||||||
|
{
|
||||||
|
byteBlock.BytesRead = HeaderLength - 1;
|
||||||
|
Response.StartAddress = ReaderExtension.ReadValue<TByteBlock, ushort>(ref byteBlock, EndianType.Big);
|
||||||
|
OperCode = 0;
|
||||||
|
Content = byteBlock.ToArrayTake(BodyLength - 4);
|
||||||
|
Response.MasterWriteDatas = Content;
|
||||||
|
crcLen = 6;
|
||||||
|
}
|
||||||
|
else if (f == 15 || f == 16)
|
||||||
|
{
|
||||||
|
byteBlock.BytesRead = HeaderLength - 1;
|
||||||
|
Response.StartAddress = ReaderExtension.ReadValue<TByteBlock, ushort>(ref byteBlock, EndianType.Big);
|
||||||
|
Response.Length = ReaderExtension.ReadValue<TByteBlock, ushort>(ref byteBlock, EndianType.Big);
|
||||||
|
OperCode = 0;
|
||||||
|
Content = Array.Empty<byte>();
|
||||||
|
crcLen = 6;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
OperCode = 999;
|
||||||
|
ErrorMessage = AppResource.ModbusError1;
|
||||||
|
return FilterResult.GoOn;
|
||||||
|
}
|
||||||
|
if (crcLen > 0)
|
||||||
|
{
|
||||||
|
var crc = CRC16Utils.Crc16Only(byteBlock.TotalSequence.Slice(pos, crcLen));
|
||||||
|
|
||||||
|
//Crc
|
||||||
|
var checkCrc = byteBlock.TotalSequence.Slice(pos + crcLen, 2);
|
||||||
|
if (checkCrc.SequenceEqual(crc))
|
||||||
|
{
|
||||||
|
//验证发送/返回站号与功能码
|
||||||
|
//站号验证
|
||||||
|
if (Request != null)
|
||||||
|
{
|
||||||
|
if (Request.Station != Response.Station)
|
||||||
|
{
|
||||||
|
OperCode = 999;
|
||||||
|
Response.ErrorCode = 1;
|
||||||
|
ErrorMessage = string.Format(AppResource.StationNotSame, Request.Station, Response.Station);
|
||||||
|
return FilterResult.GoOn;
|
||||||
|
}
|
||||||
|
if (f > 4 ? Request.WriteFunctionCode != Response.FunctionCode : Request.FunctionCode != Response.FunctionCode)
|
||||||
|
{
|
||||||
|
OperCode = 999;
|
||||||
|
Response.ErrorCode = 1;
|
||||||
|
ErrorMessage = string.Format(AppResource.FunctionNotSame, Request.FunctionCode, Response.FunctionCode);
|
||||||
|
return FilterResult.GoOn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return FilterResult.Success;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return FilterResult.GoOn;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public override void SendInfo(ISendMessage sendMessage)
|
||||||
{
|
{
|
||||||
Request = ((ModbusRtuSend)sendMessage).ModbusAddress;
|
Request = ((ModbusRtuSend)sendMessage).ModbusAddress;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,8 +29,9 @@ public class ModbusRtuSend : ISendMessage
|
|||||||
public ModbusAddress ModbusAddress { get; }
|
public ModbusAddress ModbusAddress { get; }
|
||||||
public int Sign { get; set; }
|
public int Sign { get; set; }
|
||||||
|
|
||||||
public void Build<TByteBlock>(ref TByteBlock byteBlock) where TByteBlock : IByteBlockWriter
|
public void Build<TByteBlock>(ref TByteBlock byteBlock) where TByteBlock : IBytesWriter
|
||||||
{
|
{
|
||||||
|
var span = byteBlock.GetSpan(512);
|
||||||
var f = ModbusAddress.FunctionCode > 0x30 ? ModbusAddress.FunctionCode - 0x30 : ModbusAddress.FunctionCode;
|
var f = ModbusAddress.FunctionCode > 0x30 ? ModbusAddress.FunctionCode - 0x30 : ModbusAddress.FunctionCode;
|
||||||
if (!Read)
|
if (!Read)
|
||||||
{
|
{
|
||||||
@@ -38,7 +39,7 @@ public class ModbusRtuSend : ISendMessage
|
|||||||
{
|
{
|
||||||
ModbusAddress.WriteFunctionCode = (byte)(f == 1 ? 5 : 6);
|
ModbusAddress.WriteFunctionCode = (byte)(f == 1 ? 5 : 6);
|
||||||
}
|
}
|
||||||
if (ModbusAddress.Data.Length > 2 && ModbusAddress.WriteFunctionCode < 15)
|
if (ModbusAddress.MasterWriteDatas.Length > 2 && ModbusAddress.WriteFunctionCode < 15)
|
||||||
{
|
{
|
||||||
ModbusAddress.WriteFunctionCode = (byte)(f == 1 ? 15 : 16);
|
ModbusAddress.WriteFunctionCode = (byte)(f == 1 ? 15 : 16);
|
||||||
}
|
}
|
||||||
@@ -63,16 +64,16 @@ public class ModbusRtuSend : ISendMessage
|
|||||||
WriterExtension.WriteValue(ref byteBlock, (byte)ModbusAddress.Station);
|
WriterExtension.WriteValue(ref byteBlock, (byte)ModbusAddress.Station);
|
||||||
WriterExtension.WriteValue(ref byteBlock, (byte)ModbusAddress.WriteFunctionCode);
|
WriterExtension.WriteValue(ref byteBlock, (byte)ModbusAddress.WriteFunctionCode);
|
||||||
WriterExtension.WriteValue(ref byteBlock, (ushort)ModbusAddress.StartAddress, EndianType.Big);
|
WriterExtension.WriteValue(ref byteBlock, (ushort)ModbusAddress.StartAddress, EndianType.Big);
|
||||||
byteBlock.Write(ModbusAddress.Data.Span);
|
byteBlock.Write(ModbusAddress.MasterWriteDatas.Span);
|
||||||
}
|
}
|
||||||
else if (wf == 15 || wf == 16)
|
else if (wf == 15 || wf == 16)
|
||||||
{
|
{
|
||||||
var data = ModbusAddress.Data.ArrayExpandToLengthEven().Span;
|
var data = ModbusAddress.MasterWriteDatas.ArrayExpandToLengthEven().Span;
|
||||||
WriterExtension.WriteValue(ref byteBlock, (ushort)(data.Length + 7), EndianType.Big);
|
WriterExtension.WriteValue(ref byteBlock, (ushort)(data.Length + 7), EndianType.Big);
|
||||||
WriterExtension.WriteValue(ref byteBlock, (byte)ModbusAddress.Station);
|
WriterExtension.WriteValue(ref byteBlock, (byte)ModbusAddress.Station);
|
||||||
WriterExtension.WriteValue(ref byteBlock, (byte)ModbusAddress.WriteFunctionCode);
|
WriterExtension.WriteValue(ref byteBlock, (byte)ModbusAddress.WriteFunctionCode);
|
||||||
WriterExtension.WriteValue(ref byteBlock, (ushort)ModbusAddress.StartAddress, EndianType.Big);
|
WriterExtension.WriteValue(ref byteBlock, (ushort)ModbusAddress.StartAddress, EndianType.Big);
|
||||||
var len = (ushort)Math.Ceiling(wf == 15 ? ModbusAddress.Data.Length * 8 : ModbusAddress.Data.Length / 2.0);
|
var len = (ushort)Math.Ceiling(wf == 15 ? ModbusAddress.MasterWriteDatas.Length * 8 : ModbusAddress.MasterWriteDatas.Length / 2.0);
|
||||||
WriterExtension.WriteValue(ref byteBlock, len, EndianType.Big);
|
WriterExtension.WriteValue(ref byteBlock, len, EndianType.Big);
|
||||||
WriterExtension.WriteValue(ref byteBlock, (byte)(len * 2));
|
WriterExtension.WriteValue(ref byteBlock, (byte)(len * 2));
|
||||||
byteBlock.Write(data);
|
byteBlock.Write(data);
|
||||||
@@ -81,6 +82,7 @@ public class ModbusRtuSend : ISendMessage
|
|||||||
{
|
{
|
||||||
throw new System.InvalidOperationException(AppResource.ModbusError1);
|
throw new System.InvalidOperationException(AppResource.ModbusError1);
|
||||||
}
|
}
|
||||||
byteBlock.Write(CRC16Utils.Crc16Only(byteBlock.Span));
|
var crclen = byteBlock.WrittenCount;
|
||||||
|
byteBlock.Write(CRC16Utils.Crc16Only(span.Slice(0, (int)crclen)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,74 +16,14 @@ namespace ThingsGateway.Foundation.Modbus;
|
|||||||
public class ModbusTcpMessage : MessageBase, IResultMessage
|
public class ModbusTcpMessage : MessageBase, IResultMessage
|
||||||
{
|
{
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public override int HeaderLength => 8;
|
public override long HeaderLength => 8;
|
||||||
|
|
||||||
public ModbusResponse Response { get; set; } = new();
|
public ModbusResponse Response { get; set; } = new();
|
||||||
|
|
||||||
public override FilterResult CheckBody<TByteBlock>(ref TByteBlock byteBlock)
|
|
||||||
{
|
|
||||||
var f = Response.FunctionCode > 0x30 ? Response.FunctionCode - 0x30 : Response.FunctionCode;
|
|
||||||
if (error)
|
|
||||||
{
|
|
||||||
Response.ErrorCode = ReaderExtension.ReadValue<TByteBlock, byte>(ref byteBlock);
|
|
||||||
OperCode = Response.ErrorCode;
|
|
||||||
ErrorMessage = ModbusHelper.GetDescriptionByErrorCode(Response.ErrorCode.Value);
|
|
||||||
ErrorType = ErrorTypeEnum.DeviceError;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Response.ErrorCode = null;
|
|
||||||
if (f <= 4)
|
|
||||||
{
|
|
||||||
Response.Length = ReaderExtension.ReadValue<TByteBlock, byte>(ref byteBlock);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Response.StartAddress = ReaderExtension.ReadValue<TByteBlock, ushort>(ref byteBlock);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (Response.ErrorCode != null)
|
|
||||||
{
|
|
||||||
return FilterResult.Success;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (f <= 4)
|
|
||||||
{
|
|
||||||
OperCode = 0;
|
|
||||||
Content = byteBlock.ToArrayTake(BodyLength - 1);
|
|
||||||
Response.Data = Content;
|
|
||||||
return FilterResult.Success;
|
|
||||||
}
|
|
||||||
else if (f == 5 || f == 6)
|
|
||||||
{
|
|
||||||
byteBlock.Position = HeaderLength - 1;
|
|
||||||
Response.StartAddress = ReaderExtension.ReadValue<TByteBlock, ushort>(ref byteBlock);
|
|
||||||
OperCode = 0;
|
|
||||||
Content = byteBlock.ToArrayTake(BodyLength - 2);
|
|
||||||
Response.Data = Content;
|
|
||||||
return FilterResult.Success;
|
|
||||||
}
|
|
||||||
else if (f == 15 || f == 16)
|
|
||||||
{
|
|
||||||
byteBlock.Position = HeaderLength - 1;
|
|
||||||
Response.StartAddress = ReaderExtension.ReadValue<TByteBlock, ushort>(ref byteBlock, EndianType.Big);
|
|
||||||
Response.Length = ReaderExtension.ReadValue<TByteBlock, ushort>(ref byteBlock, EndianType.Big);
|
|
||||||
OperCode = 0;
|
|
||||||
Content = Array.Empty<byte>();
|
|
||||||
return FilterResult.Success;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
OperCode = 999;
|
|
||||||
ErrorMessage = AppResource.ModbusError1;
|
|
||||||
}
|
|
||||||
return FilterResult.GoOn;
|
|
||||||
}
|
|
||||||
bool error = false;
|
|
||||||
public override bool CheckHead<TByteBlock>(ref TByteBlock byteBlock)
|
public override bool CheckHead<TByteBlock>(ref TByteBlock byteBlock)
|
||||||
{
|
{
|
||||||
Sign = ReaderExtension.ReadValue<TByteBlock, ushort>(ref byteBlock, EndianType.Big);
|
Sign = ReaderExtension.ReadValue<TByteBlock, ushort>(ref byteBlock, EndianType.Big);
|
||||||
byteBlock.Position += 2;
|
byteBlock.BytesRead += 2;
|
||||||
BodyLength = ReaderExtension.ReadValue<TByteBlock, ushort>(ref byteBlock, EndianType.Big) - 2;
|
BodyLength = ReaderExtension.ReadValue<TByteBlock, ushort>(ref byteBlock, EndianType.Big) - 2;
|
||||||
Response.Station = ReaderExtension.ReadValue<TByteBlock, byte>(ref byteBlock);
|
Response.Station = ReaderExtension.ReadValue<TByteBlock, byte>(ref byteBlock);
|
||||||
Response.FunctionCode = ReaderExtension.ReadValue<TByteBlock, byte>(ref byteBlock);
|
Response.FunctionCode = ReaderExtension.ReadValue<TByteBlock, byte>(ref byteBlock);
|
||||||
@@ -98,4 +38,57 @@ public class ModbusTcpMessage : MessageBase, IResultMessage
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public override FilterResult CheckBody<TByteBlock>(ref TByteBlock byteBlock)
|
||||||
|
{
|
||||||
|
var f = Response.FunctionCode > 0x30 ? Response.FunctionCode - 0x30 : Response.FunctionCode;
|
||||||
|
if (error)
|
||||||
|
{
|
||||||
|
Response.ErrorCode = ReaderExtension.ReadValue<TByteBlock, byte>(ref byteBlock);
|
||||||
|
OperCode = Response.ErrorCode;
|
||||||
|
ErrorMessage = ModbusHelper.GetDescriptionByErrorCode(Response.ErrorCode.Value);
|
||||||
|
ErrorType = ErrorTypeEnum.DeviceError;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Response.ErrorCode = null;
|
||||||
|
}
|
||||||
|
if (Response.ErrorCode != null)
|
||||||
|
{
|
||||||
|
return FilterResult.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (f <= 4)
|
||||||
|
{
|
||||||
|
OperCode = 0;
|
||||||
|
Response.Length = ReaderExtension.ReadValue<TByteBlock, byte>(ref byteBlock);
|
||||||
|
Content = byteBlock.ToArrayTake(BodyLength - 1);
|
||||||
|
Response.MasterWriteDatas = Content;
|
||||||
|
return FilterResult.Success;
|
||||||
|
}
|
||||||
|
else if (f == 5 || f == 6)
|
||||||
|
{
|
||||||
|
Response.StartAddress = ReaderExtension.ReadValue<TByteBlock, ushort>(ref byteBlock, EndianType.Big);
|
||||||
|
OperCode = 0;
|
||||||
|
Content = byteBlock.ToArrayTake(BodyLength - 2);
|
||||||
|
Response.MasterWriteDatas = Content;
|
||||||
|
return FilterResult.Success;
|
||||||
|
}
|
||||||
|
else if (f == 15 || f == 16)
|
||||||
|
{
|
||||||
|
Response.StartAddress = ReaderExtension.ReadValue<TByteBlock, ushort>(ref byteBlock, EndianType.Big);
|
||||||
|
OperCode = 0;
|
||||||
|
Response.Length = ReaderExtension.ReadValue<TByteBlock, ushort>(ref byteBlock, EndianType.Big);
|
||||||
|
Content = Array.Empty<byte>();
|
||||||
|
return FilterResult.Success;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
OperCode = 999;
|
||||||
|
ErrorMessage = AppResource.ModbusError1;
|
||||||
|
}
|
||||||
|
return FilterResult.GoOn;
|
||||||
|
}
|
||||||
|
bool error = false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ public class ModbusTcpSend : ISendMessage
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public ushort TransactionId { get; private set; }
|
public ushort TransactionId { get; private set; }
|
||||||
|
|
||||||
public void Build<TByteBlock>(ref TByteBlock byteBlock) where TByteBlock : IByteBlockWriter
|
public void Build<TByteBlock>(ref TByteBlock byteBlock) where TByteBlock : IBytesWriter
|
||||||
{
|
{
|
||||||
TransactionId = (ushort)Sign;
|
TransactionId = (ushort)Sign;
|
||||||
var f = ModbusAddress.FunctionCode > 0x30 ? ModbusAddress.FunctionCode - 0x30 : ModbusAddress.FunctionCode;
|
var f = ModbusAddress.FunctionCode > 0x30 ? ModbusAddress.FunctionCode - 0x30 : ModbusAddress.FunctionCode;
|
||||||
@@ -52,7 +52,7 @@ public class ModbusTcpSend : ISendMessage
|
|||||||
{
|
{
|
||||||
ModbusAddress.WriteFunctionCode = (byte)(f == 1 ? 5 : 6);
|
ModbusAddress.WriteFunctionCode = (byte)(f == 1 ? 5 : 6);
|
||||||
}
|
}
|
||||||
if (ModbusAddress.Data.Length > 2 && ModbusAddress.WriteFunctionCode < 15)
|
if (ModbusAddress.MasterWriteDatas.Length > 2 && ModbusAddress.WriteFunctionCode < 15)
|
||||||
{
|
{
|
||||||
ModbusAddress.WriteFunctionCode = (byte)(f == 1 ? 15 : 16);
|
ModbusAddress.WriteFunctionCode = (byte)(f == 1 ? 15 : 16);
|
||||||
}
|
}
|
||||||
@@ -82,16 +82,16 @@ public class ModbusTcpSend : ISendMessage
|
|||||||
WriterExtension.WriteValue(ref byteBlock, (byte)ModbusAddress.Station);
|
WriterExtension.WriteValue(ref byteBlock, (byte)ModbusAddress.Station);
|
||||||
WriterExtension.WriteValue(ref byteBlock, (byte)ModbusAddress.WriteFunctionCode);
|
WriterExtension.WriteValue(ref byteBlock, (byte)ModbusAddress.WriteFunctionCode);
|
||||||
WriterExtension.WriteValue(ref byteBlock, (ushort)ModbusAddress.StartAddress, EndianType.Big);
|
WriterExtension.WriteValue(ref byteBlock, (ushort)ModbusAddress.StartAddress, EndianType.Big);
|
||||||
byteBlock.Write(ModbusAddress.Data.Span);
|
byteBlock.Write(ModbusAddress.MasterWriteDatas.Span);
|
||||||
}
|
}
|
||||||
else if (wf == 15 || wf == 16)
|
else if (wf == 15 || wf == 16)
|
||||||
{
|
{
|
||||||
var data = ModbusAddress.Data.ArrayExpandToLengthEven().Span;
|
var data = ModbusAddress.MasterWriteDatas.ArrayExpandToLengthEven().Span;
|
||||||
WriterExtension.WriteValue(ref byteBlock, (ushort)(data.Length + 7), EndianType.Big);
|
WriterExtension.WriteValue(ref byteBlock, (ushort)(data.Length + 7), EndianType.Big);
|
||||||
WriterExtension.WriteValue(ref byteBlock, (byte)ModbusAddress.Station);
|
WriterExtension.WriteValue(ref byteBlock, (byte)ModbusAddress.Station);
|
||||||
WriterExtension.WriteValue(ref byteBlock, (byte)ModbusAddress.WriteFunctionCode);
|
WriterExtension.WriteValue(ref byteBlock, (byte)ModbusAddress.WriteFunctionCode);
|
||||||
WriterExtension.WriteValue(ref byteBlock, (ushort)ModbusAddress.StartAddress, EndianType.Big);
|
WriterExtension.WriteValue(ref byteBlock, (ushort)ModbusAddress.StartAddress, EndianType.Big);
|
||||||
var len = (ushort)Math.Ceiling(wf == 15 ? ModbusAddress.Data.Length * 8 : ModbusAddress.Data.Length / 2.0);
|
var len = (ushort)Math.Ceiling(wf == 15 ? ModbusAddress.MasterWriteDatas.Length * 8 : ModbusAddress.MasterWriteDatas.Length / 2.0);
|
||||||
WriterExtension.WriteValue(ref byteBlock, len, EndianType.Big);
|
WriterExtension.WriteValue(ref byteBlock, len, EndianType.Big);
|
||||||
WriterExtension.WriteValue(ref byteBlock, (byte)(len * 2));
|
WriterExtension.WriteValue(ref byteBlock, (byte)(len * 2));
|
||||||
byteBlock.Write(data);
|
byteBlock.Write(data);
|
||||||
|
|||||||
@@ -167,7 +167,7 @@ public partial class ModbusMaster : DtuServiceDeviceBase, IModbusAddress
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
var mAddress = GetModbusAddress(address, Station);
|
var mAddress = GetModbusAddress(address, Station);
|
||||||
mAddress.Data = value;
|
mAddress.MasterWriteDatas = value;
|
||||||
|
|
||||||
if (mAddress.BitIndex == null)
|
if (mAddress.BitIndex == null)
|
||||||
{
|
{
|
||||||
@@ -185,7 +185,7 @@ public partial class ModbusMaster : DtuServiceDeviceBase, IModbusAddress
|
|||||||
else
|
else
|
||||||
writeValye[0] = v;
|
writeValye[0] = v;
|
||||||
|
|
||||||
mAddress.Data = writeValye;
|
mAddress.MasterWriteDatas = writeValye;
|
||||||
return await ModbusRequestAsync(mAddress, false, cancellationToken).ConfigureAwait(false);
|
return await ModbusRequestAsync(mAddress, false, cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -212,13 +212,13 @@ public partial class ModbusMaster : DtuServiceDeviceBase, IModbusAddress
|
|||||||
if (value.Length > 1 && (mAddress.FunctionCode == 1 || mAddress.FunctionCode == 0x31))
|
if (value.Length > 1 && (mAddress.FunctionCode == 1 || mAddress.FunctionCode == 0x31))
|
||||||
{
|
{
|
||||||
mAddress.WriteFunctionCode = 15;
|
mAddress.WriteFunctionCode = 15;
|
||||||
mAddress.Data = value.Span.BoolArrayToByte();
|
mAddress.MasterWriteDatas = value.Span.BoolArrayToByte();
|
||||||
return await ModbusRequestAsync(mAddress, false, cancellationToken).ConfigureAwait(false);
|
return await ModbusRequestAsync(mAddress, false, cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
else if (mAddress.BitIndex == null)
|
else if (mAddress.BitIndex == null)
|
||||||
{
|
{
|
||||||
var span = value.Span;
|
var span = value.Span;
|
||||||
mAddress.Data = span[0] ? new byte[2] { 255, 0 } : [0, 0];
|
mAddress.MasterWriteDatas = span[0] ? new byte[2] { 255, 0 } : [0, 0];
|
||||||
return await ModbusRequestAsync(mAddress, false, cancellationToken).ConfigureAwait(false);
|
return await ModbusRequestAsync(mAddress, false, cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -234,7 +234,7 @@ public partial class ModbusMaster : DtuServiceDeviceBase, IModbusAddress
|
|||||||
{
|
{
|
||||||
writeData = writeData.SetBit(mAddress.BitIndex.Value + i, span[i]);
|
writeData = writeData.SetBit(mAddress.BitIndex.Value + i, span[i]);
|
||||||
}
|
}
|
||||||
mAddress.Data = ThingsGatewayBitConverter.GetBytes(writeData);
|
mAddress.MasterWriteDatas = ThingsGatewayBitConverter.GetBytes(writeData);
|
||||||
return await ModbusRequestAsync(mAddress, false, cancellationToken).ConfigureAwait(false);
|
return await ModbusRequestAsync(mAddress, false, cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -8,6 +8,8 @@
|
|||||||
// QQ群:605534569
|
// QQ群:605534569
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
using System.Buffers;
|
||||||
|
|
||||||
namespace ThingsGateway.Foundation.Modbus;
|
namespace ThingsGateway.Foundation.Modbus;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -18,10 +20,10 @@ public class ModbusRtuSlaveMessage : MessageBase, IResultMessage
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 当前关联的字节数组
|
/// 当前关联的字节数组
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ReadOnlyMemory<byte> Bytes { get; set; }
|
public ReadOnlySequence<byte> Sequences { get; set; }
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public override int HeaderLength => 7;
|
public override long HeaderLength => 7;
|
||||||
|
|
||||||
public ModbusRequest Request { get; set; } = new();
|
public ModbusRequest Request { get; set; } = new();
|
||||||
|
|
||||||
@@ -46,13 +48,13 @@ public class ModbusRtuSlaveMessage : MessageBase, IResultMessage
|
|||||||
}
|
}
|
||||||
else if (f == 5)
|
else if (f == 5)
|
||||||
{
|
{
|
||||||
Request.Data = byteBlock.Memory.Slice(byteBlock.Position, 1);
|
Request.SlaveWriteDatas = byteBlock.Sequence.Slice(0, 1);
|
||||||
BodyLength = 1;
|
BodyLength = 1;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else if (f == 6)
|
else if (f == 6)
|
||||||
{
|
{
|
||||||
Request.Data = byteBlock.Memory.Slice(byteBlock.Position, 2);
|
Request.SlaveWriteDatas = byteBlock.Sequence.Slice(0, 2);
|
||||||
BodyLength = 1;
|
BodyLength = 1;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -73,26 +75,27 @@ public class ModbusRtuSlaveMessage : MessageBase, IResultMessage
|
|||||||
|
|
||||||
public override FilterResult CheckBody<TByteBlock>(ref TByteBlock byteBlock)
|
public override FilterResult CheckBody<TByteBlock>(ref TByteBlock byteBlock)
|
||||||
{
|
{
|
||||||
var pos = byteBlock.Position - HeaderLength;
|
var pos = byteBlock.BytesRead - HeaderLength;
|
||||||
var crcLen = 0;
|
long crcLen = 0;
|
||||||
Bytes = byteBlock.Memory.Slice(pos, HeaderLength + BodyLength);
|
Sequences = byteBlock.TotalSequence.Slice(pos, HeaderLength + BodyLength);
|
||||||
|
|
||||||
var f = Request.FunctionCode > 0x30 ? Request.FunctionCode - 0x30 : Request.FunctionCode;
|
var f = Request.FunctionCode > 0x30 ? Request.FunctionCode - 0x30 : Request.FunctionCode;
|
||||||
if (f == 15)
|
if (f == 15)
|
||||||
{
|
{
|
||||||
Request.Data = byteBlock.Memory.Slice(byteBlock.Position, Request.Length).Span.ByteToBoolArray(Request.Length).BoolToByte();
|
Request.SlaveWriteDatas = new ReadOnlySequence<byte>(byteBlock.TotalSequence.Slice(byteBlock.BytesRead, Request.Length).ByteToBoolArray(Request.Length).BoolToByte());
|
||||||
}
|
}
|
||||||
else if (f == 16)
|
else if (f == 16)
|
||||||
{
|
{
|
||||||
Request.Data = byteBlock.Memory.Slice(byteBlock.Position, Request.Length);
|
|
||||||
|
Request.SlaveWriteDatas = byteBlock.TotalSequence.Slice(byteBlock.BytesRead, Request.Length);
|
||||||
}
|
}
|
||||||
|
|
||||||
crcLen = HeaderLength + BodyLength - 2;
|
crcLen = HeaderLength + BodyLength - 2;
|
||||||
|
|
||||||
var crc = CRC16Utils.Crc16Only(byteBlock.Span.Slice(pos, crcLen));
|
var crc = CRC16Utils.Crc16Only(byteBlock.TotalSequence.Slice(pos, crcLen));
|
||||||
|
|
||||||
//Crc
|
//Crc
|
||||||
var checkCrc = byteBlock.Span.Slice(pos + crcLen, 2);
|
var checkCrc = byteBlock.TotalSequence.Slice(pos + crcLen, 2);
|
||||||
if (checkCrc.SequenceEqual(crc))
|
if (checkCrc.SequenceEqual(crc))
|
||||||
{
|
{
|
||||||
OperCode = 0;
|
OperCode = 0;
|
||||||
|
|||||||
@@ -8,6 +8,8 @@
|
|||||||
// QQ群:605534569
|
// QQ群:605534569
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
using System.Buffers;
|
||||||
|
|
||||||
namespace ThingsGateway.Foundation.Modbus;
|
namespace ThingsGateway.Foundation.Modbus;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -18,17 +20,17 @@ public class ModbusTcpSlaveMessage : MessageBase, IResultMessage
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 当前关联的字节数组
|
/// 当前关联的字节数组
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ReadOnlyMemory<byte> Bytes { get; set; }
|
public ReadOnlySequence<byte> Sequences { get; set; }
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public override int HeaderLength => 12;
|
public override long HeaderLength => 12;
|
||||||
|
|
||||||
public ModbusRequest Request { get; set; } = new();
|
public ModbusRequest Request { get; set; } = new();
|
||||||
|
|
||||||
public override bool CheckHead<TByteBlock>(ref TByteBlock byteBlock)
|
public override bool CheckHead<TByteBlock>(ref TByteBlock byteBlock)
|
||||||
{
|
{
|
||||||
Sign = ReaderExtension.ReadValue<TByteBlock, ushort>(ref byteBlock, EndianType.Big);
|
Sign = ReaderExtension.ReadValue<TByteBlock, ushort>(ref byteBlock, EndianType.Big);
|
||||||
byteBlock.Position += 2;
|
byteBlock.BytesRead += 2;
|
||||||
BodyLength = ReaderExtension.ReadValue<TByteBlock, ushort>(ref byteBlock, EndianType.Big) - 6;
|
BodyLength = ReaderExtension.ReadValue<TByteBlock, ushort>(ref byteBlock, EndianType.Big) - 6;
|
||||||
Request.Station = ReaderExtension.ReadValue<TByteBlock, byte>(ref byteBlock);
|
Request.Station = ReaderExtension.ReadValue<TByteBlock, byte>(ref byteBlock);
|
||||||
Request.FunctionCode = ReaderExtension.ReadValue<TByteBlock, byte>(ref byteBlock);
|
Request.FunctionCode = ReaderExtension.ReadValue<TByteBlock, byte>(ref byteBlock);
|
||||||
@@ -48,12 +50,12 @@ public class ModbusTcpSlaveMessage : MessageBase, IResultMessage
|
|||||||
}
|
}
|
||||||
else if (f == 5)
|
else if (f == 5)
|
||||||
{
|
{
|
||||||
Request.Data = byteBlock.Memory.Slice(byteBlock.Position, 1);
|
Request.SlaveWriteDatas = byteBlock.Sequence.Slice(0, 1);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else if (f == 6)
|
else if (f == 6)
|
||||||
{
|
{
|
||||||
Request.Data = byteBlock.Memory.Slice(byteBlock.Position, 2);
|
Request.SlaveWriteDatas = byteBlock.Sequence.Slice(0, 2);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else if (f == 15)
|
else if (f == 15)
|
||||||
@@ -71,20 +73,20 @@ public class ModbusTcpSlaveMessage : MessageBase, IResultMessage
|
|||||||
|
|
||||||
public override FilterResult CheckBody<TByteBlock>(ref TByteBlock byteBlock)
|
public override FilterResult CheckBody<TByteBlock>(ref TByteBlock byteBlock)
|
||||||
{
|
{
|
||||||
var pos = byteBlock.Position - HeaderLength;
|
var pos = byteBlock.BytesRead - HeaderLength;
|
||||||
Bytes = byteBlock.Memory.Slice(pos, HeaderLength + BodyLength);
|
Sequences = byteBlock.TotalSequence.Slice(pos, HeaderLength + BodyLength);
|
||||||
|
|
||||||
var f = Request.FunctionCode > 0x30 ? Request.FunctionCode - 0x30 : Request.FunctionCode;
|
var f = Request.FunctionCode > 0x30 ? Request.FunctionCode - 0x30 : Request.FunctionCode;
|
||||||
|
|
||||||
if (f == 15)
|
if (f == 15)
|
||||||
{
|
{
|
||||||
byteBlock.Position += 1;
|
byteBlock.BytesRead += 1;
|
||||||
Request.Data = byteBlock.Memory.Slice(byteBlock.Position, Request.Length).Span.ByteToBoolArray(Request.Length).BoolToByte();
|
Request.SlaveWriteDatas = new ReadOnlySequence<byte>(byteBlock.Sequence.Slice(0, Request.Length).ByteToBoolArray(Request.Length).BoolToByte());
|
||||||
}
|
}
|
||||||
else if (f == 16)
|
else if (f == 16)
|
||||||
{
|
{
|
||||||
byteBlock.Position += 1;
|
byteBlock.BytesRead += 1;
|
||||||
Request.Data = byteBlock.Memory.Slice(byteBlock.Position, Request.Length);
|
Request.SlaveWriteDatas = byteBlock.Sequence.Slice(0, Request.Length);
|
||||||
}
|
}
|
||||||
|
|
||||||
OperCode = 0;
|
OperCode = 0;
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
// QQ群:605534569
|
// QQ群:605534569
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
using System.Buffers;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
|
|
||||||
using TouchSocket.Sockets;
|
using TouchSocket.Sockets;
|
||||||
@@ -298,26 +299,29 @@ public class ModbusSlave : DeviceBase, IModbusAddress
|
|||||||
{
|
{
|
||||||
case 2:
|
case 2:
|
||||||
ModbusServer02ByteBlock.Position = mAddress.StartAddress;
|
ModbusServer02ByteBlock.Position = mAddress.StartAddress;
|
||||||
ModbusServer02ByteBlock.Write(mAddress.Data.Span);
|
ByteBlockExtension.Write(ref ModbusServer02ByteBlock, mAddress.SlaveWriteDatas);
|
||||||
return new();
|
return new();
|
||||||
|
|
||||||
case 1:
|
case 1:
|
||||||
case 5:
|
case 5:
|
||||||
case 15:
|
case 15:
|
||||||
ModbusServer01ByteBlock.Position = mAddress.StartAddress;
|
ModbusServer01ByteBlock.Position = mAddress.StartAddress;
|
||||||
ModbusServer01ByteBlock.Write(mAddress.Data.Span);
|
ByteBlockExtension.Write(ref ModbusServer01ByteBlock, mAddress.SlaveWriteDatas);
|
||||||
|
|
||||||
return new();
|
return new();
|
||||||
|
|
||||||
case 4:
|
case 4:
|
||||||
ModbusServer04ByteBlock.Position = mAddress.StartAddress * RegisterByteLength;
|
ModbusServer04ByteBlock.Position = mAddress.StartAddress * RegisterByteLength;
|
||||||
ModbusServer04ByteBlock.Write(mAddress.Data.Span);
|
ByteBlockExtension.Write(ref ModbusServer04ByteBlock, mAddress.SlaveWriteDatas);
|
||||||
|
|
||||||
return new();
|
return new();
|
||||||
|
|
||||||
case 3:
|
case 3:
|
||||||
case 6:
|
case 6:
|
||||||
case 16:
|
case 16:
|
||||||
ModbusServer03ByteBlock.Position = mAddress.StartAddress * RegisterByteLength;
|
ModbusServer03ByteBlock.Position = mAddress.StartAddress * RegisterByteLength;
|
||||||
ModbusServer03ByteBlock.Write(mAddress.Data.Span);
|
ByteBlockExtension.Write(ref ModbusServer03ByteBlock, mAddress.SlaveWriteDatas);
|
||||||
|
|
||||||
return new();
|
return new();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -386,7 +390,7 @@ public class ModbusSlave : DeviceBase, IModbusAddress
|
|||||||
{
|
{
|
||||||
await EasyValueTask.CompletedTask.ConfigureAwait(false);
|
await EasyValueTask.CompletedTask.ConfigureAwait(false);
|
||||||
var mAddress = GetModbusAddress(address, Station);
|
var mAddress = GetModbusAddress(address, Station);
|
||||||
mAddress.Data = value;
|
mAddress.SlaveWriteDatas = new(value);
|
||||||
return ModbusRequest(mAddress, false, cancellationToken);
|
return ModbusRequest(mAddress, false, cancellationToken);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@@ -404,7 +408,7 @@ public class ModbusSlave : DeviceBase, IModbusAddress
|
|||||||
var mAddress = GetModbusAddress(address, Station);
|
var mAddress = GetModbusAddress(address, Station);
|
||||||
if (mAddress.IsBitFunction)
|
if (mAddress.IsBitFunction)
|
||||||
{
|
{
|
||||||
mAddress.Data = value.Span.BoolToByte();
|
mAddress.SlaveWriteDatas = new(value.Span.BoolToByte());
|
||||||
ModbusRequest(mAddress, false, cancellationToken);
|
ModbusRequest(mAddress, false, cancellationToken);
|
||||||
return OperResult.Success;
|
return OperResult.Success;
|
||||||
}
|
}
|
||||||
@@ -421,7 +425,7 @@ public class ModbusSlave : DeviceBase, IModbusAddress
|
|||||||
{
|
{
|
||||||
writeData = writeData.SetBit(mAddress.BitIndex.Value + i, span[i]);
|
writeData = writeData.SetBit(mAddress.BitIndex.Value + i, span[i]);
|
||||||
}
|
}
|
||||||
mAddress.Data = ThingsGatewayBitConverter.GetBytes(writeData);
|
mAddress.SlaveWriteDatas = new(ThingsGatewayBitConverter.GetBytes(writeData));
|
||||||
ModbusRequest(mAddress, false, cancellationToken);
|
ModbusRequest(mAddress, false, cancellationToken);
|
||||||
return OperResult.Success;
|
return OperResult.Success;
|
||||||
}
|
}
|
||||||
@@ -443,7 +447,7 @@ public class ModbusSlave : DeviceBase, IModbusAddress
|
|||||||
var requestInfo = e.RequestInfo;
|
var requestInfo = e.RequestInfo;
|
||||||
bool modbusRtu = false;
|
bool modbusRtu = false;
|
||||||
ModbusRequest modbusRequest = default;
|
ModbusRequest modbusRequest = default;
|
||||||
ReadOnlyMemory<byte> Bytes = default;
|
ReadOnlySequence<byte> readOnlySequences = default;
|
||||||
//接收外部报文
|
//接收外部报文
|
||||||
if (requestInfo is ModbusRtuSlaveMessage modbusRtuSlaveMessage)
|
if (requestInfo is ModbusRtuSlaveMessage modbusRtuSlaveMessage)
|
||||||
{
|
{
|
||||||
@@ -452,7 +456,7 @@ public class ModbusSlave : DeviceBase, IModbusAddress
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
modbusRequest = modbusRtuSlaveMessage.Request;
|
modbusRequest = modbusRtuSlaveMessage.Request;
|
||||||
Bytes = modbusRtuSlaveMessage.Bytes;
|
readOnlySequences = modbusRtuSlaveMessage.Sequences;
|
||||||
modbusRtu = true;
|
modbusRtu = true;
|
||||||
}
|
}
|
||||||
else if (requestInfo is ModbusTcpSlaveMessage modbusTcpSlaveMessage)
|
else if (requestInfo is ModbusTcpSlaveMessage modbusTcpSlaveMessage)
|
||||||
@@ -462,7 +466,7 @@ public class ModbusSlave : DeviceBase, IModbusAddress
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
modbusRequest = modbusTcpSlaveMessage.Request;
|
modbusRequest = modbusTcpSlaveMessage.Request;
|
||||||
Bytes = modbusTcpSlaveMessage.Bytes;
|
readOnlySequences = modbusTcpSlaveMessage.Sequences;
|
||||||
modbusRtu = false;
|
modbusRtu = false;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -486,7 +490,7 @@ public class ModbusSlave : DeviceBase, IModbusAddress
|
|||||||
{
|
{
|
||||||
if (modbusRtu)
|
if (modbusRtu)
|
||||||
{
|
{
|
||||||
byteBlock.Write(Bytes.Slice(0, 2).Span);
|
ByteBlockExtension.Write(ref byteBlock, readOnlySequences.Slice(0, 2));
|
||||||
if (modbusRequest.IsBitFunction)
|
if (modbusRequest.IsBitFunction)
|
||||||
{
|
{
|
||||||
var bitdata = data.Content.Span.ByteToBool().AsSpan().BoolArrayToByte();
|
var bitdata = data.Content.Span.ByteToBool().AsSpan().BoolArrayToByte();
|
||||||
@@ -504,7 +508,7 @@ public class ModbusSlave : DeviceBase, IModbusAddress
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
byteBlock.Write(Bytes.Slice(0, 8).Span);
|
ByteBlockExtension.Write(ref byteBlock, readOnlySequences.Slice(0, 8));
|
||||||
if (modbusRequest.IsBitFunction)
|
if (modbusRequest.IsBitFunction)
|
||||||
{
|
{
|
||||||
var bitdata = data.Content.Span.ByteToBool().AsSpan().BoolArrayToByte();
|
var bitdata = data.Content.Span.ByteToBool().AsSpan().BoolArrayToByte();
|
||||||
@@ -517,13 +521,13 @@ public class ModbusSlave : DeviceBase, IModbusAddress
|
|||||||
WriterExtension.WriteValue(ref byteBlock, (byte)data.Content.Length);
|
WriterExtension.WriteValue(ref byteBlock, (byte)data.Content.Length);
|
||||||
byteBlock.Write(data.Content.Span);
|
byteBlock.Write(data.Content.Span);
|
||||||
}
|
}
|
||||||
ByteBlockExtension.WriteBackValue(ref byteBlock, (byte)(byteBlock.Length - 6), 5);
|
ByteBlockExtension.WriteBackValue(ref byteBlock, (byte)(byteBlock.Length - 6), EndianType.Big, 5);
|
||||||
await ReturnData(client, byteBlock.Memory, e).ConfigureAwait(false);
|
await ReturnData(client, byteBlock.Memory, e).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
await WriteError(modbusRtu, client, Bytes, e).ConfigureAwait(false);
|
await WriteError(modbusRtu, client, readOnlySequences, e).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
@@ -532,7 +536,7 @@ public class ModbusSlave : DeviceBase, IModbusAddress
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
await WriteError(modbusRtu, client, Bytes, e).ConfigureAwait(false);//返回错误码
|
await WriteError(modbusRtu, client, readOnlySequences, e).ConfigureAwait(false);//返回错误码
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else//写入
|
else//写入
|
||||||
@@ -546,21 +550,21 @@ public class ModbusSlave : DeviceBase, IModbusAddress
|
|||||||
// 接收外部写入时,传出变量地址/写入字节组/转换规则/客户端
|
// 接收外部写入时,传出变量地址/写入字节组/转换规则/客户端
|
||||||
if ((await WriteData(modbusAddress, ThingsGatewayBitConverter, client).ConfigureAwait(false)).IsSuccess)
|
if ((await WriteData(modbusAddress, ThingsGatewayBitConverter, client).ConfigureAwait(false)).IsSuccess)
|
||||||
{
|
{
|
||||||
await WriteSuccess(modbusRtu, client, Bytes, e).ConfigureAwait(false);
|
await WriteSuccess(modbusRtu, client, readOnlySequences, e).ConfigureAwait(false);
|
||||||
if (IsWriteMemory)
|
if (IsWriteMemory)
|
||||||
{
|
{
|
||||||
var result = ModbusRequest(modbusRequest, false);
|
var result = ModbusRequest(modbusRequest, false);
|
||||||
if (result.IsSuccess)
|
if (result.IsSuccess)
|
||||||
await WriteSuccess(modbusRtu, client, Bytes, e).ConfigureAwait(false);
|
await WriteSuccess(modbusRtu, client, readOnlySequences, e).ConfigureAwait(false);
|
||||||
else
|
else
|
||||||
await WriteError(modbusRtu, client, Bytes, e).ConfigureAwait(false);
|
await WriteError(modbusRtu, client, readOnlySequences, e).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
await WriteSuccess(modbusRtu, client, Bytes, e).ConfigureAwait(false);
|
await WriteSuccess(modbusRtu, client, readOnlySequences, e).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
await WriteError(modbusRtu, client, Bytes, e).ConfigureAwait(false);
|
await WriteError(modbusRtu, client, readOnlySequences, e).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -569,11 +573,11 @@ public class ModbusSlave : DeviceBase, IModbusAddress
|
|||||||
var result = ModbusRequest(modbusRequest, false);
|
var result = ModbusRequest(modbusRequest, false);
|
||||||
if (result.IsSuccess)
|
if (result.IsSuccess)
|
||||||
{
|
{
|
||||||
await WriteSuccess(modbusRtu, client, Bytes, e).ConfigureAwait(false);
|
await WriteSuccess(modbusRtu, client, readOnlySequences, e).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
await WriteError(modbusRtu, client, Bytes, e).ConfigureAwait(false);
|
await WriteError(modbusRtu, client, readOnlySequences, e).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -589,16 +593,16 @@ public class ModbusSlave : DeviceBase, IModbusAddress
|
|||||||
{
|
{
|
||||||
var result = ModbusRequest(modbusRequest, false);
|
var result = ModbusRequest(modbusRequest, false);
|
||||||
if (result.IsSuccess)
|
if (result.IsSuccess)
|
||||||
await WriteSuccess(modbusRtu, client, Bytes, e).ConfigureAwait(false);
|
await WriteSuccess(modbusRtu, client, readOnlySequences, e).ConfigureAwait(false);
|
||||||
else
|
else
|
||||||
await WriteError(modbusRtu, client, Bytes, e).ConfigureAwait(false);
|
await WriteError(modbusRtu, client, readOnlySequences, e).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
await WriteSuccess(modbusRtu, client, Bytes, e).ConfigureAwait(false);
|
await WriteSuccess(modbusRtu, client, readOnlySequences, e).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
await WriteError(modbusRtu, client, Bytes, e).ConfigureAwait(false);
|
await WriteError(modbusRtu, client, readOnlySequences, e).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -606,11 +610,11 @@ public class ModbusSlave : DeviceBase, IModbusAddress
|
|||||||
var result = ModbusRequest(modbusRequest, false);
|
var result = ModbusRequest(modbusRequest, false);
|
||||||
if (result.IsSuccess)
|
if (result.IsSuccess)
|
||||||
{
|
{
|
||||||
await WriteSuccess(modbusRtu, client, Bytes, e).ConfigureAwait(false);
|
await WriteSuccess(modbusRtu, client, readOnlySequences, e).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
await WriteError(modbusRtu, client, Bytes, e).ConfigureAwait(false);
|
await WriteError(modbusRtu, client, readOnlySequences, e).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -627,24 +631,24 @@ public class ModbusSlave : DeviceBase, IModbusAddress
|
|||||||
await client.SendAsync(sendData, client.ClosedToken).ConfigureAwait(false);
|
await client.SendAsync(sendData, client.ClosedToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task WriteError(bool modbusRtu, IClientChannel client, ReadOnlyMemory<byte> bytes, ReceivedDataEventArgs e)
|
private async Task WriteError(bool modbusRtu, IClientChannel client, ReadOnlySequence<byte> bytes, ReceivedDataEventArgs e)
|
||||||
{
|
{
|
||||||
ValueByteBlock byteBlock = new(20);
|
ValueByteBlock byteBlock = new(20);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (modbusRtu)
|
if (modbusRtu)
|
||||||
{
|
{
|
||||||
byteBlock.Write(bytes.Slice(0, 2).Span);
|
ByteBlockExtension.Write(ref byteBlock, bytes.Slice(0, 2));
|
||||||
WriterExtension.WriteValue(ref byteBlock, (byte)1);
|
WriterExtension.WriteValue(ref byteBlock, (byte)1);
|
||||||
byteBlock.Write(CRC16Utils.Crc16Only(byteBlock.Span));
|
byteBlock.Write(CRC16Utils.Crc16Only(byteBlock.Span));
|
||||||
ByteBlockExtension.WriteBackValue(ref byteBlock, (byte)(byteBlock.Span[1] + 128), 1);
|
ByteBlockExtension.WriteBackValue(ref byteBlock, (byte)(byteBlock.Span[1] + 128), EndianType.Big, 1);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
byteBlock.Write(bytes.Slice(0, 8).Span);
|
ByteBlockExtension.Write(ref byteBlock, bytes.Slice(0, 8));
|
||||||
WriterExtension.WriteValue(ref byteBlock, (byte)1);
|
WriterExtension.WriteValue(ref byteBlock, (byte)1);
|
||||||
ByteBlockExtension.WriteBackValue(ref byteBlock, (byte)(byteBlock.Length - 6), 5);
|
ByteBlockExtension.WriteBackValue(ref byteBlock, (byte)(byteBlock.Length - 6), EndianType.Big, 5);
|
||||||
ByteBlockExtension.WriteBackValue(ref byteBlock, (byte)(byteBlock.Span[7] + 128), 7);
|
ByteBlockExtension.WriteBackValue(ref byteBlock, (byte)(byteBlock.Span[7] + 128), EndianType.Big, 7);
|
||||||
}
|
}
|
||||||
await ReturnData(client, byteBlock.Memory, e).ConfigureAwait(false);
|
await ReturnData(client, byteBlock.Memory, e).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
@@ -654,20 +658,20 @@ public class ModbusSlave : DeviceBase, IModbusAddress
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task WriteSuccess(bool modbusRtu, IClientChannel client, ReadOnlyMemory<byte> bytes, ReceivedDataEventArgs e)
|
private async Task WriteSuccess(bool modbusRtu, IClientChannel client, ReadOnlySequence<byte> bytes, ReceivedDataEventArgs e)
|
||||||
{
|
{
|
||||||
ValueByteBlock byteBlock = new(20);
|
ValueByteBlock byteBlock = new(20);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (modbusRtu)
|
if (modbusRtu)
|
||||||
{
|
{
|
||||||
byteBlock.Write(bytes.Slice(0, 6).Span);
|
ByteBlockExtension.Write(ref byteBlock, bytes.Slice(0, 6));
|
||||||
byteBlock.Write(CRC16Utils.Crc16Only(byteBlock.Span));
|
byteBlock.Write(CRC16Utils.Crc16Only(byteBlock.Span));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
byteBlock.Write(bytes.Slice(0, 12).Span);
|
ByteBlockExtension.Write(ref byteBlock, bytes.Slice(0, 12));
|
||||||
ByteBlockExtension.WriteBackValue(ref byteBlock, (byte)(byteBlock.Length - 6), 5);
|
ByteBlockExtension.WriteBackValue(ref byteBlock, (byte)(byteBlock.Length - 6), EndianType.Big, 5);
|
||||||
}
|
}
|
||||||
await ReturnData(client, byteBlock.Memory, e).ConfigureAwait(false);
|
await ReturnData(client, byteBlock.Memory, e).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -423,12 +423,14 @@ public class OpcUaMaster : IDisposable, IAsyncDisposable
|
|||||||
Disconnect();
|
Disconnect();
|
||||||
_variableDicts?.Clear();
|
_variableDicts?.Clear();
|
||||||
_subscriptionDicts?.Clear();
|
_subscriptionDicts?.Clear();
|
||||||
|
waitLock?.Dispose();
|
||||||
}
|
}
|
||||||
public async ValueTask DisposeAsync()
|
public async ValueTask DisposeAsync()
|
||||||
{
|
{
|
||||||
await DisconnectAsync().ConfigureAwait(false);
|
await DisconnectAsync().ConfigureAwait(false);
|
||||||
_variableDicts?.Clear();
|
_variableDicts?.Clear();
|
||||||
_subscriptionDicts?.Clear();
|
_subscriptionDicts?.Clear();
|
||||||
|
waitLock?.Dispose();
|
||||||
}
|
}
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取变量说明
|
/// 获取变量说明
|
||||||
|
|||||||
@@ -1932,7 +1932,7 @@
|
|||||||
// }
|
// }
|
||||||
// private void WriteDiagnosticInfo(string fieldName, DiagnosticInfo value, int depth)
|
// private void WriteDiagnosticInfo(string fieldName, DiagnosticInfo value, int depth)
|
||||||
// {
|
// {
|
||||||
// if (value == null || value.IsNullDiagnosticInfo)
|
// if (value?.IsNullDiagnosticInfo != false)
|
||||||
// {
|
// {
|
||||||
// WriteSimpleField(fieldName, null, quotes: false);
|
// WriteSimpleField(fieldName, null, quotes: false);
|
||||||
// return;
|
// return;
|
||||||
|
|||||||
@@ -8,6 +8,8 @@
|
|||||||
// QQ群:605534569
|
// QQ群:605534569
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
using System.Buffers;
|
||||||
|
|
||||||
using TouchSocket.Core;
|
using TouchSocket.Core;
|
||||||
|
|
||||||
namespace ThingsGateway.Foundation.SiemensS7;
|
namespace ThingsGateway.Foundation.SiemensS7;
|
||||||
@@ -23,108 +25,116 @@ public class S7Message : MessageBase, IResultMessage
|
|||||||
public byte? Error { get; set; }
|
public byte? Error { get; set; }
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public override int HeaderLength => 4;
|
public override long HeaderLength => 4;
|
||||||
|
|
||||||
public override bool CheckHead<TByteBlock>(ref TByteBlock byteBlock)
|
public override bool CheckHead<TByteBlock>(ref TByteBlock byteBlock)
|
||||||
{
|
{
|
||||||
byteBlock.Position += 2;
|
byteBlock.BytesRead += 2;
|
||||||
BodyLength = ReaderExtension.ReadValue<TByteBlock, ushort>(ref byteBlock, EndianType.Big) - 4;
|
BodyLength = ReaderExtension.ReadValue<TByteBlock, ushort>(ref byteBlock, EndianType.Big) - 4;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
public override FilterResult CheckBody<TByteBlock>(ref TByteBlock byteBlock)
|
public override FilterResult CheckBody<TByteBlock>(ref TByteBlock byteBlock)
|
||||||
{
|
{
|
||||||
var pos = byteBlock.Position;
|
var pos = byteBlock.BytesRead;
|
||||||
var span = byteBlock.Span;
|
var sequence = byteBlock.TotalSequence;
|
||||||
if (span[pos + 1] == 0xD0) // 首次握手0XD0连接确认
|
if (sequence.GetByte(pos + 1) == 0xD0) // 首次握手0XD0连接确认
|
||||||
{
|
{
|
||||||
OperCode = 0;
|
OperCode = 0;
|
||||||
return FilterResult.Success;
|
return FilterResult.Success;
|
||||||
}
|
}
|
||||||
else if (span[pos + 15] == 0xF0) // PDU
|
else if (sequence.GetByte(pos + 15) == 0xF0) // PDU
|
||||||
{
|
{
|
||||||
// 其余情况判断错误代码
|
// 其余情况判断错误代码
|
||||||
if (span[pos + 13] + span[pos + 14] > 0) // 如果错误代码不为0
|
if (sequence.GetByte(pos + 13) + sequence.GetByte(pos + 14) > 0) // 如果错误代码不为0
|
||||||
{
|
{
|
||||||
OperCode = 999;
|
OperCode = 999;
|
||||||
ErrorMessage = string.Format(AppResource.ReturnError, span[pos + 13].ToString("X2"), span[pos + 14].ToString("X2"));
|
ErrorMessage = string.Format(AppResource.ReturnError, sequence.GetByte(pos + 13).ToString("X2"), sequence.GetByte(pos + 14).ToString("X2"));
|
||||||
return FilterResult.Success;
|
return FilterResult.Success;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Content = byteBlock.ToArray(byteBlock.Length - 2, 2);
|
Content = byteBlock.TotalSequence.Slice(byteBlock.BytesRead + byteBlock.BytesRemaining - 2, 2).ToArray();
|
||||||
OperCode = 0;
|
OperCode = 0;
|
||||||
return FilterResult.Success;
|
return FilterResult.Success;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//分bit/byte解析
|
//分bit/byte解析
|
||||||
else if (span[pos + 15] == 0x04) // Read
|
else if (sequence.GetByte(pos + 15) == 0x04) // Read
|
||||||
{
|
{
|
||||||
byteBlock.Position = pos + 7;
|
byteBlock.BytesRead = pos + 7;
|
||||||
var sign = ReaderExtension.ReadValue<TByteBlock, ushort>(ref byteBlock, EndianType.Big);//数据ID标识
|
var sign = ReaderExtension.ReadValue<TByteBlock, ushort>(ref byteBlock, EndianType.Big);//数据ID标识
|
||||||
Sign = sign;
|
Sign = sign;
|
||||||
byteBlock.Position = pos;
|
byteBlock.BytesRead = pos;
|
||||||
|
|
||||||
int length = span[pos + 17];
|
int length = sequence.GetByte(pos + 17);
|
||||||
int itemLen = span[pos + 16];
|
int itemLen = sequence.GetByte(pos + 16);
|
||||||
|
|
||||||
//添加错误代码校验
|
//添加错误代码校验
|
||||||
// 其余情况判断错误代码
|
// 其余情况判断错误代码
|
||||||
if (span[pos + 13] + span[pos + 14] > 0) // 如果错误代码不为0
|
if (sequence.GetByte(pos + 13) + sequence.GetByte(pos + 14) > 0) // 如果错误代码不为0
|
||||||
{
|
{
|
||||||
OperCode = 999;
|
OperCode = 999;
|
||||||
ErrorMessage = string.Format(AppResource.ReturnError, span[pos + 13].ToString("X2"), span[pos + 14].ToString("X2"));
|
ErrorMessage = string.Format(AppResource.ReturnError, sequence.GetByte(pos + 13).ToString("X2"), sequence.GetByte(pos + 14).ToString("X2"));
|
||||||
return FilterResult.Success;
|
return FilterResult.Success;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (byteBlock.Length < pos + 18)
|
if (byteBlock.BytesRead + byteBlock.BytesRemaining < pos + 18)
|
||||||
{
|
{
|
||||||
OperCode = 999;
|
OperCode = 999;
|
||||||
ErrorMessage = AppResource.DataLengthError;
|
ErrorMessage = AppResource.DataLengthError;
|
||||||
return FilterResult.Success;
|
return FilterResult.Success;
|
||||||
}
|
}
|
||||||
if (span[pos + 17] != byte.MaxValue)
|
if (sequence.GetByte(pos + 17) != byte.MaxValue)
|
||||||
{
|
{
|
||||||
OperCode = 999;
|
OperCode = 999;
|
||||||
ErrorMessage = string.Format(AppResource.ValidateDataError, span[pos + 17], SiemensHelper.GetCpuError(span[pos + 17]));
|
ErrorMessage = string.Format(AppResource.ValidateDataError, sequence.GetByte(pos + 17), SiemensHelper.GetCpuError(sequence.GetByte(pos + 17)));
|
||||||
return FilterResult.Success;
|
return FilterResult.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
using ValueByteBlock data = new(length);
|
ValueByteBlock data = new(length);
|
||||||
var dataIndex = pos + 17;
|
var dataIndex = pos + 17;
|
||||||
for (int index = 0; index < itemLen; index++)
|
for (int index = 0; index < itemLen; index++)
|
||||||
{
|
{
|
||||||
if (span[dataIndex] != byte.MaxValue)
|
if (sequence.GetByte(dataIndex) != byte.MaxValue)
|
||||||
{
|
{
|
||||||
OperCode = 999;
|
OperCode = 999;
|
||||||
ErrorMessage = string.Format(AppResource.ValidateDataError, span[dataIndex], SiemensHelper.GetCpuError(span[dataIndex]));
|
ErrorMessage = string.Format(AppResource.ValidateDataError, sequence.GetByte(dataIndex), SiemensHelper.GetCpuError(sequence.GetByte(dataIndex)));
|
||||||
return FilterResult.Success;
|
return FilterResult.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (span[dataIndex + 1] == 4)//Bit:3;Byte:4;Counter或者Timer:9
|
if (sequence.GetByte(dataIndex + 1) == 4)//Bit:3;Byte:4;Counter或者Timer:9
|
||||||
{
|
{
|
||||||
byteBlock.Position = dataIndex + 2;
|
byteBlock.BytesRead = dataIndex + 2;
|
||||||
var byteLength = ReaderExtension.ReadValue<TByteBlock, ushort>(ref byteBlock, EndianType.Big) / 8;
|
var byteLength = ReaderExtension.ReadValue<TByteBlock, ushort>(ref byteBlock, EndianType.Big) / 8;
|
||||||
data.Write(span.Slice(dataIndex + 4, byteLength));
|
ByteBlockExtension.Write(ref data, sequence.Slice(dataIndex + 4, byteLength));
|
||||||
dataIndex += byteLength + 4;
|
dataIndex += byteLength + 4;
|
||||||
}
|
}
|
||||||
else if (span[dataIndex + 1] == 9)//Counter或者Timer:9
|
else if (sequence.GetByte(dataIndex + 1) == 9)//Counter或者Timer:9
|
||||||
{
|
{
|
||||||
byteBlock.Position = dataIndex + 2;
|
byteBlock.BytesRead = dataIndex + 2;
|
||||||
var byteLength = ReaderExtension.ReadValue<TByteBlock, ushort>(ref byteBlock, EndianType.Big);
|
var byteLength = ReaderExtension.ReadValue<TByteBlock, ushort>(ref byteBlock, EndianType.Big);
|
||||||
if (byteLength % 3 == 0)
|
if (byteLength % 3 == 0)
|
||||||
{
|
{
|
||||||
for (int indexCT = 0; indexCT < byteLength / 3; indexCT++)
|
for (int indexCT = 0; indexCT < byteLength / 3; indexCT++)
|
||||||
{
|
{
|
||||||
data.Write(byteBlock.Span.Slice(dataIndex + 5 + (3 * indexCT), 2));
|
var readOnlyMemories = byteBlock.TotalSequence.Slice(dataIndex + 5 + (3 * indexCT), 2);
|
||||||
|
foreach (var item in readOnlyMemories)
|
||||||
|
{
|
||||||
|
data.Write(item.Span);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
for (int indexCT = 0; indexCT < byteLength / 5; indexCT++)
|
for (int indexCT = 0; indexCT < byteLength / 5; indexCT++)
|
||||||
{
|
{
|
||||||
data.Write(byteBlock.Span.Slice(dataIndex + 7 + (5 * indexCT), 2));
|
var readOnlyMemories = byteBlock.TotalSequence.Slice(dataIndex + 7 + (5 * indexCT), 2);
|
||||||
|
foreach (var item in readOnlyMemories)
|
||||||
|
{
|
||||||
|
data.Write(item.Span);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dataIndex += byteLength + 4;
|
dataIndex += byteLength + 4;
|
||||||
@@ -133,23 +143,24 @@ public class S7Message : MessageBase, IResultMessage
|
|||||||
|
|
||||||
OperCode = 0;
|
OperCode = 0;
|
||||||
Content = data.ToArray();
|
Content = data.ToArray();
|
||||||
|
data.SafeDispose();
|
||||||
return FilterResult.Success;
|
return FilterResult.Success;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (span[pos + 15] == 0x05) // Write
|
else if (sequence.GetByte(pos + 15) == 0x05) // Write
|
||||||
{
|
{
|
||||||
byteBlock.Position = pos + 7;
|
byteBlock.BytesRead = pos + 7;
|
||||||
var sign = ReaderExtension.ReadValue<TByteBlock, ushort>(ref byteBlock, EndianType.Big);//数据ID标识
|
var sign = ReaderExtension.ReadValue<TByteBlock, ushort>(ref byteBlock, EndianType.Big);//数据ID标识
|
||||||
Sign = sign;
|
Sign = sign;
|
||||||
byteBlock.Position = pos;
|
byteBlock.BytesRead = pos;
|
||||||
int itemLen = span[pos + 16];
|
int itemLen = sequence.GetByte(pos + 16);
|
||||||
if (span[pos + 13] + span[pos + 14] > 0) // 如果错误代码不为0
|
if (sequence.GetByte(pos + 13) + sequence.GetByte(pos + 14) > 0) // 如果错误代码不为0
|
||||||
{
|
{
|
||||||
OperCode = 999;
|
OperCode = 999;
|
||||||
ErrorMessage = string.Format(AppResource.ReturnError, span[pos + 13].ToString("X2"), span[pos + 14].ToString("X2"));
|
ErrorMessage = string.Format(AppResource.ReturnError, sequence.GetByte(pos + 13).ToString("X2"), sequence.GetByte(pos + 14).ToString("X2"));
|
||||||
return FilterResult.Success;
|
return FilterResult.Success;
|
||||||
}
|
}
|
||||||
if (byteBlock.Length < pos + 18)
|
if (byteBlock.BytesRead + byteBlock.BytesRemaining < pos + 18)
|
||||||
{
|
{
|
||||||
OperCode = 999;
|
OperCode = 999;
|
||||||
ErrorMessage = AppResource.DataLengthError;
|
ErrorMessage = AppResource.DataLengthError;
|
||||||
@@ -157,10 +168,10 @@ public class S7Message : MessageBase, IResultMessage
|
|||||||
}
|
}
|
||||||
for (int i = 0; i < itemLen; i++)
|
for (int i = 0; i < itemLen; i++)
|
||||||
{
|
{
|
||||||
if (span[pos + 17 + i] != byte.MaxValue)
|
if (sequence.GetByte(pos + 17 + i) != byte.MaxValue)
|
||||||
{
|
{
|
||||||
OperCode = 999;
|
OperCode = 999;
|
||||||
ErrorMessage = string.Format(AppResource.ValidateDataError, span[pos + 17 + i], SiemensHelper.GetCpuError(span[pos + 17 + i]));
|
ErrorMessage = string.Format(AppResource.ValidateDataError, sequence.GetByte(pos + 17 + i), SiemensHelper.GetCpuError(sequence.GetByte(pos + 17 + i)));
|
||||||
return FilterResult.Success;
|
return FilterResult.Success;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ public class S7Send : ISendMessage
|
|||||||
public int MaxLength => 2048;
|
public int MaxLength => 2048;
|
||||||
public int Sign { get; set; }
|
public int Sign { get; set; }
|
||||||
|
|
||||||
public void Build<TByteBlock>(ref TByteBlock byteBlock) where TByteBlock : IByteBlockWriter
|
public void Build<TByteBlock>(ref TByteBlock byteBlock) where TByteBlock : IBytesWriter
|
||||||
{
|
{
|
||||||
if (Handshake == true)
|
if (Handshake == true)
|
||||||
{
|
{
|
||||||
@@ -95,7 +95,7 @@ public class S7Send : ISendMessage
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void GetReadCommand<TByteBlock>(ref TByteBlock byteBlock, SiemensS7Address[] siemensS7Address) where TByteBlock : IByteBlockWriter
|
internal void GetReadCommand<TByteBlock>(ref TByteBlock byteBlock, SiemensS7Address[] siemensS7Address) where TByteBlock : IBytesWriter
|
||||||
{
|
{
|
||||||
byte len = (byte)siemensS7Address.Length;
|
byte len = (byte)siemensS7Address.Length;
|
||||||
ushort telegramLen = (ushort)(len * 12 + 19);
|
ushort telegramLen = (ushort)(len * 12 + 19);
|
||||||
@@ -142,7 +142,7 @@ public class S7Send : ISendMessage
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void GetWriteByteCommand<TByteBlock>(ref TByteBlock byteBlock, SiemensS7Address[] addresss) where TByteBlock : IByteBlockWriter
|
internal void GetWriteByteCommand<TByteBlock>(ref TByteBlock byteBlock, SiemensS7Address[] addresss) where TByteBlock : IBytesWriter
|
||||||
{
|
{
|
||||||
byte itemLen = (byte)addresss.Length;
|
byte itemLen = (byte)addresss.Length;
|
||||||
ushort parameterLen = (ushort)(itemLen * 12 + 2);
|
ushort parameterLen = (ushort)(itemLen * 12 + 2);
|
||||||
@@ -200,10 +200,8 @@ public class S7Send : ISendMessage
|
|||||||
dataLen = (ushort)(dataLen + data.Length + 4);
|
dataLen = (ushort)(dataLen + data.Length + 4);
|
||||||
}
|
}
|
||||||
ushort telegramLen = (ushort)(itemLen * 12 + 19 + dataLen);
|
ushort telegramLen = (ushort)(itemLen * 12 + 19 + dataLen);
|
||||||
byteBlock.Position = 2;
|
|
||||||
WriterExtension.WriteValue(ref byteBlock, (ushort)telegramLen, EndianType.Big);//长度
|
ByteBlockExtension.WriteBackValue(ref byteBlock, (ushort)telegramLen, EndianType.Big, 2);//长度
|
||||||
byteBlock.Position = 15;
|
ByteBlockExtension.WriteBackValue(ref byteBlock, (ushort)dataLen, EndianType.Big, 15);//长度
|
||||||
WriterExtension.WriteValue(ref byteBlock, (ushort)dataLen, EndianType.Big);//长度
|
|
||||||
byteBlock.Position = byteBlock.Length;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,10 +15,19 @@ using ThingsGateway.Foundation.Extension.String;
|
|||||||
|
|
||||||
using TouchSocket.Core;
|
using TouchSocket.Core;
|
||||||
|
|
||||||
|
using Xunit.Abstractions;
|
||||||
|
|
||||||
namespace ThingsGateway.Foundation.Test;
|
namespace ThingsGateway.Foundation.Test;
|
||||||
|
|
||||||
public class Dlt645Test
|
public class Dlt645Test
|
||||||
{
|
{
|
||||||
|
public Dlt645Test(ITestOutputHelper output)
|
||||||
|
{
|
||||||
|
_output = output;
|
||||||
|
|
||||||
|
}
|
||||||
|
private readonly ITestOutputHelper _output;
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[InlineData("02010100", "FE FE FE FE 68 11 11 11 11 11 11 68 91 07 33 34 34 35 33 59 36 60 16 ")]
|
[InlineData("02010100", "FE FE FE FE 68 11 11 11 11 11 11 68 91 07 33 34 34 35 33 59 36 60 16 ")]
|
||||||
public async Task Dlt645_Read_OK(string address, string data)
|
public async Task Dlt645_Read_OK(string address, string data)
|
||||||
@@ -31,10 +40,11 @@ public class Dlt645Test
|
|||||||
{
|
{
|
||||||
a.AddEasyLogger((a, b, c, d) =>
|
a.AddEasyLogger((a, b, c, d) =>
|
||||||
{
|
{
|
||||||
Debug.WriteLine($"{c}{Environment.NewLine}{d?.ToString()}");
|
_output.WriteLine($"{c}{Environment.NewLine}{d?.ToString()}");
|
||||||
}, LogLevel.Trace);
|
}, LogLevel.Trace);
|
||||||
});
|
});
|
||||||
var dltMaster = new Dlt645_2007Master() { Timeout = 10000, Station = "111111111111" };
|
|
||||||
|
var dltMaster = new Dlt645_2007Master() { Timeout = 30000, Station = "111111111111" };
|
||||||
dltMaster.InitChannel(dltChannel);
|
dltMaster.InitChannel(dltChannel);
|
||||||
await dltChannel.SetupAsync(dltChannel.Config);
|
await dltChannel.SetupAsync(dltChannel.Config);
|
||||||
await dltMaster.ConnectAsync(CancellationToken.None);
|
await dltMaster.ConnectAsync(CancellationToken.None);
|
||||||
@@ -42,19 +52,20 @@ public class Dlt645Test
|
|||||||
|
|
||||||
var task1 = Task.Run(async () =>
|
var task1 = Task.Run(async () =>
|
||||||
{
|
{
|
||||||
|
Stopwatch stopwatch = new Stopwatch();
|
||||||
|
stopwatch.Start();
|
||||||
var result = await dltMaster.ReadAsync(address, default).ConfigureAwait(false);
|
var result = await dltMaster.ReadAsync(address, default).ConfigureAwait(false);
|
||||||
|
stopwatch.Stop();
|
||||||
Assert.True(result.IsSuccess, result.ToString());
|
Assert.True(result.IsSuccess, result.ToString());
|
||||||
});
|
});
|
||||||
await Task.Delay(50);
|
await Task.Delay(50);
|
||||||
var task2 = Task.Run(async () =>
|
var task2 = Task.Run(async () =>
|
||||||
{
|
{
|
||||||
var bytes = data.HexStringToBytes().GetArray();
|
SingleStreamDataHandlingAdapterTest singleStreamDataHandlingAdapterTest = new();
|
||||||
foreach (var item in bytes)
|
await singleStreamDataHandlingAdapterTest.SendCallback(data.HexStringToBytes(), (a) => singleStreamDataHandlingAdapterTest.ReceivedAsync(adapter, CancellationToken.None), 1, CancellationToken.None).ConfigureAwait(false);
|
||||||
{
|
|
||||||
var data = new ByteBlock(1); data.WriteByte(item);
|
|
||||||
await adapter.ReceivedInputAsync(data).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
await Task.WhenAll(task1, task2);
|
await Task.WhenAll(task1, task2);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,24 +8,34 @@
|
|||||||
// QQ群:605534569
|
// QQ群:605534569
|
||||||
// ------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------
|
||||||
|
|
||||||
using System.Diagnostics;
|
|
||||||
|
|
||||||
using ThingsGateway.Foundation.Extension.String;
|
using ThingsGateway.Foundation.Extension.String;
|
||||||
using ThingsGateway.Foundation.Modbus;
|
using ThingsGateway.Foundation.Modbus;
|
||||||
|
|
||||||
using TouchSocket.Core;
|
using TouchSocket.Core;
|
||||||
|
|
||||||
|
using Xunit.Abstractions;
|
||||||
|
|
||||||
namespace ThingsGateway.Foundation.Test;
|
namespace ThingsGateway.Foundation.Test;
|
||||||
|
|
||||||
public class ModbusTest
|
public class ModbusTest
|
||||||
{
|
{
|
||||||
|
public ModbusTest(ITestOutputHelper output)
|
||||||
|
{
|
||||||
|
_output = output;
|
||||||
|
|
||||||
|
}
|
||||||
|
private readonly ITestOutputHelper _output;
|
||||||
|
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[InlineData("400045", true, "00000000002F01032C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")]
|
[InlineData("400045", true, "00010000002F01032C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")]
|
||||||
[InlineData("300045", true, "00000000002F01042C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")]
|
[InlineData("300045", true, "00010000002F01042C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")]
|
||||||
[InlineData("100045", true, "000000000009010206000000000000")]
|
[InlineData("100045", true, "000100000009010206000000000000")]
|
||||||
[InlineData("000045", true, "000000000009010106000000000000")]
|
[InlineData("000045", true, "000100000009010106000000000000")]
|
||||||
[InlineData("400045", false, "0000000000060106002C0001", "1", DataTypeEnum.UInt16)]
|
[InlineData("400045", false, "0001000000060106002C0001", "1", DataTypeEnum.UInt16)]
|
||||||
[InlineData("000045", false, "0000000000060105002CFF00", "true", DataTypeEnum.Boolean)]
|
[InlineData("000045", false, "0001000000060105002CFF00", "true", DataTypeEnum.Boolean)]
|
||||||
|
[InlineData("400045;w=16", false, "0001000000090110002C0001020001", "1", DataTypeEnum.UInt16)]
|
||||||
|
[InlineData("000045;w=15", false, "000100000008010F002C00010101", "true", DataTypeEnum.Boolean)]
|
||||||
public async Task ModbusTcp_ReadWrite_OK(string address, bool read, string data, string writeData = null, DataTypeEnum dataTypeEnum = DataTypeEnum.UInt16)
|
public async Task ModbusTcp_ReadWrite_OK(string address, bool read, string data, string writeData = null, DataTypeEnum dataTypeEnum = DataTypeEnum.UInt16)
|
||||||
{
|
{
|
||||||
var modbusChannel = new TouchSocketConfig().GetChannel(new ChannelOptions()
|
var modbusChannel = new TouchSocketConfig().GetChannel(new ChannelOptions()
|
||||||
@@ -36,7 +46,7 @@ public class ModbusTest
|
|||||||
{
|
{
|
||||||
a.AddEasyLogger((a, b, c, d) =>
|
a.AddEasyLogger((a, b, c, d) =>
|
||||||
{
|
{
|
||||||
Debug.WriteLine($"{c}{Environment.NewLine}{d?.ToString()}");
|
_output.WriteLine($"{c}{Environment.NewLine}{d?.ToString()}");
|
||||||
}, LogLevel.Trace);
|
}, LogLevel.Trace);
|
||||||
});
|
});
|
||||||
var modbusMaster = new ModbusMaster() { ModbusType = ModbusTypeEnum.ModbusTcp, Timeout = 10000 };
|
var modbusMaster = new ModbusMaster() { ModbusType = ModbusTypeEnum.ModbusTcp, Timeout = 10000 };
|
||||||
@@ -61,12 +71,8 @@ public class ModbusTest
|
|||||||
await Task.Delay(50);
|
await Task.Delay(50);
|
||||||
var task2 = Task.Run(async () =>
|
var task2 = Task.Run(async () =>
|
||||||
{
|
{
|
||||||
var bytes = data.HexStringToBytes().GetArray();
|
SingleStreamDataHandlingAdapterTest singleStreamDataHandlingAdapterTest = new();
|
||||||
foreach (var item in bytes)
|
await singleStreamDataHandlingAdapterTest.SendCallback(data.HexStringToBytes(), (a) => singleStreamDataHandlingAdapterTest.ReceivedAsync(adapter, CancellationToken.None), 1, CancellationToken.None).ConfigureAwait(false);
|
||||||
{
|
|
||||||
var data = new ByteBlock(1); data.WriteByte(item);
|
|
||||||
await adapter.ReceivedInputAsync(data).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
await Task.WhenAll(task1, task2);
|
await Task.WhenAll(task1, task2);
|
||||||
}
|
}
|
||||||
@@ -88,7 +94,7 @@ public class ModbusTest
|
|||||||
{
|
{
|
||||||
a.AddEasyLogger((a, b, c, d) =>
|
a.AddEasyLogger((a, b, c, d) =>
|
||||||
{
|
{
|
||||||
Debug.WriteLine($"{c}{Environment.NewLine}{d?.ToString()}");
|
_output.WriteLine($"{c}{Environment.NewLine}{d?.ToString()}");
|
||||||
}, LogLevel.Trace);
|
}, LogLevel.Trace);
|
||||||
});
|
});
|
||||||
var modbusMaster = new ModbusMaster() { ModbusType = ModbusTypeEnum.ModbusRtu, Timeout = 10000, Station = 1 };
|
var modbusMaster = new ModbusMaster() { ModbusType = ModbusTypeEnum.ModbusRtu, Timeout = 10000, Station = 1 };
|
||||||
@@ -113,12 +119,8 @@ public class ModbusTest
|
|||||||
await Task.Delay(50);
|
await Task.Delay(50);
|
||||||
var task2 = Task.Run(async () =>
|
var task2 = Task.Run(async () =>
|
||||||
{
|
{
|
||||||
var bytes = data.HexStringToBytes().GetArray();
|
SingleStreamDataHandlingAdapterTest singleStreamDataHandlingAdapterTest = new();
|
||||||
foreach (var item in bytes)
|
await singleStreamDataHandlingAdapterTest.SendCallback(data.HexStringToBytes(), (a) => singleStreamDataHandlingAdapterTest.ReceivedAsync(adapter, CancellationToken.None), 1, CancellationToken.None).ConfigureAwait(false);
|
||||||
{
|
|
||||||
var data = new ByteBlock(1); data.WriteByte(item);
|
|
||||||
await (adapter).ReceivedInputAsync(data).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
await Task.WhenAll(task1, task2);
|
await Task.WhenAll(task1, task2);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,22 +8,29 @@
|
|||||||
// QQ群:605534569
|
// QQ群:605534569
|
||||||
// ------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------
|
||||||
|
|
||||||
using System.Diagnostics;
|
|
||||||
|
|
||||||
using ThingsGateway.Foundation.Extension.String;
|
using ThingsGateway.Foundation.Extension.String;
|
||||||
using ThingsGateway.Foundation.SiemensS7;
|
using ThingsGateway.Foundation.SiemensS7;
|
||||||
using ThingsGateway.NewLife.Extension;
|
|
||||||
using ThingsGateway.NewLife.Reflection;
|
|
||||||
|
|
||||||
using TouchSocket.Core;
|
using TouchSocket.Core;
|
||||||
|
|
||||||
|
using Xunit.Abstractions;
|
||||||
|
|
||||||
namespace ThingsGateway.Foundation.Test;
|
namespace ThingsGateway.Foundation.Test;
|
||||||
|
|
||||||
public class SiemensS7Test
|
public class SiemensS7Test
|
||||||
{
|
{
|
||||||
|
|
||||||
|
public SiemensS7Test(ITestOutputHelper output)
|
||||||
|
{
|
||||||
|
_output = output;
|
||||||
|
|
||||||
|
}
|
||||||
|
private readonly ITestOutputHelper _output;
|
||||||
|
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[InlineData("M100", true, "03 00 00 1B 02 F0 80 32 03 00 00 00 03 00 02 00 06 00 00 04 01 FF 04 00 10 00 00")]
|
[InlineData("M100", true, "03 00 00 1B 02 F0 80 32 03 00 00 00 01 00 02 00 06 00 00 04 01 FF 04 00 10 00 00")]
|
||||||
[InlineData("M100", false, "03 00 00 16 02 F0 80 32 03 00 00 00 03 00 02 00 01 00 00 05 01 FF", "1", DataTypeEnum.UInt16)]
|
[InlineData("M100", false, "03 00 00 16 02 F0 80 32 03 00 00 00 01 00 02 00 01 00 00 05 01 FF", "1", DataTypeEnum.UInt16)]
|
||||||
public async Task SiemensS7_ReadWrite_OK(string address, bool read, string data, string writeData = null, DataTypeEnum dataTypeEnum = DataTypeEnum.UInt16)
|
public async Task SiemensS7_ReadWrite_OK(string address, bool read, string data, string writeData = null, DataTypeEnum dataTypeEnum = DataTypeEnum.UInt16)
|
||||||
{
|
{
|
||||||
var siemensS7Channel = new TouchSocketConfig().GetChannel(new ChannelOptions()
|
var siemensS7Channel = new TouchSocketConfig().GetChannel(new ChannelOptions()
|
||||||
@@ -34,7 +41,7 @@ public class SiemensS7Test
|
|||||||
{
|
{
|
||||||
a.AddEasyLogger((a, b, c, d) =>
|
a.AddEasyLogger((a, b, c, d) =>
|
||||||
{
|
{
|
||||||
Debug.WriteLine($"{c}{Environment.NewLine}{d?.ToString()}");
|
_output.WriteLine($"{c}{Environment.NewLine}{d?.ToString()}");
|
||||||
}, LogLevel.Trace);
|
}, LogLevel.Trace);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -57,16 +64,12 @@ public class SiemensS7Test
|
|||||||
Assert.True(result.IsSuccess, result.ToString());
|
Assert.True(result.IsSuccess, result.ToString());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
await Task.Delay(500);
|
||||||
|
|
||||||
var task2 = Task.Run(async () =>
|
var task2 = Task.Run(async () =>
|
||||||
{
|
{
|
||||||
await Task.Delay(1000).ConfigureAwait(false);
|
SingleStreamDataHandlingAdapterTest singleStreamDataHandlingAdapterTest = new();
|
||||||
var bytes = data.HexStringToBytes().GetArray();
|
await singleStreamDataHandlingAdapterTest.SendCallback(data.HexStringToBytes(), (a) => singleStreamDataHandlingAdapterTest.ReceivedAsync(adapter, CancellationToken.None), 1, CancellationToken.None).ConfigureAwait(false);
|
||||||
bytes[12] = (byte)(((IClientChannel)(siemensS7Master.Channel)).WaitHandlePool.GetValue("m_currentSign").ToInt() - 1);
|
|
||||||
foreach (var item in bytes)
|
|
||||||
{
|
|
||||||
var data = new ByteBlock(1); data.WriteByte(item);
|
|
||||||
await adapter.ReceivedInputAsync(data).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
await Task.WhenAll(task1, task2);
|
await Task.WhenAll(task1, task2);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,72 @@
|
|||||||
|
// ------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://thingsgateway.cn/
|
||||||
|
// QQ群:605534569
|
||||||
|
// ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
using System.IO.Pipelines;
|
||||||
|
|
||||||
|
using TouchSocket.Core;
|
||||||
|
|
||||||
|
namespace ThingsGateway.Foundation.Test;
|
||||||
|
|
||||||
|
public class SingleStreamDataHandlingAdapterTest
|
||||||
|
{
|
||||||
|
private readonly Pipe m_pipe = new Pipe(new PipeOptions(new SmallBlockMemoryPool(1), default, default, 1024 * 1024 * 1024, 1024 * 1024 * 512, -1));
|
||||||
|
public async Task SendCallback(ReadOnlyMemory<byte> memory, Func<int, Task> func, int bufferLength = 1, CancellationToken token = default)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var offset = 0;
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
token.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
|
var remainingLength = memory.Length - offset;
|
||||||
|
if (remainingLength <= 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
var sliceMemory = memory.Slice(offset, Math.Min(remainingLength, bufferLength));
|
||||||
|
await this.m_pipe.Writer.WriteAsync(sliceMemory, token).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||||
|
offset += sliceMemory.Length;
|
||||||
|
|
||||||
|
//await this.m_pipe.Writer.FlushAsync(token).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||||
|
|
||||||
|
await func(offset).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
await this.m_pipe.Writer.CompleteAsync().ConfigureAwait(false);
|
||||||
|
await this.m_pipe.Reader.CompleteAsync().ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public async Task ReceivedAsync(SingleStreamDataHandlingAdapter adapter, CancellationToken token)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var readResult = await this.m_pipe.Reader.ReadAsync(token).ConfigureAwait(false);
|
||||||
|
var sequence = readResult.Buffer;
|
||||||
|
var reader = new ClassBytesReader(sequence);
|
||||||
|
await adapter.ReceivedInputAsync(reader).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||||
|
var position = sequence.GetPosition(reader.BytesRead);
|
||||||
|
// 通知PipeReader已消费
|
||||||
|
this.m_pipe.Reader.AdvanceTo(position, sequence.End);
|
||||||
|
// 如果本次ReadAsync已完成或被取消,直接返回
|
||||||
|
if (readResult.IsCanceled || readResult.IsCompleted)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
// ------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://thingsgateway.cn/
|
||||||
|
// QQ群:605534569
|
||||||
|
// ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
namespace ThingsGateway.Foundation.Test;
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Buffers;
|
||||||
|
|
||||||
|
public class SmallBlockMemoryPool : MemoryPool<byte>
|
||||||
|
{
|
||||||
|
private readonly int _blockSize;
|
||||||
|
|
||||||
|
public SmallBlockMemoryPool(int blockSize)
|
||||||
|
{
|
||||||
|
_blockSize = blockSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int MaxBufferSize => _blockSize;
|
||||||
|
|
||||||
|
public override IMemoryOwner<byte> Rent(int minBufferSize = -1)
|
||||||
|
{
|
||||||
|
if (minBufferSize <= 0) minBufferSize = _blockSize;
|
||||||
|
return new SmallBlockOwner(new byte[Math.Min(minBufferSize, _blockSize)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Dispose(bool disposing) { }
|
||||||
|
|
||||||
|
private class SmallBlockOwner : IMemoryOwner<byte>
|
||||||
|
{
|
||||||
|
private readonly byte[] _array;
|
||||||
|
public SmallBlockOwner(byte[] array) => _array = array;
|
||||||
|
public Memory<byte> Memory => _array;
|
||||||
|
public void Dispose() { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -11,11 +11,31 @@
|
|||||||
using ThingsGateway.Foundation;
|
using ThingsGateway.Foundation;
|
||||||
|
|
||||||
using TouchSocket.Core;
|
using TouchSocket.Core;
|
||||||
using TouchSocket.Sockets;
|
|
||||||
|
|
||||||
internal static class TestAdapterHelper
|
internal static class TestAdapterHelper
|
||||||
{
|
{
|
||||||
public static void TestAdapter<T>(byte[] data, bool isSingleStreamData) where T : MessageBase, new()
|
|
||||||
|
public static async Task ReceivedInputAsync(SingleStreamDataHandlingAdapter adapter, ReadOnlyMemory<byte> memory, CancellationToken token)
|
||||||
|
{
|
||||||
|
var offset = 0;
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
token.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
|
var remainingLength = memory.Length - offset;
|
||||||
|
if (remainingLength <= 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
var sliceMemory = memory.Slice(offset, Math.Min(remainingLength, 1));
|
||||||
|
var reader = new ClassBytesReader(sliceMemory);
|
||||||
|
await adapter.ReceivedInputAsync(reader).ConfigureAwait(false);
|
||||||
|
offset += sliceMemory.Length;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task TestAdapter<T>(byte[] data, bool isSingleStreamData) where T : MessageBase, new()
|
||||||
{
|
{
|
||||||
bool isSuccess = false;
|
bool isSuccess = false;
|
||||||
MessageBase message = default;
|
MessageBase message = default;
|
||||||
@@ -25,7 +45,7 @@ internal static class TestAdapterHelper
|
|||||||
for (int bufferLength = 1; bufferLength < 256; bufferLength += 1)
|
for (int bufferLength = 1; bufferLength < 256; bufferLength += 1)
|
||||||
{
|
{
|
||||||
SingleStreamDataAdapterTester tester = SingleStreamDataAdapterTester.CreateTester(new DeviceSingleStreamDataHandleAdapter<T>()
|
SingleStreamDataAdapterTester tester = SingleStreamDataAdapterTester.CreateTester(new DeviceSingleStreamDataHandleAdapter<T>()
|
||||||
, bufferLength, (byteBlock, requestInfo) =>
|
, (byteBlock, requestInfo) =>
|
||||||
{
|
{
|
||||||
//此处就是接收,如果是自定义适配器,可以将requestInfo强制转换为实际对象,然后判断数据的确定性
|
//此处就是接收,如果是自定义适配器,可以将requestInfo强制转换为实际对象,然后判断数据的确定性
|
||||||
//if (byteBlock.Length != 15 || (!byteBlock.ToArray().SequenceEqual(data)))
|
//if (byteBlock.Length != 15 || (!byteBlock.ToArray().SequenceEqual(data)))
|
||||||
@@ -50,7 +70,8 @@ internal static class TestAdapterHelper
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var time = tester.Run(data, 2, 2, 1000 * 10);
|
using var cts = new CancellationTokenSource(1000 * 10);
|
||||||
|
var time = await tester.RunAsync(data, 2, 2, bufferLength, cts.Token).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -78,7 +99,7 @@ internal static class TestAdapterHelper
|
|||||||
});
|
});
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var time = tester.Run(data, 2, 2, 1000 * 10);
|
var time = await tester.RunAsync(data, 2, 2, 1000 * 10).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,35 +1,36 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>net9.0</TargetFrameworks>
|
<TargetFrameworks>net9.0</TargetFrameworks>
|
||||||
<IsPackable>false</IsPackable>
|
<IsPackable>false</IsPackable>
|
||||||
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
</PropertyGroup>
|
||||||
<PackageReference Include="coverlet.collector" Version="6.0.4">
|
|
||||||
<PrivateAssets>all</PrivateAssets>
|
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
|
||||||
</PackageReference>
|
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
|
|
||||||
<PackageReference Include="xunit" Version="2.9.3" />
|
|
||||||
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.3">
|
|
||||||
<PrivateAssets>all</PrivateAssets>
|
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
|
||||||
</PackageReference>
|
|
||||||
<ProjectReference Include="..\ThingsGateway.Foundation.Dlt645\ThingsGateway.Foundation.Dlt645.csproj" />
|
|
||||||
|
|
||||||
|
|
||||||
<ProjectReference Include="..\ThingsGateway.Foundation.Modbus\ThingsGateway.Foundation.Modbus.csproj" />
|
<ItemGroup>
|
||||||
|
<PackageReference Include="coverlet.collector" Version="6.0.4">
|
||||||
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
|
</PackageReference>
|
||||||
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
|
||||||
|
<PackageReference Include="xunit" Version="2.9.3" />
|
||||||
|
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.4">
|
||||||
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
|
</PackageReference>
|
||||||
|
<ProjectReference Include="..\ThingsGateway.Foundation.Dlt645\ThingsGateway.Foundation.Dlt645.csproj" />
|
||||||
|
<ProjectReference Include="..\ThingsGateway.Foundation.Modbus\ThingsGateway.Foundation.Modbus.csproj" />
|
||||||
|
|
||||||
|
<ProjectReference Include="..\ThingsGateway.Foundation.SiemensS7\ThingsGateway.Foundation.SiemensS7.csproj" />
|
||||||
|
|
||||||
|
|
||||||
<ProjectReference Include="..\ThingsGateway.Foundation.SiemensS7\ThingsGateway.Foundation.SiemensS7.csproj" />
|
|
||||||
|
|
||||||
|
</ItemGroup>
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<Using Include="Xunit" />
|
|
||||||
</ItemGroup>
|
<ItemGroup>
|
||||||
|
<Using Include="Xunit" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
// ------------------------------------------------------------------------------
|
||||||
|
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||||
|
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||||
|
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||||
|
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||||
|
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||||
|
// 使用文档:https://thingsgateway.cn/
|
||||||
|
// QQ群:605534569
|
||||||
|
// ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
using System.Net;
|
||||||
|
|
||||||
|
using TouchSocket.Core;
|
||||||
|
|
||||||
|
namespace ThingsGateway.Foundation.Test;
|
||||||
|
|
||||||
|
public class UdpDataHandlingAdapterTest
|
||||||
|
{
|
||||||
|
public async Task SendCallback(UdpDataHandlingAdapter adapter, ReadOnlyMemory<byte> memory, CancellationToken token = default)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await ReceivedAsync(IPEndPoint.Parse("127.0.0.1:502"), adapter, memory, token).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public async Task ReceivedAsync(IPEndPoint iPEndPoint, UdpDataHandlingAdapter adapter, ReadOnlyMemory<byte> memory, CancellationToken token)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await adapter.ReceivedInputAsync(iPEndPoint, memory).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -27,19 +27,19 @@
|
|||||||
<Target Name="IncludeAllFilesInTargetDir" AfterTargets="Build">
|
<Target Name="IncludeAllFilesInTargetDir" AfterTargets="Build">
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Content Include="$(ProjectDir)bin\$(Configuration)\$(TargetFramework)\**\*Plugin.DB*.dll">
|
<Content Include="$(ProjectDir)$(OutputPath)\$(TargetFramework)\**\*Plugin.DB*.dll">
|
||||||
<Pack>true</Pack>
|
<Pack>true</Pack>
|
||||||
<PackagePath>Content</PackagePath>
|
<PackagePath>Content</PackagePath>
|
||||||
</Content>
|
</Content>
|
||||||
<Content Include="$(ProjectDir)bin\$(Configuration)\$(TargetFramework)\**\*TDengine*.dll">
|
<Content Include="$(ProjectDir)$(OutputPath)\$(TargetFramework)\**\*TDengine*.dll">
|
||||||
<Pack>true</Pack>
|
<Pack>true</Pack>
|
||||||
<PackagePath>Content</PackagePath>
|
<PackagePath>Content</PackagePath>
|
||||||
</Content>
|
</Content>
|
||||||
<Content Include="$(ProjectDir)bin\$(Configuration)\$(TargetFramework)\**\*QuestDb*.dll">
|
<Content Include="$(ProjectDir)$(OutputPath)\$(TargetFramework)\**\*QuestDb*.dll">
|
||||||
<Pack>true</Pack>
|
<Pack>true</Pack>
|
||||||
<PackagePath>Content</PackagePath>
|
<PackagePath>Content</PackagePath>
|
||||||
</Content>
|
</Content>
|
||||||
<Content Include="$(ProjectDir)bin\$(Configuration)\$(TargetFramework)\**\*CsvHelper*.dll">
|
<Content Include="$(ProjectDir)$(OutputPath)\$(TargetFramework)\**\*CsvHelper*.dll">
|
||||||
<Pack>true</Pack>
|
<Pack>true</Pack>
|
||||||
<PackagePath>Content</PackagePath>
|
<PackagePath>Content</PackagePath>
|
||||||
</Content>
|
</Content>
|
||||||
|
|||||||
@@ -3,8 +3,10 @@
|
|||||||
<Import Project="..\..\PackNuget.props" />
|
<Import Project="..\..\PackNuget.props" />
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>net8.0;</TargetFrameworks>
|
<TargetFrameworks>net8.0;</TargetFrameworks>
|
||||||
|
|
||||||
|
<OutputPath>bin\$(Configuration)</OutputPath>
|
||||||
|
<IntermediateOutputPath>obj\$(Configuration)\</IntermediateOutputPath>
|
||||||
|
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -1,16 +1,21 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk.Razor">
|
<Project Sdk="Microsoft.NET.Sdk.Razor">
|
||||||
|
|
||||||
<Import Project="..\..\Version.props" />
|
<Import Project="..\..\Version.props" />
|
||||||
<Import Project="..\..\PackNuget.props" />
|
<Import Project="..\..\PackNuget.props" />
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>net8.0;</TargetFrameworks>
|
<TargetFrameworks>net8.0;</TargetFrameworks>
|
||||||
|
|
||||||
<DefineConstants>$(DefineConstants);Management</DefineConstants>
|
<DefineConstants>$(DefineConstants);Management</DefineConstants>
|
||||||
|
|
||||||
<OutputPath>bin\$(Configuration)\Management\</OutputPath>
|
<OutputPath>bin\$(Configuration)\Management</OutputPath>
|
||||||
<IntermediateOutputPath>obj\$(Configuration)\Management\$(TargetFramework)\</IntermediateOutputPath>
|
<IntermediateOutputPath>obj\Management\$(Configuration)\</IntermediateOutputPath>
|
||||||
|
<!--<BaseIntermediateOutputPath>obj\Management\</BaseIntermediateOutputPath>
|
||||||
|
<MSBuildProjectExtensionsPath>$(BaseIntermediateOutputPath)</MSBuildProjectExtensionsPath>-->
|
||||||
<PackageOutputPath>$(MSBuildThisFileDirectory)..\..\..\..\nupkgs1</PackageOutputPath>
|
<PackageOutputPath>$(MSBuildThisFileDirectory)..\..\..\..\nupkgs1</PackageOutputPath>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<AssemblyName>ThingsGateway.Plugin.DB</AssemblyName>
|
<AssemblyName>ThingsGateway.Plugin.DB</AssemblyName>
|
||||||
@@ -22,7 +27,9 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\..\Foundation\ThingsGateway.Foundation.Razor\ThingsGateway.Foundation.Razor.csproj">
|
<ProjectReference Include="..\..\Foundation\ThingsGateway.Foundation.Razor\ThingsGateway.Foundation.Razor.csproj">
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
<ProjectReference Include="..\..\PluginPro\ThingsGateway.Management.Razor\ThingsGateway.Management.Razor.csproj" />
|
<ProjectReference Include="..\..\PluginPro\ThingsGateway.Management.Razor\ThingsGateway.Management.Razor.csproj" >
|
||||||
|
<PrivateAssets>contentFiles;compile;build;buildMultitargeting;buildTransitive;analyzers;</PrivateAssets>
|
||||||
|
</ProjectReference>
|
||||||
|
|
||||||
<PackageReference Include="Riok.Mapperly" Version="4.2.1" ExcludeAssets="runtime" PrivateAssets="all" />
|
<PackageReference Include="Riok.Mapperly" Version="4.2.1" ExcludeAssets="runtime" PrivateAssets="all" />
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
<Target Name="IncludeAllFilesInTargetDir" AfterTargets="Build">
|
<Target Name="IncludeAllFilesInTargetDir" AfterTargets="Build">
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Content Include="$(ProjectDir)bin\$(Configuration)\$(TargetFramework)\**\*Dlt645*.dll">
|
<Content Include="$(ProjectDir)$(OutputPath)\$(TargetFramework)\**\*Dlt645*.dll">
|
||||||
<Pack>true</Pack>
|
<Pack>true</Pack>
|
||||||
<PackagePath>Content</PackagePath>
|
<PackagePath>Content</PackagePath>
|
||||||
</Content>
|
</Content>
|
||||||
|
|||||||
@@ -3,7 +3,9 @@
|
|||||||
<Import Project="..\..\PackNuget.props" />
|
<Import Project="..\..\PackNuget.props" />
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>net8.0;</TargetFrameworks>
|
<TargetFrameworks>net8.0;</TargetFrameworks>
|
||||||
|
<OutputPath>bin\$(Configuration)</OutputPath>
|
||||||
|
<IntermediateOutputPath>obj\$(Configuration)\</IntermediateOutputPath>
|
||||||
|
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\..\Foundation\ThingsGateway.Foundation.Razor\ThingsGateway.Foundation.Razor.csproj">
|
<ProjectReference Include="..\..\Foundation\ThingsGateway.Foundation.Razor\ThingsGateway.Foundation.Razor.csproj">
|
||||||
|
|||||||
@@ -6,8 +6,10 @@
|
|||||||
|
|
||||||
|
|
||||||
<DefineConstants>$(DefineConstants);Management</DefineConstants>
|
<DefineConstants>$(DefineConstants);Management</DefineConstants>
|
||||||
<OutputPath>bin\$(Configuration)\Management\</OutputPath>
|
<OutputPath>bin\$(Configuration)\Management</OutputPath>
|
||||||
<IntermediateOutputPath>obj\$(Configuration)\Management\$(TargetFramework)\</IntermediateOutputPath>
|
<IntermediateOutputPath>obj\Management\$(Configuration)\</IntermediateOutputPath>
|
||||||
|
<!--<BaseIntermediateOutputPath>obj\Management\</BaseIntermediateOutputPath>
|
||||||
|
<MSBuildProjectExtensionsPath>$(BaseIntermediateOutputPath)</MSBuildProjectExtensionsPath>-->
|
||||||
<PackageOutputPath>$(MSBuildThisFileDirectory)..\..\..\..\nupkgs1</PackageOutputPath>
|
<PackageOutputPath>$(MSBuildThisFileDirectory)..\..\..\..\nupkgs1</PackageOutputPath>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
@@ -21,7 +23,9 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\..\Foundation\ThingsGateway.Foundation.Razor\ThingsGateway.Foundation.Razor.csproj">
|
<ProjectReference Include="..\..\Foundation\ThingsGateway.Foundation.Razor\ThingsGateway.Foundation.Razor.csproj">
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
<ProjectReference Include="..\..\PluginPro\ThingsGateway.Management.Razor\ThingsGateway.Management.Razor.csproj" />
|
<ProjectReference Include="..\..\PluginPro\ThingsGateway.Management.Razor\ThingsGateway.Management.Razor.csproj" >
|
||||||
|
<PrivateAssets>contentFiles;compile;build;buildMultitargeting;buildTransitive;analyzers;</PrivateAssets>
|
||||||
|
</ProjectReference>
|
||||||
|
|
||||||
<ProjectReference Include="..\ThingsGateway.Foundation.Dlt645\ThingsGateway.Foundation.Dlt645.csproj">
|
<ProjectReference Include="..\ThingsGateway.Foundation.Dlt645\ThingsGateway.Foundation.Dlt645.csproj">
|
||||||
<PrivateAssets>contentFiles;compile;build;buildMultitargeting;buildTransitive;analyzers;</PrivateAssets>
|
<PrivateAssets>contentFiles;compile;build;buildMultitargeting;buildTransitive;analyzers;</PrivateAssets>
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user