mirror of
https://gitee.com/ThingsGateway/ThingsGateway.git
synced 2025-10-20 18:51:28 +08:00
添加Modbus系列插件调试页面;添加Modbus组包解析缓存超时时间;
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
using System.Threading;
|
||||
using System.ComponentModel;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using ThingsGateway.Foundation.Serial;
|
||||
@@ -18,10 +19,13 @@ namespace ThingsGateway.Foundation.Adapter.Modbus
|
||||
waitingClient = SerialClient.GetTGWaitingClient(new());
|
||||
}
|
||||
private IWaitingClient<SerialClient> waitingClient;
|
||||
|
||||
[Description("Crc校验")]
|
||||
public bool Crc16CheckEnable { get; set; }
|
||||
[Description("帧前时间")]
|
||||
public int FrameTime { get; set; }
|
||||
|
||||
[Description("组包缓存时间")]
|
||||
public double CacheTimeout { get; set; } = 1;
|
||||
[Description("站号")]
|
||||
public byte Station { get; set; } = 1;
|
||||
public override async Task<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken token = default)
|
||||
{
|
||||
@@ -71,7 +75,7 @@ namespace ThingsGateway.Foundation.Adapter.Modbus
|
||||
{
|
||||
DataHandleAdapter = new();
|
||||
DataHandleAdapter.Crc16CheckEnable = Crc16CheckEnable;
|
||||
DataHandleAdapter.Crc16CheckEnable = Crc16CheckEnable;
|
||||
DataHandleAdapter.CacheTimeout = TimeSpan.FromSeconds(CacheTimeout);
|
||||
SerialClient.SetDataHandlingAdapter(DataHandleAdapter);
|
||||
}
|
||||
public override async Task<OperResult> WriteAsync(string address, byte[] value, CancellationToken token = default)
|
||||
|
@@ -20,6 +20,7 @@ namespace ThingsGateway.Foundation.Adapter.Modbus
|
||||
|
||||
public byte Station { get; set; } = 1;
|
||||
public int FrameTime { get; set; }
|
||||
public double CacheTimeout { get; set; } = 1;
|
||||
private async Task<ResponsedData> SendThenReturnAsync(OperResult<byte[]> commandResult, CancellationToken token)
|
||||
{
|
||||
try
|
||||
@@ -68,6 +69,7 @@ namespace ThingsGateway.Foundation.Adapter.Modbus
|
||||
{
|
||||
DataHandleAdapter = new();
|
||||
DataHandleAdapter.Crc16CheckEnable = Crc16CheckEnable;
|
||||
DataHandleAdapter.CacheTimeout = TimeSpan.FromSeconds(CacheTimeout);
|
||||
TGTcpClient.SetDataHandlingAdapter(DataHandleAdapter);
|
||||
}
|
||||
public override async Task<OperResult> WriteAsync(string address, byte[] value, CancellationToken token = default)
|
||||
|
@@ -1,4 +1,5 @@
|
||||
using System.Threading;
|
||||
using System.ComponentModel;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using TouchSocket.Resources;
|
||||
@@ -16,10 +17,16 @@ namespace ThingsGateway.Foundation.Adapter.Modbus
|
||||
waitingClient = TGTcpClient.GetTGWaitingClient(new());
|
||||
}
|
||||
private IWaitingClient<TGTcpClient> waitingClient;
|
||||
|
||||
[Description("检测事务标识符")]
|
||||
public bool IsCheckMessageId { get; set; }
|
||||
|
||||
[Description("站号")]
|
||||
public byte Station { get; set; } = 1;
|
||||
[Description("帧前时间")]
|
||||
public int FrameTime { get; set; }
|
||||
[Description("组包缓存时间")]
|
||||
public double CacheTimeout { get; set; } = 1;
|
||||
private async Task<ResponsedData> SendThenReturnAsync(OperResult<byte[]> commandResult, CancellationToken token)
|
||||
{
|
||||
try
|
||||
@@ -69,6 +76,7 @@ namespace ThingsGateway.Foundation.Adapter.Modbus
|
||||
{
|
||||
DataHandleAdapter = new();
|
||||
DataHandleAdapter.IsCheckMessageId = IsCheckMessageId;
|
||||
DataHandleAdapter.CacheTimeout = TimeSpan.FromSeconds(CacheTimeout);
|
||||
TGTcpClient.SetDataHandlingAdapter(DataHandleAdapter);
|
||||
}
|
||||
public override async Task<OperResult> WriteAsync(string address, byte[] value, CancellationToken token = default)
|
||||
|
@@ -302,13 +302,13 @@ namespace ThingsGateway.Foundation.Adapter.Siemens
|
||||
var result1 = SendThenResponse(ISO_CR);
|
||||
if (!result1.IsSuccess)
|
||||
{
|
||||
Logger?.Error(client.GetIPPort() + "ISO初始化失败");
|
||||
Logger?.Error(client.IP + ":" + client.Port + "ISO初始化失败");
|
||||
return;
|
||||
}
|
||||
var result2 = SendThenResponse(S7_PN);
|
||||
if (!result2.IsSuccess)
|
||||
{
|
||||
Logger?.Error(client.GetIPPort() + "初始化失败");
|
||||
Logger?.Error(client.IP + ":" + client.Port + "初始化失败");
|
||||
return;
|
||||
}
|
||||
pdu_length = ThingsGatewayBitConverter.ToUInt16(result2.Content.SelectLast(2), 0);
|
||||
|
@@ -83,6 +83,7 @@ public class ModbusRtu : CollectBase, IDisposable
|
||||
_plc = new((SerialClient)client);
|
||||
_plc.Crc16CheckEnable = driverPropertys.Crc16CheckEnable;
|
||||
_plc.FrameTime = driverPropertys.FrameTime;
|
||||
_plc.CacheTimeout = driverPropertys.CacheTimeout;
|
||||
_plc.DataFormat = driverPropertys.DataFormat;
|
||||
_plc.Station = driverPropertys.Station;
|
||||
_plc.TimeOut = driverPropertys.TimeOut;
|
||||
@@ -109,7 +110,8 @@ public class ModbusRtuProperty : CollectDriverPropertyBase
|
||||
|
||||
[DeviceProperty("帧前时间", "某些设备性能较弱,报文间需要间隔较长时间")]
|
||||
public int FrameTime { get; set; } = 0;
|
||||
|
||||
[DeviceProperty("组包缓存超时", "某些设备性能较弱,报文间需要间隔较长时间,可以设置更长的组包缓存,默认1s")]
|
||||
public double CacheTimeout { get; set; } = 1;
|
||||
[DeviceProperty("共享链路", "")]
|
||||
public override bool IsShareChannel { get; set; } = false;
|
||||
|
||||
|
@@ -1,32 +1,145 @@
|
||||
@using BlazorComponent;
|
||||
@using Microsoft.AspNetCore.Components.Web;
|
||||
@using Microsoft.JSInterop;
|
||||
@using ThingsGateway.Core;
|
||||
@using ThingsGateway.Foundation.Adapter.Modbus;
|
||||
@using ThingsGateway.Foundation;
|
||||
@using ThingsGateway.Foundation.Extension;
|
||||
@using ThingsGateway.Foundation.Serial;
|
||||
@using ThingsGateway.Web.Foundation;
|
||||
@using Masa.Blazor
|
||||
@inherits DriverDebugUIBase
|
||||
<MCard Class="ma-0">
|
||||
<MCardTitle Class="indigo white--text text-h6">
|
||||
@nameof(ModbusRtu)未完成
|
||||
</MCardTitle>
|
||||
<MRow Class="pa-4"
|
||||
Justify="JustifyTypes.SpaceBetween">
|
||||
<SerialClientPage @ref=serialClientPage></SerialClientPage>
|
||||
<SerialClientPage @ref=serialClientPage></SerialClientPage>
|
||||
@if (_plc != null)
|
||||
{
|
||||
<MCard Flat Elevation="0">
|
||||
<MRow Class="my-1" NoGutters Justify="JustifyTypes.SpaceBetween" Align="AlignTypes.Center">
|
||||
<MCheckbox Class="mx-1" Label=@(_plc.Description(x => x.Crc16CheckEnable)) Dense Outlined HideDetails="@("auto")" @bind-Value=@_plc.Crc16CheckEnable></MCheckbox>
|
||||
<MTextField Class="mx-1" Label=@(_plc.Description(x => x.FrameTime)) Dense Outlined HideDetails="@("auto")" @bind-Value=@_plc.FrameTime></MTextField>
|
||||
<MTextField Class="mx-1" Label=@(_plc.Description(x => x.CacheTimeout)) Dense Outlined HideDetails="@("auto")" @bind-Value=@_plc.CacheTimeout></MTextField>
|
||||
|
||||
<MDivider Vertical></MDivider>
|
||||
<MTextField Class="mx-1" Label=@(_plc.Description(x => x.Station)) Dense Outlined HideDetails="@("auto")" @bind-Value=@_plc.Station></MTextField>
|
||||
<MTextField Class="mx-1" Label=@(_plc.Description(x => x.TimeOut)) Dense Outlined HideDetails="@("auto")" @bind-Value=@_plc.TimeOut></MTextField>
|
||||
<MSelect Class="mx-1" Style="max-width:200px" @bind-Value="_plc.DataFormat" Outlined Label="@(_plc.Description(x => x.DataFormat))"
|
||||
Items=@(typeof(DataFormat).GetEnumList())
|
||||
MenuProps="@(props => { props.Auto = true; props.OffsetY = true; })"
|
||||
ItemText=@((u) =>u.des)
|
||||
ItemValue=@(u =>(DataFormat)u.value)
|
||||
HideDetails=@("auto") Height="30"
|
||||
Dense>
|
||||
</MSelect>
|
||||
</MRow>
|
||||
</MCard>
|
||||
|
||||
}
|
||||
<MCard Class="pa-4" Flat Elevation="0">
|
||||
<MRow Class="my-1" NoGutters>
|
||||
|
||||
<MCol Md="4">
|
||||
<MCard Flat Elevation="0">
|
||||
<MCol Class="my-1 py-1">
|
||||
<MTextField Class="mx-1 my-1" Label="变量地址" Dense Outlined HideDetails="@("auto")" @bind-Value=@address />
|
||||
<MTextField Class="mx-1 my-1" Label="长度" Dense Outlined HideDetails="@("auto")" @bind-Value=@length />
|
||||
<MSelect Class="mx-1 my-1" Style="max-width:200px" @bind-Value="dataTypeEnum" Outlined Label="数据类型"
|
||||
Items=@(typeof(DataTypeEnum).GetEnumList())
|
||||
MenuProps="@(props => { props.Auto = true; props.OffsetY = true; })"
|
||||
ItemText=@((u) =>u.des)
|
||||
ItemValue=@(u =>(DataTypeEnum)u.value)
|
||||
HideDetails=@("auto") Height="30"
|
||||
Dense>
|
||||
</MSelect>
|
||||
|
||||
<MButton Class="mx-1 my-1" Color="primary" OnClick="Read">
|
||||
读取
|
||||
</MButton>
|
||||
<MTextField Class="mx-1 mt-3 my-1" Label="值" Dense Outlined HideDetails="@("auto")" @bind-Value=@writeValue />
|
||||
<MButton Class="mx-1 my-1" Color="primary" OnClick="Write">
|
||||
写入
|
||||
</MButton>
|
||||
|
||||
</MCol>
|
||||
</MCard>
|
||||
</MCol>
|
||||
|
||||
<MCol Md="8">
|
||||
|
||||
|
||||
<MCard Height=@("calc(100vh - 420px)") Style="overflow-y:auto;width:100%" Elevation="0" Flat Class="ml-4">
|
||||
<MCardActions>
|
||||
输出日志
|
||||
<MSpacer></MSpacer>
|
||||
|
||||
<MTooltip Bottom Context="tip">
|
||||
<ActivatorContent>
|
||||
<MButton Loading=isDownExport Class="mx-2" @attributes="@tip.Attrs" Dark Fab Small
|
||||
OnClick=@(async()=>
|
||||
{
|
||||
await DownDeviceMessageExport(Messages.Select(a=>a.message));
|
||||
}
|
||||
)>
|
||||
<MIcon>mdi-export</MIcon>
|
||||
</MButton>
|
||||
</ActivatorContent>
|
||||
<ChildContent>
|
||||
<span>导出</span>
|
||||
</ChildContent>
|
||||
</MTooltip>
|
||||
|
||||
</MCardActions>
|
||||
@{
|
||||
var item = Messages;
|
||||
<MRow Class="ml-2 mr-2 d-flex" NoGutters>
|
||||
<MVirtualScroll Context="itemMessage" Height=@("calc(100vh - 500px)") OverscanCount=2 ItemSize="100" Items="item.OrderByDescending(a=>a.id).ToList()">
|
||||
<ItemContent>
|
||||
<div class=@(itemMessage.isRed==null?"black--text":itemMessage.isRed==true?"red--text":"green--text")>
|
||||
@itemMessage.message
|
||||
</div>
|
||||
|
||||
</ItemContent>
|
||||
</MVirtualScroll>
|
||||
</MRow>
|
||||
}
|
||||
</MCard>
|
||||
</MCol>
|
||||
|
||||
</MRow>
|
||||
|
||||
|
||||
</MCard>
|
||||
|
||||
|
||||
@code
|
||||
{
|
||||
private SerialClientPage serialClientPage;
|
||||
protected override void OnInitialized()
|
||||
private ThingsGateway.Foundation.Adapter.Modbus.ModbusRtu _plc
|
||||
{
|
||||
get
|
||||
{
|
||||
return plc as ThingsGateway.Foundation.Adapter.Modbus.ModbusRtu;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnAfterRender(bool firstRender)
|
||||
{
|
||||
if (firstRender)
|
||||
{
|
||||
serialClientPage.LogAction = LogOut;
|
||||
//载入配置
|
||||
plc = new ThingsGateway.Foundation.Adapter.Modbus.ModbusRtu((SerialClient)serialClientPage.GetSerialClient());
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
base.OnAfterRender(firstRender);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
plc.SafeDispose();
|
||||
serialClientPage.SafeDispose();
|
||||
base.Dispose();
|
||||
}
|
||||
public override string ToString()
|
||||
{
|
||||
return nameof(ModbusRtu);
|
||||
}
|
||||
}
|
@@ -16,7 +16,7 @@ public class ModbusRtuOverTcp : CollectBase, IDisposable
|
||||
}
|
||||
public override CollectDriverPropertyBase DriverPropertys => driverPropertys;
|
||||
public override IThingsGatewayBitConverter ThingsGatewayBitConverter { get => _plc?.ThingsGatewayBitConverter; }
|
||||
|
||||
public override Type DriverDebugUIType => typeof(ModbusRtuOverTcpDebugDriverPage);
|
||||
public override void AfterStop()
|
||||
{
|
||||
_plc?.Disconnect();
|
||||
@@ -69,6 +69,7 @@ public class ModbusRtuOverTcp : CollectBase, IDisposable
|
||||
_plc = new((TGTcpClient)client);
|
||||
_plc.Crc16CheckEnable = driverPropertys.Crc16CheckEnable;
|
||||
_plc.FrameTime = driverPropertys.FrameTime;
|
||||
_plc.CacheTimeout = driverPropertys.CacheTimeout;
|
||||
_plc.DataFormat = driverPropertys.DataFormat;
|
||||
_plc.ConnectTimeOut = driverPropertys.ConnectTimeOut;
|
||||
_plc.Station = driverPropertys.Station;
|
||||
@@ -104,7 +105,8 @@ public class ModbusRtuOverTcpProperty : CollectDriverPropertyBase
|
||||
|
||||
[DeviceProperty("帧前时间", "某些设备性能较弱,报文间需要间隔较长时间")]
|
||||
public int FrameTime { get; set; } = 0;
|
||||
|
||||
[DeviceProperty("组包缓存超时", "某些设备性能较弱,报文间需要间隔较长时间,可以设置更长的组包缓存,默认1s")]
|
||||
public double CacheTimeout { get; set; } = 1;
|
||||
[DeviceProperty("共享链路", "")]
|
||||
public override bool IsShareChannel { get; set; } = false;
|
||||
public override ShareChannelEnum ShareChannel => ShareChannelEnum.TcpClient;
|
||||
|
@@ -0,0 +1,145 @@
|
||||
@using BlazorComponent;
|
||||
@using Microsoft.AspNetCore.Components.Web;
|
||||
@using Microsoft.JSInterop;
|
||||
@using ThingsGateway.Core;
|
||||
@using ThingsGateway.Foundation.Adapter.Modbus;
|
||||
@using ThingsGateway.Foundation;
|
||||
@using ThingsGateway.Foundation.Extension;
|
||||
@using ThingsGateway.Foundation.Serial;
|
||||
@using ThingsGateway.Web.Foundation;
|
||||
@using Masa.Blazor
|
||||
@inherits DriverDebugUIBase
|
||||
<TcpClientPage @ref=tcpClientPage></TcpClientPage>
|
||||
@if (_plc != null)
|
||||
{
|
||||
<MCard Flat Elevation="0">
|
||||
<MRow Class="my-1" NoGutters Justify="JustifyTypes.SpaceBetween" Align="AlignTypes.Center">
|
||||
<MCheckbox Class="mx-1" Label=@(_plc.Description(x => x.Crc16CheckEnable)) Dense Outlined HideDetails="@("auto")" @bind-Value=@_plc.Crc16CheckEnable></MCheckbox>
|
||||
<MTextField Class="mx-1" Label=@(_plc.Description(x => x.FrameTime)) Dense Outlined HideDetails="@("auto")" @bind-Value=@_plc.FrameTime></MTextField>
|
||||
<MTextField Class="mx-1" Label=@(_plc.Description(x => x.CacheTimeout)) Dense Outlined HideDetails="@("auto")" @bind-Value=@_plc.CacheTimeout></MTextField>
|
||||
|
||||
<MTextField Class="mx-1" Label=@(_plc.Description(x => x.Station)) Dense Outlined HideDetails="@("auto")" @bind-Value=@_plc.Station></MTextField>
|
||||
<MTextField Class="mx-1" Label=@(_plc.Description(x => x.TimeOut)) Dense Outlined HideDetails="@("auto")" @bind-Value=@_plc.TimeOut></MTextField>
|
||||
<MSelect Class="mx-1" Style="max-width:200px" @bind-Value="_plc.DataFormat" Outlined Label="@(_plc.Description(x => x.DataFormat))"
|
||||
Items=@(typeof(DataFormat).GetEnumList())
|
||||
MenuProps="@(props => { props.Auto = true; props.OffsetY = true; })"
|
||||
ItemText=@((u) =>u.des)
|
||||
ItemValue=@(u =>(DataFormat)u.value)
|
||||
HideDetails=@("auto") Height="30"
|
||||
Dense>
|
||||
</MSelect>
|
||||
</MRow>
|
||||
</MCard>
|
||||
|
||||
}
|
||||
<MCard Class="pa-4" Flat Elevation="0">
|
||||
<MRow Class="my-1" NoGutters>
|
||||
|
||||
<MCol Md="4">
|
||||
<MCard Flat Elevation="0">
|
||||
<MCol Class="my-1 py-1">
|
||||
<MTextField Class="mx-1 my-1" Label="变量地址" Dense Outlined HideDetails="@("auto")" @bind-Value=@address />
|
||||
<MTextField Class="mx-1 my-1" Label="长度" Dense Outlined HideDetails="@("auto")" @bind-Value=@length />
|
||||
<MSelect Class="mx-1 my-1" Style="max-width:200px" @bind-Value="dataTypeEnum" Outlined Label="数据类型"
|
||||
Items=@(typeof(DataTypeEnum).GetEnumList())
|
||||
MenuProps="@(props => { props.Auto = true; props.OffsetY = true; })"
|
||||
ItemText=@((u) =>u.des)
|
||||
ItemValue=@(u =>(DataTypeEnum)u.value)
|
||||
HideDetails=@("auto") Height="30"
|
||||
Dense>
|
||||
</MSelect>
|
||||
|
||||
<MButton Class="mx-1 my-1" Color="primary" OnClick="Read">
|
||||
读取
|
||||
</MButton>
|
||||
<MTextField Class="mx-1 mt-3 my-1" Label="值" Dense Outlined HideDetails="@("auto")" @bind-Value=@writeValue />
|
||||
<MButton Class="mx-1 my-1" Color="primary" OnClick="Write">
|
||||
写入
|
||||
</MButton>
|
||||
|
||||
</MCol>
|
||||
</MCard>
|
||||
</MCol>
|
||||
|
||||
<MCol Md="8">
|
||||
|
||||
|
||||
<MCard Height=@("calc(100vh - 420px)") Style="overflow-y:auto;width:100%" Elevation="0" Flat Class="ml-4">
|
||||
<MCardActions>
|
||||
输出日志
|
||||
<MSpacer></MSpacer>
|
||||
|
||||
<MTooltip Bottom Context="tip">
|
||||
<ActivatorContent>
|
||||
<MButton Loading=isDownExport Class="mx-2" @attributes="@tip.Attrs" Dark Fab Small
|
||||
OnClick=@(async()=>
|
||||
{
|
||||
await DownDeviceMessageExport(Messages.Select(a=>a.message));
|
||||
}
|
||||
)>
|
||||
<MIcon>mdi-export</MIcon>
|
||||
</MButton>
|
||||
</ActivatorContent>
|
||||
<ChildContent>
|
||||
<span>导出</span>
|
||||
</ChildContent>
|
||||
</MTooltip>
|
||||
|
||||
</MCardActions>
|
||||
@{
|
||||
var item = Messages;
|
||||
<MRow Class="ml-2 mr-2 d-flex" NoGutters>
|
||||
<MVirtualScroll Context="itemMessage" Height=@("calc(100vh - 500px)") OverscanCount=2 ItemSize="100" Items="item.OrderByDescending(a=>a.id).ToList()">
|
||||
<ItemContent>
|
||||
<div class=@(itemMessage.isRed==null?"black--text":itemMessage.isRed==true?"red--text":"green--text")>
|
||||
@itemMessage.message
|
||||
</div>
|
||||
|
||||
</ItemContent>
|
||||
</MVirtualScroll>
|
||||
</MRow>
|
||||
}
|
||||
</MCard>
|
||||
</MCol>
|
||||
|
||||
</MRow>
|
||||
|
||||
</MCard>
|
||||
|
||||
|
||||
@code
|
||||
{
|
||||
private TcpClientPage tcpClientPage;
|
||||
private ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverTcp _plc
|
||||
{
|
||||
get
|
||||
{
|
||||
return plc as ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverTcp;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnAfterRender(bool firstRender)
|
||||
{
|
||||
if (firstRender)
|
||||
{
|
||||
tcpClientPage.LogAction = LogOut;
|
||||
//载入配置
|
||||
plc = new ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverTcp((TGTcpClient)tcpClientPage.GetTGTcpClient());
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
base.OnAfterRender(firstRender);
|
||||
}
|
||||
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
plc.SafeDispose();
|
||||
tcpClientPage.SafeDispose();
|
||||
base.Dispose();
|
||||
}
|
||||
public override string ToString()
|
||||
{
|
||||
return nameof(ModbusRtuOverTcp);
|
||||
}
|
||||
}
|
@@ -14,6 +14,7 @@ public class ModbusRtuOverUdp : CollectBase
|
||||
public ModbusRtuOverUdp(IServiceScopeFactory scopeFactory) : base(scopeFactory)
|
||||
{
|
||||
}
|
||||
public override Type DriverDebugUIType => typeof(ModbusRtuOverUdpDebugDriverPage);
|
||||
|
||||
public override CollectDriverPropertyBase DriverPropertys => driverPropertys;
|
||||
|
||||
|
@@ -0,0 +1,144 @@
|
||||
@using BlazorComponent;
|
||||
@using Microsoft.AspNetCore.Components.Web;
|
||||
@using Microsoft.JSInterop;
|
||||
@using ThingsGateway.Core;
|
||||
@using ThingsGateway.Foundation.Adapter.Modbus;
|
||||
@using ThingsGateway.Foundation;
|
||||
@using ThingsGateway.Foundation.Extension;
|
||||
@using ThingsGateway.Foundation.Serial;
|
||||
@using ThingsGateway.Web.Foundation;
|
||||
@using Masa.Blazor
|
||||
@inherits DriverDebugUIBase
|
||||
<UdpSessionPage @ref=udpClientPage></UdpSessionPage>
|
||||
@if (_plc != null)
|
||||
{
|
||||
<MCard Flat Elevation="0">
|
||||
<MRow Class="my-1" NoGutters Justify="JustifyTypes.SpaceBetween" Align="AlignTypes.Center">
|
||||
<MCheckbox Class="mx-1" Label=@(_plc.Description(x => x.Crc16CheckEnable)) Dense Outlined HideDetails="@("auto")" @bind-Value=@_plc.Crc16CheckEnable></MCheckbox>
|
||||
<MTextField Class="mx-1" Label=@(_plc.Description(x => x.FrameTime)) Dense Outlined HideDetails="@("auto")" @bind-Value=@_plc.FrameTime></MTextField>
|
||||
|
||||
<MTextField Class="mx-1" Label=@(_plc.Description(x => x.Station)) Dense Outlined HideDetails="@("auto")" @bind-Value=@_plc.Station></MTextField>
|
||||
<MTextField Class="mx-1" Label=@(_plc.Description(x => x.TimeOut)) Dense Outlined HideDetails="@("auto")" @bind-Value=@_plc.TimeOut></MTextField>
|
||||
<MSelect Class="mx-1" Style="max-width:200px" @bind-Value="_plc.DataFormat" Outlined Label="@(_plc.Description(x => x.DataFormat))"
|
||||
Items=@(typeof(DataFormat).GetEnumList())
|
||||
MenuProps="@(props => { props.Auto = true; props.OffsetY = true; })"
|
||||
ItemText=@((u) =>u.des)
|
||||
ItemValue=@(u =>(DataFormat)u.value)
|
||||
HideDetails=@("auto") Height="30"
|
||||
Dense>
|
||||
</MSelect>
|
||||
</MRow>
|
||||
</MCard>
|
||||
|
||||
}
|
||||
<MCard Class="pa-4" Flat Elevation="0">
|
||||
<MRow Class="my-1" NoGutters>
|
||||
|
||||
<MCol Md="4">
|
||||
<MCard Flat Elevation="0">
|
||||
<MCol Class="my-1 py-1">
|
||||
<MTextField Class="mx-1 my-1" Label="变量地址" Dense Outlined HideDetails="@("auto")" @bind-Value=@address />
|
||||
<MTextField Class="mx-1 my-1" Label="长度" Dense Outlined HideDetails="@("auto")" @bind-Value=@length />
|
||||
<MSelect Class="mx-1 my-1" Style="max-width:200px" @bind-Value="dataTypeEnum" Outlined Label="数据类型"
|
||||
Items=@(typeof(DataTypeEnum).GetEnumList())
|
||||
MenuProps="@(props => { props.Auto = true; props.OffsetY = true; })"
|
||||
ItemText=@((u) =>u.des)
|
||||
ItemValue=@(u =>(DataTypeEnum)u.value)
|
||||
HideDetails=@("auto") Height="30"
|
||||
Dense>
|
||||
</MSelect>
|
||||
|
||||
<MButton Class="mx-1 my-1" Color="primary" OnClick="Read">
|
||||
读取
|
||||
</MButton>
|
||||
<MTextField Class="mx-1 mt-3 my-1" Label="值" Dense Outlined HideDetails="@("auto")" @bind-Value=@writeValue />
|
||||
<MButton Class="mx-1 my-1" Color="primary" OnClick="Write">
|
||||
写入
|
||||
</MButton>
|
||||
|
||||
</MCol>
|
||||
</MCard>
|
||||
</MCol>
|
||||
|
||||
<MCol Md="8">
|
||||
|
||||
|
||||
<MCard Height=@("calc(100vh - 420px)") Style="overflow-y:auto;width:100%" Elevation="0" Flat Class="ml-4">
|
||||
<MCardActions>
|
||||
输出日志
|
||||
<MSpacer></MSpacer>
|
||||
|
||||
<MTooltip Bottom Context="tip">
|
||||
<ActivatorContent>
|
||||
<MButton Loading=isDownExport Class="mx-2" @attributes="@tip.Attrs" Dark Fab Small
|
||||
OnClick=@(async()=>
|
||||
{
|
||||
await DownDeviceMessageExport(Messages.Select(a=>a.message));
|
||||
}
|
||||
)>
|
||||
<MIcon>mdi-export</MIcon>
|
||||
</MButton>
|
||||
</ActivatorContent>
|
||||
<ChildContent>
|
||||
<span>导出</span>
|
||||
</ChildContent>
|
||||
</MTooltip>
|
||||
|
||||
</MCardActions>
|
||||
@{
|
||||
var item = Messages;
|
||||
<MRow Class="ml-2 mr-2 d-flex" NoGutters>
|
||||
<MVirtualScroll Context="itemMessage" Height=@("calc(100vh - 500px)") OverscanCount=2 ItemSize="100" Items="item.OrderByDescending(a=>a.id).ToList()">
|
||||
<ItemContent>
|
||||
<div class=@(itemMessage.isRed==null?"black--text":itemMessage.isRed==true?"red--text":"green--text")>
|
||||
@itemMessage.message
|
||||
</div>
|
||||
|
||||
</ItemContent>
|
||||
</MVirtualScroll>
|
||||
</MRow>
|
||||
}
|
||||
</MCard>
|
||||
</MCol>
|
||||
|
||||
</MRow>
|
||||
|
||||
</MCard>
|
||||
|
||||
|
||||
@code
|
||||
{
|
||||
private UdpSessionPage udpClientPage;
|
||||
private ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdp _plc
|
||||
{
|
||||
get
|
||||
{
|
||||
return plc as ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdp;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnAfterRender(bool firstRender)
|
||||
{
|
||||
if (firstRender)
|
||||
{
|
||||
udpClientPage.LogAction = LogOut;
|
||||
//载入配置
|
||||
plc = new ThingsGateway.Foundation.Adapter.Modbus.ModbusRtuOverUdp((TGUdpSession)udpClientPage.GetTGUdpSession());
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
base.OnAfterRender(firstRender);
|
||||
}
|
||||
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
plc.SafeDispose();
|
||||
udpClientPage.SafeDispose();
|
||||
base.Dispose();
|
||||
}
|
||||
public override string ToString()
|
||||
{
|
||||
return nameof(ModbusRtuOverUdp);
|
||||
}
|
||||
}
|
@@ -29,6 +29,7 @@ public class ModbusServer : UpLoadBase
|
||||
public override UpDriverPropertyBase DriverPropertys => driverPropertys;
|
||||
public override List<CollectVariableRunTime> UploadVariables => _ModbusTags?.Values.ToList();
|
||||
public override VariablePropertyBase VariablePropertys => variablePropertys;
|
||||
public override Type DriverDebugUIType => typeof(ModbusServerDebugDriverPage);
|
||||
|
||||
public override async Task BeforStartAsync()
|
||||
{
|
||||
@@ -149,12 +150,12 @@ public class ModbusServer : UpLoadBase
|
||||
if (tag.Value == null) return OperResult.CreateSuccessResult();
|
||||
var enable = tag.Value.VariablePropertys[curDevice.Id].FirstOrDefault(a => a.PropertyName == nameof(variablePropertys.VariableRpcEnable))?.Value.ToBoolean() == true && driverPropertys.DeviceRpcEnable;
|
||||
if (!enable) return new OperResult("不允许写入");
|
||||
var result = await rpcCore.InvokeDeviceMethodAsync($"{nameof(ModbusServer)}-{curDevice.Name}-{client.GetIPPort()}",
|
||||
new()
|
||||
{
|
||||
Name = tag.Value.Name,
|
||||
Value = thingsGatewayBitConverter.GetDynamicData(tag.Value.DataType, bytes).ToString()
|
||||
});
|
||||
var result = await rpcCore.InvokeDeviceMethodAsync($"{nameof(ModbusServer)}-{curDevice.Name}-{client.IP + ":" + client.Port}",
|
||||
new()
|
||||
{
|
||||
Name = tag.Value.Name,
|
||||
Value = thingsGatewayBitConverter.GetDynamicData(tag.Value.DataType, bytes).ToString()
|
||||
});
|
||||
return result;
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
@@ -0,0 +1,143 @@
|
||||
@using BlazorComponent;
|
||||
@using Microsoft.AspNetCore.Components.Web;
|
||||
@using Microsoft.JSInterop;
|
||||
@using ThingsGateway.Core;
|
||||
@using ThingsGateway.Foundation.Adapter.Modbus;
|
||||
@using ThingsGateway.Foundation;
|
||||
@using ThingsGateway.Foundation.Extension;
|
||||
@using ThingsGateway.Foundation.Serial;
|
||||
@using ThingsGateway.Web.Foundation;
|
||||
@using Masa.Blazor
|
||||
@inherits DriverDebugUIBase
|
||||
<TcpServerPage @ref=tcpServerPage></TcpServerPage>
|
||||
@if (_plc != null)
|
||||
{
|
||||
<MCard Flat Elevation="0">
|
||||
<MRow Class="my-1" NoGutters Justify="JustifyTypes.SpaceBetween" Align="AlignTypes.Center">
|
||||
<MCheckbox Class="mx-1" Label=@(_plc.Description(x => x.MulStation)) Dense Outlined HideDetails="@("auto")" @bind-Value=@_plc.MulStation></MCheckbox>
|
||||
|
||||
<MTextField Class="mx-1" Label=@(_plc.Description(x => x.Station)) Dense Outlined HideDetails="@("auto")" @bind-Value=@_plc.Station></MTextField>
|
||||
<MTextField Class="mx-1" Label=@(_plc.Description(x => x.TimeOut)) Dense Outlined HideDetails="@("auto")" @bind-Value=@_plc.TimeOut></MTextField>
|
||||
<MSelect Class="mx-1" Style="max-width:200px" @bind-Value="_plc.DataFormat" Outlined Label="@(_plc.Description(x => x.DataFormat))"
|
||||
Items=@(typeof(DataFormat).GetEnumList())
|
||||
MenuProps="@(props => { props.Auto = true; props.OffsetY = true; })"
|
||||
ItemText=@((u) =>u.des)
|
||||
ItemValue=@(u =>(DataFormat)u.value)
|
||||
HideDetails=@("auto") Height="30"
|
||||
Dense>
|
||||
</MSelect>
|
||||
</MRow>
|
||||
</MCard>
|
||||
|
||||
}
|
||||
<MCard Class="pa-4" Flat Elevation="0">
|
||||
<MRow Class="my-1" NoGutters>
|
||||
|
||||
<MCol Md="4">
|
||||
<MCard Flat Elevation="0">
|
||||
<MCol Class="my-1 py-1">
|
||||
<MTextField Class="mx-1 my-1" Label="变量地址" Dense Outlined HideDetails="@("auto")" @bind-Value=@address />
|
||||
<MTextField Class="mx-1 my-1" Label="长度" Dense Outlined HideDetails="@("auto")" @bind-Value=@length />
|
||||
<MSelect Class="mx-1 my-1" Style="max-width:200px" @bind-Value="dataTypeEnum" Outlined Label="数据类型"
|
||||
Items=@(typeof(DataTypeEnum).GetEnumList())
|
||||
MenuProps="@(props => { props.Auto = true; props.OffsetY = true; })"
|
||||
ItemText=@((u) =>u.des)
|
||||
ItemValue=@(u =>(DataTypeEnum)u.value)
|
||||
HideDetails=@("auto") Height="30"
|
||||
Dense>
|
||||
</MSelect>
|
||||
|
||||
<MButton Class="mx-1 my-1" Color="primary" OnClick="Read">
|
||||
读取
|
||||
</MButton>
|
||||
<MTextField Class="mx-1 mt-3 my-1" Label="值" Dense Outlined HideDetails="@("auto")" @bind-Value=@writeValue />
|
||||
<MButton Class="mx-1 my-1" Color="primary" OnClick="Write">
|
||||
写入
|
||||
</MButton>
|
||||
|
||||
</MCol>
|
||||
</MCard>
|
||||
</MCol>
|
||||
|
||||
<MCol Md="8">
|
||||
|
||||
|
||||
<MCard Height=@("calc(100vh - 420px)") Style="overflow-y:auto;width:100%" Elevation="0" Flat Class="ml-4">
|
||||
<MCardActions>
|
||||
输出日志
|
||||
<MSpacer></MSpacer>
|
||||
|
||||
<MTooltip Bottom Context="tip">
|
||||
<ActivatorContent>
|
||||
<MButton Loading=isDownExport Class="mx-2" @attributes="@tip.Attrs" Dark Fab Small
|
||||
OnClick=@(async()=>
|
||||
{
|
||||
await DownDeviceMessageExport(Messages.Select(a=>a.message));
|
||||
}
|
||||
)>
|
||||
<MIcon>mdi-export</MIcon>
|
||||
</MButton>
|
||||
</ActivatorContent>
|
||||
<ChildContent>
|
||||
<span>导出</span>
|
||||
</ChildContent>
|
||||
</MTooltip>
|
||||
|
||||
</MCardActions>
|
||||
@{
|
||||
var item = Messages;
|
||||
<MRow Class="ml-2 mr-2 d-flex" NoGutters>
|
||||
<MVirtualScroll Context="itemMessage" Height=@("calc(100vh - 500px)") OverscanCount=2 ItemSize="100" Items="item.OrderByDescending(a=>a.id).ToList()">
|
||||
<ItemContent>
|
||||
<div class=@(itemMessage.isRed==null?"black--text":itemMessage.isRed==true?"red--text":"green--text")>
|
||||
@itemMessage.message
|
||||
</div>
|
||||
|
||||
</ItemContent>
|
||||
</MVirtualScroll>
|
||||
</MRow>
|
||||
}
|
||||
</MCard>
|
||||
</MCol>
|
||||
|
||||
</MRow>
|
||||
|
||||
</MCard>
|
||||
|
||||
|
||||
@code
|
||||
{
|
||||
private TcpServerPage tcpServerPage;
|
||||
private ThingsGateway.Foundation.Adapter.Modbus.ModbusServer _plc
|
||||
{
|
||||
get
|
||||
{
|
||||
return plc as ThingsGateway.Foundation.Adapter.Modbus.ModbusServer;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnAfterRender(bool firstRender)
|
||||
{
|
||||
if (firstRender)
|
||||
{
|
||||
tcpServerPage.LogAction = LogOut;
|
||||
//载入配置
|
||||
plc = new ThingsGateway.Foundation.Adapter.Modbus.ModbusServer((TcpService)tcpServerPage.GetTGTcpServer());
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
base.OnAfterRender(firstRender);
|
||||
}
|
||||
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
plc.SafeDispose();
|
||||
tcpServerPage.SafeDispose();
|
||||
base.Dispose();
|
||||
}
|
||||
public override string ToString()
|
||||
{
|
||||
return nameof(ModbusServer);
|
||||
}
|
||||
}
|
@@ -16,6 +16,7 @@ public class ModbusTcp : CollectBase
|
||||
}
|
||||
|
||||
public override CollectDriverPropertyBase DriverPropertys => driverPropertys;
|
||||
public override Type DriverDebugUIType => typeof(ModbusTcpDebugDriverPage);
|
||||
|
||||
public override IThingsGatewayBitConverter ThingsGatewayBitConverter { get => _plc?.ThingsGatewayBitConverter; }
|
||||
|
||||
@@ -70,6 +71,7 @@ public class ModbusTcp : CollectBase
|
||||
_plc = new((TGTcpClient)client);
|
||||
_plc.DataFormat = driverPropertys.DataFormat;
|
||||
_plc.FrameTime = driverPropertys.FrameTime;
|
||||
_plc.CacheTimeout = driverPropertys.CacheTimeout;
|
||||
_plc.ConnectTimeOut = driverPropertys.ConnectTimeOut;
|
||||
_plc.Station = driverPropertys.Station;
|
||||
_plc.TimeOut = driverPropertys.TimeOut;
|
||||
@@ -97,7 +99,8 @@ public class ModbusTcpProperty : CollectDriverPropertyBase
|
||||
|
||||
[DeviceProperty("帧前时间", "某些设备性能较弱,报文间需要间隔较长时间")]
|
||||
public int FrameTime { get; set; } = 0;
|
||||
|
||||
[DeviceProperty("组包缓存超时", "某些设备性能较弱,报文间需要间隔较长时间,可以设置更长的组包缓存,默认1s")]
|
||||
public double CacheTimeout { get; set; } = 1;
|
||||
[DeviceProperty("默认解析顺序", "")]
|
||||
public DataFormat DataFormat { get; set; }
|
||||
|
||||
|
145
src/Plugins/ThingsGateway.Modbus/ModbusTcpDebugDriverPage.razor
Normal file
145
src/Plugins/ThingsGateway.Modbus/ModbusTcpDebugDriverPage.razor
Normal file
@@ -0,0 +1,145 @@
|
||||
@using BlazorComponent;
|
||||
@using Microsoft.AspNetCore.Components.Web;
|
||||
@using Microsoft.JSInterop;
|
||||
@using ThingsGateway.Core;
|
||||
@using ThingsGateway.Foundation.Adapter.Modbus;
|
||||
@using ThingsGateway.Foundation;
|
||||
@using ThingsGateway.Foundation.Extension;
|
||||
@using ThingsGateway.Foundation.Serial;
|
||||
@using ThingsGateway.Web.Foundation;
|
||||
@using Masa.Blazor
|
||||
@inherits DriverDebugUIBase
|
||||
<TcpClientPage @ref=tcpClientPage></TcpClientPage>
|
||||
@if (_plc != null)
|
||||
{
|
||||
<MCard Flat Elevation="0">
|
||||
<MRow Class="my-1" NoGutters Justify="JustifyTypes.SpaceBetween" Align="AlignTypes.Center">
|
||||
<MCheckbox Class="mx-1" Label=@(_plc.Description(x => x.IsCheckMessageId)) Dense Outlined HideDetails="@("auto")" @bind-Value=@_plc.IsCheckMessageId></MCheckbox>
|
||||
<MTextField Class="mx-1" Label=@(_plc.Description(x => x.FrameTime)) Dense Outlined HideDetails="@("auto")" @bind-Value=@_plc.FrameTime></MTextField>
|
||||
<MTextField Class="mx-1" Label=@(_plc.Description(x => x.CacheTimeout)) Dense Outlined HideDetails="@("auto")" @bind-Value=@_plc.CacheTimeout></MTextField>
|
||||
|
||||
<MTextField Class="mx-1" Label=@(_plc.Description(x => x.Station)) Dense Outlined HideDetails="@("auto")" @bind-Value=@_plc.Station></MTextField>
|
||||
<MTextField Class="mx-1" Label=@(_plc.Description(x => x.TimeOut)) Dense Outlined HideDetails="@("auto")" @bind-Value=@_plc.TimeOut></MTextField>
|
||||
<MSelect Class="mx-1" Style="max-width:200px" @bind-Value="_plc.DataFormat" Outlined Label="@(_plc.Description(x => x.DataFormat))"
|
||||
Items=@(typeof(DataFormat).GetEnumList())
|
||||
MenuProps="@(props => { props.Auto = true; props.OffsetY = true; })"
|
||||
ItemText=@((u) =>u.des)
|
||||
ItemValue=@(u =>(DataFormat)u.value)
|
||||
HideDetails=@("auto") Height="30"
|
||||
Dense>
|
||||
</MSelect>
|
||||
</MRow>
|
||||
</MCard>
|
||||
|
||||
}
|
||||
<MCard Class="pa-4" Flat Elevation="0">
|
||||
<MRow Class="my-1" NoGutters>
|
||||
|
||||
<MCol Md="4">
|
||||
<MCard Flat Elevation="0">
|
||||
<MCol Class="my-1 py-1">
|
||||
<MTextField Class="mx-1 my-1" Label="变量地址" Dense Outlined HideDetails="@("auto")" @bind-Value=@address />
|
||||
<MTextField Class="mx-1 my-1" Label="长度" Dense Outlined HideDetails="@("auto")" @bind-Value=@length />
|
||||
<MSelect Class="mx-1 my-1" Style="max-width:200px" @bind-Value="dataTypeEnum" Outlined Label="数据类型"
|
||||
Items=@(typeof(DataTypeEnum).GetEnumList())
|
||||
MenuProps="@(props => { props.Auto = true; props.OffsetY = true; })"
|
||||
ItemText=@((u) =>u.des)
|
||||
ItemValue=@(u =>(DataTypeEnum)u.value)
|
||||
HideDetails=@("auto") Height="30"
|
||||
Dense>
|
||||
</MSelect>
|
||||
|
||||
<MButton Class="mx-1 my-1" Color="primary" OnClick="Read">
|
||||
读取
|
||||
</MButton>
|
||||
<MTextField Class="mx-1 mt-3 my-1" Label="值" Dense Outlined HideDetails="@("auto")" @bind-Value=@writeValue />
|
||||
<MButton Class="mx-1 my-1" Color="primary" OnClick="Write">
|
||||
写入
|
||||
</MButton>
|
||||
|
||||
</MCol>
|
||||
</MCard>
|
||||
</MCol>
|
||||
|
||||
<MCol Md="8">
|
||||
|
||||
|
||||
<MCard Height=@("calc(100vh - 420px)") Style="overflow-y:auto;width:100%" Elevation="0" Flat Class="ml-4">
|
||||
<MCardActions>
|
||||
输出日志
|
||||
<MSpacer></MSpacer>
|
||||
|
||||
<MTooltip Bottom Context="tip">
|
||||
<ActivatorContent>
|
||||
<MButton Loading=isDownExport Class="mx-2" @attributes="@tip.Attrs" Dark Fab Small
|
||||
OnClick=@(async()=>
|
||||
{
|
||||
await DownDeviceMessageExport(Messages.Select(a=>a.message));
|
||||
}
|
||||
)>
|
||||
<MIcon>mdi-export</MIcon>
|
||||
</MButton>
|
||||
</ActivatorContent>
|
||||
<ChildContent>
|
||||
<span>导出</span>
|
||||
</ChildContent>
|
||||
</MTooltip>
|
||||
|
||||
</MCardActions>
|
||||
@{
|
||||
var item = Messages;
|
||||
<MRow Class="ml-2 mr-2 d-flex" NoGutters>
|
||||
<MVirtualScroll Context="itemMessage" Height=@("calc(100vh - 500px)") OverscanCount=2 ItemSize="100" Items="item.OrderByDescending(a=>a.id).ToList()">
|
||||
<ItemContent>
|
||||
<div class=@(itemMessage.isRed==null?"black--text":itemMessage.isRed==true?"red--text":"green--text")>
|
||||
@itemMessage.message
|
||||
</div>
|
||||
|
||||
</ItemContent>
|
||||
</MVirtualScroll>
|
||||
</MRow>
|
||||
}
|
||||
</MCard>
|
||||
</MCol>
|
||||
|
||||
</MRow>
|
||||
|
||||
</MCard>
|
||||
|
||||
|
||||
@code
|
||||
{
|
||||
private TcpClientPage tcpClientPage;
|
||||
private ThingsGateway.Foundation.Adapter.Modbus.ModbusTcp _plc
|
||||
{
|
||||
get
|
||||
{
|
||||
return plc as ThingsGateway.Foundation.Adapter.Modbus.ModbusTcp;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnAfterRender(bool firstRender)
|
||||
{
|
||||
if (firstRender)
|
||||
{
|
||||
tcpClientPage.LogAction = LogOut;
|
||||
//载入配置
|
||||
plc = new ThingsGateway.Foundation.Adapter.Modbus.ModbusTcp((TGTcpClient)tcpClientPage.GetTGTcpClient());
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
base.OnAfterRender(firstRender);
|
||||
}
|
||||
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
plc.SafeDispose();
|
||||
tcpClientPage.SafeDispose();
|
||||
base.Dispose();
|
||||
}
|
||||
public override string ToString()
|
||||
{
|
||||
return nameof(ModbusTcp);
|
||||
}
|
||||
}
|
@@ -19,6 +19,7 @@ public class ModbusUdp : CollectBase
|
||||
public override CollectDriverPropertyBase DriverPropertys => driverPropertys;
|
||||
|
||||
public override IThingsGatewayBitConverter ThingsGatewayBitConverter { get => _plc?.ThingsGatewayBitConverter; }
|
||||
public override Type DriverDebugUIType => typeof(ModbusUdpDebugDriverPage);
|
||||
|
||||
public override void AfterStop()
|
||||
{
|
||||
|
144
src/Plugins/ThingsGateway.Modbus/ModbusUdpDebugDriverPage.razor
Normal file
144
src/Plugins/ThingsGateway.Modbus/ModbusUdpDebugDriverPage.razor
Normal file
@@ -0,0 +1,144 @@
|
||||
@using BlazorComponent;
|
||||
@using Microsoft.AspNetCore.Components.Web;
|
||||
@using Microsoft.JSInterop;
|
||||
@using ThingsGateway.Core;
|
||||
@using ThingsGateway.Foundation.Adapter.Modbus;
|
||||
@using ThingsGateway.Foundation;
|
||||
@using ThingsGateway.Foundation.Extension;
|
||||
@using ThingsGateway.Foundation.Serial;
|
||||
@using ThingsGateway.Web.Foundation;
|
||||
@using Masa.Blazor
|
||||
@inherits DriverDebugUIBase
|
||||
<UdpSessionPage @ref=udpClientPage></UdpSessionPage>
|
||||
@if (_plc != null)
|
||||
{
|
||||
<MCard Flat Elevation="0">
|
||||
<MRow Class="my-1" NoGutters Justify="JustifyTypes.SpaceBetween" Align="AlignTypes.Center">
|
||||
<MCheckbox Class="mx-1" Label=@(_plc.Description(x => x.IsCheckMessageId)) Dense Outlined HideDetails="@("auto")" @bind-Value=@_plc.IsCheckMessageId></MCheckbox>
|
||||
<MTextField Class="mx-1" Label=@(_plc.Description(x => x.FrameTime)) Dense Outlined HideDetails="@("auto")" @bind-Value=@_plc.FrameTime></MTextField>
|
||||
|
||||
<MTextField Class="mx-1" Label=@(_plc.Description(x => x.Station)) Dense Outlined HideDetails="@("auto")" @bind-Value=@_plc.Station></MTextField>
|
||||
<MTextField Class="mx-1" Label=@(_plc.Description(x => x.TimeOut)) Dense Outlined HideDetails="@("auto")" @bind-Value=@_plc.TimeOut></MTextField>
|
||||
<MSelect Class="mx-1" Style="max-width:200px" @bind-Value="_plc.DataFormat" Outlined Label="@(_plc.Description(x => x.DataFormat))"
|
||||
Items=@(typeof(DataFormat).GetEnumList())
|
||||
MenuProps="@(props => { props.Auto = true; props.OffsetY = true; })"
|
||||
ItemText=@((u) =>u.des)
|
||||
ItemValue=@(u =>(DataFormat)u.value)
|
||||
HideDetails=@("auto") Height="30"
|
||||
Dense>
|
||||
</MSelect>
|
||||
</MRow>
|
||||
</MCard>
|
||||
|
||||
}
|
||||
<MCard Class="pa-4" Flat Elevation="0">
|
||||
<MRow Class="my-1" NoGutters>
|
||||
|
||||
<MCol Md="4">
|
||||
<MCard Flat Elevation="0">
|
||||
<MCol Class="my-1 py-1">
|
||||
<MTextField Class="mx-1 my-1" Label="变量地址" Dense Outlined HideDetails="@("auto")" @bind-Value=@address />
|
||||
<MTextField Class="mx-1 my-1" Label="长度" Dense Outlined HideDetails="@("auto")" @bind-Value=@length />
|
||||
<MSelect Class="mx-1 my-1" Style="max-width:200px" @bind-Value="dataTypeEnum" Outlined Label="数据类型"
|
||||
Items=@(typeof(DataTypeEnum).GetEnumList())
|
||||
MenuProps="@(props => { props.Auto = true; props.OffsetY = true; })"
|
||||
ItemText=@((u) =>u.des)
|
||||
ItemValue=@(u =>(DataTypeEnum)u.value)
|
||||
HideDetails=@("auto") Height="30"
|
||||
Dense>
|
||||
</MSelect>
|
||||
|
||||
<MButton Class="mx-1 my-1" Color="primary" OnClick="Read">
|
||||
读取
|
||||
</MButton>
|
||||
<MTextField Class="mx-1 mt-3 my-1" Label="值" Dense Outlined HideDetails="@("auto")" @bind-Value=@writeValue />
|
||||
<MButton Class="mx-1 my-1" Color="primary" OnClick="Write">
|
||||
写入
|
||||
</MButton>
|
||||
|
||||
</MCol>
|
||||
</MCard>
|
||||
</MCol>
|
||||
|
||||
<MCol Md="8">
|
||||
|
||||
|
||||
<MCard Height=@("calc(100vh - 420px)") Style="overflow-y:auto;width:100%" Elevation="0" Flat Class="ml-4">
|
||||
<MCardActions>
|
||||
输出日志
|
||||
<MSpacer></MSpacer>
|
||||
|
||||
<MTooltip Bottom Context="tip">
|
||||
<ActivatorContent>
|
||||
<MButton Loading=isDownExport Class="mx-2" @attributes="@tip.Attrs" Dark Fab Small
|
||||
OnClick=@(async()=>
|
||||
{
|
||||
await DownDeviceMessageExport(Messages.Select(a=>a.message));
|
||||
}
|
||||
)>
|
||||
<MIcon>mdi-export</MIcon>
|
||||
</MButton>
|
||||
</ActivatorContent>
|
||||
<ChildContent>
|
||||
<span>导出</span>
|
||||
</ChildContent>
|
||||
</MTooltip>
|
||||
|
||||
</MCardActions>
|
||||
@{
|
||||
var item = Messages;
|
||||
<MRow Class="ml-2 mr-2 d-flex" NoGutters>
|
||||
<MVirtualScroll Context="itemMessage" Height=@("calc(100vh - 500px)") OverscanCount=2 ItemSize="100" Items="item.OrderByDescending(a=>a.id).ToList()">
|
||||
<ItemContent>
|
||||
<div class=@(itemMessage.isRed==null?"black--text":itemMessage.isRed==true?"red--text":"green--text")>
|
||||
@itemMessage.message
|
||||
</div>
|
||||
|
||||
</ItemContent>
|
||||
</MVirtualScroll>
|
||||
</MRow>
|
||||
}
|
||||
</MCard>
|
||||
</MCol>
|
||||
|
||||
</MRow>
|
||||
|
||||
</MCard>
|
||||
|
||||
|
||||
@code
|
||||
{
|
||||
private UdpSessionPage udpClientPage;
|
||||
private ThingsGateway.Foundation.Adapter.Modbus.ModbusUdp _plc
|
||||
{
|
||||
get
|
||||
{
|
||||
return plc as ThingsGateway.Foundation.Adapter.Modbus.ModbusUdp;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnAfterRender(bool firstRender)
|
||||
{
|
||||
if (firstRender)
|
||||
{
|
||||
udpClientPage.LogAction = LogOut;
|
||||
//载入配置
|
||||
plc = new ThingsGateway.Foundation.Adapter.Modbus.ModbusUdp((TGUdpSession)udpClientPage.GetTGUdpSession());
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
base.OnAfterRender(firstRender);
|
||||
}
|
||||
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
plc.SafeDispose();
|
||||
udpClientPage.SafeDispose();
|
||||
base.Dispose();
|
||||
}
|
||||
public override string ToString()
|
||||
{
|
||||
return nameof(ModbusUdp);
|
||||
}
|
||||
}
|
@@ -2,18 +2,18 @@
|
||||
@using Microsoft.AspNetCore.Components.Web;
|
||||
@using System.IO.Ports;
|
||||
@using ThingsGateway.Core;
|
||||
@using ThingsGateway.Foundation.Adapter.Modbus;
|
||||
@using ThingsGateway.Foundation;
|
||||
@using ThingsGateway.Foundation.Serial;
|
||||
@using ThingsGateway.Web.Foundation;
|
||||
@using Masa.Blazor
|
||||
<MCard Class="ma-4" Flat Elevation="0">
|
||||
@implements IDisposable
|
||||
<MCard Class="pa-4" Flat Elevation="0" Rounded="false">
|
||||
<MRow Justify="JustifyTypes.SpaceBetween" Align="AlignTypes.Center">
|
||||
<MTextField Class="mx-1" Label=@(serialProperty.Description(x => x.PortName)) Dense Outlined HideDetails="@("auto")" @bind-Value=@serialProperty.PortName />
|
||||
<MTextField Class="mx-1" Label=@(serialProperty.Description(x => x.BaudRate)) Dense Outlined HideDetails="@("auto")" @bind-Value=@serialProperty.BaudRate />
|
||||
|
||||
<MTextField Class="mx-1" Label=@(serialProperty.Description(x => x.DataBits)) Dense Outlined HideDetails="@("auto")" @bind-Value=@serialProperty.DataBits />
|
||||
<MTextField Class="mx-1" Label=@(serialProperty.Description(x => x.BaudRate)) Dense Outlined HideDetails="@("auto")" @bind-Value=@serialProperty.BaudRate />
|
||||
<MSelect Class="mx-1" Style="max-width:100px" @bind-Value="serialProperty.Parity" Outlined Label="@(serialProperty.Description(x => x.Parity))"
|
||||
<MSelect Class="mx-1" Style="max-width:200px" @bind-Value="serialProperty.Parity" Outlined Label="@(serialProperty.Description(x => x.Parity))"
|
||||
Items=@(typeof(Parity).GetEnumList())
|
||||
MenuProps="@(props => { props.Auto = true; props.OffsetY = true; })"
|
||||
ItemText=@((u) =>u.des)
|
||||
@@ -21,7 +21,7 @@
|
||||
HideDetails=@("auto") Height="30"
|
||||
Dense>
|
||||
</MSelect>
|
||||
<MSelect Class="mx-1" Style="max-width:100px" @bind-Value="serialProperty.StopBits" Outlined Label="@(serialProperty.Description(x => x.StopBits))"
|
||||
<MSelect Class="mx-1" Style="max-width:200px" @bind-Value="serialProperty.StopBits" Outlined Label="@(serialProperty.Description(x => x.StopBits))"
|
||||
Items=@(typeof(StopBits).GetEnumList())
|
||||
MenuProps="@(props => { props.Auto = true; props.OffsetY = true; })"
|
||||
ItemText=@((u) =>u.des)
|
||||
@@ -31,28 +31,53 @@
|
||||
</MSelect>
|
||||
<MButton Class="mx-1" OnClick=@(async() =>
|
||||
{
|
||||
serialClient.Close();
|
||||
GetSerialClient().Close();
|
||||
await GetSerialClient().OpenAsync();
|
||||
}
|
||||
) Color="primary">
|
||||
连接
|
||||
</MButton>
|
||||
|
||||
<MButton Class="mx-1" OnClick=@(() =>
|
||||
{
|
||||
GetSerialClient().Close();
|
||||
}
|
||||
) Color="red">
|
||||
断开
|
||||
</MButton>
|
||||
</MRow>
|
||||
|
||||
|
||||
|
||||
</MCard>
|
||||
@code
|
||||
{
|
||||
public void Dispose()
|
||||
{
|
||||
serialClient.SafeDispose();
|
||||
}
|
||||
private SerialProperty serialProperty = new SerialProperty();
|
||||
private SerialClient serialClient { get; set; } = new();
|
||||
private TouchSocketConfig config;
|
||||
public Action<string> LogAction;
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
config = new TouchSocketConfig();
|
||||
var logMessage = new TouchSocket.Core.LoggerGroup();
|
||||
logMessage.AddLogger(new EasyLogger(LogOut));
|
||||
config.ConfigureContainer(a => a.RegisterSingleton<ILog>(logMessage));
|
||||
serialClient = config.Container.Resolve<SerialClient>();
|
||||
base.OnInitialized();
|
||||
}
|
||||
private void LogOut(string str)
|
||||
{
|
||||
LogAction?.Invoke(str);
|
||||
}
|
||||
public SerialClient GetSerialClient()
|
||||
{
|
||||
config = new TouchSocketConfig();
|
||||
var logMessage = new TouchSocket.Core.LoggerGroup();
|
||||
logMessage.AddLogger(new EasyLogger(LogOut));
|
||||
config.ConfigureContainer(a => a.RegisterSingleton<ILog>(logMessage));
|
||||
config.SetSerialProperty(serialProperty).SetBufferLength(300);
|
||||
//载入配置
|
||||
serialClient.Setup(config);
|
||||
|
76
src/Plugins/ThingsGateway.Modbus/TcpClientPage.razor
Normal file
76
src/Plugins/ThingsGateway.Modbus/TcpClientPage.razor
Normal file
@@ -0,0 +1,76 @@
|
||||
@using BlazorComponent;
|
||||
@using Microsoft.AspNetCore.Components.Web;
|
||||
@using System.IO.Ports;
|
||||
@using System.Collections.Concurrent;
|
||||
@using ThingsGateway.Core;
|
||||
@using ThingsGateway.Foundation;
|
||||
@using ThingsGateway.Foundation.Serial;
|
||||
@using ThingsGateway.Web.Foundation;
|
||||
@using Masa.Blazor
|
||||
@implements IDisposable
|
||||
<MCard Class="pa-4" Flat Elevation="0" Rounded="false">
|
||||
<MRow Justify="JustifyTypes.SpaceBetween" Align="AlignTypes.Center">
|
||||
<MTextField Class="mx-1" Label="IP地址" Dense Outlined HideDetails="@("auto")" @bind-Value=@ip />
|
||||
<MTextField Class="mx-1" Label="端口" Dense Outlined HideDetails="@("auto")" @bind-Value=@port />
|
||||
|
||||
<MButton Class="mx-1" OnClick=@(async() =>
|
||||
{
|
||||
tgTcpClient.Close();
|
||||
await GetTGTcpClient().ConnectAsync();
|
||||
}
|
||||
) Color="primary">
|
||||
连接
|
||||
</MButton>
|
||||
<MButton Class="mx-1" OnClick=@(() =>
|
||||
{
|
||||
tgTcpClient.Close();
|
||||
}
|
||||
) Color="red">
|
||||
断开
|
||||
</MButton>
|
||||
</MRow>
|
||||
|
||||
|
||||
|
||||
</MCard>
|
||||
@code
|
||||
{
|
||||
public void Dispose()
|
||||
{
|
||||
tgTcpClient.SafeDispose();
|
||||
}
|
||||
private string ip="127.0.0.1";
|
||||
private int port=502;
|
||||
private TGTcpClient tgTcpClient { get; set; } = new();
|
||||
private TouchSocketConfig config;
|
||||
public Action<string> LogAction;
|
||||
|
||||
private void LogOut(string str)
|
||||
{
|
||||
LogAction?.Invoke(str);
|
||||
}
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
config = new TouchSocketConfig();
|
||||
var logMessage = new TouchSocket.Core.LoggerGroup();
|
||||
logMessage.AddLogger(new EasyLogger(LogOut));
|
||||
config.ConfigureContainer(a => a.RegisterSingleton<ILog>(logMessage));
|
||||
config.SetRemoteIPHost(new IPHost(ip + ":" + port)).SetBufferLength(300);
|
||||
tgTcpClient = config.Container.Resolve<TGTcpClient>();
|
||||
base.OnInitialized();
|
||||
|
||||
}
|
||||
public TGTcpClient GetTGTcpClient()
|
||||
{
|
||||
config = new TouchSocketConfig();
|
||||
var logMessage = new TouchSocket.Core.LoggerGroup();
|
||||
logMessage.AddLogger(new EasyLogger(LogOut));
|
||||
config.ConfigureContainer(a => a.RegisterSingleton<ILog>(logMessage));
|
||||
config.SetRemoteIPHost(new IPHost(ip + ":" + port)).SetBufferLength(300);
|
||||
//载入配置
|
||||
tgTcpClient.Setup(config);
|
||||
return tgTcpClient;
|
||||
}
|
||||
|
||||
|
||||
}
|
78
src/Plugins/ThingsGateway.Modbus/TcpServerPage.razor
Normal file
78
src/Plugins/ThingsGateway.Modbus/TcpServerPage.razor
Normal file
@@ -0,0 +1,78 @@
|
||||
@using BlazorComponent;
|
||||
@using Microsoft.AspNetCore.Components.Web;
|
||||
@using System.IO.Ports;
|
||||
@using System.Collections.Concurrent;
|
||||
@using ThingsGateway.Core;
|
||||
@using ThingsGateway.Foundation;
|
||||
@using ThingsGateway.Foundation.Serial;
|
||||
@using ThingsGateway.Web.Foundation;
|
||||
@using Masa.Blazor
|
||||
@implements IDisposable
|
||||
<MCard Class="pa-4" Flat Elevation="0" Rounded="false">
|
||||
<MRow Justify="JustifyTypes.SpaceBetween" Align="AlignTypes.Center">
|
||||
<MTextField Class="mx-1" Label="IP地址" Dense Outlined HideDetails="@("auto")" @bind-Value=@ip />
|
||||
<MTextField Class="mx-1" Label="端口" Dense Outlined HideDetails="@("auto")" @bind-Value=@port />
|
||||
|
||||
<MButton Class="mx-1" OnClick=@(() =>
|
||||
{
|
||||
tgTcpServer.Stop();
|
||||
GetTGTcpServer().Start();
|
||||
}
|
||||
) Color="primary">
|
||||
连接
|
||||
</MButton>
|
||||
<MButton Class="mx-1" OnClick=@(() =>
|
||||
{
|
||||
tgTcpServer.Stop();
|
||||
}
|
||||
) Color="red">
|
||||
断开
|
||||
</MButton>
|
||||
</MRow>
|
||||
|
||||
|
||||
|
||||
</MCard>
|
||||
@code
|
||||
{
|
||||
public void Dispose()
|
||||
{
|
||||
tgTcpServer.SafeDispose();
|
||||
}
|
||||
private string ip = "127.0.0.1";
|
||||
private int port = 502;
|
||||
private TcpService tgTcpServer { get; set; } = new();
|
||||
private TouchSocketConfig config;
|
||||
public Action<string> LogAction;
|
||||
|
||||
private void LogOut(string str)
|
||||
{
|
||||
LogAction?.Invoke(str);
|
||||
}
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
config = new TouchSocketConfig();
|
||||
var logMessage = new TouchSocket.Core.LoggerGroup();
|
||||
logMessage.AddLogger(new EasyLogger(LogOut));
|
||||
config.ConfigureContainer(a => a.RegisterSingleton<ILog>(logMessage));
|
||||
config.SetListenIPHosts(new IPHost[] { new IPHost(ip + ":" + port) });
|
||||
config.SetBufferLength(300);
|
||||
tgTcpServer = config.Container.Resolve<TcpService>();
|
||||
base.OnInitialized();
|
||||
|
||||
}
|
||||
public TcpService GetTGTcpServer()
|
||||
{
|
||||
config = new TouchSocketConfig();
|
||||
var logMessage = new TouchSocket.Core.LoggerGroup();
|
||||
logMessage.AddLogger(new EasyLogger(LogOut));
|
||||
config.ConfigureContainer(a => a.RegisterSingleton<ILog>(logMessage));
|
||||
config.SetListenIPHosts(new IPHost[] { new IPHost(ip + ":" + port) });
|
||||
config.SetBufferLength(300);
|
||||
//载入配置
|
||||
tgTcpServer.Setup(config);
|
||||
return tgTcpServer;
|
||||
}
|
||||
|
||||
|
||||
}
|
78
src/Plugins/ThingsGateway.Modbus/UdpSessionPage.razor
Normal file
78
src/Plugins/ThingsGateway.Modbus/UdpSessionPage.razor
Normal file
@@ -0,0 +1,78 @@
|
||||
@using BlazorComponent;
|
||||
@using Microsoft.AspNetCore.Components.Web;
|
||||
@using System.IO.Ports;
|
||||
@using System.Collections.Concurrent;
|
||||
@using ThingsGateway.Core;
|
||||
@using ThingsGateway.Foundation;
|
||||
@using ThingsGateway.Foundation.Serial;
|
||||
@using ThingsGateway.Web.Foundation;
|
||||
@using Masa.Blazor
|
||||
@implements IDisposable
|
||||
<MCard Class="pa-4" Flat Elevation="0" Rounded="false">
|
||||
<MRow Justify="JustifyTypes.SpaceBetween" Align="AlignTypes.Center">
|
||||
<MTextField Class="mx-1" Label="IP地址" Dense Outlined HideDetails="@("auto")" @bind-Value=@ip />
|
||||
<MTextField Class="mx-1" Label="端口" Dense Outlined HideDetails="@("auto")" @bind-Value=@port />
|
||||
|
||||
<MButton Class="mx-1" OnClick=@(() =>
|
||||
{
|
||||
tgUdpSession.Stop();
|
||||
GetTGUdpSession().Start();
|
||||
}
|
||||
) Color="primary">
|
||||
连接
|
||||
</MButton>
|
||||
<MButton Class="mx-1" OnClick=@(() =>
|
||||
{
|
||||
tgUdpSession.Stop();
|
||||
}
|
||||
) Color="red">
|
||||
断开
|
||||
</MButton>
|
||||
</MRow>
|
||||
|
||||
|
||||
|
||||
</MCard>
|
||||
@code
|
||||
{
|
||||
public void Dispose()
|
||||
{
|
||||
tgUdpSession.SafeDispose();
|
||||
}
|
||||
private string ip = "127.0.0.1";
|
||||
private int port = 502;
|
||||
private TGUdpSession tgUdpSession { get; set; } = new();
|
||||
private TouchSocketConfig config;
|
||||
public Action<string> LogAction;
|
||||
|
||||
private void LogOut(string str)
|
||||
{
|
||||
LogAction?.Invoke(str);
|
||||
}
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
config = new TouchSocketConfig();
|
||||
var logMessage = new TouchSocket.Core.LoggerGroup();
|
||||
logMessage.AddLogger(new EasyLogger(LogOut));
|
||||
config.ConfigureContainer(a => a.RegisterSingleton<ILog>(logMessage));
|
||||
config.SetRemoteIPHost(new IPHost(ip + ":" + port)).SetBufferLength(300);
|
||||
config.SetBindIPHost(new IPHost(0));
|
||||
tgUdpSession = config.Container.Resolve<TGUdpSession>();
|
||||
base.OnInitialized();
|
||||
|
||||
}
|
||||
public TGUdpSession GetTGUdpSession()
|
||||
{
|
||||
config = new TouchSocketConfig();
|
||||
var logMessage = new TouchSocket.Core.LoggerGroup();
|
||||
logMessage.AddLogger(new EasyLogger(LogOut));
|
||||
config.ConfigureContainer(a => a.RegisterSingleton<ILog>(logMessage));
|
||||
config.SetRemoteIPHost(new IPHost(ip + ":" + port)).SetBufferLength(300);
|
||||
config.SetBindIPHost(new IPHost(0));
|
||||
//载入配置
|
||||
tgUdpSession.Setup(config);
|
||||
return tgUdpSession;
|
||||
}
|
||||
|
||||
|
||||
}
|
@@ -1,4 +1,6 @@
|
||||
namespace ThingsGateway.Foundation
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace ThingsGateway.Foundation
|
||||
{
|
||||
/// <summary>
|
||||
/// 读写设备基类
|
||||
@@ -6,6 +8,7 @@
|
||||
public abstract class ReadWriteDevicesBase : DisposableObject, IReadWriteDevice
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
[Description("数据转换")]
|
||||
public DataFormat DataFormat
|
||||
{
|
||||
get
|
||||
@@ -23,6 +26,7 @@
|
||||
/// <inheritdoc/>
|
||||
public IThingsGatewayBitConverter ThingsGatewayBitConverter { get; protected set; } = new ThingsGatewayBitConverter(EndianType.Big);
|
||||
/// <inheritdoc/>
|
||||
[Description("读写超时")]
|
||||
public ushort TimeOut { get; set; } = 3000;
|
||||
/// <inheritdoc/>
|
||||
public ushort RegisterByteLength { get; set; } = 1;
|
||||
|
@@ -88,23 +88,23 @@
|
||||
|
||||
private void Connected(ITcpClient client, MsgEventArgs e)
|
||||
{
|
||||
Logger?.Debug(client.GetIPPort() + "连接成功");
|
||||
Logger?.Debug(client.RemoteIPHost.ToString() + "连接成功");
|
||||
}
|
||||
|
||||
private void Connecting(ITcpClient client, ConnectingEventArgs e)
|
||||
{
|
||||
Logger?.Debug(client.GetIPPort() + "正在连接");
|
||||
Logger?.Debug(client.RemoteIPHost.ToString() + "正在连接");
|
||||
SetDataAdapter();
|
||||
}
|
||||
|
||||
private void Disconnected(ITcpClientBase client, DisconnectEventArgs e)
|
||||
{
|
||||
Logger?.Debug(client.GetIPPort() + "断开连接-" + e.Message);
|
||||
Logger?.Debug(client.IP + ":" + client.Port + "断开连接-" + e.Message);
|
||||
}
|
||||
|
||||
private void Disconnecting(ITcpClientBase client, DisconnectEventArgs e)
|
||||
{
|
||||
Logger?.Debug(client.GetIPPort() + "正在主动断开连接-" + e.Message);
|
||||
Logger?.Debug(client.IP + ":" + client.Port + "正在主动断开连接-" + e.Message);
|
||||
}
|
||||
}
|
||||
}
|
@@ -71,23 +71,23 @@ namespace ThingsGateway.Foundation
|
||||
|
||||
private void Connected(SocketClient client, TouchSocketEventArgs e)
|
||||
{
|
||||
Logger?.Debug(client.GetIPPort() + "连接成功");
|
||||
Logger?.Debug(client.IP + ":" + client.Port + "连接成功");
|
||||
}
|
||||
|
||||
private void Connecting(SocketClient client, OperationEventArgs e)
|
||||
{
|
||||
Logger?.Debug(client.GetIPPort() + "正在连接");
|
||||
Logger?.Debug(client.IP + ":" + client.Port + "正在连接");
|
||||
SetDataAdapter(client);
|
||||
}
|
||||
|
||||
private void Disconnected(ITcpClientBase client, DisconnectEventArgs e)
|
||||
{
|
||||
Logger?.Debug(client.GetIPPort() + "断开连接-" + e.Message);
|
||||
Logger?.Debug(client.IP + ":" + client.Port + "断开连接-" + e.Message);
|
||||
}
|
||||
|
||||
private void Disconnecting(ITcpClientBase client, DisconnectEventArgs e)
|
||||
{
|
||||
Logger?.Debug(client.GetIPPort() + "正在主动断开连接-" + e.Message);
|
||||
Logger?.Debug(client.IP + ":" + client.Port + "正在主动断开连接-" + e.Message);
|
||||
}
|
||||
|
||||
private async void Received(SocketClient client, ByteBlock byteBlock, IRequestInfo requestInfo)
|
||||
|
@@ -41,7 +41,7 @@
|
||||
protected override FilterResult Filter(ByteBlock byteBlock, bool beCached, ref TRequest request, ref int tempCapacity)
|
||||
{
|
||||
var allBytes = byteBlock.ToArray(0, byteBlock.Len);
|
||||
Client.Logger?.Trace("报文-" + Client.GetIPPort().ToString() + "-" + ThingsGateway.Foundation.Resources.Resource.Received + ":" + allBytes.ToHexString(" "));
|
||||
Client.Logger?.Trace("报文-" + Client.IP + ":" + Client.Port + "-" + ThingsGateway.Foundation.Resources.Resource.Received + ":" + allBytes.ToHexString(" "));
|
||||
|
||||
//if (Request?.SendBytes == null)
|
||||
//{
|
||||
@@ -111,7 +111,7 @@
|
||||
{
|
||||
byteBlock.Pos = byteBlock.Len;
|
||||
request.ReceivedBytes = allBytes;
|
||||
Client.Logger?.Warning(Client.GetIPPort().ToString() + unpackbytes.Message);
|
||||
Client.Logger?.Warning(Client.IP + ":" + Client.Port.ToString() + unpackbytes.Message);
|
||||
return FilterResult.Success;
|
||||
}
|
||||
}
|
||||
@@ -135,7 +135,7 @@
|
||||
Request = GetInstance();
|
||||
Request.SendBytes = bytes;
|
||||
GoSend(bytes, 0, bytes.Length);
|
||||
Client.Logger?.Trace("报文-" + Client.GetIPPort().ToString() + "-" + ThingsGateway.Foundation.Resources.Resource.Send + ":" + Request.SendBytes.ToHexString(" "));
|
||||
Client.Logger?.Trace("报文-" + Client.IP + ":" + Client.Port.ToString() + "-" + ThingsGateway.Foundation.Resources.Resource.Send + ":" + Request.SendBytes.ToHexString(" "));
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
|
@@ -531,6 +531,8 @@ namespace TouchSocket.Sockets
|
||||
ConnectingEventArgs args = new ConnectingEventArgs(this.MainSocket);
|
||||
this.PrivateOnConnecting(args);
|
||||
{
|
||||
this.CanSend = true;
|
||||
this.LoadSocketAndReadIpPort();
|
||||
#if (NET6_0_OR_GREATER)
|
||||
var nowTime = DateTime.UtcNow;
|
||||
using CancellationTokenSource cancellationTokenSource = new();
|
||||
@@ -580,8 +582,6 @@ namespace TouchSocket.Sockets
|
||||
|
||||
if (this.MainSocket.Connected)
|
||||
{
|
||||
this.CanSend = true;
|
||||
this.LoadSocketAndReadIpPort();
|
||||
|
||||
if (this.Config.GetValue(TouchSocketConfigExtension.DelaySenderProperty) is DelaySenderOption senderOption)
|
||||
{
|
||||
|
@@ -1,12 +1,141 @@
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.JSInterop;
|
||||
|
||||
using System.IO;
|
||||
using System.Timers;
|
||||
|
||||
using ThingsGateway.Foundation;
|
||||
|
||||
using TouchSocket.Core;
|
||||
|
||||
namespace ThingsGateway.Web.Foundation;
|
||||
|
||||
/// <summary>
|
||||
/// 调试UI
|
||||
/// </summary>
|
||||
public abstract class DriverDebugUIBase : ComponentBase
|
||||
public abstract class DriverDebugUIBase : ComponentBase, IDisposable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
protected IReadWriteDevice plc;
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected bool isDownExport;
|
||||
/// <inheritdoc/>
|
||||
protected string address = "40001";
|
||||
/// <inheritdoc/>
|
||||
protected DataTypeEnum dataTypeEnum = DataTypeEnum.Int16;
|
||||
/// <inheritdoc/>
|
||||
protected int length = 1;
|
||||
/// <inheritdoc/>
|
||||
protected string writeValue = "1";
|
||||
/// <inheritdoc/>
|
||||
protected ConcurrentList<(long id, bool? isRed, string message)> Messages = new();
|
||||
/// <inheritdoc/>
|
||||
[Inject]
|
||||
protected IJSRuntime JS { get; set; }
|
||||
private System.Timers.Timer DelayTimer;
|
||||
/// <inheritdoc/>
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
DelayTimer = new System.Timers.Timer(1000);
|
||||
DelayTimer.Elapsed += timer_Elapsed;
|
||||
DelayTimer.AutoReset = true;
|
||||
DelayTimer.Start();
|
||||
base.OnInitialized();
|
||||
}
|
||||
|
||||
private async void timer_Elapsed(object sender, ElapsedEventArgs e)
|
||||
{
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public virtual void Dispose()
|
||||
{
|
||||
DelayTimer?.Dispose();
|
||||
|
||||
}
|
||||
/// <summary>
|
||||
/// 导出
|
||||
/// </summary>
|
||||
/// <param name="values"></param>
|
||||
/// <returns></returns>
|
||||
protected async Task DownDeviceMessageExport(IEnumerable<string> values)
|
||||
{
|
||||
try
|
||||
{
|
||||
isDownExport = true;
|
||||
StateHasChanged();
|
||||
var memoryStream = new MemoryStream();
|
||||
StreamWriter writer = new StreamWriter(memoryStream);
|
||||
foreach (var item in values)
|
||||
{
|
||||
writer.WriteLine(item);
|
||||
}
|
||||
|
||||
writer.Flush();
|
||||
memoryStream.Seek(0, SeekOrigin.Begin);
|
||||
using var streamRef = new DotNetStreamReference(stream: memoryStream);
|
||||
await JS.InvokeVoidAsync("downloadFileFromStream", $"报文导出{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss fff")}.txt", streamRef);
|
||||
}
|
||||
finally
|
||||
{
|
||||
isDownExport = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected void LogOut(string str)
|
||||
{
|
||||
Messages.Add((Yitter.IdGenerator.YitIdHelper.NextId(), null, str));
|
||||
if (Messages.Count > 2500)
|
||||
{
|
||||
Messages.RemoveRange(0, 2000);
|
||||
}
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public async Task Read()
|
||||
{
|
||||
var data = await plc.ReadAsync(address, length);
|
||||
if (data.IsSuccess)
|
||||
{
|
||||
try
|
||||
{
|
||||
var byteConverter = ByteConverterHelper.GetTransByAddress(ref address, plc.ThingsGatewayBitConverter, out int length, out BcdFormat bcdFormat);
|
||||
var value = plc.ThingsGatewayBitConverter.GetDynamicData(dataTypeEnum.GetNetType(), data.Content).ToString();
|
||||
Messages.Add((Yitter.IdGenerator.YitIdHelper.NextId(), false, DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss ffff") + " - 对应类型值:" + value + " - 原始字节:" + data.Content.ToHexString(" ")));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Messages.Add((Yitter.IdGenerator.YitIdHelper.NextId(), true, DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss ffff") + " - 操作成功,但转换数据类型失败 - 原因:" + ex.Message + " - 原始字节:" + data.Content.ToHexString(" ")));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Messages.Add((Yitter.IdGenerator.YitIdHelper.NextId(), true, DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss ffff") + " - " + data.Message));
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task Write()
|
||||
{
|
||||
try
|
||||
{
|
||||
var byteConverter = ByteConverterHelper.GetTransByAddress(ref address, plc.ThingsGatewayBitConverter, out int length, out BcdFormat bcdFormat);
|
||||
var data = await plc.WriteAsync(dataTypeEnum.GetNetType(), address, writeValue, dataTypeEnum == DataTypeEnum.Bcd);
|
||||
if (data.IsSuccess)
|
||||
{
|
||||
Messages.Add((Yitter.IdGenerator.YitIdHelper.NextId(), false, DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss ffff") + " - " + data.Message));
|
||||
}
|
||||
else
|
||||
{
|
||||
Messages.Add((Yitter.IdGenerator.YitIdHelper.NextId(), true, DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss ffff") + " - " + data.Message));
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Messages.Add((Yitter.IdGenerator.YitIdHelper.NextId(), true, DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss ffff") + " - " + "写入前失败:" + ex.Message));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -82,6 +82,31 @@ namespace ThingsGateway.Web.Foundation
|
||||
var data = GetCacheList();
|
||||
return data.FirstOrDefault(it => it.Id == Id);
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public List<DriverPluginCategory> GetDriverPluginChildrenList()
|
||||
{
|
||||
var data = GetCacheList();
|
||||
var driverPluginCategories = data.GroupBy(a => a.FileName).Select(it =>
|
||||
{
|
||||
var childrens = new List<DriverPluginCategory>();
|
||||
foreach (var item in it)
|
||||
{
|
||||
childrens.Add(new DriverPluginCategory
|
||||
{
|
||||
Id = item.Id,
|
||||
Name = item.AssembleName,
|
||||
}
|
||||
);
|
||||
}
|
||||
return new DriverPluginCategory
|
||||
{
|
||||
Id = YitIdHelper.NextId(),
|
||||
Name = it.Key,
|
||||
Children = childrens,
|
||||
};
|
||||
});
|
||||
return driverPluginCategories.ToList();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public List<DriverPluginCategory> GetDriverPluginChildrenList(DriverEnum driverTypeEnum)
|
||||
|
@@ -30,6 +30,12 @@ namespace ThingsGateway.Web.Foundation
|
||||
/// <param name="driverTypeEnum"></param>
|
||||
/// <returns></returns>
|
||||
List<DriverPluginCategory> GetDriverPluginChildrenList(DriverEnum driverTypeEnum);
|
||||
/// <summary>
|
||||
/// 获取插件树
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
List<DriverPluginCategory> GetDriverPluginChildrenList();
|
||||
|
||||
/// <summary>
|
||||
/// 根据ID获取名称
|
||||
/// </summary>
|
||||
|
@@ -31,9 +31,9 @@ namespace ThingsGateway.Web.Foundation
|
||||
{
|
||||
hardwareInfo = new();
|
||||
}
|
||||
catch(Exception ex)
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex,"初始化硬件信息失败");
|
||||
_logger.LogError(ex, "初始化硬件信息失败");
|
||||
}
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
|
@@ -166,6 +166,7 @@
|
||||
|
||||
}
|
||||
@code {
|
||||
[Inject] public JsInitVariables JsInitVariables { get; set; } = default!;
|
||||
[Inject]
|
||||
IJSRuntime JS { get; set; }
|
||||
bool isDownExport;
|
||||
@@ -178,7 +179,7 @@
|
||||
using var memoryStream = await CollectDeviceService.ExportFileAsync();
|
||||
memoryStream.Seek(0, SeekOrigin.Begin);
|
||||
using var streamRef = new DotNetStreamReference(stream: memoryStream);
|
||||
await JS.InvokeVoidAsync("downloadFileFromStream", $"采集设备导出{DateTime.UtcNow.ToString("MM-dd-HH-mm-ss")}.xlsx", streamRef);
|
||||
await JS.InvokeVoidAsync("downloadFileFromStream", $"采集设备导出{DateTime.UtcNow.Add(JsInitVariables.TimezoneOffset).ToString("MM-dd-HH-mm-ss")}.xlsx", streamRef);
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
@@ -121,6 +121,7 @@
|
||||
|
||||
}
|
||||
@code {
|
||||
[Inject] public JsInitVariables JsInitVariables { get; set; } = default!;
|
||||
[Inject]
|
||||
IJSRuntime JS { get; set; }
|
||||
bool isDownExport;
|
||||
@@ -133,7 +134,7 @@
|
||||
using var memoryStream = await VariableService.ExportFileAsync();
|
||||
memoryStream.Seek(0, SeekOrigin.Begin);
|
||||
using var streamRef = new DotNetStreamReference(stream: memoryStream);
|
||||
await JS.InvokeVoidAsync("downloadFileFromStream", $"变量导出{DateTime.UtcNow.ToString("MM-dd-HH-mm-ss")}.xlsx", streamRef);
|
||||
await JS.InvokeVoidAsync("downloadFileFromStream", $"变量导出{DateTime.UtcNow.Add(JsInitVariables.TimezoneOffset).ToString("MM-dd-HH-mm-ss")}.xlsx", streamRef);
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
@@ -15,10 +15,9 @@
|
||||
@inject UserResoures UserResoures
|
||||
@inject ICollectDeviceService CollectDeviceService
|
||||
@layout MainLayout
|
||||
|
||||
<MRow>
|
||||
<MCol Md=4 Cols="12">
|
||||
<MCard Class="ma-2" Style="height: calc(100vh - 200px)">
|
||||
<MRow NoGutters>
|
||||
<MCol Md="@("auto")">
|
||||
<MCard Show=IsShowTreeView Style="height: calc(100vh - 200px)">
|
||||
<MCardTitle>
|
||||
<MTextField Dense Style="max-width:200px;" HideDetails=@("auto") Class="mx-2 my-1" @bind-Value="_searchName" Outlined Label=@typeof(CollectDevice).GetDescription(nameof(CollectDevice.PluginId)) />
|
||||
</MCardTitle>
|
||||
@@ -37,17 +36,33 @@
|
||||
</MTreeview>
|
||||
</MCard>
|
||||
</MCol>
|
||||
<MCol Md=8 Cols="12">
|
||||
<MCard Class="ma-2" Style="height: calc(100vh - 200px)">
|
||||
<MCol>
|
||||
<MCard Style="height: calc(100vh - 200px)">
|
||||
<MCard Class="mb-3 pa-2 text-h6" Flat Outlined Rounded="false">
|
||||
<MRow Align="AlignTypes.Center">
|
||||
<MButton Class="mx-1" OnClick=@(async() =>
|
||||
{
|
||||
IsShowTreeView=!IsShowTreeView;
|
||||
}
|
||||
) Color="primary" Icon>
|
||||
<MIcon>mdi-menu</MIcon>
|
||||
</MButton>
|
||||
<MLabel>
|
||||
@_importRef?.ToString()
|
||||
</MLabel>
|
||||
</MRow>
|
||||
</MCard>
|
||||
@if (_importRender != null)
|
||||
{
|
||||
@_importRender
|
||||
}
|
||||
</MCard>
|
||||
</MCol>
|
||||
</MRow>
|
||||
|
||||
</MCol>
|
||||
|
||||
</MRow>
|
||||
@code {
|
||||
bool IsShowTreeView = true;
|
||||
class PluginDebugUIInput
|
||||
{
|
||||
public string PluginName { get; set; }
|
||||
@@ -68,8 +83,7 @@
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
DriverPlugins = DriverPluginService.GetDriverPluginChildrenList(DriverEnum.Collect);
|
||||
|
||||
DriverPlugins = DriverPluginService.GetDriverPluginChildrenList();
|
||||
base.OnInitialized();
|
||||
}
|
||||
async Task ImportVaiable(long driverId)
|
@@ -1,8 +1,6 @@
|
||||
using SqlSugar;
|
||||
|
||||
namespace ThingsGateway.Web.Page
|
||||
{
|
||||
public partial class PluginDebugPage
|
||||
public partial class DriverDebugPage
|
||||
{
|
||||
|
||||
[CascadingParameter]
|
@@ -154,6 +154,7 @@
|
||||
|
||||
}
|
||||
@code {
|
||||
[Inject] public JsInitVariables JsInitVariables { get; set; } = default!;
|
||||
[Inject]
|
||||
IJSRuntime JS { get; set; }
|
||||
bool isDownExport;
|
||||
@@ -166,7 +167,7 @@
|
||||
using var memoryStream = await UploadDeviceService.ExportFileAsync();
|
||||
memoryStream.Seek(0, SeekOrigin.Begin);
|
||||
using var streamRef = new DotNetStreamReference(stream: memoryStream);
|
||||
await JS.InvokeVoidAsync("downloadFileFromStream", $"上传设备导出{DateTime.UtcNow.ToString("MM-dd-HH-mm-ss")}.xlsx", streamRef);
|
||||
await JS.InvokeVoidAsync("downloadFileFromStream", $"上传设备导出{DateTime.UtcNow.Add(JsInitVariables.TimezoneOffset).ToString("MM-dd-HH-mm-ss")}.xlsx", streamRef);
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
Reference in New Issue
Block a user