添加Modbus系列插件调试页面;添加Modbus组包解析缓存超时时间;

This commit is contained in:
2248356998 qq.com
2023-05-23 20:50:44 +08:00
parent 98fd011def
commit da8bbd321f
34 changed files with 1362 additions and 68 deletions

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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);

View File

@@ -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;

View File

@@ -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);
}
}

View File

@@ -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;

View 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.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);
}
}

View File

@@ -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;

View 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.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);
}
}

View File

@@ -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)

View File

@@ -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);
}
}

View File

@@ -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; }

View 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);
}
}

View File

@@ -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()
{

View 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);
}
}

View File

@@ -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);

View 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;
}
}

View 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;
}
}

View 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;
}
}

View File

@@ -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;

View File

@@ -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);
}
}
}

View File

@@ -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)

View File

@@ -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/>

View File

@@ -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)
{

View File

@@ -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));
}
}
}

View File

@@ -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)

View File

@@ -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>

View File

@@ -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 () =>
{

View File

@@ -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
{

View File

@@ -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
{

View File

@@ -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)

View File

@@ -1,8 +1,6 @@
using SqlSugar;
namespace ThingsGateway.Web.Page
{
public partial class PluginDebugPage
public partial class DriverDebugPage
{
[CascadingParameter]

View File

@@ -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
{